aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2011-09-18 03:01:49 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2011-09-18 03:01:49 +0200
commit34c469ae0495bb010969eb920c63d31ed4a0e793 (patch)
treef9865b5433f2fc47f2d8006a7de1ef0bb06ebe76
parent0851d125c33d65cc8a0655758f2928960077c20c (diff)
downloadbusybox-w32-34c469ae0495bb010969eb920c63d31ed4a0e793.tar.gz
busybox-w32-34c469ae0495bb010969eb920c63d31ed4a0e793.tar.bz2
busybox-w32-34c469ae0495bb010969eb920c63d31ed4a0e793.zip
sendmail: don't talk until 220 code is seen. Closes 3487
function old new delta sendmail_main 934 939 +5 smtp_checkp 167 165 -2 packed_usage 28634 28621 -13 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--mailutils/mail.c11
-rw-r--r--mailutils/sendmail.c90
2 files changed, 58 insertions, 43 deletions
diff --git a/mailutils/mail.c b/mailutils/mail.c
index 66c79471f..f5260d9db 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -57,10 +57,13 @@ void FAST_FUNC launch_helper(const char **argv)
57 G.helper_pid = xvfork(); 57 G.helper_pid = xvfork();
58 58
59 i = (!G.helper_pid) * 2; // for parent:0, for child:2 59 i = (!G.helper_pid) * 2; // for parent:0, for child:2
60 close(pipes[i + 1]); // 1 or 3 - closing one write end 60 close(pipes[i + 1]); // 1 or 3 - closing one write end
61 close(pipes[2 - i]); // 2 or 0 - closing one read end 61 close(pipes[2 - i]); // 2 or 0 - closing one read end
62 xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end 62 xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end
63 xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - other write end 63 xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end
64 // End result:
65 // parent stdout [3] -> child stdin [2]
66 // child stdout [1] -> parent stdin [0]
64 67
65 if (!G.helper_pid) { 68 if (!G.helper_pid) {
66 // child: try to execute connection helper 69 // child: try to execute connection helper
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index e0aff20fb..dbd491002 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -26,18 +26,18 @@
26//usage: "\n Examples:" 26//usage: "\n Examples:"
27//usage: "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp" 27//usage: "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp"
28//usage: "\n -connect smtp.gmail.com:25' <email.txt" 28//usage: "\n -connect smtp.gmail.com:25' <email.txt"
29//usage: "\n [4<username_and_passwd.txt | -au<username> -ap<password>]" 29//usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]"
30//usage: "\n -H 'exec openssl s_client -quiet -tls1" 30//usage: "\n -H 'exec openssl s_client -quiet -tls1"
31//usage: "\n -connect smtp.gmail.com:465' <email.txt" 31//usage: "\n -connect smtp.gmail.com:465' <email.txt"
32//usage: "\n [4<username_and_passwd.txt | -au<username> -ap<password>]" 32//usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]"
33//usage: "\n -S HOST[:PORT] Server" 33//usage: "\n -S HOST[:PORT] Server"
34//usage: "\n -au<username> Username for AUTH LOGIN" 34//usage: "\n -auUSER Username for AUTH LOGIN"
35//usage: "\n -ap<password> Password for AUTH LOGIN" 35//usage: "\n -apPASS Password for AUTH LOGIN"
36//usage: "\n -am<method> Authentication method. Ignored. LOGIN is implied" 36////usage: "\n -amMETHOD Authentication method. Ignored. LOGIN is implied"
37//usage: "\n" 37//usage: "\n"
38//usage: "\nOther options are silently ignored; -oi -t is implied" 38//usage: "\nOther options are silently ignored; -oi -t is implied"
39//usage: IF_MAKEMIME( 39//usage: IF_MAKEMIME(
40//usage: "\nUse makemime applet to create message with attachments" 40//usage: "\nUse makemime to create emails with attachments"
41//usage: ) 41//usage: )
42 42
43#include "libbb.h" 43#include "libbb.h"
@@ -66,7 +66,7 @@ static int smtp_checkp(const char *fmt, const char *param, int code)
66 // if not equal -> die saying msg 66 // if not equal -> die saying msg
67 while ((answer = xmalloc_fgetline(stdin)) != NULL) { 67 while ((answer = xmalloc_fgetline(stdin)) != NULL) {
68 if (verbose) 68 if (verbose)
69 bb_error_msg("recv:'%.*s' %d", (int)(strchrnul(answer, '\r') - answer), answer, verbose); 69 bb_error_msg("recv:'%.*s'", (int)(strchrnul(answer, '\r') - answer), answer);
70 if (strlen(answer) <= 3 || '-' != answer[3]) 70 if (strlen(answer) <= 3 || '-' != answer[3])
71 break; 71 break;
72 free(answer); 72 free(answer);
@@ -75,10 +75,11 @@ static int smtp_checkp(const char *fmt, const char *param, int code)
75 int n = atoi(answer); 75 int n = atoi(answer);
76 if (timeout) 76 if (timeout)
77 alarm(0); 77 alarm(0);
78 free(msg);
79 free(answer); 78 free(answer);
80 if (-1 == code || n == code) 79 if (-1 == code || n == code) {
80 free(msg);
81 return n; 81 return n;
82 }
82 } 83 }
83 bb_error_msg_and_die("%s failed", msg); 84 bb_error_msg_and_die("%s failed", msg);
84} 85}
@@ -176,11 +177,35 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
176 const char *args[] = { "sh", "-c", opt_connect, NULL }; 177 const char *args[] = { "sh", "-c", opt_connect, NULL };
177 // plug it in 178 // plug it in
178 launch_helper(args); 179 launch_helper(args);
179 // vanilla connection 180 // Now:
181 // our stdout will go to helper's stdin,
182 // helper's stdout will be available on our stdin.
183
184 // Wait for initial server message.
185 // If helper (such as openssl) invokes STARTTLS, the initial 220
186 // is swallowed by helper (and not repeated after TLS is initiated).
187 // We will send NOOP cmd to server and check the response.
188 // We should get 220+250 on plain connection, 250 on STARTTLSed session.
189 //
190 // The problem here is some servers delay initial 220 message,
191 // and consider client to be a spammer if it starts sending cmds
192 // before 220 reached it. The code below is unsafe in this regard:
193 // in non-STARTTLSed case, we potentially send NOOP before 220
194 // is sent by server.
195 // Ideas? (--delay SECS opt? --assume-starttls-helper opt?)
196 code = smtp_check("NOOP", -1);
197 if (code == 220)
198 // we got 220 - this is not STARTTLSed connection,
199 // eat 250 response to our NOOP
200 smtp_check(NULL, 250);
201 else
202 if (code != 250)
203 bb_error_msg_and_die("SMTP init failed");
180 } else { 204 } else {
205 // vanilla connection
181 int fd; 206 int fd;
182 // host[:port] not explicitly specified? -> use $SMTPHOST 207 // host[:port] not explicitly specified? -> use $SMTPHOST
183 // no $SMTPHOST ? -> use localhost 208 // no $SMTPHOST? -> use localhost
184 if (!(opts & OPT_S)) { 209 if (!(opts & OPT_S)) {
185 opt_connect = getenv("SMTPHOST"); 210 opt_connect = getenv("SMTPHOST");
186 if (!opt_connect) 211 if (!opt_connect)
@@ -191,25 +216,14 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
191 // and make ourselves a simple IO filter 216 // and make ourselves a simple IO filter
192 xmove_fd(fd, STDIN_FILENO); 217 xmove_fd(fd, STDIN_FILENO);
193 xdup2(STDIN_FILENO, STDOUT_FILENO); 218 xdup2(STDIN_FILENO, STDOUT_FILENO);
194 }
195 // N.B. from now we know nothing about network :)
196 219
197 // wait for initial server OK 220 // Wait for initial server 220 message
198 // N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure 221 smtp_check(NULL, 220);
199 // so we need to kick the server to see whether we are ok 222 }
200 code = smtp_check("NOOP", -1);
201 // 220 on plain connection, 250 on openssl-helped TLS session
202 if (220 == code)
203 smtp_check(NULL, 250); // reread the code to stay in sync
204 else if (250 != code)
205 bb_error_msg_and_die("INIT failed");
206 223
207 // we should start with modern EHLO 224 // we should start with modern EHLO
208 if (250 != smtp_checkp("EHLO %s", domain, -1)) { 225 if (250 != smtp_checkp("EHLO %s", domain, -1))
209 smtp_checkp("HELO %s", domain, 250); 226 smtp_checkp("HELO %s", domain, 250);
210 }
211 if (ENABLE_FEATURE_CLEAN_UP)
212 free(domain);
213 227
214 // perform authentication 228 // perform authentication
215 if (opts & OPT_a) { 229 if (opts & OPT_a) {
@@ -224,7 +238,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
224 } 238 }
225 239
226 // set sender 240 // set sender
227 // N.B. we have here a very loosely defined algotythm 241 // N.B. we have here a very loosely defined algorythm
228 // since sendmail historically offers no means to specify secrets on cmdline. 242 // since sendmail historically offers no means to specify secrets on cmdline.
229 // 1) server can require no authentication -> 243 // 1) server can require no authentication ->
230 // we must just provide a (possibly fake) reply address. 244 // we must just provide a (possibly fake) reply address.
@@ -241,8 +255,6 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
241 // G.user = xuid2uname(getuid()); 255 // G.user = xuid2uname(getuid());
242 // opt_from = xasprintf("%s@%s", G.user, domain); 256 // opt_from = xasprintf("%s@%s", G.user, domain);
243 //} 257 //}
244 //if (ENABLE_FEATURE_CLEAN_UP)
245 // free(domain);
246 smtp_checkp("MAIL FROM:<%s>", opt_from, 250); 258 smtp_checkp("MAIL FROM:<%s>", opt_from, 250);
247 259
248 // process message 260 // process message
@@ -272,26 +284,26 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
272 if (0 == strncasecmp("To:", s, 3) || 0 == strncasecmp("Bcc:" + 1, s, 3)) { 284 if (0 == strncasecmp("To:", s, 3) || 0 == strncasecmp("Bcc:" + 1, s, 3)) {
273 rcptto(sane_address(s+3)); 285 rcptto(sane_address(s+3));
274 goto addheader; 286 goto addheader;
287 }
275 // Bcc: header adds blind copy (hidden) recipient 288 // Bcc: header adds blind copy (hidden) recipient
276 } else if (0 == strncasecmp("Bcc:", s, 4)) { 289 if (0 == strncasecmp("Bcc:", s, 4)) {
277 rcptto(sane_address(s+4)); 290 rcptto(sane_address(s+4));
278 free(s); 291 free(s);
279 // N.B. Bcc: vanishes from headers! 292 // N.B. Bcc: vanishes from headers!
280 293 } else
281 // other headers go verbatim 294 if (strchr(s, ':') || (list && skip_whitespace(s) != s)) {
282 295 // other headers go verbatim
283 // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines. 296 // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines.
284 // Continuation is denoted by prefixing additional lines with whitespace(s). 297 // Continuation is denoted by prefixing additional lines with whitespace(s).
285 // Thanks (stefan.seyfried at googlemail.com) for pointing this out. 298 // Thanks (stefan.seyfried at googlemail.com) for pointing this out.
286 } else if (strchr(s, ':') || (list && skip_whitespace(s) != s)) {
287 addheader: 299 addheader:
288 // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks 300 // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks
289 if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) 301 if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
290 goto bail; 302 goto bail;
291 llist_add_to_end(&list, s); 303 llist_add_to_end(&list, s);
292 // a line without ":" (an empty line too, by definition) doesn't look like a valid header
293 // so stop "analyze headers" mode
294 } else { 304 } else {
305 // a line without ":" (an empty line too, by definition) doesn't look like a valid header
306 // so stop "analyze headers" mode
295 reenter: 307 reenter:
296 // put recipients specified on cmdline 308 // put recipients specified on cmdline
297 while (*argv) { 309 while (*argv) {