1Bug-Debian: http://bugs.debian.org/584162
2Reported-By: Christoph Biedl <debian.axhn@manchmal.in-ulm.de>
3Forwarded: not-needed
4Reviewed-By: Anibal Monsalve Salazar <anibal@debian.org>
5Last-Update: 2014-08-15
6
7From: "Daniel Richard G." <skunk@iSKUNK.ORG>
8Subject: Re: ssmtp: Partial loss of message body, sending message to wrong recipicients
9Date: Thu, 19 Jun 2014 14:44:30 -0400
10
11Attached is a patch against the original 2.64 source that should address
12this bug, and hopefully not break anything. An overview of my changes:
13
14* Added code to standarise() to drop the trailing '\r' if the line
15  originally ended with "\r\n".
16
17* Added a check to header_parse() that effectively converts an "\r\n" in
18  the input into '\n'.
19
20* Added a conditional so that header_parse() doesn't pass the empty
21  string to header_save()---a behavior I observed in testing, at the end
22  of a header block with "\r\n" line endings.
23
24* Simplified the last if(in_header) conditional in header_parse(),
25  because it erroneously assumes that if in_header == True, then c could
26  have some value other than EOF. (See the condition on the previous
27  "while" loop, and the lack of any other way to exit said loop.)
28
29  header_parse() will now properly grab a header if fed a message
30  without a body (i.e. no "\n\n" ending the header block), although this
31  code will still drop a header if there is no newline at the end.
32
33Christoph, thank you for your excellent analysis, and the test cases. I
34made use of them, and with my changes sSMTP appears to do the right
35thing.
36
37Debian patch from: https://sources.debian.net/patches/ssmtp/2.64-8/
38
39Upstream-Status: Backport [debian]
40
41Signed-off-by: Andre McCurdy <armccurdy@gmail.com>
42
43Index: ssmtp-2.64/ssmtp.c
44===================================================================
45--- ssmtp-2.64.orig/ssmtp.c
46+++ ssmtp-2.64/ssmtp.c
47@@ -375,6 +375,12 @@ bool_t standardise(char *str, bool_t *li
48 	if((p = strchr(str, '\n'))) {
49 		*p = (char)NULL;
50 		*linestart = True;
51+
52+		/* If the line ended in "\r\n", then drop the '\r' too */
53+		sl = strlen(str);
54+		if(sl >= 1 && str[sl - 1] == '\r') {
55+			str[sl - 1] = (char)NULL;
56+		}
57 	}
58 	return(leadingdot);
59 }
60@@ -768,6 +774,14 @@ void header_parse(FILE *stream)
61 		}
62 		len++;
63
64+		if(l == '\r' && c == '\n') {
65+			/* Properly handle input that already has "\r\n"
66+			   line endings; see https://bugs.debian.org/584162 */
67+			l = (len >= 2 ? *(q - 2) : '\n');
68+			q--;
69+			len--;
70+		}
71+
72 		if(l == '\n') {
73 			switch(c) {
74 				case ' ':
75@@ -790,7 +804,9 @@ void header_parse(FILE *stream)
76 						if((q = strrchr(p, '\n'))) {
77 							*q = (char)NULL;
78 						}
79-						header_save(p);
80+						if(len > 0) {
81+							header_save(p);
82+						}
83
84 						q = p;
85 						len = 0;
86@@ -800,35 +816,12 @@ void header_parse(FILE *stream)
87
88 		l = c;
89 	}
90-	if(in_header) {
91-		if(l == '\n') {
92-			switch(c) {
93-				case ' ':
94-				case '\t':
95-						/* Must insert '\r' before '\n's embedded in header
96-						   fields otherwise qmail won't accept our mail
97-						   because a bare '\n' violates some RFC */
98-
99-						*(q - 1) = '\r';	/* Replace previous \n with \r */
100-						*q++ = '\n';		/* Insert \n */
101-						len++;
102-
103-						break;
104-
105-				case '\n':
106-						in_header = False;
107-
108-				default:
109-						*q = (char)NULL;
110-						if((q = strrchr(p, '\n'))) {
111-							*q = (char)NULL;
112-						}
113-						header_save(p);
114-
115-						q = p;
116-						len = 0;
117-			}
118+	if(in_header && l == '\n') {
119+		/* Got EOF while reading the header */
120+		if((q = strrchr(p, '\n'))) {
121+			*q = (char)NULL;
122 		}
123+		header_save(p);
124 	}
125 	(void)free(p);
126 }
127