Implemented several maximums in parsing MIME messages to avoid Denial of
Service attempts, as well as improving parsing logic to avoid repeatedly
calling the realloc function. These are in response to excessively long scan
times for specially crafted files.
This is in response to CVE-2019-15961.
The limits added are
1. Limit on number of MIME parts per message.
2. Limit on number of header bytes.
3. Limit on number of email headers.
4. Limit on number of line folds.
5. Limit on numbef of MIME arguments.
... | ... |
@@ -23,7 +23,6 @@ |
23 | 23 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
24 | 24 |
* MA 02110-1301, USA. |
25 | 25 |
*/ |
26 |
- |
|
27 | 26 |
#if HAVE_CONFIG_H |
28 | 27 |
#include "clamav-config.h" |
29 | 28 |
#endif |
... | ... |
@@ -201,9 +200,9 @@ typedef struct mbox_ctx { |
201 | 201 |
#endif |
202 | 202 |
|
203 | 203 |
static int cli_parse_mbox(const char *dir, cli_ctx *ctx); |
204 |
-static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir); |
|
205 |
-static message *parseEmailHeaders(message *m, const table_t *rfc821Table); |
|
206 |
-static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); |
|
204 |
+static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir, cli_ctx *ctx, bool *heuristicFound); |
|
205 |
+static message *parseEmailHeaders(message *m, const table_t *rfc821Table, bool *heuristicFound); |
|
206 |
+static int parseEmailHeader(message *m, const char *line, const table_t *rfc821, cli_ctx *ctx, bool *heuristicFound); |
|
207 | 207 |
static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata); |
208 | 208 |
static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t); |
209 | 209 |
static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level); |
... | ... |
@@ -212,7 +211,7 @@ static int boundaryEnd(const char *line, const char *boundary); |
212 | 212 |
static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); |
213 | 213 |
static int getTextPart(message *const messages[], size_t size); |
214 | 214 |
static size_t strip(char *buf, int len); |
215 |
-static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg); |
|
215 |
+static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg, cli_ctx *ctx, bool *heuristicFound); |
|
216 | 216 |
static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text); |
217 | 217 |
static char *rfc2047(const char *in); |
218 | 218 |
static char *rfc822comments(const char *in, char *out); |
... | ... |
@@ -233,6 +232,12 @@ static blob *getHrefs(message *m, tag_arguments_t *hrefs); |
233 | 233 |
static void hrefs_done(blob *b, tag_arguments_t *hrefs); |
234 | 234 |
static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); |
235 | 235 |
|
236 |
+static bool haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx); |
|
237 |
+static bool hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx); |
|
238 |
+static bool haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx); |
|
239 |
+static bool haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx); |
|
240 |
+static bool haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx); |
|
241 |
+ |
|
236 | 242 |
/* Maximum line length according to RFC2821 */ |
237 | 243 |
#define RFC2821LENGTH 1000 |
238 | 244 |
|
... | ... |
@@ -281,6 +286,12 @@ static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); |
281 | 281 |
*/ |
282 | 282 |
#define KNOWBOT 14 /* Unknown and undocumented format? */ |
283 | 283 |
|
284 |
+#define HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER (256 * 1024) |
|
285 |
+#define HEURISTIC_EMAIL_MAX_HEADER_BYTES (1024 * 256) |
|
286 |
+#define HEURISTIC_EMAIL_MAX_HEADERS 1024 |
|
287 |
+#define HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE 1024 |
|
288 |
+#define HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER 256 |
|
289 |
+ |
|
284 | 290 |
static const struct tableinit { |
285 | 291 |
const char *key; |
286 | 292 |
int value; |
... | ... |
@@ -423,7 +434,7 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) |
423 | 423 |
*/ |
424 | 424 |
bool lastLineWasEmpty; |
425 | 425 |
int messagenumber; |
426 |
- message *m = messageCreate(); |
|
426 |
+ message *m = messageCreate(); /*Create an empty email */ |
|
427 | 427 |
|
428 | 428 |
if (m == NULL) |
429 | 429 |
return CL_EMEM; |
... | ... |
@@ -434,15 +445,20 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) |
434 | 434 |
|
435 | 435 |
do { |
436 | 436 |
cli_chomp(buffer); |
437 |
- /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ |
|
437 |
+ /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) */ |
|
438 | 438 |
if (lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0)) { |
439 | 439 |
cli_dbgmsg("Deal with message number %d\n", messagenumber++); |
440 | 440 |
/* |
441 | 441 |
* End of a message in the mail box |
442 | 442 |
*/ |
443 |
- body = parseEmailHeaders(m, rfc821); |
|
443 |
+ bool heuristicFound = FALSE; |
|
444 |
+ body = parseEmailHeaders(m, rfc821, &heuristicFound); |
|
444 | 445 |
if (body == NULL) { |
445 | 446 |
messageReset(m); |
447 |
+ if (heuristicFound) { |
|
448 |
+ retcode = CL_VIRUS; |
|
449 |
+ break; |
|
450 |
+ } |
|
446 | 451 |
continue; |
447 | 452 |
} |
448 | 453 |
messageSetCTX(body, ctx); |
... | ... |
@@ -493,7 +509,11 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) |
493 | 493 |
|
494 | 494 |
if (retcode == CL_SUCCESS) { |
495 | 495 |
cli_dbgmsg("Extract attachments from email %d\n", messagenumber); |
496 |
- body = parseEmailHeaders(m, rfc821); |
|
496 |
+ bool heuristicFound = FALSE; |
|
497 |
+ body = parseEmailHeaders(m, rfc821, &heuristicFound); |
|
498 |
+ if (heuristicFound) { |
|
499 |
+ retcode = CL_VIRUS; |
|
500 |
+ } |
|
497 | 501 |
} |
498 | 502 |
if (m) |
499 | 503 |
messageDestroy(m); |
... | ... |
@@ -520,7 +540,11 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) |
520 | 520 |
|
521 | 521 |
buffer[sizeof(buffer) - 1] = '\0'; |
522 | 522 |
|
523 |
- body = parseEmailFile(map, &at, rfc821, buffer, dir); |
|
523 |
+ bool heuristicFound = FALSE; |
|
524 |
+ body = parseEmailFile(map, &at, rfc821, buffer, dir, ctx, &heuristicFound); |
|
525 |
+ if (heuristicFound) { |
|
526 |
+ retcode = CL_VIRUS; |
|
527 |
+ } |
|
524 | 528 |
} |
525 | 529 |
|
526 | 530 |
if (body) { |
... | ... |
@@ -576,6 +600,253 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) |
576 | 576 |
return retcode; |
577 | 577 |
} |
578 | 578 |
|
579 |
+/*TODO: move these to a header.*/ |
|
580 |
+#define DO_STRDUP(buf, var) \ |
|
581 |
+ do { \ |
|
582 |
+ var = strdup(buf); \ |
|
583 |
+ if (NULL == var) { \ |
|
584 |
+ goto done; \ |
|
585 |
+ } \ |
|
586 |
+ } while (0) |
|
587 |
+ |
|
588 |
+#define DO_FREE(var) \ |
|
589 |
+ do { \ |
|
590 |
+ if (NULL != var) { \ |
|
591 |
+ free(var); \ |
|
592 |
+ var = NULL; \ |
|
593 |
+ } \ |
|
594 |
+ } while (0) |
|
595 |
+ |
|
596 |
+#define DO_MALLOC(var, size) \ |
|
597 |
+ do { \ |
|
598 |
+ var = malloc(size); \ |
|
599 |
+ if (NULL == var) { \ |
|
600 |
+ goto done; \ |
|
601 |
+ } \ |
|
602 |
+ } while (0) |
|
603 |
+ |
|
604 |
+#define DO_CALLOC(var, size) \ |
|
605 |
+ do { \ |
|
606 |
+ (var) = calloc((size), sizeof *(var)); \ |
|
607 |
+ if (NULL == var) { \ |
|
608 |
+ goto done; \ |
|
609 |
+ } \ |
|
610 |
+ } while (0) |
|
611 |
+ |
|
612 |
+#define DO_VERIFY_POINTER(ptr) \ |
|
613 |
+ do { \ |
|
614 |
+ if (NULL == ptr) { \ |
|
615 |
+ goto done; \ |
|
616 |
+ } \ |
|
617 |
+ } while (0) |
|
618 |
+ |
|
619 |
+#define READ_STRUCT_BUFFER_LEN 1024 |
|
620 |
+typedef struct _ReadStruct { |
|
621 |
+ char buffer[READ_STRUCT_BUFFER_LEN + 1]; |
|
622 |
+ |
|
623 |
+ size_t bufferLen; |
|
624 |
+ |
|
625 |
+ struct _ReadStruct *next; |
|
626 |
+ |
|
627 |
+} ReadStruct; |
|
628 |
+ |
|
629 |
+static ReadStruct * |
|
630 |
+appendReadStruct(ReadStruct *rs, const char *const buffer) |
|
631 |
+{ |
|
632 |
+ if (NULL == rs) { |
|
633 |
+ assert(rs && "Invalid argument"); |
|
634 |
+ goto done; |
|
635 |
+ } |
|
636 |
+ |
|
637 |
+ size_t spaceLeft = (READ_STRUCT_BUFFER_LEN - rs->bufferLen); |
|
638 |
+ |
|
639 |
+ if (strlen(buffer) > spaceLeft) { |
|
640 |
+ ReadStruct *next = NULL; |
|
641 |
+ int part = spaceLeft; |
|
642 |
+ strncpy(&(rs->buffer[rs->bufferLen]), buffer, part); |
|
643 |
+ rs->bufferLen += part; |
|
644 |
+ |
|
645 |
+ DO_CALLOC(next, 1); |
|
646 |
+ |
|
647 |
+ rs->next = next; |
|
648 |
+ strcpy(next->buffer, &(buffer[part])); |
|
649 |
+ next->bufferLen = strlen(&(buffer[part])); |
|
650 |
+ |
|
651 |
+ rs = next; |
|
652 |
+ } else { |
|
653 |
+ strcpy(&(rs->buffer[rs->bufferLen]), buffer); |
|
654 |
+ rs->bufferLen += strlen(buffer); |
|
655 |
+ } |
|
656 |
+ |
|
657 |
+done: |
|
658 |
+ return rs; |
|
659 |
+} |
|
660 |
+ |
|
661 |
+static char * |
|
662 |
+getMallocedBufferFromList(const ReadStruct *head) |
|
663 |
+{ |
|
664 |
+ |
|
665 |
+ const ReadStruct *rs = head; |
|
666 |
+ int bufferLen = 1; |
|
667 |
+ char *working = NULL; |
|
668 |
+ char *ret = NULL; |
|
669 |
+ |
|
670 |
+ while (rs) { |
|
671 |
+ bufferLen += rs->bufferLen; |
|
672 |
+ rs = rs->next; |
|
673 |
+ } |
|
674 |
+ |
|
675 |
+ DO_MALLOC(working, bufferLen); |
|
676 |
+ |
|
677 |
+ rs = head; |
|
678 |
+ bufferLen = 0; |
|
679 |
+ while (rs) { |
|
680 |
+ memcpy(&(working[bufferLen]), rs->buffer, rs->bufferLen); |
|
681 |
+ bufferLen += rs->bufferLen; |
|
682 |
+ working[bufferLen] = 0; |
|
683 |
+ rs = rs->next; |
|
684 |
+ } |
|
685 |
+ |
|
686 |
+ ret = working; |
|
687 |
+done: |
|
688 |
+ if (NULL == ret) { |
|
689 |
+ DO_FREE(working); |
|
690 |
+ } |
|
691 |
+ |
|
692 |
+ return ret; |
|
693 |
+} |
|
694 |
+ |
|
695 |
+static void |
|
696 |
+freeList(ReadStruct *head) |
|
697 |
+{ |
|
698 |
+ while (head) { |
|
699 |
+ ReadStruct *rs = head->next; |
|
700 |
+ DO_FREE(head); |
|
701 |
+ head = rs; |
|
702 |
+ } |
|
703 |
+} |
|
704 |
+ |
|
705 |
+#ifndef FREELIST_REALLOC |
|
706 |
+#define FREELIST_REALLOC(head, curr) \ |
|
707 |
+ do { \ |
|
708 |
+ if (curr != head) { \ |
|
709 |
+ freeList(head->next); \ |
|
710 |
+ } \ |
|
711 |
+ head->bufferLen = 0; \ |
|
712 |
+ head->next = 0; \ |
|
713 |
+ curr = head; \ |
|
714 |
+ } while (0) |
|
715 |
+#endif /*FREELIST_REALLOC*/ |
|
716 |
+ |
|
717 |
+/*Check if we have repeated blank lines with only a semicolon at the end. Semicolon is a delimiter for parameters, |
|
718 |
+ * but if there is no data, it isn't a parameter. Allow the first one because it may be continuation of a previous line |
|
719 |
+ * that actually had data in it.*/ |
|
720 |
+static bool |
|
721 |
+doContinueMultipleEmptyOptions(const char *const line, bool *lastWasOnlySemi) |
|
722 |
+{ |
|
723 |
+ if (line) { |
|
724 |
+ size_t i = 0; |
|
725 |
+ int doCont = 1; |
|
726 |
+ for (; i < strlen(line); i++) { |
|
727 |
+ if (isblank(line[i])) { |
|
728 |
+ } else if (';' == line[i]) { |
|
729 |
+ } else { |
|
730 |
+ doCont = 0; |
|
731 |
+ break; |
|
732 |
+ } |
|
733 |
+ } |
|
734 |
+ |
|
735 |
+ if (1 == doCont) { |
|
736 |
+ if (*lastWasOnlySemi) { |
|
737 |
+ return TRUE; |
|
738 |
+ } |
|
739 |
+ *lastWasOnlySemi = TRUE; |
|
740 |
+ } else { |
|
741 |
+ *lastWasOnlySemi = FALSE; |
|
742 |
+ } |
|
743 |
+ } |
|
744 |
+ return FALSE; |
|
745 |
+} |
|
746 |
+ |
|
747 |
+static bool |
|
748 |
+hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx) |
|
749 |
+{ |
|
750 |
+ |
|
751 |
+ if (line) { |
|
752 |
+ if (isblank(line[0])) { |
|
753 |
+ (*lineFoldCnt)++; |
|
754 |
+ } else { |
|
755 |
+ (*lineFoldCnt) = 0; |
|
756 |
+ } |
|
757 |
+ |
|
758 |
+ if ((*lineFoldCnt) >= HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER) { |
|
759 |
+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { |
|
760 |
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxLineFoldCnt"); |
|
761 |
+ } |
|
762 |
+ |
|
763 |
+ return TRUE; |
|
764 |
+ } |
|
765 |
+ } |
|
766 |
+ return FALSE; |
|
767 |
+} |
|
768 |
+ |
|
769 |
+static bool |
|
770 |
+haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx) |
|
771 |
+{ |
|
772 |
+ |
|
773 |
+ if (totalLen > HEURISTIC_EMAIL_MAX_HEADER_BYTES) { |
|
774 |
+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { |
|
775 |
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxHeaderBytes"); |
|
776 |
+ } |
|
777 |
+ |
|
778 |
+ return TRUE; |
|
779 |
+ } |
|
780 |
+ return FALSE; |
|
781 |
+} |
|
782 |
+ |
|
783 |
+static bool |
|
784 |
+haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx) |
|
785 |
+{ |
|
786 |
+ |
|
787 |
+ if (totalHeaderCnt > HEURISTIC_EMAIL_MAX_HEADERS) { |
|
788 |
+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { |
|
789 |
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxEmailHeaders"); |
|
790 |
+ } |
|
791 |
+ |
|
792 |
+ return TRUE; |
|
793 |
+ } |
|
794 |
+ return FALSE; |
|
795 |
+} |
|
796 |
+ |
|
797 |
+static bool |
|
798 |
+haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx) |
|
799 |
+{ |
|
800 |
+ |
|
801 |
+ if (mimePartCnt >= HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE) { |
|
802 |
+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { |
|
803 |
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEPartsPerMessage"); |
|
804 |
+ } |
|
805 |
+ |
|
806 |
+ return TRUE; |
|
807 |
+ } |
|
808 |
+ return FALSE; |
|
809 |
+} |
|
810 |
+ |
|
811 |
+static bool |
|
812 |
+haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx) |
|
813 |
+{ |
|
814 |
+ |
|
815 |
+ if (argCnt >= HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER) { |
|
816 |
+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { |
|
817 |
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEArguments"); |
|
818 |
+ } |
|
819 |
+ |
|
820 |
+ return TRUE; |
|
821 |
+ } |
|
822 |
+ |
|
823 |
+ return FALSE; |
|
824 |
+} |
|
825 |
+ |
|
579 | 826 |
/* |
580 | 827 |
* Read in an email message from fin, parse it, and return the message |
581 | 828 |
* |
... | ... |
@@ -583,7 +854,7 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) |
583 | 583 |
* handled ungracefully... |
584 | 584 |
*/ |
585 | 585 |
static message * |
586 |
-parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir) |
|
586 |
+parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir, cli_ctx *ctx, bool *heuristicFound) |
|
587 | 587 |
{ |
588 | 588 |
bool inHeader = TRUE; |
589 | 589 |
bool bodyIsEmpty = TRUE; |
... | ... |
@@ -591,9 +862,21 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
591 | 591 |
message *ret; |
592 | 592 |
bool anyHeadersFound = FALSE; |
593 | 593 |
int commandNumber = -1; |
594 |
- char *fullline = NULL, *boundary = NULL; |
|
595 |
- size_t fulllinelength = 0; |
|
594 |
+ char *boundary = NULL; |
|
596 | 595 |
char buffer[RFC2821LENGTH + 1]; |
596 |
+ bool lastWasOnlySemi = FALSE; |
|
597 |
+ int err = 1; |
|
598 |
+ size_t totalHeaderBytes = 0; |
|
599 |
+ size_t totalHeaderCnt = 0; |
|
600 |
+ |
|
601 |
+ size_t lineFoldCnt = 0; |
|
602 |
+ |
|
603 |
+ *heuristicFound = FALSE; |
|
604 |
+ |
|
605 |
+ ReadStruct *head = NULL; |
|
606 |
+ ReadStruct *curr = NULL; |
|
607 |
+ DO_CALLOC(head, 1); |
|
608 |
+ curr = head; |
|
597 | 609 |
|
598 | 610 |
cli_dbgmsg("parseEmailFile\n"); |
599 | 611 |
|
... | ... |
@@ -612,6 +895,15 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
612 | 612 |
else |
613 | 613 |
line = buffer; |
614 | 614 |
|
615 |
+ if (doContinueMultipleEmptyOptions(line, &lastWasOnlySemi)) { |
|
616 |
+ continue; |
|
617 |
+ } |
|
618 |
+ |
|
619 |
+ if (hitLineFoldCnt(line, &lineFoldCnt, ctx)) { |
|
620 |
+ *heuristicFound = TRUE; |
|
621 |
+ break; |
|
622 |
+ } |
|
623 |
+ |
|
615 | 624 |
/* |
616 | 625 |
* Don't blank lines which are only spaces from headers, |
617 | 626 |
* otherwise they'll be treated as the end of header marker |
... | ... |
@@ -624,8 +916,8 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
624 | 624 |
} |
625 | 625 |
} |
626 | 626 |
if (inHeader) { |
627 |
- cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n", |
|
628 |
- buffer, fullline); |
|
627 |
+ cli_dbgmsg("parseEmailFile: check '%s'\n", buffer); |
|
628 |
+ |
|
629 | 629 |
/* |
630 | 630 |
* Ensure wide characters are handled where |
631 | 631 |
* sizeof(char) > 1 |
... | ... |
@@ -649,13 +941,30 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
649 | 649 |
* content-type line. So we just have |
650 | 650 |
* to make a best guess. Sigh. |
651 | 651 |
*/ |
652 |
- if (fullline) { |
|
653 |
- if (parseEmailHeader(ret, fullline, rfc821) < 0) |
|
654 |
- continue; |
|
652 |
+ if (head->bufferLen) { |
|
653 |
+ char *header = getMallocedBufferFromList(head); |
|
654 |
+ int needContinue = 0; |
|
655 |
+ DO_VERIFY_POINTER(header); |
|
656 |
+ |
|
657 |
+ totalHeaderCnt++; |
|
658 |
+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) { |
|
659 |
+ *heuristicFound = TRUE; |
|
660 |
+ break; |
|
661 |
+ } |
|
662 |
+ needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0); |
|
663 |
+ if (*heuristicFound) { |
|
664 |
+ DO_FREE(header); |
|
665 |
+ break; |
|
666 |
+ } |
|
655 | 667 |
|
656 |
- free(fullline); |
|
657 |
- fullline = NULL; |
|
668 |
+ DO_FREE(header); |
|
669 |
+ FREELIST_REALLOC(head, curr); |
|
670 |
+ |
|
671 |
+ if (needContinue) { |
|
672 |
+ continue; |
|
673 |
+ } |
|
658 | 674 |
} |
675 |
+ |
|
659 | 676 |
if (boundary || |
660 | 677 |
((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) { |
661 | 678 |
lastWasBlank = TRUE; |
... | ... |
@@ -663,7 +972,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
663 | 663 |
} |
664 | 664 |
} |
665 | 665 |
} |
666 |
- if ((line == NULL) && (fullline == NULL)) { /* empty line */ |
|
666 |
+ if ((line == NULL) && (0 == head->bufferLen)) { /* empty line */ |
|
667 | 667 |
/* |
668 | 668 |
* A blank line signifies the end of |
669 | 669 |
* the header and the start of the text |
... | ... |
@@ -678,8 +987,9 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
678 | 678 |
} else { |
679 | 679 |
char *ptr; |
680 | 680 |
const char *lookahead; |
681 |
+ bool lineAdded = TRUE; |
|
681 | 682 |
|
682 |
- if (fullline == NULL) { |
|
683 |
+ if (0 == head->bufferLen) { |
|
683 | 684 |
char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1]; |
684 | 685 |
|
685 | 686 |
/* |
... | ... |
@@ -712,23 +1022,26 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
712 | 712 |
anyHeadersFound = usefulHeader(commandNumber, cmd); |
713 | 713 |
continue; |
714 | 714 |
} |
715 |
- fullline = cli_strdup(line); |
|
716 |
- fulllinelength = strlen(line) + 1; |
|
717 |
- if (!fullline) { |
|
718 |
- if (ret) |
|
715 |
+ curr = appendReadStruct(curr, line); |
|
716 |
+ if (NULL == curr) { |
|
717 |
+ if (ret) { |
|
719 | 718 |
ret->isTruncated = TRUE; |
719 |
+ } |
|
720 | 720 |
break; |
721 | 721 |
} |
722 | 722 |
} else if (line != NULL) { |
723 |
- fulllinelength += strlen(line) + 1; |
|
724 |
- ptr = cli_realloc(fullline, fulllinelength); |
|
725 |
- if (ptr == NULL) |
|
726 |
- continue; |
|
727 |
- fullline = ptr; |
|
728 |
- cli_strlcat(fullline, line, fulllinelength); |
|
723 |
+ curr = appendReadStruct(curr, line); |
|
724 |
+ } else { |
|
725 |
+ lineAdded = FALSE; |
|
729 | 726 |
} |
730 | 727 |
|
731 |
- assert(fullline != NULL); |
|
728 |
+ if (lineAdded) { |
|
729 |
+ totalHeaderBytes += strlen(line); |
|
730 |
+ if (haveTooManyHeaderBytes(totalHeaderBytes, ctx)) { |
|
731 |
+ *heuristicFound = TRUE; |
|
732 |
+ break; |
|
733 |
+ } |
|
734 |
+ } |
|
732 | 735 |
|
733 | 736 |
if ((lookahead = fmap_need_off_once(map, *at, 1))) { |
734 | 737 |
/* |
... | ... |
@@ -746,24 +1059,34 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
746 | 746 |
* Handle broken headers, where the next |
747 | 747 |
* line isn't indented by whitespace |
748 | 748 |
*/ |
749 |
- if (fullline[strlen(fullline) - 1] == ';') |
|
750 |
- /* Add arguments to this line */ |
|
751 |
- continue; |
|
749 |
+ { |
|
750 |
+ char *header = getMallocedBufferFromList(head); /*This is the issue */ |
|
751 |
+ int needContinue = 0; |
|
752 |
+ needContinue = (header[strlen(header) - 1] == ';'); |
|
753 |
+ if (0 == needContinue) { |
|
754 |
+ needContinue = (line && (count_quotes(header) & 1)); |
|
755 |
+ } |
|
752 | 756 |
|
753 |
- if (line && (count_quotes(fullline) & 1)) |
|
754 |
- continue; |
|
757 |
+ if (0 == needContinue) { |
|
758 |
+ totalHeaderCnt++; |
|
759 |
+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) { |
|
760 |
+ *heuristicFound = TRUE; |
|
761 |
+ break; |
|
762 |
+ } |
|
763 |
+ needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0); |
|
764 |
+ if (*heuristicFound) { |
|
765 |
+ DO_FREE(header); |
|
766 |
+ break; |
|
767 |
+ } |
|
768 |
+ /*Check total headers here;*/ |
|
769 |
+ } |
|
755 | 770 |
|
756 |
- ptr = rfc822comments(fullline, NULL); |
|
757 |
- if (ptr) { |
|
758 |
- free(fullline); |
|
759 |
- fullline = ptr; |
|
771 |
+ DO_FREE(header); |
|
772 |
+ if (needContinue) { |
|
773 |
+ continue; |
|
774 |
+ } |
|
775 |
+ FREELIST_REALLOC(head, curr); |
|
760 | 776 |
} |
761 |
- |
|
762 |
- if (parseEmailHeader(ret, fullline, rfc821) < 0) |
|
763 |
- continue; |
|
764 |
- |
|
765 |
- free(fullline); |
|
766 |
- fullline = NULL; |
|
767 | 777 |
} |
768 | 778 |
} else if (line && isuuencodebegin(line)) { |
769 | 779 |
/* |
... | ... |
@@ -807,19 +1130,17 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
807 | 807 |
} |
808 | 808 |
} while (getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL); |
809 | 809 |
|
810 |
- if (boundary) |
|
811 |
- free(boundary); |
|
812 |
- |
|
813 |
- if (fullline) { |
|
814 |
- if (*fullline) switch (commandNumber) { |
|
815 |
- case CONTENT_TRANSFER_ENCODING: |
|
816 |
- case CONTENT_DISPOSITION: |
|
817 |
- case CONTENT_TYPE: |
|
818 |
- cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline); |
|
819 |
- } |
|
820 |
- free(fullline); |
|
810 |
+ err = 0; |
|
811 |
+done: |
|
812 |
+ if (err) { |
|
813 |
+ cli_errmsg("parseEmailFile: ERROR parsing file\n"); |
|
814 |
+ ret->isTruncated = TRUE; |
|
821 | 815 |
} |
822 | 816 |
|
817 |
+ DO_FREE(boundary); |
|
818 |
+ |
|
819 |
+ freeList(head); |
|
820 |
+ |
|
823 | 821 |
if (!anyHeadersFound) { |
824 | 822 |
/* |
825 | 823 |
* False positive in believing we have an e-mail when we don't |
... | ... |
@@ -829,6 +1150,12 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
829 | 829 |
return NULL; |
830 | 830 |
} |
831 | 831 |
|
832 |
+ if (*heuristicFound) { |
|
833 |
+ messageDestroy(ret); |
|
834 |
+ cli_dbgmsg("parseEmailFile: found heuristic\n"); |
|
835 |
+ return NULL; |
|
836 |
+ } |
|
837 |
+ |
|
832 | 838 |
cli_dbgmsg("parseEmailFile: return\n"); |
833 | 839 |
|
834 | 840 |
return ret; |
... | ... |
@@ -843,7 +1170,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first |
843 | 843 |
* TODO: remove the duplication with parseEmailFile |
844 | 844 |
*/ |
845 | 845 |
static message * |
846 |
-parseEmailHeaders(message *m, const table_t *rfc821) |
|
846 |
+parseEmailHeaders(message *m, const table_t *rfc821, bool *heuristicFound) |
|
847 | 847 |
{ |
848 | 848 |
bool inHeader = TRUE; |
849 | 849 |
bool bodyIsEmpty = TRUE; |
... | ... |
@@ -853,9 +1180,14 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
853 | 853 |
int commandNumber = -1; |
854 | 854 |
char *fullline = NULL; |
855 | 855 |
size_t fulllinelength = 0; |
856 |
+ bool lastWasOnlySemi = FALSE; |
|
857 |
+ size_t lineFoldCnt = 0; |
|
858 |
+ size_t totalHeaderCnt = 0; |
|
856 | 859 |
|
857 | 860 |
cli_dbgmsg("parseEmailHeaders\n"); |
858 | 861 |
|
862 |
+ *heuristicFound = FALSE; |
|
863 |
+ |
|
859 | 864 |
if (m == NULL) |
860 | 865 |
return NULL; |
861 | 866 |
|
... | ... |
@@ -869,6 +1201,15 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
869 | 869 |
else |
870 | 870 |
line = NULL; |
871 | 871 |
|
872 |
+ if (doContinueMultipleEmptyOptions(line, &lastWasOnlySemi)) { |
|
873 |
+ continue; |
|
874 |
+ } |
|
875 |
+ |
|
876 |
+ if (hitLineFoldCnt(line, &lineFoldCnt, m->ctx)) { |
|
877 |
+ *heuristicFound = TRUE; |
|
878 |
+ break; |
|
879 |
+ } |
|
880 |
+ |
|
872 | 881 |
if (inHeader) { |
873 | 882 |
cli_dbgmsg("parseEmailHeaders: check '%s'\n", |
874 | 883 |
line ? line : ""); |
... | ... |
@@ -886,6 +1227,7 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
886 | 886 |
bodyIsEmpty = TRUE; |
887 | 887 |
} else { |
888 | 888 |
char *ptr; |
889 |
+ bool lineAdded = TRUE; |
|
889 | 890 |
|
890 | 891 |
if (fullline == NULL) { |
891 | 892 |
char cmd[RFC2821LENGTH + 1]; |
... | ... |
@@ -931,8 +1273,21 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
931 | 931 |
continue; |
932 | 932 |
fullline = ptr; |
933 | 933 |
cli_strlcat(fullline, line, fulllinelength); |
934 |
+ } else { |
|
935 |
+ lineAdded = FALSE; |
|
934 | 936 |
} |
935 | 937 |
assert(fullline != NULL); |
938 |
+ /*continue doesn't seem right here, but that is what is done everywhere else when a malloc fails.*/ |
|
939 |
+ if (NULL == fullline) { |
|
940 |
+ continue; |
|
941 |
+ } |
|
942 |
+ |
|
943 |
+ if (lineAdded) { |
|
944 |
+ if (haveTooManyHeaderBytes(fulllinelength, m->ctx)) { |
|
945 |
+ *heuristicFound = TRUE; |
|
946 |
+ break; |
|
947 |
+ } |
|
948 |
+ } |
|
936 | 949 |
|
937 | 950 |
if (next_is_folded_header(t)) |
938 | 951 |
/* Add arguments to this line */ |
... | ... |
@@ -950,8 +1305,17 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
950 | 950 |
fullline = ptr; |
951 | 951 |
} |
952 | 952 |
|
953 |
- if (parseEmailHeader(ret, fullline, rfc821) < 0) |
|
953 |
+ totalHeaderCnt++; |
|
954 |
+ if (haveTooManyEmailHeaders(totalHeaderCnt, m->ctx)) { |
|
955 |
+ *heuristicFound = TRUE; |
|
956 |
+ break; |
|
957 |
+ } |
|
958 |
+ if (parseEmailHeader(ret, fullline, rfc821, m->ctx, heuristicFound) < 0) { |
|
954 | 959 |
continue; |
960 |
+ } |
|
961 |
+ if (*heuristicFound) { |
|
962 |
+ break; |
|
963 |
+ } |
|
955 | 964 |
|
956 | 965 |
free(fullline); |
957 | 966 |
fullline = NULL; |
... | ... |
@@ -997,6 +1361,11 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
997 | 997 |
cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n"); |
998 | 998 |
return NULL; |
999 | 999 |
} |
1000 |
+ if (*heuristicFound) { |
|
1001 |
+ messageDestroy(ret); |
|
1002 |
+ cli_dbgmsg("parseEmailHeaders: found a heuristic, delete message and stop parsing.\n"); |
|
1003 |
+ return NULL; |
|
1004 |
+ } |
|
1000 | 1005 |
|
1001 | 1006 |
cli_dbgmsg("parseEmailHeaders: return\n"); |
1002 | 1007 |
|
... | ... |
@@ -1007,9 +1376,9 @@ parseEmailHeaders(message *m, const table_t *rfc821) |
1007 | 1007 |
* Handle a header line of an email message |
1008 | 1008 |
*/ |
1009 | 1009 |
static int |
1010 |
-parseEmailHeader(message *m, const char *line, const table_t *rfc821) |
|
1010 |
+parseEmailHeader(message *m, const char *line, const table_t *rfc821, cli_ctx *ctx, bool *heuristicFound) |
|
1011 | 1011 |
{ |
1012 |
- int ret; |
|
1012 |
+ int ret = -1; |
|
1013 | 1013 |
#ifdef CL_THREAD_SAFE |
1014 | 1014 |
char *strptr; |
1015 | 1015 |
#endif |
... | ... |
@@ -1032,15 +1401,17 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) |
1032 | 1032 |
return -1; |
1033 | 1033 |
|
1034 | 1034 |
copy = rfc2047(line); |
1035 |
- if (copy == NULL) |
|
1035 |
+ if (copy == NULL) { |
|
1036 | 1036 |
/* an RFC checker would return -1 here */ |
1037 | 1037 |
copy = cli_strdup(line); |
1038 |
+ if (NULL == copy) { |
|
1039 |
+ goto done; |
|
1040 |
+ } |
|
1041 |
+ } |
|
1038 | 1042 |
|
1039 | 1043 |
tokenseparator[0] = *separator; |
1040 | 1044 |
tokenseparator[1] = '\0'; |
1041 | 1045 |
|
1042 |
- ret = -1; |
|
1043 |
- |
|
1044 | 1046 |
#ifdef CL_THREAD_SAFE |
1045 | 1047 |
cmd = strtok_r(copy, tokenseparator, &strptr); |
1046 | 1048 |
#else |
... | ... |
@@ -1054,7 +1425,7 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) |
1054 | 1054 |
char *arg = strtok(NULL, ""); |
1055 | 1055 |
#endif |
1056 | 1056 |
|
1057 |
- if (arg) |
|
1057 |
+ if (arg) { |
|
1058 | 1058 |
/* |
1059 | 1059 |
* Found a header such as |
1060 | 1060 |
* Content-Type: multipart/mixed; |
... | ... |
@@ -1062,9 +1433,12 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) |
1062 | 1062 |
* "multipart/mixed" and cmd to |
1063 | 1063 |
* be "Content-Type" |
1064 | 1064 |
*/ |
1065 |
- ret = parseMimeHeader(m, cmd, rfc821, arg); |
|
1065 |
+ ret = parseMimeHeader(m, cmd, rfc821, arg, ctx, heuristicFound); |
|
1066 |
+ } |
|
1066 | 1067 |
} |
1067 |
- free(copy); |
|
1068 |
+done: |
|
1069 |
+ DO_FREE(copy); |
|
1070 |
+ |
|
1068 | 1071 |
return ret; |
1069 | 1072 |
} |
1070 | 1073 |
|
... | ... |
@@ -1115,9 +1489,6 @@ parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata |
1115 | 1115 |
#if HAVE_LIBXML2 |
1116 | 1116 |
const char *xmlsrt, *xmlend; |
1117 | 1117 |
xmlTextReaderPtr reader; |
1118 |
-#if HAVE_JSON |
|
1119 |
- json_object *thisjobj = (json_object *)wrkjobj; |
|
1120 |
-#endif |
|
1121 | 1118 |
int ret = CL_SUCCESS; |
1122 | 1119 |
|
1123 | 1120 |
UNUSEDPARAM(cbdata); |
... | ... |
@@ -1310,6 +1681,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1310 | 1310 |
#if HAVE_JSON |
1311 | 1311 |
json_object *saveobj = mctx->wrkobj; |
1312 | 1312 |
#endif |
1313 |
+ bool heuristicFound = FALSE; |
|
1313 | 1314 |
|
1314 | 1315 |
cli_dbgmsg("in parseEmailBody, %u files saved so far\n", |
1315 | 1316 |
mctx->files); |
... | ... |
@@ -1391,12 +1763,14 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1391 | 1391 |
cli_dbgmsg("Not a mime encoded message\n"); |
1392 | 1392 |
aText = textAddMessage(aText, mainMessage); |
1393 | 1393 |
|
1394 |
- if (!doPhishingScan) |
|
1394 |
+ if (!doPhishingScan) { |
|
1395 | 1395 |
break; |
1396 |
+ } |
|
1396 | 1397 |
/* |
1397 | 1398 |
* Fall through: some phishing mails claim they are |
1398 | 1399 |
* text/plain, when they are in fact html |
1399 | 1400 |
*/ |
1401 |
+ /* fall through */ |
|
1400 | 1402 |
case TEXT: |
1401 | 1403 |
/* text/plain has been preprocessed as no encoding */ |
1402 | 1404 |
if (doPhishingScan) { |
... | ... |
@@ -1605,7 +1979,11 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1605 | 1605 |
* Content-Type: application/octet-stream; |
1606 | 1606 |
* Content-Transfer-Encoding: base64 |
1607 | 1607 |
*/ |
1608 |
- parseEmailHeader(aMessage, line, mctx->rfc821Table); |
|
1608 |
+ parseEmailHeader(aMessage, line, mctx->rfc821Table, mctx->ctx, &heuristicFound); |
|
1609 |
+ if (heuristicFound) { |
|
1610 |
+ rc = VIRUS; |
|
1611 |
+ break; |
|
1612 |
+ } |
|
1609 | 1613 |
|
1610 | 1614 |
while (isspace((int)*line)) |
1611 | 1615 |
line++; |
... | ... |
@@ -1750,8 +2128,11 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1750 | 1750 |
cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", |
1751 | 1751 |
multiparts, fullline); |
1752 | 1752 |
|
1753 |
- parseEmailHeader(aMessage, fullline, mctx->rfc821Table); |
|
1753 |
+ parseEmailHeader(aMessage, fullline, mctx->rfc821Table, mctx->ctx, &heuristicFound); |
|
1754 | 1754 |
free(fullline); |
1755 |
+ if (heuristicFound) { |
|
1756 |
+ rc = VIRUS; |
|
1757 |
+ } |
|
1755 | 1758 |
} else if (boundaryEnd(line, boundary)) { |
1756 | 1759 |
/* |
1757 | 1760 |
* Some viruses put information |
... | ... |
@@ -1828,6 +2209,12 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1828 | 1828 |
|
1829 | 1829 |
free((char *)boundary); |
1830 | 1830 |
|
1831 |
+ if (haveTooManyMIMEPartsPerMessage(multiparts, mctx->ctx)) { |
|
1832 |
+ DO_FREE(messages); |
|
1833 |
+ rc = VIRUS; |
|
1834 |
+ break; |
|
1835 |
+ } |
|
1836 |
+ |
|
1831 | 1837 |
/* |
1832 | 1838 |
* Preprocess. Anything special to be done before |
1833 | 1839 |
* we handle the multiparts? |
... | ... |
@@ -1906,25 +2293,28 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1906 | 1906 |
htmltextPart = getTextPart(messages, multiparts); |
1907 | 1907 |
|
1908 | 1908 |
if (htmltextPart >= 0 && messages) { |
1909 |
- if (messageGetBody(messages[htmltextPart])) |
|
1909 |
+ if (messageGetBody(messages[htmltextPart])) { |
|
1910 | 1910 |
|
1911 | 1911 |
aText = textAddMessage(aText, messages[htmltextPart]); |
1912 |
- } else |
|
1912 |
+ } |
|
1913 |
+ } else { |
|
1913 | 1914 |
/* |
1914 | 1915 |
* There isn't an HTML bit. If there's a |
1915 | 1916 |
* multipart bit, it'll may be in there |
1916 | 1917 |
* somewhere |
1917 | 1918 |
*/ |
1918 |
- for (i = 0; i < multiparts; i++) |
|
1919 |
+ for (i = 0; i < multiparts; i++) { |
|
1919 | 1920 |
if (messageGetMimeType(messages[i]) == MULTIPART) { |
1920 | 1921 |
aMessage = messages[i]; |
1921 | 1922 |
htmltextPart = i; |
1922 | 1923 |
break; |
1923 | 1924 |
} |
1925 |
+ } |
|
1926 |
+ } |
|
1924 | 1927 |
|
1925 |
- if (htmltextPart == -1) |
|
1928 |
+ if (htmltextPart == -1) { |
|
1926 | 1929 |
cli_dbgmsg("No HTML code found to be scanned\n"); |
1927 |
- else { |
|
1930 |
+ } else { |
|
1928 | 1931 |
#if HAVE_JSON |
1929 | 1932 |
/* Send root HTML file for preclassification */ |
1930 | 1933 |
if (mctx->ctx->wrkproperty) |
... | ... |
@@ -1950,6 +2340,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1950 | 1950 |
* Content-Type: multipart/related; |
1951 | 1951 |
* type="multipart/alternative" |
1952 | 1952 |
*/ |
1953 |
+ /* fall through */ |
|
1953 | 1954 |
case DIGEST: |
1954 | 1955 |
/* |
1955 | 1956 |
* According to section 5.1.5 RFC2046, the |
... | ... |
@@ -1971,6 +2362,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
1971 | 1971 |
* virus is broken that way, and anyway we |
1972 | 1972 |
* wish to scan all of the alternatives |
1973 | 1973 |
*/ |
1974 |
+ /* fall through */ |
|
1974 | 1975 |
case REPORT: |
1975 | 1976 |
/* |
1976 | 1977 |
* According to section 1 of RFC1892, the |
... | ... |
@@ -2081,7 +2473,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
2081 | 2081 |
rc = FAIL; |
2082 | 2082 |
if ((strcasecmp(mimeSubtype, "rfc822") == 0) || |
2083 | 2083 |
(strcasecmp(mimeSubtype, "delivery-status") == 0)) { |
2084 |
- message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); |
|
2084 |
+ message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table, &heuristicFound); |
|
2085 | 2085 |
if (m) { |
2086 | 2086 |
cli_dbgmsg("Decode rfc822\n"); |
2087 | 2087 |
|
... | ... |
@@ -2096,6 +2488,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
2096 | 2096 |
rc = parseEmailBody(m, NULL, mctx, recursion_level + 1); |
2097 | 2097 |
|
2098 | 2098 |
messageDestroy(m); |
2099 |
+ } else if (heuristicFound) { |
|
2100 |
+ rc = VIRUS; |
|
2099 | 2101 |
} |
2100 | 2102 |
break; |
2101 | 2103 |
} else if (strcasecmp(mimeSubtype, "disposition-notification") == 0) { |
... | ... |
@@ -2134,6 +2528,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re |
2134 | 2134 |
* Content-Type: application/unknown; |
2135 | 2135 |
* so let's try our best to salvage something |
2136 | 2136 |
*/ |
2137 |
+ /* fall through */ |
|
2137 | 2138 |
case APPLICATION: |
2138 | 2139 |
/*cptr = messageGetMimeSubtype(mainMessage); |
2139 | 2140 |
|
... | ... |
@@ -2734,11 +3129,14 @@ strstrip(char *s) |
2734 | 2734 |
* Returns 0 for OK, -1 for error |
2735 | 2735 |
*/ |
2736 | 2736 |
static int |
2737 |
-parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg) |
|
2737 |
+parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg, cli_ctx *ctx, bool *heuristicFound) |
|
2738 | 2738 |
{ |
2739 | 2739 |
char *copy, *p, *buf; |
2740 | 2740 |
const char *ptr; |
2741 | 2741 |
int commandNumber; |
2742 |
+ size_t argCnt = 0; |
|
2743 |
+ |
|
2744 |
+ *heuristicFound = FALSE; |
|
2742 | 2745 |
|
2743 | 2746 |
cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg); |
2744 | 2747 |
|
... | ... |
@@ -2746,15 +3144,17 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c |
2746 | 2746 |
if (copy) { |
2747 | 2747 |
commandNumber = tableFind(rfc821Table, copy); |
2748 | 2748 |
free(copy); |
2749 |
- } else |
|
2749 |
+ } else { |
|
2750 | 2750 |
commandNumber = tableFind(rfc821Table, cmd); |
2751 |
+ } |
|
2751 | 2752 |
|
2752 | 2753 |
copy = rfc822comments(arg, NULL); |
2753 | 2754 |
|
2754 |
- if (copy) |
|
2755 |
+ if (copy) { |
|
2755 | 2756 |
ptr = copy; |
2756 |
- else |
|
2757 |
+ } else { |
|
2757 | 2758 |
ptr = arg; |
2759 |
+ } |
|
2758 | 2760 |
|
2759 | 2761 |
buf = NULL; |
2760 | 2762 |
|
... | ... |
@@ -2889,6 +3289,11 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c |
2889 | 2889 |
while (cli_strtokbuf(ptr, i++, ";", buf) != NULL) { |
2890 | 2890 |
cli_dbgmsg("mimeArgs = '%s'\n", buf); |
2891 | 2891 |
|
2892 |
+ argCnt++; |
|
2893 |
+ if (haveTooManyMIMEArguments(argCnt, ctx)) { |
|
2894 |
+ *heuristicFound = TRUE; |
|
2895 |
+ break; |
|
2896 |
+ } |
|
2892 | 2897 |
messageAddArguments(m, buf); |
2893 | 2898 |
} |
2894 | 2899 |
} |
... | ... |
@@ -3277,7 +3682,7 @@ rfc1341(message *m, const char *dir) |
3277 | 3277 |
while ((dent = readdir(dd))) { |
3278 | 3278 |
#endif |
3279 | 3279 |
FILE *fin; |
3280 |
- char buffer[BUFSIZ], fullname[NAME_MAX + 1]; |
|
3280 |
+ char buffer[BUFSIZ], fullname[PATH_MAX + 1]; |
|
3281 | 3281 |
int nblanks; |
3282 | 3282 |
STATBUF statb; |
3283 | 3283 |
const char *dentry_idpart; |
... | ... |
@@ -3802,8 +4207,9 @@ static const char *getMimeTypeStr(mime_type mimetype) |
3802 | 3802 |
const struct tableinit *entry = mimeTypeStr; |
3803 | 3803 |
|
3804 | 3804 |
while (entry->key) { |
3805 |
- if (mimetype == entry->value) |
|
3805 |
+ if (mimetype == ((mime_type)entry->value)) { |
|
3806 | 3806 |
return entry->key; |
3807 |
+ } |
|
3807 | 3808 |
entry++; |
3808 | 3809 |
} |
3809 | 3810 |
return "UNKNOWN"; |
... | ... |
@@ -3817,8 +4223,9 @@ static const char *getEncTypeStr(encoding_type enctype) |
3817 | 3817 |
const struct tableinit *entry = encTypeStr; |
3818 | 3818 |
|
3819 | 3819 |
while (entry->key) { |
3820 |
- if (enctype == entry->value) |
|
3820 |
+ if (enctype == ((encoding_type)entry->value)) { |
|
3821 | 3821 |
return entry->key; |
3822 |
+ } |
|
3822 | 3823 |
entry++; |
3823 | 3824 |
} |
3824 | 3825 |
return "UNKNOWN"; |
... | ... |
@@ -3838,7 +4245,6 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m |
3838 | 3838 |
message *aMessage = messages[i]; |
3839 | 3839 |
const int doPhishingScan = mctx->ctx->engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); |
3840 | 3840 |
#if HAVE_JSON |
3841 |
- const char *mtype = NULL; |
|
3842 | 3841 |
json_object *thisobj = NULL, *saveobj = mctx->wrkobj; |
3843 | 3842 |
|
3844 | 3843 |
if (mctx->wrkobj != NULL) { |
... | ... |
@@ -4058,8 +4464,9 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m |
4058 | 4058 |
messages[i] = NULL; |
4059 | 4059 |
} else { |
4060 | 4060 |
*rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); |
4061 |
- if (mainMessage && (mainMessage != messageIn)) |
|
4061 |
+ if (mainMessage && (mainMessage != messageIn)) { |
|
4062 | 4062 |
messageDestroy(mainMessage); |
4063 |
+ } |
|
4063 | 4064 |
mainMessage = NULL; |
4064 | 4065 |
} |
4065 | 4066 |
#if HAVE_JSON |
... | ... |
@@ -4080,18 +4487,21 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m |
4080 | 4080 |
|
4081 | 4081 |
if (thisobj != NULL) { |
4082 | 4082 |
/* attempt to determine container size - prevents incorrect type reporting */ |
4083 |
- if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) |
|
4083 |
+ if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) { |
|
4084 | 4084 |
arrlen = json_object_array_length(arrobj); |
4085 |
+ } |
|
4085 | 4086 |
} |
4086 | 4087 |
|
4087 | 4088 |
#endif |
4088 | 4089 |
if (fb) { |
4089 | 4090 |
/* aMessage doesn't always have a ctx set */ |
4090 | 4091 |
fileblobSetCTX(fb, mctx->ctx); |
4091 |
- if (fileblobScanAndDestroy(fb) == CL_VIRUS) |
|
4092 |
+ if (fileblobScanAndDestroy(fb) == CL_VIRUS) { |
|
4092 | 4093 |
*rc = VIRUS; |
4093 |
- if (!addToText) |
|
4094 |
+ } |
|
4095 |
+ if (!addToText) { |
|
4094 | 4096 |
mctx->files++; |
4097 |
+ } |
|
4095 | 4098 |
} |
4096 | 4099 |
#if HAVE_JSON |
4097 | 4100 |
if (thisobj != NULL) { |
... | ... |
@@ -4099,20 +4509,24 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m |
4099 | 4099 |
const char *dtype = NULL; |
4100 | 4100 |
|
4101 | 4101 |
/* attempt to acquire container type */ |
4102 |
- if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) |
|
4103 |
- if (json_object_array_length(arrobj) > arrlen) |
|
4102 |
+ if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) { |
|
4103 |
+ if (json_object_array_length(arrobj) > ((int)arrlen)) { |
|
4104 | 4104 |
entry = json_object_array_get_idx(arrobj, arrlen); |
4105 |
+ } |
|
4106 |
+ } |
|
4105 | 4107 |
if (entry) { |
4106 | 4108 |
json_object_object_get_ex(entry, "FileType", &entry); |
4107 |
- if (entry) |
|
4109 |
+ if (entry) { |
|
4108 | 4110 |
dtype = json_object_get_string(entry); |
4111 |
+ } |
|
4109 | 4112 |
} |
4110 | 4113 |
cli_jsonint(thisobj, "ContainedObjectsIndex", arrlen); |
4111 | 4114 |
cli_jsonstr(thisobj, "ClamAVFileType", dtype ? dtype : "UNKNOWN"); |
4112 | 4115 |
} |
4113 | 4116 |
#endif |
4114 |
- if (messageContainsVirus(aMessage)) |
|
4117 |
+ if (messageContainsVirus(aMessage)) { |
|
4115 | 4118 |
*rc = VIRUS; |
4119 |
+ } |
|
4116 | 4120 |
} |
4117 | 4121 |
messageDestroy(aMessage); |
4118 | 4122 |
messages[i] = NULL; |
... | ... |
@@ -4216,5 +4630,7 @@ newline_in_header(const char *line) |
4216 | 4216 |
if (strncmp(line, "Date: ", 6) == 0) |
4217 | 4217 |
return TRUE; |
4218 | 4218 |
|
4219 |
+ cli_dbgmsg("newline_in_header, returning \"%s\"\n", line); |
|
4220 |
+ |
|
4219 | 4221 |
return FALSE; |
4220 | 4222 |
} |