sendmail: update by Vladimir
function old new delta
sendmail_main 897 939 +42
rcptto 17 40 +23
Signed-off-by: Vladimir Dronnikov <dronnikov@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index 014b20d..a216d66 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 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 @@
char *s;
llist_t *list = NULL;
char *domain = sane_address(safe_getdomainname());
+ unsigned nheaders = 0;
int code;
enum {
@@ -197,6 +204,7 @@
// 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 @@
// 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