sendmail: update by Vladimir
authorVladimir Dronnikov <dronnikov@gmail.com>
Thu, 15 Oct 2009 21:50:48 +0000 (23:50 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 15 Oct 2009 21:50:48 +0000 (23:50 +0200)
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>
mailutils/sendmail.c

index 014b20d..a216d66 100644 (file)
@@ -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