Browse code

First draft of code to handle RFC1341

git-svn-id: file:///var/lib/svn/clamav-devel/trunk/clamav-devel@972 77e5149b-7576-45b1-b177-96237e5ba77b

Nigel Horne authored on 2004/10/06 00:48:47
Showing 3 changed files
... ...
@@ -1,3 +1,13 @@
1
+Tue Oct  5 16:45:28 BST 2004 (njh)
2
+----------------------------------
3
+  * libclamav:		First draft of RFC1341 code. It is not enabled by
4
+				default. To enable it, locate PARTIAL_DIR
5
+				in libclamav/mbox.c, uncomment the line and set
6
+				that to some nice place.
7
+			Note that it is up to YOU to ensure that the PARTIAL_DIR
8
+				directory is secure and to trim out old files
9
+				in there from time to time.
10
+
1 11
 Tue Oct  5 11:59:09 BST 2004 (njh)
2 12
 ----------------------------------
3 13
   * libclamav/message.c:	Faster base64 decoding
... ...
@@ -60,7 +70,7 @@ Fri Oct  1 02:28:08 CEST 2004 (tk)
60 60
 ----------------------------------
61 61
   * clamd: add new directive IdleTimeout (patch by Andrey J. Melnikoff (TEMHOTA)
62 62
 	   <temnota*kmv.ru>)
63
-  * clamscan/others.c: preserve Mac OS X resource forks in filecopy() 
63
+  * clamscan/others.c: preserve Mac OS X resource forks in filecopy()
64 64
 		       (thanks to Remi Mommsen <remigius.mommsen*cern.ch>)
65 65
   * cosmetic fixes (thanks to Damian Menscher <menscherr*uiuc.edu>)
66 66
 
... ...
@@ -17,6 +17,9 @@
17 17
  *
18 18
  * Change History:
19 19
  * $Log: mbox.c,v $
20
+ * Revision 1.148  2004/10/05 15:41:53  nigelhorne
21
+ * First draft of code to handle RFC1341
22
+ *
20 23
  * Revision 1.147  2004/10/04 12:18:09  nigelhorne
21 24
  * Better warning message about PGP attachments not being scanned
22 25
  *
... ...
@@ -429,7 +432,7 @@
429 429
  * Compilable under SCO; removed duplicate code with message.c
430 430
  *
431 431
  */
432
-static	char	const	rcsid[] = "$Id: mbox.c,v 1.147 2004/10/04 12:18:09 nigelhorne Exp $";
432
+static	char	const	rcsid[] = "$Id: mbox.c,v 1.148 2004/10/05 15:41:53 nigelhorne Exp $";
433 433
 
434 434
 #if HAVE_CONFIG_H
435 435
 #include "clamav-config.h"
... ...
@@ -459,6 +462,7 @@ static	char	const	rcsid[] = "$Id: mbox.c,v 1.147 2004/10/04 12:18:09 nigelhorne
459 459
 #include <sys/types.h>
460 460
 #include <sys/param.h>
461 461
 #include <clamav.h>
462
+#include <dirent.h>
462 463
 
463 464
 #ifdef	CL_THREAD_SAFE
464 465
 #include <pthread.h>
... ...
@@ -538,6 +542,13 @@ typedef enum	{ FALSE = 0, TRUE = 1 } bool;
538 538
 #undef	WITH_CURL
539 539
 #endif
540 540
 
541
+/*
542
+ * Define this to handle RFC1341 messages.
543
+ *	This is experimental code so it is up to YOU to (1) ensure it's secure
544
+ * (2) peridically trim the directory of old files
545
+ */
546
+/*#define	PARTIAL_DIR	"/tmp/partial"	/* FIXME: should be config based on TMPDIR */
547
+
541 548
 static	message	*parseEmailHeaders(const message *m, const table_t *rfc821Table);
542 549
 static	int	parseEmailHeader(message *m, const char *line, const table_t *rfc821Table);
543 550
 static	int	parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t *rfc821Table, const table_t *subtypeTable, unsigned int options);
... ...
@@ -551,6 +562,9 @@ static	int	parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Tab
551 551
 static	void	saveTextPart(message *m, const char *dir);
552 552
 static	char	*rfc2047(const char *in);
553 553
 static	char	*rfc822comments(const char *in);
554
+#ifdef	PARTIAL_DIR
555
+static	int	rfc1341(message *m, const char *dir);
556
+#endif
554 557
 
555 558
 static	void	checkURLs(message *m, const char *dir);
556 559
 #ifdef	WITH_CURL
... ...
@@ -876,9 +890,6 @@ parseEmailHeaders(const message *m, const table_t *rfc821)
876 876
 
877 877
 	for(t = messageGetBody(m); t; t = t->t_next) {
878 878
 		const char *buffer;
879
-#ifdef CL_THREAD_SAFE
880
-		char *strptr;
881
-#endif
882 879
 
883 880
 		if(t->t_line)
884 881
 			buffer = lineGetData(t->t_line);
... ...
@@ -903,6 +914,9 @@ parseEmailHeaders(const message *m, const table_t *rfc821)
903 903
 				 */
904 904
 				const char *ptr;
905 905
 				char copy[LINE_LENGTH + 1];
906
+#ifdef CL_THREAD_SAFE
907
+				char *strptr;
908
+#endif
906 909
 
907 910
 				assert(strlen(buffer) < sizeof(copy));
908 911
 				strcpy(copy, buffer);
... ...
@@ -1787,6 +1801,7 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t
1787 1787
 					cli_warnmsg("MIME type 'message' cannot be decoded\n");
1788 1788
 					break;
1789 1789
 			}
1790
+			rc = 0;
1790 1791
 			if((strcasecmp(mimeSubtype, "rfc822") == 0) ||
1791 1792
 			   (strcasecmp(mimeSubtype, "delivery-status") == 0)) {
1792 1793
 				message *m = parseEmailHeaders(mainMessage, rfc821Table);
... ...
@@ -1807,26 +1822,27 @@ parseEmailBody(message *messageIn, text *textIn, const char *dir, const table_t
1807 1807
 			} else if(strcasecmp(mimeSubtype, "disposition-notification") == 0)
1808 1808
 				/* RFC 2298 - handle like a normal email */
1809 1809
 				break;
1810
-			else if(strcasecmp(mimeSubtype, "partial") == 0)
1811
-				/*
1812
-				 * How can we be expected to reassemble a message when in
1813
-				 * practice we'll be called once for each email so we won't
1814
-				 * have all the parts for reassembly. It is up to the
1815
-				 * calling program to reassemble full messages and pass
1816
-				 * them on to us for scanning
1817
-				 */
1810
+			else if(strcasecmp(mimeSubtype, "partial") == 0) {
1811
+#ifdef	PARTIAL_DIR
1812
+				/* RFC1341 message split over many emails */
1813
+				if(rfc1341(mainMessage, dir) >= 0)
1814
+					rc = 1;
1815
+#else
1818 1816
 				cli_warnmsg("Partial message received from MUA/MTA - message cannot be scanned\n");
1819
-			else if(strcasecmp(mimeSubtype, "external-body") == 0)
1817
+				rc = 0;
1818
+#endif
1819
+			} else if(strcasecmp(mimeSubtype, "external-body") == 0)
1820 1820
 				/* TODO */
1821 1821
 				cli_warnmsg("Attempt to send Content-type message/external-body trapped");
1822 1822
 			else
1823 1823
 				cli_warnmsg("Unsupported message format `%s' - please report to bugs@clamav.net\n", mimeSubtype);
1824 1824
 
1825
+
1825 1826
 			if(mainMessage && (mainMessage != messageIn))
1826 1827
 				messageDestroy(mainMessage);
1827 1828
 			if(messages)
1828 1829
 				free(messages);
1829
-			return 0;
1830
+			return rc;
1830 1831
 
1831 1832
 		case APPLICATION:
1832 1833
 			cptr = messageGetMimeSubtype(mainMessage);
... ...
@@ -2584,6 +2600,157 @@ rfc2047(const char *in)
2584 2584
 	return out;
2585 2585
 }
2586 2586
 
2587
+#ifdef	PARTIAL_DIR
2588
+/*
2589
+ * Handle partial messages
2590
+ */
2591
+static int
2592
+rfc1341(message *m, const char *dir)
2593
+{
2594
+	fileblob *fb;
2595
+	char *arg;
2596
+	char *id;
2597
+	char *number;
2598
+	char *total;
2599
+	char *oldfilename;
2600
+
2601
+	if((mkdir(PARTIAL_DIR, 0700) < 0) && (errno != EEXIST)) {
2602
+		/*perror(PARTIAL_DIR);*/
2603
+		return -1;
2604
+	}
2605
+
2606
+	id = (char *)messageFindArgument(m, "id");
2607
+	if(id == NULL)
2608
+		return -1;
2609
+	number = (char *)messageFindArgument(m, "number");
2610
+	if(number == NULL) {
2611
+		free(id);
2612
+		return -1;
2613
+	}
2614
+
2615
+	oldfilename = (char *)messageFindArgument(m, "filename");
2616
+	if(oldfilename == NULL)
2617
+		oldfilename = (char *)messageFindArgument(m, "name");
2618
+
2619
+	arg = cli_malloc(10 + strlen(id) + strlen(number));
2620
+	sprintf(arg, "filename=%s%s", id, number);
2621
+	messageAddArgument(m, arg);
2622
+	free(arg);
2623
+
2624
+	if(oldfilename) {
2625
+		cli_warnmsg("Must reset to %s\n", oldfilename);
2626
+		free(oldfilename);
2627
+	}
2628
+
2629
+	if((fb = messageToFileblob(m, PARTIAL_DIR)) == NULL) {
2630
+		free(id);
2631
+		free(number);
2632
+		return -1;
2633
+	}
2634
+
2635
+	fileblobDestroy(fb);
2636
+
2637
+	total = (char *)messageFindArgument(m, "total");
2638
+	cli_dbgmsg("rfc1341: %s, %s of %s\n", id, number, (total) ? total : "?");
2639
+	if(total) {
2640
+		int n = atoi(number);
2641
+		int t = atoi(total);
2642
+		DIR *dd = NULL;
2643
+		struct dirent *dent;
2644
+#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
2645
+		struct dirent result;
2646
+#endif
2647
+
2648
+		/*
2649
+		 * If it's the last one - reassemble it
2650
+		 */
2651
+		if((n == t) && ((dd = opendir(PARTIAL_DIR)) != NULL)) {
2652
+			FILE *fout;
2653
+			char outname[NAME_MAX + 1];
2654
+
2655
+			snprintf(outname, sizeof(outname) - 1, "%s/%s", dir, id);
2656
+
2657
+			cli_dbgmsg("outname: %s\n", outname);
2658
+
2659
+			fout = fopen(outname, "wb");
2660
+			if(fout == NULL) {
2661
+				/*perror(outname);*/
2662
+				free(id);
2663
+				free(total);
2664
+				free(number);
2665
+				closedir(dd);
2666
+				return -1;
2667
+			}
2668
+
2669
+			for(n = 1; n <= t; n++) {
2670
+				char filename[NAME_MAX + 1];
2671
+
2672
+				snprintf(filename, sizeof(filename), "%s%d", id, n);
2673
+#ifdef HAVE_READDIR_R_3
2674
+				while((readdir_r(dd, &result, &dent) == 0) && dent) {
2675
+#elif defined(HAVE_READDIR_R_2)
2676
+				while((dent = (struct dirent *)readdir_r(dd, &result))) {
2677
+#else
2678
+				while((dent = readdir(dd))) {
2679
+#endif
2680
+					char fullname[NAME_MAX + 1];
2681
+					FILE *fin;
2682
+					char buffer[BUFSIZ];
2683
+					int nblanks;
2684
+
2685
+					if(dent->d_ino == 0)
2686
+						continue;
2687
+
2688
+					if(strncmp(filename, dent->d_name, strlen(filename)) != 0)
2689
+						continue;
2690
+
2691
+					sprintf(fullname, "%s/%s", PARTIAL_DIR, dent->d_name);
2692
+					fin = fopen(fullname, "rb");
2693
+					if(fin == NULL) {
2694
+						/*perror(fullname);*/
2695
+						fclose(fout);
2696
+						unlink(outname);
2697
+						free(id);
2698
+						free(total);
2699
+						free(number);
2700
+						closedir(dd);
2701
+
2702
+						return -1;
2703
+					}
2704
+					nblanks = 0;
2705
+					while(fgets(buffer, sizeof(buffer), fin) != NULL)
2706
+						/*
2707
+						 * Ensure that trailing newlines
2708
+						 * aren't copied
2709
+						 */
2710
+						if(buffer[0] == '\n') {
2711
+							nblanks++;
2712
+						} else {
2713
+							if(nblanks)
2714
+								do
2715
+									putc('\n', fout);
2716
+								while(--nblanks > 0);
2717
+							fputs(buffer, fout);
2718
+						}
2719
+					fclose(fin);
2720
+					/* FIXME: don't unlink if leave temps */
2721
+					unlink(fullname);
2722
+					break;
2723
+				}
2724
+				rewinddir(dd);
2725
+			}
2726
+			closedir(dd);
2727
+			fclose(fout);
2728
+		}
2729
+		free(number);
2730
+	}
2731
+	free(id);
2732
+	free(total);
2733
+
2734
+	return 0;
2735
+}
2736
+#endif
2737
+
2587 2738
 #ifdef	FOLLOWURLS
2588 2739
 static void
2589 2740
 checkURLs(message *m, const char *dir)
... ...
@@ -2783,7 +2950,7 @@ getURL(struct arg *arg)
2783 2783
 	fp = fopen(fout, "w");
2784 2784
 
2785 2785
 	if(fp == NULL) {
2786
-		perror(fout);
2786
+		/*perror(fout);*/
2787 2787
 		free(fout);
2788 2788
 		curl_easy_cleanup(curl);
2789 2789
 		return NULL;
... ...
@@ -17,6 +17,9 @@
17 17
  *
18 18
  * Change History:
19 19
  * $Log: message.c,v $
20
+ * Revision 1.96  2004/10/05 15:46:18  nigelhorne
21
+ * First draft of code to handle RFC1341
22
+ *
20 23
  * Revision 1.95  2004/10/05 10:58:00  nigelhorne
21 24
  * Table driven base64 decoding
22 25
  *
... ...
@@ -282,7 +285,7 @@
282 282
  * uuencodebegin() no longer static
283 283
  *
284 284
  */
285
-static	char	const	rcsid[] = "$Id: message.c,v 1.95 2004/10/05 10:58:00 nigelhorne Exp $";
285
+static	char	const	rcsid[] = "$Id: message.c,v 1.96 2004/10/05 15:46:18 nigelhorne Exp $";
286 286
 
287 287
 #if HAVE_CONFIG_H
288 288
 #include "clamav-config.h"
... ...
@@ -2333,6 +2336,9 @@ usefulArg(const char *arg)
2333 2333
 	   (strncasecmp(arg, "filename", 8) != 0) &&
2334 2334
 	   (strncasecmp(arg, "boundary", 8) != 0) &&
2335 2335
 	   (strncasecmp(arg, "protocol", 8) != 0) &&
2336
+	   (strncasecmp(arg, "id", 2) != 0) &&
2337
+	   (strncasecmp(arg, "number", 6) != 0) &&
2338
+	   (strncasecmp(arg, "total", 5) != 0) &&
2336 2339
 	   (strncasecmp(arg, "type", 4) != 0)) {
2337 2340
 		cli_dbgmsg("Discarding unwanted argument '%s'\n", arg);
2338 2341
 		return 0;