Browse code

libclamav: base code for unified container metadata matcher (bb#1579)

Tomasz Kojm authored on 2010/01/08 02:26:12
Showing 10 changed files
... ...
@@ -1,3 +1,7 @@
1
+Thu Jan  7 18:22:39 CET 2010 (tk)
2
+---------------------------------
3
+ * libclamav: base code for unified container metadata matcher (bb#1579)
4
+
1 5
 Tue Jan  5 12:04:47 CET 2010 (acab)
2 6
 -----------------------------------
3 7
  * libclamav/readdb.c: force VI anchored sigs into AC
... ...
@@ -636,10 +636,7 @@ fileblobScan(const fileblob *fb)
636 636
 	fflush(fb->fp);
637 637
 	lseek(fb->fd, 0, SEEK_SET);
638 638
 
639
-	fb->ctx->container_type = CL_TYPE_MAIL;
640 639
 	rc = cli_magic_scandesc(fb->fd, fb->ctx);
641
-	fb->ctx->container_type = 0;
642
-
643 640
 	if(rc == CL_VIRUS) {
644 641
 		cli_dbgmsg("%s is infected\n", fb->fullname);
645 642
 		return CL_VIRUS;
... ...
@@ -53,7 +53,7 @@ static const struct ftmap_s {
53 53
     { "CL_TYPE_TEXT_UTF16BE",	CL_TYPE_TEXT_UTF16BE	},
54 54
     { "CL_TYPE_BINARY_DATA",	CL_TYPE_BINARY_DATA	},
55 55
     { "CL_TYPE_IGNORED",	CL_TYPE_IGNORED		},
56
-    { "CL_TYPE_ANY",		0			}, /* for ft-sigs */
56
+    { "CL_TYPE_ANY",		CL_TYPE_ANY		},
57 57
     { "CL_TYPE_MSEXE",		CL_TYPE_MSEXE		},
58 58
     { "CL_TYPE_ELF",		CL_TYPE_ELF		},
59 59
     { "CL_TYPE_MACHO",		CL_TYPE_MACHO		},
... ...
@@ -32,6 +32,7 @@
32 32
 #define MAX_EMBEDDED_OBJ 10
33 33
 
34 34
 typedef enum {
35
+    CL_TYPE_ANY = 0,
35 36
     CL_TYPE_TEXT_ASCII = CL_TYPENO, /* X3.4, ISO-8859, non-ISO ext. ASCII */
36 37
     CL_TYPE_TEXT_UTF8,
37 38
     CL_TYPE_TEXT_UTF16LE,
... ...
@@ -47,6 +47,7 @@
47 47
 #include "macho.h"
48 48
 #include "fmap.h"
49 49
 #include "pe_icons.h"
50
+#include "regex/regex.h"
50 51
 
51 52
 int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata)
52 53
 {
... ...
@@ -541,3 +542,52 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
541 541
 
542 542
     return (acmode & AC_SCAN_FT) ? type : CL_CLEAN;
543 543
 }
544
+
545
+int cli_matchmeta(cli_ctx *ctx, cli_file_t ftype, const char *fname, size_t fsizec, size_t fsizer, int encrypted, void *res1, void *res2)
546
+{
547
+	const struct cli_cdb *cdb;
548
+
549
+    if(!(cdb = ctx->engine->cdb))
550
+	return CL_CLEAN;
551
+
552
+    do {
553
+	if(cdb->ctype != CL_TYPE_ANY && cdb->ctype != ctx->container_type)
554
+	    continue;
555
+
556
+	if(cdb->ftype != CL_TYPE_ANY && cdb->ftype != ftype)
557
+	    continue;
558
+
559
+	if(cdb->encrypted != 2 && cdb->encrypted != encrypted)
560
+	    continue;
561
+
562
+	if(cdb->csize[0] != CLI_OFF_ANY) {
563
+	    if(cdb->csize[0] == cdb->csize[1] && cdb->csize[0] != ctx->container_size)
564
+		continue;
565
+	    else if(cdb->csize[0] != cdb->csize[1] && ((cdb->csize[0] && cdb->csize[0] > ctx->container_size) || (cdb->csize[1] && cdb->csize[1] < ctx->container_size)))
566
+		continue;
567
+	}
568
+
569
+	if(cdb->fsizec[0] != CLI_OFF_ANY) {
570
+	    if(cdb->fsizec[0] == cdb->fsizec[1] && cdb->fsizec[0] != fsizec)
571
+		continue;
572
+	    else if(cdb->fsizec[0] != cdb->fsizec[1] && ((cdb->fsizec[0] && cdb->fsizec[0] > fsizec) || (cdb->fsizec[1] && cdb->fsizec[1] < fsizec)))
573
+		continue;
574
+	}
575
+
576
+	if(cdb->fsizer[0] != CLI_OFF_ANY) {
577
+	    if(cdb->fsizer[0] == cdb->fsizer[1] && cdb->fsizer[0] != fsizer)
578
+		continue;
579
+	    else if(cdb->fsizer[0] != cdb->fsizer[1] && ((cdb->fsizer[0] && cdb->fsizer[0] > fsizer) || (cdb->fsizer[1] && cdb->fsizer[1] < fsizer)))
580
+		continue;
581
+	}
582
+
583
+	if(cdb->name.re_magic && (!fname || cli_regexec(&cdb->name, fname, 0, NULL, 0) == REG_NOMATCH))
584
+	    continue;
585
+
586
+	*ctx->virname = cdb->virname;
587
+	return CL_VIRUS;
588
+
589
+    } while((cdb = cdb->next));
590
+
591
+    return CL_CLEAN;
592
+}
... ...
@@ -106,6 +106,24 @@ struct cli_meta_node {
106 106
     unsigned int crc32, fileno, encrypted, maxdepth;
107 107
 };
108 108
 
109
+struct cli_cdb
110
+{
111
+    char	*virname;   /* virus name */
112
+    cli_file_t	ctype;	    /* container type */
113
+    cli_file_t	ftype;	    /* file type */
114
+    regex_t	name;	    /* filename regex */
115
+    size_t	csize[2];   /* container size (min, max); if csize[0] != csize[1]
116
+			     * then value of 0 makes the field ignored
117
+			     */
118
+    size_t	fsizec[2];  /* file size in container */
119
+    size_t	fsizer[2];  /* real file size */
120
+    int		encrypted;  /* file is encrypted; 2 == ignore */
121
+    void	*res1;	    /* reserved / format specific */
122
+    void	*res2;	    /* reserved / format specific */
123
+
124
+    struct cli_cdb *next;
125
+};
126
+
109 127
 struct cli_mtarget {
110 128
     cli_file_t target;
111 129
     const char *name;
... ...
@@ -152,4 +170,6 @@ int cli_caloff(const char *offstr, struct cli_target_info *info, fmap_t *map, un
152 152
 
153 153
 int cli_checkfp(int fd, cli_ctx *ctx);
154 154
 
155
+int cli_matchmeta(cli_ctx *ctx, cli_file_t ftype, const char *fname, size_t fsizec, size_t fsizer, int encrypted, void *res1, void *res2);
156
+
155 157
 #endif
... ...
@@ -111,6 +111,7 @@ typedef struct {
111 111
     unsigned int scannedfiles;
112 112
     unsigned int found_possibly_unwanted;
113 113
     cli_file_t container_type; /* FIXME: to be made into a stack or array - see bb#1579 & bb#1293 */
114
+    size_t container_size;
114 115
     struct cli_dconf *dconf;
115 116
     fmap_t **fmap;
116 117
 } cli_ctx;
... ...
@@ -199,6 +200,9 @@ struct cl_engine {
199 199
     /* RAR metadata */
200 200
     struct cli_meta_node *rar_mlist;
201 201
 
202
+    /* Container metadata */
203
+    struct cli_cdb *cdb;
204
+
202 205
     /* Phishing .pdb and .wdb databases*/
203 206
     struct regex_matcher *whitelist_matcher;
204 207
     struct regex_matcher *domainlist_matcher;
... ...
@@ -1869,6 +1869,200 @@ static int cli_loadmd(FILE *fs, struct cl_engine *engine, unsigned int *signo, i
1869 1869
     return CL_SUCCESS;
1870 1870
 }
1871 1871
 
1872
+/*    0		1	     2		3	       4	       5		 6	     7	      8     9    10     11
1873
+ * VirusName:ContainerType:FileType:FileNameREGEX:ContainerSize:FileSizeInContainer:FileSizeReal:IsEncrypted:Res1:Res2[:MinFL[:MaxFL]]
1874
+ */
1875
+#define CDB_TOKENS 12
1876
+static int cli_loadcdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
1877
+{
1878
+	const char *tokens[MD_TOKENS + 1];
1879
+	char buffer[FILEBUFF], *buffer_cpy;
1880
+	unsigned int line = 0, sigs = 0, tokens_count, n0, n1;
1881
+	int ret = CL_SUCCESS;
1882
+	struct cli_cdb *new;
1883
+
1884
+
1885
+    if(engine->ignored)
1886
+	if(!(buffer_cpy = cli_malloc(FILEBUFF)))
1887
+	    return CL_EMEM;
1888
+
1889
+    while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
1890
+	line++;
1891
+	if(buffer[0] == '#')
1892
+	    continue;
1893
+
1894
+	cli_chomp(buffer);
1895
+	if(engine->ignored)
1896
+	    strcpy(buffer_cpy, buffer);
1897
+
1898
+	tokens_count = cli_strtokenize(buffer, ':', CDB_TOKENS + 1, tokens);
1899
+	if(tokens_count > CDB_TOKENS) {
1900
+	    ret = CL_EMALFDB;
1901
+	    break;
1902
+	}
1903
+
1904
+	if(tokens_count > 10) { /* min version */
1905
+	    if(!cli_isnumber(tokens[10])) {
1906
+		ret = CL_EMALFDB;
1907
+		break;
1908
+	    }
1909
+	    if((unsigned int) atoi(tokens[10]) > cl_retflevel()) {
1910
+		cli_dbgmsg("cli_loadcdb: Container signature for %s not loaded (required f-level: %u)\n", tokens[0], atoi(tokens[10]));
1911
+		continue;
1912
+	    }
1913
+	    if(tokens_count == 12) { /* max version */
1914
+		if(!cli_isnumber(tokens[11])) {
1915
+		    ret = CL_EMALFDB;
1916
+		    break;
1917
+		}
1918
+		if((unsigned int) atoi(tokens[11]) < cl_retflevel())
1919
+		    continue;
1920
+	    }
1921
+	}
1922
+
1923
+	new = (struct cli_cdb *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_cdb));
1924
+	if(!new) {
1925
+	    ret = CL_EMEM;
1926
+	    break;
1927
+	}
1928
+
1929
+	new->virname = cli_mpool_virname(engine->mempool, (char *)tokens[0], options & CL_DB_OFFICIAL);
1930
+	if(!new->virname) {
1931
+	    mpool_free(engine->mempool, new);
1932
+	    ret = CL_EMEM;
1933
+	    break;
1934
+	}
1935
+
1936
+	if(engine->ignored && cli_chkign(engine->ignored, new->virname, buffer/*_cpy*/)) {
1937
+	    mpool_free(engine->mempool, new->virname);
1938
+	    mpool_free(engine->mempool, new);
1939
+	    continue;
1940
+	}
1941
+
1942
+	if(!strcmp(tokens[1], "*")) {
1943
+	    new->ctype = CL_TYPE_ANY;
1944
+	} else if((new->ctype = cli_ftcode(tokens[1])) == CL_TYPE_ERROR) {
1945
+	    cli_dbgmsg("cli_cdb: Unknown container type %s in signature for %s, skipping\n", tokens[1], tokens[0]);
1946
+	    mpool_free(engine->mempool, new->virname);
1947
+	    mpool_free(engine->mempool, new);
1948
+	    continue;
1949
+	}
1950
+
1951
+	if(!strcmp(tokens[2], "*")) {
1952
+	    new->ftype = CL_TYPE_ANY;
1953
+	} else if((new->ftype = cli_ftcode(tokens[2])) == CL_TYPE_ERROR) {
1954
+	    cli_dbgmsg("cli_cdb: Unknown file type %s in signature for %s, skipping\n", tokens[2], tokens[0]);
1955
+	    mpool_free(engine->mempool, new->virname);
1956
+	    mpool_free(engine->mempool, new);
1957
+	    continue;
1958
+	}
1959
+
1960
+	if(strcmp(tokens[3], "*") && cli_regcomp(&new->name, tokens[3], REG_EXTENDED | REG_NOSUB)) {
1961
+	    cli_errmsg("cli_cdb: Can't compile regular expression %s in signature for %s\n", tokens[3], tokens[0]);
1962
+	    mpool_free(engine->mempool, new->virname);
1963
+	    mpool_free(engine->mempool, new);
1964
+	    ret = CL_EMEM;
1965
+	    break;
1966
+	}
1967
+
1968
+#define CDBRANGE(token_str, dest)					    \
1969
+	if(strcmp(token_str, "*")) {					    \
1970
+	    if(strchr(token_str, '-')) {				    \
1971
+		if(sscanf(token_str, "%u-%u", &n0, &n1) != 2) {		    \
1972
+		    ret = CL_EMALFDB;					    \
1973
+		} else {						    \
1974
+		    dest[0] = n0;					    \
1975
+		    dest[1] = n1;					    \
1976
+		}							    \
1977
+	    } else {							    \
1978
+		if(!cli_isnumber(token_str))				    \
1979
+		    ret = CL_EMALFDB;					    \
1980
+		else							    \
1981
+		    dest[0] = dest[1] = atoi(token_str);		    \
1982
+	    }								    \
1983
+	    if(ret != CL_SUCCESS) {					    \
1984
+		cli_errmsg("cli_cdb: Invalid value %s in signature for %s\n",\
1985
+		    token_str, tokens[0]);				    \
1986
+		if(new->name.re_magic)					    \
1987
+		    cli_regfree(&new->name);				    \
1988
+		mpool_free(engine->mempool, new->virname);		    \
1989
+		mpool_free(engine->mempool, new);			    \
1990
+		ret = CL_EMEM;						    \
1991
+		break;							    \
1992
+	    }								    \
1993
+	} else {							    \
1994
+	    dest[0] = dest[1] = CLI_OFF_ANY;				    \
1995
+	}
1996
+
1997
+	CDBRANGE(tokens[4], new->csize);
1998
+	CDBRANGE(tokens[5], new->fsizec);
1999
+	CDBRANGE(tokens[6], new->fsizer);
2000
+
2001
+	if(!strcmp(tokens[7], "*")) {
2002
+	    new->encrypted = 2;
2003
+	} else {
2004
+	    if(strcmp(tokens[7], "0") && strcmp(tokens[7], "1")) {
2005
+		cli_errmsg("cli_cdb: Invalid encryption flag value in signature for %s\n", tokens[0]);
2006
+		if(new->name.re_magic)
2007
+		    cli_regfree(&new->name);
2008
+		mpool_free(engine->mempool, new->virname);
2009
+		mpool_free(engine->mempool, new);
2010
+		ret = CL_EMEM;
2011
+		break;
2012
+	    }
2013
+	    new->encrypted = *tokens[7] - 0x30;
2014
+	}
2015
+
2016
+	if(strcmp(tokens[8], "*")) {
2017
+	    new->res1 = cli_mpool_strdup(engine->mempool, tokens[8]);
2018
+	    if(!new->res1) {
2019
+		cli_errmsg("cli_cdb: Can't allocate memory for res1 in signature for %s\n", tokens[0]);
2020
+		if(new->name.re_magic)
2021
+		    cli_regfree(&new->name);
2022
+		mpool_free(engine->mempool, new->virname);
2023
+		mpool_free(engine->mempool, new);
2024
+		ret = CL_EMEM;
2025
+		break;
2026
+	    }
2027
+	}
2028
+
2029
+	if(strcmp(tokens[9], "*")) {
2030
+	    new->res2 = cli_mpool_strdup(engine->mempool, tokens[9]);
2031
+	    if(!new->res2) {
2032
+		cli_errmsg("cli_cdb: Can't allocate memory for res2 in signature for %s\n", tokens[0]);
2033
+		if(new->name.re_magic)
2034
+		    cli_regfree(&new->name);
2035
+		mpool_free(engine->mempool, new->res1);
2036
+		mpool_free(engine->mempool, new->virname);
2037
+		mpool_free(engine->mempool, new);
2038
+		ret = CL_EMEM;
2039
+		break;
2040
+	    }
2041
+	}
2042
+
2043
+	new->next = engine->cdb;
2044
+	engine->cdb = new;
2045
+	sigs++;
2046
+    }
2047
+    if(engine->ignored)
2048
+	free(buffer_cpy);
2049
+
2050
+    if(!line) {
2051
+	cli_errmsg("Empty database file\n");
2052
+	return CL_EMALFDB;
2053
+    }
2054
+
2055
+    if(ret) {
2056
+	cli_errmsg("Problem parsing database at line %u\n", line);
2057
+	return ret;
2058
+    }
2059
+
2060
+    if(signo)
2061
+	*signo += sigs;
2062
+
2063
+    return CL_SUCCESS;
2064
+}
2065
+
1872 2066
 static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options);
1873 2067
 
1874 2068
 int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
... ...
@@ -1980,6 +2174,9 @@ int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo
1980 1980
     } else if(cli_strbcasestr(dbname, ".idb")) {
1981 1981
     	ret = cli_loadidb(fs, engine, signo, options, dbio);
1982 1982
 
1983
+    } else if(cli_strbcasestr(dbname, ".cdb")) {
1984
+    	ret = cli_loadcdb(fs, engine, signo, options, dbio);
1985
+
1983 1986
     } else {
1984 1987
 	cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
1985 1988
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
... ...
@@ -2494,6 +2691,17 @@ int cl_engine_free(struct cl_engine *engine)
2494 2494
 	mpool_free(engine->mempool, metah);
2495 2495
     }
2496 2496
 
2497
+    while(engine->cdb) {
2498
+	struct cli_cdb *pt = engine->cdb;
2499
+	engine->cdb = pt->next;
2500
+	if(pt->name.re_magic)
2501
+	    cli_regfree(&pt->name);
2502
+	mpool_free(engine->mempool, pt->res2);
2503
+	mpool_free(engine->mempool, pt->res1);
2504
+	mpool_free(engine->mempool, pt->virname);
2505
+	mpool_free(engine->mempool, pt);
2506
+    }
2507
+
2497 2508
     if(engine->dconf->bytecode & BYTECODE_ENGINE_MASK) {
2498 2509
 	unsigned i;
2499 2510
 	if (engine->bcs.all_bcs)
... ...
@@ -52,6 +52,7 @@
52 52
 	cli_strbcasestr(ext, ".cfg")   ||	\
53 53
 	cli_strbcasestr(ext, ".cvd")   ||	\
54 54
 	cli_strbcasestr(ext, ".cld")   ||	\
55
+	cli_strbcasestr(ext, ".cdb")   ||	\
55 56
 	cli_strbcasestr(ext, ".idb")		\
56 57
     )
57 58
 
... ...
@@ -978,7 +978,6 @@ static int cli_scanscript(cli_ctx *ctx)
978 978
 	unsigned char *buff;
979 979
 	unsigned char* normalized;
980 980
 	struct text_norm_state state;
981
-	struct stat sb;
982 981
 	char *tmpname = NULL;
983 982
 	int ofd = -1, ret;
984 983
 	const struct cli_matcher *troot = ctx->engine->root[7];
... ...
@@ -1529,9 +1528,7 @@ static int cli_scanmail(int desc, cli_ctx *ctx)
1529 1529
 	return ret;
1530 1530
     }
1531 1531
 
1532
-    ctx->container_type = CL_TYPE_MAIL;
1533 1532
     ret = cli_scandir(dir, ctx);
1534
-    ctx->container_type = 0;
1535 1533
 
1536 1534
     if(!ctx->engine->keeptmp)
1537 1535
 	cli_rmdirs(dir);
... ...
@@ -1839,7 +1836,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1839 1839
 	cli_file_t type, dettype = 0;
1840 1840
 	struct stat sb;
1841 1841
 	uint8_t typercg = 1;
1842
-	cli_file_t current_container = ctx->container_type; /* TODO: container tracking code TBD - bb#1293 */
1842
+	cli_file_t current_container_type = ctx->container_type; /* TODO: container tracking code TBD - bb#1293 */
1843
+	size_t current_container_size = ctx->container_size;
1843 1844
 
1844 1845
     if(ctx->engine->maxreclevel && ctx->recursion > ctx->engine->maxreclevel) {
1845 1846
         cli_dbgmsg("cli_magic_scandesc: Archive recursion limit exceeded (%u, max: %u)\n", ctx->recursion, ctx->engine->maxreclevel);
... ...
@@ -1906,18 +1904,21 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1906 1906
 	lseek(desc, 0, SEEK_SET); /* FIXMEFMAP: remove ? */
1907 1907
     }
1908 1908
 
1909
-    ctx->container_type = 0;
1910 1909
     ctx->recursion++;
1911 1910
     switch(type) {
1912 1911
 	case CL_TYPE_IGNORED:
1913 1912
 	    break;
1914 1913
 
1915 1914
 	case CL_TYPE_RAR:
1915
+	    ctx->container_type = CL_TYPE_RAR;
1916
+	    ctx->container_size = sb.st_size;
1916 1917
 	    if(have_rar && SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_RAR))
1917 1918
 		ret = cli_scanrar(desc, ctx, 0, NULL);
1918 1919
 	    break;
1919 1920
 
1920 1921
 	case CL_TYPE_ZIP:
1922
+	    ctx->container_type = CL_TYPE_ZIP;
1923
+	    ctx->container_size = sb.st_size;
1921 1924
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ZIP))
1922 1925
 		ret = cli_unzip(ctx);
1923 1926
 	    break;
... ...
@@ -1931,17 +1932,24 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1931 1931
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_BZ))
1932 1932
 		ret = cli_scanbzip(desc, ctx);
1933 1933
 	    break;
1934
+
1934 1935
 	case CL_TYPE_ARJ:
1936
+	    ctx->container_type = CL_TYPE_ARJ;
1937
+	    ctx->container_size = sb.st_size;
1935 1938
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ARJ))
1936 1939
 		ret = cli_scanarj(desc, ctx, 0, NULL);
1937 1940
 	    break;
1938 1941
 
1939 1942
         case CL_TYPE_NULSFT:
1940
-	  if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_NSIS))
1943
+	    ctx->container_type = CL_TYPE_NULSFT;
1944
+	    ctx->container_size = sb.st_size;
1945
+	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_NSIS))
1941 1946
 		ret = cli_scannulsft(desc, ctx, 0);
1942 1947
 	    break;
1943 1948
 
1944 1949
         case CL_TYPE_AUTOIT:
1950
+	    ctx->container_type = CL_TYPE_AUTOIT;
1951
+	    ctx->container_size = sb.st_size;
1945 1952
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_AUTOIT))
1946 1953
 		ret = cli_scanautoit(desc, ctx, 23);
1947 1954
 	    break;
... ...
@@ -1952,6 +1960,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1952 1952
 	    break;
1953 1953
 
1954 1954
 	case CL_TYPE_MSCAB:
1955
+	    ctx->container_type = CL_TYPE_MSCAB;
1956
+	    ctx->container_size = sb.st_size;
1955 1957
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_CAB))
1956 1958
 		ret = cli_scanmscab(desc, ctx, 0);
1957 1959
 	    break;
... ...
@@ -1972,11 +1982,15 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1972 1972
 	    break;
1973 1973
 
1974 1974
 	case CL_TYPE_RTF:
1975
+	    ctx->container_type = CL_TYPE_RTF;
1976
+	    ctx->container_size = sb.st_size;
1975 1977
 	    if(SCAN_ARCHIVE && (DCONF_DOC & DOC_CONF_RTF))
1976 1978
 		ret = cli_scanrtf(desc, ctx);
1977 1979
 	    break;
1978 1980
 
1979 1981
 	case CL_TYPE_MAIL:
1982
+	    ctx->container_type = CL_TYPE_MAIL;
1983
+	    ctx->container_size = sb.st_size;
1980 1984
 	    if(SCAN_MAIL && (DCONF_MAIL & MAIL_CONF_MBOX))
1981 1985
 		ret = cli_scanmail(desc, ctx);
1982 1986
 	    break;
... ...
@@ -1992,46 +2006,64 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1992 1992
 	    break;
1993 1993
 
1994 1994
 	case CL_TYPE_MSCHM:
1995
+	    ctx->container_type = CL_TYPE_MSCHM;
1996
+	    ctx->container_size = sb.st_size;
1995 1997
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_CHM))
1996 1998
 		ret = cli_scanmschm(desc, ctx);
1997 1999
 	    break;
1998 2000
 
1999 2001
 	case CL_TYPE_MSOLE2:
2002
+	    ctx->container_type = CL_TYPE_MSOLE2;
2003
+	    ctx->container_size = sb.st_size;
2000 2004
 	    if(SCAN_OLE2 && (DCONF_ARCH & ARCH_CONF_OLE2))
2001 2005
 		ret = cli_scanole2(ctx);
2002 2006
 	    break;
2003 2007
 
2004 2008
 	case CL_TYPE_7Z:
2009
+	    ctx->container_type = CL_TYPE_7Z;
2010
+	    ctx->container_size = sb.st_size;
2005 2011
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_7Z))
2006 2012
 		ret = cli_7unz(desc, ctx);
2007 2013
 	    break;
2008 2014
 
2009 2015
 	case CL_TYPE_POSIX_TAR:
2016
+	    ctx->container_type = CL_TYPE_POSIX_TAR;
2017
+	    ctx->container_size = sb.st_size;
2010 2018
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_TAR))
2011 2019
 		ret = cli_scantar(desc, ctx, 1);
2012 2020
 	    break;
2013 2021
 
2014 2022
 	case CL_TYPE_OLD_TAR:
2023
+	    ctx->container_type = CL_TYPE_OLD_TAR;
2024
+	    ctx->container_size = sb.st_size;
2015 2025
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_TAR))
2016 2026
 		ret = cli_scantar(desc, ctx, 0);
2017 2027
 	    break;
2018 2028
 
2019 2029
 	case CL_TYPE_CPIO_OLD:
2030
+	    ctx->container_type = CL_TYPE_CPIO_OLD;
2031
+	    ctx->container_size = sb.st_size;
2020 2032
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_CPIO))
2021 2033
 		ret = cli_scancpio_old(desc, ctx);
2022 2034
 	    break;
2023 2035
 
2024 2036
 	case CL_TYPE_CPIO_ODC:
2037
+	    ctx->container_type = CL_TYPE_CPIO_ODC;
2038
+	    ctx->container_size = sb.st_size;
2025 2039
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_CPIO))
2026 2040
 		ret = cli_scancpio_odc(desc, ctx);
2027 2041
 	    break;
2028 2042
 
2029 2043
 	case CL_TYPE_CPIO_NEWC:
2044
+	    ctx->container_type = CL_TYPE_CPIO_NEWC;
2045
+	    ctx->container_size = sb.st_size;
2030 2046
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_CPIO))
2031 2047
 		ret = cli_scancpio_newc(desc, ctx, 0);
2032 2048
 	    break;
2033 2049
 
2034 2050
 	case CL_TYPE_CPIO_CRC:
2051
+	    ctx->container_type = CL_TYPE_CPIO_CRC;
2052
+	    ctx->container_size = sb.st_size;
2035 2053
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_CPIO))
2036 2054
 		ret = cli_scancpio_newc(desc, ctx, 1);
2037 2055
 	    break;
... ...
@@ -2057,6 +2089,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
2057 2057
 	    break;
2058 2058
 
2059 2059
         case CL_TYPE_PDF: /* FIXMELIMITS: pdf should be an archive! */
2060
+	    ctx->container_type = CL_TYPE_PDF;
2061
+	    ctx->container_size = sb.st_size;
2060 2062
 	    if(SCAN_PDF && (DCONF_DOC & DOC_CONF_PDF))
2061 2063
 		ret = cli_scanpdf(ctx, 0);
2062 2064
 	    break;
... ...
@@ -2082,6 +2116,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
2082 2082
 	    break;
2083 2083
 
2084 2084
 	case CL_TYPE_SIS:
2085
+	    ctx->container_type = CL_TYPE_SIS;
2086
+	    ctx->container_size = sb.st_size;
2085 2087
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_SIS))
2086 2088
 		ret = cli_scansis(desc, ctx);
2087 2089
 	    break;
... ...
@@ -2103,7 +2139,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
2103 2103
 	    break;
2104 2104
     }
2105 2105
     ctx->recursion--;
2106
-    ctx->container_type = current_container;
2106
+    ctx->container_type = current_container_type;
2107
+    ctx->container_size = current_container_size;
2107 2108
 
2108 2109
     if(ret == CL_VIRUS) {
2109 2110
 	ret = cli_checkfp(desc, ctx) ? CL_CLEAN : CL_VIRUS;
... ...
@@ -2184,7 +2221,8 @@ int cl_scandesc(int desc, const char **virname, unsigned long int *scanned, cons
2184 2184
     ctx.scanned = scanned;
2185 2185
     ctx.options = scanoptions;
2186 2186
     ctx.found_possibly_unwanted = 0;
2187
-    ctx.container_type = 0;
2187
+    ctx.container_type = CL_TYPE_ANY;
2188
+    ctx.container_size = 0;
2188 2189
     ctx.dconf = (struct cli_dconf *) engine->dconf;
2189 2190
     ctx.fmap = cli_calloc(sizeof(fmap_t *), ctx.engine->maxreclevel + 1);
2190 2191
     if(!ctx.fmap)