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 | |
| 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>
| -rw-r--r-- | mailutils/mail.c | 11 | ||||
| -rw-r--r-- | mailutils/sendmail.c | 90 |
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) { |
