diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-09-18 03:01:49 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-09-18 03:01:49 +0200 |
commit | 34c469ae0495bb010969eb920c63d31ed4a0e793 (patch) | |
tree | f9865b5433f2fc47f2d8006a7de1ef0bb06ebe76 /mailutils/sendmail.c | |
parent | 0851d125c33d65cc8a0655758f2928960077c20c (diff) | |
download | busybox-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>
Diffstat (limited to 'mailutils/sendmail.c')
-rw-r--r-- | mailutils/sendmail.c | 90 |
1 files changed, 51 insertions, 39 deletions
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) { |