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(&reg, 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, &reg);
1711         free(dbdir);
1712         cli_regfree(&reg);
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