git-svn: trunk@2053
Nigel Horne authored on 2006/06/29 06:07:36... | ... |
@@ -16,7 +16,7 @@ |
16 | 16 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
17 | 17 |
* MA 02110-1301, USA. |
18 | 18 |
*/ |
19 |
-static char const rcsid[] = "$Id: mbox.c,v 1.312 2006/06/28 16:06:07 njh Exp $"; |
|
19 |
+static char const rcsid[] = "$Id: mbox.c,v 1.313 2006/06/28 21:07:36 njh Exp $"; |
|
20 | 20 |
|
21 | 21 |
#if HAVE_CONFIG_H |
22 | 22 |
#include "clamav-config.h" |
... | ... |
@@ -187,11 +187,18 @@ typedef enum { FALSE = 0, TRUE = 1 } bool; |
187 | 187 |
* of EICAR within bounces, which don't matter |
188 | 188 |
*/ |
189 | 189 |
|
190 |
+typedef struct mbox_ctx { |
|
191 |
+ const char *dir; |
|
192 |
+ const table_t *rfc821Table; |
|
193 |
+ const table_t *subtypeTable; |
|
194 |
+ cli_ctx *ctx; |
|
195 |
+} mbox_ctx; |
|
196 |
+ |
|
190 | 197 |
static int cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx); |
191 | 198 |
static message *parseEmailFile(FILE *fin, const table_t *rfc821Table, const char *firstLine, const char *dir); |
192 | 199 |
static message *parseEmailHeaders(const message *m, const table_t *rfc821Table); |
193 | 200 |
static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); |
194 |
-static int parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t *rfc821Table, const table_t *subtypeTable, cli_ctx *ctx); |
|
201 |
+static int parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx); |
|
195 | 202 |
static int boundaryStart(const char *line, const char *boundary); |
196 | 203 |
static int endOfMessage(const char *line, const char *boundary); |
197 | 204 |
static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); |
... | ... |
@@ -209,6 +216,7 @@ static bool usefulHeader(int commandNumber, const char *cmd); |
209 | 209 |
static char *getline_from_mbox(char *buffer, size_t len, FILE *fin); |
210 | 210 |
static bool isBounceStart(const char *line); |
211 | 211 |
static bool binhexMessage(const char *dir, message *message); |
212 |
+static message *do_multipart(message *mainMessage, message **messages, int i, int *rc, mbox_ctx *mctx, message *messageIn, text **tptr); |
|
212 | 213 |
|
213 | 214 |
static void checkURLs(message *m, const char *dir); |
214 | 215 |
#ifdef WITH_CURL |
... | ... |
@@ -1127,6 +1135,7 @@ cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx) |
1127 | 1127 |
message *body; |
1128 | 1128 |
FILE *fd; |
1129 | 1129 |
char buffer[RFC2821LENGTH + 1]; |
1130 |
+ mbox_ctx mctx; |
|
1130 | 1131 |
#ifdef HAVE_BACKTRACE |
1131 | 1132 |
void (*segv)(int); |
1132 | 1133 |
#endif |
... | ... |
@@ -1211,6 +1220,11 @@ cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx) |
1211 | 1211 |
retcode = CL_SUCCESS; |
1212 | 1212 |
body = NULL; |
1213 | 1213 |
|
1214 |
+ mctx.dir = dir; |
|
1215 |
+ mctx.rfc821Table = rfc821; |
|
1216 |
+ mctx.subtypeTable = subtype; |
|
1217 |
+ mctx.ctx = ctx; |
|
1218 |
+ |
|
1214 | 1219 |
/* |
1215 | 1220 |
* Is it a UNIX style mbox with more than one |
1216 | 1221 |
* mail message, or just a single mail message? |
... | ... |
@@ -1276,7 +1290,7 @@ cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx) |
1276 | 1276 |
messageSetCTX(body, ctx); |
1277 | 1277 |
messageDestroy(m); |
1278 | 1278 |
if(messageGetBody(body)) { |
1279 |
- int rc = parseEmailBody(body, NULL, dir, rfc821, subtype, ctx); |
|
1279 |
+ int rc = parseEmailBody(body, NULL, &mctx); |
|
1280 | 1280 |
if(rc == 0) { |
1281 | 1281 |
messageReset(body); |
1282 | 1282 |
m = body; |
... | ... |
@@ -1357,7 +1371,7 @@ cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx) |
1357 | 1357 |
*/ |
1358 | 1358 |
if((retcode == CL_SUCCESS) && messageGetBody(body)) { |
1359 | 1359 |
messageSetCTX(body, ctx); |
1360 |
- switch(parseEmailBody(body, NULL, dir, rfc821, subtype, ctx)) { |
|
1360 |
+ switch(parseEmailBody(body, NULL, &mctx)) { |
|
1361 | 1361 |
case 0: |
1362 | 1362 |
retcode = CL_EFORMAT; |
1363 | 1363 |
break; |
... | ... |
@@ -1921,12 +1935,11 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) |
1921 | 1921 |
* 3 for virus found |
1922 | 1922 |
*/ |
1923 | 1923 |
static int /* success or fail */ |
1924 |
-parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t *rfc821Table, const table_t *subtypeTable, cli_ctx *ctx) |
|
1924 |
+parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx) |
|
1925 | 1925 |
{ |
1926 | 1926 |
message **messages; /* parts of a multipart message */ |
1927 | 1927 |
int inMimeHead, i, rc = 1, htmltextPart, multiparts = 0; |
1928 | 1928 |
text *aText; |
1929 |
- const char *cptr; |
|
1930 | 1929 |
message *mainMessage; |
1931 | 1930 |
fileblob *fb; |
1932 | 1931 |
bool infected = FALSE; |
... | ... |
@@ -1953,7 +1966,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
1953 | 1953 |
mimeSubtype = messageGetMimeSubtype(mainMessage); |
1954 | 1954 |
|
1955 | 1955 |
/* pre-process */ |
1956 |
- subtype = tableFind(subtypeTable, mimeSubtype); |
|
1956 |
+ subtype = tableFind(mctx->subtypeTable, mimeSubtype); |
|
1957 | 1957 |
if((mimeType == TEXT) && (subtype == PLAIN)) { |
1958 | 1958 |
/* |
1959 | 1959 |
* This is effectively no encoding, notice that we |
... | ... |
@@ -1983,8 +1996,8 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
1983 | 1983 |
break; |
1984 | 1984 |
case TEXT: |
1985 | 1985 |
/* text/plain has been preprocessed as no encoding */ |
1986 |
- if((ctx->options&CL_SCAN_MAILURL) && (subtype == HTML)) |
|
1987 |
- checkURLs(mainMessage, dir); |
|
1986 |
+ if((mctx->ctx->options&CL_SCAN_MAILURL) && (subtype == HTML)) |
|
1987 |
+ checkURLs(mainMessage, mctx->dir); |
|
1988 | 1988 |
break; |
1989 | 1989 |
case MULTIPART: |
1990 | 1990 |
cli_dbgmsg("Content-type 'multipart' handler\n"); |
... | ... |
@@ -2030,7 +2043,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2030 | 2030 |
* TODO: check yEnc |
2031 | 2031 |
*/ |
2032 | 2032 |
if(binhexBegin(mainMessage) == t_line) { |
2033 |
- if(binhexMessage(dir, mainMessage)) { |
|
2033 |
+ if(binhexMessage(mctx->dir, mainMessage)) { |
|
2034 | 2034 |
/* virus found */ |
2035 | 2035 |
rc = 3; |
2036 | 2036 |
break; |
... | ... |
@@ -2096,7 +2109,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2096 | 2096 |
multiparts--; |
2097 | 2097 |
continue; |
2098 | 2098 |
} |
2099 |
- messageSetCTX(aMessage, ctx); |
|
2099 |
+ messageSetCTX(aMessage, mctx->ctx); |
|
2100 | 2100 |
|
2101 | 2101 |
cli_dbgmsg("Now read in part %d\n", multiparts); |
2102 | 2102 |
|
... | ... |
@@ -2153,7 +2166,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2153 | 2153 |
* Content-Type: application/octet-stream; |
2154 | 2154 |
* Content-Transfer-Encoding: base64 |
2155 | 2155 |
*/ |
2156 |
- parseEmailHeader(aMessage, line, rfc821Table); |
|
2156 |
+ parseEmailHeader(aMessage, line, mctx->rfc821Table); |
|
2157 | 2157 |
|
2158 | 2158 |
while(isspace((int)*line)) |
2159 | 2159 |
line++; |
... | ... |
@@ -2313,7 +2326,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2313 | 2313 |
cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", |
2314 | 2314 |
multiparts, fullline); |
2315 | 2315 |
|
2316 |
- parseEmailHeader(aMessage, fullline, rfc821Table); |
|
2316 |
+ parseEmailHeader(aMessage, fullline, mctx->rfc821Table); |
|
2317 | 2317 |
free(fullline); |
2318 | 2318 |
} else if(endOfMessage(line, boundary)) { |
2319 | 2319 |
/* |
... | ... |
@@ -2348,7 +2361,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2348 | 2348 |
* Preprocess. Anything special to be done before |
2349 | 2349 |
* we handle the multiparts? |
2350 | 2350 |
*/ |
2351 |
- switch(tableFind(subtypeTable, mimeSubtype)) { |
|
2351 |
+ switch(tableFind(mctx->subtypeTable, mimeSubtype)) { |
|
2352 | 2352 |
case KNOWBOT: |
2353 | 2353 |
/* TODO */ |
2354 | 2354 |
cli_dbgmsg("multipart/knowbot parsed as multipart/mixed for now\n"); |
... | ... |
@@ -2388,7 +2401,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2388 | 2388 |
* message *messages[multiparts] |
2389 | 2389 |
* Let's decide what to do with them all |
2390 | 2390 |
*/ |
2391 |
- switch(tableFind(subtypeTable, mimeSubtype)) { |
|
2391 |
+ switch(tableFind(mctx->subtypeTable, mimeSubtype)) { |
|
2392 | 2392 |
case RELATED: |
2393 | 2393 |
cli_dbgmsg("Multipart related handler\n"); |
2394 | 2394 |
/* |
... | ... |
@@ -2418,7 +2431,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2418 | 2418 |
if(htmltextPart == -1) |
2419 | 2419 |
cli_dbgmsg("No HTML code found to be scanned\n"); |
2420 | 2420 |
else { |
2421 |
- rc = parseEmailBody(aMessage, aText, dir, rfc821Table, subtypeTable, ctx); |
|
2421 |
+ rc = parseEmailBody(aMessage, aText, mctx); |
|
2422 | 2422 |
if(rc == 1) { |
2423 | 2423 |
assert(aMessage == messages[htmltextPart]); |
2424 | 2424 |
messageDestroy(aMessage); |
... | ... |
@@ -2497,230 +2510,16 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2497 | 2497 |
|
2498 | 2498 |
cli_dbgmsg("Mixed message with %d parts\n", multiparts); |
2499 | 2499 |
for(i = 0; i < multiparts; i++) { |
2500 |
- bool addToText = FALSE; |
|
2501 |
- const char *dtype; |
|
2502 |
-#ifndef SAVE_TO_DISC |
|
2503 |
- message *body; |
|
2504 |
-#endif |
|
2505 |
- |
|
2506 |
- aMessage = messages[i]; |
|
2507 |
- |
|
2508 |
- if(aMessage == NULL) |
|
2509 |
- continue; |
|
2510 |
- |
|
2511 |
- cli_dbgmsg("Mixed message part %d is of type %d\n", |
|
2512 |
- i, messageGetMimeType(aMessage)); |
|
2513 |
- |
|
2514 |
- switch(messageGetMimeType(aMessage)) { |
|
2515 |
- case APPLICATION: |
|
2516 |
- case AUDIO: |
|
2517 |
- case IMAGE: |
|
2518 |
- case VIDEO: |
|
2519 |
- break; |
|
2520 |
- case NOMIME: |
|
2521 |
- cli_dbgmsg("No mime headers found in multipart part %d\n", i); |
|
2522 |
- if(mainMessage) { |
|
2523 |
- if(binhexBegin(aMessage)) { |
|
2524 |
- cli_dbgmsg("Found binhex message in multipart/mixed mainMessage\n"); |
|
2525 |
- |
|
2526 |
- if(binhexMessage(dir, mainMessage)) { |
|
2527 |
- infected = TRUE; |
|
2528 |
- rc = 3; |
|
2529 |
- } |
|
2530 |
- } |
|
2531 |
- if(mainMessage != messageIn) |
|
2532 |
- messageDestroy(mainMessage); |
|
2533 |
- mainMessage = NULL; |
|
2534 |
- } else if(aMessage) { |
|
2535 |
- if(binhexBegin(aMessage)) { |
|
2536 |
- cli_dbgmsg("Found binhex message in multipart/mixed non mime part\n"); |
|
2537 |
- if(binhexMessage(dir, aMessage)) { |
|
2538 |
- infected = TRUE; |
|
2539 |
- rc = 3; |
|
2540 |
- } |
|
2541 |
- assert(aMessage == messages[i]); |
|
2542 |
- messageReset(messages[i]); |
|
2543 |
- } |
|
2544 |
- } |
|
2545 |
- addToText = TRUE; |
|
2546 |
- if(messageGetBody(aMessage) == NULL) |
|
2547 |
- /* |
|
2548 |
- * No plain text version |
|
2549 |
- */ |
|
2550 |
- cli_dbgmsg("No plain text alternative\n"); |
|
2551 |
- break; |
|
2552 |
- case TEXT: |
|
2553 |
- dtype = messageGetDispositionType(aMessage); |
|
2554 |
- cli_dbgmsg("Mixed message text part disposition \"%s\"\n", |
|
2555 |
- dtype); |
|
2556 |
- if(strcasecmp(dtype, "attachment") == 0) |
|
2557 |
- break; |
|
2558 |
- if((*dtype == '\0') || (strcasecmp(dtype, "inline") == 0)) { |
|
2559 |
- if(mainMessage && (mainMessage != messageIn)) |
|
2560 |
- messageDestroy(mainMessage); |
|
2561 |
- mainMessage = NULL; |
|
2562 |
- cptr = messageGetMimeSubtype(aMessage); |
|
2563 |
- cli_dbgmsg("Mime subtype \"%s\"\n", cptr); |
|
2564 |
- if((tableFind(subtypeTable, cptr) == PLAIN) && |
|
2565 |
- (messageGetEncoding(aMessage) == NOENCODING)) { |
|
2566 |
- char *filename; |
|
2567 |
- /* |
|
2568 |
- * Strictly speaking |
|
2569 |
- * a text/plain part is |
|
2570 |
- * not an attachment. We |
|
2571 |
- * pretend it is so that |
|
2572 |
- * we can decode and |
|
2573 |
- * scan it |
|
2574 |
- */ |
|
2575 |
- filename = (char *)messageFindArgument(aMessage, "filename"); |
|
2576 |
- if(filename == NULL) |
|
2577 |
- filename = (char *)messageFindArgument(aMessage, "name"); |
|
2578 |
- |
|
2579 |
- if(filename == NULL) { |
|
2580 |
- cli_dbgmsg("Adding part to main message\n"); |
|
2581 |
- addToText = TRUE; |
|
2582 |
- } else { |
|
2583 |
- cli_dbgmsg("Treating %s as attachment\n", |
|
2584 |
- filename); |
|
2585 |
- free(filename); |
|
2586 |
- } |
|
2587 |
- } else { |
|
2588 |
- if(ctx->options&CL_SCAN_MAILURL) |
|
2589 |
- if(tableFind(subtypeTable, cptr) == HTML) |
|
2590 |
- checkURLs(aMessage, dir); |
|
2591 |
- messageAddArgument(aMessage, "filename=mixedtextportion"); |
|
2592 |
- } |
|
2593 |
- } else { |
|
2594 |
- cli_dbgmsg("Text type %s is not supported\n", dtype); |
|
2595 |
- continue; |
|
2596 |
- } |
|
2597 |
- break; |
|
2598 |
- case MESSAGE: |
|
2599 |
- /* Content-Type: message/rfc822 */ |
|
2600 |
- cli_dbgmsg("Found message inside multipart (encoding type %d)\n", |
|
2601 |
- messageGetEncoding(aMessage)); |
|
2602 |
-#ifndef SCAN_UNENCODED_BOUNCES |
|
2603 |
- switch(messageGetEncoding(aMessage)) { |
|
2604 |
- case NOENCODING: |
|
2605 |
- case EIGHTBIT: |
|
2606 |
- case BINARY: |
|
2607 |
- if(encodingLine(aMessage) == NULL) { |
|
2608 |
- /* |
|
2609 |
- * This means that the message has no attachments |
|
2610 |
- * The test for messageGetEncoding is needed since |
|
2611 |
- * encodingLine won't have been set if the message |
|
2612 |
- * itself has been encoded |
|
2613 |
- */ |
|
2614 |
- cli_dbgmsg("Unencoded multipart/message will not be scanned\n"); |
|
2615 |
- assert(aMessage == messages[i]); |
|
2616 |
- messageDestroy(messages[i]); |
|
2617 |
- messages[i] = NULL; |
|
2618 |
- continue; |
|
2619 |
- } |
|
2620 |
- /* FALLTHROUGH */ |
|
2621 |
- default: |
|
2622 |
- cli_dbgmsg("Encoded multipart/message will be scanned\n"); |
|
2623 |
- } |
|
2624 |
-#endif |
|
2625 |
-#if 0 |
|
2626 |
- messageAddStrAtTop(aMessage, |
|
2627 |
- "Received: by clamd (message/rfc822)"); |
|
2628 |
-#endif |
|
2629 |
-#ifdef SAVE_TO_DISC |
|
2630 |
- /* |
|
2631 |
- * Save this embedded message |
|
2632 |
- * to a temporary file |
|
2633 |
- */ |
|
2634 |
- saveTextPart(aMessage, dir); |
|
2635 |
- assert(aMessage == messages[i]); |
|
2636 |
- messageDestroy(messages[i]); |
|
2637 |
- messages[i] = NULL; |
|
2638 |
-#else |
|
2639 |
- /* |
|
2640 |
- * Scan in memory, faster but |
|
2641 |
- * is open to DoS attacks when |
|
2642 |
- * many nested levels are |
|
2643 |
- * involved. |
|
2644 |
- */ |
|
2645 |
- body = parseEmailHeaders(aMessage, rfc821Table, TRUE); |
|
2646 |
- /* |
|
2647 |
- * We've fininished with the |
|
2648 |
- * original copy of the message, |
|
2649 |
- * so throw that away and |
|
2650 |
- * deal with the encapsulated |
|
2651 |
- * message as a message. |
|
2652 |
- * This can save a lot of memory |
|
2653 |
- */ |
|
2654 |
- assert(aMessage == messages[i]); |
|
2655 |
- messageDestroy(messages[i]); |
|
2656 |
- messages[i] = NULL; |
|
2657 |
- if(body) { |
|
2658 |
- messageSetCTX(body, ctx); |
|
2659 |
- rc = parseEmailBody(body, NULL, dir, rfc821Table, subtypeTable, ctx); |
|
2660 |
- if(messageContainsVirus(body)) |
|
2661 |
- infected = TRUE; |
|
2662 |
- messageDestroy(body); |
|
2663 |
- } |
|
2664 |
- if(infected) { |
|
2665 |
- rc = 3; |
|
2666 |
- break; |
|
2667 |
- } |
|
2668 |
-#endif |
|
2669 |
- continue; |
|
2670 |
- case MULTIPART: |
|
2671 |
- /* |
|
2672 |
- * It's a multi part within a multi part |
|
2673 |
- * Run the message parser on this bit, it won't |
|
2674 |
- * be an attachment |
|
2675 |
- */ |
|
2676 |
- cli_dbgmsg("Found multipart inside multipart\n"); |
|
2677 |
- if(aMessage) { |
|
2678 |
- /* |
|
2679 |
- * The headers were parsed when reading in the |
|
2680 |
- * whole multipart section |
|
2681 |
- */ |
|
2682 |
- rc = parseEmailBody(aMessage, aText, dir, rfc821Table, subtypeTable, ctx); |
|
2683 |
- cli_dbgmsg("Finished recursion\n"); |
|
2684 |
- assert(aMessage == messages[i]); |
|
2685 |
- messageDestroy(messages[i]); |
|
2686 |
- messages[i] = NULL; |
|
2687 |
- } else { |
|
2688 |
- rc = parseEmailBody(NULL, NULL, dir, rfc821Table, subtypeTable, ctx); |
|
2689 |
- if(mainMessage && (mainMessage != messageIn)) |
|
2690 |
- messageDestroy(mainMessage); |
|
2691 |
- mainMessage = NULL; |
|
2692 |
- } |
|
2693 |
- continue; |
|
2694 |
- default: |
|
2695 |
- cli_warnmsg("Only text and application attachments are supported, type = %d\n", |
|
2696 |
- messageGetMimeType(aMessage)); |
|
2697 |
- continue; |
|
2698 |
- } |
|
2699 |
- |
|
2700 |
- if(addToText) { |
|
2701 |
- cli_dbgmsg("Adding to non mime-part\n"); |
|
2702 |
- aText = textAdd(aText, messageGetBody(aMessage)); |
|
2703 |
- } else { |
|
2704 |
- fb = messageToFileblob(aMessage, dir); |
|
2705 |
- |
|
2706 |
- if(fb) { |
|
2707 |
- if(fileblobContainsVirus(fb)) |
|
2708 |
- infected = TRUE; |
|
2709 |
- fileblobDestroy(fb); |
|
2710 |
- } |
|
2711 |
- } |
|
2712 |
- assert(aMessage == messages[i]); |
|
2713 |
- if(messageContainsVirus(aMessage)) |
|
2500 |
+ mainMessage = do_multipart(mainMessage, |
|
2501 |
+ messages, i, &rc, mctx, |
|
2502 |
+ messageIn, &aText); |
|
2503 |
+ if(rc == 3) { |
|
2714 | 2504 |
infected = TRUE; |
2715 |
- messageDestroy(aMessage); |
|
2716 |
- messages[i] = NULL; |
|
2717 |
- if(infected) { |
|
2718 |
- rc = 3; |
|
2719 | 2505 |
break; |
2720 | 2506 |
} |
2721 | 2507 |
} |
2722 | 2508 |
|
2723 |
- /* rc = parseEmailBody(NULL, NULL, dir, rfc821Table, subtypeTable, ctx); */ |
|
2509 |
+ /* rc = parseEmailBody(NULL, NULL, mctx); */ |
|
2724 | 2510 |
break; |
2725 | 2511 |
case SIGNED: |
2726 | 2512 |
case PARALLEL: |
... | ... |
@@ -2736,7 +2535,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2736 | 2736 |
if(htmltextPart == -1) |
2737 | 2737 |
htmltextPart = 0; |
2738 | 2738 |
|
2739 |
- rc = parseEmailBody(messages[htmltextPart], aText, dir, rfc821Table, subtypeTable, ctx); |
|
2739 |
+ rc = parseEmailBody(messages[htmltextPart], aText, mctx); |
|
2740 | 2740 |
break; |
2741 | 2741 |
case ENCRYPTED: |
2742 | 2742 |
rc = 0; |
... | ... |
@@ -2763,9 +2562,9 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2763 | 2763 |
if(aText && (textIn == NULL)) { |
2764 | 2764 |
if((!infected) && (fb = fileblobCreate()) != NULL) { |
2765 | 2765 |
cli_dbgmsg("Save non mime and/or text/plain part\n"); |
2766 |
- fileblobSetFilename(fb, dir, "textpart"); |
|
2766 |
+ fileblobSetFilename(fb, mctx->dir, "textpart"); |
|
2767 | 2767 |
/*fileblobAddData(fb, "Received: by clamd (textpart)\n", 30);*/ |
2768 |
- fileblobSetCTX(fb, ctx); |
|
2768 |
+ fileblobSetCTX(fb, mctx->ctx); |
|
2769 | 2769 |
(void)textToFileblob(aText, fb); |
2770 | 2770 |
|
2771 | 2771 |
fileblobDestroy(fb); |
... | ... |
@@ -2798,11 +2597,11 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2798 | 2798 |
rc = 0; |
2799 | 2799 |
if((strcasecmp(mimeSubtype, "rfc822") == 0) || |
2800 | 2800 |
(strcasecmp(mimeSubtype, "delivery-status") == 0)) { |
2801 |
- message *m = parseEmailHeaders(mainMessage, rfc821Table); |
|
2801 |
+ message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); |
|
2802 | 2802 |
if(m) { |
2803 | 2803 |
cli_dbgmsg("Decode rfc822\n"); |
2804 | 2804 |
|
2805 |
- messageSetCTX(m, ctx); |
|
2805 |
+ messageSetCTX(m, mctx->ctx); |
|
2806 | 2806 |
|
2807 | 2807 |
if(mainMessage && (mainMessage != messageIn)) { |
2808 | 2808 |
messageDestroy(mainMessage); |
... | ... |
@@ -2810,7 +2609,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2810 | 2810 |
} else |
2811 | 2811 |
messageReset(mainMessage); |
2812 | 2812 |
if(messageGetBody(m)) |
2813 |
- rc = parseEmailBody(m, NULL, dir, rfc821Table, subtypeTable, ctx); |
|
2813 |
+ rc = parseEmailBody(m, NULL, mctx); |
|
2814 | 2814 |
|
2815 | 2815 |
messageDestroy(m); |
2816 | 2816 |
} |
... | ... |
@@ -2822,7 +2621,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2822 | 2822 |
} else if(strcasecmp(mimeSubtype, "partial") == 0) { |
2823 | 2823 |
#ifdef PARTIAL_DIR |
2824 | 2824 |
/* RFC1341 message split over many emails */ |
2825 |
- if(rfc1341(mainMessage, dir) >= 0) |
|
2825 |
+ if(rfc1341(mainMessage, mctx->dir) >= 0) |
|
2826 | 2826 |
rc = 1; |
2827 | 2827 |
#else |
2828 | 2828 |
cli_warnmsg("Partial message received from MUA/MTA - message cannot be scanned\n"); |
... | ... |
@@ -2847,7 +2646,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2847 | 2847 |
if((strcasecmp(cptr, "octet-stream") == 0) || |
2848 | 2848 |
(strcasecmp(cptr, "x-msdownload") == 0)) {*/ |
2849 | 2849 |
{ |
2850 |
- fb = messageToFileblob(mainMessage, dir); |
|
2850 |
+ fb = messageToFileblob(mainMessage, mctx->dir); |
|
2851 | 2851 |
|
2852 | 2852 |
if(fb) { |
2853 | 2853 |
cli_dbgmsg("Saving main message as attachment\n"); |
... | ... |
@@ -2951,9 +2750,9 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
2951 | 2951 |
if((fb = fileblobCreate()) == NULL) |
2952 | 2952 |
break; |
2953 | 2953 |
cli_dbgmsg("Save non mime part bounce message\n"); |
2954 |
- fileblobSetFilename(fb, dir, "bounce"); |
|
2954 |
+ fileblobSetFilename(fb, mctx->dir, "bounce"); |
|
2955 | 2955 |
fileblobAddData(fb, (unsigned char *)"Received: by clamd (bounce)\n", 28); |
2956 |
- fileblobSetCTX(fb, ctx); |
|
2956 |
+ fileblobSetCTX(fb, mctx->ctx); |
|
2957 | 2957 |
|
2958 | 2958 |
inheader = TRUE; |
2959 | 2959 |
topofbounce = NULL; |
... | ... |
@@ -3003,7 +2802,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
3003 | 3003 |
*/ |
3004 | 3004 |
cli_dbgmsg("%d multiparts found\n", multiparts); |
3005 | 3005 |
for(i = 0; i < multiparts; i++) { |
3006 |
- fb = messageToFileblob(messages[i], dir); |
|
3006 |
+ fb = messageToFileblob(messages[i], mctx->dir); |
|
3007 | 3007 |
|
3008 | 3008 |
if(fb) { |
3009 | 3009 |
cli_dbgmsg("Saving multipart %d\n", i); |
... | ... |
@@ -3049,7 +2848,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
3049 | 3049 |
if(cli_strtokbuf(txt, 0, ":", cmd) == NULL) |
3050 | 3050 |
continue; |
3051 | 3051 |
|
3052 |
- switch(tableFind(rfc821Table, cmd)) { |
|
3052 |
+ switch(tableFind(mctx->rfc821Table, cmd)) { |
|
3053 | 3053 |
case CONTENT_TRANSFER_ENCODING: |
3054 | 3054 |
if((strstr(txt, "7bit") == NULL) && |
3055 | 3055 |
(strstr(txt, "8bit") == NULL)) |
... | ... |
@@ -3072,7 +2871,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
3072 | 3072 |
} |
3073 | 3073 |
if(t && ((fb = fileblobCreate()) != NULL)) { |
3074 | 3074 |
cli_dbgmsg("Found a bounce message\n"); |
3075 |
- fileblobSetFilename(fb, dir, "bounce"); |
|
3075 |
+ fileblobSetFilename(fb, mctx->dir, "bounce"); |
|
3076 | 3076 |
/*fileblobSetCTX(fb, ctx);*/ |
3077 | 3077 |
if(textToFileblob(start, fb) == NULL) |
3078 | 3078 |
cli_dbgmsg("Nothing new to save in the bounce message\n"); |
... | ... |
@@ -3103,7 +2902,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
3103 | 3103 |
if((fb = fileblobCreate()) != NULL) { |
3104 | 3104 |
cli_dbgmsg("Found a bounce message with no header at '%s'\n", |
3105 | 3105 |
lineGetData(t_line->t_line)); |
3106 |
- fileblobSetFilename(fb, dir, "bounce"); |
|
3106 |
+ fileblobSetFilename(fb, mctx->dir, "bounce"); |
|
3107 | 3107 |
fileblobAddData(fb, |
3108 | 3108 |
(const unsigned char *)"Received: by clamd (bounce)\n", |
3109 | 3109 |
28); |
... | ... |
@@ -3129,7 +2928,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t |
3129 | 3129 |
/* |
3130 | 3130 |
* TODO: May be better to save aText |
3131 | 3131 |
*/ |
3132 |
- saveTextPart(mainMessage, dir); |
|
3132 |
+ saveTextPart(mainMessage, mctx->dir); |
|
3133 | 3133 |
if(mainMessage != messageIn) { |
3134 | 3134 |
messageDestroy(mainMessage); |
3135 | 3135 |
mainMessage = NULL; |
... | ... |
@@ -4509,3 +4308,228 @@ binhexMessage(const char *dir, message *m) |
4509 | 4509 |
|
4510 | 4510 |
return infected; |
4511 | 4511 |
} |
4512 |
+ |
|
4513 |
+/* |
|
4514 |
+ * Handle the ith element of a number of multiparts, e.g. multipart/alternative |
|
4515 |
+ */ |
|
4516 |
+static message * |
|
4517 |
+do_multipart(message *mainMessage, message **messages, int i, int *rc, mbox_ctx *mctx, message *messageIn, text **tptr) |
|
4518 |
+{ |
|
4519 |
+ bool addToText = FALSE; |
|
4520 |
+ const char *dtype; |
|
4521 |
+#ifndef SAVE_TO_DISC |
|
4522 |
+ message *body; |
|
4523 |
+#endif |
|
4524 |
+ message *aMessage = messages[i]; |
|
4525 |
+ |
|
4526 |
+ if(aMessage == NULL) |
|
4527 |
+ return mainMessage; |
|
4528 |
+ |
|
4529 |
+ cli_dbgmsg("Mixed message part %d is of type %d\n", |
|
4530 |
+ i, messageGetMimeType(aMessage)); |
|
4531 |
+ |
|
4532 |
+ switch(messageGetMimeType(aMessage)) { |
|
4533 |
+ case APPLICATION: |
|
4534 |
+ case AUDIO: |
|
4535 |
+ case IMAGE: |
|
4536 |
+ case VIDEO: |
|
4537 |
+ break; |
|
4538 |
+ case NOMIME: |
|
4539 |
+ cli_dbgmsg("No mime headers found in multipart part %d\n", i); |
|
4540 |
+ if(mainMessage) { |
|
4541 |
+ if(binhexBegin(aMessage)) { |
|
4542 |
+ cli_dbgmsg("Found binhex message in multipart/mixed mainMessage\n"); |
|
4543 |
+ |
|
4544 |
+ if(binhexMessage(mctx->dir, mainMessage)) |
|
4545 |
+ *rc = 3; |
|
4546 |
+ } |
|
4547 |
+ if(mainMessage != messageIn) |
|
4548 |
+ messageDestroy(mainMessage); |
|
4549 |
+ mainMessage = NULL; |
|
4550 |
+ } else if(aMessage) { |
|
4551 |
+ if(binhexBegin(aMessage)) { |
|
4552 |
+ cli_dbgmsg("Found binhex message in multipart/mixed non mime part\n"); |
|
4553 |
+ if(binhexMessage(mctx->dir, aMessage)) |
|
4554 |
+ *rc = 3; |
|
4555 |
+ assert(aMessage == messages[i]); |
|
4556 |
+ messageReset(messages[i]); |
|
4557 |
+ } |
|
4558 |
+ } |
|
4559 |
+ addToText = TRUE; |
|
4560 |
+ if(messageGetBody(aMessage) == NULL) |
|
4561 |
+ /* |
|
4562 |
+ * No plain text version |
|
4563 |
+ */ |
|
4564 |
+ cli_dbgmsg("No plain text alternative\n"); |
|
4565 |
+ break; |
|
4566 |
+ case TEXT: |
|
4567 |
+ dtype = messageGetDispositionType(aMessage); |
|
4568 |
+ cli_dbgmsg("Mixed message text part disposition \"%s\"\n", |
|
4569 |
+ dtype); |
|
4570 |
+ if(strcasecmp(dtype, "attachment") == 0) |
|
4571 |
+ break; |
|
4572 |
+ if((*dtype == '\0') || (strcasecmp(dtype, "inline") == 0)) { |
|
4573 |
+ const char *cptr; |
|
4574 |
+ |
|
4575 |
+ if(mainMessage && (mainMessage != messageIn)) |
|
4576 |
+ messageDestroy(mainMessage); |
|
4577 |
+ mainMessage = NULL; |
|
4578 |
+ cptr = messageGetMimeSubtype(aMessage); |
|
4579 |
+ cli_dbgmsg("Mime subtype \"%s\"\n", cptr); |
|
4580 |
+ if((tableFind(mctx->subtypeTable, cptr) == PLAIN) && |
|
4581 |
+ (messageGetEncoding(aMessage) == NOENCODING)) { |
|
4582 |
+ char *filename; |
|
4583 |
+ /* |
|
4584 |
+ * Strictly speaking |
|
4585 |
+ * a text/plain part is |
|
4586 |
+ * not an attachment. We |
|
4587 |
+ * pretend it is so that |
|
4588 |
+ * we can decode and |
|
4589 |
+ * scan it |
|
4590 |
+ */ |
|
4591 |
+ filename = (char *)messageFindArgument(aMessage, "filename"); |
|
4592 |
+ if(filename == NULL) |
|
4593 |
+ filename = (char *)messageFindArgument(aMessage, "name"); |
|
4594 |
+ |
|
4595 |
+ if(filename == NULL) { |
|
4596 |
+ cli_dbgmsg("Adding part to main message\n"); |
|
4597 |
+ addToText = TRUE; |
|
4598 |
+ } else { |
|
4599 |
+ cli_dbgmsg("Treating %s as attachment\n", |
|
4600 |
+ filename); |
|
4601 |
+ free(filename); |
|
4602 |
+ } |
|
4603 |
+ } else { |
|
4604 |
+ if(mctx->ctx->options&CL_SCAN_MAILURL) |
|
4605 |
+ if(tableFind(mctx->subtypeTable, cptr) == HTML) |
|
4606 |
+ checkURLs(aMessage, |
|
4607 |
+ mctx->dir); |
|
4608 |
+ messageAddArgument(aMessage, |
|
4609 |
+ "filename=mixedtextportion"); |
|
4610 |
+ } |
|
4611 |
+ break; |
|
4612 |
+ } |
|
4613 |
+ cli_dbgmsg("Text type %s is not supported\n", dtype); |
|
4614 |
+ return mainMessage; |
|
4615 |
+ case MESSAGE: |
|
4616 |
+ /* Content-Type: message/rfc822 */ |
|
4617 |
+ cli_dbgmsg("Found message inside multipart (encoding type %d)\n", |
|
4618 |
+ messageGetEncoding(aMessage)); |
|
4619 |
+#ifndef SCAN_UNENCODED_BOUNCES |
|
4620 |
+ switch(messageGetEncoding(aMessage)) { |
|
4621 |
+ case NOENCODING: |
|
4622 |
+ case EIGHTBIT: |
|
4623 |
+ case BINARY: |
|
4624 |
+ if(encodingLine(aMessage) == NULL) { |
|
4625 |
+ /* |
|
4626 |
+ * This means that the message |
|
4627 |
+ * has no attachments |
|
4628 |
+ * |
|
4629 |
+ * The test for |
|
4630 |
+ * messageGetEncoding is needed |
|
4631 |
+ * since encodingLine won't have |
|
4632 |
+ * been set if the message |
|
4633 |
+ * itself has been encoded |
|
4634 |
+ */ |
|
4635 |
+ cli_dbgmsg("Unencoded multipart/message will not be scanned\n"); |
|
4636 |
+ assert(aMessage == messages[i]); |
|
4637 |
+ messageDestroy(messages[i]); |
|
4638 |
+ messages[i] = NULL; |
|
4639 |
+ return mainMessage; |
|
4640 |
+ } |
|
4641 |
+ /* FALLTHROUGH */ |
|
4642 |
+ default: |
|
4643 |
+ cli_dbgmsg("Encoded multipart/message will be scanned\n"); |
|
4644 |
+ } |
|
4645 |
+#endif |
|
4646 |
+#if 0 |
|
4647 |
+ messageAddStrAtTop(aMessage, |
|
4648 |
+ "Received: by clamd (message/rfc822)"); |
|
4649 |
+#endif |
|
4650 |
+#ifdef SAVE_TO_DISC |
|
4651 |
+ /* |
|
4652 |
+ * Save this embedded message |
|
4653 |
+ * to a temporary file |
|
4654 |
+ */ |
|
4655 |
+ saveTextPart(aMessage, mctx->dir); |
|
4656 |
+ assert(aMessage == messages[i]); |
|
4657 |
+ messageDestroy(messages[i]); |
|
4658 |
+ messages[i] = NULL; |
|
4659 |
+#else |
|
4660 |
+ /* |
|
4661 |
+ * Scan in memory, faster but |
|
4662 |
+ * is open to DoS attacks when |
|
4663 |
+ * many nested levels are |
|
4664 |
+ * involved. |
|
4665 |
+ */ |
|
4666 |
+ body = parseEmailHeaders(aMessage, mctx->rfc821Table, |
|
4667 |
+ TRUE); |
|
4668 |
+ /* |
|
4669 |
+ * We've fininished with the |
|
4670 |
+ * original copy of the message, |
|
4671 |
+ * so throw that away and |
|
4672 |
+ * deal with the encapsulated |
|
4673 |
+ * message as a message. |
|
4674 |
+ * This can save a lot of memory |
|
4675 |
+ */ |
|
4676 |
+ assert(aMessage == messages[i]); |
|
4677 |
+ messageDestroy(messages[i]); |
|
4678 |
+ messages[i] = NULL; |
|
4679 |
+ if(body) { |
|
4680 |
+ messageSetCTX(body, ctx); |
|
4681 |
+ rc = parseEmailBody(body, NULL, mctx); |
|
4682 |
+ if(messageContainsVirus(body)) |
|
4683 |
+ *rc = 3; |
|
4684 |
+ messageDestroy(body); |
|
4685 |
+ } |
|
4686 |
+#endif |
|
4687 |
+ return mainMessage; |
|
4688 |
+ case MULTIPART: |
|
4689 |
+ /* |
|
4690 |
+ * It's a multi part within a multi part |
|
4691 |
+ * Run the message parser on this bit, it won't |
|
4692 |
+ * be an attachment |
|
4693 |
+ */ |
|
4694 |
+ cli_dbgmsg("Found multipart inside multipart\n"); |
|
4695 |
+ if(aMessage) { |
|
4696 |
+ /* |
|
4697 |
+ * The headers were parsed when reading in the |
|
4698 |
+ * whole multipart section |
|
4699 |
+ */ |
|
4700 |
+ *rc = parseEmailBody(aMessage, *tptr, mctx); |
|
4701 |
+ cli_dbgmsg("Finished recursion\n"); |
|
4702 |
+ assert(aMessage == messages[i]); |
|
4703 |
+ messageDestroy(messages[i]); |
|
4704 |
+ messages[i] = NULL; |
|
4705 |
+ } else { |
|
4706 |
+ *rc = parseEmailBody(NULL, NULL, mctx); |
|
4707 |
+ if(mainMessage && (mainMessage != messageIn)) |
|
4708 |
+ messageDestroy(mainMessage); |
|
4709 |
+ mainMessage = NULL; |
|
4710 |
+ } |
|
4711 |
+ return mainMessage; |
|
4712 |
+ default: |
|
4713 |
+ cli_warnmsg("Only text and application attachments are supported, type = %d\n", |
|
4714 |
+ messageGetMimeType(aMessage)); |
|
4715 |
+ return mainMessage; |
|
4716 |
+ } |
|
4717 |
+ |
|
4718 |
+ if(addToText) { |
|
4719 |
+ cli_dbgmsg("Adding to non mime-part\n"); |
|
4720 |
+ *tptr = textAdd(*tptr, messageGetBody(aMessage)); |
|
4721 |
+ } else { |
|
4722 |
+ fileblob *fb = messageToFileblob(aMessage, mctx->dir); |
|
4723 |
+ |
|
4724 |
+ if(fb) { |
|
4725 |
+ if(fileblobContainsVirus(fb)) |
|
4726 |
+ *rc = 3; |
|
4727 |
+ fileblobDestroy(fb); |
|
4728 |
+ } |
|
4729 |
+ } |
|
4730 |
+ if(messageContainsVirus(aMessage)) |
|
4731 |
+ *rc = 3; |
|
4732 |
+ messageDestroy(aMessage); |
|
4733 |
+ messages[i] = NULL; |
|
4734 |
+ |
|
4735 |
+ return mainMessage; |
|
4736 |
+} |