diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index 014b20dc8..a216d66c1 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c @@ -9,6 +9,10 @@ #include "libbb.h" #include "mail.h" +// limit maximum allowed number of headers to prevent overflows. +// set to 0 to not limit +#define MAX_HEADERS 256 + static int smtp_checkp(const char *fmt, const char *param, int code) { char *answer; @@ -55,7 +59,9 @@ static char *sane_address(char *str) static void rcptto(const char *s) { - smtp_checkp("RCPT TO:<%s>", s, 250); + // N.B. we don't die if recipient is rejected, for the other recipients may be accepted + if (250 != smtp_checkp("RCPT TO:<%s>", s, -1)) + bb_error_msg("Bad recipient: <%s>", s); } int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; @@ -66,6 +72,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) char *s; llist_t *list = NULL; char *domain = sane_address(safe_getdomainname()); + unsigned nheaders = 0; int code; enum { @@ -197,6 +204,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) // and then use the rest of stdin as message body code = 0; // set "analyze headers" mode while ((s = xmalloc_fgetline(G.fp0)) != NULL) { + dump: // put message lines doubling leading dots if (code) { // escape leading dots @@ -215,41 +223,62 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) // To: or Cc: headers add recipients if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Bcc: " + 1, s, 4)) { rcptto(sane_address(s+4)); -// goto addh; - llist_add_to_end(&list, s); + goto addheader; // Bcc: header adds blind copy (hidden) recipient } else if (0 == strncasecmp("Bcc: ", s, 5)) { rcptto(sane_address(s+5)); free(s); // N.B. Bcc: vanishes from headers! // other headers go verbatim - } else if (s[0]) { -// addh: + // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks + } else if (strchr(s, ':')) { + addheader: + if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) + goto bail; llist_add_to_end(&list, s); - // the empty line stops analyzing headers + // a line without ":" (an empty line too, by definition) doesn't look like a valid header + // so stop "analyze headers" mode } else { - free(s); + reenter: // put recipients specified on cmdline while (*argv) { - s = sane_address(*argv); - rcptto(s); - llist_add_to_end(&list, xasprintf("To: %s", s)); + char *t = sane_address(*argv); + rcptto(t); + //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) + // goto bail; + llist_add_to_end(&list, xasprintf("To: %s", t)); argv++; } // enter "put message" mode - smtp_check("DATA", 354); + // N.B. DATA fails iff no recipients were accepted (or even provided) + // in this case just bail out gracefully + if (354 != smtp_check("DATA", -1)) + goto bail; // dump the headers while (list) { printf("%s\r\n", (char *) llist_pop(&list)); } - printf("%s\r\n" + 2); // quirk for format string to be reused // stop analyzing headers code++; + // N.B. !s means: we read nothing, and nothing to be read in the future. + // just dump empty line and break the loop + if (!s) { + puts("\r"); + break; + } + // go dump message body + // N.B. "s" already contains the first non-header line, so pretend we read it from input + goto dump; } } + // odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop + // N.B. after reenter code will be > 0 + if (!code) + goto reenter; // finalize the message smtp_check(".", 250); + bail: // ... and say goodbye smtp_check("QUIT", 221); // cleanup