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 <math.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 // common
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
634 struct sigload_progress {
635 time_t startTime;
636 time_t lastRunTime;
637 uint8_t bComplete;
638 };
639
640 struct engine_compile_progress {
641 time_t startTime;
642 time_t lastRunTime;
643 uint8_t bComplete;
644 };
645
646 struct engine_free_progress {
647 time_t startTime;
648 time_t lastRunTime;
649 uint8_t bComplete;
650 };
651
print_time(time_t seconds)652 static void print_time(time_t seconds)
653 {
654 if (seconds >= 3600) {
655 fprintf(stdout, "%2lldh %02lldm", (long long)seconds / 3600, ((long long)seconds % 3600) / 60);
656 } else if (seconds >= 60) {
657 fprintf(stdout, "%2lldm %02llds", (long long)seconds / 60, (long long)seconds % 60);
658 } else {
659 fprintf(stdout, "%3llds", (long long)seconds);
660 }
661 }
662
print_num_sigs(size_t sigs,int bPad)663 static void print_num_sigs(size_t sigs, int bPad)
664 {
665 if (sigs >= (1000 * 1000)) {
666 const char *format = bPad ? "%7.02fM" : "%.02fM";
667 double megasigs = sigs / (double)(1000 * 1000);
668 fprintf(stdout, format, megasigs);
669 } else if (sigs >= 1000) {
670 const char *format = bPad ? "%7.02fK" : "%.02fK";
671 double kilosigs = sigs / (double)(1000);
672 fprintf(stdout, format, kilosigs);
673 } else {
674 const char *format = bPad ? "%8zu" : "%zu";
675 fprintf(stdout, format, sigs);
676 }
677 }
678
679 /**
680 * @brief Progress callback for sig-load
681 *
682 * @param total_items Total number of items
683 * @param now_completed Number of items completed
684 * @param context Opaque application provided data; This maps to sigload_progress
685 */
sigload_callback(size_t total_items,size_t now_completed,void * context)686 static cl_error_t sigload_callback(size_t total_items, size_t now_completed, void *context)
687 {
688 time_t curtime = 0;
689 time_t remtime = 0;
690
691 struct sigload_progress *sigloadProgress = (struct sigload_progress *)context;
692
693 uint32_t i = 0;
694 uint32_t totalNumDots = 25;
695 uint32_t numDots = 0;
696 double fraction_loaded = 0.0;
697
698 if ((total_items <= 0) || (sigloadProgress->bComplete)) {
699 return CL_SUCCESS;
700 }
701
702 fraction_loaded = (double)now_completed / (double)total_items;
703 numDots = round(fraction_loaded * totalNumDots);
704
705 if (0 == sigloadProgress->startTime) {
706 sigloadProgress->startTime = time(0);
707 }
708 curtime = time(0) - sigloadProgress->startTime;
709
710 sigloadProgress->lastRunTime = curtime;
711
712 #ifndef _WIN32
713 fprintf(stdout, "\e[?7l");
714 #endif
715 if (fraction_loaded <= 0.0) {
716 fprintf(stdout, "Loading: ");
717 print_time(curtime);
718 fprintf(stdout, " ");
719 } else {
720 remtime = (curtime / fraction_loaded) - curtime;
721 fprintf(stdout, "Loading: ");
722 print_time(curtime);
723 fprintf(stdout, ", ETA: ");
724 print_time(remtime);
725 fprintf(stdout, " ");
726 }
727
728 fprintf(stdout, "[");
729 if (numDots > 0) {
730 if (numDots > 1) {
731 for (i = 0; i < numDots - 1; i++) {
732 fprintf(stdout, "=");
733 }
734 }
735 fprintf(stdout, ">");
736 i++;
737 }
738 for (; i < totalNumDots; i++) {
739 fprintf(stdout, " ");
740 }
741
742 fprintf(stdout, "] ");
743
744 print_num_sigs(now_completed, 1);
745 fprintf(stdout, "/");
746 print_num_sigs(total_items, 0);
747 fprintf(stdout, " sigs ");
748
749 if (now_completed < total_items) {
750 fprintf(stdout, "\r");
751 } else {
752 fprintf(stdout, "\n");
753 sigloadProgress->bComplete = 1;
754 }
755 #ifndef _WIN32
756 fprintf(stdout, "\e[?7h");
757 #endif
758 fflush(stdout);
759
760 return CL_SUCCESS;
761 }
762
763 /**
764 * @brief Progress callback for sig-load
765 *
766 * @param total_items Total number of items
767 * @param now_completed Number of items completed
768 * @param context Opaque application provided data; This maps to engine_compile_progress
769 */
engine_compile_callback(size_t total_items,size_t now_completed,void * context)770 static cl_error_t engine_compile_callback(size_t total_items, size_t now_completed, void *context)
771 {
772 time_t curtime = 0;
773 time_t remtime = 0;
774
775 struct engine_compile_progress *engineCompileProgress = (struct engine_compile_progress *)context;
776
777 uint32_t i = 0;
778 uint32_t totalNumDots = 25;
779 uint32_t numDots = 0;
780 double fraction_compiled = 0.0;
781
782 if ((total_items <= 0) || (engineCompileProgress->bComplete)) {
783 return CL_SUCCESS;
784 }
785
786 fraction_compiled = (double)now_completed / (double)total_items;
787 numDots = round(fraction_compiled * totalNumDots);
788
789 if (0 == engineCompileProgress->startTime) {
790 engineCompileProgress->startTime = time(0);
791 }
792 curtime = time(0) - engineCompileProgress->startTime;
793
794 engineCompileProgress->lastRunTime = curtime;
795
796 #ifndef _WIN32
797 fprintf(stdout, "\e[?7l");
798 #endif
799 if (fraction_compiled <= 0.0) {
800 fprintf(stdout, "Compiling: ");
801 print_time(curtime);
802 fprintf(stdout, " ");
803 } else {
804 remtime = (curtime / fraction_compiled) - curtime;
805 fprintf(stdout, "Compiling: ");
806 print_time(curtime);
807 fprintf(stdout, ", ETA: ");
808 print_time(remtime);
809 fprintf(stdout, " ");
810 }
811
812 fprintf(stdout, "[");
813 if (numDots > 0) {
814 if (numDots > 1) {
815 for (i = 0; i < numDots - 1; i++) {
816 fprintf(stdout, "=");
817 }
818 }
819 fprintf(stdout, ">");
820 i++;
821 }
822 for (; i < totalNumDots; i++) {
823 fprintf(stdout, " ");
824 }
825
826 fprintf(stdout, "] ");
827
828 print_num_sigs(now_completed, 1);
829 fprintf(stdout, "/");
830 print_num_sigs(total_items, 0);
831 fprintf(stdout, " tasks ");
832
833 if (now_completed < total_items) {
834 fprintf(stdout, "\r");
835 } else {
836 fprintf(stdout, "\n");
837 engineCompileProgress->bComplete = 1;
838 }
839 #ifndef _WIN32
840 fprintf(stdout, "\e[?7h");
841 #endif
842 fflush(stdout);
843
844 return CL_SUCCESS;
845 }
846
847 #ifdef ENABLE_ENGINE_FREE_PROGRESSBAR
848 /**
849 * @brief Progress callback for sig-load
850 *
851 * @param total_items Total number of items
852 * @param now_completed Number of items completed
853 * @param context Opaque application provided data; This maps to engine_free_progress
854 */
engine_free_callback(size_t total_items,size_t now_completed,void * context)855 static cl_error_t engine_free_callback(size_t total_items, size_t now_completed, void *context)
856 {
857 time_t curtime = 0;
858
859 struct engine_free_progress *engineFreeProgress = (struct engine_free_progress *)context;
860
861 uint32_t i = 0;
862 uint32_t totalNumDots = 10;
863 uint32_t numDots = 0;
864 double fraction_freed = 0.0;
865
866 if ((total_items <= 0) || (engineFreeProgress->bComplete)) {
867 return CL_SUCCESS;
868 }
869
870 fraction_freed = (double)now_completed / (double)total_items;
871 numDots = round(fraction_freed * totalNumDots);
872
873 if (0 == engineFreeProgress->startTime) {
874 engineFreeProgress->startTime = time(0);
875 }
876 curtime = time(0) - engineFreeProgress->startTime;
877
878 engineFreeProgress->lastRunTime = curtime;
879
880 #ifndef _WIN32
881 fprintf(stdout, "\e[?7l");
882 #endif
883 fprintf(stdout, "Unloading");
884
885 if (numDots > 0) {
886 if (numDots > 1) {
887 for (i = 0; i < numDots - 1; i++) {
888 fprintf(stdout, ".");
889 }
890 }
891 i++;
892 }
893 for (; i < totalNumDots; i++) {
894 fprintf(stdout, " ");
895 }
896
897 fprintf(stdout, " ");
898
899 print_num_sigs(now_completed, 1);
900 fprintf(stdout, "/");
901 print_num_sigs(total_items, 0);
902 fprintf(stdout, " tasks ");
903
904 if (now_completed < total_items) {
905 fprintf(stdout, "\r");
906 } else {
907 fprintf(stdout, "\n");
908 engineFreeProgress->bComplete = 1;
909 }
910 #ifndef _WIN32
911 fprintf(stdout, "\e[?7h");
912 #endif
913 fflush(stdout);
914
915 return CL_SUCCESS;
916 }
917 #endif
918
scanmanager(const struct optstruct * opts)919 int scanmanager(const struct optstruct *opts)
920 {
921 int ret = 0, i;
922 struct cl_scan_options options;
923 unsigned int dboptions = 0, dirlnk = 1, filelnk = 1;
924 struct cl_engine *engine = NULL;
925 STATBUF sb;
926 char *file, cwd[1024], *pua_cats = NULL;
927 const char *filename;
928 const struct optstruct *opt;
929 #ifndef _WIN32
930 struct rlimit rlim;
931 #endif
932 struct sigload_progress sigload_progress_ctx = {0};
933 struct engine_compile_progress engine_compile_progress_ctx = {0};
934 #ifdef ENABLE_ENGINE_FREE_PROGRESSBAR
935 struct engine_free_progress engine_free_progress_ctx = {0};
936 #endif
937
938 /* Initalize scan options struct */
939 memset(&options, 0, sizeof(struct cl_scan_options));
940
941 dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
942 if (dirlnk > 2) {
943 logg("!--follow-dir-symlinks: Invalid argument\n");
944 ret = 2;
945 goto done;
946 }
947
948 filelnk = optget(opts, "follow-file-symlinks")->numarg;
949 if (filelnk > 2) {
950 logg("!--follow-file-symlinks: Invalid argument\n");
951 ret = 2;
952 goto done;
953 }
954
955 if (optget(opts, "yara-rules")->enabled) {
956 char *p = optget(opts, "yara-rules")->strarg;
957 if (strcmp(p, "yes")) {
958 if (!strcmp(p, "only"))
959 dboptions |= CL_DB_YARA_ONLY;
960 else if (!strcmp(p, "no"))
961 dboptions |= CL_DB_YARA_EXCLUDE;
962 }
963 }
964
965 if (optget(opts, "phishing-sigs")->enabled)
966 dboptions |= CL_DB_PHISHING;
967
968 if (optget(opts, "official-db-only")->enabled)
969 dboptions |= CL_DB_OFFICIAL_ONLY;
970
971 if (optget(opts, "phishing-scan-urls")->enabled)
972 dboptions |= CL_DB_PHISHING_URLS;
973
974 if (optget(opts, "bytecode")->enabled)
975 dboptions |= CL_DB_BYTECODE;
976
977 if ((ret = cl_init(CL_INIT_DEFAULT))) {
978 logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
979 ret = 2;
980 goto done;
981 }
982
983 if (!(engine = cl_engine_new())) {
984 logg("!Can't initialize antivirus engine\n");
985 ret = 2;
986 goto done;
987 }
988
989 cl_engine_set_clcb_virus_found(engine, clamscan_virus_found_cb);
990
991 if (isatty(fileno(stdout)) &&
992 !optget(opts, "debug")->enabled &&
993 !optget(opts, "quiet")->enabled &&
994 !optget(opts, "infected")->enabled &&
995 !optget(opts, "no-summary")->enabled) {
996 /* set progress callbacks */
997 cl_engine_set_clcb_sigload_progress(engine, sigload_callback, &sigload_progress_ctx);
998 cl_engine_set_clcb_engine_compile_progress(engine, engine_compile_callback, &engine_compile_progress_ctx);
999 #ifdef ENABLE_ENGINE_FREE_PROGRESSBAR
1000 cl_engine_set_clcb_engine_free_progress(engine, engine_free_callback, &engine_free_progress_ctx);
1001 #endif
1002 }
1003
1004 if (optget(opts, "disable-cache")->enabled)
1005 cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1);
1006
1007 if (optget(opts, "detect-pua")->enabled) {
1008 dboptions |= CL_DB_PUA;
1009 if ((opt = optget(opts, "exclude-pua"))->enabled) {
1010 dboptions |= CL_DB_PUA_EXCLUDE;
1011 i = 0;
1012 while (opt) {
1013 if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
1014 logg("!Can't allocate memory for pua_cats\n");
1015
1016 ret = 2;
1017 goto done;
1018 }
1019
1020 sprintf(pua_cats + i, ".%s", opt->strarg);
1021 i += strlen(opt->strarg) + 1;
1022 pua_cats[i] = 0;
1023
1024 opt = opt->nextarg;
1025 }
1026 pua_cats[i] = '.';
1027 pua_cats[i + 1] = 0;
1028 }
1029
1030 if ((opt = optget(opts, "include-pua"))->enabled) {
1031 if (pua_cats) {
1032 logg("!--exclude-pua and --include-pua cannot be used at the same time\n");
1033
1034 free(pua_cats);
1035 ret = 2;
1036 goto done;
1037 }
1038
1039 dboptions |= CL_DB_PUA_INCLUDE;
1040 i = 0;
1041 while (opt) {
1042 if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
1043 logg("!Can't allocate memory for pua_cats\n");
1044 ret = 2;
1045 goto done;
1046 }
1047
1048 sprintf(pua_cats + i, ".%s", opt->strarg);
1049 i += strlen(opt->strarg) + 1;
1050 pua_cats[i] = 0;
1051
1052 opt = opt->nextarg;
1053 }
1054
1055 pua_cats[i] = '.';
1056 pua_cats[i + 1] = 0;
1057 }
1058
1059 if (pua_cats) {
1060 if ((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
1061 logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
1062
1063 free(pua_cats);
1064 ret = 2;
1065 goto done;
1066 }
1067
1068 free(pua_cats);
1069 }
1070 }
1071
1072 if (optget(opts, "dev-ac-only")->enabled)
1073 cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
1074
1075 if (optget(opts, "dev-ac-depth")->enabled)
1076 cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, optget(opts, "dev-ac-depth")->numarg);
1077
1078 if (optget(opts, "leave-temps")->enabled)
1079 cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
1080
1081 if (optget(opts, "force-to-disk")->enabled)
1082 cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
1083
1084 if (optget(opts, "bytecode-unsigned")->enabled)
1085 dboptions |= CL_DB_BYTECODE_UNSIGNED;
1086
1087 if ((opt = optget(opts, "bytecode-timeout"))->enabled)
1088 cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
1089
1090 if (optget(opts, "nocerts")->enabled)
1091 cl_engine_set_num(engine, CL_ENGINE_DISABLE_PE_CERTS, 1);
1092
1093 if (optget(opts, "dumpcerts")->enabled)
1094 cl_engine_set_num(engine, CL_ENGINE_PE_DUMPCERTS, 1);
1095
1096 if ((opt = optget(opts, "bytecode-mode"))->enabled) {
1097 enum bytecode_mode mode;
1098
1099 if (!strcmp(opt->strarg, "ForceJIT"))
1100 mode = CL_BYTECODE_MODE_JIT;
1101 else if (!strcmp(opt->strarg, "ForceInterpreter"))
1102 mode = CL_BYTECODE_MODE_INTERPRETER;
1103 else if (!strcmp(opt->strarg, "Test"))
1104 mode = CL_BYTECODE_MODE_TEST;
1105 else
1106 mode = CL_BYTECODE_MODE_AUTO;
1107
1108 cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
1109 }
1110
1111 if ((opt = optget(opts, "statistics"))->enabled) {
1112 while (opt) {
1113 if (!strcasecmp(opt->strarg, "bytecode")) {
1114 dboptions |= CL_DB_BYTECODE_STATS;
1115 } else if (!strcasecmp(opt->strarg, "pcre")) {
1116 dboptions |= CL_DB_PCRE_STATS;
1117 }
1118 opt = opt->nextarg;
1119 }
1120 }
1121
1122 /* JSON check to prevent engine loading if specified without libjson-c */
1123 #if HAVE_JSON
1124 if (optget(opts, "gen-json")->enabled)
1125 options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
1126 #else
1127 if (optget(opts, "gen-json")->enabled) {
1128 logg("!Can't generate json (gen-json). libjson-c dev library was missing or misconfigured when ClamAV was built.\n");
1129
1130 ret = 2;
1131 goto done;
1132 }
1133 #endif
1134
1135 if ((opt = optget(opts, "tempdir"))->enabled) {
1136 if ((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
1137 logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
1138
1139 ret = 2;
1140 goto done;
1141 }
1142 }
1143
1144 if ((opt = optget(opts, "database"))->active) {
1145 while (opt) {
1146 if ((ret = cl_load(opt->strarg, engine, &info.sigs, dboptions))) {
1147 logg("!%s\n", cl_strerror(ret));
1148
1149 ret = 2;
1150 goto done;
1151 }
1152
1153 opt = opt->nextarg;
1154 }
1155 } else {
1156 char *dbdir = freshdbdir();
1157
1158 if ((ret = cl_load(dbdir, engine, &info.sigs, dboptions))) {
1159 logg("!%s\n", cl_strerror(ret));
1160
1161 free(dbdir);
1162 ret = 2;
1163 goto done;
1164 }
1165
1166 free(dbdir);
1167 }
1168
1169 /* pcre engine limits - required for cl_engine_compile */
1170 if ((opt = optget(opts, "pcre-match-limit"))->active) {
1171 if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
1172 logg("!cli_engine_set_num(CL_ENGINE_PCRE_MATCH_LIMIT) failed: %s\n", cl_strerror(ret));
1173 ret = 2;
1174 goto done;
1175 }
1176 }
1177
1178 if ((opt = optget(opts, "pcre-recmatch-limit"))->active) {
1179 if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
1180 logg("!cli_engine_set_num(CL_ENGINE_PCRE_RECMATCH_LIMIT) failed: %s\n", cl_strerror(ret));
1181 ret = 2;
1182 goto done;
1183 }
1184 }
1185
1186 if ((ret = cl_engine_compile(engine)) != 0) {
1187 logg("!Database initialization error: %s\n", cl_strerror(ret));
1188 ret = 2;
1189 goto done;
1190 }
1191
1192 if (isatty(fileno(stdout)) &&
1193 !optget(opts, "debug")->enabled &&
1194 !optget(opts, "quiet")->enabled &&
1195 !optget(opts, "infected")->enabled &&
1196 !optget(opts, "no-summary")->enabled) {
1197 /* For a space after the progress bars */
1198 logg("\n");
1199 }
1200
1201 if (optget(opts, "archive-verbose")->enabled) {
1202 cl_engine_set_clcb_meta(engine, meta);
1203 cl_engine_set_clcb_pre_cache(engine, pre);
1204 cl_engine_set_clcb_post_scan(engine, post);
1205 }
1206
1207 /* set limits */
1208
1209 /* TODO: Remove deprecated option in a future feature release */
1210 if ((opt = optget(opts, "timelimit"))->active) {
1211 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
1212 logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
1213 ret = 2;
1214 goto done;
1215 }
1216 }
1217 if ((opt = optget(opts, "max-scantime"))->active) {
1218 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
1219 logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
1220 ret = 2;
1221 goto done;
1222 }
1223 }
1224
1225 if ((opt = optget(opts, "max-scansize"))->active) {
1226 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
1227 logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
1228 ret = 2;
1229 goto done;
1230 }
1231 }
1232
1233 if ((opt = optget(opts, "max-filesize"))->active) {
1234 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
1235 logg("!cli_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
1236 ret = 2;
1237 goto done;
1238 }
1239 }
1240
1241 #ifndef _WIN32
1242 if (getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
1243 if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
1244 logg("^System limit for file size is lower than engine->maxfilesize\n");
1245 if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
1246 logg("^System limit for file size is lower than engine->maxscansize\n");
1247 } else {
1248 logg("^Cannot obtain resource limits for file size\n");
1249 }
1250 #endif
1251
1252 if ((opt = optget(opts, "max-files"))->active) {
1253 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
1254 logg("!cli_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
1255 ret = 2;
1256 goto done;
1257 }
1258 }
1259
1260 if ((opt = optget(opts, "max-recursion"))->active) {
1261 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
1262 logg("!cli_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
1263 ret = 2;
1264 goto done;
1265 }
1266 }
1267
1268 /* Engine max sizes */
1269
1270 if ((opt = optget(opts, "max-embeddedpe"))->active) {
1271 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
1272 logg("!cli_engine_set_num(CL_ENGINE_MAX_EMBEDDEDPE) failed: %s\n", cl_strerror(ret));
1273 ret = 2;
1274 goto done;
1275 }
1276 }
1277
1278 if ((opt = optget(opts, "max-htmlnormalize"))->active) {
1279 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
1280 logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNORMALIZE) failed: %s\n", cl_strerror(ret));
1281 ret = 2;
1282 goto done;
1283 }
1284 }
1285
1286 if ((opt = optget(opts, "max-htmlnotags"))->active) {
1287 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
1288 logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNOTAGS) failed: %s\n", cl_strerror(ret));
1289 ret = 2;
1290 goto done;
1291 }
1292 }
1293
1294 if ((opt = optget(opts, "max-scriptnormalize"))->active) {
1295 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
1296 logg("!cli_engine_set_num(CL_ENGINE_MAX_SCRIPTNORMALIZE) failed: %s\n", cl_strerror(ret));
1297 ret = 2;
1298 goto done;
1299 }
1300 }
1301
1302 if ((opt = optget(opts, "max-ziptypercg"))->active) {
1303 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
1304 logg("!cli_engine_set_num(CL_ENGINE_MAX_ZIPTYPERCG) failed: %s\n", cl_strerror(ret));
1305 ret = 2;
1306 goto done;
1307 }
1308 }
1309
1310 if ((opt = optget(opts, "max-partitions"))->active) {
1311 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
1312 logg("!cli_engine_set_num(CL_ENGINE_MAX_PARTITIONS) failed: %s\n", cl_strerror(ret));
1313 ret = 2;
1314 goto done;
1315 }
1316 }
1317
1318 if ((opt = optget(opts, "max-iconspe"))->active) {
1319 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
1320 logg("!cli_engine_set_num(CL_ENGINE_MAX_ICONSPE) failed: %s\n", cl_strerror(ret));
1321 ret = 2;
1322 goto done;
1323 }
1324 }
1325
1326 if ((opt = optget(opts, "max-rechwp3"))->active) {
1327 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECHWP3, opt->numarg))) {
1328 logg("!cli_engine_set_num(CL_ENGINE_MAX_RECHWP3) failed: %s\n", cl_strerror(ret));
1329 ret = 2;
1330 goto done;
1331 }
1332 }
1333
1334 if ((opt = optget(opts, "pcre-max-filesize"))->active) {
1335 if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
1336 logg("!cli_engine_set_num(CL_ENGINE_PCRE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
1337 ret = 2;
1338 goto done;
1339 }
1340 }
1341
1342 /* set scan options */
1343 if (optget(opts, "allmatch")->enabled) {
1344 options.general |= CL_SCAN_GENERAL_ALLMATCHES;
1345 }
1346
1347 /* TODO: Remove deprecated option in a future feature release */
1348 if ((optget(opts, "phishing-ssl")->enabled) ||
1349 (optget(opts, "alert-phishing-ssl")->enabled))
1350 options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
1351
1352 /* TODO: Remove deprecated option in a future feature release */
1353 if ((optget(opts, "phishing-cloak")->enabled) ||
1354 (optget(opts, "alert-phishing-cloak")->enabled))
1355 options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_CLOAK;
1356
1357 /* TODO: Remove deprecated option in a future feature release */
1358 if ((optget(opts, "partition-intersection")->enabled) ||
1359 (optget(opts, "alert-partition-intersection")->enabled))
1360 options.heuristic |= CL_SCAN_HEURISTIC_PARTITION_INTXN;
1361
1362 if (optget(opts, "heuristic-scan-precedence")->enabled)
1363 options.general |= CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
1364
1365 if (optget(opts, "scan-archive")->enabled)
1366 options.parse |= CL_SCAN_PARSE_ARCHIVE;
1367
1368 /* TODO: Remove deprecated option in a future feature release */
1369 if ((optget(opts, "detect-broken")->enabled) ||
1370 (optget(opts, "alert-broken")->enabled)) {
1371 options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
1372 }
1373
1374 if (optget(opts, "alert-broken-media")->enabled) {
1375 options.heuristic |= CL_SCAN_HEURISTIC_BROKEN_MEDIA;
1376 }
1377
1378 /* TODO: Remove deprecated option in a future feature release */
1379 if ((optget(opts, "block-encrypted")->enabled) ||
1380 (optget(opts, "alert-encrypted")->enabled)) {
1381 options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
1382 options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
1383 }
1384
1385 if (optget(opts, "alert-encrypted-archive")->enabled)
1386 options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
1387
1388 if (optget(opts, "alert-encrypted-doc")->enabled)
1389 options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
1390
1391 /* TODO: Remove deprecated option in a future feature release */
1392 if ((optget(opts, "block-macros")->enabled) ||
1393 (optget(opts, "alert-macros")->enabled)) {
1394 options.heuristic |= CL_SCAN_HEURISTIC_MACROS;
1395 }
1396
1397 if (optget(opts, "scan-pe")->enabled)
1398 options.parse |= CL_SCAN_PARSE_PE;
1399
1400 if (optget(opts, "scan-elf")->enabled)
1401 options.parse |= CL_SCAN_PARSE_ELF;
1402
1403 if (optget(opts, "scan-ole2")->enabled)
1404 options.parse |= CL_SCAN_PARSE_OLE2;
1405
1406 if (optget(opts, "scan-pdf")->enabled)
1407 options.parse |= CL_SCAN_PARSE_PDF;
1408
1409 if (optget(opts, "scan-swf")->enabled)
1410 options.parse |= CL_SCAN_PARSE_SWF;
1411
1412 if (optget(opts, "scan-html")->enabled && optget(opts, "normalize")->enabled)
1413 options.parse |= CL_SCAN_PARSE_HTML;
1414
1415 if (optget(opts, "scan-mail")->enabled)
1416 options.parse |= CL_SCAN_PARSE_MAIL;
1417
1418 if (optget(opts, "scan-xmldocs")->enabled)
1419 options.parse |= CL_SCAN_PARSE_XMLDOCS;
1420
1421 if (optget(opts, "scan-hwp3")->enabled)
1422 options.parse |= CL_SCAN_PARSE_HWP3;
1423
1424 /* TODO: Remove deprecated option in a future feature release */
1425 if ((optget(opts, "algorithmic-detection")->enabled) && /* && used due to default-yes for both options */
1426 (optget(opts, "heuristic-alerts")->enabled)) {
1427 options.general |= CL_SCAN_GENERAL_HEURISTICS;
1428 }
1429
1430 /* TODO: Remove deprecated option in a future feature release */
1431 if ((optget(opts, "block-max")->enabled) ||
1432 (optget(opts, "alert-exceeds-max")->enabled)) {
1433 options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
1434 }
1435
1436 #ifdef HAVE__INTERNAL__SHA_COLLECT
1437 if (optget(opts, "dev-collect-hashes")->enabled)
1438 options.dev |= CL_SCAN_DEV_COLLECT_SHA;
1439 #endif
1440
1441 if (optget(opts, "dev-performance")->enabled)
1442 options.dev |= CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO;
1443
1444 if (optget(opts, "detect-structured")->enabled) {
1445 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED;
1446
1447 if ((opt = optget(opts, "structured-ssn-format"))->enabled) {
1448 switch (opt->numarg) {
1449 case 0:
1450 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
1451 break;
1452 case 1:
1453 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
1454 break;
1455 case 2:
1456 options.heuristic |= (CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL | CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED);
1457 break;
1458 default:
1459 logg("!Invalid argument for --structured-ssn-format\n");
1460 ret = 2;
1461 goto done;
1462 }
1463 } else {
1464 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
1465 }
1466
1467 if ((opt = optget(opts, "structured-ssn-count"))->active) {
1468 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
1469 logg("!cli_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
1470 ret = 2;
1471 goto done;
1472 }
1473 }
1474
1475 if ((opt = optget(opts, "structured-cc-count"))->active) {
1476 if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
1477 logg("!cli_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
1478 ret = 2;
1479 goto done;
1480 }
1481 }
1482
1483 if ((opt = optget(opts, "structured-cc-mode"))->active) {
1484 switch (opt->numarg) {
1485 case 0:
1486 break;
1487 case 1:
1488 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_CC;
1489 break;
1490 default:
1491 logg("!Invalid argument for --structured-cc-mode\n");
1492 ret = 2;
1493 goto done;
1494 }
1495 }
1496 } else {
1497 options.heuristic &= ~CL_SCAN_HEURISTIC_STRUCTURED;
1498 }
1499
1500 #ifdef C_LINUX
1501 procdev = (dev_t)0;
1502 if (CLAMSTAT("/proc", &sb) != -1 && !sb.st_size)
1503 procdev = sb.st_dev;
1504 #endif
1505
1506 /* check filetype */
1507 if (!opts->filename && !optget(opts, "file-list")->enabled) {
1508 /* we need full path for some reasons (eg. archive handling) */
1509 if (!getcwd(cwd, sizeof(cwd))) {
1510 logg("!Can't get absolute pathname of current working directory\n");
1511 ret = 2;
1512 } else {
1513 CLAMSTAT(cwd, &sb);
1514 scandirs(cwd, engine, opts, &options, 1, sb.st_dev);
1515 }
1516
1517 } else if (opts->filename && !optget(opts, "file-list")->enabled && !strcmp(opts->filename[0], "-")) { /* read data from stdin */
1518 ret = scanstdin(engine, &options);
1519 } else {
1520 if (opts->filename && optget(opts, "file-list")->enabled)
1521 logg("^Only scanning files from --file-list (files passed at cmdline are ignored)\n");
1522
1523 while ((filename = filelist(opts, &ret)) && (file = strdup(filename))) {
1524 if (LSTAT(file, &sb) == -1) {
1525 perror(file);
1526 logg("^%s: Can't access file\n", file);
1527 ret = 2;
1528 } else {
1529 for (i = strlen(file) - 1; i > 0; i--) {
1530 if (file[i] == *PATHSEP)
1531 file[i] = 0;
1532 else
1533 break;
1534 }
1535
1536 if (S_ISLNK(sb.st_mode)) {
1537 if (dirlnk == 0 && filelnk == 0) {
1538 if (!printinfected)
1539 logg("%s: Symbolic link\n", file);
1540 } else if (CLAMSTAT(file, &sb) != -1) {
1541 if (S_ISREG(sb.st_mode) && filelnk) {
1542 scanfile(file, engine, opts, &options);
1543 } else if (S_ISDIR(sb.st_mode) && dirlnk) {
1544 scandirs(file, engine, opts, &options, 1, sb.st_dev);
1545 } else {
1546 if (!printinfected)
1547 logg("%s: Symbolic link\n", file);
1548 }
1549 }
1550 } else if (S_ISREG(sb.st_mode)) {
1551 scanfile(file, engine, opts, &options);
1552 } else if (S_ISDIR(sb.st_mode)) {
1553 scandirs(file, engine, opts, &options, 1, sb.st_dev);
1554 } else {
1555 logg("^%s: Not supported file type\n", file);
1556 ret = 2;
1557 }
1558 }
1559
1560 free(file);
1561 }
1562 }
1563
1564 if ((opt = optget(opts, "statistics"))->enabled) {
1565 while (opt) {
1566 if (!strcasecmp(opt->strarg, "bytecode")) {
1567 cli_sigperf_print();
1568 cli_sigperf_events_destroy();
1569 }
1570 #if HAVE_PCRE
1571 else if (!strcasecmp(opt->strarg, "pcre")) {
1572 cli_pcre_perf_print();
1573 cli_pcre_perf_events_destroy();
1574 }
1575 #endif
1576 opt = opt->nextarg;
1577 }
1578 }
1579
1580 done:
1581 /* free the engine */
1582 cl_engine_free(engine);
1583
1584 /* overwrite return code - infection takes priority */
1585 if (info.ifiles)
1586 ret = 1;
1587 else if (info.errors)
1588 ret = 2;
1589
1590 return ret;
1591 }
1592