File clamav-CVE-2019-15961.patch of Package clamav.13406
--- libclamav/mbox.c.orig
+++ libclamav/mbox.c
@@ -206,9 +205,9 @@ typedef struct mbox_ctx {
#endif
static int cli_parse_mbox(const char *dir, cli_ctx *ctx);
-static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir);
-static message *parseEmailHeaders(message *m, const table_t *rfc821Table);
-static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table);
+static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir, cli_ctx *ctx, bool *heuristicFound);
+static message *parseEmailHeaders(message *m, const table_t *rfc821Table, bool *heuristicFound);
+static int parseEmailHeader(message *m, const char *line, const table_t *rfc821, cli_ctx *ctx, bool *heuristicFound);
static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata);
static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t);
static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level);
@@ -217,7 +216,7 @@ static int boundaryEnd(const char *line,
static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable);
static int getTextPart(message *const messages[], size_t size);
static size_t strip(char *buf, int len);
-static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg);
+static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg, cli_ctx *ctx, bool *heuristicFound);
static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text);
static char *rfc2047(const char *in);
static char *rfc822comments(const char *in, char *out);
@@ -238,6 +237,12 @@ static blob *getHrefs(message *m, tag_ar
static void hrefs_done(blob *b, tag_arguments_t *hrefs);
static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html);
+static bool haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx);
+static bool hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx);
+static bool haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx);
+static bool haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx);
+static bool haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx);
+
/* Maximum line length according to RFC2821 */
#define RFC2821LENGTH 1000
@@ -286,6 +291,12 @@ static void checkURLs(message *m, mbox_c
*/
#define KNOWBOT 14 /* Unknown and undocumented format? */
+#define HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER (256 * 1024)
+#define HEURISTIC_EMAIL_MAX_HEADER_BYTES (1024 * 256)
+#define HEURISTIC_EMAIL_MAX_HEADERS 1024
+#define HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE 1024
+#define HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER 256
+
static const struct tableinit {
const char *key;
int value;
@@ -468,9 +479,14 @@ cli_parse_mbox(const char *dir, cli_ctx
/*
* End of a message in the mail box
*/
- body = parseEmailHeaders(m, rfc821);
+ bool heuristicFound = FALSE;
+ body = parseEmailHeaders(m, rfc821, &heuristicFound);
if(body == NULL) {
messageReset(m);
+ if (heuristicFound) {
+ retcode = CL_VIRUS;
+ break;
+ }
continue;
}
messageSetCTX(body, ctx);
@@ -521,7 +537,11 @@ cli_parse_mbox(const char *dir, cli_ctx
if(retcode == CL_SUCCESS) {
cli_dbgmsg("Extract attachments from email %d\n", messagenumber);
- body = parseEmailHeaders(m, rfc821);
+ bool heuristicFound = FALSE;
+ body = parseEmailHeaders(m, rfc821, &heuristicFound);
+ if (heuristicFound) {
+ retcode = CL_VIRUS;
+ }
}
if(m)
messageDestroy(m);
@@ -548,7 +568,11 @@ cli_parse_mbox(const char *dir, cli_ctx
buffer[sizeof(buffer) - 1] = '\0';
- body = parseEmailFile(map, &at, rfc821, buffer, dir);
+ bool heuristicFound = FALSE;
+ body = parseEmailFile(map, &at, rfc821, buffer, dir, ctx, &heuristicFound);
+ if (heuristicFound) {
+ retcode = CL_VIRUS;
+ }
}
if(body) {
@@ -604,6 +628,253 @@ cli_parse_mbox(const char *dir, cli_ctx
return retcode;
}
+/*TODO: move these to a header.*/
+#define DO_STRDUP(buf, var) \
+ do { \
+ var = strdup(buf); \
+ if (NULL == var) { \
+ goto done; \
+ } \
+ } while (0)
+
+#define DO_FREE(var) \
+ do { \
+ if (NULL != var) { \
+ free(var); \
+ var = NULL; \
+ } \
+ } while (0)
+
+#define DO_MALLOC(var, size) \
+ do { \
+ var = malloc(size); \
+ if (NULL == var) { \
+ goto done; \
+ } \
+ } while (0)
+
+#define DO_CALLOC(var, size) \
+ do { \
+ (var) = calloc((size), sizeof *(var)); \
+ if (NULL == var) { \
+ goto done; \
+ } \
+ } while (0)
+
+#define DO_VERIFY_POINTER(ptr) \
+ do { \
+ if (NULL == ptr) { \
+ goto done; \
+ } \
+ } while (0)
+
+#define READ_STRUCT_BUFFER_LEN 1024
+typedef struct _ReadStruct {
+ char buffer[READ_STRUCT_BUFFER_LEN + 1];
+
+ size_t bufferLen;
+
+ struct _ReadStruct *next;
+
+} ReadStruct;
+
+static ReadStruct *
+appendReadStruct(ReadStruct *rs, const char *const buffer)
+{
+ if (NULL == rs) {
+ assert(rs && "Invalid argument");
+ goto done;
+ }
+
+ size_t spaceLeft = (READ_STRUCT_BUFFER_LEN - rs->bufferLen);
+
+ if (strlen(buffer) > spaceLeft) {
+ ReadStruct *next = NULL;
+ int part = spaceLeft;
+ strncpy(&(rs->buffer[rs->bufferLen]), buffer, part);
+ rs->bufferLen += part;
+
+ DO_CALLOC(next, 1);
+
+ rs->next = next;
+ strcpy(next->buffer, &(buffer[part]));
+ next->bufferLen = strlen(&(buffer[part]));
+
+ rs = next;
+ } else {
+ strcpy(&(rs->buffer[rs->bufferLen]), buffer);
+ rs->bufferLen += strlen(buffer);
+ }
+
+done:
+ return rs;
+}
+
+static char *
+getMallocedBufferFromList(const ReadStruct *head)
+{
+
+ const ReadStruct *rs = head;
+ int bufferLen = 1;
+ char *working = NULL;
+ char *ret = NULL;
+
+ while (rs) {
+ bufferLen += rs->bufferLen;
+ rs = rs->next;
+ }
+
+ DO_MALLOC(working, bufferLen);
+
+ rs = head;
+ bufferLen = 0;
+ while (rs) {
+ memcpy(&(working[bufferLen]), rs->buffer, rs->bufferLen);
+ bufferLen += rs->bufferLen;
+ working[bufferLen] = 0;
+ rs = rs->next;
+ }
+
+ ret = working;
+done:
+ if (NULL == ret) {
+ DO_FREE(working);
+ }
+
+ return ret;
+}
+
+static void
+freeList(ReadStruct *head)
+{
+ while (head) {
+ ReadStruct *rs = head->next;
+ DO_FREE(head);
+ head = rs;
+ }
+}
+
+#ifndef FREELIST_REALLOC
+#define FREELIST_REALLOC(head, curr) \
+ do { \
+ if (curr != head) { \
+ freeList(head->next); \
+ } \
+ head->bufferLen = 0; \
+ head->next = 0; \
+ curr = head; \
+ } while (0)
+#endif /*FREELIST_REALLOC*/
+
+/*Check if we have repeated blank lines with only a semicolon at the end. Semicolon is a delimiter for parameters,
+ * but if there is no data, it isn't a parameter. Allow the first one because it may be continuation of a previous line
+ * that actually had data in it.*/
+static bool
+doContinueMultipleEmptyOptions(const char *const line, bool *lastWasOnlySemi)
+{
+ if (line) {
+ size_t i = 0;
+ int doCont = 1;
+ for (; i < strlen(line); i++) {
+ if (isblank(line[i])) {
+ } else if (';' == line[i]) {
+ } else {
+ doCont = 0;
+ break;
+ }
+ }
+
+ if (1 == doCont) {
+ if (*lastWasOnlySemi) {
+ return TRUE;
+ }
+ *lastWasOnlySemi = TRUE;
+ } else {
+ *lastWasOnlySemi = FALSE;
+ }
+ }
+ return FALSE;
+}
+
+static bool
+hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx)
+{
+
+ if (line) {
+ if (isblank(line[0])) {
+ (*lineFoldCnt)++;
+ } else {
+ (*lineFoldCnt) = 0;
+ }
+
+ if ((*lineFoldCnt) >= HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER) {
+ if (ctx->options & CL_SCAN_ALGORITHMIC) {
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxLineFoldCnt");
+ }
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool
+haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx)
+{
+
+ if (totalLen > HEURISTIC_EMAIL_MAX_HEADER_BYTES) {
+ if (ctx->options & CL_SCAN_ALGORITHMIC) {
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxHeaderBytes");
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool
+haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx)
+{
+
+ if (totalHeaderCnt > HEURISTIC_EMAIL_MAX_HEADERS) {
+ if (ctx->options & CL_SCAN_ALGORITHMIC) {
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxEmailHeaders");
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool
+haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx)
+{
+
+ if (mimePartCnt >= HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE) {
+ if (ctx->options & CL_SCAN_ALGORITHMIC) {
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEPartsPerMessage");
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool
+haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx)
+{
+
+ if (argCnt >= HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER) {
+ if (ctx->options & CL_SCAN_ALGORITHMIC) {
+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEArguments");
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*
* Read in an email message from fin, parse it, and return the message
*
@@ -611,7 +882,7 @@ cli_parse_mbox(const char *dir, cli_ctx
* handled ungracefully...
*/
static message *
-parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir)
+parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir, cli_ctx *ctx, bool *heuristicFound)
{
bool inHeader = TRUE;
bool bodyIsEmpty = TRUE;
@@ -619,9 +890,21 @@ parseEmailFile(fmap_t *map, size_t *at,
message *ret;
bool anyHeadersFound = FALSE;
int commandNumber = -1;
- char *fullline = NULL, *boundary = NULL;
- size_t fulllinelength = 0;
+ char *boundary = NULL;
char buffer[RFC2821LENGTH + 1];
+ bool lastWasOnlySemi = FALSE;
+ int err = 1;
+ size_t totalHeaderBytes = 0;
+ size_t totalHeaderCnt = 0;
+
+ size_t lineFoldCnt = 0;
+
+ *heuristicFound = FALSE;
+
+ ReadStruct *head = NULL;
+ ReadStruct *curr = NULL;
+ DO_CALLOC(head, 1);
+ curr = head;
cli_dbgmsg("parseEmailFile\n");
@@ -640,6 +923,15 @@ parseEmailFile(fmap_t *map, size_t *at,
else
line = buffer;
+ if (doContinueMultipleEmptyOptions(line, &lastWasOnlySemi)) {
+ continue;
+ }
+
+ if (hitLineFoldCnt(line, &lineFoldCnt, ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+
/*
* Don't blank lines which are only spaces from headers,
* otherwise they'll be treated as the end of header marker
@@ -652,8 +944,8 @@ parseEmailFile(fmap_t *map, size_t *at,
}
}
if(inHeader) {
- cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n",
- buffer, fullline);
+ cli_dbgmsg("parseEmailFile: check '%s'\n", buffer);
+
/*
* Ensure wide characters are handled where
* sizeof(char) > 1
@@ -677,13 +969,29 @@ parseEmailFile(fmap_t *map, size_t *at,
* content-type line. So we just have
* to make a best guess. Sigh.
*/
- if(fullline) {
- if(parseEmailHeader(ret, fullline, rfc821) < 0)
+ if (head->bufferLen) {
+ char *header = getMallocedBufferFromList(head);
+ int needContinue = 0;
+ DO_VERIFY_POINTER(header);
+
+ totalHeaderCnt++;
+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+ needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0);
+ if (*heuristicFound) {
+ DO_FREE(header);
+ break;
+ }
+ DO_FREE(header);
+ FREELIST_REALLOC(head, curr);
+
+ if (needContinue) {
continue;
-
- free(fullline);
- fullline = NULL;
+ }
}
+
if(boundary ||
((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) {
lastWasBlank = TRUE;
@@ -691,7 +999,7 @@ parseEmailFile(fmap_t *map, size_t *at,
}
}
}
- if((line == NULL) && (fullline == NULL)) { /* empty line */
+ if((line == NULL) && (0 == head->bufferLen)) { /* empty line */
/*
* A blank line signifies the end of
* the header and the start of the text
@@ -706,8 +1014,9 @@ parseEmailFile(fmap_t *map, size_t *at,
} else {
char *ptr;
const char *lookahead;
+ bool lineAdded = TRUE;
- if(fullline == NULL) {
+ if(0 == head->bufferLen) {
char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1];
/*
@@ -740,23 +1049,26 @@ parseEmailFile(fmap_t *map, size_t *at,
anyHeadersFound = usefulHeader(commandNumber, cmd);
continue;
}
- fullline = cli_strdup(line);
- fulllinelength = strlen(line) + 1;
- if(!fullline) {
- if(ret)
+ curr = appendReadStruct(curr, line);
+ if (NULL == curr) {
+ if (ret) {
ret->isTruncated = TRUE;
+ }
break;
}
} else if(line != NULL) {
- fulllinelength += strlen(line) + 1;
- ptr = cli_realloc(fullline, fulllinelength);
- if(ptr == NULL)
- continue;
- fullline = ptr;
- cli_strlcat(fullline, line, fulllinelength);
+ curr = appendReadStruct(curr, line);
+ } else {
+ lineAdded = FALSE;
}
- assert(fullline != NULL);
+ if (lineAdded) {
+ totalHeaderBytes += strlen(line);
+ if (haveTooManyHeaderBytes(totalHeaderBytes, ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+ }
if((lookahead = fmap_need_off_once(map, *at, 1))) {
/*
@@ -774,24 +1086,35 @@ parseEmailFile(fmap_t *map, size_t *at,
* Handle broken headers, where the next
* line isn't indented by whitespace
*/
- if(fullline[strlen(fullline) - 1] == ';')
- /* Add arguments to this line */
- continue;
+ {
+ char *header = getMallocedBufferFromList(head); /*This is the issue */
+ int needContinue = 0;
+ needContinue = (header[strlen(header) - 1] == ';');
+ if (0 == needContinue) {
+ needContinue = (line && (count_quotes(header) & 1));
+ }
- if(line && (count_quotes(fullline) & 1))
- continue;
+ if (0 == needContinue) {
+ totalHeaderCnt++;
+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+ needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0);
+ if (*heuristicFound) {
+ DO_FREE(header);
+ break;
+ }
+ /*Check total headers here;*/
+ }
- ptr = rfc822comments(fullline, NULL);
- if(ptr) {
- free(fullline);
- fullline = ptr;
+ DO_FREE(header);
+ if (needContinue) {
+ continue;
+ }
+ FREELIST_REALLOC(head, curr);
}
- if(parseEmailHeader(ret, fullline, rfc821) < 0)
- continue;
-
- free(fullline);
- fullline = NULL;
}
} else if(line && isuuencodebegin(line)) {
/*
@@ -835,19 +1158,17 @@ parseEmailFile(fmap_t *map, size_t *at,
}
} while(getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL);
- if(boundary)
- free(boundary);
-
- if(fullline) {
- if(*fullline) switch(commandNumber) {
- case CONTENT_TRANSFER_ENCODING:
- case CONTENT_DISPOSITION:
- case CONTENT_TYPE:
- cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline);
- }
- free(fullline);
+ err = 0;
+ done:
+ if (err) {
+ cli_errmsg("parseEmailFile: ERROR parsing file\n");
+ ret->isTruncated = TRUE;
}
+ DO_FREE(boundary);
+
+ freeList(head);
+
if(!anyHeadersFound) {
/*
* False positive in believing we have an e-mail when we don't
@@ -857,6 +1178,12 @@ parseEmailFile(fmap_t *map, size_t *at,
return NULL;
}
+ if (*heuristicFound) {
+ messageDestroy(ret);
+ cli_dbgmsg("parseEmailFile: found heuristic\n");
+ return NULL;
+ }
+
cli_dbgmsg("parseEmailFile: return\n");
return ret;
@@ -871,7 +1198,7 @@ parseEmailFile(fmap_t *map, size_t *at,
* TODO: remove the duplication with parseEmailFile
*/
static message *
-parseEmailHeaders(message *m, const table_t *rfc821)
+parseEmailHeaders(message *m, const table_t *rfc821, bool *heuristicFound)
{
bool inHeader = TRUE;
bool bodyIsEmpty = TRUE;
@@ -881,9 +1208,14 @@ parseEmailHeaders(message *m, const tabl
int commandNumber = -1;
char *fullline = NULL;
size_t fulllinelength = 0;
+ bool lastWasOnlySemi = FALSE;
+ size_t lineFoldCnt = 0;
+ size_t totalHeaderCnt = 0;
cli_dbgmsg("parseEmailHeaders\n");
+ *heuristicFound = FALSE;
+
if(m == NULL)
return NULL;
@@ -897,6 +1229,15 @@ parseEmailHeaders(message *m, const tabl
else
line = NULL;
+ if (doContinueMultipleEmptyOptions(line, &lastWasOnlySemi)) {
+ continue;
+ }
+
+ if (hitLineFoldCnt(line, &lineFoldCnt, m->ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+
if(inHeader) {
cli_dbgmsg("parseEmailHeaders: check '%s'\n",
line ? line : "");
@@ -914,6 +1255,7 @@ parseEmailHeaders(message *m, const tabl
bodyIsEmpty = TRUE;
} else {
char *ptr;
+ bool lineAdded = TRUE;
if(fullline == NULL) {
char cmd[RFC2821LENGTH + 1];
@@ -959,8 +1301,21 @@ parseEmailHeaders(message *m, const tabl
continue;
fullline = ptr;
cli_strlcat(fullline, line, fulllinelength);
+ } else {
+ lineAdded = FALSE;
}
assert(fullline != NULL);
+ /*continue doesn't seem right here, but that is what is done everywhere else when a malloc fails.*/
+ if (NULL == fullline) {
+ continue;
+ }
+
+ if (lineAdded) {
+ if (haveTooManyHeaderBytes(fulllinelength, m->ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+ }
if(next_is_folded_header(t))
/* Add arguments to this line */
@@ -978,8 +1333,17 @@ parseEmailHeaders(message *m, const tabl
fullline = ptr;
}
- if(parseEmailHeader(ret, fullline, rfc821) < 0)
+ totalHeaderCnt++;
+ if (haveTooManyEmailHeaders(totalHeaderCnt, m->ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
+ if (parseEmailHeader(ret, fullline, rfc821, m->ctx, heuristicFound) < 0) {
continue;
+ }
+ if (*heuristicFound) {
+ break;
+ }
free(fullline);
fullline = NULL;
@@ -1025,6 +1389,11 @@ parseEmailHeaders(message *m, const tabl
cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n");
return NULL;
}
+ if (*heuristicFound) {
+ messageDestroy(ret);
+ cli_dbgmsg("parseEmailHeaders: found a heuristic, delete message and stop parsing.\n");
+ return NULL;
+ }
cli_dbgmsg("parseEmailHeaders: return\n");
@@ -1035,9 +1404,9 @@ parseEmailHeaders(message *m, const tabl
* Handle a header line of an email message
*/
static int
-parseEmailHeader(message *m, const char *line, const table_t *rfc821)
+parseEmailHeader(message *m, const char *line, const table_t *rfc821, cli_ctx *ctx, bool *heuristicFound)
{
- int ret;
+ int ret = -1;
#ifdef CL_THREAD_SAFE
char *strptr;
#endif
@@ -1060,15 +1429,17 @@ parseEmailHeader(message *m, const char
return -1;
copy = rfc2047(line);
- if(copy == NULL)
+ if(copy == NULL) {
/* an RFC checker would return -1 here */
copy = cli_strdup(line);
+ if (NULL == copy) {
+ goto done;
+ }
+ }
tokenseparator[0] = *separator;
tokenseparator[1] = '\0';
- ret = -1;
-
#ifdef CL_THREAD_SAFE
cmd = strtok_r(copy, tokenseparator, &strptr);
#else
@@ -1082,7 +1453,7 @@ parseEmailHeader(message *m, const char
char *arg = strtok(NULL, "");
#endif
- if(arg)
+ if(arg) {
/*
* Found a header such as
* Content-Type: multipart/mixed;
@@ -1090,9 +1461,12 @@ parseEmailHeader(message *m, const char
* "multipart/mixed" and cmd to
* be "Content-Type"
*/
- ret = parseMimeHeader(m, cmd, rfc821, arg);
+ ret = parseMimeHeader(m, cmd, rfc821, arg, ctx, heuristicFound);
+ }
}
- free(copy);
+done:
+ DO_FREE(copy);
+
return ret;
}
@@ -1145,9 +1519,6 @@ parseMHTMLComment(const char *comment, c
#if HAVE_LIBXML2
const char *xmlsrt, *xmlend;
xmlTextReaderPtr reader;
-#if HAVE_JSON
- json_object *thisjobj = (json_object *)wrkjobj;
-#endif
int ret = CL_SUCCESS;
UNUSEDPARAM(cbdata);
@@ -1340,6 +1711,7 @@ parseEmailBody(message *messageIn, text
#if HAVE_JSON
json_object *saveobj = mctx->wrkobj;
#endif
+ bool heuristicFound = FALSE;
cli_dbgmsg("in parseEmailBody, %u files saved so far\n",
mctx->files);
@@ -1636,7 +2008,11 @@ parseEmailBody(message *messageIn, text
* Content-Type: application/octet-stream;
* Content-Transfer-Encoding: base64
*/
- parseEmailHeader(aMessage, line, mctx->rfc821Table);
+ parseEmailHeader(aMessage, line, mctx->rfc821Table, mctx->ctx, &heuristicFound);
+ if (heuristicFound) {
+ rc = VIRUS;
+ break;
+ }
while(isspace((int)*line))
line++;
@@ -1781,8 +2157,11 @@ parseEmailBody(message *messageIn, text
cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n",
multiparts, fullline);
- parseEmailHeader(aMessage, fullline, mctx->rfc821Table);
+ parseEmailHeader(aMessage, fullline, mctx->rfc821Table, mctx->ctx, &heuristicFound);
free(fullline);
+ if (heuristicFound) {
+ rc = VIRUS;
+ }
} else if(boundaryEnd(line, boundary)) {
/*
* Some viruses put information
@@ -1859,6 +2238,12 @@ parseEmailBody(message *messageIn, text
free((char *)boundary);
+ if (haveTooManyMIMEPartsPerMessage(multiparts, mctx->ctx)) {
+ DO_FREE(messages);
+ rc = VIRUS;
+ break;
+ }
+
/*
* Preprocess. Anything special to be done before
* we handle the multiparts?
@@ -2109,7 +2499,7 @@ parseEmailBody(message *messageIn, text
rc = FAIL;
if((strcasecmp(mimeSubtype, "rfc822") == 0) ||
(strcasecmp(mimeSubtype, "delivery-status") == 0)) {
- message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table);
+ message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table, &heuristicFound);
if(m) {
cli_dbgmsg("Decode rfc822\n");
@@ -2124,6 +2514,8 @@ parseEmailBody(message *messageIn, text
rc = parseEmailBody(m, NULL, mctx, recursion_level + 1);
messageDestroy(m);
+ } else if (heuristicFound) {
+ rc = VIRUS;
}
break;
} else if(strcasecmp(mimeSubtype, "disposition-notification") == 0) {
@@ -2762,11 +3155,14 @@ strstrip(char *s)
* Returns 0 for OK, -1 for error
*/
static int
-parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg)
+parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg, cli_ctx *ctx, bool *heuristicFound)
{
char *copy, *p, *buf;
const char *ptr;
int commandNumber;
+ size_t argCnt = 0;
+
+ *heuristicFound = FALSE;
cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg);
@@ -2917,6 +3313,11 @@ parseMimeHeader(message *m, const char *
while(cli_strtokbuf(ptr, i++, ";", buf) != NULL) {
cli_dbgmsg("mimeArgs = '%s'\n", buf);
+ argCnt++;
+ if (haveTooManyMIMEArguments(argCnt, ctx)) {
+ *heuristicFound = TRUE;
+ break;
+ }
messageAddArguments(m, buf);
}
}
@@ -3866,7 +4269,6 @@ do_multipart(message *mainMessage, messa
message *aMessage = messages[i];
const int doPhishingScan = mctx->ctx->engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING&PHISHING_CONF_ENGINE);
#if HAVE_JSON
- const char *mtype = NULL;
json_object *thisobj = NULL, *saveobj = mctx->wrkobj;
if (mctx->wrkobj != NULL) {
@@ -4245,5 +4647,7 @@ newline_in_header(const char *line)
if(strncmp(line, "Date: ", 6) == 0)
return TRUE;
+ cli_dbgmsg("newline_in_header, returning \"%s\"\n", line);
+
return FALSE;
}