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