File clamav-CVE-2019-15961.patch of Package clamav.14692

--- 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;
 }
openSUSE Build Service is sponsored by