1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2007-2013 Sourcefire, Inc.
4  *
5  *  Authors: Tomasz Kojm
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  *
21  */
22 
23 #if HAVE_CONFIG_H
24 #include "clamav-config.h"
25 #endif
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #ifdef HAVE_PWD_H
34 #include <pwd.h>
35 #endif
36 #include <dirent.h>
37 #ifndef _WIN32
38 #include <sys/wait.h>
39 #include <utime.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 #endif
43 #include <fcntl.h>
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #include <sys/types.h>
48 #include <signal.h>
49 #include <errno.h>
50 #include <target.h>
51 
52 // libclamav
53 #include "clamav.h"
54 #include "others.h"
55 #include "matcher-ac.h"
56 #include "matcher-pcre.h"
57 #include "str.h"
58 #include "readdb.h"
59 
60 // shared
61 #include "optparser.h"
62 #include "actions.h"
63 #include "output.h"
64 #include "misc.h"
65 
66 #include "manager.h"
67 #include "global.h"
68 
69 #ifdef C_LINUX
70 dev_t procdev;
71 #endif
72 
73 #ifdef _WIN32
74 /* FIXME: If possible, handle users correctly */
checkaccess(const char * path,const char * username,int mode)75 static int checkaccess(const char *path, const char *username, int mode)
76 {
77     return !access(path, mode);
78 }
79 #else
checkaccess(const char * path,const char * username,int mode)80 static int checkaccess(const char *path, const char *username, int mode)
81 {
82     struct passwd *user;
83     int ret = 0, status;
84 
85     if (!geteuid()) {
86         if ((user = getpwnam(username)) == NULL) {
87             return -1;
88         }
89 
90         switch (fork()) {
91             case -1:
92                 return -2;
93             case 0:
94                 if (setgid(user->pw_gid)) {
95                     fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int)user->pw_gid);
96                     exit(0);
97                 }
98 
99                 if (setuid(user->pw_uid)) {
100                     fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int)user->pw_uid);
101                     exit(0);
102                 }
103 
104                 if (access(path, mode))
105                     exit(0);
106                 else
107                     exit(1);
108             default:
109                 wait(&status);
110                 if (WIFEXITED(status) && WEXITSTATUS(status) == 1)
111                     ret = 1;
112         }
113     } else {
114         if (!access(path, mode))
115             ret = 1;
116     }
117 
118     return ret;
119 }
120 #endif
121 
122 struct metachain {
123     char **chains;
124     size_t lastadd;
125     size_t lastvir;
126     size_t level;
127     size_t nchains;
128 };
129 
130 struct clamscan_cb_data {
131     struct metachain *chain;
132     const char *filename;
133 };
134 
pre(int fd,const char * type,void * context)135 static cl_error_t pre(int fd, const char *type, void *context)
136 {
137     struct metachain *c;
138     struct clamscan_cb_data *d;
139 
140     UNUSEDPARAM(fd);
141     UNUSEDPARAM(type);
142 
143     if (!(context))
144         return CL_CLEAN;
145     d = (struct clamscan_cb_data *)context;
146     c = d->chain;
147     if (c == NULL)
148         return CL_CLEAN;
149 
150     c->level++;
151 
152     return CL_CLEAN;
153 }
154 
print_chain(struct metachain * c,char * str,size_t len)155 static int print_chain(struct metachain *c, char *str, size_t len)
156 {
157     size_t i;
158     size_t na = 0;
159 
160     for (i = 0; i < c->nchains - 1; i++) {
161         size_t n = strlen(c->chains[i]);
162 
163         if (na)
164             str[na++] = '!';
165 
166         if (n + na + 2 > len)
167             break;
168 
169         memcpy(str + na, c->chains[i], n);
170         na += n;
171     }
172 
173     str[na]      = '\0';
174     str[len - 1] = '\0';
175 
176     return i == c->nchains - 1 ? 0 : 1;
177 }
178 
post(int fd,int result,const char * virname,void * context)179 static cl_error_t post(int fd, int result, const char *virname, void *context)
180 {
181     struct clamscan_cb_data *d = context;
182     struct metachain *c        = NULL;
183     char str[128];
184 
185     UNUSEDPARAM(fd);
186     UNUSEDPARAM(result);
187 
188     if (d != NULL)
189         c = d->chain;
190 
191     if (c && c->nchains) {
192         print_chain(c, str, sizeof(str));
193 
194         if (c->level == c->lastadd && !virname)
195             free(c->chains[--c->nchains]);
196 
197         if (virname && !c->lastvir)
198             c->lastvir = c->level;
199     }
200 
201     if (c)
202         c->level--;
203 
204     return CL_CLEAN;
205 }
206 
meta(const char * container_type,unsigned long fsize_container,const char * filename,unsigned long fsize_real,int is_encrypted,unsigned int filepos_container,void * context)207 static cl_error_t meta(const char *container_type, unsigned long fsize_container, const char *filename,
208                        unsigned long fsize_real, int is_encrypted, unsigned int filepos_container, void *context)
209 {
210     char prev[128];
211     struct metachain *c;
212     struct clamscan_cb_data *d;
213     const char *type;
214     size_t n;
215     char *chain;
216     char **chains;
217     int toolong;
218 
219     UNUSEDPARAM(fsize_container);
220     UNUSEDPARAM(fsize_real);
221     UNUSEDPARAM(is_encrypted);
222     UNUSEDPARAM(filepos_container);
223 
224     if (!(context))
225         return CL_CLEAN;
226     d = (struct clamscan_cb_data *)context;
227 
228     c    = d->chain;
229     type = (strncmp(container_type, "CL_TYPE_", 8) == 0 ? container_type + 8 : container_type);
230     n    = strlen(type) + strlen(filename) + 2;
231 
232     if (!c)
233         return CL_CLEAN;
234 
235     chain = malloc(n);
236 
237     if (!chain)
238         return CL_CLEAN;
239 
240     if (!strcmp(type, "ANY"))
241         snprintf(chain, n, "%s", filename);
242     else
243         snprintf(chain, n, "%s:%s", type, filename);
244 
245     if (c->lastadd != c->level) {
246         n = c->nchains + 1;
247 
248         chains = realloc(c->chains, n * sizeof(*chains));
249         if (!chains) {
250             free(chain);
251             return CL_CLEAN;
252         }
253 
254         c->chains  = chains;
255         c->nchains = n;
256         c->lastadd = c->level;
257     } else {
258         if (c->nchains > 0)
259             free(c->chains[c->nchains - 1]);
260     }
261 
262     if (c->nchains > 0) {
263         c->chains[c->nchains - 1] = chain;
264         toolong                   = print_chain(c, prev, sizeof(prev));
265         logg("*Scanning %s%s!%s\n", prev, toolong ? "..." : "", chain);
266     } else {
267         free(chain);
268     }
269 
270     return CL_CLEAN;
271 }
272 
clamscan_virus_found_cb(int fd,const char * virname,void * context)273 static void clamscan_virus_found_cb(int fd, const char *virname, void *context)
274 {
275     struct clamscan_cb_data *data = (struct clamscan_cb_data *)context;
276     const char *filename;
277 
278     UNUSEDPARAM(fd);
279 
280     if (data == NULL)
281         return;
282     if (data->filename != NULL)
283         filename = data->filename;
284     else
285         filename = "(filename not set)";
286     logg("~%s: %s FOUND\n", filename, virname);
287     return;
288 }
289 
scanfile(const char * filename,struct cl_engine * engine,const struct optstruct * opts,struct cl_scan_options * options)290 static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options)
291 {
292     cl_error_t ret = CL_SUCCESS;
293     int fd, included;
294     unsigned i;
295     const struct optstruct *opt;
296     const char *virname = NULL;
297     STATBUF sb;
298     struct metachain chain;
299     struct clamscan_cb_data data;
300 
301     char *real_filename = NULL;
302 
303     if (NULL == filename || NULL == engine || NULL == opts || NULL == options) {
304         logg("scanfile: Invalid args.\n");
305         ret = CL_EARG;
306         goto done;
307     }
308 
309     ret = cli_realpath((const char *)filename, &real_filename);
310     if (CL_SUCCESS != ret) {
311         logg("*Failed to determine real filename of %s.\n", filename);
312         logg("*Quarantine of the file may fail if file path contains symlinks.\n");
313     } else {
314         filename = real_filename;
315     }
316 
317     if ((opt = optget(opts, "exclude"))->enabled) {
318         while (opt) {
319             if (match_regex(filename, opt->strarg) == 1) {
320                 if (!printinfected)
321                     logg("~%s: Excluded\n", filename);
322 
323                 goto done;
324             }
325 
326             opt = opt->nextarg;
327         }
328     }
329 
330     if ((opt = optget(opts, "include"))->enabled) {
331         included = 0;
332 
333         while (opt) {
334             if (match_regex(filename, opt->strarg) == 1) {
335                 included = 1;
336                 break;
337             }
338 
339             opt = opt->nextarg;
340         }
341 
342         if (!included) {
343             if (!printinfected)
344                 logg("~%s: Excluded\n", filename);
345 
346             goto done;
347         }
348     }
349 
350     /* argh, don't scan /proc files */
351     if (CLAMSTAT(filename, &sb) != -1) {
352 #ifdef C_LINUX
353         if (procdev && sb.st_dev == procdev) {
354             if (!printinfected)
355                 logg("~%s: Excluded (/proc)\n", filename);
356 
357             goto done;
358         }
359 #endif
360         if (!sb.st_size) {
361             if (!printinfected)
362                 logg("~%s: Empty file\n", filename);
363 
364             goto done;
365         }
366 
367         info.rblocks += sb.st_size / CL_COUNT_PRECISION;
368     }
369 
370 #ifndef _WIN32
371     if (geteuid()) {
372         if (checkaccess(filename, NULL, R_OK) != 1) {
373             if (!printinfected)
374                 logg("~%s: Access denied\n", filename);
375 
376             info.errors++;
377             goto done;
378         }
379     }
380 #endif
381 
382     memset(&chain, 0, sizeof(chain));
383     if (optget(opts, "archive-verbose")->enabled) {
384         chain.chains = malloc(sizeof(char **));
385         if (chain.chains) {
386             chain.chains[0] = strdup(filename);
387             if (!chain.chains[0]) {
388                 free(chain.chains);
389                 logg("Unable to allocate memory in scanfile()\n");
390                 info.errors++;
391                 goto done;
392             }
393             chain.nchains = 1;
394         }
395     }
396 
397     logg("*Scanning %s\n", filename);
398 
399     if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) == -1) {
400         logg("^Can't open file %s: %s\n", filename, strerror(errno));
401         info.errors++;
402         goto done;
403     }
404 
405     data.chain    = &chain;
406     data.filename = filename;
407     if ((ret = cl_scandesc_callback(fd, filename, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) {
408         if (optget(opts, "archive-verbose")->enabled) {
409             if (chain.nchains > 1) {
410                 char str[128];
411                 int toolong = print_chain(&chain, str, sizeof(str));
412 
413                 logg("~%s%s!(%llu)%s: %s FOUND\n", str, toolong ? "..." : "", (long long unsigned)(chain.lastvir - 1), chain.chains[chain.nchains - 1], virname);
414             } else if (chain.lastvir) {
415                 logg("~%s!(%llu): %s FOUND\n", filename, (long long unsigned)(chain.lastvir - 1), virname);
416             }
417         }
418         info.files++;
419         info.ifiles++;
420 
421         if (bell)
422             fprintf(stderr, "\007");
423     } else if (ret == CL_CLEAN) {
424         if (!printinfected && printclean)
425             mprintf("~%s: OK\n", filename);
426 
427         info.files++;
428     } else {
429         if (!printinfected)
430             logg("~%s: %s ERROR\n", filename, cl_strerror(ret));
431 
432         info.errors++;
433     }
434 
435     for (i = 0; i < chain.nchains; i++)
436         free(chain.chains[i]);
437 
438     free(chain.chains);
439     close(fd);
440 
441     if (ret == CL_VIRUS && action)
442         action(filename);
443 
444 done:
445     if (NULL != real_filename) {
446         free(real_filename);
447     }
448     return;
449 }
450 
scandirs(const char * dirname,struct cl_engine * engine,const struct optstruct * opts,struct cl_scan_options * options,unsigned int depth,dev_t dev)451 static void scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options, unsigned int depth, dev_t dev)
452 {
453     DIR *dd;
454     struct dirent *dent;
455     STATBUF sb;
456     char *fname;
457     int included;
458     const struct optstruct *opt;
459     unsigned int dirlnk, filelnk;
460 
461     if ((opt = optget(opts, "exclude-dir"))->enabled) {
462         while (opt) {
463             if (match_regex(dirname, opt->strarg) == 1) {
464                 if (!printinfected)
465                     logg("~%s: Excluded\n", dirname);
466 
467                 return;
468             }
469 
470             opt = opt->nextarg;
471         }
472     }
473 
474     if ((opt = optget(opts, "include-dir"))->enabled) {
475         included = 0;
476         while (opt) {
477             if (match_regex(dirname, opt->strarg) == 1) {
478                 included = 1;
479                 break;
480             }
481 
482             opt = opt->nextarg;
483         }
484 
485         if (!included) {
486             if (!printinfected)
487                 logg("~%s: Excluded\n", dirname);
488 
489             return;
490         }
491     }
492 
493     if (depth > (unsigned int)optget(opts, "max-dir-recursion")->numarg)
494         return;
495 
496     dirlnk  = optget(opts, "follow-dir-symlinks")->numarg;
497     filelnk = optget(opts, "follow-file-symlinks")->numarg;
498 
499     if ((dd = opendir(dirname)) != NULL) {
500         info.dirs++;
501         depth++;
502         while ((dent = readdir(dd))) {
503             if (dent->d_ino) {
504                 if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
505                     /* build the full name */
506                     fname = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
507                     if (fname == NULL) { /* oops, malloc() failed, print warning and return */
508                         logg("!scandirs: Memory allocation failed for fname\n");
509                         break;
510                     }
511 
512                     if (!strcmp(dirname, PATHSEP))
513                         sprintf(fname, PATHSEP "%s", dent->d_name);
514                     else
515                         sprintf(fname, "%s" PATHSEP "%s", dirname, dent->d_name);
516 
517                     /* stat the file */
518                     if (LSTAT(fname, &sb) != -1) {
519                         if (!optget(opts, "cross-fs")->enabled) {
520                             if (sb.st_dev != dev) {
521                                 if (!printinfected)
522                                     logg("~%s: Excluded\n", fname);
523 
524                                 free(fname);
525                                 continue;
526                             }
527                         }
528                         if (S_ISLNK(sb.st_mode)) {
529                             if (dirlnk != 2 && filelnk != 2) {
530                                 if (!printinfected)
531                                     logg("%s: Symbolic link\n", fname);
532                             } else if (CLAMSTAT(fname, &sb) != -1) {
533                                 if (S_ISREG(sb.st_mode) && filelnk == 2) {
534                                     scanfile(fname, engine, opts, options);
535                                 } else if (S_ISDIR(sb.st_mode) && dirlnk == 2) {
536                                     if (recursion)
537                                         scandirs(fname, engine, opts, options, depth, dev);
538                                 } else {
539                                     if (!printinfected)
540                                         logg("%s: Symbolic link\n", fname);
541                                 }
542                             }
543                         } else if (S_ISREG(sb.st_mode)) {
544                             scanfile(fname, engine, opts, options);
545                         } else if (S_ISDIR(sb.st_mode) && recursion) {
546                             scandirs(fname, engine, opts, options, depth, dev);
547                         }
548                     }
549 
550                     free(fname);
551                 }
552             }
553         }
554         closedir(dd);
555     } else {
556         if (!printinfected)
557             logg("~%s: Can't open directory.\n", dirname);
558 
559         info.errors++;
560     }
561 }
562 
scanstdin(const struct cl_engine * engine,struct cl_scan_options * options)563 static int scanstdin(const struct cl_engine *engine, struct cl_scan_options *options)
564 {
565     int ret;
566     unsigned int fsize  = 0;
567     const char *virname = NULL;
568     const char *tmpdir  = NULL;
569     char *file, buff[FILEBUFF];
570     size_t bread;
571     FILE *fs;
572     struct clamscan_cb_data data;
573 
574     tmpdir = cl_engine_get_str(engine, CL_ENGINE_TMPDIR, NULL);
575     if (NULL == tmpdir) {
576         tmpdir = cli_gettmpdir();
577     }
578 
579     if (access(tmpdir, R_OK | W_OK) == -1) {
580         logg("!Can't write to temporary directory\n");
581         return 2;
582     }
583 
584     if (!(file = cli_gentemp(tmpdir))) {
585         logg("!Can't generate tempfile name\n");
586         return 2;
587     }
588 
589     if (!(fs = fopen(file, "wb"))) {
590         logg("!Can't open %s for writing\n", file);
591         free(file);
592         return 2;
593     }
594 
595     while ((bread = fread(buff, 1, FILEBUFF, stdin))) {
596         fsize += bread;
597         if (fwrite(buff, 1, bread, fs) < bread) {
598             logg("!Can't write to %s\n", file);
599             free(file);
600             fclose(fs);
601             return 2;
602         }
603     }
604 
605     fclose(fs);
606 
607     logg("*Checking %s\n", file);
608 
609     info.files++;
610     info.rblocks += fsize / CL_COUNT_PRECISION;
611 
612     data.filename = "stdin";
613     data.chain    = NULL;
614     if ((ret = cl_scanfile_callback(file, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) {
615         info.ifiles++;
616 
617         if (bell)
618             fprintf(stderr, "\007");
619     } else if (ret == CL_CLEAN) {
620         if (!printinfected)
621             mprintf("stdin: OK\n");
622     } else {
623         if (!printinfected)
624             logg("stdin: %s ERROR\n", cl_strerror(ret));
625 
626         info.errors++;
627     }
628 
629     unlink(file);
630     free(file);
631     return ret;
632 }
633 
scanmanager(const struct optstruct * opts)634 int scanmanager(const struct optstruct *opts)
635 {
636     int ret = 0, i;
637     struct cl_scan_options options;
638     unsigned int dboptions = 0, dirlnk = 1, filelnk = 1;
639     struct cl_engine *engine;
640     STATBUF sb;
641     char *file, cwd[1024], *pua_cats = NULL;
642     const char *filename;
643     const struct optstruct *opt;
644 #ifndef _WIN32
645     struct rlimit rlim;
646 #endif
647 
648     /* Initalize scan options struct */
649     memset(&options, 0, sizeof(struct cl_scan_options));
650 
651     dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
652     if (dirlnk > 2) {
653         logg("!--follow-dir-symlinks: Invalid argument\n");
654         return 2;
655     }
656 
657     filelnk = optget(opts, "follow-file-symlinks")->numarg;
658     if (filelnk > 2) {
659         logg("!--follow-file-symlinks: Invalid argument\n");
660         return 2;
661     }
662 
663     if (optget(opts, "yara-rules")->enabled) {
664         char *p = optget(opts, "yara-rules")->strarg;
665         if (strcmp(p, "yes")) {
666             if (!strcmp(p, "only"))
667                 dboptions |= CL_DB_YARA_ONLY;
668             else if (!strcmp(p, "no"))
669                 dboptions |= CL_DB_YARA_EXCLUDE;
670         }
671     }
672 
673     if (optget(opts, "phishing-sigs")->enabled)
674         dboptions |= CL_DB_PHISHING;
675 
676     if (optget(opts, "official-db-only")->enabled)
677         dboptions |= CL_DB_OFFICIAL_ONLY;
678 
679     if (optget(opts, "phishing-scan-urls")->enabled)
680         dboptions |= CL_DB_PHISHING_URLS;
681 
682     if (optget(opts, "bytecode")->enabled)
683         dboptions |= CL_DB_BYTECODE;
684 
685     if ((ret = cl_init(CL_INIT_DEFAULT))) {
686         logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
687         return 2;
688     }
689 
690     if (!(engine = cl_engine_new())) {
691         logg("!Can't initialize antivirus engine\n");
692         return 2;
693     }
694 
695     cl_engine_set_clcb_virus_found(engine, clamscan_virus_found_cb);
696 
697     if (optget(opts, "disable-cache")->enabled)
698         cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1);
699 
700     if (optget(opts, "detect-pua")->enabled) {
701         dboptions |= CL_DB_PUA;
702         if ((opt = optget(opts, "exclude-pua"))->enabled) {
703             dboptions |= CL_DB_PUA_EXCLUDE;
704             i = 0;
705             while (opt) {
706                 if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
707                     logg("!Can't allocate memory for pua_cats\n");
708 
709                     cl_engine_free(engine);
710                     return 2;
711                 }
712 
713                 sprintf(pua_cats + i, ".%s", opt->strarg);
714                 i += strlen(opt->strarg) + 1;
715                 pua_cats[i] = 0;
716 
717                 opt = opt->nextarg;
718             }
719             pua_cats[i]     = '.';
720             pua_cats[i + 1] = 0;
721         }
722 
723         if ((opt = optget(opts, "include-pua"))->enabled) {
724             if (pua_cats) {
725                 logg("!--exclude-pua and --include-pua cannot be used at the same time\n");
726 
727                 cl_engine_free(engine);
728                 free(pua_cats);
729                 return 2;
730             }
731 
732             dboptions |= CL_DB_PUA_INCLUDE;
733             i = 0;
734             while (opt) {
735                 if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
736                     logg("!Can't allocate memory for pua_cats\n");
737                     cl_engine_free(engine);
738                     return 2;
739                 }
740 
741                 sprintf(pua_cats + i, ".%s", opt->strarg);
742                 i += strlen(opt->strarg) + 1;
743                 pua_cats[i] = 0;
744 
745                 opt = opt->nextarg;
746             }
747 
748             pua_cats[i]     = '.';
749             pua_cats[i + 1] = 0;
750         }
751 
752         if (pua_cats) {
753             if ((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
754                 logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
755 
756                 free(pua_cats);
757                 cl_engine_free(engine);
758                 return 2;
759             }
760 
761             free(pua_cats);
762         }
763     }
764 
765     if (optget(opts, "dev-ac-only")->enabled)
766         cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
767 
768     if (optget(opts, "dev-ac-depth")->enabled)
769         cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, optget(opts, "dev-ac-depth")->numarg);
770 
771     if (optget(opts, "leave-temps")->enabled)
772         cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
773 
774     if (optget(opts, "force-to-disk")->enabled)
775         cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
776 
777     if (optget(opts, "bytecode-unsigned")->enabled)
778         dboptions |= CL_DB_BYTECODE_UNSIGNED;
779 
780     if ((opt = optget(opts, "bytecode-timeout"))->enabled)
781         cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
782 
783     if (optget(opts, "nocerts")->enabled)
784         cl_engine_set_num(engine, CL_ENGINE_DISABLE_PE_CERTS, 1);
785 
786     if (optget(opts, "dumpcerts")->enabled)
787         cl_engine_set_num(engine, CL_ENGINE_PE_DUMPCERTS, 1);
788 
789     if ((opt = optget(opts, "bytecode-mode"))->enabled) {
790         enum bytecode_mode mode;
791 
792         if (!strcmp(opt->strarg, "ForceJIT"))
793             mode = CL_BYTECODE_MODE_JIT;
794         else if (!strcmp(opt->strarg, "ForceInterpreter"))
795             mode = CL_BYTECODE_MODE_INTERPRETER;
796         else if (!strcmp(opt->strarg, "Test"))
797             mode = CL_BYTECODE_MODE_TEST;
798         else
799             mode = CL_BYTECODE_MODE_AUTO;
800 
801         cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
802     }
803 
804     if ((opt = optget(opts, "statistics"))->enabled) {
805         while (opt) {
806             if (!strcasecmp(opt->strarg, "bytecode")) {
807                 dboptions |= CL_DB_BYTECODE_STATS;
808             } else if (!strcasecmp(opt->strarg, "pcre")) {
809                 dboptions |= CL_DB_PCRE_STATS;
810             }
811             opt = opt->nextarg;
812         }
813     }
814 
815     /* JSON check to prevent engine loading if specified without libjson-c  */
816 #if HAVE_JSON
817     if (optget(opts, "gen-json")->enabled)
818         options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
819 #else
820     if (optget(opts, "gen-json")->enabled) {
821         logg("!Can't generate json (gen-json). libjson-c dev library was missing or misconfigured when ClamAV was built.\n");
822 
823         cl_engine_free(engine);
824         return 2;
825     }
826 #endif
827 
828     if ((opt = optget(opts, "tempdir"))->enabled) {
829         if ((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
830             logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
831 
832             cl_engine_free(engine);
833             return 2;
834         }
835     }
836 
837     if ((opt = optget(opts, "database"))->active) {
838         while (opt) {
839             if ((ret = cl_load(opt->strarg, engine, &info.sigs, dboptions))) {
840                 logg("!%s\n", cl_strerror(ret));
841 
842                 cl_engine_free(engine);
843                 return 2;
844             }
845 
846             opt = opt->nextarg;
847         }
848     } else {
849         char *dbdir = freshdbdir();
850 
851         if ((ret = cl_load(dbdir, engine, &info.sigs, dboptions))) {
852             logg("!%s\n", cl_strerror(ret));
853 
854             free(dbdir);
855             cl_engine_free(engine);
856             return 2;
857         }
858 
859         free(dbdir);
860     }
861 
862     /* pcre engine limits - required for cl_engine_compile */
863     if ((opt = optget(opts, "pcre-match-limit"))->active) {
864         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
865             logg("!cli_engine_set_num(CL_ENGINE_PCRE_MATCH_LIMIT) failed: %s\n", cl_strerror(ret));
866             cl_engine_free(engine);
867             return 2;
868         }
869     }
870 
871     if ((opt = optget(opts, "pcre-recmatch-limit"))->active) {
872         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
873             logg("!cli_engine_set_num(CL_ENGINE_PCRE_RECMATCH_LIMIT) failed: %s\n", cl_strerror(ret));
874             cl_engine_free(engine);
875             return 2;
876         }
877     }
878 
879     if ((ret = cl_engine_compile(engine)) != 0) {
880         logg("!Database initialization error: %s\n", cl_strerror(ret));
881 
882         cl_engine_free(engine);
883         return 2;
884     }
885 
886     if (optget(opts, "archive-verbose")->enabled) {
887         cl_engine_set_clcb_meta(engine, meta);
888         cl_engine_set_clcb_pre_cache(engine, pre);
889         cl_engine_set_clcb_post_scan(engine, post);
890     }
891 
892     /* set limits */
893 
894     /* TODO: Remove deprecated option in a future feature release */
895     if ((opt = optget(opts, "timelimit"))->active) {
896         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
897             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
898 
899             cl_engine_free(engine);
900             return 2;
901         }
902     }
903     if ((opt = optget(opts, "max-scantime"))->active) {
904         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
905             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
906 
907             cl_engine_free(engine);
908             return 2;
909         }
910     }
911 
912     if ((opt = optget(opts, "max-scansize"))->active) {
913         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
914             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
915 
916             cl_engine_free(engine);
917             return 2;
918         }
919     }
920 
921     if ((opt = optget(opts, "max-filesize"))->active) {
922         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
923             logg("!cli_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
924 
925             cl_engine_free(engine);
926             return 2;
927         }
928     }
929 
930 #ifndef _WIN32
931     if (getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
932         if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
933             logg("^System limit for file size is lower than engine->maxfilesize\n");
934         if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
935             logg("^System limit for file size is lower than engine->maxscansize\n");
936     } else {
937         logg("^Cannot obtain resource limits for file size\n");
938     }
939 #endif
940 
941     if ((opt = optget(opts, "max-files"))->active) {
942         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
943             logg("!cli_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
944 
945             cl_engine_free(engine);
946             return 2;
947         }
948     }
949 
950     if ((opt = optget(opts, "max-recursion"))->active) {
951         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
952             logg("!cli_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
953 
954             cl_engine_free(engine);
955             return 2;
956         }
957     }
958 
959     /* Engine max sizes */
960 
961     if ((opt = optget(opts, "max-embeddedpe"))->active) {
962         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
963             logg("!cli_engine_set_num(CL_ENGINE_MAX_EMBEDDEDPE) failed: %s\n", cl_strerror(ret));
964 
965             cl_engine_free(engine);
966             return 2;
967         }
968     }
969 
970     if ((opt = optget(opts, "max-htmlnormalize"))->active) {
971         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
972             logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNORMALIZE) failed: %s\n", cl_strerror(ret));
973 
974             cl_engine_free(engine);
975             return 2;
976         }
977     }
978 
979     if ((opt = optget(opts, "max-htmlnotags"))->active) {
980         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
981             logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNOTAGS) failed: %s\n", cl_strerror(ret));
982 
983             cl_engine_free(engine);
984             return 2;
985         }
986     }
987 
988     if ((opt = optget(opts, "max-scriptnormalize"))->active) {
989         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
990             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCRIPTNORMALIZE) failed: %s\n", cl_strerror(ret));
991 
992             cl_engine_free(engine);
993             return 2;
994         }
995     }
996 
997     if ((opt = optget(opts, "max-ziptypercg"))->active) {
998         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
999             logg("!cli_engine_set_num(CL_ENGINE_MAX_ZIPTYPERCG) failed: %s\n", cl_strerror(ret));
1000 
1001             cl_engine_free(engine);
1002             return 2;
1003         }
1004     }
1005 
1006     if ((opt = optget(opts, "max-partitions"))->active) {
1007         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
1008             logg("!cli_engine_set_num(CL_ENGINE_MAX_PARTITIONS) failed: %s\n", cl_strerror(ret));
1009 
1010             cl_engine_free(engine);
1011             return 2;
1012         }
1013     }
1014 
1015     if ((opt = optget(opts, "max-iconspe"))->active) {
1016         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
1017             logg("!cli_engine_set_num(CL_ENGINE_MAX_ICONSPE) failed: %s\n", cl_strerror(ret));
1018 
1019             cl_engine_free(engine);
1020             return 2;
1021         }
1022     }
1023 
1024     if ((opt = optget(opts, "max-rechwp3"))->active) {
1025         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECHWP3, opt->numarg))) {
1026             logg("!cli_engine_set_num(CL_ENGINE_MAX_RECHWP3) failed: %s\n", cl_strerror(ret));
1027 
1028             cl_engine_free(engine);
1029             return 2;
1030         }
1031     }
1032 
1033     if ((opt = optget(opts, "pcre-max-filesize"))->active) {
1034         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
1035             logg("!cli_engine_set_num(CL_ENGINE_PCRE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
1036             cl_engine_free(engine);
1037             return 2;
1038         }
1039     }
1040 
1041     /* set scan options */
1042     if (optget(opts, "allmatch")->enabled) {
1043         options.general |= CL_SCAN_GENERAL_ALLMATCHES;
1044     }
1045 
1046     /* TODO: Remove deprecated option in a future feature release */
1047     if ((optget(opts, "phishing-ssl")->enabled) ||
1048         (optget(opts, "alert-phishing-ssl")->enabled))
1049         options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
1050 
1051     /* TODO: Remove deprecated option in a future feature release */
1052     if ((optget(opts, "phishing-cloak")->enabled) ||
1053         (optget(opts, "alert-phishing-cloak")->enabled))
1054         options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_CLOAK;
1055 
1056     /* TODO: Remove deprecated option in a future feature release */
1057     if ((optget(opts, "partition-intersection")->enabled) ||
1058         (optget(opts, "alert-partition-intersection")->enabled))
1059         options.heuristic |= CL_SCAN_HEURISTIC_PARTITION_INTXN;
1060 
1061     if (optget(opts, "heuristic-scan-precedence")->enabled)
1062         options.general |= CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
1063 
1064     if (optget(opts, "scan-archive")->enabled)
1065         options.parse |= CL_SCAN_PARSE_ARCHIVE;
1066 
1067     /* TODO: Remove deprecated option in a future feature release */
1068     if ((optget(opts, "detect-broken")->enabled) ||
1069         (optget(opts, "alert-broken")->enabled)) {
1070         options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
1071     }
1072 
1073     if (optget(opts, "alert-broken-media")->enabled) {
1074         options.heuristic |= CL_SCAN_HEURISTIC_BROKEN_MEDIA;
1075     }
1076 
1077     /* TODO: Remove deprecated option in a future feature release */
1078     if ((optget(opts, "block-encrypted")->enabled) ||
1079         (optget(opts, "alert-encrypted")->enabled)) {
1080         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
1081         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
1082     }
1083 
1084     if (optget(opts, "alert-encrypted-archive")->enabled)
1085         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
1086 
1087     if (optget(opts, "alert-encrypted-doc")->enabled)
1088         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
1089 
1090     /* TODO: Remove deprecated option in a future feature release */
1091     if ((optget(opts, "block-macros")->enabled) ||
1092         (optget(opts, "alert-macros")->enabled)) {
1093         options.heuristic |= CL_SCAN_HEURISTIC_MACROS;
1094     }
1095 
1096     if (optget(opts, "scan-pe")->enabled)
1097         options.parse |= CL_SCAN_PARSE_PE;
1098 
1099     if (optget(opts, "scan-elf")->enabled)
1100         options.parse |= CL_SCAN_PARSE_ELF;
1101 
1102     if (optget(opts, "scan-ole2")->enabled)
1103         options.parse |= CL_SCAN_PARSE_OLE2;
1104 
1105     if (optget(opts, "scan-pdf")->enabled)
1106         options.parse |= CL_SCAN_PARSE_PDF;
1107 
1108     if (optget(opts, "scan-swf")->enabled)
1109         options.parse |= CL_SCAN_PARSE_SWF;
1110 
1111     if (optget(opts, "scan-html")->enabled && optget(opts, "normalize")->enabled)
1112         options.parse |= CL_SCAN_PARSE_HTML;
1113 
1114     if (optget(opts, "scan-mail")->enabled)
1115         options.parse |= CL_SCAN_PARSE_MAIL;
1116 
1117     if (optget(opts, "scan-xmldocs")->enabled)
1118         options.parse |= CL_SCAN_PARSE_XMLDOCS;
1119 
1120     if (optget(opts, "scan-hwp3")->enabled)
1121         options.parse |= CL_SCAN_PARSE_HWP3;
1122 
1123     /* TODO: Remove deprecated option in a future feature release */
1124     if ((optget(opts, "algorithmic-detection")->enabled) && /* && used due to default-yes for both options */
1125         (optget(opts, "heuristic-alerts")->enabled)) {
1126         options.general |= CL_SCAN_GENERAL_HEURISTICS;
1127     }
1128 
1129     /* TODO: Remove deprecated option in a future feature release */
1130     if ((optget(opts, "block-max")->enabled) ||
1131         (optget(opts, "alert-exceeds-max")->enabled)) {
1132         options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
1133     }
1134 
1135 #ifdef HAVE__INTERNAL__SHA_COLLECT
1136     if (optget(opts, "dev-collect-hashes")->enabled)
1137         options.dev |= CL_SCAN_DEV_COLLECT_SHA;
1138 #endif
1139 
1140     if (optget(opts, "dev-performance")->enabled)
1141         options.dev |= CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO;
1142 
1143     if (optget(opts, "detect-structured")->enabled) {
1144         options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED;
1145 
1146         if ((opt = optget(opts, "structured-ssn-format"))->enabled) {
1147             switch (opt->numarg) {
1148                 case 0:
1149                     options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
1150                     break;
1151                 case 1:
1152                     options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
1153                     break;
1154                 case 2:
1155                     options.heuristic |= (CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL | CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED);
1156                     break;
1157                 default:
1158                     logg("!Invalid argument for --structured-ssn-format\n");
1159                     return 2;
1160             }
1161         } else {
1162             options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
1163         }
1164 
1165         if ((opt = optget(opts, "structured-ssn-count"))->active) {
1166             if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
1167                 logg("!cli_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
1168 
1169                 cl_engine_free(engine);
1170                 return 2;
1171             }
1172         }
1173 
1174         if ((opt = optget(opts, "structured-cc-count"))->active) {
1175             if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
1176                 logg("!cli_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
1177                 cl_engine_free(engine);
1178                 return 2;
1179             }
1180         }
1181 
1182         if ((opt = optget(opts, "structured-cc-mode"))->active) {
1183             switch (opt->numarg) {
1184                 case 0:
1185                     break;
1186                 case 1:
1187                     options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_CC;
1188                     break;
1189                 default:
1190                     logg("!Invalid argument for --structured-cc-mode\n");
1191                     return 2;
1192             }
1193         }
1194     } else {
1195         options.heuristic &= ~CL_SCAN_HEURISTIC_STRUCTURED;
1196     }
1197 
1198 #ifdef C_LINUX
1199     procdev = (dev_t)0;
1200     if (CLAMSTAT("/proc", &sb) != -1 && !sb.st_size)
1201         procdev = sb.st_dev;
1202 #endif
1203 
1204     /* check filetype */
1205     if (!opts->filename && !optget(opts, "file-list")->enabled) {
1206         /* we need full path for some reasons (eg. archive handling) */
1207         if (!getcwd(cwd, sizeof(cwd))) {
1208             logg("!Can't get absolute pathname of current working directory\n");
1209             ret = 2;
1210         } else {
1211             CLAMSTAT(cwd, &sb);
1212             scandirs(cwd, engine, opts, &options, 1, sb.st_dev);
1213         }
1214 
1215     } else if (opts->filename && !optget(opts, "file-list")->enabled && !strcmp(opts->filename[0], "-")) { /* read data from stdin */
1216         ret = scanstdin(engine, &options);
1217     } else {
1218         if (opts->filename && optget(opts, "file-list")->enabled)
1219             logg("^Only scanning files from --file-list (files passed at cmdline are ignored)\n");
1220 
1221         while ((filename = filelist(opts, &ret)) && (file = strdup(filename))) {
1222             if (LSTAT(file, &sb) == -1) {
1223                 perror(file);
1224                 logg("^%s: Can't access file\n", file);
1225                 ret = 2;
1226             } else {
1227                 for (i = strlen(file) - 1; i > 0; i--) {
1228                     if (file[i] == *PATHSEP)
1229                         file[i] = 0;
1230                     else
1231                         break;
1232                 }
1233 
1234                 if (S_ISLNK(sb.st_mode)) {
1235                     if (dirlnk == 0 && filelnk == 0) {
1236                         if (!printinfected)
1237                             logg("%s: Symbolic link\n", file);
1238                     } else if (CLAMSTAT(file, &sb) != -1) {
1239                         if (S_ISREG(sb.st_mode) && filelnk) {
1240                             scanfile(file, engine, opts, &options);
1241                         } else if (S_ISDIR(sb.st_mode) && dirlnk) {
1242                             scandirs(file, engine, opts, &options, 1, sb.st_dev);
1243                         } else {
1244                             if (!printinfected)
1245                                 logg("%s: Symbolic link\n", file);
1246                         }
1247                     }
1248                 } else if (S_ISREG(sb.st_mode)) {
1249                     scanfile(file, engine, opts, &options);
1250                 } else if (S_ISDIR(sb.st_mode)) {
1251                     scandirs(file, engine, opts, &options, 1, sb.st_dev);
1252                 } else {
1253                     logg("^%s: Not supported file type\n", file);
1254                     ret = 2;
1255                 }
1256             }
1257 
1258             free(file);
1259         }
1260     }
1261 
1262     if ((opt = optget(opts, "statistics"))->enabled) {
1263         while (opt) {
1264             if (!strcasecmp(opt->strarg, "bytecode")) {
1265                 cli_sigperf_print();
1266                 cli_sigperf_events_destroy();
1267             }
1268 #if HAVE_PCRE
1269             else if (!strcasecmp(opt->strarg, "pcre")) {
1270                 cli_pcre_perf_print();
1271                 cli_pcre_perf_events_destroy();
1272             }
1273 #endif
1274             opt = opt->nextarg;
1275         }
1276     }
1277 
1278     /* free the engine */
1279     cl_engine_free(engine);
1280 
1281     /* overwrite return code - infection takes priority */
1282     if (info.ifiles)
1283         ret = 1;
1284     else if (info.errors)
1285         ret = 2;
1286 
1287     return ret;
1288 }
1289