Browse code

clamscan: new flag --archive-verbose

This shows the archive member names (if known) when a virus is found.
When combined with -v it shows the archive member names while scanning too.

Török Edvin authored on 2011/08/22 22:58:48
Showing 5 changed files
... ...
@@ -195,6 +195,7 @@ void help(void)
195 195
     mprintf("    --help                -h             Print this help screen\n");
196 196
     mprintf("    --version             -V             Print version number\n");
197 197
     mprintf("    --verbose             -v             Be verbose\n");
198
+    mprintf("    --archive-verbose     -a             Show filenames inside scanned archives\n");
198 199
     mprintf("    --debug                              Enable libclamav's debug messages\n");
199 200
     mprintf("    --quiet                              Only output error messages\n");
200 201
     mprintf("    --stdout                             Write to stdout instead of stderr\n");
... ...
@@ -120,12 +120,104 @@ static int checkaccess(const char *path, const char *username, int mode)
120 120
 }
121 121
 #endif
122 122
 
123
+struct metachain {
124
+    char **chains;
125
+    unsigned lastadd;
126
+    unsigned lastvir;
127
+    unsigned level;
128
+    unsigned n;
129
+};
130
+
131
+static cl_error_t pre(int fd, const char *type, void *context)
132
+{
133
+    struct metachain *c = context;
134
+    if (c) {
135
+	c->level++;
136
+    }
137
+    return CL_CLEAN;
138
+}
139
+
140
+static int print_chain(struct metachain *c, char *str, unsigned len)
141
+{
142
+    unsigned i;
143
+    unsigned na = 0;
144
+    for (i=0;i<c->n-1;i++) {
145
+	unsigned int n = strlen(c->chains[i]);
146
+	if (na)
147
+	    str[na++] = '!';
148
+	if (n + na + 2 > len)
149
+	    break;
150
+	memcpy(str + na, c->chains[i], n);
151
+	na += n;
152
+    }
153
+    str[na] = '\0';
154
+    str[len-1] = '\0';
155
+    return i == c->n-1 ? 0 : 1;
156
+}
157
+
158
+static cl_error_t post(int fd, int result, const char *virname, void *context)
159
+{
160
+    struct metachain *c = context;
161
+    if (c && c->n) {
162
+	char str[128];
163
+	int toolong = print_chain(c, str, sizeof(str));
164
+	if (c->level == c->lastadd && !virname)
165
+	    free(c->chains[--c->n]);
166
+	if (virname && !c->lastvir)
167
+	    c->lastvir = c->level;
168
+    }
169
+    if (c)
170
+	c->level--;
171
+    return CL_CLEAN;
172
+}
173
+
174
+static cl_error_t meta(const char* container_type, unsigned long fsize_container, const char *filename,
175
+		       unsigned long fsize_real,  int is_encrypted, unsigned int filepos_container, void *context)
176
+{
177
+    int na = 0;
178
+    char prev[128];
179
+    struct metachain *c = context;
180
+    const char *type = !strncmp(container_type,"CL_TYPE_",8) ? container_type + 8 : container_type;
181
+    unsigned n = strlen(type) + 1 + strlen(filename) + 1;
182
+    char *chain;
183
+    char **chains;
184
+    int toolong;
185
+
186
+    if (!c)
187
+	return CL_CLEAN;
188
+    chain = malloc(n);
189
+    if (!chain)
190
+	return CL_CLEAN;
191
+    if (!strcmp(type, "ANY"))
192
+	snprintf(chain, n,"%s", filename);
193
+    else
194
+	snprintf(chain, n,"%s:%s", type, filename);
195
+    if (c->lastadd != c->level) {
196
+	n = c->n + 1;
197
+	chains = realloc(c->chains, n * sizeof(*chains));
198
+	if (!chains) {
199
+	    free(chain);
200
+	    return CL_CLEAN;
201
+	}
202
+	c->chains = chains;
203
+	c->n = n;
204
+	c->lastadd = c->level;
205
+    } else {
206
+	free(c->chains[c->n-1]);
207
+    }
208
+    c->chains[c->n-1] = chain;
209
+    toolong = print_chain(c, prev, sizeof(prev));
210
+    logg("*Scanning %s%s!%s\n", prev,toolong ? "..." : "", chain);
211
+    return CL_CLEAN;
212
+}
213
+
123 214
 static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, unsigned int options)
124 215
 {
125
-	int ret = 0, fd, included, printclean = 1;
216
+	int ret = 0, fd, included, printclean = 1, i;
126 217
 	const struct optstruct *opt;
127 218
 	const char *virname;
128 219
 	struct stat sb;
220
+	struct metachain chain;
129 221
 
130 222
     if((opt = optget(opts, "exclude"))->enabled) {
131 223
 	while(opt) {
... ...
@@ -181,6 +273,14 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
181 181
 	}
182 182
 #endif
183 183
 
184
+    memset(&chain, 0, sizeof(chain));
185
+    if(optget(opts, "archive-verbose")->enabled) {
186
+	chain.chains = malloc(sizeof(*chain.chains));
187
+	if (chain.chains) {
188
+	    chain.chains[0] = strdup(filename);
189
+	    chain.n = 1;
190
+	}
191
+    }
184 192
     logg("*Scanning %s\n", filename);
185 193
 
186 194
     if((fd = safe_open(filename, O_RDONLY|O_BINARY)) == -1) {
... ...
@@ -189,7 +289,16 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
189 189
 	return;
190 190
     }
191 191
 
192
-    if((ret = cl_scandesc(fd, &virname, &info.blocks, engine, options)) == CL_VIRUS) {
192
+
193
+    if((ret = cl_scandesc_callback(fd, &virname, &info.blocks, engine, options, &chain)) == CL_VIRUS) {
194
+	if(optget(opts, "archive-verbose")->enabled) {
195
+	    if (chain.n > 1) {
196
+		char str[128];
197
+		int toolong = print_chain(&chain, str, sizeof(str));
198
+		logg("~%s%s!(%d)%s: %s FOUND\n", str, toolong ? "..." : "", chain.lastvir-1, chain.chains[chain.n-1], virname);
199
+	    } else if (chain.lastvir)
200
+		logg("~%s!(%d): %s FOUND\n", filename, chain.lastvir-1, virname);
201
+	}
193 202
 	logg("~%s: %s FOUND\n", filename, virname);
194 203
 	info.files++;
195 204
 	info.ifiles++;
... ...
@@ -207,6 +316,9 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
207 207
 	info.errors++;
208 208
     }
209 209
 
210
+    for (i=0;i<chain.n;i++)
211
+	free(chain.chains[i]);
212
+    free(chain.chains);
210 213
     close(fd);
211 214
 
212 215
     if(ret == CL_VIRUS && action)
... ...
@@ -541,6 +653,12 @@ int scanmanager(const struct optstruct *opts)
541 541
 	return 2;
542 542
     }
543 543
 
544
+    if(optget(opts, "archive-verbose")->enabled) {
545
+	cl_engine_set_clcb_meta(engine, meta);
546
+	cl_engine_set_clcb_pre_scan(engine, pre);
547
+	cl_engine_set_clcb_post_scan(engine, post);
548
+    }
549
+
544 550
     /* set limits */
545 551
 
546 552
     if((opt = optget(opts, "max-scansize"))->active) {
... ...
@@ -343,6 +343,7 @@ int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz) {
343 343
     off_t coff = off;
344 344
     struct IS_CABSTUFF c = { NULL, -1, 0, 0 };
345 345
     fmap_t *map = *ctx->fmap;
346
+    unsigned fc = 0;
346 347
 
347 348
     while(ret == CL_CLEAN) {
348 349
 	fname = fmap_need_offstr(map, coff, 2048);
... ...
@@ -371,6 +372,10 @@ int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz) {
371 371
 	) break;
372 372
 
373 373
 	cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int) coff, fname, path, version, (unsigned long int) fsize);
374
+	if(cli_matchmeta(ctx, fname, fsize, fsize, 0, fc++, 0, NULL) == CL_VIRUS) {
375
+	    ret = CL_VIRUS;
376
+	    break;
377
+	}
374 378
 	sz -= (data - fname) + fsize;
375 379
 
376 380
 	if(!strncasecmp(fname, "data", 4)) {
... ...
@@ -824,6 +824,7 @@ int cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t fsizer,
824 824
     if (ctx->engine && ctx->engine->cb_meta)
825 825
 	if (ctx->engine->cb_meta(cli_ftname(ctx->container_type), fsizec, fname, fsizer, encrypted, filepos, ctx->cb_ctx) == CL_VIRUS) {
826 826
 	    cli_dbgmsg("inner file blacklisted by callback: %s\n", fname);
827
+	    *ctx->virname = "Detected.By.Callback";
827 828
 	    return CL_VIRUS;
828 829
 	}
829 830
 
... ...
@@ -134,6 +134,8 @@ const struct clam_option __clam_options[] = {
134 134
     { NULL, "trace", 't', TYPE_NUMBER, MATCH_NUMBER, 7, NULL, 0, OPT_CLAMBC, "bytecode trace level",""},
135 135
     { NULL, "no-trace-showsource", 's', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMBC, "Don't show source line during tracing",""},
136 136
 
137
+    { NULL, "archive-verbose", 'a', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN, "", ""},
138
+
137 139
     /* cmdline only - deprecated */
138 140
     { NULL, "bytecode-trust-all", 't', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN | OPT_DEPRECATED, "", ""},
139 141
     { NULL, "http-proxy", 0, TYPE_STRING, NULL, 0, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", "" },