... | ... |
@@ -25,6 +25,8 @@ |
25 | 25 |
#include "clamav-config.h" |
26 | 26 |
#endif |
27 | 27 |
|
28 |
+#include <errno.h> |
|
29 |
+ |
|
28 | 30 |
#include "clamav.h" |
29 | 31 |
#include "cltypes.h" |
30 | 32 |
#include "others.h" |
... | ... |
@@ -67,8 +69,9 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons |
67 | 67 |
size_t toks = 0; |
68 | 68 |
int16_t ref_subsigid = -1; |
69 | 69 |
int64_t offset_param = 0; |
70 |
+ int64_t ret = CL_SUCCESS; |
|
70 | 71 |
size_t byte_length = 0; |
71 |
- uint32_t comp_val = 0; |
|
72 |
+ uint64_t comp_val = 0; |
|
72 | 73 |
char *hexcpy = NULL; |
73 | 74 |
|
74 | 75 |
if (!hexsig || !(*hexsig) || !root || !virname) { |
... | ... |
@@ -210,31 +213,64 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons |
210 | 210 |
/* the byte length indicator options are stored in a bitmask--by design each option gets its own nibble */ |
211 | 211 |
buf_start = tokens[1]; |
212 | 212 |
|
213 |
- switch (*buf_start) { |
|
214 |
- case 'h': bcomp->options |= CLI_BCOMP_HEX; break; |
|
215 |
- case 'd': bcomp->options |= CLI_BCOMP_DEC; break; |
|
216 |
- |
|
217 |
- default: |
|
218 |
- cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), hex/decimal byte length indicator was found invalid\n", tokens[0], tokens[1], tokens[2]); |
|
219 |
- free(buf); |
|
220 |
- cli_bcomp_freemeta(root, bcomp); |
|
221 |
- return CL_EMALFDB; |
|
222 |
- } |
|
213 |
+ while (!isdigit(*buf_start)) { |
|
214 |
+ |
|
215 |
+ switch (*buf_start) { |
|
216 |
+ case 'h': |
|
217 |
+ /* hex, decimal, and binary options are mutually exclusive parameters */ |
|
218 |
+ if (bcomp->options & CLI_BCOMP_DEC || bcomp->options & CLI_BCOMP_BIN) { |
|
219 |
+ ret = CL_EMALFDB; |
|
220 |
+ } else { |
|
221 |
+ bcomp->options |= CLI_BCOMP_HEX; |
|
222 |
+ } break; |
|
223 |
+ case 'd': |
|
224 |
+ /* hex, decimal, and binary options are mutually exclusive parameters */ |
|
225 |
+ if (bcomp->options & CLI_BCOMP_HEX || bcomp->options & CLI_BCOMP_BIN) { |
|
226 |
+ ret = CL_EMALFDB; |
|
227 |
+ } else { |
|
228 |
+ bcomp->options |= CLI_BCOMP_DEC; |
|
229 |
+ } break; |
|
230 |
+ case 'i': |
|
231 |
+ /* hex, decimal, and binary options are mutually exclusive parameters */ |
|
232 |
+ if (bcomp->options & CLI_BCOMP_HEX || bcomp->options & CLI_BCOMP_DEC) { |
|
233 |
+ ret = CL_EMALFDB; |
|
234 |
+ } else { |
|
235 |
+ bcomp->options |= CLI_BCOMP_BIN; |
|
236 |
+ } break; |
|
237 |
+ case 'l': |
|
238 |
+ /* little and big endian options are mutually exclusive parameters */ |
|
239 |
+ if (bcomp->options & CLI_BCOMP_BE) { |
|
240 |
+ ret = CL_EMALFDB; |
|
241 |
+ } else { |
|
242 |
+ bcomp->options |= CLI_BCOMP_LE; |
|
243 |
+ } break; |
|
244 |
+ case 'b': |
|
245 |
+ /* little and big endian options are mutually exclusive parameters */ |
|
246 |
+ if (bcomp->options & CLI_BCOMP_LE) { |
|
247 |
+ ret = CL_EMALFDB; |
|
248 |
+ } else { |
|
249 |
+ bcomp->options |= CLI_BCOMP_BE; |
|
250 |
+ } break; |
|
251 |
+ case 'e': |
|
252 |
+ /* for exact byte length matches */ |
|
253 |
+ bcomp->options |= CLI_BCOMP_EXACT; |
|
254 |
+ break; |
|
223 | 255 |
|
224 |
- buf_start++; |
|
225 |
- switch (*buf_start) { |
|
226 |
- case 'l': bcomp->options |= CLI_BCOMP_LE; break; |
|
227 |
- case 'b': bcomp->options |= CLI_BCOMP_BE; break; |
|
256 |
+ default: |
|
257 |
+ ret = CL_EMALFDB; |
|
258 |
+ break; |
|
259 |
+ } |
|
228 | 260 |
|
229 |
- default: |
|
230 |
- cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), little/big endian byte length indicator was invalid\n", tokens[0], tokens[1], tokens[2]); |
|
261 |
+ if (CL_EMALFDB == ret) { |
|
262 |
+ cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), option parameter was found invalid\n", tokens[0], tokens[1], tokens[2]); |
|
231 | 263 |
free(buf); |
232 | 264 |
cli_bcomp_freemeta(root, bcomp); |
233 |
- return CL_EMALFDB; |
|
265 |
+ return ret; |
|
266 |
+ } |
|
267 |
+ buf_start++; |
|
234 | 268 |
} |
235 | 269 |
|
236 | 270 |
/* parse out the byte length parameter */ |
237 |
- buf_start++; |
|
238 | 271 |
buf_end = NULL; |
239 | 272 |
byte_length = strtol(buf_start, (char **) &buf_end, 0); |
240 | 273 |
if (buf_end && buf_end+1 != tokens[2]) { |
... | ... |
@@ -244,6 +280,13 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons |
244 | 244 |
return CL_EMALFDB; |
245 | 245 |
} |
246 | 246 |
|
247 |
+ if (bcomp->options | CLI_BCOMP_BIN && byte_length > CLI_BCOMP_MAX_BIN_BLEN) { |
|
248 |
+ cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), byte length for binary extraction was too long (max size (%d))\n", tokens[0], tokens[1], tokens[3], CLI_BCOMP_MAX_BIN_BLEN); |
|
249 |
+ free(buf); |
|
250 |
+ cli_bcomp_freemeta(root, bcomp); |
|
251 |
+ return CL_EMALFDB; |
|
252 |
+ } |
|
253 |
+ |
|
247 | 254 |
bcomp->byte_len = byte_length; |
248 | 255 |
|
249 | 256 |
/* currently only >, <, and = are supported comparison symbols--this makes parsing very simple */ |
... | ... |
@@ -262,10 +305,10 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons |
262 | 262 |
} |
263 | 263 |
|
264 | 264 |
|
265 |
- /* no more tokens after this, so we take advantage of strtol and check if the buf_end is null terminated or not */ |
|
265 |
+ /* no more tokens after this, so we take advantage of strtoll and check if the buf_end is null terminated or not */ |
|
266 | 266 |
buf_start++; |
267 | 267 |
buf_end = NULL; |
268 |
- comp_val = strtol(buf_start, (char **) &buf_end, 0); |
|
268 |
+ comp_val = strtoll(buf_start, (char **) &buf_end, 0); |
|
269 | 269 |
if (*buf_end) { |
270 | 270 |
cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), comparison value contained invalid input\n", tokens[0], tokens[1], tokens[2]); |
271 | 271 |
free(buf); |
... | ... |
@@ -276,12 +319,13 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons |
276 | 276 |
bcomp->comp_value = comp_val; |
277 | 277 |
|
278 | 278 |
/* manually verify successful pattern parsing */ |
279 |
- bcm_dbgmsg("Matcher Byte Compare: (%s%ld#%c%c%zu#%c%d)\n", |
|
279 |
+ bcm_dbgmsg("Matcher Byte Compare: (%s%ld#%c%c%s%zu#%c%lu)\n", |
|
280 | 280 |
bcomp->offset == 0 ? "" : |
281 | 281 |
(bcomp->offset < 0 ? "<<" : ">>"), |
282 | 282 |
bcomp->offset, |
283 |
- bcomp->options & CLI_BCOMP_HEX ? 'h' : 'd', |
|
283 |
+ bcomp->options & CLI_BCOMP_HEX ? 'h' : (bcomp->options & CLI_BCOMP_DEC ? 'd' : 'i'), |
|
284 | 284 |
bcomp->options & CLI_BCOMP_LE ? 'l' : 'b', |
285 |
+ bcomp->options & CLI_BCOMP_EXACT ? "e" : "", |
|
285 | 286 |
bcomp->byte_len, |
286 | 287 |
bcomp->comp_symbol, |
287 | 288 |
bcomp->comp_value); |
... | ... |
@@ -409,10 +453,11 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met |
409 | 409 |
|
410 | 410 |
uint32_t byte_len = 0; |
411 | 411 |
uint32_t length = 0; |
412 |
+ uint16_t opt = 0; |
|
412 | 413 |
const unsigned char *buffer = NULL; |
413 | 414 |
unsigned char *conversion_buf = NULL; |
414 |
- char opt = (char) bm->options; |
|
415 |
- uint32_t value = 0; |
|
415 |
+ uint64_t value = 0; |
|
416 |
+ uint64_t *bin_value = NULL; |
|
416 | 417 |
const unsigned char* end_buf = NULL; |
417 | 418 |
|
418 | 419 |
if (!map || !bm) { |
... | ... |
@@ -422,6 +467,7 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met |
422 | 422 |
|
423 | 423 |
byte_len = bm->byte_len; |
424 | 424 |
length = map->len; |
425 |
+ opt = bm->options; |
|
425 | 426 |
|
426 | 427 |
/* ensure we won't run off the end of the file buffer */ |
427 | 428 |
if (bm->offset > 0) { |
... | ... |
@@ -443,57 +489,110 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met |
443 | 443 |
bcm_dbgmsg("bcmp_compare_check: could not extract bytes from buffer offset\n"); |
444 | 444 |
return CL_EMEM; |
445 | 445 |
} |
446 |
- bcm_dbgmsg("bcmp_compare_check: literal extracted bytes before comparison (%s)\n", buffer); |
|
446 |
+ bcm_dbgmsg("bcmp_compare_check: literal extracted bytes before comparison %s\n", buffer); |
|
447 | 447 |
|
448 |
- /* handle byte length options to convert the string appropriately */ |
|
449 |
- switch(opt) { |
|
448 |
+ /* grab the first byte to handle byte length options to convert the string appropriately */ |
|
449 |
+ switch((opt & 0x00FF)) { |
|
450 | 450 |
/*hl*/ |
451 | 451 |
case CLI_BCOMP_HEX | CLI_BCOMP_LE: |
452 |
+ errno = 0; |
|
452 | 453 |
value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 16); |
453 |
- if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) { |
|
454 |
+ if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) { |
|
455 |
+ |
|
454 | 456 |
bcm_dbgmsg("bcmp_compare_check: little endian hex conversion unsuccessful\n"); |
455 | 457 |
return CL_CLEAN; |
456 | 458 |
} |
459 |
+ /*hle*/ |
|
460 |
+ if (opt & CLI_BCOMP_EXACT) { |
|
461 |
+ if (buffer+byte_len != end_buf) { |
|
462 |
+ |
|
463 |
+ bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n"); |
|
464 |
+ return CL_CLEAN; |
|
465 |
+ } |
|
466 |
+ } |
|
457 | 467 |
|
458 |
- value = le32_to_host(value); |
|
468 |
+ value = le64_to_host(value); |
|
459 | 469 |
break; |
460 | 470 |
|
461 | 471 |
/*hb*/ |
462 | 472 |
case CLI_BCOMP_HEX | CLI_BCOMP_BE: |
463 | 473 |
value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 16); |
464 |
- if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) { |
|
474 |
+ if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) { |
|
465 | 475 |
|
466 | 476 |
bcm_dbgmsg("bcmp_compare_check: big endian hex conversion unsuccessful\n"); |
467 | 477 |
return CL_CLEAN; |
468 | 478 |
} |
479 |
+ /*hbe*/ |
|
480 |
+ if (opt & CLI_BCOMP_EXACT) { |
|
481 |
+ if (buffer+byte_len != end_buf) { |
|
469 | 482 |
|
470 |
- value = be32_to_host(value); |
|
483 |
+ bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n"); |
|
484 |
+ return CL_CLEAN; |
|
485 |
+ } |
|
486 |
+ } |
|
487 |
+ |
|
488 |
+ value = be64_to_host(value); |
|
471 | 489 |
break; |
472 | 490 |
|
473 | 491 |
/*dl*/ |
474 | 492 |
case CLI_BCOMP_DEC | CLI_BCOMP_LE: |
475 | 493 |
value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 10); |
476 |
- if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) { |
|
494 |
+ if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) { |
|
477 | 495 |
|
478 | 496 |
bcm_dbgmsg("bcmp_compare_check: little endian decimal conversion unsuccessful\n"); |
479 | 497 |
return CL_CLEAN; |
480 | 498 |
} |
499 |
+ /*dle*/ |
|
500 |
+ if (opt & CLI_BCOMP_EXACT) { |
|
501 |
+ if (buffer+byte_len != end_buf) { |
|
502 |
+ |
|
503 |
+ bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n"); |
|
504 |
+ return CL_CLEAN; |
|
505 |
+ } |
|
506 |
+ } |
|
481 | 507 |
|
482 |
- value = le32_to_host(value); |
|
508 |
+ value = le64_to_host(value); |
|
483 | 509 |
break; |
484 | 510 |
|
485 | 511 |
/*db*/ |
486 | 512 |
case CLI_BCOMP_DEC | CLI_BCOMP_BE: |
487 | 513 |
value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 10); |
488 |
- if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) { |
|
514 |
+ if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) { |
|
489 | 515 |
|
490 | 516 |
bcm_dbgmsg("bcmp_compare_check: big endian decimal conversion unsuccessful\n"); |
491 | 517 |
return CL_CLEAN; |
492 | 518 |
} |
519 |
+ /*dbe*/ |
|
520 |
+ if (opt & CLI_BCOMP_EXACT) { |
|
521 |
+ if (buffer+byte_len != end_buf) { |
|
522 |
+ |
|
523 |
+ bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n"); |
|
524 |
+ return CL_CLEAN; |
|
525 |
+ } |
|
526 |
+ } |
|
493 | 527 |
|
494 |
- value = be32_to_host(value); |
|
528 |
+ value = be64_to_host(value); |
|
495 | 529 |
break; |
530 |
+ case CLI_BCOMP_BIN | CLI_BCOMP_LE: |
|
531 |
+ /* dropping bin_value on the heap to ensure our local stack is isolated from raw user input */ |
|
532 |
+ bin_value = cli_calloc(1, sizeof(uint64_t)); |
|
496 | 533 |
|
534 |
+ /* copy, then shift to align */ |
|
535 |
+ memcpy(bin_value, buffer, byte_len); |
|
536 |
+ *bin_value = *bin_value >> CLI_BCOMP_MAX_BIN_BLEN - byte_len; |
|
537 |
+ |
|
538 |
+ value = le64_to_host(*bin_value); |
|
539 |
+ break; |
|
540 |
+ case CLI_BCOMP_BIN | CLI_BCOMP_BE: |
|
541 |
+ /* dropping bin_value on the heap to ensure our local stack is isolated from raw user input */ |
|
542 |
+ bin_value = cli_calloc(1, sizeof(uint64_t)); |
|
543 |
+ |
|
544 |
+ /* copy, then shift to align */ |
|
545 |
+ memcpy(bin_value, buffer, byte_len); |
|
546 |
+ *bin_value = *bin_value >> CLI_BCOMP_MAX_BIN_BLEN - byte_len; |
|
547 |
+ |
|
548 |
+ value = be64_to_host(*bin_value); |
|
549 |
+ break; |
|
497 | 550 |
default: |
498 | 551 |
return CL_ENULLARG; |
499 | 552 |
} |
... | ... |
@@ -503,21 +602,21 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met |
503 | 503 |
|
504 | 504 |
case '>': |
505 | 505 |
if (value > bm->comp_value) { |
506 |
- bcm_dbgmsg("bcmp_compare_check: extracted value (%u) greater than comparison value (%u)\n", value, bm->comp_value); |
|
506 |
+ bcm_dbgmsg("bcmp_compare_check: extracted value (%lu) greater than comparison value (%lu)\n", value, bm->comp_value); |
|
507 | 507 |
return CL_VIRUS; |
508 | 508 |
} |
509 | 509 |
break; |
510 | 510 |
|
511 | 511 |
case '<': |
512 | 512 |
if (value < bm->comp_value) { |
513 |
- bcm_dbgmsg("bcmp_compare_check: extracted value (%u) less than comparison value (%u)\n", value, bm->comp_value); |
|
513 |
+ bcm_dbgmsg("bcmp_compare_check: extracted value (%lu) less than comparison value (%lu)\n", value, bm->comp_value); |
|
514 | 514 |
return CL_VIRUS; |
515 | 515 |
} |
516 | 516 |
break; |
517 | 517 |
|
518 | 518 |
case '=': |
519 | 519 |
if (value == bm->comp_value) { |
520 |
- bcm_dbgmsg("bcmp_compare_check: extracted value (%u) equal to comparison value (%u)\n", value, bm->comp_value); |
|
520 |
+ bcm_dbgmsg("bcmp_compare_check: extracted value (%lu) equal to comparison value (%lu)\n", value, bm->comp_value); |
|
521 | 521 |
return CL_VIRUS; |
522 | 522 |
} |
523 | 523 |
break; |
... | ... |
@@ -528,7 +627,7 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met |
528 | 528 |
} |
529 | 529 |
|
530 | 530 |
/* comparison was not successful */ |
531 |
- bcm_dbgmsg("bcmp_compare_check: extracted value was not %c %u\n", bm->comp_symbol, bm->comp_value); |
|
531 |
+ bcm_dbgmsg("bcmp_compare_check: extracted value was not %c %lu\n", bm->comp_symbol, bm->comp_value); |
|
532 | 532 |
return CL_CLEAN; |
533 | 533 |
} |
534 | 534 |
|
... | ... |
@@ -34,10 +34,14 @@ |
34 | 34 |
#include "dconf.h" |
35 | 35 |
#include "mpool.h" |
36 | 36 |
|
37 |
-#define CLI_BCOMP_HEX 0x0001 |
|
38 |
-#define CLI_BCOMP_DEC 0x0002 |
|
39 |
-#define CLI_BCOMP_LE 0x0010 |
|
40 |
-#define CLI_BCOMP_BE 0x0020 |
|
37 |
+#define CLI_BCOMP_MAX_BIN_BLEN 8 |
|
38 |
+ |
|
39 |
+#define CLI_BCOMP_HEX 0x0001 |
|
40 |
+#define CLI_BCOMP_DEC 0x0002 |
|
41 |
+#define CLI_BCOMP_BIN 0x0004 |
|
42 |
+#define CLI_BCOMP_LE 0x0010 |
|
43 |
+#define CLI_BCOMP_BE 0x0020 |
|
44 |
+#define CLI_BCOMP_EXACT 0x0100 |
|
41 | 45 |
|
42 | 46 |
struct cli_bcomp_meta { |
43 | 47 |
char *virname; |
... | ... |
@@ -47,7 +51,7 @@ struct cli_bcomp_meta { |
47 | 47 |
uint16_t options; /* bitmask */ |
48 | 48 |
size_t byte_len; |
49 | 49 |
char comp_symbol; /* <, >, = are supported */ |
50 |
- uint32_t comp_value; |
|
50 |
+ uint64_t comp_value; |
|
51 | 51 |
}; |
52 | 52 |
|
53 | 53 |
cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, const char* hexsig, const uint32_t *lsigid, unsigned int options); |