1 /*
2 * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3 * Copyright (C) 2007-2013 Sourcefire, Inc.
4 * Copyright (C) 2002-2007 Tomasz Kojm <tkojm@clamav.net>
5 *
6 * CDIFF code (C) 2006 Sensory Networks, Inc.
7 *
8 * Author: Tomasz Kojm <tkojm@clamav.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24
25 #if HAVE_CONFIG_H
26 #include "clamav-config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #include <zlib.h>
36 #include <time.h>
37 #include <locale.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #ifndef _WIN32
42 #include <sys/socket.h>
43 #include <sys/un.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <sys/wait.h>
47 #else
48 #include "w32_stat.h"
49 #endif
50 #include <dirent.h>
51 #include <ctype.h>
52 #include <libgen.h>
53
54 #ifdef HAVE_TERMIOS_H
55 #include <termios.h>
56 #endif
57
58 // libclamav
59 #include "clamav.h"
60 #include "matcher.h"
61 #include "cvd.h"
62 #include "str.h"
63 #include "ole2_extract.h"
64 #include "htmlnorm.h"
65 #include "textnorm.h"
66 #include "default.h"
67 #include "fmap.h"
68 #include "readdb.h"
69 #include "others.h"
70 #include "pe.h"
71 #include "entconv.h"
72
73 // common
74 #include "output.h"
75 #include "optparser.h"
76 #include "misc.h"
77 #include "cdiff.h"
78 #include "tar.h"
79
80 #include "vba.h"
81
82 #define MAX_DEL_LOOKAHEAD 5000
83
84 //struct s_info info;
85 short recursion = 0, bell = 0;
86 short printinfected = 0, printclean = 1;
87
88 static const struct dblist_s {
89 const char *ext;
90 unsigned int count;
91 } dblist[] = {
92
93 /* special files */
94 {"info", 0},
95 {"cfg", 0},
96 {"ign", 0},
97 {"ign2", 0},
98 {"ftm", 0},
99
100 /* databases */
101 {"db", 1},
102 {"hdb", 1},
103 {"hdu", 1},
104 {"hsb", 1},
105 {"hsu", 1},
106 {"mdb", 1},
107 {"mdu", 1},
108 {"msb", 1},
109 {"msu", 1},
110 {"ndb", 1},
111 {"ndu", 1},
112 {"ldb", 1},
113 {"ldu", 1},
114 {"sdb", 1},
115 {"zmd", 1},
116 {"rmd", 1},
117 {"idb", 0},
118 {"fp", 1}, // TODO Should count be 0 here? We don't count others like this
119 {"sfp", 0},
120 {"gdb", 1},
121 {"pdb", 1},
122 {"wdb", 0},
123 {"crb", 1},
124 {"cdb", 1},
125 {"imp", 1},
126 // TODO Should we add .ioc, .yar, .yara, and .pwdb so that sigtool will
127 // include these sigs in a build (just in case we need this functionality
128 // in the future?)
129
130 {NULL, 0}};
131
getdbname(const char * str,char * dst,int dstlen)132 static char *getdbname(const char *str, char *dst, int dstlen)
133 {
134 int len = strlen(str);
135
136 if (cli_strbcasestr(str, ".cvd") || cli_strbcasestr(str, ".cld") || cli_strbcasestr(str, ".cud"))
137 len -= 4;
138
139 if (dst) {
140 strncpy(dst, str, MIN(dstlen - 1, len));
141 dst[MIN(dstlen - 1, len)] = 0;
142 } else {
143 dst = (char *)malloc(len + 1);
144 if (!dst)
145 return NULL;
146 strncpy(dst, str, len - 4);
147 dst[MIN(dstlen - 1, len - 4)] = 0;
148 }
149 return dst;
150 }
151
hexdump(void)152 static int hexdump(void)
153 {
154 char buffer[FILEBUFF], *pt;
155 int bytes;
156
157 while ((bytes = read(0, buffer, FILEBUFF)) > 0) {
158 pt = cli_str2hex(buffer, bytes);
159 if (write(1, pt, 2 * bytes) == -1) {
160 mprintf("!hexdump: Can't write to stdout\n");
161 free(pt);
162 return -1;
163 }
164 free(pt);
165 }
166
167 if (bytes == -1)
168 return -1;
169
170 return 0;
171 }
172
hashpe(const char * filename,unsigned int class,int type)173 static int hashpe(const char *filename, unsigned int class, int type)
174 {
175 int status = -1;
176 STATBUF sb;
177 const char *fmptr;
178 struct cl_engine *engine = NULL;
179 cli_ctx ctx = {0};
180 struct cl_scan_options options = {0};
181 cl_fmap_t *new_map = NULL;
182 int fd = -1;
183 cl_error_t ret;
184
185 /* Prepare file */
186 fd = open(filename, O_RDONLY);
187 if (fd < 0) {
188 mprintf("!hashpe: Can't open file %s!\n", filename);
189 goto done;
190 }
191
192 lseek(fd, 0, SEEK_SET);
193 FSTAT(fd, &sb);
194
195 new_map = fmap(fd, 0, sb.st_size, filename);
196 if (NULL == new_map) {
197 mprintf("!hashpe: Can't create fmap for open file\n");
198 goto done;
199 }
200
201 /* build engine */
202 if (!(engine = cl_engine_new())) {
203 mprintf("!hashpe: Can't create new engine\n");
204 goto done;
205 }
206 cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
207
208 if (cli_initroots(engine, 0) != CL_SUCCESS) {
209 mprintf("!hashpe: cli_initroots() failed\n");
210 goto done;
211 }
212
213 if (cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
214 mprintf("!hashpe: Can't parse signature\n");
215 goto done;
216 }
217
218 if (cl_engine_compile(engine) != CL_SUCCESS) {
219 mprintf("!hashpe: Can't compile engine\n");
220 goto done;
221 }
222
223 /* prepare context */
224 ctx.engine = engine;
225 ctx.options = &options;
226 ctx.options->parse = ~0;
227 ctx.dconf = (struct cli_dconf *)engine->dconf;
228
229 ctx.recursion_stack_size = ctx.engine->max_recursion_level;
230 ctx.recursion_stack = cli_calloc(sizeof(recursion_level_t), ctx.recursion_stack_size);
231 if (!ctx.recursion_stack) {
232 goto done;
233 }
234
235 // ctx was memset, so recursion_level starts at 0.
236 ctx.recursion_stack[ctx.recursion_level].fmap = new_map;
237 ctx.recursion_stack[ctx.recursion_level].type = CL_TYPE_ANY; // ANY for the top level, because we don't yet know the type.
238 ctx.recursion_stack[ctx.recursion_level].size = new_map->len;
239
240 ctx.fmap = ctx.recursion_stack[ctx.recursion_level].fmap;
241
242 fmptr = fmap_need_off_once(ctx.fmap, 0, sb.st_size);
243 if (!fmptr) {
244 mprintf("!hashpe: fmap_need_off_once failed!\n");
245 goto done;
246 }
247
248 cl_debug();
249
250 /* Send to PE-specific hasher */
251 switch (class) {
252 case 1:
253 ret = cli_genhash_pe(&ctx, CL_GENHASH_PE_CLASS_SECTION, type, NULL);
254 break;
255 case 2:
256 ret = cli_genhash_pe(&ctx, CL_GENHASH_PE_CLASS_IMPTBL, type, NULL);
257 break;
258 default:
259 mprintf("!hashpe: unknown classification(%u) for pe hash!\n", class);
260 goto done;
261 }
262
263 /* THIS MAY BE UNNECESSARY */
264 switch (ret) {
265 case CL_CLEAN:
266 break;
267 case CL_VIRUS:
268 mprintf("*hashpe: CL_VIRUS after cli_genhash_pe()!\n");
269 break;
270 case CL_BREAK:
271 mprintf("*hashpe: CL_BREAK after cli_genhash_pe()!\n");
272 break;
273 case CL_EFORMAT:
274 mprintf("!hashpe: Not a valid PE file!\n");
275 break;
276 default:
277 mprintf("!hashpe: Other error %d inside cli_genhash_pe.\n", ret);
278 break;
279 }
280
281 status = 0;
282
283 done:
284 /* Cleanup */
285 if (NULL != new_map) {
286 funmap(new_map);
287 }
288 if (NULL != ctx.recursion_stack) {
289 free(ctx.recursion_stack);
290 }
291 if (NULL != engine) {
292 cl_engine_free(engine);
293 }
294 if (-1 != fd) {
295 close(fd);
296 }
297 return status;
298 }
299
hashsig(const struct optstruct * opts,unsigned int class,int type)300 static int hashsig(const struct optstruct *opts, unsigned int class, int type)
301 {
302 char *hash;
303 unsigned int i;
304 STATBUF sb;
305
306 if (opts->filename) {
307 for (i = 0; opts->filename[i]; i++) {
308 if (CLAMSTAT(opts->filename[i], &sb) == -1) {
309 perror("hashsig");
310 mprintf("!hashsig: Can't access file %s\n", opts->filename[i]);
311 return -1;
312 } else {
313 if ((sb.st_mode & S_IFMT) == S_IFREG) {
314 if ((class == 0) && (hash = cli_hashfile(opts->filename[i], type))) {
315 mprintf("%s:%u:%s\n", hash, (unsigned int)sb.st_size, basename(opts->filename[i]));
316 free(hash);
317 } else if ((class > 0) && (hashpe(opts->filename[i], class, type) == 0)) {
318 /* intentionally empty - printed in cli_genhash_pe() */
319 } else {
320 mprintf("!hashsig: Can't generate hash for %s\n", opts->filename[i]);
321 return -1;
322 }
323 }
324 }
325 }
326
327 } else { /* stream */
328 if (class > 0) {
329 mprintf("!hashsig: Can't generate requested hash for input stream\n");
330 return -1;
331 }
332 hash = cli_hashstream(stdin, NULL, type);
333 if (!hash) {
334 mprintf("!hashsig: Can't generate hash for input stream\n");
335 return -1;
336 }
337 mprintf("%s\n", hash);
338 free(hash);
339 }
340
341 return 0;
342 }
343
htmlnorm(const struct optstruct * opts)344 static int htmlnorm(const struct optstruct *opts)
345 {
346 int fd;
347 fmap_t *map;
348
349 if ((fd = open(optget(opts, "html-normalise")->strarg, O_RDONLY | O_BINARY)) == -1) {
350 mprintf("!htmlnorm: Can't open file %s\n", optget(opts, "html-normalise")->strarg);
351 return -1;
352 }
353
354 if ((map = fmap(fd, 0, 0, optget(opts, "html-normalise")->strarg))) {
355 html_normalise_map(map, ".", NULL, NULL);
356 funmap(map);
357 } else
358 mprintf("!fmap failed\n");
359
360 close(fd);
361
362 return 0;
363 }
364
asciinorm(const struct optstruct * opts)365 static int asciinorm(const struct optstruct *opts)
366 {
367 const char *fname;
368 unsigned char *norm_buff;
369 struct text_norm_state state;
370 size_t map_off;
371 fmap_t *map;
372 int fd, ofd;
373
374 fname = optget(opts, "ascii-normalise")->strarg;
375 fd = open(fname, O_RDONLY | O_BINARY);
376
377 if (fd == -1) {
378 mprintf("!asciinorm: Can't open file %s\n", fname);
379 return -1;
380 }
381
382 if (!(norm_buff = malloc(ASCII_FILE_BUFF_LENGTH))) {
383 mprintf("!asciinorm: Can't allocate memory\n");
384 close(fd);
385 return -1;
386 }
387
388 if (!(map = fmap(fd, 0, 0, fname))) {
389 mprintf("!fmap: Could not map fd %d\n", fd);
390 close(fd);
391 free(norm_buff);
392 return -1;
393 }
394
395 if (map->len > MAX_ASCII_FILE_SIZE) {
396 mprintf("!asciinorm: File size of %zu too large\n", map->len);
397 close(fd);
398 free(norm_buff);
399 funmap(map);
400 return -1;
401 }
402
403 ofd = open("./normalised_text", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
404 if (ofd == -1) {
405 mprintf("!asciinorm: Can't open file ./normalised_text\n");
406 close(fd);
407 free(norm_buff);
408 funmap(map);
409 return -1;
410 }
411
412 text_normalize_init(&state, norm_buff, ASCII_FILE_BUFF_LENGTH);
413
414 map_off = 0;
415 while (map_off != map->len) {
416 size_t written;
417 if (!(written = text_normalize_map(&state, map, map_off))) break;
418 map_off += written;
419
420 if (write(ofd, norm_buff, state.out_pos) == -1) {
421 mprintf("!asciinorm: Can't write to file ./normalised_text\n");
422 close(fd);
423 close(ofd);
424 free(norm_buff);
425 funmap(map);
426 return -1;
427 }
428 text_normalize_reset(&state);
429 }
430
431 close(fd);
432 close(ofd);
433 free(norm_buff);
434 funmap(map);
435 return 0;
436 }
437
utf16decode(const struct optstruct * opts)438 static int utf16decode(const struct optstruct *opts)
439 {
440 const char *fname;
441 char *newname, buff[512], *decoded;
442 int fd1, fd2, bytes;
443
444 fname = optget(opts, "utf16-decode")->strarg;
445 if ((fd1 = open(fname, O_RDONLY | O_BINARY)) == -1) {
446 mprintf("!utf16decode: Can't open file %s\n", fname);
447 return -1;
448 }
449
450 newname = malloc(strlen(fname) + 7);
451 if (!newname) {
452 mprintf("!utf16decode: Can't allocate memory\n");
453 close(fd1);
454 return -1;
455 }
456 sprintf(newname, "%s.ascii", fname);
457
458 if ((fd2 = open(newname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
459 mprintf("!utf16decode: Can't create file %s\n", newname);
460 free(newname);
461 close(fd1);
462 return -1;
463 }
464
465 while ((bytes = read(fd1, buff, sizeof(buff))) > 0) {
466 decoded = cli_utf16toascii(buff, bytes);
467 if (decoded) {
468 if (write(fd2, decoded, strlen(decoded)) == -1) {
469 mprintf("!utf16decode: Can't write to file %s\n", newname);
470 free(decoded);
471 close(fd1);
472 close(fd2);
473 unlink(newname);
474 free(newname);
475 return -1;
476 }
477 free(decoded);
478 }
479 }
480
481 free(newname);
482 close(fd1);
483 close(fd2);
484
485 return 0;
486 }
487
getdsig(const char * host,const char * user,const unsigned char * data,unsigned int datalen,unsigned short mode)488 static char *getdsig(const char *host, const char *user, const unsigned char *data, unsigned int datalen, unsigned short mode)
489 {
490 char buff[512], cmd[128], pass[30], *pt;
491 struct sockaddr_in server;
492 int sockd, bread, len;
493 #ifdef HAVE_TERMIOS_H
494 struct termios old, new;
495 #endif
496
497 memset(&server, 0x00, sizeof(struct sockaddr_in));
498
499 if ((pt = getenv("SIGNDPASS"))) {
500 strncpy(pass, pt, sizeof(pass));
501 pass[sizeof(pass) - 1] = '\0';
502 } else {
503 mprintf("Password: ");
504
505 #ifdef HAVE_TERMIOS_H
506 if (tcgetattr(0, &old)) {
507 mprintf("!getdsig: tcgetattr() failed\n");
508 return NULL;
509 }
510 new = old;
511 new.c_lflag &= ~ECHO;
512 if (tcsetattr(0, TCSAFLUSH, &new)) {
513 mprintf("!getdsig: tcsetattr() failed\n");
514 return NULL;
515 }
516 #endif
517 if (scanf("%30s", pass) == EOF) {
518 mprintf("!getdsig: Can't get password\n");
519 #ifdef HAVE_TERMIOS_H
520 tcsetattr(0, TCSAFLUSH, &old);
521 #endif
522 return NULL;
523 }
524
525 #ifdef HAVE_TERMIOS_H
526 if (tcsetattr(0, TCSAFLUSH, &old)) {
527 mprintf("!getdsig: tcsetattr() failed\n");
528 memset(pass, 0, sizeof(pass));
529 return NULL;
530 }
531 #endif
532 mprintf("\n");
533 }
534
535 if ((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
536 perror("socket()");
537 mprintf("!getdsig: Can't create socket\n");
538 memset(pass, 0, sizeof(pass));
539 return NULL;
540 }
541
542 server.sin_family = AF_INET;
543 server.sin_addr.s_addr = inet_addr(host);
544 server.sin_port = htons(33101);
545
546 if (connect(sockd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
547 perror("connect()");
548 closesocket(sockd);
549 mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host);
550 memset(pass, 0, sizeof(pass));
551 return NULL;
552 }
553 memset(cmd, 0, sizeof(cmd));
554
555 if (mode == 1)
556 snprintf(cmd, sizeof(cmd) - datalen, "ClamSign:%s:%s:", user, pass);
557 else if (mode == 2)
558 snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS:%s:%s:", user, pass);
559 else
560 snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS2:%s:%s:", user, pass);
561
562 len = strlen(cmd);
563 pt = cmd + len;
564 memcpy(pt, data, datalen);
565 len += datalen;
566
567 if (send(sockd, cmd, len, 0) < 0) {
568 mprintf("!getdsig: Can't write to socket\n");
569 closesocket(sockd);
570 memset(cmd, 0, sizeof(cmd));
571 memset(pass, 0, sizeof(pass));
572 return NULL;
573 }
574
575 memset(cmd, 0, sizeof(cmd));
576 memset(pass, 0, sizeof(pass));
577 memset(buff, 0, sizeof(buff));
578
579 if ((bread = recv(sockd, buff, sizeof(buff) - 1, 0)) > 0) {
580 buff[bread] = '\0';
581 if (!strstr(buff, "Signature:")) {
582 mprintf("!getdsig: Error generating digital signature\n");
583 mprintf("!getdsig: Answer from remote server: %s\n", buff);
584 closesocket(sockd);
585 return NULL;
586 } else {
587 mprintf("Signature received (length = %lu)\n", (unsigned long)strlen(buff) - 10);
588 }
589 } else {
590 mprintf("!getdsig: Communication error with remote server\n");
591 closesocket(sockd);
592 return NULL;
593 }
594
595 closesocket(sockd);
596
597 pt = buff;
598 pt += 10;
599 return strdup(pt);
600 }
601
sha256file(const char * file,unsigned int * size)602 static char *sha256file(const char *file, unsigned int *size)
603 {
604 FILE *fh;
605 unsigned int i, bytes;
606 unsigned char digest[32], buffer[FILEBUFF];
607 char *sha;
608 void *ctx;
609
610 ctx = cl_hash_init("sha256");
611 if (!(ctx))
612 return NULL;
613
614 if (!(fh = fopen(file, "rb"))) {
615 mprintf("!sha256file: Can't open file %s\n", file);
616 cl_hash_destroy(ctx);
617 return NULL;
618 }
619 if (size)
620 *size = 0;
621 while ((bytes = fread(buffer, 1, sizeof(buffer), fh))) {
622 cl_update_hash(ctx, buffer, bytes);
623 if (size)
624 *size += bytes;
625 }
626 cl_finish_hash(ctx, digest);
627 sha = (char *)malloc(65);
628 if (!sha) {
629 fclose(fh);
630 return NULL;
631 }
632 for (i = 0; i < 32; i++)
633 sprintf(sha + i * 2, "%02x", digest[i]);
634
635 fclose(fh);
636 return sha;
637 }
638
writeinfo(const char * dbname,const char * builder,const char * header,const struct optstruct * opts,char * const * dblist2,unsigned int dblist2cnt)639 static int writeinfo(const char *dbname, const char *builder, const char *header, const struct optstruct *opts, char *const *dblist2, unsigned int dblist2cnt)
640 {
641 FILE *fh;
642 unsigned int i, bytes;
643 char file[32], *pt, dbfile[32];
644 unsigned char digest[32], buffer[FILEBUFF];
645 void *ctx;
646
647 snprintf(file, sizeof(file), "%s.info", dbname);
648 if (!access(file, R_OK)) {
649 if (unlink(file) == -1) {
650 mprintf("!writeinfo: Can't unlink %s\n", file);
651 return -1;
652 }
653 }
654
655 if (!(fh = fopen(file, "wb+"))) {
656 mprintf("!writeinfo: Can't create file %s\n", file);
657 return -1;
658 }
659
660 if (fprintf(fh, "%s\n", header) < 0) {
661 mprintf("!writeinfo: Can't write to %s\n", file);
662 fclose(fh);
663 return -1;
664 }
665
666 if (dblist2cnt) {
667 for (i = 0; i < dblist2cnt; i++) {
668 if (!(pt = sha256file(dblist2[i], &bytes))) {
669 mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
670 fclose(fh);
671 return -1;
672 }
673 if (fprintf(fh, "%s:%u:%s\n", dblist2[i], bytes, pt) < 0) {
674 mprintf("!writeinfo: Can't write to info file\n");
675 fclose(fh);
676 free(pt);
677 return -1;
678 }
679 free(pt);
680 }
681 }
682 if (!dblist2cnt || optget(opts, "hybrid")->enabled) {
683 for (i = 0; dblist[i].ext; i++) {
684 snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
685 if (strcmp(dblist[i].ext, "info") && !access(dbfile, R_OK)) {
686 if (!(pt = sha256file(dbfile, &bytes))) {
687 mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
688 fclose(fh);
689 return -1;
690 }
691 if (fprintf(fh, "%s:%u:%s\n", dbfile, bytes, pt) < 0) {
692 mprintf("!writeinfo: Can't write to info file\n");
693 fclose(fh);
694 free(pt);
695 return -1;
696 }
697 free(pt);
698 }
699 }
700 }
701 if (!optget(opts, "unsigned")->enabled) {
702 rewind(fh);
703 ctx = cl_hash_init("sha256");
704 if (!(ctx)) {
705 fclose(fh);
706 return -1;
707 }
708
709 while ((bytes = fread(buffer, 1, sizeof(buffer), fh)))
710 cl_update_hash(ctx, buffer, bytes);
711 cl_finish_hash(ctx, digest);
712 if (!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 3))) {
713 mprintf("!writeinfo: Can't get digital signature from remote server\n");
714 fclose(fh);
715 return -1;
716 }
717 fprintf(fh, "DSIG:%s\n", pt);
718 free(pt);
719 }
720 fclose(fh);
721 return 0;
722 }
723
724 static int diffdirs(const char *old, const char *new, const char *patch);
725 static int verifydiff(const char *diff, const char *cvd, const char *incdir);
726
script2cdiff(const char * script,const char * builder,const struct optstruct * opts)727 static int script2cdiff(const char *script, const char *builder, const struct optstruct *opts)
728 {
729 char *cdiff, *pt, buffer[FILEBUFF];
730 unsigned char digest[32];
731 void *ctx;
732 STATBUF sb;
733 FILE *scripth, *cdiffh;
734 gzFile gzh;
735 unsigned int ver, osize;
736 int bytes;
737
738 if (CLAMSTAT(script, &sb) == -1) {
739 mprintf("!script2diff: Can't stat file %s\n", script);
740 return -1;
741 }
742 osize = (unsigned int)sb.st_size;
743
744 cdiff = strdup(script);
745 if (NULL == cdiff) {
746 mprintf("!script2cdiff: Unable to allocate memory for file name\n");
747 return -1;
748 }
749 pt = strstr(cdiff, ".script");
750 if (!pt) {
751 mprintf("!script2cdiff: Incorrect file name (no .script extension)\n");
752 free(cdiff);
753 return -1;
754 }
755 strcpy(pt, ".cdiff");
756
757 if (!(pt = strchr(script, '-'))) {
758 mprintf("!script2cdiff: Incorrect file name syntax\n");
759 free(cdiff);
760 return -1;
761 }
762
763 if (sscanf(++pt, "%u.script", &ver) == EOF) {
764 mprintf("!script2cdiff: Incorrect file name syntax\n");
765 free(cdiff);
766 return -1;
767 }
768
769 if (!(cdiffh = fopen(cdiff, "wb"))) {
770 mprintf("!script2cdiff: Can't open %s for writing\n", cdiff);
771 free(cdiff);
772 return -1;
773 }
774
775 if (fprintf(cdiffh, "ClamAV-Diff:%u:%u:", ver, osize) < 0) {
776 mprintf("!script2cdiff: Can't write to %s\n", cdiff);
777 fclose(cdiffh);
778 free(cdiff);
779 return -1;
780 }
781 fclose(cdiffh);
782
783 if (!(scripth = fopen(script, "rb"))) {
784 mprintf("!script2cdiff: Can't open file %s for reading\n", script);
785 unlink(cdiff);
786 free(cdiff);
787 return -1;
788 }
789
790 if (!(gzh = gzopen(cdiff, "ab9f"))) {
791 mprintf("!script2cdiff: Can't open file %s for appending\n", cdiff);
792 unlink(cdiff);
793 free(cdiff);
794 fclose(scripth);
795 return -1;
796 }
797
798 while ((bytes = fread(buffer, 1, sizeof(buffer), scripth)) > 0) {
799 if (!gzwrite(gzh, buffer, bytes)) {
800 mprintf("!script2cdiff: Can't gzwrite to %s\n", cdiff);
801 unlink(cdiff);
802 free(cdiff);
803 fclose(scripth);
804 gzclose(gzh);
805 return -1;
806 }
807 }
808 fclose(scripth);
809 gzclose(gzh);
810
811 if (!(cdiffh = fopen(cdiff, "rb"))) {
812 mprintf("!script2cdiff: Can't open %s for reading/writing\n", cdiff);
813 unlink(cdiff);
814 free(cdiff);
815 return -1;
816 }
817
818 ctx = cl_hash_init("sha256");
819 if (!(ctx)) {
820 unlink(cdiff);
821 free(cdiff);
822 fclose(cdiffh);
823 return -1;
824 }
825
826 while ((bytes = fread(buffer, 1, sizeof(buffer), cdiffh)))
827 cl_update_hash(ctx, (unsigned char *)buffer, bytes);
828
829 fclose(cdiffh);
830 cl_finish_hash(ctx, digest);
831
832 if (!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 2))) {
833 mprintf("!script2cdiff: Can't get digital signature from remote server\n");
834 unlink(cdiff);
835 free(cdiff);
836 return -1;
837 }
838
839 if (!(cdiffh = fopen(cdiff, "ab"))) {
840 mprintf("!script2cdiff: Can't open %s for appending\n", cdiff);
841 free(pt);
842 unlink(cdiff);
843 free(cdiff);
844 return -1;
845 }
846 fprintf(cdiffh, ":%s", pt);
847 free(pt);
848 fclose(cdiffh);
849
850 mprintf("Created %s\n", cdiff);
851 free(cdiff);
852
853 return 0;
854 }
855
qcompare(const void * a,const void * b)856 static int qcompare(const void *a, const void *b)
857 {
858 return strcmp(*(char *const *)a, *(char *const *)b);
859 }
860
build(const struct optstruct * opts)861 static int build(const struct optstruct *opts)
862 {
863 int ret, bc = 0, hy = 0;
864 size_t bytes;
865 unsigned int i, sigs = 0, oldsigs = 0, entries = 0, version, real_header, fl, maxentries;
866 STATBUF foo;
867 unsigned char buffer[FILEBUFF];
868 char *tarfile, header[513], smbuff[32], builder[32], *pt, olddb[512];
869 char patch[32], broken[32], dbname[32], dbfile[32];
870 const char *newcvd, *localdbdir = NULL;
871 struct cl_engine *engine;
872 FILE *cvd, *fh;
873 gzFile tar;
874 time_t timet;
875 struct tm *brokent;
876 struct cl_cvd *oldcvd;
877 char **dblist2 = NULL;
878 unsigned int dblist2cnt = 0;
879 DIR *dd;
880 struct dirent *dent;
881
882 #define FREE_LS(x) \
883 for (i = 0; i < dblist2cnt; i++) \
884 free(x[i]); \
885 free(x);
886
887 if (!optget(opts, "server")->enabled && !optget(opts, "unsigned")->enabled) {
888 mprintf("!build: --server is required for --build\n");
889 return -1;
890 }
891
892 if (optget(opts, "datadir")->active)
893 localdbdir = optget(opts, "datadir")->strarg;
894
895 if (CLAMSTAT("COPYING", &foo) == -1) {
896 mprintf("!build: COPYING file not found in current working directory.\n");
897 return -1;
898 }
899
900 getdbname(optget(opts, "build")->strarg, dbname, sizeof(dbname));
901 if (!strcmp(dbname, "bytecode"))
902 bc = 1;
903
904 if (optget(opts, "hybrid")->enabled)
905 hy = 1;
906
907 if (!(engine = cl_engine_new())) {
908 mprintf("!build: Can't initialize antivirus engine\n");
909 return 50;
910 }
911
912 if ((ret = cl_load(".", engine, &sigs, CL_DB_STDOPT | CL_DB_PUA | CL_DB_SIGNED))) {
913 mprintf("!build: Can't load database: %s\n", cl_strerror(ret));
914 cl_engine_free(engine);
915 return -1;
916 }
917 cl_engine_free(engine);
918
919 if (!sigs) {
920 mprintf("!build: There are no signatures in database files\n");
921 } else {
922 if (bc || hy) {
923 if ((dd = opendir(".")) == NULL) {
924 mprintf("!build: Can't open current directory\n");
925 return -1;
926 }
927 while ((dent = readdir(dd))) {
928 if (dent->d_ino) {
929 if (cli_strbcasestr(dent->d_name, ".cbc")) {
930 dblist2 = (char **)realloc(dblist2, (dblist2cnt + 1) * sizeof(char *));
931 if (!dblist2) { /* dblist2 leaked but we don't really care */
932 mprintf("!build: Memory allocation error\n");
933 closedir(dd);
934 return -1;
935 }
936 dblist2[dblist2cnt] = strdup(dent->d_name);
937 if (!dblist2[dblist2cnt]) {
938 FREE_LS(dblist2);
939 mprintf("!build: Memory allocation error\n");
940 return -1;
941 }
942 dblist2cnt++;
943 }
944 }
945 }
946 closedir(dd);
947 entries += dblist2cnt;
948 if (dblist2 != NULL) {
949 qsort(dblist2, dblist2cnt, sizeof(char *), qcompare);
950 }
951
952 if (!access("last.hdb", R_OK)) {
953 if (!dblist2cnt) {
954 mprintf("!build: dblist2 == NULL (no .cbc files?)\n");
955 return -1;
956 }
957 dblist2 = (char **)realloc(dblist2, (dblist2cnt + 1) * sizeof(char *));
958 if (!dblist2) {
959 mprintf("!build: Memory allocation error\n");
960 return -1;
961 }
962 dblist2[dblist2cnt] = strdup("last.hdb");
963 if (!dblist2[dblist2cnt]) {
964 FREE_LS(dblist2);
965 mprintf("!build: Memory allocation error\n");
966 return -1;
967 }
968 dblist2cnt++;
969 entries += countlines("last.hdb");
970 }
971 }
972 if (!bc || hy) {
973 for (i = 0; dblist[i].ext; i++) {
974 snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
975 if (dblist[i].count && !access(dbfile, R_OK))
976 entries += countlines(dbfile);
977 }
978 }
979
980 if (entries != sigs)
981 mprintf("^build: Signatures in %s db files: %u, loaded by libclamav: %u\n", dbname, entries, sigs);
982
983 maxentries = optget(opts, "max-bad-sigs")->numarg;
984
985 if (maxentries) {
986 if (!entries || (sigs > entries && sigs - entries >= maxentries)) {
987 mprintf("!Bad number of signatures in database files\n");
988 FREE_LS(dblist2);
989 return -1;
990 }
991 }
992 }
993
994 /* try to read cvd header of current database */
995 if (opts->filename) {
996 if (cli_strbcasestr(opts->filename[0], ".cvd") || cli_strbcasestr(opts->filename[0], ".cld") || cli_strbcasestr(opts->filename[0], ".cud")) {
997 strncpy(olddb, opts->filename[0], sizeof(olddb));
998 olddb[sizeof(olddb) - 1] = '\0';
999 } else {
1000 mprintf("!build: Not a CVD/CLD/CUD file\n");
1001 FREE_LS(dblist2);
1002 return -1;
1003 }
1004
1005 } else {
1006 pt = freshdbdir();
1007 snprintf(olddb, sizeof(olddb), "%s" PATHSEP "%s.cvd", localdbdir ? localdbdir : pt, dbname);
1008 if (access(olddb, R_OK))
1009 snprintf(olddb, sizeof(olddb), "%s" PATHSEP "%s.cld", localdbdir ? localdbdir : pt, dbname);
1010 if (access(olddb, R_OK))
1011 snprintf(olddb, sizeof(olddb), "%s" PATHSEP "%s.cud", localdbdir ? localdbdir : pt, dbname);
1012 free(pt);
1013 }
1014
1015 if (!(oldcvd = cl_cvdhead(olddb)) && !optget(opts, "unsigned")->enabled) {
1016 mprintf("^build: CAN'T READ CVD HEADER OF CURRENT DATABASE %s (wait 3 s)\n", olddb);
1017 sleep(3);
1018 }
1019
1020 if (oldcvd) {
1021 version = oldcvd->version + 1;
1022 oldsigs = oldcvd->sigs;
1023 cl_cvdfree(oldcvd);
1024 } else if (optget(opts, "cvd-version")->numarg != 0) {
1025 version = optget(opts, "cvd-version")->numarg;
1026 } else {
1027 mprintf("Version number: ");
1028 if (scanf("%u", &version) == EOF) {
1029 mprintf("!build: scanf() failed\n");
1030 FREE_LS(dblist2);
1031 return -1;
1032 }
1033 }
1034
1035 mprintf("Total sigs: %u\n", sigs);
1036 if (sigs > oldsigs)
1037 mprintf("New sigs: %u\n", sigs - oldsigs);
1038
1039 strcpy(header, "ClamAV-VDB:");
1040
1041 /* time */
1042 time(&timet);
1043 brokent = localtime(&timet);
1044 setlocale(LC_TIME, "C");
1045 strftime(smbuff, sizeof(smbuff), "%d %b %Y %H-%M %z", brokent);
1046 strcat(header, smbuff);
1047
1048 /* version */
1049 sprintf(header + strlen(header), ":%u:", version);
1050
1051 /* number of signatures */
1052 sprintf(header + strlen(header), "%u:", sigs);
1053
1054 /* functionality level */
1055 fl = (unsigned int)(optget(opts, "flevel")->numarg);
1056 sprintf(header + strlen(header), "%u:", fl);
1057
1058 real_header = strlen(header);
1059
1060 /* add fake MD5 and dsig (for writeinfo) */
1061 strcat(header, "X:X:");
1062
1063 if ((pt = getenv("SIGNDUSER"))) {
1064 strncpy(builder, pt, sizeof(builder));
1065 builder[sizeof(builder) - 1] = '\0';
1066 } else {
1067 mprintf("Builder name: ");
1068 if (scanf("%32s", builder) == EOF) {
1069 mprintf("!build: Can't get builder name\n");
1070 free(dblist2);
1071 return -1;
1072 }
1073 }
1074
1075 /* add builder */
1076 strcat(header, builder);
1077
1078 /* add current time */
1079 sprintf(header + strlen(header), ":%u", (unsigned int)timet);
1080
1081 if (writeinfo(dbname, builder, header, opts, dblist2, dblist2cnt) == -1) {
1082 mprintf("!build: Can't generate info file\n");
1083 FREE_LS(dblist2);
1084 return -1;
1085 }
1086
1087 header[real_header] = 0;
1088
1089 if (!(tarfile = cli_gentemp("."))) {
1090 mprintf("!build: Can't generate temporary name for tarfile\n");
1091 FREE_LS(dblist2);
1092 return -1;
1093 }
1094
1095 if ((tar = gzopen(tarfile, "wb9f")) == NULL) {
1096 mprintf("!build: Can't open file %s for writing\n", tarfile);
1097 free(tarfile);
1098 FREE_LS(dblist2);
1099 return -1;
1100 }
1101
1102 if (tar_addfile(-1, tar, "COPYING") == -1) {
1103 mprintf("!build: Can't add COPYING to tar archive\n");
1104 gzclose(tar);
1105 unlink(tarfile);
1106 free(tarfile);
1107 FREE_LS(dblist2);
1108 return -1;
1109 }
1110
1111 if (bc || hy) {
1112 if (!hy && tar_addfile(-1, tar, "bytecode.info") == -1) {
1113 gzclose(tar);
1114 unlink(tarfile);
1115 free(tarfile);
1116 FREE_LS(dblist2);
1117 return -1;
1118 }
1119 for (i = 0; i < dblist2cnt; i++) {
1120 if (tar_addfile(-1, tar, dblist2[i]) == -1) {
1121 gzclose(tar);
1122 unlink(tarfile);
1123 free(tarfile);
1124 FREE_LS(dblist2);
1125 return -1;
1126 }
1127 }
1128 }
1129 if (!bc || hy) {
1130 for (i = 0; dblist[i].ext; i++) {
1131 snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
1132 if (!access(dbfile, R_OK)) {
1133 if (tar_addfile(-1, tar, dbfile) == -1) {
1134 gzclose(tar);
1135 unlink(tarfile);
1136 free(tarfile);
1137 FREE_LS(dblist2);
1138 return -1;
1139 }
1140 }
1141 }
1142 }
1143 gzclose(tar);
1144 FREE_LS(dblist2);
1145
1146 /* MD5 + dsig */
1147 if (!(fh = fopen(tarfile, "rb"))) {
1148 mprintf("!build: Can't open file %s for reading\n", tarfile);
1149 unlink(tarfile);
1150 free(tarfile);
1151 return -1;
1152 }
1153
1154 if (!(pt = cli_hashstream(fh, buffer, 1))) {
1155 mprintf("!build: Can't generate MD5 checksum for %s\n", tarfile);
1156 fclose(fh);
1157 unlink(tarfile);
1158 free(tarfile);
1159 return -1;
1160 }
1161 rewind(fh);
1162 sprintf(header + strlen(header), "%s:", pt);
1163 free(pt);
1164
1165 if (!optget(opts, "unsigned")->enabled) {
1166 if (!(pt = getdsig(optget(opts, "server")->strarg, builder, buffer, 16, 1))) {
1167 mprintf("!build: Can't get digital signature from remote server\n");
1168 fclose(fh);
1169 unlink(tarfile);
1170 free(tarfile);
1171 return -1;
1172 }
1173 sprintf(header + strlen(header), "%s:", pt);
1174 free(pt);
1175 } else {
1176 sprintf(header + strlen(header), "X:");
1177 }
1178
1179 /* add builder */
1180 strcat(header, builder);
1181
1182 /* add current time */
1183 sprintf(header + strlen(header), ":%u", (unsigned int)timet);
1184
1185 /* fill up with spaces */
1186 while (strlen(header) < sizeof(header) - 1)
1187 strcat(header, " ");
1188
1189 /* build the final database */
1190 newcvd = optget(opts, "build")->strarg;
1191 if (!(cvd = fopen(newcvd, "wb"))) {
1192 mprintf("!build: Can't create final database %s\n", newcvd);
1193 fclose(fh);
1194 unlink(tarfile);
1195 free(tarfile);
1196 return -1;
1197 }
1198
1199 if (fwrite(header, 1, 512, cvd) != 512) {
1200 mprintf("!build: Can't write to %s\n", newcvd);
1201 fclose(fh);
1202 unlink(tarfile);
1203 free(tarfile);
1204 fclose(cvd);
1205 unlink(newcvd);
1206 return -1;
1207 }
1208
1209 while ((bytes = fread(buffer, 1, FILEBUFF, fh)) > 0) {
1210 if (fwrite(buffer, 1, bytes, cvd) != bytes) {
1211 mprintf("!build: Can't write to %s\n", newcvd);
1212 fclose(fh);
1213 unlink(tarfile);
1214 free(tarfile);
1215 fclose(cvd);
1216 unlink(newcvd);
1217 return -1;
1218 }
1219 }
1220
1221 fclose(fh);
1222 fclose(cvd);
1223
1224 if (unlink(tarfile) == -1) {
1225 mprintf("^build: Can't unlink %s\n", tarfile);
1226 unlink(tarfile);
1227 free(tarfile);
1228 unlink(newcvd);
1229 return -1;
1230 }
1231 free(tarfile);
1232
1233 mprintf("Created %s\n", newcvd);
1234
1235 if (optget(opts, "unsigned")->enabled)
1236 return 0;
1237
1238 if (!oldcvd || optget(opts, "no-cdiff")->enabled) {
1239 mprintf("Skipping .cdiff creation\n");
1240 return 0;
1241 }
1242
1243 /* generate patch */
1244 if (!(pt = cli_gentemp(NULL))) {
1245 mprintf("!build: Can't generate temporary name\n");
1246 unlink(newcvd);
1247 return -1;
1248 }
1249
1250 if (mkdir(pt, 0700)) {
1251 mprintf("!build: Can't create temporary directory %s\n", pt);
1252 free(pt);
1253 unlink(newcvd);
1254 return -1;
1255 }
1256
1257 if (cli_cvdunpack(olddb, pt) == -1) {
1258 mprintf("!build: Can't unpack CVD file %s\n", olddb);
1259 cli_rmdirs(pt);
1260 free(pt);
1261 unlink(newcvd);
1262 return -1;
1263 }
1264 strncpy(olddb, pt, sizeof(olddb));
1265 olddb[sizeof(olddb) - 1] = '\0';
1266 free(pt);
1267
1268 if (!(pt = cli_gentemp(NULL))) {
1269 mprintf("!build: Can't generate temporary name\n");
1270 cli_rmdirs(olddb);
1271 unlink(newcvd);
1272 return -1;
1273 }
1274
1275 if (mkdir(pt, 0700)) {
1276 mprintf("!build: Can't create temporary directory %s\n", pt);
1277 free(pt);
1278 cli_rmdirs(olddb);
1279 unlink(newcvd);
1280 return -1;
1281 }
1282
1283 if (cli_cvdunpack(newcvd, pt) == -1) {
1284 mprintf("!build: Can't unpack CVD file %s\n", newcvd);
1285 cli_rmdirs(pt);
1286 free(pt);
1287 cli_rmdirs(olddb);
1288 unlink(newcvd);
1289 return -1;
1290 }
1291
1292 snprintf(patch, sizeof(patch), "%s-%u.script", dbname, version);
1293 ret = diffdirs(olddb, pt, patch);
1294
1295 cli_rmdirs(pt);
1296 free(pt);
1297
1298 if (ret == -1) {
1299 cli_rmdirs(olddb);
1300 unlink(newcvd);
1301 return -1;
1302 }
1303
1304 ret = verifydiff(patch, NULL, olddb);
1305 cli_rmdirs(olddb);
1306
1307 if (ret == -1) {
1308 snprintf(broken, sizeof(broken), "%s.broken", patch);
1309 if (rename(patch, broken)) {
1310 unlink(patch);
1311 mprintf("!Generated file is incorrect, removed");
1312 } else {
1313 mprintf("!Generated file is incorrect, renamed to %s\n", broken);
1314 }
1315 } else {
1316 ret = script2cdiff(patch, builder, opts);
1317 }
1318
1319 return ret;
1320 }
1321
unpack(const struct optstruct * opts)1322 static int unpack(const struct optstruct *opts)
1323 {
1324 char name[512], *dbdir;
1325 const char *localdbdir = NULL;
1326
1327 if (optget(opts, "datadir")->active)
1328 localdbdir = optget(opts, "datadir")->strarg;
1329
1330 if (optget(opts, "unpack-current")->enabled) {
1331 dbdir = freshdbdir();
1332 snprintf(name, sizeof(name), "%s" PATHSEP "%s.cvd", localdbdir ? localdbdir : dbdir, optget(opts, "unpack-current")->strarg);
1333 if (access(name, R_OK)) {
1334 snprintf(name, sizeof(name), "%s" PATHSEP "%s.cld", localdbdir ? localdbdir : dbdir, optget(opts, "unpack-current")->strarg);
1335 if (access(name, R_OK)) {
1336 mprintf("!unpack: Couldn't find %s CLD/CVD database in %s\n", optget(opts, "unpack-current")->strarg, localdbdir ? localdbdir : dbdir);
1337 free(dbdir);
1338 return -1;
1339 }
1340 }
1341 free(dbdir);
1342
1343 } else {
1344 strncpy(name, optget(opts, "unpack")->strarg, sizeof(name));
1345 name[sizeof(name) - 1] = '\0';
1346 }
1347
1348 if (cl_cvdverify(name) != CL_SUCCESS) {
1349 mprintf("!unpack: %s is not a valid CVD\n", name);
1350 return -1;
1351 }
1352
1353 if (cli_cvdunpack(name, ".") == -1) {
1354 mprintf("!unpack: Can't unpack file %s\n", name);
1355 return -1;
1356 }
1357
1358 return 0;
1359 }
1360
cvdinfo(const struct optstruct * opts)1361 static int cvdinfo(const struct optstruct *opts)
1362 {
1363 struct cl_cvd *cvd;
1364 char *pt;
1365 int ret;
1366
1367 pt = optget(opts, "info")->strarg;
1368 if ((cvd = cl_cvdhead(pt)) == NULL) {
1369 mprintf("!cvdinfo: Can't read/parse CVD header of %s\n", pt);
1370 return -1;
1371 }
1372 mprintf("File: %s\n", pt);
1373
1374 pt = strchr(cvd->time, '-');
1375 if (!pt) {
1376 cl_cvdfree(cvd);
1377 return -1;
1378 }
1379 *pt = ':';
1380 mprintf("Build time: %s\n", cvd->time);
1381 mprintf("Version: %u\n", cvd->version);
1382 mprintf("Signatures: %u\n", cvd->sigs);
1383 mprintf("Functionality level: %u\n", cvd->fl);
1384 mprintf("Builder: %s\n", cvd->builder);
1385
1386 pt = optget(opts, "info")->strarg;
1387 if (cli_strbcasestr(pt, ".cvd")) {
1388 mprintf("MD5: %s\n", cvd->md5);
1389 mprintf("Digital signature: %s\n", cvd->dsig);
1390 }
1391 cl_cvdfree(cvd);
1392 if (cli_strbcasestr(pt, ".cud"))
1393 mprintf("Verification: Unsigned container\n");
1394 else if ((ret = cl_cvdverify(pt))) {
1395 mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret));
1396 return -1;
1397 } else
1398 mprintf("Verification OK.\n");
1399
1400 return 0;
1401 }
1402
1403 static int listdb(const char *filename, const regex_t *regex);
1404
listdir(const char * dirname,const regex_t * regex)1405 static int listdir(const char *dirname, const regex_t *regex)
1406 {
1407 DIR *dd;
1408 struct dirent *dent;
1409 char *dbfile;
1410
1411 if ((dd = opendir(dirname)) == NULL) {
1412 mprintf("!listdir: Can't open directory %s\n", dirname);
1413 return -1;
1414 }
1415
1416 while ((dent = readdir(dd))) {
1417 if (dent->d_ino) {
1418 if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
1419 (cli_strbcasestr(dent->d_name, ".db") ||
1420 cli_strbcasestr(dent->d_name, ".hdb") ||
1421 cli_strbcasestr(dent->d_name, ".hdu") ||
1422 cli_strbcasestr(dent->d_name, ".hsb") ||
1423 cli_strbcasestr(dent->d_name, ".hsu") ||
1424 cli_strbcasestr(dent->d_name, ".mdb") ||
1425 cli_strbcasestr(dent->d_name, ".mdu") ||
1426 cli_strbcasestr(dent->d_name, ".msb") ||
1427 cli_strbcasestr(dent->d_name, ".msu") ||
1428 cli_strbcasestr(dent->d_name, ".ndb") ||
1429 cli_strbcasestr(dent->d_name, ".ndu") ||
1430 cli_strbcasestr(dent->d_name, ".ldb") ||
1431 cli_strbcasestr(dent->d_name, ".ldu") ||
1432 cli_strbcasestr(dent->d_name, ".sdb") ||
1433 cli_strbcasestr(dent->d_name, ".zmd") ||
1434 cli_strbcasestr(dent->d_name, ".rmd") ||
1435 cli_strbcasestr(dent->d_name, ".cdb") ||
1436 cli_strbcasestr(dent->d_name, ".cbc") ||
1437 cli_strbcasestr(dent->d_name, ".cld") ||
1438 cli_strbcasestr(dent->d_name, ".cvd") ||
1439 cli_strbcasestr(dent->d_name, ".crb") ||
1440 cli_strbcasestr(dent->d_name, ".imp"))) {
1441
1442 dbfile = (char *)malloc(strlen(dent->d_name) + strlen(dirname) + 2);
1443 if (!dbfile) {
1444 mprintf("!listdir: Can't allocate memory for dbfile\n");
1445 closedir(dd);
1446 return -1;
1447 }
1448 sprintf(dbfile, "%s" PATHSEP "%s", dirname, dent->d_name);
1449
1450 if (listdb(dbfile, regex) == -1) {
1451 mprintf("!listdb: Error listing database %s\n", dbfile);
1452 free(dbfile);
1453 closedir(dd);
1454 return -1;
1455 }
1456 free(dbfile);
1457 }
1458 }
1459 }
1460
1461 closedir(dd);
1462 return 0;
1463 }
1464
listdb(const char * filename,const regex_t * regex)1465 static int listdb(const char *filename, const regex_t *regex)
1466 {
1467 FILE *fh;
1468 char *buffer, *pt, *start, *dir;
1469 const char *dbname, *pathsep = PATHSEP;
1470 unsigned int line = 0;
1471
1472 if ((fh = fopen(filename, "rb")) == NULL) {
1473 mprintf("!listdb: Can't open file %s\n", filename);
1474 return -1;
1475 }
1476
1477 if (!(buffer = (char *)malloc(CLI_DEFAULT_LSIG_BUFSIZE + 1))) {
1478 mprintf("!listdb: Can't allocate memory for buffer\n");
1479 fclose(fh);
1480 return -1;
1481 }
1482
1483 /* check for CVD file */
1484 if (!fgets(buffer, 12, fh)) {
1485 mprintf("!listdb: fgets failed\n");
1486 free(buffer);
1487 fclose(fh);
1488 return -1;
1489 }
1490 rewind(fh);
1491
1492 if (!strncmp(buffer, "ClamAV-VDB:", 11)) {
1493 free(buffer);
1494 fclose(fh);
1495
1496 if (!(dir = cli_gentemp(NULL))) {
1497 mprintf("!listdb: Can't generate temporary name\n");
1498 return -1;
1499 }
1500
1501 if (mkdir(dir, 0700)) {
1502 mprintf("!listdb: Can't create temporary directory %s\n", dir);
1503 free(dir);
1504 return -1;
1505 }
1506
1507 if (cli_cvdunpack(filename, dir) == -1) {
1508 mprintf("!listdb: Can't unpack CVD file %s\n", filename);
1509 cli_rmdirs(dir);
1510 free(dir);
1511 return -1;
1512 }
1513
1514 /* list extracted directory */
1515 if (listdir(dir, regex) == -1) {
1516 mprintf("!listdb: Can't list directory %s\n", filename);
1517 cli_rmdirs(dir);
1518 free(dir);
1519 return -1;
1520 }
1521
1522 cli_rmdirs(dir);
1523 free(dir);
1524
1525 return 0;
1526 }
1527
1528 if (!(dbname = strrchr(filename, *pathsep))) {
1529 mprintf("!listdb: Invalid filename %s\n", filename);
1530 fclose(fh);
1531 free(buffer);
1532 return -1;
1533 }
1534 dbname++;
1535
1536 if (cli_strbcasestr(filename, ".db")) { /* old style database */
1537
1538 while (fgets(buffer, CLI_DEFAULT_LSIG_BUFSIZE, fh)) {
1539 if (regex) {
1540 cli_chomp(buffer);
1541 if (!cli_regexec(regex, buffer, 0, NULL, 0))
1542 mprintf("[%s] %s\n", dbname, buffer);
1543 continue;
1544 }
1545 line++;
1546
1547 if (buffer && buffer[0] == '#')
1548 continue;
1549
1550 pt = strchr(buffer, '=');
1551 if (!pt) {
1552 mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
1553 fclose(fh);
1554 free(buffer);
1555 return -1;
1556 }
1557
1558 start = buffer;
1559 *pt = 0;
1560
1561 if ((pt = strstr(start, " (Clam)")))
1562 *pt = 0;
1563
1564 mprintf("%s\n", start);
1565 }
1566
1567 } else if (cli_strbcasestr(filename, ".crb")) {
1568 while (fgets(buffer, CLI_DEFAULT_LSIG_BUFSIZE, fh)) {
1569 cli_chomp(buffer);
1570
1571 if (buffer[0] == '#')
1572 continue;
1573
1574 if (regex) {
1575 if (!cli_regexec(regex, buffer, 0, NULL, 0))
1576 mprintf("[%s] %s\n", dbname, buffer);
1577
1578 continue;
1579 }
1580 line++;
1581 mprintf("%s\n", buffer);
1582 }
1583 } else if (cli_strbcasestr(filename, ".hdb") || cli_strbcasestr(filename, ".hdu") || cli_strbcasestr(filename, ".mdb") || cli_strbcasestr(filename, ".mdu") || cli_strbcasestr(filename, ".hsb") || cli_strbcasestr(filename, ".hsu") || cli_strbcasestr(filename, ".msb") || cli_strbcasestr(filename, ".msu") || cli_strbcasestr(filename, ".imp")) { /* hash database */
1584
1585 while (fgets(buffer, CLI_DEFAULT_LSIG_BUFSIZE, fh)) {
1586 cli_chomp(buffer);
1587 if (regex) {
1588 if (!cli_regexec(regex, buffer, 0, NULL, 0))
1589 mprintf("[%s] %s\n", dbname, buffer);
1590 continue;
1591 }
1592 line++;
1593
1594 if (buffer && buffer[0] == '#')
1595 continue;
1596
1597 start = cli_strtok(buffer, 2, ":");
1598
1599 if (!start) {
1600 mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
1601 fclose(fh);
1602 free(buffer);
1603 return -1;
1604 }
1605
1606 if ((pt = strstr(start, " (Clam)")))
1607 *pt = 0;
1608
1609 mprintf("%s\n", start);
1610 free(start);
1611 }
1612
1613 } else if (cli_strbcasestr(filename, ".ndb") || cli_strbcasestr(filename, ".ndu") || cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu") || cli_strbcasestr(filename, ".sdb") || cli_strbcasestr(filename, ".zmd") || cli_strbcasestr(filename, ".rmd") || cli_strbcasestr(filename, ".cdb")) {
1614
1615 while (fgets(buffer, CLI_DEFAULT_LSIG_BUFSIZE, fh)) {
1616 cli_chomp(buffer);
1617 if (regex) {
1618 if (!cli_regexec(regex, buffer, 0, NULL, 0))
1619 mprintf("[%s] %s\n", dbname, buffer);
1620 continue;
1621 }
1622 line++;
1623
1624 if (buffer && buffer[0] == '#')
1625 continue;
1626
1627 if (cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu"))
1628 pt = strchr(buffer, ';');
1629 else
1630 pt = strchr(buffer, ':');
1631
1632 if (!pt) {
1633 mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
1634 fclose(fh);
1635 free(buffer);
1636 return -1;
1637 }
1638 *pt = 0;
1639
1640 if ((pt = strstr(buffer, " (Clam)")))
1641 *pt = 0;
1642
1643 mprintf("%s\n", buffer);
1644 }
1645
1646 } else if (cli_strbcasestr(filename, ".cbc")) {
1647 if (fgets(buffer, CLI_DEFAULT_LSIG_BUFSIZE, fh) && fgets(buffer, CLI_DEFAULT_LSIG_BUFSIZE, fh)) {
1648 pt = strchr(buffer, ';');
1649 if (!pt) { /* not a real sig */
1650 fclose(fh);
1651 free(buffer);
1652 return 0;
1653 }
1654 if (regex) {
1655 if (!cli_regexec(regex, buffer, 0, NULL, 0)) {
1656 mprintf("[%s BYTECODE] %s", dbname, buffer);
1657 }
1658 } else {
1659 *pt = 0;
1660 mprintf("%s\n", buffer);
1661 }
1662 }
1663 }
1664 fclose(fh);
1665 free(buffer);
1666 return 0;
1667 }
1668
listsigs(const struct optstruct * opts,int mode)1669 static int listsigs(const struct optstruct *opts, int mode)
1670 {
1671 int ret;
1672 const char *name;
1673 char *dbdir;
1674 STATBUF sb;
1675 regex_t reg;
1676 const char *localdbdir = NULL;
1677
1678 if (optget(opts, "datadir")->active)
1679 localdbdir = optget(opts, "datadir")->strarg;
1680
1681 if (mode == 0) {
1682 name = optget(opts, "list-sigs")->strarg;
1683 if (access(name, R_OK) && localdbdir)
1684 name = localdbdir;
1685 if (CLAMSTAT(name, &sb) == -1) {
1686 mprintf("--list-sigs: Can't get status of %s\n", name);
1687 return -1;
1688 }
1689
1690 mprintf_stdout = 1;
1691 if (S_ISDIR(sb.st_mode)) {
1692 if (!strcmp(name, DATADIR)) {
1693 dbdir = freshdbdir();
1694 ret = listdir(localdbdir ? localdbdir : dbdir, NULL);
1695 free(dbdir);
1696 } else {
1697 ret = listdir(name, NULL);
1698 }
1699 } else {
1700 ret = listdb(name, NULL);
1701 }
1702
1703 } else {
1704 if (cli_regcomp(®, optget(opts, "find-sigs")->strarg, REG_EXTENDED | REG_NOSUB) != 0) {
1705 mprintf("--find-sigs: Can't compile regex\n");
1706 return -1;
1707 }
1708 mprintf_stdout = 1;
1709 dbdir = freshdbdir();
1710 ret = listdir(localdbdir ? localdbdir : dbdir, ®);
1711 free(dbdir);
1712 cli_regfree(®);
1713 }
1714
1715 return ret;
1716 }
1717
vbadump(const struct optstruct * opts)1718 static int vbadump(const struct optstruct *opts)
1719 {
1720 int fd, hex_output;
1721 char *dir;
1722 const char *pt;
1723 struct uniq *files = NULL;
1724 cli_ctx *ctx;
1725 int has_vba = 0, has_xlm = 0;
1726
1727 if (optget(opts, "vba-hex")->enabled) {
1728 hex_output = 1;
1729 pt = optget(opts, "vba-hex")->strarg;
1730 } else {
1731 hex_output = 0;
1732 pt = optget(opts, "vba")->strarg;
1733 }
1734
1735 if ((fd = open(pt, O_RDONLY | O_BINARY)) == -1) {
1736 mprintf("!vbadump: Can't open file %s\n", pt);
1737 return -1;
1738 }
1739
1740 /* generate the temporary directory */
1741 if (!(dir = cli_gentemp(NULL))) {
1742 mprintf("!vbadump: Can't generate temporary name\n");
1743 close(fd);
1744 return -1;
1745 }
1746
1747 if (mkdir(dir, 0700)) {
1748 mprintf("!vbadump: Can't create temporary directory %s\n", dir);
1749 free(dir);
1750 close(fd);
1751 return -1;
1752 }
1753 if (!(ctx = convenience_ctx(fd))) {
1754 close(fd);
1755 free(dir);
1756 return -1;
1757 }
1758 if (cli_ole2_extract(dir, ctx, &files, &has_vba, &has_xlm, NULL)) {
1759 destroy_ctx(ctx);
1760 cli_rmdirs(dir);
1761 free(dir);
1762 close(fd);
1763 return -1;
1764 }
1765 destroy_ctx(ctx);
1766 if (has_vba && files)
1767 sigtool_vba_scandir(dir, hex_output, files);
1768 cli_rmdirs(dir);
1769 free(dir);
1770 close(fd);
1771 return 0;
1772 }
1773
comparesha(const char * diff)1774 static int comparesha(const char *diff)
1775 {
1776 char info[32], buff[FILEBUFF], *sha, *pt, *name;
1777 const char *tokens[3];
1778 FILE *fh;
1779 int ret = 0, tokens_count;
1780
1781 name = strdup(diff);
1782 if (!name) {
1783 mprintf("!verifydiff: strdup() failed\n");
1784 return -1;
1785 }
1786 if (!(pt = strrchr(name, '-')) || !isdigit(pt[1])) {
1787 mprintf("!verifydiff: Invalid diff name\n");
1788 free(name);
1789 return -1;
1790 }
1791 *pt = 0;
1792 if ((pt = strrchr(name, *PATHSEP)))
1793 pt++;
1794 else
1795 pt = name;
1796
1797 snprintf(info, sizeof(info), "%s.info", pt);
1798 free(name);
1799
1800 if (!(fh = fopen(info, "rb"))) {
1801 mprintf("!verifydiff: Can't open %s\n", info);
1802 return -1;
1803 }
1804
1805 if (!fgets(buff, sizeof(buff), fh) || strncmp(buff, "ClamAV-VDB", 10)) {
1806 mprintf("!verifydiff: Incorrect info file %s\n", info);
1807 fclose(fh);
1808 return -1;
1809 }
1810
1811 while (fgets(buff, sizeof(buff), fh)) {
1812 cli_chomp(buff);
1813 tokens_count = cli_strtokenize(buff, ':', 3, tokens);
1814 if (tokens_count != 3) {
1815 if (!strcmp(tokens[0], "DSIG"))
1816 continue;
1817 mprintf("!verifydiff: Incorrect format of %s\n", info);
1818 ret = -1;
1819 break;
1820 }
1821 if (!(sha = sha256file(tokens[0], NULL))) {
1822 mprintf("!verifydiff: Can't generate SHA256 for %s\n", buff);
1823 ret = -1;
1824 break;
1825 }
1826 if (strcmp(sha, tokens[2])) {
1827 mprintf("!verifydiff: %s has incorrect checksum\n", buff);
1828 ret = -1;
1829 free(sha);
1830 break;
1831 }
1832 free(sha);
1833 }
1834
1835 fclose(fh);
1836 return ret;
1837 }
1838
rundiff(const struct optstruct * opts)1839 static int rundiff(const struct optstruct *opts)
1840 {
1841 int fd, ret;
1842 unsigned short mode;
1843 const char *diff;
1844
1845 diff = optget(opts, "run-cdiff")->strarg;
1846 if (strstr(diff, ".cdiff")) {
1847 mode = 1;
1848 } else if (strstr(diff, ".script")) {
1849 mode = 0;
1850 } else {
1851 mprintf("!rundiff: Incorrect file name (no .cdiff/.script extension)\n");
1852 return -1;
1853 }
1854
1855 if ((fd = open(diff, O_RDONLY | O_BINARY)) == -1) {
1856 mprintf("!rundiff: Can't open file %s\n", diff);
1857 return -1;
1858 }
1859
1860 ret = cdiff_apply(fd, mode);
1861 close(fd);
1862
1863 if (!ret)
1864 ret = comparesha(diff);
1865
1866 return ret;
1867 }
1868
maxlinelen(const char * file)1869 static int maxlinelen(const char *file)
1870 {
1871 int fd, bytes, n = 0, nmax = 0, i;
1872 char buff[512];
1873
1874 if ((fd = open(file, O_RDONLY | O_BINARY)) == -1) {
1875 mprintf("!maxlinelen: Can't open file %s\n", file);
1876 return -1;
1877 }
1878
1879 while ((bytes = read(fd, buff, 512)) > 0) {
1880 for (i = 0; i < bytes; i++, ++n) {
1881 if (buff[i] == '\n') {
1882 if (n > nmax)
1883 nmax = n;
1884 n = 0;
1885 }
1886 }
1887 }
1888
1889 if (bytes == -1) {
1890 mprintf("!maxlinelen: Can't read file %s\n", file);
1891 close(fd);
1892 return -1;
1893 }
1894
1895 close(fd);
1896 return nmax + 1;
1897 }
1898
compare(const char * oldpath,const char * newpath,FILE * diff)1899 static int compare(const char *oldpath, const char *newpath, FILE *diff)
1900 {
1901 FILE *old, *new;
1902 char *obuff, *nbuff, *tbuff, *pt, *omd5, *nmd5;
1903 unsigned int oline = 0, tline, found, i, badxchg = 0;
1904 int l1 = 0, l2;
1905 long opos;
1906
1907 if (!access(oldpath, R_OK) && (omd5 = cli_hashfile(oldpath, 1))) {
1908 if (!(nmd5 = cli_hashfile(newpath, 1))) {
1909 mprintf("!compare: Can't get MD5 checksum of %s\n", newpath);
1910 free(omd5);
1911 return -1;
1912 }
1913 if (!strcmp(omd5, nmd5)) {
1914 free(omd5);
1915 free(nmd5);
1916 return 0;
1917 }
1918 free(omd5);
1919 free(nmd5);
1920 l1 = maxlinelen(oldpath);
1921 }
1922
1923 l2 = maxlinelen(newpath);
1924 if (l1 == -1 || l2 == -1)
1925 return -1;
1926 l1 = MAX(l1, l2) + 1;
1927
1928 obuff = malloc(l1);
1929 if (!obuff) {
1930 mprintf("!compare: Can't allocate memory for 'obuff'\n");
1931 return -1;
1932 }
1933 nbuff = malloc(l1);
1934 if (!nbuff) {
1935 mprintf("!compare: Can't allocate memory for 'nbuff'\n");
1936 free(obuff);
1937 return -1;
1938 }
1939 tbuff = malloc(l1);
1940 if (!tbuff) {
1941 mprintf("!compare: Can't allocate memory for 'tbuff'\n");
1942 free(obuff);
1943 free(nbuff);
1944 return -1;
1945 }
1946
1947 if (l1 > CLI_DEFAULT_LSIG_BUFSIZE)
1948 fprintf(diff, "#LSIZE %u\n", l1 + 32);
1949
1950 fprintf(diff, "OPEN %s\n", newpath);
1951
1952 if (!(new = fopen(newpath, "rb"))) {
1953 mprintf("!compare: Can't open file %s for reading\n", newpath);
1954 free(obuff);
1955 free(nbuff);
1956 free(tbuff);
1957 return -1;
1958 }
1959 old = fopen(oldpath, "rb");
1960
1961 while (fgets(nbuff, l1, new)) {
1962 i = strlen(nbuff);
1963 if (i >= 2 && (nbuff[i - 1] == '\r' || (nbuff[i - 1] == '\n' && nbuff[i - 2] == '\r'))) {
1964 mprintf("!compare: New %s file contains lines terminated with CRLF or CR\n", newpath);
1965 if (old)
1966 fclose(old);
1967 fclose(new);
1968 free(obuff);
1969 free(nbuff);
1970 free(tbuff);
1971 return -1;
1972 }
1973 cli_chomp(nbuff);
1974 if (!old) {
1975 fprintf(diff, "ADD %s\n", nbuff);
1976 } else {
1977 if (fgets(obuff, l1, old)) {
1978 oline++;
1979 cli_chomp(obuff);
1980 if (!strcmp(nbuff, obuff)) {
1981 continue;
1982 } else {
1983 tline = 0;
1984 found = 0;
1985 opos = ftell(old);
1986 while (fgets(tbuff, l1, old)) {
1987 tline++;
1988 cli_chomp(tbuff);
1989
1990 if (tline > MAX_DEL_LOOKAHEAD)
1991 break;
1992
1993 if (!strcmp(tbuff, nbuff)) {
1994 found = 1;
1995 break;
1996 }
1997 }
1998 fseek(old, opos, SEEK_SET);
1999
2000 if (found) {
2001 strncpy(tbuff, obuff, l1);
2002 tbuff[l1 - 1] = '\0';
2003 for (i = 0; i < tline; i++) {
2004 tbuff[MIN(16, l1 - 1)] = 0;
2005 if ((pt = strchr(tbuff, ' ')))
2006 *pt = 0;
2007 fprintf(diff, "DEL %u %s\n", oline + i, tbuff);
2008 if (!fgets(tbuff, l1, old))
2009 break;
2010 }
2011 oline += tline;
2012
2013 } else {
2014 if (!*obuff || *obuff == ' ') {
2015 badxchg = 1;
2016 break;
2017 }
2018 obuff[MIN(16, l1 - 1)] = 0;
2019 if ((pt = strchr(obuff, ' ')))
2020 *pt = 0;
2021 fprintf(diff, "XCHG %u %s %s\n", oline, obuff, nbuff);
2022 }
2023 }
2024 } else {
2025 fclose(old);
2026 old = NULL;
2027 fprintf(diff, "ADD %s\n", nbuff);
2028 }
2029 }
2030 }
2031
2032 if (old) {
2033 if (!badxchg) {
2034 while (fgets(obuff, l1, old)) {
2035 oline++;
2036 cli_chomp(obuff);
2037 obuff[MIN(16, l1 - 1)] = 0;
2038 if ((pt = strchr(obuff, ' ')))
2039 *pt = 0;
2040 fprintf(diff, "DEL %u %s\n", oline, obuff);
2041 }
2042 }
2043 fclose(old);
2044 }
2045 fprintf(diff, "CLOSE\n");
2046 free(obuff);
2047 free(tbuff);
2048 if (badxchg) {
2049 fprintf(diff, "UNLINK %s\n", newpath);
2050 fprintf(diff, "OPEN %s\n", newpath);
2051 rewind(new);
2052 while (fgets(nbuff, l1, new)) {
2053 cli_chomp(nbuff);
2054 fprintf(diff, "ADD %s\n", nbuff);
2055 }
2056 fprintf(diff, "CLOSE\n");
2057 }
2058 free(nbuff);
2059 fclose(new);
2060 return 0;
2061 }
2062
compareone(const struct optstruct * opts)2063 static int compareone(const struct optstruct *opts)
2064 {
2065 if (!opts->filename) {
2066 mprintf("!makediff: --compare requires two arguments\n");
2067 return -1;
2068 }
2069 return compare(optget(opts, "compare")->strarg, opts->filename[0], stdout);
2070 }
2071
dircopy(const char * src,const char * dest)2072 static int dircopy(const char *src, const char *dest)
2073 {
2074 DIR *dd;
2075 struct dirent *dent;
2076 STATBUF sb;
2077 char spath[512], dpath[512];
2078
2079 if (CLAMSTAT(dest, &sb) == -1) {
2080 if (mkdir(dest, 0755)) {
2081 /* mprintf("!dircopy: Can't create temporary directory %s\n", dest); */
2082 return -1;
2083 }
2084 }
2085
2086 if ((dd = opendir(src)) == NULL) {
2087 /* mprintf("!dircopy: Can't open directory %s\n", src); */
2088 return -1;
2089 }
2090
2091 while ((dent = readdir(dd))) {
2092 if (dent->d_ino) {
2093 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
2094 continue;
2095
2096 snprintf(spath, sizeof(spath), "%s" PATHSEP "%s", src, dent->d_name);
2097 snprintf(dpath, sizeof(dpath), "%s" PATHSEP "%s", dest, dent->d_name);
2098
2099 if (filecopy(spath, dpath) == -1) {
2100 /* mprintf("!dircopy: Can't copy %s to %s\n", spath, dpath); */
2101 cli_rmdirs(dest);
2102 closedir(dd);
2103 return -1;
2104 }
2105 }
2106 }
2107
2108 closedir(dd);
2109 return 0;
2110 }
2111
verifydiff(const char * diff,const char * cvd,const char * incdir)2112 static int verifydiff(const char *diff, const char *cvd, const char *incdir)
2113 {
2114 char *tempdir, cwd[512];
2115 int ret = 0, fd;
2116 unsigned short mode;
2117
2118 if (strstr(diff, ".cdiff")) {
2119 mode = 1;
2120 } else if (strstr(diff, ".script")) {
2121 mode = 0;
2122 } else {
2123 mprintf("!verifydiff: Incorrect file name (no .cdiff/.script extension)\n");
2124 return -1;
2125 }
2126
2127 tempdir = cli_gentemp(NULL);
2128 if (!tempdir) {
2129 mprintf("!verifydiff: Can't generate temporary name for tempdir\n");
2130 return -1;
2131 }
2132
2133 if (mkdir(tempdir, 0700) == -1) {
2134 mprintf("!verifydiff: Can't create directory %s\n", tempdir);
2135 free(tempdir);
2136 return -1;
2137 }
2138
2139 if (cvd) {
2140 if (cli_cvdunpack(cvd, tempdir) == -1) {
2141 mprintf("!verifydiff: Can't unpack CVD file %s\n", cvd);
2142 cli_rmdirs(tempdir);
2143 free(tempdir);
2144 return -1;
2145 }
2146 } else {
2147 if (dircopy(incdir, tempdir) == -1) {
2148 mprintf("!verifydiff: Can't copy dir %s to %s\n", incdir, tempdir);
2149 cli_rmdirs(tempdir);
2150 free(tempdir);
2151 return -1;
2152 }
2153 }
2154
2155 if (!getcwd(cwd, sizeof(cwd))) {
2156 mprintf("!verifydiff: getcwd() failed\n");
2157 cli_rmdirs(tempdir);
2158 free(tempdir);
2159 return -1;
2160 }
2161
2162 if ((fd = open(diff, O_RDONLY | O_BINARY)) == -1) {
2163 mprintf("!verifydiff: Can't open diff file %s\n", diff);
2164 cli_rmdirs(tempdir);
2165 free(tempdir);
2166 return -1;
2167 }
2168
2169 if (chdir(tempdir) == -1) {
2170 mprintf("!verifydiff: Can't chdir to %s\n", tempdir);
2171 cli_rmdirs(tempdir);
2172 free(tempdir);
2173 close(fd);
2174 return -1;
2175 }
2176
2177 if (cdiff_apply(fd, mode) == -1) {
2178 mprintf("!verifydiff: Can't apply %s\n", diff);
2179 if (chdir(cwd) == -1)
2180 mprintf("^verifydiff: Can't chdir to %s\n", cwd);
2181 cli_rmdirs(tempdir);
2182 free(tempdir);
2183 close(fd);
2184 return -1;
2185 }
2186 close(fd);
2187
2188 ret = comparesha(diff);
2189
2190 if (chdir(cwd) == -1)
2191 mprintf("^verifydiff: Can't chdir to %s\n", cwd);
2192 cli_rmdirs(tempdir);
2193 free(tempdir);
2194
2195 if (!ret) {
2196 if (cvd)
2197 mprintf("Verification: %s correctly applies to %s\n", diff, cvd);
2198 else
2199 mprintf("Verification: %s correctly applies to the previous version\n", diff);
2200 }
2201
2202 return ret;
2203 }
2204
matchsig(const char * sig,const char * offset,int fd)2205 static void matchsig(const char *sig, const char *offset, int fd)
2206 {
2207 struct cli_ac_result *acres = NULL, *res;
2208 STATBUF sb;
2209 unsigned int matches = 0;
2210 struct cl_engine *engine = NULL;
2211 cli_ctx ctx = {0};
2212 struct cl_scan_options options = {0};
2213 cl_fmap_t *new_map = NULL;
2214
2215 mprintf("SUBSIG: %s\n", sig);
2216
2217 /* Prepare file */
2218 lseek(fd, 0, SEEK_SET);
2219 FSTAT(fd, &sb);
2220
2221 new_map = fmap(fd, 0, sb.st_size, NULL);
2222 if (NULL == new_map) {
2223 goto done;
2224 }
2225
2226 /* build engine */
2227 if (!(engine = cl_engine_new())) {
2228 mprintf("!matchsig: Can't create new engine\n");
2229 goto done;
2230 }
2231 cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
2232
2233 if (cli_initroots(engine, 0) != CL_SUCCESS) {
2234 mprintf("!matchsig: cli_initroots() failed\n");
2235 goto done;
2236 }
2237
2238 if (cli_parse_add(engine->root[0], "test", sig, 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2239 mprintf("!matchsig: Can't parse signature\n");
2240 goto done;
2241 }
2242
2243 if (cl_engine_compile(engine) != CL_SUCCESS) {
2244 mprintf("!matchsig: Can't compile engine\n");
2245 goto done;
2246 }
2247
2248 ctx.engine = engine;
2249 ctx.options = &options;
2250 ctx.options->parse = ~0;
2251 ctx.dconf = (struct cli_dconf *)engine->dconf;
2252
2253 ctx.recursion_stack_size = ctx.engine->max_recursion_level;
2254 ctx.recursion_stack = cli_calloc(sizeof(recursion_level_t), ctx.recursion_stack_size);
2255 if (!ctx.recursion_stack) {
2256 goto done;
2257 }
2258
2259 // ctx was memset, so recursion_level starts at 0.
2260 ctx.recursion_stack[ctx.recursion_level].fmap = new_map;
2261 ctx.recursion_stack[ctx.recursion_level].type = CL_TYPE_ANY; // ANY for the top level, because we don't yet know the type.
2262 ctx.recursion_stack[ctx.recursion_level].size = new_map->len;
2263
2264 ctx.fmap = ctx.recursion_stack[ctx.recursion_level].fmap;
2265
2266 (void)cli_scan_fmap(&ctx, 0, 0, NULL, AC_SCAN_VIR, &acres, NULL);
2267
2268 res = acres;
2269 while (res) {
2270 matches++;
2271 res = res->next;
2272 }
2273
2274 if (matches) {
2275 /* TODO: check offsets automatically */
2276 mprintf("MATCH: ** YES%s ** (%u %s:", offset ? "/CHECK OFFSET" : "", matches, matches > 1 ? "matches at offsets" : "match at offset");
2277 res = acres;
2278 while (res) {
2279 mprintf(" %u", (unsigned int)res->offset);
2280 res = res->next;
2281 }
2282 mprintf(")\n");
2283 } else {
2284 mprintf("MATCH: ** NO **\n");
2285 }
2286
2287 done:
2288 /* Cleanup */
2289 while (acres) {
2290 res = acres;
2291 acres = acres->next;
2292 free(res);
2293 }
2294 if (NULL != new_map) {
2295 funmap(new_map);
2296 }
2297 if (NULL != ctx.recursion_stack) {
2298 free(ctx.recursion_stack);
2299 }
2300 if (NULL != engine) {
2301 cl_engine_free(engine);
2302 }
2303 }
2304
decodehexstr(const char * hex,unsigned int * dlen)2305 static char *decodehexstr(const char *hex, unsigned int *dlen)
2306 {
2307 uint16_t *str16;
2308 char *decoded;
2309 unsigned int i, p = 0, wildcard = 0, len = strlen(hex) / 2;
2310
2311 str16 = cli_hex2ui(hex);
2312 if (!str16)
2313 return NULL;
2314
2315 for (i = 0; i < len; i++)
2316 if (str16[i] & CLI_MATCH_WILDCARD)
2317 wildcard++;
2318
2319 decoded = calloc(len + 1 + wildcard * 32, sizeof(char));
2320 if (!decoded) {
2321 free(str16);
2322 mprintf("!decodehexstr: Can't allocate memory for decoded\n");
2323 return NULL;
2324 }
2325
2326 for (i = 0; i < len; i++) {
2327 if (str16[i] & CLI_MATCH_WILDCARD) {
2328 switch (str16[i] & CLI_MATCH_WILDCARD) {
2329 case CLI_MATCH_IGNORE:
2330 p += sprintf(decoded + p, "{WILDCARD_IGNORE}");
2331 break;
2332
2333 case CLI_MATCH_NIBBLE_HIGH:
2334 p += sprintf(decoded + p, "{WILDCARD_NIBBLE_HIGH:0x%x}", str16[i] & 0x00f0);
2335 break;
2336
2337 case CLI_MATCH_NIBBLE_LOW:
2338 p += sprintf(decoded + p, "{WILDCARD_NIBBLE_LOW:0x%x}", str16[i] & 0x000f);
2339 break;
2340
2341 default:
2342 mprintf("!decodehexstr: Unknown wildcard (0x%x@%u)\n", str16[i] & CLI_MATCH_WILDCARD, i);
2343 free(decoded);
2344 free(str16);
2345 return NULL;
2346 }
2347 } else {
2348 decoded[p] = str16[i];
2349 p++;
2350 }
2351 }
2352
2353 if (dlen)
2354 *dlen = p;
2355 free(str16);
2356 return decoded;
2357 }
2358
get_paren_end(char * hexstr)2359 inline static char *get_paren_end(char *hexstr)
2360 {
2361 char *pt;
2362 int level = 0;
2363
2364 pt = hexstr;
2365 while (*pt != '\0') {
2366 if (*pt == '(') {
2367 level++;
2368 } else if (*pt == ')') {
2369 if (!level)
2370 return pt;
2371 level--;
2372 }
2373 pt++;
2374 }
2375 return NULL;
2376 }
2377
decodehexspecial(const char * hex,unsigned int * dlen)2378 static char *decodehexspecial(const char *hex, unsigned int *dlen)
2379 {
2380 char *pt, *start, *hexcpy, *decoded, *h, *e, *c, op, lop;
2381 unsigned int len = 0, hlen, negative;
2382 int level;
2383 char *buff;
2384
2385 hexcpy = NULL;
2386 buff = NULL;
2387
2388 hexcpy = strdup(hex);
2389 if (!hexcpy) {
2390 mprintf("!decodehexspecial: strdup(hex) failed\n");
2391 return NULL;
2392 }
2393 pt = strchr(hexcpy, '(');
2394 if (!pt) {
2395 free(hexcpy);
2396 return decodehexstr(hex, dlen);
2397 } else {
2398 buff = calloc(strlen(hex) + 512, sizeof(char));
2399 if (!buff) {
2400 mprintf("!decodehexspecial: Can't allocate memory for buff\n");
2401 free(hexcpy);
2402 return NULL;
2403 }
2404 start = hexcpy;
2405 do {
2406 negative = 0;
2407 *pt++ = 0;
2408 if (!start) {
2409 mprintf("!decodehexspecial: Unexpected EOL\n");
2410 free(hexcpy);
2411 free(buff);
2412 return NULL;
2413 }
2414 if (pt >= hexcpy + 2) {
2415 if (pt[-2] == '!') {
2416 negative = 1;
2417 pt[-2] = 0;
2418 }
2419 }
2420 if (!(decoded = decodehexstr(start, &hlen))) {
2421 mprintf("!Decoding failed (1): %s\n", pt);
2422 free(hexcpy);
2423 free(buff);
2424 return NULL;
2425 }
2426 memcpy(&buff[len], decoded, hlen);
2427 len += hlen;
2428 free(decoded);
2429
2430 if (!(start = get_paren_end(pt))) {
2431 mprintf("!decodehexspecial: Missing closing parenthesis\n");
2432 free(hexcpy);
2433 free(buff);
2434 return NULL;
2435 }
2436
2437 *start++ = 0;
2438 if (!strlen(pt)) {
2439 mprintf("!decodehexspecial: Empty block\n");
2440 free(hexcpy);
2441 free(buff);
2442 return NULL;
2443 }
2444
2445 if (!strcmp(pt, "B")) {
2446 if (!*start) {
2447 if (negative)
2448 len += sprintf(buff + len, "{NOT_BOUNDARY_RIGHT}");
2449 else
2450 len += sprintf(buff + len, "{BOUNDARY_RIGHT}");
2451 continue;
2452 } else if (pt - 1 == hexcpy) {
2453 if (negative)
2454 len += sprintf(buff + len, "{NOT_BOUNDARY_LEFT}");
2455 else
2456 len += sprintf(buff + len, "{BOUNDARY_LEFT}");
2457 continue;
2458 }
2459 } else if (!strcmp(pt, "L")) {
2460 if (!*start) {
2461 if (negative)
2462 len += sprintf(buff + len, "{NOT_LINE_MARKER_RIGHT}");
2463 else
2464 len += sprintf(buff + len, "{LINE_MARKER_RIGHT}");
2465 continue;
2466 } else if (pt - 1 == hexcpy) {
2467 if (negative)
2468 len += sprintf(buff + len, "{NOT_LINE_MARKER_LEFT}");
2469 else
2470 len += sprintf(buff + len, "{LINE_MARKER_LEFT}");
2471 continue;
2472 }
2473 } else if (!strcmp(pt, "W")) {
2474 if (!*start) {
2475 if (negative)
2476 len += sprintf(buff + len, "{NOT_WORD_MARKER_RIGHT}");
2477 else
2478 len += sprintf(buff + len, "{WORD_MARKER_RIGHT}");
2479 continue;
2480 } else if (pt - 1 == hexcpy) {
2481 if (negative)
2482 len += sprintf(buff + len, "{NOT_WORD_MARKER_LEFT}");
2483 else
2484 len += sprintf(buff + len, "{WORD_MARKER_LEFT}");
2485 continue;
2486 }
2487 } else {
2488 if (!strlen(pt)) {
2489 mprintf("!decodehexspecial: Empty block\n");
2490 free(hexcpy);
2491 free(buff);
2492 return NULL;
2493 }
2494
2495 /* TODO: analyze string alternative for typing */
2496 if (negative)
2497 len += sprintf(buff + len, "{EXCLUDING_STRING_ALTERNATIVE:");
2498 else
2499 len += sprintf(buff + len, "{STRING_ALTERNATIVE:");
2500
2501 level = 0;
2502 h = e = pt;
2503 op = '\0';
2504 while ((level >= 0) && (e = strpbrk(h, "()|"))) {
2505 lop = op;
2506 op = *e;
2507
2508 *e++ = 0;
2509 if (op != '(' && lop != ')' && !strlen(h)) {
2510 mprintf("!decodehexspecial: Empty string alternative block\n");
2511 free(hexcpy);
2512 free(buff);
2513 return NULL;
2514 }
2515
2516 //mprintf("decodehexspecial: %s\n", h);
2517 if (!(c = cli_hex2str(h))) {
2518 mprintf("!Decoding failed (3): %s\n", h);
2519 free(hexcpy);
2520 free(buff);
2521 return NULL;
2522 }
2523 memcpy(&buff[len], c, strlen(h) / 2);
2524 len += strlen(h) / 2;
2525 free(c);
2526
2527 switch (op) {
2528 case '(':
2529 level++;
2530 negative = 0;
2531 if (e >= pt + 2) {
2532 if (e[-2] == '!') {
2533 negative = 1;
2534 e[-2] = 0;
2535 }
2536 }
2537
2538 if (negative)
2539 len += sprintf(buff + len, "{EXCLUDING_STRING_ALTERNATIVE:");
2540 else
2541 len += sprintf(buff + len, "{STRING_ALTERNATIVE:");
2542
2543 break;
2544 case ')':
2545 level--;
2546 buff[len++] = '}';
2547
2548 break;
2549 case '|':
2550 buff[len++] = '|';
2551
2552 break;
2553 default:;
2554 }
2555
2556 h = e;
2557 }
2558 if (!(c = cli_hex2str(h))) {
2559 mprintf("!Decoding failed (4): %s\n", h);
2560 free(hexcpy);
2561 free(buff);
2562 return NULL;
2563 }
2564 memcpy(&buff[len], c, strlen(h) / 2);
2565 len += strlen(h) / 2;
2566 free(c);
2567
2568 buff[len++] = '}';
2569 if (level != 0) {
2570 mprintf("!decodehexspecial: Invalid string alternative nesting\n");
2571 free(hexcpy);
2572 free(buff);
2573 return NULL;
2574 }
2575 }
2576 } while ((pt = strchr(start, '(')));
2577
2578 if (start) {
2579 if (!(decoded = decodehexstr(start, &hlen))) {
2580 mprintf("!Decoding failed (2)\n");
2581 free(buff);
2582 free(hexcpy);
2583 return NULL;
2584 }
2585 memcpy(&buff[len], decoded, hlen);
2586 len += hlen;
2587 free(decoded);
2588 }
2589 }
2590 free(hexcpy);
2591 if (dlen)
2592 *dlen = len;
2593 return buff;
2594 }
2595
decodehex(const char * hexsig)2596 static int decodehex(const char *hexsig)
2597 {
2598 char *pt, *hexcpy, *start, *n, *decoded, *wild;
2599 int asterisk = 0;
2600 unsigned int i, j, hexlen, dlen, parts = 0, bw;
2601 int mindist = 0, maxdist = 0, error = 0;
2602
2603 hexlen = strlen(hexsig);
2604 if ((wild = strchr(hexsig, '/'))) {
2605 /* ^offset:trigger-logic/regex/options$ */
2606 char *trigger, *regex, *regex_end, *cflags;
2607 size_t tlen = wild - hexsig, rlen, clen;
2608
2609 /* check for trigger */
2610 if (!tlen) {
2611 mprintf("!pcre without logical trigger\n");
2612 return -1;
2613 }
2614
2615 /* locate end of regex for options start, locate options length */
2616 if ((regex_end = strchr(wild + 1, '/')) == NULL) {
2617 mprintf("!missing regex expression terminator /\n");
2618 return -1;
2619 }
2620
2621 /* gotta make sure we treat escaped slashes */
2622 for (i = tlen + 1; i < hexlen; i++) {
2623 if (hexsig[i] == '/' && hexsig[i - 1] != '\\') {
2624 rlen = i - tlen - 1;
2625 break;
2626 }
2627 }
2628 if (i == hexlen) {
2629 mprintf("!missing regex expression terminator /\n");
2630 return -1;
2631 }
2632
2633 clen = hexlen - tlen - rlen - 2; /* 2 from regex boundaries '/' */
2634
2635 /* get the trigger statement */
2636 trigger = cli_calloc(tlen + 1, sizeof(char));
2637 if (!trigger) {
2638 mprintf("!cannot allocate memory for trigger string\n");
2639 return -1;
2640 }
2641 strncpy(trigger, hexsig, tlen);
2642 trigger[tlen] = '\0';
2643
2644 /* get the regex expression */
2645 regex = cli_calloc(rlen + 1, sizeof(char));
2646 if (!regex) {
2647 mprintf("!cannot allocate memory for regex expression\n");
2648 free(trigger);
2649 return -1;
2650 }
2651 strncpy(regex, hexsig + tlen + 1, rlen);
2652 regex[rlen] = '\0';
2653
2654 /* get the compile flags */
2655 if (clen) {
2656 cflags = cli_calloc(clen + 1, sizeof(char));
2657 if (!cflags) {
2658 mprintf("!cannot allocate memory for compile flags\n");
2659 free(trigger);
2660 free(regex);
2661 return -1;
2662 }
2663 strncpy(cflags, hexsig + tlen + rlen + 2, clen);
2664 cflags[clen] = '\0';
2665 } else {
2666 cflags = NULL;
2667 }
2668
2669 /* print components of regex subsig */
2670 mprintf(" +-> TRIGGER: %s\n", trigger);
2671 mprintf(" +-> REGEX: %s\n", regex);
2672 mprintf(" +-> CFLAGS: %s\n", cflags);
2673
2674 free(trigger);
2675 free(regex);
2676 if (cflags)
2677 free(cflags);
2678 #if HAVE_PCRE
2679 return 0;
2680 #else
2681 mprintf("!PCRE subsig cannot be loaded without PCRE support\n");
2682 return -1;
2683 #endif
2684 } else if (strchr(hexsig, '{') || strchr(hexsig, '[')) {
2685 if (!(hexcpy = strdup(hexsig)))
2686 return -1;
2687
2688 for (i = 0; i < hexlen; i++)
2689 if (hexsig[i] == '{' || hexsig[i] == '[' || hexsig[i] == '*')
2690 parts++;
2691
2692 if (parts)
2693 parts++;
2694
2695 start = pt = hexcpy;
2696 for (i = 1; i <= parts; i++) {
2697 if (i != parts) {
2698 for (j = 0; j < strlen(start); j++) {
2699 if (start[j] == '{' || start[j] == '[') {
2700 asterisk = 0;
2701 pt = start + j;
2702 break;
2703 }
2704 if (start[j] == '*') {
2705 asterisk = 1;
2706 pt = start + j;
2707 break;
2708 }
2709 }
2710 *pt++ = 0;
2711 }
2712
2713 if (mindist && maxdist) {
2714 if (mindist == maxdist)
2715 mprintf("{WILDCARD_ANY_STRING(LENGTH==%u)}", mindist);
2716 else
2717 mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u&&<=%u)}", mindist, maxdist);
2718 } else if (mindist)
2719 mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u)}", mindist);
2720 else if (maxdist)
2721 mprintf("{WILDCARD_ANY_STRING(LENGTH<=%u)}", maxdist);
2722
2723 if (!(decoded = decodehexspecial(start, &dlen))) {
2724 mprintf("!Decoding failed\n");
2725 free(hexcpy);
2726 return -1;
2727 }
2728 bw = write(1, decoded, dlen);
2729 free(decoded);
2730
2731 if (i == parts)
2732 break;
2733
2734 if (asterisk)
2735 mprintf("{WILDCARD_ANY_STRING}");
2736
2737 mindist = maxdist = 0;
2738
2739 if (asterisk) {
2740 start = pt;
2741 continue;
2742 }
2743
2744 if (!(start = strchr(pt, '}')) && !(start = strchr(pt, ']'))) {
2745 error = 1;
2746 break;
2747 }
2748 *start++ = 0;
2749
2750 if (!pt) {
2751 error = 1;
2752 break;
2753 }
2754
2755 if (!strchr(pt, '-')) {
2756 if (!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
2757 error = 1;
2758 break;
2759 }
2760 } else {
2761 if ((n = cli_strtok(pt, 0, "-"))) {
2762 if (!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
2763 error = 1;
2764 free(n);
2765 break;
2766 }
2767 free(n);
2768 }
2769
2770 if ((n = cli_strtok(pt, 1, "-"))) {
2771 if (!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
2772 error = 1;
2773 free(n);
2774 break;
2775 }
2776 free(n);
2777 }
2778
2779 if ((n = cli_strtok(pt, 2, "-"))) { /* strict check */
2780 error = 1;
2781 free(n);
2782 break;
2783 }
2784 }
2785 }
2786
2787 free(hexcpy);
2788 if (error)
2789 return -1;
2790
2791 } else if (strchr(hexsig, '*')) {
2792 for (i = 0; i < hexlen; i++)
2793 if (hexsig[i] == '*')
2794 parts++;
2795
2796 if (parts)
2797 parts++;
2798
2799 for (i = 1; i <= parts; i++) {
2800 if ((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
2801 mprintf("!Can't extract part %u of partial signature\n", i);
2802 return -1;
2803 }
2804 if (!(decoded = decodehexspecial(pt, &dlen))) {
2805 mprintf("!Decoding failed\n");
2806 free(pt);
2807 return -1;
2808 }
2809 bw = write(1, decoded, dlen);
2810 free(decoded);
2811 if (i < parts)
2812 mprintf("{WILDCARD_ANY_STRING}");
2813 free(pt);
2814 }
2815
2816 } else {
2817 if (!(decoded = decodehexspecial(hexsig, &dlen))) {
2818 mprintf("!Decoding failed\n");
2819 return -1;
2820 }
2821 bw = write(1, decoded, dlen);
2822 free(decoded);
2823 }
2824
2825 mprintf("\n");
2826 return 0;
2827 }
2828
decodesigmod(const char * sigmod)2829 static int decodesigmod(const char *sigmod)
2830 {
2831 size_t i;
2832
2833 for (i = 0; i < strlen(sigmod); i++) {
2834 mprintf(" ");
2835
2836 switch (sigmod[i]) {
2837 case 'i':
2838 mprintf("NOCASE");
2839 break;
2840 case 'f':
2841 mprintf("FULLWORD");
2842 break;
2843 case 'w':
2844 mprintf("WIDE");
2845 break;
2846 case 'a':
2847 mprintf("ASCII");
2848 break;
2849 default:
2850 mprintf("UNKNOWN");
2851 return -1;
2852 }
2853 }
2854
2855 mprintf("\n");
2856 return 0;
2857 }
2858
decodecdb(char ** tokens)2859 static int decodecdb(char **tokens)
2860 {
2861 int sz = 0;
2862 char *range[2];
2863
2864 if (!tokens)
2865 return -1;
2866
2867 mprintf("VIRUS NAME: %s\n", tokens[0]);
2868 mprintf("CONTAINER TYPE: %s\n", (strcmp(tokens[1], "*") ? tokens[1] : "ANY"));
2869 mprintf("CONTAINER SIZE: ");
2870 if (!cli_isnumber(tokens[2])) {
2871 if (!strcmp(tokens[2], "*")) {
2872 mprintf("ANY\n");
2873
2874 } else if (strchr(tokens[2], '-')) {
2875 sz = cli_strtokenize(tokens[2], '-', 2, (const char **)range);
2876 if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
2877 mprintf("!decodesig: Invalid container size range\n");
2878 return -1;
2879 }
2880 mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
2881
2882 } else {
2883 mprintf("!decodesig: Invalid container size\n");
2884 return -1;
2885 }
2886 } else {
2887 mprintf("%s\n", tokens[2]);
2888 }
2889 mprintf("FILENAME REGEX: %s\n", tokens[3]);
2890 mprintf("COMPRESSED FILESIZE: ");
2891 if (!cli_isnumber(tokens[4])) {
2892 if (!strcmp(tokens[4], "*")) {
2893 mprintf("ANY\n");
2894
2895 } else if (strchr(tokens[4], '-')) {
2896 sz = cli_strtokenize(tokens[4], '-', 2, (const char **)range);
2897 if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
2898 mprintf("!decodesig: Invalid container size range\n");
2899 return -1;
2900 }
2901 mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
2902
2903 } else {
2904 mprintf("!decodesig: Invalid compressed filesize\n");
2905 return -1;
2906 }
2907 } else {
2908 mprintf("%s\n", tokens[4]);
2909 }
2910 mprintf("UNCOMPRESSED FILESIZE: ");
2911 if (!cli_isnumber(tokens[5])) {
2912 if (!strcmp(tokens[5], "*")) {
2913 mprintf("ANY\n");
2914
2915 } else if (strchr(tokens[5], '-')) {
2916 sz = cli_strtokenize(tokens[5], '-', 2, (const char **)range);
2917 if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
2918 mprintf("!decodesig: Invalid container size range\n");
2919 return -1;
2920 }
2921 mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
2922
2923 } else {
2924 mprintf("!decodesig: Invalid uncompressed filesize\n");
2925 return -1;
2926 }
2927 } else {
2928 mprintf("%s\n", tokens[5]);
2929 }
2930
2931 mprintf("ENCRYPTION: ");
2932 if (!cli_isnumber(tokens[6])) {
2933 if (!strcmp(tokens[6], "*")) {
2934 mprintf("IGNORED\n");
2935 } else {
2936 mprintf("!decodesig: Invalid encryption flag\n");
2937 return -1;
2938 }
2939 } else {
2940 mprintf("%s\n", (atoi(tokens[6]) ? "YES" : "NO"));
2941 }
2942
2943 mprintf("FILE POSITION: ");
2944 if (!cli_isnumber(tokens[7])) {
2945 if (!strcmp(tokens[7], "*")) {
2946 mprintf("ANY\n");
2947
2948 } else if (strchr(tokens[7], '-')) {
2949 sz = cli_strtokenize(tokens[7], '-', 2, (const char **)range);
2950 if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
2951 mprintf("!decodesig: Invalid container size range\n");
2952 return -1;
2953 }
2954 mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
2955
2956 } else {
2957 mprintf("!decodesig: Invalid file position\n");
2958 return -1;
2959 }
2960 } else {
2961 mprintf("%s\n", tokens[7]);
2962 }
2963
2964 if (!strcmp(tokens[1], "CL_TYPE_ZIP") || !strcmp(tokens[1], "CL_TYPE_RAR")) {
2965 if (!strcmp(tokens[8], "*")) {
2966 mprintf("CRC SUM: ANY\n");
2967 } else {
2968
2969 errno = 0;
2970 sz = (int)strtol(tokens[8], NULL, 16);
2971 if (!sz && errno) {
2972 mprintf("!decodesig: Invalid cyclic redundancy check sum\n");
2973 return -1;
2974 } else {
2975 mprintf("CRC SUM: %d\n", sz);
2976 }
2977 }
2978 }
2979
2980 return 0;
2981 }
2982
decodeftm(char ** tokens,int tokens_count)2983 static int decodeftm(char **tokens, int tokens_count)
2984 {
2985 mprintf("FILE TYPE NAME: %s\n", tokens[3]);
2986 mprintf("FILE SIGNATURE TYPE: %s\n", tokens[0]);
2987 mprintf("FILE MAGIC OFFSET: %s\n", tokens[1]);
2988 mprintf("FILE MAGIC HEX: %s\n", tokens[2]);
2989 mprintf("FILE MAGIC DECODED:\n");
2990 decodehex(tokens[2]);
2991 mprintf("FILE TYPE REQUIRED: %s\n", tokens[4]);
2992 mprintf("FILE TYPE DETECTED: %s\n", tokens[5]);
2993 if (tokens_count == 7)
2994 mprintf("FTM FLEVEL: >=%s\n", tokens[6]);
2995 else if (tokens_count == 8)
2996 mprintf("FTM FLEVEL: %s..%s\n", tokens[6], tokens[7]);
2997 return 0;
2998 }
2999
decodesig(char * sig,int fd)3000 static int decodesig(char *sig, int fd)
3001 {
3002 char *pt;
3003 char *tokens[68], *subtokens[4], *subhex;
3004 int tokens_count, subtokens_count, subsigs, i, bc = 0;
3005
3006 if (*sig == '[') {
3007 if (!(pt = strchr(sig, ']'))) {
3008 mprintf("!decodesig: Invalid input\n");
3009 return -1;
3010 }
3011 sig = &pt[2];
3012 }
3013
3014 if (strchr(sig, ';')) { /* lsig */
3015 tokens_count = cli_ldbtokenize(sig, ';', 67 + 1, (const char **)tokens, 2);
3016 if (tokens_count < 4) {
3017 mprintf("!decodesig: Invalid or not supported signature format\n");
3018 return -1;
3019 }
3020 mprintf("VIRUS NAME: %s\n", tokens[0]);
3021 if (strlen(tokens[0]) && strstr(tokens[0], ".{") && tokens[0][strlen(tokens[0]) - 1] == '}')
3022 bc = 1;
3023 mprintf("TDB: %s\n", tokens[1]);
3024 mprintf("LOGICAL EXPRESSION: %s\n", tokens[2]);
3025 subsigs = cli_ac_chklsig(tokens[2], tokens[2] + strlen(tokens[2]), NULL, NULL, NULL, 1);
3026 if (subsigs == -1) {
3027 mprintf("!decodesig: Broken logical expression\n");
3028 return -1;
3029 }
3030 subsigs++;
3031 if (subsigs > 64) {
3032 mprintf("!decodesig: Too many subsignatures\n");
3033 return -1;
3034 }
3035 if (!bc && subsigs != tokens_count - 3) {
3036 mprintf("!decodesig: The number of subsignatures (==%u) doesn't match the IDs in the logical expression (==%u)\n", tokens_count - 3, subsigs);
3037 return -1;
3038 }
3039 for (i = 0; i < tokens_count - 3; i++) {
3040 if (i >= subsigs)
3041 mprintf(" * BYTECODE SUBSIG\n");
3042 else
3043 mprintf(" * SUBSIG ID %d\n", i);
3044
3045 subtokens_count = cli_ldbtokenize(tokens[3 + i], ':', 4, (const char **)subtokens, 0);
3046 if (!subtokens_count) {
3047 mprintf("!decodesig: Invalid or not supported subsignature format\n");
3048 return -1;
3049 }
3050 if ((subtokens_count % 2) == 0)
3051 mprintf(" +-> OFFSET: %s\n", subtokens[0]);
3052 else
3053 mprintf(" +-> OFFSET: ANY\n");
3054
3055 if (subtokens_count == 3) {
3056 mprintf(" +-> SIGMOD:");
3057 decodesigmod(subtokens[2]);
3058 } else if (subtokens_count == 4) {
3059 mprintf(" +-> SIGMOD:");
3060 decodesigmod(subtokens[3]);
3061 } else {
3062 mprintf(" +-> SIGMOD: NONE\n");
3063 }
3064
3065 subhex = (subtokens_count % 2) ? subtokens[0] : subtokens[1];
3066 if (fd == -1) {
3067 mprintf(" +-> DECODED SUBSIGNATURE:\n");
3068 decodehex(subhex);
3069 } else {
3070 mprintf(" +-> ");
3071 matchsig(subhex, subhex, fd);
3072 }
3073 }
3074 } else if (strchr(sig, ':')) { /* ndb or cdb or ftm*/
3075 tokens_count = cli_strtokenize(sig, ':', 12 + 1, (const char **)tokens);
3076
3077 if (tokens_count > 9 && tokens_count < 13) { /* cdb*/
3078 return decodecdb(tokens);
3079 }
3080
3081 if (tokens_count > 5 && tokens_count < 9) { /* ftm */
3082 long ftmsigtype;
3083 char *end;
3084 ftmsigtype = strtol(tokens[0], &end, 10);
3085 if (end == tokens[0] + 1 && (ftmsigtype == 0 || ftmsigtype == 1 || ftmsigtype == 4))
3086 return decodeftm(tokens, tokens_count);
3087 }
3088
3089 if (tokens_count < 4 || tokens_count > 6) {
3090 mprintf("!decodesig: Invalid or not supported signature format\n");
3091 mprintf("TOKENS COUNT: %u\n", tokens_count);
3092 return -1;
3093 }
3094 mprintf("VIRUS NAME: %s\n", tokens[0]);
3095 if (tokens_count == 5)
3096 mprintf("FUNCTIONALITY LEVEL: >=%s\n", tokens[4]);
3097 else if (tokens_count == 6)
3098 mprintf("FUNCTIONALITY LEVEL: %s..%s\n", tokens[4], tokens[5]);
3099
3100 if (!cli_isnumber(tokens[1])) {
3101 mprintf("!decodesig: Invalid target type\n");
3102 return -1;
3103 }
3104 mprintf("TARGET TYPE: ");
3105 switch (atoi(tokens[1])) {
3106 case 0:
3107 mprintf("ANY FILE\n");
3108 break;
3109 case 1:
3110 mprintf("PE\n");
3111 break;
3112 case 2:
3113 mprintf("OLE2\n");
3114 break;
3115 case 3:
3116 mprintf("HTML\n");
3117 break;
3118 case 4:
3119 mprintf("MAIL\n");
3120 break;
3121 case 5:
3122 mprintf("GRAPHICS\n");
3123 break;
3124 case 6:
3125 mprintf("ELF\n");
3126 break;
3127 case 7:
3128 mprintf("NORMALIZED ASCII TEXT\n");
3129 break;
3130 case 8:
3131 mprintf("DISASM DATA\n");
3132 break;
3133 case 9:
3134 mprintf("MACHO\n");
3135 break;
3136 case 10:
3137 mprintf("PDF\n");
3138 break;
3139 case 11:
3140 mprintf("FLASH\n");
3141 break;
3142 case 12:
3143 mprintf("JAVA CLASS\n");
3144 break;
3145 default:
3146 mprintf("!decodesig: Invalid target type\n");
3147 return -1;
3148 }
3149 mprintf("OFFSET: %s\n", tokens[2]);
3150 if (fd == -1) {
3151 mprintf("DECODED SIGNATURE:\n");
3152 decodehex(tokens[3]);
3153 } else {
3154 matchsig(tokens[3], strcmp(tokens[2], "*") ? tokens[2] : NULL, fd);
3155 }
3156 } else if ((pt = strchr(sig, '='))) {
3157 *pt++ = 0;
3158 mprintf("VIRUS NAME: %s\n", sig);
3159 if (fd == -1) {
3160 mprintf("DECODED SIGNATURE:\n");
3161 decodehex(pt);
3162 } else {
3163 matchsig(pt, NULL, fd);
3164 }
3165 } else {
3166 mprintf("decodesig: Not supported signature format\n");
3167 return -1;
3168 }
3169
3170 return 0;
3171 }
3172
decodesigs(void)3173 static int decodesigs(void)
3174 {
3175 char buffer[32769];
3176
3177 fflush(stdin);
3178 while (fgets(buffer, sizeof(buffer), stdin)) {
3179 cli_chomp(buffer);
3180 if (!strlen(buffer))
3181 break;
3182 if (decodesig(buffer, -1) == -1)
3183 return -1;
3184 }
3185 return 0;
3186 }
3187
testsigs(const struct optstruct * opts)3188 static int testsigs(const struct optstruct *opts)
3189 {
3190 char buffer[32769];
3191 FILE *sigs;
3192 int ret = 0, fd;
3193
3194 if (!opts->filename) {
3195 mprintf("!--test-sigs requires two arguments\n");
3196 return -1;
3197 }
3198
3199 sigs = fopen(optget(opts, "test-sigs")->strarg, "rb");
3200 if (!sigs) {
3201 mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
3202 return -1;
3203 }
3204
3205 fd = open(opts->filename[0], O_RDONLY | O_BINARY);
3206 if (fd == -1) {
3207 mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
3208 fclose(sigs);
3209 return -1;
3210 }
3211
3212 while (fgets(buffer, sizeof(buffer), sigs)) {
3213 cli_chomp(buffer);
3214 if (!strlen(buffer))
3215 break;
3216 if (decodesig(buffer, fd) == -1) {
3217 ret = -1;
3218 break;
3219 }
3220 }
3221
3222 close(fd);
3223 fclose(sigs);
3224 return ret;
3225 }
3226
diffdirs(const char * old,const char * new,const char * patch)3227 static int diffdirs(const char *old, const char *new, const char *patch)
3228 {
3229 FILE *diff;
3230 DIR *dd;
3231 struct dirent *dent;
3232 char cwd[512], path[1024];
3233
3234 if (!getcwd(cwd, sizeof(cwd))) {
3235 mprintf("!diffdirs: getcwd() failed\n");
3236 return -1;
3237 }
3238
3239 if (!(diff = fopen(patch, "wb"))) {
3240 mprintf("!diffdirs: Can't open %s for writing\n", patch);
3241 return -1;
3242 }
3243
3244 if (chdir(new) == -1) {
3245 mprintf("!diffdirs: Can't chdir to %s\n", new);
3246 fclose(diff);
3247 return -1;
3248 }
3249
3250 if ((dd = opendir(new)) == NULL) {
3251 mprintf("!diffdirs: Can't open directory %s\n", new);
3252 fclose(diff);
3253 return -1;
3254 }
3255
3256 while ((dent = readdir(dd))) {
3257 if (dent->d_ino) {
3258 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
3259 continue;
3260
3261 snprintf(path, sizeof(path), "%s" PATHSEP "%s", old, dent->d_name);
3262 if (compare(path, dent->d_name, diff) == -1) {
3263 if (chdir(cwd) == -1)
3264 mprintf("^diffdirs: Can't chdir to %s\n", cwd);
3265 fclose(diff);
3266 unlink(patch);
3267 closedir(dd);
3268 return -1;
3269 }
3270 }
3271 }
3272 closedir(dd);
3273
3274 /* check for removed files */
3275 if ((dd = opendir(old)) == NULL) {
3276 mprintf("!diffdirs: Can't open directory %s\n", old);
3277 fclose(diff);
3278 return -1;
3279 }
3280
3281 while ((dent = readdir(dd))) {
3282 if (dent->d_ino) {
3283 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
3284 continue;
3285
3286 snprintf(path, sizeof(path), "%s" PATHSEP "%s", new, dent->d_name);
3287 if (access(path, R_OK))
3288 fprintf(diff, "UNLINK %s\n", dent->d_name);
3289 }
3290 }
3291 closedir(dd);
3292
3293 fclose(diff);
3294 mprintf("Generated diff file %s\n", patch);
3295 if (chdir(cwd) == -1)
3296 mprintf("^diffdirs: Can't chdir to %s\n", cwd);
3297
3298 return 0;
3299 }
3300
makediff(const struct optstruct * opts)3301 static int makediff(const struct optstruct *opts)
3302 {
3303 char *odir, *ndir, name[32], broken[32], dbname[32];
3304 struct cl_cvd *cvd;
3305 unsigned int oldver, newver;
3306 int ret;
3307
3308 if (!opts->filename) {
3309 mprintf("!makediff: --diff requires two arguments\n");
3310 return -1;
3311 }
3312
3313 if (!(cvd = cl_cvdhead(opts->filename[0]))) {
3314 mprintf("!makediff: Can't read CVD header from %s\n", opts->filename[0]);
3315 return -1;
3316 }
3317 newver = cvd->version;
3318 free(cvd);
3319
3320 if (!(cvd = cl_cvdhead(optget(opts, "diff")->strarg))) {
3321 mprintf("!makediff: Can't read CVD header from %s\n", optget(opts, "diff")->strarg);
3322 return -1;
3323 }
3324 oldver = cvd->version;
3325 free(cvd);
3326
3327 if (oldver + 1 != newver) {
3328 mprintf("!makediff: The old CVD must be %u\n", newver - 1);
3329 return -1;
3330 }
3331
3332 odir = cli_gentemp(NULL);
3333 if (!odir) {
3334 mprintf("!makediff: Can't generate temporary name for odir\n");
3335 return -1;
3336 }
3337
3338 if (mkdir(odir, 0700) == -1) {
3339 mprintf("!makediff: Can't create directory %s\n", odir);
3340 free(odir);
3341 return -1;
3342 }
3343
3344 if (cli_cvdunpack(optget(opts, "diff")->strarg, odir) == -1) {
3345 mprintf("!makediff: Can't unpack CVD file %s\n", optget(opts, "diff")->strarg);
3346 cli_rmdirs(odir);
3347 free(odir);
3348 return -1;
3349 }
3350
3351 ndir = cli_gentemp(NULL);
3352 if (!ndir) {
3353 mprintf("!makediff: Can't generate temporary name for ndir\n");
3354 cli_rmdirs(odir);
3355 free(odir);
3356 return -1;
3357 }
3358
3359 if (mkdir(ndir, 0700) == -1) {
3360 mprintf("!makediff: Can't create directory %s\n", ndir);
3361 free(ndir);
3362 cli_rmdirs(odir);
3363 free(odir);
3364 return -1;
3365 }
3366
3367 if (cli_cvdunpack(opts->filename[0], ndir) == -1) {
3368 mprintf("!makediff: Can't unpack CVD file %s\n", opts->filename[0]);
3369 cli_rmdirs(odir);
3370 cli_rmdirs(ndir);
3371 free(odir);
3372 free(ndir);
3373 return -1;
3374 }
3375
3376 snprintf(name, sizeof(name), "%s-%u.script", getdbname(opts->filename[0], dbname, sizeof(dbname)), newver);
3377 ret = diffdirs(odir, ndir, name);
3378
3379 cli_rmdirs(odir);
3380 cli_rmdirs(ndir);
3381 free(odir);
3382 free(ndir);
3383
3384 if (ret == -1)
3385 return -1;
3386
3387 if (verifydiff(name, optget(opts, "diff")->strarg, NULL) == -1) {
3388 snprintf(broken, sizeof(broken), "%s.broken", name);
3389 if (rename(name, broken)) {
3390 unlink(name);
3391 mprintf("!Generated file is incorrect, removed");
3392 } else {
3393 mprintf("!Generated file is incorrect, renamed to %s\n", broken);
3394 }
3395 return -1;
3396 }
3397
3398 return 0;
3399 }
3400
dumpcerts(const struct optstruct * opts)3401 static int dumpcerts(const struct optstruct *opts)
3402 {
3403 int status = -1;
3404 char *filename = NULL;
3405 STATBUF sb;
3406 struct cl_engine *engine = NULL;
3407 cli_ctx ctx = {0};
3408 struct cl_scan_options options = {0};
3409 int fd = -1;
3410 cl_fmap_t *new_map = NULL;
3411 cl_error_t ret;
3412
3413 logg_file = NULL;
3414
3415 filename = optget(opts, "print-certs")->strarg;
3416 if (!filename) {
3417 mprintf("!dumpcerts: No filename!\n");
3418 goto done;
3419 }
3420
3421 /* Prepare file */
3422 fd = open(filename, O_RDONLY | O_BINARY);
3423 if (fd < 0) {
3424 mprintf("!dumpcerts: Can't open file %s!\n", filename);
3425 goto done;
3426 }
3427
3428 lseek(fd, 0, SEEK_SET);
3429 FSTAT(fd, &sb);
3430
3431 new_map = fmap(fd, 0, sb.st_size, filename);
3432 if (NULL == new_map) {
3433 mprintf("!dumpcerts: Can't create fmap for open file\n");
3434 goto done;
3435 }
3436
3437 /* build engine */
3438 if (!(engine = cl_engine_new())) {
3439 mprintf("!dumpcerts: Can't create new engine\n");
3440 goto done;
3441 }
3442 cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
3443
3444 if (cli_initroots(engine, 0) != CL_SUCCESS) {
3445 mprintf("!dumpcerts: cli_initroots() failed\n");
3446 goto done;
3447 }
3448
3449 if (cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
3450 mprintf("!dumpcerts: Can't parse signature\n");
3451 goto done;
3452 }
3453
3454 if (cl_engine_compile(engine) != CL_SUCCESS) {
3455 mprintf("!dumpcerts: Can't compile engine\n");
3456 goto done;
3457 }
3458
3459 cl_engine_set_num(engine, CL_ENGINE_PE_DUMPCERTS, 1);
3460 cl_debug();
3461
3462 /* prepare context */
3463 ctx.engine = engine;
3464 ctx.options = &options;
3465 ctx.options->parse = ~0;
3466 ctx.dconf = (struct cli_dconf *)engine->dconf;
3467
3468 ctx.recursion_stack_size = ctx.engine->max_recursion_level;
3469 ctx.recursion_stack = cli_calloc(sizeof(recursion_level_t), ctx.recursion_stack_size);
3470 if (!ctx.recursion_stack) {
3471 goto done;
3472 }
3473
3474 // ctx was memset, so recursion_level starts at 0.
3475 ctx.recursion_stack[ctx.recursion_level].fmap = new_map;
3476 ctx.recursion_stack[ctx.recursion_level].type = CL_TYPE_ANY; // ANY for the top level, because we don't yet know the type.
3477 ctx.recursion_stack[ctx.recursion_level].size = new_map->len;
3478
3479 ctx.fmap = ctx.recursion_stack[ctx.recursion_level].fmap;
3480
3481 ret = cli_check_auth_header(&ctx, NULL);
3482
3483 switch (ret) {
3484 case CL_VERIFIED:
3485 case CL_VIRUS:
3486 // These shouldn't happen, since sigtool doesn't load in any sigs
3487 break;
3488 case CL_EVERIFY:
3489 // The Authenticode header was parsed successfully but there were
3490 // no applicable trust/block rules
3491 break;
3492 case CL_BREAK:
3493 mprintf("*dumpcerts: No Authenticode signature detected\n");
3494 break;
3495 case CL_EFORMAT:
3496 mprintf("!dumpcerts: An error occurred when parsing the file\n");
3497 break;
3498 default:
3499 mprintf("!dumpcerts: Other error %d inside cli_check_auth_header.\n", ret);
3500 break;
3501 }
3502
3503 status = 0;
3504
3505 done:
3506 /* Cleanup */
3507 if (NULL != new_map) {
3508 funmap(new_map);
3509 }
3510 if (NULL != ctx.recursion_stack) {
3511 free(ctx.recursion_stack);
3512 }
3513 if (NULL != engine) {
3514 cl_engine_free(engine);
3515 }
3516 if (-1 != fd) {
3517 close(fd);
3518 }
3519 return status;
3520 }
3521
help(void)3522 static void help(void)
3523 {
3524 mprintf("\n");
3525 mprintf(" Clam AntiVirus: Signature Tool %s\n", get_version());
3526 mprintf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
3527 mprintf(" (C) 2022 Cisco Systems, Inc.\n");
3528 mprintf("\n");
3529 mprintf(" sigtool [options]\n");
3530 mprintf("\n");
3531 mprintf(" --help -h Show this help\n");
3532 mprintf(" --version -V Print version number and exit\n");
3533 mprintf(" --quiet Be quiet, output only error messages\n");
3534 mprintf(" --debug Enable debug messages\n");
3535 mprintf(" --stdout Write to stdout instead of stderr. Does not affect 'debug' messages.\n");
3536 mprintf(" --hex-dump Convert data from stdin to a hex\n");
3537 mprintf(" string and print it on stdout\n");
3538 mprintf(" --md5 [FILES] Generate MD5 checksum from stdin\n");
3539 mprintf(" or MD5 sigs for FILES\n");
3540 mprintf(" --sha1 [FILES] Generate SHA1 checksum from stdin\n");
3541 mprintf(" or SHA1 sigs for FILES\n");
3542 mprintf(" --sha256 [FILES] Generate SHA256 checksum from stdin\n");
3543 mprintf(" or SHA256 sigs for FILES\n");
3544 mprintf(" --mdb [FILES] Generate .mdb (section hash) sigs\n");
3545 mprintf(" --imp [FILES] Generate .imp (import table hash) sigs\n");
3546 mprintf(" --html-normalise=FILE Create normalised parts of HTML file\n");
3547 mprintf(" --ascii-normalise=FILE Create normalised text file from ascii source\n");
3548 mprintf(" --utf16-decode=FILE Decode UTF16 encoded files\n");
3549 mprintf(" --info=FILE -i FILE Print database information\n");
3550 mprintf(" --build=NAME [cvd] -b NAME Build a CVD file\n");
3551 mprintf(" --max-bad-sigs=NUMBER Maximum number of mismatched signatures\n");
3552 mprintf(" When building a CVD. Default: 3000\n");
3553 mprintf(" --flevel=FLEVEL Specify a custom flevel.\n");
3554 mprintf(" Default: %u\n", cl_retflevel());
3555 mprintf(" --cvd-version=NUMBER Specify the version number to use for\n");
3556 mprintf(" the build. Default is to use the value+1\n");
3557 mprintf(" from the current CVD in --datadir.\n");
3558 mprintf(" If no datafile is found the default\n");
3559 mprintf(" behaviour is to prompt for a version\n");
3560 mprintf(" number, this switch will prevent the\n");
3561 mprintf(" prompt. NOTE: If a CVD is found in the\n");
3562 mprintf(" --datadir its version+1 is used and\n");
3563 mprintf(" this value is ignored.\n");
3564 mprintf(" --no-cdiff Don't generate .cdiff file\n");
3565 mprintf(" --unsigned Create unsigned database file (.cud)\n");
3566 mprintf(" --hybrid Create a hybrid (standard and bytecode) database file\n");
3567 mprintf(" --print-certs=FILE Print Authenticode details from a PE\n");
3568 mprintf(" --server=ADDR ClamAV Signing Service address\n");
3569 mprintf(" --datadir=DIR Use DIR as default database directory\n");
3570 mprintf(" --unpack=FILE -u FILE Unpack a CVD/CLD file\n");
3571 mprintf(" --unpack-current=SHORTNAME Unpack local CVD/CLD into cwd\n");
3572 mprintf(" --list-sigs[=FILE] -l[FILE] List signature names\n");
3573 mprintf(" --find-sigs=REGEX -fREGEX Find signatures matching REGEX\n");
3574 mprintf(" --decode-sigs Decode signatures from stdin\n");
3575 mprintf(" --test-sigs=DATABASE TARGET_FILE Test signatures from DATABASE against \n");
3576 mprintf(" TARGET_FILE\n");
3577 mprintf(" --vba=FILE Extract VBA/Word6 macro code\n");
3578 mprintf(" --vba-hex=FILE Extract Word6 macro code with hex values\n");
3579 mprintf(" --diff=OLD NEW -d OLD NEW Create diff for OLD and NEW CVDs\n");
3580 mprintf(" --compare=OLD NEW -c OLD NEW Show diff between OLD and NEW files in\n");
3581 mprintf(" cdiff format\n");
3582 mprintf(" --run-cdiff=FILE -r FILE Execute update script FILE in cwd\n");
3583 mprintf(" --verify-cdiff=DIFF CVD/CLD Verify DIFF against CVD/CLD\n");
3584 mprintf("\n");
3585
3586 return;
3587 }
3588
main(int argc,char ** argv)3589 int main(int argc, char **argv)
3590 {
3591 int ret;
3592 struct optstruct *opts;
3593 STATBUF sb;
3594
3595 if (check_flevel())
3596 exit(1);
3597
3598 if ((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
3599 mprintf("!Can't initialize libclamav: %s\n", cl_strerror(ret));
3600 return -1;
3601 }
3602 ret = 1;
3603
3604 opts = optparse(NULL, argc, argv, 1, OPT_SIGTOOL, 0, NULL);
3605 if (!opts) {
3606 mprintf("!Can't parse command line options\n");
3607 return 1;
3608 }
3609
3610 if (optget(opts, "quiet")->enabled)
3611 mprintf_quiet = 1;
3612
3613 if (optget(opts, "stdout")->enabled)
3614 mprintf_stdout = 1;
3615
3616 if (optget(opts, "debug")->enabled)
3617 cl_debug();
3618
3619 if (optget(opts, "version")->enabled) {
3620 print_version(NULL);
3621 optfree(opts);
3622 return 0;
3623 }
3624
3625 if (optget(opts, "help")->enabled) {
3626 optfree(opts);
3627 help();
3628 return 0;
3629 }
3630
3631 if (optget(opts, "hex-dump")->enabled)
3632 ret = hexdump();
3633 else if (optget(opts, "md5")->enabled)
3634 ret = hashsig(opts, 0, 1);
3635 else if (optget(opts, "sha1")->enabled)
3636 ret = hashsig(opts, 0, 2);
3637 else if (optget(opts, "sha256")->enabled)
3638 ret = hashsig(opts, 0, 3);
3639 else if (optget(opts, "mdb")->enabled)
3640 ret = hashsig(opts, 1, 1);
3641 else if (optget(opts, "imp")->enabled)
3642 ret = hashsig(opts, 2, 1);
3643 else if (optget(opts, "html-normalise")->enabled)
3644 ret = htmlnorm(opts);
3645 else if (optget(opts, "ascii-normalise")->enabled)
3646 ret = asciinorm(opts);
3647 else if (optget(opts, "utf16-decode")->enabled)
3648 ret = utf16decode(opts);
3649 else if (optget(opts, "build")->enabled)
3650 ret = build(opts);
3651 else if (optget(opts, "unpack")->enabled)
3652 ret = unpack(opts);
3653 else if (optget(opts, "unpack-current")->enabled)
3654 ret = unpack(opts);
3655 else if (optget(opts, "info")->enabled)
3656 ret = cvdinfo(opts);
3657 else if (optget(opts, "list-sigs")->active)
3658 ret = listsigs(opts, 0);
3659 else if (optget(opts, "find-sigs")->active)
3660 ret = listsigs(opts, 1);
3661 else if (optget(opts, "decode-sigs")->active)
3662 ret = decodesigs();
3663 else if (optget(opts, "test-sigs")->enabled)
3664 ret = testsigs(opts);
3665 else if (optget(opts, "vba")->enabled || optget(opts, "vba-hex")->enabled)
3666 ret = vbadump(opts);
3667 else if (optget(opts, "diff")->enabled)
3668 ret = makediff(opts);
3669 else if (optget(opts, "compare")->enabled)
3670 ret = compareone(opts);
3671 else if (optget(opts, "print-certs")->enabled)
3672 ret = dumpcerts(opts);
3673 else if (optget(opts, "run-cdiff")->enabled)
3674 ret = rundiff(opts);
3675 else if (optget(opts, "verify-cdiff")->enabled) {
3676 if (!opts->filename) {
3677 mprintf("!--verify-cdiff requires two arguments\n");
3678 ret = -1;
3679 } else {
3680 if (CLAMSTAT(opts->filename[0], &sb) == -1) {
3681 mprintf("--verify-cdiff: Can't get status of %s\n", opts->filename[0]);
3682 ret = -1;
3683 } else {
3684 if (S_ISDIR(sb.st_mode))
3685 ret = verifydiff(optget(opts, "verify-cdiff")->strarg, NULL, opts->filename[0]);
3686 else
3687 ret = verifydiff(optget(opts, "verify-cdiff")->strarg, opts->filename[0], NULL);
3688 }
3689 }
3690 } else
3691 help();
3692
3693 optfree(opts);
3694
3695 return ret ? 1 : 0;
3696 }
3697