source: clamav/trunk/libclamav/cvd.c@ 319

Last change on this file since 319 was 319, checked in by Yuri Dario, 14 years ago

clamav: update trunk to 0.97.

File size: 16.2 KB
Line 
1/*
2 * Copyright (C) 2007-2010 Sourcefire, Inc.
3 *
4 * Authors: Tomasz Kojm
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 * MA 02110-1301, USA.
19 */
20
21#if HAVE_CONFIG_H
22#include "clamav-config.h"
23#endif
24
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#ifdef HAVE_UNISTD_H
32#include <unistd.h>
33#endif
34#include "zlib.h"
35#include <time.h>
36#include <errno.h>
37
38#include "clamav.h"
39#include "others.h"
40#include "dsig.h"
41#include "str.h"
42#include "cvd.h"
43#include "readdb.h"
44#include "default.h"
45#include "sha256.h"
46
47#define TAR_BLOCKSIZE 512
48
49static int cli_untgz(int fd, const char *destdir)
50{
51 char *path, osize[13], name[101], type;
52 char block[TAR_BLOCKSIZE];
53 int nbytes, nread, nwritten, in_block = 0, fdd;
54 unsigned int size, pathlen = strlen(destdir) + 100 + 5;
55 FILE *outfile = NULL;
56 struct stat foo;
57 gzFile *infile;
58
59
60 cli_dbgmsg("in cli_untgz()\n");
61
62 if((fdd = dup(fd)) == -1) {
63 cli_errmsg("cli_untgz: Can't duplicate descriptor %d\n", fd);
64 return -1;
65 }
66
67 if((infile = gzdopen(fdd, "rb")) == NULL) {
68 cli_errmsg("cli_untgz: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
69 if(fstat(fdd, &foo) == 0)
70 close(fdd);
71 return -1;
72 }
73
74 path = (char *) cli_calloc(sizeof(char), pathlen);
75 if(!path) {
76 cli_errmsg("cli_untgz: Can't allocate memory for path\n");
77 gzclose(infile);
78 return -1;
79 }
80
81 while(1) {
82
83 nread = gzread(infile, block, TAR_BLOCKSIZE);
84
85 if(!in_block && !nread)
86 break;
87
88 if(nread != TAR_BLOCKSIZE) {
89 cli_errmsg("cli_untgz: Incomplete block read\n");
90 free(path);
91 gzclose(infile);
92 return -1;
93 }
94
95 if(!in_block) {
96 if (block[0] == '\0') /* We're done */
97 break;
98
99 strncpy(name, block, 100);
100 name[100] = '\0';
101
102 if(strchr(name, '/')) {
103 cli_errmsg("cli_untgz: Slash separators are not allowed in CVD\n");
104 free(path);
105 gzclose(infile);
106 return -1;
107 }
108
109 snprintf(path, pathlen, "%s"PATHSEP"%s", destdir, name);
110 cli_dbgmsg("cli_untgz: Unpacking %s\n", path);
111 type = block[156];
112
113 switch(type) {
114 case '0':
115 case '\0':
116 break;
117 case '5':
118 cli_errmsg("cli_untgz: Directories are not supported in CVD\n");
119 free(path);
120 gzclose(infile);
121 return -1;
122 default:
123 cli_errmsg("cli_untgz: Unknown type flag '%c'\n", type);
124 free(path);
125 gzclose(infile);
126 return -1;
127 }
128 in_block = 1;
129
130 if(outfile) {
131 if(fclose(outfile)) {
132 cli_errmsg("cli_untgz: Cannot close file %s\n", path);
133 free(path);
134 gzclose(infile);
135 return -1;
136 }
137 outfile = NULL;
138 }
139
140 if(!(outfile = fopen(path, "wb"))) {
141 cli_errmsg("cli_untgz: Cannot create file %s\n", path);
142 free(path);
143 gzclose(infile);
144 return -1;
145 }
146
147 strncpy(osize, block + 124, 12);
148 osize[12] = '\0';
149
150 if((sscanf(osize, "%o", &size)) == 0) {
151 cli_errmsg("cli_untgz: Invalid size in header\n");
152 free(path);
153 gzclose(infile);
154 fclose(outfile);
155 return -1;
156 }
157
158 } else { /* write or continue writing file contents */
159 nbytes = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size;
160 nwritten = fwrite(block, 1, nbytes, outfile);
161
162 if(nwritten != nbytes) {
163 cli_errmsg("cli_untgz: Wrote %d instead of %d (%s)\n", nwritten, nbytes, path);
164 free(path);
165 gzclose(infile);
166 return -1;
167 }
168
169 size -= nbytes;
170 if(size == 0)
171 in_block = 0;
172 }
173 }
174
175 if(outfile)
176 fclose(outfile);
177
178 gzclose(infile);
179 free(path);
180 return 0;
181}
182
183static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, struct cli_dbinfo *dbinfo)
184{
185 char osize[13], name[101];
186 char block[TAR_BLOCKSIZE];
187 int nread, fdd, ret;
188 unsigned int type, size, pad, compr = 1;
189 off_t off;
190 struct cli_dbinfo *db;
191 unsigned char hash[32];
192
193#define CLOSE_DBIO \
194 if(compr) \
195 gzclose(dbio->gzs); \
196 else \
197 fclose(dbio->fs)
198
199 cli_dbgmsg("in cli_tgzload()\n");
200
201 lseek(fd, 512, SEEK_SET);
202 if(cli_readn(fd, block, 7) != 7)
203 return CL_EFORMAT; /* truncated file? */
204
205 if(!strncmp(block, "COPYING", 7))
206 compr = 0;
207
208 lseek(fd, 512, SEEK_SET);
209
210 if((fdd = dup(fd)) == -1) {
211 cli_errmsg("cli_tgzload: Can't duplicate descriptor %d\n", fd);
212 return CL_EDUP;
213 }
214
215 if(compr) {
216 if((dbio->gzs = gzdopen(fdd, "rb")) == NULL) {
217 cli_errmsg("cli_tgzload: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
218 return CL_EOPEN;
219 }
220 dbio->fs = NULL;
221 } else {
222 if((dbio->fs = fdopen(fdd, "rb")) == NULL) {
223 cli_errmsg("cli_tgzload: Can't fdopen() descriptor %d, errno = %d\n", fdd, errno);
224 return CL_EOPEN;
225 }
226 dbio->gzs = NULL;
227 }
228
229 dbio->bufsize = CLI_DEFAULT_DBIO_BUFSIZE;
230 dbio->buf = cli_malloc(dbio->bufsize);
231 if(!dbio->buf) {
232 cli_errmsg("cli_tgzload: Can't allocate memory for dbio->buf\n");
233 CLOSE_DBIO;
234 return CL_EMALFDB;
235 }
236 dbio->bufpt = NULL;
237 dbio->usebuf = 1;
238 dbio->readpt = dbio->buf;
239
240 while(1) {
241
242 if(compr)
243 nread = gzread(dbio->gzs, block, TAR_BLOCKSIZE);
244 else
245 nread = fread(block, 1, TAR_BLOCKSIZE, dbio->fs);
246
247 if(!nread)
248 break;
249
250 if(nread != TAR_BLOCKSIZE) {
251 cli_errmsg("cli_tgzload: Incomplete block read\n");
252 free(dbio->buf);
253 CLOSE_DBIO;
254 return CL_EMALFDB;
255 }
256
257 if(block[0] == '\0') /* We're done */
258 break;
259
260 strncpy(name, block, 100);
261 name[100] = '\0';
262
263 if(strchr(name, '/')) {
264 cli_errmsg("cli_tgzload: Slash separators are not allowed in CVD\n");
265 free(dbio->buf);
266 CLOSE_DBIO;
267 return CL_EMALFDB;
268 }
269
270 type = block[156];
271
272 switch(type) {
273 case '0':
274 case '\0':
275 break;
276 case '5':
277 cli_errmsg("cli_tgzload: Directories are not supported in CVD\n");
278 free(dbio->buf);
279 CLOSE_DBIO;
280 return CL_EMALFDB;
281 default:
282 cli_errmsg("cli_tgzload: Unknown type flag '%c'\n", type);
283 free(dbio->buf);
284 CLOSE_DBIO;
285 return CL_EMALFDB;
286 }
287
288 strncpy(osize, block + 124, 12);
289 osize[12] = '\0';
290
291 if((sscanf(osize, "%o", &size)) == 0) {
292 cli_errmsg("cli_tgzload: Invalid size in header\n");
293 free(dbio->buf);
294 CLOSE_DBIO;
295 return CL_EMALFDB;
296 }
297 dbio->size = size;
298 dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
299 dbio->bufpt = NULL;
300 dbio->readpt = dbio->buf;
301 sha256_init(&dbio->sha256ctx);
302 dbio->bread = 0;
303
304 /* cli_dbgmsg("cli_tgzload: Loading %s, size: %u\n", name, size); */
305 if(compr)
306 off = (off_t) gzseek(dbio->gzs, 0, SEEK_CUR);
307 else
308 off = ftell(dbio->fs);
309
310 if((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && (CLI_DBEXT(name) || cli_strbcasestr(name, ".ign") || cli_strbcasestr(name, ".ign2")))) {
311 ret = cli_load(name, engine, signo, options, dbio);
312 if(ret) {
313 cli_errmsg("cli_tgzload: Can't load %s\n", name);
314 free(dbio->buf);
315 CLOSE_DBIO;
316 return CL_EMALFDB;
317 }
318 if(!dbinfo) {
319 free(dbio->buf);
320 CLOSE_DBIO;
321 return CL_SUCCESS;
322 } else {
323 db = dbinfo;
324 while(db && strcmp(db->name, name))
325 db = db->next;
326 if(!db) {
327 cli_errmsg("cli_tgzload: File %s not found in .info\n", name);
328 free(dbio->buf);
329 CLOSE_DBIO;
330 return CL_EMALFDB;
331 }
332 if(dbio->bread) {
333 if(db->size != dbio->bread) {
334 cli_errmsg("cli_tgzload: File %s not correctly loaded\n", name);
335 free(dbio->buf);
336 CLOSE_DBIO;
337 return CL_EMALFDB;
338 }
339 sha256_final(&dbio->sha256ctx, hash);
340 if(memcmp(db->hash, hash, 32)) {
341 cli_errmsg("cli_tgzload: Invalid checksum for file %s\n", name);
342 free(dbio->buf);
343 CLOSE_DBIO;
344 return CL_EMALFDB;
345 }
346 }
347 }
348 }
349 pad = size % TAR_BLOCKSIZE ? (TAR_BLOCKSIZE - (size % TAR_BLOCKSIZE)) : 0;
350 if(compr) {
351 if(off == gzseek(dbio->gzs, 0, SEEK_CUR))
352 gzseek(dbio->gzs, size + pad, SEEK_CUR);
353 else if(pad)
354 gzseek(dbio->gzs, pad, SEEK_CUR);
355 } else {
356 if(off == ftell(dbio->fs))
357 fseek(dbio->fs, size + pad, SEEK_CUR);
358 else if(pad)
359 fseek(dbio->fs, pad, SEEK_CUR);
360 }
361 }
362
363 free(dbio->buf);
364 CLOSE_DBIO;
365 return CL_SUCCESS;
366}
367
368struct cl_cvd *cl_cvdparse(const char *head)
369{
370 struct cl_cvd *cvd;
371 char *pt;
372
373
374 if(strncmp(head, "ClamAV-VDB:", 11)) {
375 cli_errmsg("cli_cvdparse: Not a CVD file\n");
376 return NULL;
377 }
378
379 if(!(cvd = (struct cl_cvd *) cli_malloc(sizeof(struct cl_cvd)))) {
380 cli_errmsg("cl_cvdparse: Can't allocate memory for cvd\n");
381 return NULL;
382 }
383
384 if(!(cvd->time = cli_strtok(head, 1, ":"))) {
385 cli_errmsg("cli_cvdparse: Can't parse the creation time\n");
386 free(cvd);
387 return NULL;
388 }
389
390 if(!(pt = cli_strtok(head, 2, ":"))) {
391 cli_errmsg("cli_cvdparse: Can't parse the version number\n");
392 free(cvd->time);
393 free(cvd);
394 return NULL;
395 }
396 cvd->version = atoi(pt);
397 free(pt);
398
399 if(!(pt = cli_strtok(head, 3, ":"))) {
400 cli_errmsg("cli_cvdparse: Can't parse the number of signatures\n");
401 free(cvd->time);
402 free(cvd);
403 return NULL;
404 }
405 cvd->sigs = atoi(pt);
406 free(pt);
407
408 if(!(pt = cli_strtok(head, 4, ":"))) {
409 cli_errmsg("cli_cvdparse: Can't parse the functionality level\n");
410 free(cvd->time);
411 free(cvd);
412 return NULL;
413 }
414 cvd->fl = atoi(pt);
415 free(pt);
416
417 if(!(cvd->md5 = cli_strtok(head, 5, ":"))) {
418 cli_errmsg("cli_cvdparse: Can't parse the MD5 checksum\n");
419 free(cvd->time);
420 free(cvd);
421 return NULL;
422 }
423
424 if(!(cvd->dsig = cli_strtok(head, 6, ":"))) {
425 cli_errmsg("cli_cvdparse: Can't parse the digital signature\n");
426 free(cvd->time);
427 free(cvd->md5);
428 free(cvd);
429 return NULL;
430 }
431
432 if(!(cvd->builder = cli_strtok(head, 7, ":"))) {
433 cli_errmsg("cli_cvdparse: Can't parse the builder name\n");
434 free(cvd->time);
435 free(cvd->md5);
436 free(cvd->dsig);
437 free(cvd);
438 return NULL;
439 }
440
441 if((pt = cli_strtok(head, 8, ":"))) {
442 cvd->stime = atoi(pt);
443 free(pt);
444 } else {
445 cli_dbgmsg("cli_cvdparse: No creation time in seconds (old file format)\n");
446 cvd->stime = 0;
447 }
448
449 return cvd;
450}
451
452struct cl_cvd *cl_cvdhead(const char *file)
453{
454 FILE *fs;
455 char head[513], *pt;
456 int i;
457 unsigned int bread;
458
459
460 if((fs = fopen(file, "rb")) == NULL) {
461 cli_errmsg("cl_cvdhead: Can't open file %s\n", file);
462 return NULL;
463 }
464
465 if(!(bread = fread(head, 1, 512, fs))) {
466 cli_errmsg("cl_cvdhead: Can't read CVD header in %s\n", file);
467 fclose(fs);
468 return NULL;
469 }
470
471 fclose(fs);
472
473 head[bread] = 0;
474 if((pt = strpbrk(head, "\n\r")))
475 *pt = 0;
476
477 for(i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--);
478
479 return cl_cvdparse(head);
480}
481
482void cl_cvdfree(struct cl_cvd *cvd)
483{
484 free(cvd->time);
485 free(cvd->md5);
486 free(cvd->dsig);
487 free(cvd->builder);
488 free(cvd);
489}
490
491static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int cld)
492{
493 struct cl_cvd *cvd;
494 char *md5, head[513];
495 int i;
496
497
498 fseek(fs, 0, SEEK_SET);
499 if(fread(head, 1, 512, fs) != 512) {
500 cli_errmsg("cli_cvdverify: Can't read CVD header\n");
501 return CL_ECVD;
502 }
503
504 head[512] = 0;
505 for(i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--);
506
507 if((cvd = cl_cvdparse(head)) == NULL)
508 return CL_ECVD;
509
510 if(cvdpt)
511 memcpy(cvdpt, cvd, sizeof(struct cl_cvd));
512
513 if(cld) {
514 cl_cvdfree(cvd);
515 return CL_SUCCESS;
516 }
517
518 md5 = cli_hashstream(fs, NULL, 1);
519 cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
520
521 if(strncmp(md5, cvd->md5, 32)) {
522 cli_dbgmsg("cli_cvdverify: MD5 verification error\n");
523 free(md5);
524 cl_cvdfree(cvd);
525 return CL_EVERIFY;
526 }
527
528 if(cli_versig(md5, cvd->dsig)) {
529 cli_dbgmsg("cli_cvdverify: Digital signature verification error\n");
530 free(md5);
531 cl_cvdfree(cvd);
532 return CL_EVERIFY;
533 }
534
535 free(md5);
536 cl_cvdfree(cvd);
537 return CL_SUCCESS;
538}
539
540int cl_cvdverify(const char *file)
541{
542 struct cl_engine *engine;
543 FILE *fs;
544 int ret;
545
546
547 if((fs = fopen(file, "rb")) == NULL) {
548 cli_errmsg("cl_cvdverify: Can't open file %s\n", file);
549 return CL_EOPEN;
550 }
551
552 if(!(engine = cl_engine_new())) {
553 cli_errmsg("cld_cvdverify: Can't create new engine\n");
554 fclose(fs);
555 return CL_EMEM;
556 }
557
558 ret = cli_cvdload(fs, engine, NULL, CL_DB_STDOPT | CL_DB_PUA, !!cli_strbcasestr(file, ".cld"), file, 1);
559
560 cl_engine_free(engine);
561 fclose(fs);
562 return ret;
563}
564
565int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int cld, const char *filename, unsigned int chkonly)
566{
567 struct cl_cvd cvd, dupcvd;
568 FILE *dupfs;
569 int ret;
570 time_t s_time;
571 int cfd;
572 struct cli_dbio dbio;
573 struct cli_dbinfo *dbinfo = NULL;
574 char *dupname;
575
576 cli_dbgmsg("in cli_cvdload()\n");
577
578 /* verify */
579 if((ret = cli_cvdverify(fs, &cvd, cld)))
580 return ret;
581
582 /* check for duplicate db */
583 dupname = cli_strdup(filename);
584 if(!dupname)
585 return CL_EMEM;
586 dupname[strlen(dupname) - 2] = (cld ? 'v' : 'l');
587 if(!access(dupname, R_OK) && (dupfs = fopen(dupname, "rb"))) {
588 if((ret = cli_cvdverify(dupfs, &dupcvd, !cld))) {
589 fclose(dupfs);
590 free(dupname);
591 return ret;
592 }
593 fclose(dupfs);
594 if(dupcvd.version > cvd.version) {
595 cli_warnmsg("Detected duplicate databases %s and %s. The %s database is older and will not be loaded, you should manually remove it from the database directory.\n", filename, dupname, filename);
596 free(dupname);
597 return CL_SUCCESS;
598 } else if(dupcvd.version == cvd.version && !cld) {
599 cli_warnmsg("Detected duplicate databases %s and %s, please manually remove one of them\n", filename, dupname);
600 free(dupname);
601 return CL_SUCCESS;
602 }
603 }
604 free(dupname);
605
606 if(strstr(filename, "daily.")) {
607 time(&s_time);
608 if(cvd.stime > s_time) {
609 if(cvd.stime - (unsigned int ) s_time > 3600) {
610 cli_warnmsg("******************************************************\n");
611 cli_warnmsg("*** Virus database timestamp in the future! ***\n");
612 cli_warnmsg("*** Please check the timezone and clock settings ***\n");
613 cli_warnmsg("******************************************************\n");
614 }
615 } else if((unsigned int) s_time - cvd.stime > 604800) {
616 cli_warnmsg("**************************************************\n");
617 cli_warnmsg("*** The virus database is older than 7 days! ***\n");
618 cli_warnmsg("*** Please update it as soon as possible. ***\n");
619 cli_warnmsg("**************************************************\n");
620 }
621 engine->dbversion[0] = cvd.version;
622 engine->dbversion[1] = cvd.stime;
623 }
624
625 if(cvd.fl > cl_retflevel()) {
626 cli_warnmsg("***********************************************************\n");
627 cli_warnmsg("*** This version of the ClamAV engine is outdated. ***\n");
628 cli_warnmsg("*** DON'T PANIC! Read http://www.clamav.net/support/faq ***\n");
629 cli_warnmsg("***********************************************************\n");
630 }
631
632 cfd = fileno(fs);
633 dbio.chkonly = 0;
634 ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, NULL);
635 if(ret != CL_SUCCESS)
636 return ret;
637
638 dbinfo = engine->dbinfo;
639 if(!dbinfo || !dbinfo->cvd || (dbinfo->cvd->version != cvd.version) || (dbinfo->cvd->sigs != cvd.sigs) || (dbinfo->cvd->fl != cvd.fl) || (dbinfo->cvd->stime != cvd.stime)) {
640 cli_errmsg("cli_cvdload: Corrupted CVD header\n");
641 return CL_EMALFDB;
642 }
643 dbinfo = engine->dbinfo ? engine->dbinfo->next : NULL;
644 if(!dbinfo)
645 return CL_EMALFDB;
646
647 dbio.chkonly = chkonly;
648 options |= CL_DB_SIGNED;
649 ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, dbinfo);
650
651 while(engine->dbinfo) {
652 dbinfo = engine->dbinfo;
653 engine->dbinfo = dbinfo->next;
654 mpool_free(engine->mempool, dbinfo->name);
655 mpool_free(engine->mempool, dbinfo->hash);
656 if(dbinfo->cvd)
657 cl_cvdfree(dbinfo->cvd);
658 mpool_free(engine->mempool, dbinfo);
659 }
660
661 return ret;
662}
663
664int cli_cvdunpack(const char *file, const char *dir)
665{
666 int fd, ret;
667
668
669 fd = open(file, O_RDONLY|O_BINARY);
670 if(fd == -1)
671 return -1;
672
673 if(lseek(fd, 512, SEEK_SET) < 0) {
674 close(fd);
675 return -1;
676 }
677
678 ret = cli_untgz(fd, dir);
679 close(fd);
680 return ret;
681}
Note: See TracBrowser for help on using the repository browser.