Browse code

Add ForceToDisk option for clamd and force-to-disk arg for clamscan

David Raynor authored on 2013/11/09 07:10:43
Showing 13 changed files
... ...
@@ -1,3 +1,7 @@
1
+Fri Nov 8 17:08:09 2013 EDT 2013 (morgan)
2
+------------------------------------
3
+ * Add ForceToDisk option for clamd and force-to-disk arg for clamscan
4
+
1 5
 Wed Oct 31 12:48:00 2013 EDT 2013 (morgan)
2 6
 ------------------------------------
3 7
  * libclamav: bb#5341 - Change floating point byte order check from compile time to run time.
... ...
@@ -431,6 +431,9 @@ int main(int argc, char **argv)
431 431
     if(optget(opts, "LeaveTemporaryFiles")->enabled)
432 432
 	cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
433 433
 
434
+    if(optget(opts, "ForceToDisk")->enabled)
435
+	cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
436
+
434 437
     if(optget(opts, "PhishingSignatures")->enabled)
435 438
 	dboptions |= CL_DB_PHISHING;
436 439
     else
... ...
@@ -631,6 +631,9 @@ int scanmanager(const struct optstruct *opts)
631 631
     if(optget(opts, "leave-temps")->enabled)
632 632
 	cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
633 633
 
634
+    if(optget(opts, "force-to-disk")->enabled)
635
+	cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
636
+
634 637
     if(optget(opts, "bytecode-unsigned")->enabled)
635 638
 	dboptions |= CL_DB_BYTECODE_UNSIGNED;
636 639
 
... ...
@@ -241,6 +241,10 @@ Example
241 241
 # Default: yes
242 242
 #AlgorithmicDetection yes
243 243
 
244
+# This option causes memory or nested map scans to dump the content to disk.
245
+# If you turn on this option, more data is written to disk and is available
246
+# when the LeaveTemporaryFiles option is enabled.
247
+#ForceToDisk yes
244 248
 
245 249
 ##
246 250
 ## Executable files
... ...
@@ -193,7 +193,8 @@ enum cl_engine_field {
193 193
     CL_ENGINE_MAX_HTMLNORMALIZE,    /* uint64_t */
194 194
     CL_ENGINE_MAX_HTMLNOTAGS,       /* uint64_t */
195 195
     CL_ENGINE_MAX_SCRIPTNORMALIZE,  /* uint64_t */
196
-    CL_ENGINE_MAX_ZIPTYPERCG        /* uint64_t */
196
+    CL_ENGINE_MAX_ZIPTYPERCG,       /* uint64_t */
197
+    CL_ENGINE_FORCETODISK           /* uint32_t */
197 198
 };
198 199
 
199 200
 enum bytecode_security {
... ...
@@ -157,7 +157,7 @@ int cli_scancpio_old(cli_ctx *ctx)
157 157
 	    if(ret == CL_EMAXFILES) {
158 158
 		return ret;
159 159
 	    } else if(ret == CL_SUCCESS) {
160
-		ret = cli_map_scandesc(*ctx->fmap, pos, filesize, ctx);
160
+		ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx);
161 161
 		if(ret == CL_VIRUS)
162 162
 		    return ret;
163 163
 	    }
... ...
@@ -234,7 +234,7 @@ int cli_scancpio_odc(cli_ctx *ctx)
234 234
 	if(ret == CL_EMAXFILES) {
235 235
 	    return ret;
236 236
 	} else if(ret == CL_SUCCESS) {
237
-	    ret = cli_map_scandesc(*ctx->fmap, pos, filesize, ctx);
237
+	    ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx);
238 238
 	    if(ret == CL_VIRUS)
239 239
 		return ret;
240 240
 	}
... ...
@@ -313,7 +313,7 @@ int cli_scancpio_newc(cli_ctx *ctx, int crc)
313 313
 	if(ret == CL_EMAXFILES) {
314 314
 	    return ret;
315 315
 	} else if(ret == CL_SUCCESS) {
316
-	    ret = cli_map_scandesc(*ctx->fmap, pos, filesize, ctx);
316
+	    ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx);
317 317
 	    if(ret == CL_VIRUS)
318 318
 		return ret;
319 319
 	}
... ...
@@ -168,7 +168,7 @@ int cli_scandmg(cli_ctx *ctx)
168 168
     cli_dbgmsg("cli_scandmg: Extracting into %s\n", dirname);
169 169
 
170 170
     /* Dump XML to tempfile, if needed */
171
-    if (ctx->engine->keeptmp) {
171
+    if (ctx->engine->keeptmp && !ctx->engine->forcetodisk) {
172 172
         int xret;
173 173
         xret = dmg_extract_xml(ctx, dirname, &hdr);
174 174
 
... ...
@@ -180,7 +180,7 @@ int cli_scandmg(cli_ctx *ctx)
180 180
     }
181 181
 
182 182
     /* scan XML with cli_map_scandesc */
183
-    ret = cli_map_scandesc(*ctx->fmap, (off_t)hdr.xmlOffset, (size_t)hdr.xmlLength, ctx);
183
+    ret = cli_map_scan(*ctx->fmap, (off_t)hdr.xmlOffset, (size_t)hdr.xmlLength, ctx);
184 184
     if (ret != CL_CLEAN) {
185 185
         cli_dbgmsg("cli_scandmg: retcode from scanning TOC xml: %s\n", cl_strerror(ret));
186 186
         if (!ctx->engine->keeptmp)
... ...
@@ -553,7 +553,7 @@ int cli_scanmacho_unibin(cli_ctx *ctx)
553 553
 	cli_dbgmsg("UNIBIN: Binary %u of %u\n", i + 1, fat_header.nfats);
554 554
 	cli_dbgmsg("UNIBIN: File offset: %u\n", fat_arch.offset);
555 555
 	cli_dbgmsg("UNIBIN: File size: %u\n", fat_arch.size);
556
-	ret = cli_map_scandesc(map, fat_arch.offset, fat_arch.size, ctx);
556
+	ret = cli_map_scan(map, fat_arch.offset, fat_arch.size, ctx);
557 557
 	if(ret == CL_VIRUS)
558 558
 	    break;
559 559
     }
... ...
@@ -461,6 +461,12 @@ int cl_engine_set_num(struct cl_engine *engine, enum cl_engine_field field, long
461 461
 	case CL_ENGINE_KEEPTMP:
462 462
 	    engine->keeptmp = num;
463 463
 	    break;
464
+	case CL_ENGINE_FORCETODISK:
465
+	    if(num)
466
+	        engine->forcetodisk = 1;
467
+	    else
468
+	        engine->forcetodisk = 0;
469
+	    break;
464 470
 	case CL_ENGINE_BYTECODE_SECURITY:
465 471
 	    if (engine->dboptions & CL_DB_COMPILED) {
466 472
 		cli_errmsg("cl_engine_set_num: CL_ENGINE_BYTECODE_SECURITY cannot be set after engine was compiled\n");
... ...
@@ -541,6 +547,8 @@ long long cl_engine_get_num(const struct cl_engine *engine, enum cl_engine_field
541 541
 	    return engine->ac_maxdepth;
542 542
 	case CL_ENGINE_KEEPTMP:
543 543
 	    return engine->keeptmp;
544
+	case CL_ENGINE_FORCETODISK:
545
+	    return engine->forcetodisk;
544 546
 	case CL_ENGINE_BYTECODE_SECURITY:
545 547
 	    return engine->bytecode_security;
546 548
 	case CL_ENGINE_BYTECODE_TIMEOUT:
... ...
@@ -619,6 +627,7 @@ struct cl_settings *cl_engine_settings_copy(const struct cl_engine *engine)
619 619
     settings->ac_maxdepth = engine->ac_maxdepth;
620 620
     settings->tmpdir = engine->tmpdir ? strdup(engine->tmpdir) : NULL;
621 621
     settings->keeptmp = engine->keeptmp;
622
+    settings->forcetodisk = engine->forcetodisk;
622 623
     settings->maxscansize = engine->maxscansize;
623 624
     settings->maxfilesize = engine->maxfilesize;
624 625
     settings->maxreclevel = engine->maxreclevel;
... ...
@@ -652,6 +661,7 @@ int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings
652 652
     engine->ac_mindepth = settings->ac_mindepth;
653 653
     engine->ac_maxdepth = settings->ac_maxdepth;
654 654
     engine->keeptmp = settings->keeptmp;
655
+    engine->forcetodisk = settings->forcetodisk;
655 656
     engine->maxscansize = settings->maxscansize;
656 657
     engine->maxfilesize = settings->maxfilesize;
657 658
     engine->maxreclevel = settings->maxreclevel;
... ...
@@ -55,7 +55,7 @@
55 55
  * in re-enabling affected modules.
56 56
  */
57 57
 
58
-#define CL_FLEVEL 75
58
+#define CL_FLEVEL 77
59 59
 #define CL_FLEVEL_DCONF	CL_FLEVEL
60 60
 #define CL_FLEVEL_SIGTOOL CL_FLEVEL
61 61
 
... ...
@@ -283,6 +283,8 @@ struct cl_engine {
283 283
     uint64_t maxhtmlnotags; /* max size for scanning normalized HTML */
284 284
     uint64_t maxscriptnormalize; /* max size to normalize scripts */
285 285
     uint64_t maxziptypercg; /* max size to re-do zip filetype */
286
+
287
+    uint32_t forcetodisk; /* cause memory or map scans to dump to disk first */
286 288
 };
287 289
 
288 290
 struct cl_settings {
... ...
@@ -322,6 +324,8 @@ struct cl_settings {
322 322
     uint64_t maxhtmlnotags; /* max size for scanning normalized HTML */
323 323
     uint64_t maxscriptnormalize; /* max size to normalize scripts */
324 324
     uint64_t maxziptypercg; /* max size to re-do zip filetype */
325
+
326
+    uint32_t forcetodisk; /* cause memory or map scans to dump to disk first */
325 327
 };
326 328
 
327 329
 extern int (*cli_unrar_open)(int fd, const char *dirname, unrar_state_t *state);
... ...
@@ -2964,7 +2964,67 @@ int cl_scandesc(int desc, const char **virname, unsigned long int *scanned, cons
2964 2964
     return cl_scandesc_callback(desc, virname, scanned, engine, scanoptions, NULL);
2965 2965
 }
2966 2966
 
2967
-/* length = 0, till the end */
2967
+/* For map scans that may be forced to disk */
2968
+int cli_map_scan(cl_fmap_t *map, off_t offset, size_t length, cli_ctx *ctx)
2969
+{
2970
+    off_t old_off = map->nested_offset;
2971
+    size_t old_len = map->len;
2972
+    int ret = CL_CLEAN;
2973
+
2974
+    cli_dbgmsg("cli_map_scan: [%ld, +%lu)\n",
2975
+	       (long)offset, (unsigned long)length);
2976
+    if (offset < 0 || offset >= map->len) {
2977
+	cli_dbgmsg("Invalid offset: %ld\n", (long)offset);
2978
+	return CL_CLEAN;
2979
+    }
2980
+
2981
+    if (ctx->engine->forcetodisk) {
2982
+        /* if this is forced to disk, then need to write the nested map and scan it */
2983
+        const uint8_t *mapdata = NULL;
2984
+        char *tempfile = NULL;
2985
+        int fd = -1;
2986
+        size_t nread = 0;
2987
+
2988
+        mapdata = fmap_need_off_once_len(map, offset, length, &nread);
2989
+        if (!mapdata || (nread != length)) {
2990
+            cli_errmsg("cli_map_scan: could not map sub-file\n");
2991
+            return CL_EMAP;
2992
+        }
2993
+
2994
+        ret = cli_gentempfd(ctx->engine->tmpdir, &tempfile, &fd);
2995
+        if (ret != CL_SUCCESS) {
2996
+            return ret;
2997
+        }
2998
+
2999
+        cli_dbgmsg("cli_map_scan: writing nested map content to temp file %s\n", tempfile);
3000
+        if (cli_writen(fd, mapdata, length) < 0) {
3001
+            cli_errmsg("cli_map_scan: cli_writen error writing subdoc temporary file.\n");
3002
+            ret = CL_EWRITE;
3003
+        }
3004
+
3005
+        /* scan the temp file */
3006
+        ret = cli_base_scandesc(fd, ctx, CL_TYPE_ANY);
3007
+
3008
+        /* remove the temp file, if needed */
3009
+        if (fd > -1) {
3010
+            close(fd);
3011
+        }
3012
+        if(!ctx->engine->keeptmp) {
3013
+            if (cli_unlink(tempfile)) {
3014
+                cli_errmsg("cli_map_scan: error unlinking tempfile %s\n", tempfile);
3015
+                ret = CL_EUNLINK;
3016
+            }
3017
+        }
3018
+        free(tempfile);
3019
+    }
3020
+    else {
3021
+        /* Not forced to disk, use nested map */
3022
+        ret = cli_map_scandesc(map, offset, length, ctx);
3023
+    }
3024
+    return ret;
3025
+}
3026
+
3027
+/* For map scans that are not forced to disk */
2968 3028
 int cli_map_scandesc(cl_fmap_t *map, off_t offset, size_t length, cli_ctx *ctx)
2969 3029
 {
2970 3030
     off_t old_off = map->nested_offset;
... ...
@@ -29,6 +29,7 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx);
29 29
 int cli_partition_scandesc(int desc, cli_ctx *ctx);
30 30
 int cli_magic_scandesc_type(cli_ctx *ctx, cli_file_t type);
31 31
 int cli_map_scandesc(cl_fmap_t *map, off_t offset, size_t length, cli_ctx *ctx);
32
+int cli_map_scan(cl_fmap_t *map, off_t offset, size_t length, cli_ctx *ctx);
32 33
 int cli_mem_scandesc(const void *buffer, size_t length, cli_ctx *ctx);
33 34
 int cli_found_possibly_unwanted(cli_ctx* ctx);
34 35
 
... ...
@@ -338,6 +338,8 @@ const struct clam_option __clam_options[] = {
338 338
 
339 339
     { "ArchiveBlockEncrypted", "block-encrypted", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Mark encrypted archives as viruses (Encrypted.Zip, Encrypted.RAR).", "no" },
340 340
 
341
+    { "ForceToDisk", "force-to-disk", 0, 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" },
342
+
341 343
     { "MaxScanSize", "max-scansize", 0, 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" },
342 344
 
343 345
     { "MaxFileSize", "max-filesize", 0, 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" },