--max-scantime replaces the --timelimit clamscan option that had been experimental.
Default max-scantime set to 2 minutes (120000 milliseconds).
... | ... |
@@ -1,13 +1,39 @@ |
1 | 1 |
# ClamAV News |
2 | 2 |
|
3 | 3 |
Note: This file refers to the source tarball. Things described here may differ |
4 |
- slight |
|
4 |
+ slightly from the binary packages. |
|
5 | 5 |
|
6 | 6 |
## 0.101.4 |
7 | 7 |
|
8 |
-An out of bounds write was possible within ClamAV's NSIS bzip2 library when attempting decompression in cases where the number of selectors exceeded the max limit set by the library (CVE-2019-12900). The issue has been resolved by respecting that limit. |
|
8 |
+ClamAV 0.101.4 is a security patch release that addresses the following issues. |
|
9 | 9 |
|
10 |
-Thanks to Martin Simmons for reporting the issue [here](https://bugzilla.clamav.net/show_bug.cgi?id=12371) |
|
10 |
+- An out of bounds write was possible within ClamAV's NSIS bzip2 library when |
|
11 |
+ attempting decompression in cases where the number of selectors exceeded the |
|
12 |
+ max limit set by the library (CVE-2019-12900). The issue has been resolved |
|
13 |
+ by respecting that limit. |
|
14 |
+ |
|
15 |
+ Thanks to Martin Simmons for reporting the issue [here](https://bugzilla.clamav.net/show_bug.cgi?id=12371) |
|
16 |
+ |
|
17 |
+- A workaround for the zip-bomb vulnerability patch found in 0.101.3 was |
|
18 |
+ identified. To remediate future denial of service conditions caused by |
|
19 |
+ excessive scan times, a scan time limit has been introduced. |
|
20 |
+ |
|
21 |
+ The default value is 2 minutes (120000 milliseconds). |
|
22 |
+ |
|
23 |
+ To customize the time limit: |
|
24 |
+ |
|
25 |
+ - use the `clamscan` `--max-scantime` option |
|
26 |
+ - use the `clamd` `MaxScanTime` config option |
|
27 |
+ |
|
28 |
+ Libclamav users may customize the time limit using the `cl_engine_set_num` |
|
29 |
+ function. For example: |
|
30 |
+ |
|
31 |
+ ```c |
|
32 |
+ cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, time_limit_milliseconds) |
|
33 |
+ ``` |
|
34 |
+ |
|
35 |
+ Thanks to David Fifield for reviewing the zip-bomb mitigation in 0.101.3 |
|
36 |
+ and reporting the issue. |
|
11 | 37 |
|
12 | 38 |
## 0.101.3 |
13 | 39 |
|
... | ... |
@@ -88,7 +88,7 @@ static void scanner_thread(void *arg) |
88 | 88 |
#ifndef _WIN32 |
89 | 89 |
/* ignore all signals */ |
90 | 90 |
sigfillset(&sigset); |
91 |
- /* The behavior of a process is undefined after it ignores a |
|
91 |
+ /* The behavior of a process is undefined after it ignores a |
|
92 | 92 |
* SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ |
93 | 93 |
sigdelset(&sigset, SIGFPE); |
94 | 94 |
sigdelset(&sigset, SIGILL); |
... | ... |
@@ -552,7 +552,7 @@ static const char* parse_dispatch_cmd(client_conn_t *conn, struct fd_buf *buf, s |
552 | 552 |
/* no more commands are accepted */ |
553 | 553 |
conn->mode = MODE_WAITREPLY; |
554 | 554 |
/* Stop monitoring this FD, it will be closed either |
555 |
- * by us, or by the scanner thread. |
|
555 |
+ * by us, or by the scanner thread. |
|
556 | 556 |
* Never close a file descriptor that is being |
557 | 557 |
* monitored by poll()/select() from another thread, |
558 | 558 |
* because this can lead to subtle bugs such as: |
... | ... |
@@ -631,7 +631,7 @@ static int handle_stream(client_conn_t *conn, struct fd_buf *buf, const struct o |
631 | 631 |
int rc; |
632 | 632 |
size_t pos = *ppos; |
633 | 633 |
size_t cmdlen; |
634 |
- |
|
634 |
+ |
|
635 | 635 |
logg("$mode == MODE_STREAM\n"); |
636 | 636 |
/* we received some data, set readtimeout */ |
637 | 637 |
time(&buf->timeout_at); |
... | ... |
@@ -754,12 +754,25 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
754 | 754 |
memset(&options, 0, sizeof(struct cl_scan_options)); |
755 | 755 |
|
756 | 756 |
/* set up limits */ |
757 |
- if((opt = optget(opts, "MaxScanSize"))->active) { |
|
758 |
- if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) { |
|
759 |
- logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret)); |
|
760 |
- cl_engine_free(engine); |
|
761 |
- return 1; |
|
762 |
- } |
|
757 |
+ if ((opt = optget(opts, "MaxScanTime"))->active) { |
|
758 |
+ if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) { |
|
759 |
+ logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret)); |
|
760 |
+ cl_engine_free(engine); |
|
761 |
+ return 1; |
|
762 |
+ } |
|
763 |
+ } |
|
764 |
+ val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANTIME, NULL); |
|
765 |
+ if (val) |
|
766 |
+ logg("Limits: Global time limit set to %llu milliseconds.\n", val); |
|
767 |
+ else |
|
768 |
+ logg("^Limits: Global time limit protection disabled.\n"); |
|
769 |
+ |
|
770 |
+ if ((opt = optget(opts, "MaxScanSize"))->active) { |
|
771 |
+ if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) { |
|
772 |
+ logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret)); |
|
773 |
+ cl_engine_free(engine); |
|
774 |
+ return 1; |
|
775 |
+ } |
|
763 | 776 |
} |
764 | 777 |
val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL); |
765 | 778 |
if(val) |
... | ... |
@@ -1016,7 +1029,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1016 | 1016 |
|
1017 | 1017 |
/* TODO: Remove deprecated option in a future feature release */ |
1018 | 1018 |
if (optget(opts, "ScanPE")->enabled || optget(opts, "ScanELF")->enabled) { |
1019 |
- if ((optget(opts, "DetectBrokenExecutables")->enabled) || |
|
1019 |
+ if ((optget(opts, "DetectBrokenExecutables")->enabled) || |
|
1020 | 1020 |
(optget(opts, "AlertBrokenExecutables")->enabled)) { |
1021 | 1021 |
logg("Alerting on broken executables enabled.\n"); |
1022 | 1022 |
options.heuristic |= CL_SCAN_HEURISTIC_BROKEN; |
... | ... |
@@ -1039,7 +1052,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1039 | 1039 |
if (optget(opts, "ScanOLE2")->enabled) { |
1040 | 1040 |
logg("OLE2 support enabled.\n"); |
1041 | 1041 |
options.parse |= CL_SCAN_PARSE_OLE2; |
1042 |
- |
|
1042 |
+ |
|
1043 | 1043 |
/* TODO: Remove deprecated option in a future feature release */ |
1044 | 1044 |
if ((optget(opts, "OLE2BlockMacros")->enabled) || |
1045 | 1045 |
(optget(opts, "AlertOLE2Macros")->enabled)) { |
... | ... |
@@ -1187,7 +1200,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1187 | 1187 |
int solaris_has_extended_stdio = 0; |
1188 | 1188 |
#endif |
1189 | 1189 |
/* Condition to not run out of file descriptors: |
1190 |
- * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE |
|
1190 |
+ * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE |
|
1191 | 1191 |
* CLAMDFILES is 6: 3 standard FD + logfile + 2 FD for reloading the DB |
1192 | 1192 |
* */ |
1193 | 1193 |
#ifdef C_SOLARIS |
... | ... |
@@ -1314,12 +1327,12 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1314 | 1314 |
sigdelset(&sigset, SIGHUP); |
1315 | 1315 |
sigdelset(&sigset, SIGPIPE); |
1316 | 1316 |
sigdelset(&sigset, SIGUSR2); |
1317 |
- /* The behavior of a process is undefined after it ignores a |
|
1317 |
+ /* The behavior of a process is undefined after it ignores a |
|
1318 | 1318 |
* SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ |
1319 | 1319 |
sigdelset(&sigset, SIGFPE); |
1320 | 1320 |
sigdelset(&sigset, SIGILL); |
1321 | 1321 |
sigdelset(&sigset, SIGSEGV); |
1322 |
-#ifdef SIGBUS |
|
1322 |
+#ifdef SIGBUS |
|
1323 | 1323 |
sigdelset(&sigset, SIGBUS); |
1324 | 1324 |
#endif |
1325 | 1325 |
sigdelset(&sigset, SIGTSTP); |
... | ... |
@@ -1663,4 +1676,4 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1663 | 1663 |
logg("--- Stopped at %s", cli_ctime(¤t_time, timestr, sizeof(timestr))); |
1664 | 1664 |
|
1665 | 1665 |
return ret; |
1666 |
-} |
|
1666 |
+} |
... | ... |
@@ -145,7 +145,7 @@ int main(int argc, char **argv) |
145 | 145 |
optfree(opts); |
146 | 146 |
return 2; |
147 | 147 |
} |
148 |
- } else |
|
148 |
+ } else |
|
149 | 149 |
logg_file = NULL; |
150 | 150 |
|
151 | 151 |
if(actsetup(opts)) { |
... | ... |
@@ -277,6 +277,7 @@ void help(void) |
277 | 277 |
mprintf(" --nocerts Disable authenticode certificate chain verification in PE files\n"); |
278 | 278 |
mprintf(" --dumpcerts Dump authenticode certificate chain in PE files\n"); |
279 | 279 |
mprintf("\n"); |
280 |
+ mprintf(" --max-scantime=#n Scan time longer than this will be skipped and assumed clean\n"); |
|
280 | 281 |
mprintf(" --max-filesize=#n Files larger than this will be skipped and assumed clean\n"); |
281 | 282 |
mprintf(" --max-scansize=#n The maximum amount of data to scan for each container file (**)\n"); |
282 | 283 |
mprintf(" --max-files=#n The maximum number of files to scan for each container file (**)\n"); |
... | ... |
@@ -340,7 +340,7 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc |
340 | 340 |
|
341 | 341 |
return; |
342 | 342 |
} |
343 |
-#endif |
|
343 |
+#endif |
|
344 | 344 |
if(!sb.st_size) { |
345 | 345 |
if(!printinfected) |
346 | 346 |
logg("~%s: Empty file\n", filename); |
... | ... |
@@ -674,7 +674,7 @@ int scanmanager(const struct optstruct *opts) |
674 | 674 |
} |
675 | 675 |
|
676 | 676 |
cl_engine_set_clcb_virus_found(engine, clamscan_virus_found_cb); |
677 |
- |
|
677 |
+ |
|
678 | 678 |
if (optget(opts, "disable-cache")->enabled) |
679 | 679 |
cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1); |
680 | 680 |
|
... | ... |
@@ -873,6 +873,24 @@ int scanmanager(const struct optstruct *opts) |
873 | 873 |
|
874 | 874 |
/* set limits */ |
875 | 875 |
|
876 |
+ /* TODO: Remove deprecated option in a future feature release */ |
|
877 |
+ if ((opt = optget(opts, "timelimit"))->active) { |
|
878 |
+ if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) { |
|
879 |
+ logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret)); |
|
880 |
+ |
|
881 |
+ cl_engine_free(engine); |
|
882 |
+ return 2; |
|
883 |
+ } |
|
884 |
+ } |
|
885 |
+ if ((opt = optget(opts, "max-scantime"))->active) { |
|
886 |
+ if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) { |
|
887 |
+ logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret)); |
|
888 |
+ |
|
889 |
+ cl_engine_free(engine); |
|
890 |
+ return 2; |
|
891 |
+ } |
|
892 |
+ } |
|
893 |
+ |
|
876 | 894 |
if((opt = optget(opts, "max-scansize"))->active) { |
877 | 895 |
if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) { |
878 | 896 |
logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret)); |
... | ... |
@@ -994,15 +1012,6 @@ int scanmanager(const struct optstruct *opts) |
994 | 994 |
} |
995 | 995 |
} |
996 | 996 |
|
997 |
- if ((opt = optget(opts, "timelimit"))->active) { |
|
998 |
- if ((ret = cl_engine_set_num(engine, CL_ENGINE_TIME_LIMIT, opt->numarg))) { |
|
999 |
- logg("!cli_engine_set_num(CL_ENGINE_TIME_LIMIT) failed: %s\n", cl_strerror(ret)); |
|
1000 |
- |
|
1001 |
- cl_engine_free(engine); |
|
1002 |
- return 2; |
|
1003 |
- } |
|
1004 |
- } |
|
1005 |
- |
|
1006 | 997 |
if ((opt = optget(opts, "pcre-max-filesize"))->active) { |
1007 | 998 |
if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) { |
1008 | 999 |
logg("!cli_engine_set_num(CL_ENGINE_PCRE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret)); |
... | ... |
@@ -1038,7 +1047,7 @@ int scanmanager(const struct optstruct *opts) |
1038 | 1038 |
options.parse |= CL_SCAN_PARSE_ARCHIVE; |
1039 | 1039 |
|
1040 | 1040 |
/* TODO: Remove deprecated option in a future feature release */ |
1041 |
- if ((optget(opts, "detect-broken")->enabled) || |
|
1041 |
+ if ((optget(opts, "detect-broken")->enabled) || |
|
1042 | 1042 |
(optget(opts, "alert-broken")->enabled)) { |
1043 | 1043 |
options.heuristic |= CL_SCAN_HEURISTIC_BROKEN; |
1044 | 1044 |
} |
... | ... |
@@ -1096,7 +1105,7 @@ int scanmanager(const struct optstruct *opts) |
1096 | 1096 |
} |
1097 | 1097 |
|
1098 | 1098 |
/* TODO: Remove deprecated option in a future feature release */ |
1099 |
- if ((optget(opts, "block-max")->enabled) || |
|
1099 |
+ if ((optget(opts, "block-max")->enabled) || |
|
1100 | 1100 |
(optget(opts, "alert-exceeds-max")->enabled)) { |
1101 | 1101 |
options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX; |
1102 | 1102 |
} |
... | ... |
@@ -85,7 +85,7 @@ Example |
85 | 85 |
# Default: no |
86 | 86 |
#OfficialDatabaseOnly no |
87 | 87 |
|
88 |
-# The daemon can work in local mode, network mode or both. |
|
88 |
+# The daemon can work in local mode, network mode or both. |
|
89 | 89 |
# Due to security reasons we recommend the local mode. |
90 | 90 |
|
91 | 91 |
# Path to a local socket file the daemon will listen on. |
... | ... |
@@ -231,7 +231,7 @@ Example |
231 | 231 |
#DetectPUA yes |
232 | 232 |
|
233 | 233 |
# Exclude a specific PUA category. This directive can be used multiple times. |
234 |
-# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for |
|
234 |
+# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for |
|
235 | 235 |
# the complete list of PUA categories. |
236 | 236 |
# Default: Load all categories (if DetectPUA is activated) |
237 | 237 |
#ExcludePUA NetTool |
... | ... |
@@ -271,9 +271,9 @@ Example |
271 | 271 |
# the end of a scan. If an archive contains both a heuristically detected |
272 | 272 |
# virus/phish, and a real malware, the real malware will be reported |
273 | 273 |
# |
274 |
-# Keep this disabled if you intend to handle "*.Heuristics.*" viruses |
|
274 |
+# Keep this disabled if you intend to handle "*.Heuristics.*" viruses |
|
275 | 275 |
# differently from "real" malware. |
276 |
-# If a non-heuristically-detected virus (signature-based) is found first, |
|
276 |
+# If a non-heuristically-detected virus (signature-based) is found first, |
|
277 | 277 |
# the scan is interrupted immediately, regardless of this config option. |
278 | 278 |
# |
279 | 279 |
# Default: no |
... | ... |
@@ -475,6 +475,16 @@ Example |
475 | 475 |
# The options below protect your system against Denial of Service attacks |
476 | 476 |
# using archive bombs. |
477 | 477 |
|
478 |
+# This option sets the maximum amount of time to a scan may take. |
|
479 |
+# In this version, this field only affects the scan time of ZIP archives. |
|
480 |
+# Value of 0 disables the limit |
|
481 |
+# Note: disabling this limit or setting it too high may result allow scanning |
|
482 |
+# of certain files to lock up the scanning process/threads resulting in a Denial |
|
483 |
+# of Service. |
|
484 |
+# Time is in milliseconds. |
|
485 |
+# Default: 120000 |
|
486 |
+#MaxScanTime 300000 |
|
487 |
+ |
|
478 | 488 |
# This option sets the maximum amount of data to be scanned for each input |
479 | 489 |
# file. |
480 | 490 |
# Archives and other containers are recursively extracted and scanned up to |
... | ... |
@@ -697,7 +707,7 @@ Example |
697 | 697 |
## Bytecode |
698 | 698 |
## |
699 | 699 |
|
700 |
-# With this option enabled ClamAV will load bytecode from the database. |
|
700 |
+# With this option enabled ClamAV will load bytecode from the database. |
|
701 | 701 |
# It is highly recommended you keep this option on, otherwise you'll miss |
702 | 702 |
# detections for many new viruses. |
703 | 703 |
# Default: yes |
... | ... |
@@ -721,7 +731,7 @@ Example |
721 | 721 |
#BytecodeSecurity TrustSigned |
722 | 722 |
|
723 | 723 |
# Set bytecode timeout in milliseconds. |
724 |
-# |
|
724 |
+# |
|
725 | 725 |
# Default: 5000 |
726 | 726 |
# BytecodeTimeout 1000 |
727 | 727 |
|
... | ... |
@@ -298,7 +298,7 @@ enum cl_engine_field { |
298 | 298 |
CL_ENGINE_MAX_PARTITIONS, /* uint32_t */ |
299 | 299 |
CL_ENGINE_MAX_ICONSPE, /* uint32_t */ |
300 | 300 |
CL_ENGINE_MAX_RECHWP3, /* uint32_t */ |
301 |
- CL_ENGINE_TIME_LIMIT, /* uint32_t */ |
|
301 |
+ CL_ENGINE_MAX_SCANTIME, /* uint32_t */ |
|
302 | 302 |
CL_ENGINE_PCRE_MATCH_LIMIT, /* uint64_t */ |
303 | 303 |
CL_ENGINE_PCRE_RECMATCH_LIMIT, /* uint64_t */ |
304 | 304 |
CL_ENGINE_PCRE_MAX_FILESIZE, /* uint64_t */ |
... | ... |
@@ -687,6 +687,12 @@ int cli_pcre_scanbuf(const unsigned char *buffer, uint32_t length, const char ** |
687 | 687 |
|
688 | 688 |
/* if the global flag is set, loop through the scanning */ |
689 | 689 |
do { |
690 |
+ if (cli_checktimelimit(ctx) != CL_SUCCESS) { |
|
691 |
+ cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime); |
|
692 |
+ ret = CL_ETIMEOUT; |
|
693 |
+ break; |
|
694 |
+ } |
|
695 |
+ |
|
690 | 696 |
/* reset the match results */ |
691 | 697 |
if ((ret = cli_pcre_results_reset(&p_res, pd)) != CL_SUCCESS) |
692 | 698 |
break; |
... | ... |
@@ -262,7 +262,7 @@ const char *cl_strerror(int clerror) |
262 | 262 |
case CL_EMEM: |
263 | 263 |
return "Can't allocate memory"; |
264 | 264 |
case CL_ETIMEOUT: |
265 |
- return "Time limit reached"; |
|
265 |
+ return "CL_ETIMEOUT: Time limit reached"; |
|
266 | 266 |
/* internal (needed for debug messages) */ |
267 | 267 |
case CL_EMAXREC: |
268 | 268 |
return "CL_EMAXREC"; |
... | ... |
@@ -324,6 +324,7 @@ struct cl_engine *cl_engine_new(void) |
324 | 324 |
} |
325 | 325 |
|
326 | 326 |
/* Setup default limits */ |
327 |
+ new->maxscantime = CLI_DEFAULT_MAXSCANTIME; |
|
327 | 328 |
new->maxscansize = CLI_DEFAULT_MAXSCANSIZE; |
328 | 329 |
new->maxfilesize = CLI_DEFAULT_MAXFILESIZE; |
329 | 330 |
new->maxreclevel = CLI_DEFAULT_MAXRECLEVEL; |
... | ... |
@@ -616,9 +617,9 @@ int cl_engine_set_num(struct cl_engine *engine, enum cl_engine_field field, long |
616 | 616 |
case CL_ENGINE_MAX_RECHWP3: |
617 | 617 |
engine->maxrechwp3 = (uint32_t)num; |
618 | 618 |
break; |
619 |
- case CL_ENGINE_TIME_LIMIT: |
|
620 |
- engine->time_limit = (uint32_t)num; |
|
621 |
- break; |
|
619 |
+ case CL_ENGINE_MAX_SCANTIME: |
|
620 |
+ engine->maxscantime = (uint32_t)num; |
|
621 |
+ break; |
|
622 | 622 |
case CL_ENGINE_PCRE_MATCH_LIMIT: |
623 | 623 |
engine->pcre_match_limit = (uint64_t)num; |
624 | 624 |
break; |
... | ... |
@@ -717,8 +718,8 @@ long long cl_engine_get_num(const struct cl_engine *engine, enum cl_engine_field |
717 | 717 |
return engine->maxiconspe; |
718 | 718 |
case CL_ENGINE_MAX_RECHWP3: |
719 | 719 |
return engine->maxrechwp3; |
720 |
- case CL_ENGINE_TIME_LIMIT: |
|
721 |
- return engine->time_limit; |
|
720 |
+ case CL_ENGINE_MAX_SCANTIME: |
|
721 |
+ return engine->maxscantime; |
|
722 | 722 |
case CL_ENGINE_PCRE_MATCH_LIMIT: |
723 | 723 |
return engine->pcre_match_limit; |
724 | 724 |
case CL_ENGINE_PCRE_RECMATCH_LIMIT: |
... | ... |
@@ -798,6 +799,7 @@ struct cl_settings *cl_engine_settings_copy(const struct cl_engine *engine) |
798 | 798 |
settings->ac_maxdepth = engine->ac_maxdepth; |
799 | 799 |
settings->tmpdir = engine->tmpdir ? strdup(engine->tmpdir) : NULL; |
800 | 800 |
settings->keeptmp = engine->keeptmp; |
801 |
+ settings->maxscantime = engine->maxscantime; |
|
801 | 802 |
settings->maxscansize = engine->maxscansize; |
802 | 803 |
settings->maxfilesize = engine->maxfilesize; |
803 | 804 |
settings->maxreclevel = engine->maxreclevel; |
... | ... |
@@ -852,6 +854,7 @@ int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings |
852 | 852 |
engine->ac_mindepth = settings->ac_mindepth; |
853 | 853 |
engine->ac_maxdepth = settings->ac_maxdepth; |
854 | 854 |
engine->keeptmp = settings->keeptmp; |
855 |
+ engine->maxscantime = settings->maxscantime; |
|
855 | 856 |
engine->maxscansize = settings->maxscansize; |
856 | 857 |
engine->maxfilesize = settings->maxfilesize; |
857 | 858 |
engine->maxreclevel = settings->maxreclevel; |
... | ... |
@@ -940,8 +943,9 @@ void cli_check_blockmax(cli_ctx *ctx, int rc) |
940 | 940 |
} |
941 | 941 |
} |
942 | 942 |
|
943 |
-int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3) { |
|
944 |
- int ret = CL_SUCCESS; |
|
943 |
+cl_error_t cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3) |
|
944 |
+{ |
|
945 |
+ cl_error_t ret = CL_SUCCESS; |
|
945 | 946 |
unsigned long needed; |
946 | 947 |
|
947 | 948 |
/* if called without limits, go on, unpack, scan */ |
... | ... |
@@ -950,6 +954,9 @@ int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned |
950 | 950 |
needed = (need1>need2)?need1:need2; |
951 | 951 |
needed = (needed>need3)?needed:need3; |
952 | 952 |
|
953 |
+ /* Enforce timelimit */ |
|
954 |
+ ret = cli_checktimelimit(ctx); |
|
955 |
+ |
|
953 | 956 |
/* if we have global scan limits */ |
954 | 957 |
if(needed && ctx->engine->maxscansize) { |
955 | 958 |
/* if the remaining scansize is too small... */ |
... | ... |
@@ -978,8 +985,9 @@ int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned |
978 | 978 |
return ret; |
979 | 979 |
} |
980 | 980 |
|
981 |
-int cli_updatelimits(cli_ctx *ctx, unsigned long needed) { |
|
982 |
- int ret=cli_checklimits("cli_updatelimits", ctx, needed, 0, 0); |
|
981 |
+cl_error_t cli_updatelimits(cli_ctx *ctx, unsigned long needed) |
|
982 |
+{ |
|
983 |
+ cl_error_t ret = cli_checklimits("cli_updatelimits", ctx, needed, 0, 0); |
|
983 | 984 |
|
984 | 985 |
if (ret != CL_CLEAN) return ret; |
985 | 986 |
ctx->scannedfiles++; |
... | ... |
@@ -989,18 +997,33 @@ int cli_updatelimits(cli_ctx *ctx, unsigned long needed) { |
989 | 989 |
return CL_CLEAN; |
990 | 990 |
} |
991 | 991 |
|
992 |
-int cli_checktimelimit(cli_ctx *ctx) |
|
992 |
+/** |
|
993 |
+ * @brief Check if we've exceeded the time limit. |
|
994 |
+ * If ctx is NULL, there can be no timelimit so just return success. |
|
995 |
+ * |
|
996 |
+ * @param ctx The scanning context. |
|
997 |
+ * @return cl_error_t CL_SUCCESS if has not exceeded, CL_ETIMEOUT if has exceeded. |
|
998 |
+ */ |
|
999 |
+cl_error_t cli_checktimelimit(cli_ctx *ctx) |
|
993 | 1000 |
{ |
1001 |
+ cl_error_t ret = CL_SUCCESS; |
|
1002 |
+ |
|
1003 |
+ if (NULL == ctx) { |
|
1004 |
+ goto done; |
|
1005 |
+ } |
|
1006 |
+ |
|
994 | 1007 |
if (ctx->time_limit.tv_sec != 0) { |
995 | 1008 |
struct timeval now; |
996 | 1009 |
if (gettimeofday(&now, NULL) == 0) { |
997 |
- if (now.tv_sec < ctx->time_limit.tv_sec) |
|
998 |
- return CL_SUCCESS; |
|
999 |
- if (now.tv_sec > ctx->time_limit.tv_sec || now.tv_usec > ctx->time_limit.tv_usec) |
|
1000 |
- return CL_ETIMEOUT; |
|
1010 |
+ if (now.tv_sec > ctx->time_limit.tv_sec) |
|
1011 |
+ ret = CL_ETIMEOUT; |
|
1012 |
+ else if (now.tv_sec == ctx->time_limit.tv_sec && now.tv_usec > ctx->time_limit.tv_usec) |
|
1013 |
+ ret = CL_ETIMEOUT; |
|
1001 | 1014 |
} |
1002 | 1015 |
} |
1003 |
- return CL_SUCCESS; |
|
1016 |
+ |
|
1017 |
+done: |
|
1018 |
+ return ret; |
|
1004 | 1019 |
} |
1005 | 1020 |
|
1006 | 1021 |
/* |
... | ... |
@@ -1078,7 +1101,7 @@ int cli_unlink(const char* pathname) |
1078 | 1078 |
{ |
1079 | 1079 |
if (unlink(pathname) == -1) { |
1080 | 1080 |
#ifdef _WIN32 |
1081 |
- /* Windows may fail to unlink a file if it is marked read-only, |
|
1081 |
+ /* Windows may fail to unlink a file if it is marked read-only, |
|
1082 | 1082 |
* even if the user has permissions to delete the file. */ |
1083 | 1083 |
if (-1 == _chmod(pathname, _S_IWRITE)) { |
1084 | 1084 |
char err[128]; |
... | ... |
@@ -1105,7 +1128,7 @@ void cli_virus_found_cb(cli_ctx * ctx) |
1105 | 1105 |
ctx->engine->cb_virus_found(fmap_fd(*ctx->fmap), (const char *)*ctx->virname, ctx->cb_ctx); |
1106 | 1106 |
} |
1107 | 1107 |
|
1108 |
-int cli_append_possibly_unwanted(cli_ctx * ctx, const char * virname) |
|
1108 |
+cl_error_t cli_append_possibly_unwanted(cli_ctx *ctx, const char *virname) |
|
1109 | 1109 |
{ |
1110 | 1110 |
if (SCAN_ALLMATCHES) |
1111 | 1111 |
return cli_append_virus(ctx, virname); |
... | ... |
@@ -1128,7 +1151,7 @@ int cli_append_virus(cli_ctx * ctx, const char * virname) |
1128 | 1128 |
if (!SCAN_ALLMATCHES && ctx->num_viruses != 0) |
1129 | 1129 |
if (SCAN_HEURISTIC_PRECEDENCE) |
1130 | 1130 |
return CL_CLEAN; |
1131 |
- if (ctx->limit_exceeded == 0 || SCAN_ALLMATCHES) { |
|
1131 |
+ if (ctx->limit_exceeded == 0 || SCAN_ALLMATCHES) { |
|
1132 | 1132 |
ctx->num_viruses++; |
1133 | 1133 |
*ctx->virname = virname; |
1134 | 1134 |
cli_virus_found_cb(ctx); |
... | ... |
@@ -1225,7 +1248,7 @@ int |
1225 | 1225 |
cli_rmdirs(const char *name) |
1226 | 1226 |
{ |
1227 | 1227 |
int rc; |
1228 |
- STATBUF statb; |
|
1228 |
+ STATBUF statb; |
|
1229 | 1229 |
DIR *dd; |
1230 | 1230 |
struct dirent *dent; |
1231 | 1231 |
#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2) |
... | ... |
@@ -1288,7 +1311,7 @@ cli_rmdirs(const char *name) |
1288 | 1288 |
return -1; |
1289 | 1289 |
} |
1290 | 1290 |
|
1291 |
- return rc; |
|
1291 |
+ return rc; |
|
1292 | 1292 |
} |
1293 | 1293 |
#else |
1294 | 1294 |
int cli_rmdirs(const char *dirname) |
... | ... |
@@ -1367,7 +1390,7 @@ int cli_rmdirs(const char *dirname) |
1367 | 1367 |
rewinddir(dd); |
1368 | 1368 |
} |
1369 | 1369 |
|
1370 |
- } else { |
|
1370 |
+ } else { |
|
1371 | 1371 |
return -1; |
1372 | 1372 |
} |
1373 | 1373 |
|
... | ... |
@@ -1397,7 +1420,7 @@ static unsigned long nearest_power(unsigned long num) |
1397 | 1397 |
bitset_t *cli_bitset_init(void) |
1398 | 1398 |
{ |
1399 | 1399 |
bitset_t *bs; |
1400 |
- |
|
1400 |
+ |
|
1401 | 1401 |
bs = cli_malloc(sizeof(bitset_t)); |
1402 | 1402 |
if (!bs) { |
1403 | 1403 |
cli_errmsg("cli_bitset_init: Unable to allocate memory for bs %llu\n", (long long unsigned)sizeof(bitset_t)); |
... | ... |
@@ -1428,7 +1451,7 @@ static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size) |
1428 | 1428 |
{ |
1429 | 1429 |
unsigned long new_length; |
1430 | 1430 |
unsigned char *new_bitset; |
1431 |
- |
|
1431 |
+ |
|
1432 | 1432 |
new_length = nearest_power(min_size); |
1433 | 1433 |
new_bitset = (unsigned char *) cli_realloc(bs->bitset, new_length); |
1434 | 1434 |
if (!new_bitset) { |
... | ... |
@@ -1443,7 +1466,7 @@ static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size) |
1443 | 1443 |
int cli_bitset_set(bitset_t *bs, unsigned long bit_offset) |
1444 | 1444 |
{ |
1445 | 1445 |
unsigned long char_offset; |
1446 |
- |
|
1446 |
+ |
|
1447 | 1447 |
char_offset = bit_offset / BITS_PER_CHAR; |
1448 | 1448 |
bit_offset = bit_offset % BITS_PER_CHAR; |
1449 | 1449 |
|
... | ... |
@@ -1460,11 +1483,11 @@ int cli_bitset_set(bitset_t *bs, unsigned long bit_offset) |
1460 | 1460 |
int cli_bitset_test(bitset_t *bs, unsigned long bit_offset) |
1461 | 1461 |
{ |
1462 | 1462 |
unsigned long char_offset; |
1463 |
- |
|
1463 |
+ |
|
1464 | 1464 |
char_offset = bit_offset / BITS_PER_CHAR; |
1465 | 1465 |
bit_offset = bit_offset % BITS_PER_CHAR; |
1466 | 1466 |
|
1467 |
- if (char_offset >= bs->length) { |
|
1467 |
+ if (char_offset >= bs->length) { |
|
1468 | 1468 |
return FALSE; |
1469 | 1469 |
} |
1470 | 1470 |
return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset)); |
... | ... |
@@ -286,6 +286,7 @@ struct cl_engine { |
286 | 286 |
uint64_t engine_options; |
287 | 287 |
|
288 | 288 |
/* Limits */ |
289 |
+ uint32_t maxscantime; /* Time limit (in milliseconds) */ |
|
289 | 290 |
uint64_t maxscansize; /* during the scanning of archives this size |
290 | 291 |
* will never be exceeded |
291 | 292 |
*/ |
... | ... |
@@ -405,9 +406,6 @@ struct cl_engine { |
405 | 405 |
uint32_t maxiconspe; /* max number of icons to scan for PE */ |
406 | 406 |
uint32_t maxrechwp3; /* max recursive calls for HWP3 parsing */ |
407 | 407 |
|
408 |
- /* millisecond time limit for preclassification scanning */ |
|
409 |
- uint32_t time_limit; |
|
410 |
- |
|
411 | 408 |
/* PCRE matching limitations */ |
412 | 409 |
uint64_t pcre_match_limit; |
413 | 410 |
uint64_t pcre_recmatch_limit; |
... | ... |
@@ -429,6 +427,7 @@ struct cl_settings { |
429 | 429 |
uint32_t ac_maxdepth; |
430 | 430 |
char *tmpdir; |
431 | 431 |
uint32_t keeptmp; |
432 |
+ uint32_t maxscantime; |
|
432 | 433 |
uint64_t maxscansize; |
433 | 434 |
uint64_t maxfilesize; |
434 | 435 |
uint32_t maxreclevel; |
... | ... |
@@ -811,21 +810,20 @@ cl_error_t cli_gentempfd_with_prefix(const char* dir, char* prefix, char** name, |
811 | 811 |
|
812 | 812 |
unsigned int cli_rndnum(unsigned int max); |
813 | 813 |
int cli_filecopy(const char *src, const char *dest); |
814 |
-int cli_mapscan(fmap_t *map, off_t offset, size_t size, cli_ctx *ctx, cli_file_t type); |
|
815 | 814 |
bitset_t *cli_bitset_init(void); |
816 | 815 |
void cli_bitset_free(bitset_t *bs); |
817 | 816 |
int cli_bitset_set(bitset_t *bs, unsigned long bit_offset); |
818 | 817 |
int cli_bitset_test(bitset_t *bs, unsigned long bit_offset); |
819 | 818 |
const char* cli_ctime(const time_t *timep, char *buf, const size_t bufsize); |
820 | 819 |
void cli_check_blockmax(cli_ctx *, int); |
821 |
-int cli_checklimits(const char *, cli_ctx *, unsigned long, unsigned long, unsigned long); |
|
822 |
-int cli_updatelimits(cli_ctx *, unsigned long); |
|
820 |
+cl_error_t cli_checklimits(const char *, cli_ctx *, unsigned long, unsigned long, unsigned long); |
|
821 |
+cl_error_t cli_updatelimits(cli_ctx *, unsigned long); |
|
823 | 822 |
unsigned long cli_getsizelimit(cli_ctx *, unsigned long); |
824 | 823 |
int cli_matchregex(const char *str, const char *regex); |
825 | 824 |
void cli_qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *)); |
826 | 825 |
void cli_qsort_r(void *a, size_t n, size_t es, int (*cmp)(const void*, const void *, const void *), void *arg); |
827 |
-int cli_checktimelimit(cli_ctx *ctx); |
|
828 |
-int cli_append_possibly_unwanted(cli_ctx * ctx, const char * virname); |
|
826 |
+cl_error_t cli_checktimelimit(cli_ctx *ctx); |
|
827 |
+cl_error_t cli_append_possibly_unwanted(cli_ctx *ctx, const char *virname); |
|
829 | 828 |
|
830 | 829 |
/* symlink behaviour */ |
831 | 830 |
#define CLI_FTW_FOLLOW_FILE_SYMLINK 0x01 |
... | ... |
@@ -3743,7 +3743,6 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type) |
3743 | 3743 |
case CL_ETMPFILE: |
3744 | 3744 |
case CL_ETMPDIR: |
3745 | 3745 |
case CL_EMEM: |
3746 |
- case CL_ETIMEOUT: |
|
3747 | 3746 |
cli_dbgmsg("Descriptor[%d]: cli_scanraw error %s\n", fmap_fd(*ctx->fmap), cl_strerror(res)); |
3748 | 3747 |
cli_bitset_free(ctx->hook_lsig_matches); |
3749 | 3748 |
ctx->hook_lsig_matches = old_hook_lsig_matches; |
... | ... |
@@ -3756,7 +3755,15 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type) |
3756 | 3756 |
cli_bitset_free(ctx->hook_lsig_matches); |
3757 | 3757 |
ctx->hook_lsig_matches = old_hook_lsig_matches; |
3758 | 3758 |
return magic_scandesc_cleanup(ctx, type, hash, hashed_size, cache_clean, ret, parent_property); |
3759 |
- /* "MAX" conditions should still fully scan the current file */ |
|
3759 |
+ /* The CL_ETIMEOUT "MAX" condition should set exceeds max flag and exit out quietly. */ |
|
3760 |
+ case CL_ETIMEOUT: |
|
3761 |
+ cli_check_blockmax(ctx, ret); |
|
3762 |
+ cli_bitset_free(ctx->hook_lsig_matches); |
|
3763 |
+ ctx->hook_lsig_matches = old_hook_lsig_matches; |
|
3764 |
+ cli_dbgmsg("Descriptor[%d]: Stopping after cli_scanraw reached %s\n", |
|
3765 |
+ fmap_fd(*ctx->fmap), cl_strerror(res)); |
|
3766 |
+ return magic_scandesc_cleanup(ctx, type, hash, hashed_size, cache_clean, CL_CLEAN, parent_property); |
|
3767 |
+ /* All other "MAX" conditions should still fully scan the current file */ |
|
3760 | 3768 |
case CL_EMAXREC: |
3761 | 3769 |
case CL_EMAXSIZE: |
3762 | 3770 |
case CL_EMAXFILES: |
... | ... |
@@ -3820,14 +3827,16 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type) |
3820 | 3820 |
|
3821 | 3821 |
switch (ret) |
3822 | 3822 |
{ |
3823 |
- /* Malformed file cases */ |
|
3824 |
- case CL_EFORMAT: |
|
3825 |
- case CL_EREAD: |
|
3826 |
- case CL_EUNPACK: |
|
3827 | 3823 |
/* Limits exceeded */ |
3824 |
+ case CL_ETIMEOUT: |
|
3828 | 3825 |
case CL_EMAXREC: |
3829 | 3826 |
case CL_EMAXSIZE: |
3830 | 3827 |
case CL_EMAXFILES: |
3828 |
+ cli_check_blockmax(ctx, ret); |
|
3829 |
+ /* Malformed file cases */ |
|
3830 |
+ case CL_EFORMAT: |
|
3831 |
+ case CL_EREAD: |
|
3832 |
+ case CL_EUNPACK: |
|
3831 | 3833 |
cli_dbgmsg("Descriptor[%d]: %s\n", fmap_fd(*ctx->fmap), cl_strerror(ret)); |
3832 | 3834 |
#if HAVE_JSON |
3833 | 3835 |
ctx->wrkproperty = parent_property; |
... | ... |
@@ -3868,7 +3877,7 @@ static cl_error_t cli_base_scandesc(int desc, const char *filepath, cli_ctx *ctx |
3868 | 3868 |
|
3869 | 3869 |
status = CL_ESTAT; |
3870 | 3870 |
cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", status, __AT__); |
3871 |
- goto done; |
|
3871 |
+ goto done; |
|
3872 | 3872 |
} |
3873 | 3873 |
if (sb.st_size <= 5) |
3874 | 3874 |
{ |
... | ... |
@@ -3876,7 +3885,7 @@ static cl_error_t cli_base_scandesc(int desc, const char *filepath, cli_ctx *ctx |
3876 | 3876 |
|
3877 | 3877 |
status = CL_CLEAN; |
3878 | 3878 |
cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", status, __AT__); |
3879 |
- goto done; |
|
3879 |
+ goto done; |
|
3880 | 3880 |
} |
3881 | 3881 |
|
3882 | 3882 |
ctx->fmap++; |
... | ... |
@@ -3889,7 +3898,7 @@ static cl_error_t cli_base_scandesc(int desc, const char *filepath, cli_ctx *ctx |
3889 | 3889 |
|
3890 | 3890 |
status = CL_EMEM; |
3891 | 3891 |
cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", status, __AT__); |
3892 |
- goto done; |
|
3892 |
+ goto done; |
|
3893 | 3893 |
} |
3894 | 3894 |
perf_stop(ctx, PERFT_MAP); |
3895 | 3895 |
|
... | ... |
@@ -4144,12 +4153,12 @@ static cl_error_t scan_common(int desc, cl_fmap_t *map, const char * filepath, c |
4144 | 4144 |
} |
4145 | 4145 |
perf_init(&ctx); |
4146 | 4146 |
|
4147 |
- if (ctx.options->general & CL_SCAN_GENERAL_COLLECT_METADATA && ctx.engine->time_limit != 0) |
|
4147 |
+ if (ctx.engine->maxscantime != 0) |
|
4148 | 4148 |
{ |
4149 | 4149 |
if (gettimeofday(&ctx.time_limit, NULL) == 0) |
4150 | 4150 |
{ |
4151 |
- uint32_t secs = ctx.engine->time_limit / 1000; |
|
4152 |
- uint32_t usecs = (ctx.engine->time_limit % 1000) * 1000; |
|
4151 |
+ uint32_t secs = ctx.engine->maxscantime / 1000; |
|
4152 |
+ uint32_t usecs = (ctx.engine->maxscantime % 1000) * 1000; |
|
4153 | 4153 |
ctx.time_limit.tv_sec += secs; |
4154 | 4154 |
ctx.time_limit.tv_usec += usecs; |
4155 | 4155 |
if (ctx.time_limit.tv_usec >= 1000000) |
... | ... |
@@ -4161,7 +4170,7 @@ static cl_error_t scan_common(int desc, cl_fmap_t *map, const char * filepath, c |
4161 | 4161 |
else |
4162 | 4162 |
{ |
4163 | 4163 |
char buf[64]; |
4164 |
- cli_dbgmsg("scan_common; gettimeofday error: %s\n", cli_strerror(errno, buf, 64)); |
|
4164 |
+ cli_dbgmsg("scan_common: gettimeofday error: %s\n", cli_strerror(errno, buf, 64)); |
|
4165 | 4165 |
} |
4166 | 4166 |
} |
4167 | 4167 |
|
... | ... |
@@ -777,6 +777,11 @@ int cli_unzip(cli_ctx *ctx) { |
777 | 777 |
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles); |
778 | 778 |
ret=CL_EMAXFILES; |
779 | 779 |
} |
780 |
+ |
|
781 |
+ if (cli_checktimelimit(ctx) != CL_SUCCESS) { |
|
782 |
+ cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime); |
|
783 |
+ ret = CL_ETIMEOUT; |
|
784 |
+ } |
|
780 | 785 |
/* |
781 | 786 |
* Detect overlapping files and zip bombs. |
782 | 787 |
*/ |
... | ... |
@@ -288,13 +288,13 @@ const struct clam_option __clam_options[] = { |
288 | 288 |
/* Scan options */ |
289 | 289 |
{ "Bytecode", "bytecode", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "With this option enabled ClamAV will load bytecode from the database. It is highly recommended you keep this option on, otherwise you'll miss detections for many new viruses.", "yes" }, |
290 | 290 |
|
291 |
- { "BytecodeSecurity", NULL, 0, CLOPT_TYPE_STRING, "^(TrustSigned|Paranoid)$", -1, "TrustSigned", 0, OPT_CLAMD, |
|
291 |
+ { "BytecodeSecurity", NULL, 0, CLOPT_TYPE_STRING, "^(TrustSigned|Paranoid)$", -1, "TrustSigned", 0, OPT_CLAMD, |
|
292 | 292 |
"Set bytecode security level.\nPossible values:\n\tTrustSigned - trust bytecode loaded from signed .c[lv]d files,\n\t\t insert runtime safety checks for bytecode loaded from other sources\n\tParanoid - don't trust any bytecode, insert runtime checks for all\nRecommended: TrustSigned, because bytecode in .cvd files already has these checks.","TrustSigned"}, |
293 | 293 |
|
294 |
- { "BytecodeTimeout", "bytecode-timeout", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 5000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, |
|
294 |
+ { "BytecodeTimeout", "bytecode-timeout", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 5000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, |
|
295 | 295 |
"Set bytecode timeout in milliseconds.","5000"}, |
296 | 296 |
|
297 |
- { "BytecodeUnsigned", "bytecode-unsigned", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, |
|
297 |
+ { "BytecodeUnsigned", "bytecode-unsigned", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, |
|
298 | 298 |
"Allow loading bytecode from outside digitally signed .c[lv]d files.","no"}, |
299 | 299 |
|
300 | 300 |
{ "BytecodeMode", "bytecode-mode", 0, CLOPT_TYPE_STRING, "^(Auto|ForceJIT|ForceInterpreter|Test)$", -1, "Auto", FLAG_REQUIRED, OPT_CLAMD | OPT_CLAMSCAN, |
... | ... |
@@ -366,6 +366,8 @@ const struct clam_option __clam_options[] = { |
366 | 366 |
|
367 | 367 |
{ "ForceToDisk", "force-to-disk", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option causes memory or nested map scans to dump the content to disk.\nIf you turn on this option, more data is written to disk and is available\nwhen the leave-temps option is enabled at the cost of more disk writes.", "no" }, |
368 | 368 |
|
369 |
+ { "MaxScanTime", "max-scantime", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum amount of time a scan may take to complete.\nIn this version, this field only affects the scan time of ZIP archives.\nThe value of 0 disables the limit.\nWARNING: disabling this limit or setting it too high may result allow scanning\nof certain files to lock up the scanning process/threads resulting in a Denial of Service.\nThe value is in milliseconds.", "120000"}, |
|
370 |
+ |
|
369 | 371 |
{ "MaxScanSize", "max-scansize", 0, CLOPT_TYPE_SIZE, MATCH_SIZE, CLI_DEFAULT_MAXSCANSIZE, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum amount of data to be scanned for each input file.\nArchives and other containers are recursively extracted and scanned up to this\nvalue.\nThe value of 0 disables the limit.\nWARNING: disabling this limit or setting it too high may result in severe\ndamage.", "100M" }, |
370 | 372 |
|
371 | 373 |
{ "MaxFileSize", "max-filesize", 0, CLOPT_TYPE_SIZE, MATCH_SIZE, CLI_DEFAULT_MAXFILESIZE, NULL, 0, OPT_CLAMD | OPT_MILTER | OPT_CLAMSCAN, "Files/messages larger than this limit won't be scanned. Affects the input\nfile itself as well as files contained inside it (when the input file is\nan archive, a document or some other kind of container).\nThe value of 0 disables the limit.\nWARNING: disabling this limit or setting it too high may result in severe\ndamage to the system.", "25M" }, |
... | ... |
@@ -391,8 +393,6 @@ const struct clam_option __clam_options[] = { |
391 | 391 |
|
392 | 392 |
{ "MaxRecHWP3", "max-rechwp3", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CLI_DEFAULT_MAXRECHWP3, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum recursive calls to HWP3 parsing function.\nHWP3 files using more than this limit will be terminated and alert the user.\nScans will be unable to scan any HWP3 attachments if the recursive limit is reached.\nNegative values are not allowed.\nWARNING: setting this limit too high may result in severe damage or impact performance.", "16" }, |
393 | 393 |
|
394 |
- { "TimeLimit", "timelimit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMSCAN, "This clamscan option is currently for testing only. It sets the engine parameter CL_ENGINE_TIME_LIMIT. The value is in milliseconds.", "0" }, |
|
395 |
- |
|
396 | 394 |
{ "PCREMatchLimit", "pcre-match-limit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CLI_DEFAULT_PCRE_MATCH_LIMIT, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum calls to the PCRE match function during an instance of regex matching.\nInstances using more than this limit will be terminated and alert the user but the scan will continue.\nFor more information on match_limit, see the PCRE documentation.\nNegative values are not allowed.\nWARNING: setting this limit too high may severely impact performance.", "100000" }, |
397 | 395 |
|
398 | 396 |
{ "PCRERecMatchLimit", "pcre-recmatch-limit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CLI_DEFAULT_PCRE_RECMATCH_LIMIT, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum recursive calls to the PCRE match function during an instance of regex matching.\nInstances using more than this limit will be terminated and alert the user but the scan will continue.\nFor more information on match_limit_recursion, see the PCRE documentation.\nNegative values are not allowed and values > PCREMatchLimit are superfluous.\nWARNING: setting this limit too high may severely impact performance.", "5000" }, |
... | ... |
@@ -491,6 +491,7 @@ const struct clam_option __clam_options[] = { |
491 | 491 |
|
492 | 492 |
/* Deprecated options */ |
493 | 493 |
|
494 |
+ { "TimeLimit", "timelimit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMSCAN | OPT_DEPRECATED, "Deprecated option to set the max-scantime.\nThe value is in milliseconds.", "120000"}, |
|
494 | 495 |
{ "DetectBrokenExecutables", "detect-broken", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "Deprecated option to alert on broken PE and ELF executable files.", "no" }, |
495 | 496 |
{ "AlgorithmicDetection", "algorithmic-detection", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Deprecated option to enable heuristic alerts (e.g. \"Heuristics.<sig name>\")", "no" }, |
496 | 497 |
{ "BlockMax", "block-max", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "", "" }, |
... | ... |
@@ -1188,39 +1189,39 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int |
1188 | 1188 |
long long numarg, lnumarg; |
1189 | 1189 |
int regflags = REG_EXTENDED | REG_NOSUB; |
1190 | 1190 |
const struct clam_option *optentry = NULL; |
1191 |
- |
|
1191 |
+ |
|
1192 | 1192 |
if(oldopts) |
1193 | 1193 |
opts = oldopts; |
1194 |
- |
|
1195 |
- |
|
1194 |
+ |
|
1195 |
+ |
|
1196 | 1196 |
for(i = 0; ; i++) { |
1197 | 1197 |
optentry = &clam_options[i]; |
1198 | 1198 |
if(!optentry->name && !optentry->longopt) |
1199 | 1199 |
break; |
1200 |
- |
|
1200 |
+ |
|
1201 | 1201 |
if(((optentry->owner & toolmask) && ((optentry->owner & toolmask) != OPT_DEPRECATED)) || (ignore && (optentry->owner & ignore))) { |
1202 | 1202 |
if(!oldopts && optadd(&opts, &opts_last, optentry->name, optentry->longopt, optentry->strarg, optentry->numarg, optentry->flags, i) < 0) { |
1203 | 1203 |
fprintf(stderr, "ERROR: optparse: Can't register new option (not enough memory)\n"); |
1204 | 1204 |
optfree(opts); |
1205 | 1205 |
return NULL; |
1206 | 1206 |
} |
1207 |
- |
|
1207 |
+ |
|
1208 | 1208 |
} |
1209 | 1209 |
} |
1210 |
- |
|
1210 |
+ |
|
1211 | 1211 |
if(MAX(sc, lc) > MAXCMDOPTS) { |
1212 | 1212 |
fprintf(stderr, "ERROR: optparse: (short|long)opts[] is too small\n"); |
1213 | 1213 |
optfree(opts); |
1214 | 1214 |
return NULL; |
1215 | 1215 |
} |
1216 |
- |
|
1216 |
+ |
|
1217 | 1217 |
while(1) { |
1218 | 1218 |
if(!name) { |
1219 | 1219 |
fprintf(stderr, "ERROR: Problem parsing options (name == NULL)\n"); |
1220 | 1220 |
err = 1; |
1221 | 1221 |
break; |
1222 | 1222 |
} |
1223 |
- |
|
1223 |
+ |
|
1224 | 1224 |
opt = optget_i(opts, name); |
1225 | 1225 |
if(!opt) { |
1226 | 1226 |
if(verbose) |
... | ... |
@@ -1229,13 +1230,13 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int |
1229 | 1229 |
break; |
1230 | 1230 |
} |
1231 | 1231 |
optentry = &clam_options[opt->idx]; |
1232 |
- |
|
1232 |
+ |
|
1233 | 1233 |
if(ignore && (optentry->owner & ignore) && !(optentry->owner & toolmask)) { |
1234 | 1234 |
if(verbose) |
1235 | 1235 |
fprintf(stderr, "WARNING: Ignoring unsupported option %s\n", opt->name); |
1236 | 1236 |
continue; |
1237 | 1237 |
} |
1238 |
- |
|
1238 |
+ |
|
1239 | 1239 |
if(optentry->owner & OPT_DEPRECATED) { |
1240 | 1240 |
if(toolmask & OPT_DEPRECATED) { |
1241 | 1241 |
if(optaddarg(opts, name, "foo", 1) < 0) { |
... | ... |
@@ -1249,11 +1250,11 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int |
1249 | 1249 |
} |
1250 | 1250 |
continue; |
1251 | 1251 |
} |
1252 |
- |
|
1252 |
+ |
|
1253 | 1253 |
if(optentry->regex) { |
1254 | 1254 |
if(!(optentry->flags & FLAG_REG_CASE)) |
1255 | 1255 |
regflags |= REG_ICASE; |
1256 |
- |
|
1256 |
+ |
|
1257 | 1257 |
if(cli_regcomp(®ex, optentry->regex, regflags)) { |
1258 | 1258 |
fprintf(stderr, "ERROR: optparse: Can't compile regular expression %s for option %s\n", optentry->regex, name); |
1259 | 1259 |
err = 1; |
... | ... |
@@ -1267,15 +1268,15 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int |
1267 | 1267 |
break; |
1268 | 1268 |
} |
1269 | 1269 |
} |
1270 |
- |
|
1270 |
+ |
|
1271 | 1271 |
numarg = -1; |
1272 | 1272 |
switch(optentry->argtype) { |
1273 | 1273 |
case CLOPT_TYPE_STRING: |
1274 | 1274 |
if(!arg) |
1275 | 1275 |
arg = optentry->strarg; |
1276 |
- |
|
1276 |
+ |
|
1277 | 1277 |
break; |
1278 |
- |
|
1278 |
+ |
|
1279 | 1279 |
case CLOPT_TYPE_NUMBER: |
1280 | 1280 |
if (arg) |
1281 | 1281 |
numarg = atoi(arg); |
... | ... |
@@ -1283,7 +1284,7 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int |
1283 | 1283 |
numarg = 0; |
1284 | 1284 |
arg = NULL; |
1285 | 1285 |
break; |
1286 |
- |
|
1286 |
+ |
|
1287 | 1287 |
case CLOPT_TYPE_SIZE: |
1288 | 1288 |
errno = 0; |
1289 | 1289 |
if(arg) |
... | ... |
@@ -1311,41 +1312,41 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int |
1311 | 1311 |
err = 1; |
1312 | 1312 |
} |
1313 | 1313 |
} |
1314 |
- |
|
1314 |
+ |
|
1315 | 1315 |
arg = NULL; |
1316 | 1316 |
if(err) break; |
1317 | 1317 |
if(errno == ERANGE) { |
1318 | 1318 |
fprintf(stderr, "WARNING: Numerical value for option %s too high, resetting to 4G\n", name); |
1319 | 1319 |
lnumarg = UINT_MAX; |
1320 | 1320 |
} |
1321 |
- |
|
1321 |
+ |
|
1322 | 1322 |
numarg = lnumarg ? lnumarg : UINT_MAX; |
1323 | 1323 |
break; |
1324 |
- |
|
1324 |
+ |
|
1325 | 1325 |
case CLOPT_TYPE_BOOL: |
1326 | 1326 |
if(!strcasecmp(arg, "yes") || !strcmp(arg, "1") || !strcasecmp(arg, "true")) |
1327 | 1327 |
numarg = 1; |
1328 | 1328 |
else |
1329 | 1329 |
numarg = 0; |
1330 |
- |
|
1330 |
+ |
|
1331 | 1331 |
arg = NULL; |
1332 | 1332 |
break; |
1333 | 1333 |
} |
1334 |
- |
|
1334 |
+ |
|
1335 | 1335 |
if(err) |
1336 | 1336 |
break; |
1337 |
- |
|
1337 |
+ |
|
1338 | 1338 |
if(optaddarg(opts, name, arg, numarg) < 0) { |
1339 | 1339 |
fprintf(stderr, "ERROR: Can't register argument for option --%s\n", optentry->longopt); |
1340 | 1340 |
err = 1; |
1341 | 1341 |
} |
1342 | 1342 |
break; |
1343 | 1343 |
} |
1344 |
- |
|
1344 |
+ |
|
1345 | 1345 |
if(err) { |
1346 | 1346 |
optfree(opts); |
1347 | 1347 |
return NULL; |
1348 | 1348 |
} |
1349 |
- |
|
1349 |
+ |
|
1350 | 1350 |
return opts; |
1351 | 1351 |
} |
... | ... |
@@ -76,8 +76,8 @@ Example |
76 | 76 |
# Default: no |
77 | 77 |
#OfficialDatabaseOnly no |
78 | 78 |
|
79 |
-# The daemon on Windows only supports unsecured TCP sockets. |
|
80 |
-# Due to security reasons make sure that your IP & port is not |
|
79 |
+# The daemon on Windows only supports unsecured TCP sockets. |
|
80 |
+# Due to security reasons make sure that your IP & port is not |
|
81 | 81 |
# exposed to the open internet. |
82 | 82 |
|
83 | 83 |
# TCP port address. |
... | ... |
@@ -203,7 +203,7 @@ TCPAddr 127.0.0.1 |
203 | 203 |
#DetectPUA yes |
204 | 204 |
|
205 | 205 |
# Exclude a specific PUA category. This directive can be used multiple times. |
206 |
-# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for |
|
206 |
+# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for |
|
207 | 207 |
# the complete list of PUA categories. |
208 | 208 |
# Default: Load all categories (if DetectPUA is activated) |
209 | 209 |
#ExcludePUA NetTool |
... | ... |
@@ -243,9 +243,9 @@ TCPAddr 127.0.0.1 |
243 | 243 |
# the end of a scan. If an archive contains both a heuristically detected |
244 | 244 |
# virus/phish, and a real malware, the real malware will be reported |
245 | 245 |
# |
246 |
-# Keep this disabled if you intend to handle "*.Heuristics.*" viruses |
|
246 |
+# Keep this disabled if you intend to handle "*.Heuristics.*" viruses |
|
247 | 247 |
# differently from "real" malware. |
248 |
-# If a non-heuristically-detected virus (signature-based) is found first, |
|
248 |
+# If a non-heuristically-detected virus (signature-based) is found first, |
|
249 | 249 |
# the scan is interrupted immediately, regardless of this config option. |
250 | 250 |
# |
251 | 251 |
# Default: no |
... | ... |
@@ -446,6 +446,16 @@ TCPAddr 127.0.0.1 |
446 | 446 |
# The options below protect your system against Denial of Service attacks |
447 | 447 |
# using archive bombs. |
448 | 448 |
|
449 |
+# This option sets the maximum amount of time to a scan may take. |
|
450 |
+# In this version, this field only affects the scan time of ZIP archives. |
|
451 |
+# Value of 0 disables the limit |
|
452 |
+# Note: disabling this limit or setting it too high may result allow scanning |
|
453 |
+# of certain files to lock up the scanning process/threads resulting in a Denial |
|
454 |
+# of Service. |
|
455 |
+# Time is in milliseconds. |
|
456 |
+# Default: 120000 |
|
457 |
+#MaxScanTime 300000 |
|
458 |
+ |
|
449 | 459 |
# This option sets the maximum amount of data to be scanned for each input file. |
450 | 460 |
# Archives and other containers are recursively extracted and scanned up to this |
451 | 461 |
# value. |
... | ... |
@@ -584,7 +594,7 @@ TCPAddr 127.0.0.1 |
584 | 584 |
## Bytecode |
585 | 585 |
## |
586 | 586 |
|
587 |
-# With this option enabled ClamAV will load bytecode from the database. |
|
587 |
+# With this option enabled ClamAV will load bytecode from the database. |
|
588 | 588 |
# It is highly recommended you keep this option on, otherwise you'll miss |
589 | 589 |
# detections for many new viruses. |
590 | 590 |
# Default: yes |
... | ... |
@@ -608,7 +618,7 @@ TCPAddr 127.0.0.1 |
608 | 608 |
#BytecodeSecurity TrustSigned |
609 | 609 |
|
610 | 610 |
# Set bytecode timeout in milliseconds. |
611 |
-# |
|
611 |
+# |
|
612 | 612 |
# Default: 5000 |
613 | 613 |
# BytecodeTimeout 1000 |
614 | 614 |
|