Attached is a patch for #345780.  RFC 821 says that an implementation
should at least handle text lines of 1000 characters, but it says also
that when possible, an implementation should avoid having these
limits.  These issues happen with lines over 2k long, but even if it
didn't support them, it should return "500 Line too long." and not
just die or litter extra "\r\n" markers in between long lines.

I can NMU but I'd like to have someone review my patch.  This thing
involves far too many buffers to my liking and I'm sure I've missed an
off by one error in there somewhere.
Index: ssmtp-2.62/ssmtp.c
===================================================================
--- ssmtp-2.62.orig/ssmtp.c     2008-11-04 14:56:56.000000000 +0200
+++ ssmtp-2.62/ssmtp.c  2008-11-04 15:05:27.000000000 +0200
@@ -343,28 +343,26 @@
 /*
 standardise() -- Trim off '\n's and double leading dots
 */
-void standardise(char *str)
+bool_t standardise(char *str, bool_t *linestart)
 {
        size_t sl;
        char *p;
-
-       if((p = strchr(str, '\n'))) {
-               *p = (char)NULL;
-       }
+       bool_t leadingdot = False;
 
        /* Any line beginning with a dot has an additional dot inserted;
-       not just a line consisting solely of a dot. Thus we have to slide
-       the buffer down one */
-       sl = strlen(str);
+       not just a line consisting solely of a dot. Thus we have to move
+       the buffer start up one */
 
-       if(*str == '.') {
-               if((sl + 2) > BUF_SZ) {
-                       die("standardise() -- Buffer overflow");
-               }
-               (void)memmove((str + 1), str, (sl + 1));        /* Copy 
trailing \0 */
+       if(*linestart && *str == '.') {
+               leadingdot = True;
+       }
+       *linestart = False;
 
-               *str = '.';
+       if((p = strchr(str, '\n'))) {
+               *p = (char)NULL;
+               *linestart = True;
        }
+       return(leadingdot);
 }
 
 /*
@@ -1359,12 +1357,12 @@
 */
 ssize_t smtp_write(int fd, char *format, ...)
 {
-       char buf[(BUF_SZ + 1)];
+       char buf[(BUF_SZ + 2)];
        va_list ap;
        ssize_t outbytes = 0;
 
        va_start(ap, format);
-       if(vsnprintf(buf, (BUF_SZ - 2), format, ap) == -1) {
+       if(vsnprintf(buf, (BUF_SZ - 1), format, ap) == -1) {
                die("smtp_write() -- vsnprintf() failed");
        }
        va_end(ap);
@@ -1402,16 +1400,18 @@
 */
 int ssmtp(char *argv[])
 {
-       char buf[(BUF_SZ + 1)], *p, *q;
+       char b[(BUF_SZ + 2)], *buf = b+1, *p, *q;
 #ifdef MD5AUTH
        char challenge[(BUF_SZ + 1)];
 #endif
        struct passwd *pw;
        int i, sock;
        uid_t uid;
-       bool_t minus_v_save;
+       bool_t minus_v_save, leadingdot, linestart = True;
        int timeout = 0;
+       int bufsize = sizeof(b)-1;
 
+       b[0] = '.';
        outbytes = 0;
        ht = &headers;
 
@@ -1494,12 +1494,12 @@
                        }
                        strncpy(challenge, strchr(buf,' ') + 1, 
sizeof(challenge));
 
-                       memset(buf, 0, sizeof(buf));
+                       memset(buf, 0, bufsize);
                        crammd5(challenge, auth_user, auth_pass, buf);
                }
                else {
 #endif
-               memset(buf, 0, sizeof(buf));
+               memset(buf, 0, bufsize);
                to64frombits(buf, auth_user, strlen(auth_user));
                if (use_oldauth) {
                        outbytes += smtp_write(sock, "AUTH LOGIN %s", buf);
@@ -1511,7 +1511,7 @@
                                die("Server didn't like our AUTH LOGIN (%s)", 
buf);
                        }
                        /* we assume server asked us for Username */
-                       memset(buf, 0, sizeof(buf));
+                       memset(buf, 0, bufsize);
                        to64frombits(buf, auth_user, strlen(auth_user));
                        outbytes += smtp_write(sock, buf);
                }
@@ -1520,7 +1520,7 @@
                if(smtp_read(sock, buf) != 3) {
                        die("Server didn't accept AUTH LOGIN (%s)", buf);
                }
-               memset(buf, 0, sizeof(buf));
+               memset(buf, 0, bufsize);
 
                to64frombits(buf, auth_pass, strlen(auth_pass));
 #ifdef MD5AUTH
@@ -1631,7 +1631,7 @@
 
        /* don't hang forever when reading from stdin */
        while(!feof(stdin) && timeout < MEDWAIT) {
-               if (!fgets(buf, sizeof(buf), stdin)) {
+               if (!fgets(buf, bufsize, stdin)) {
                        /* if nothing was received, then no transmission
                         * over smtp should be done */
                        sleep(1);
@@ -1639,12 +1639,25 @@
                        continue;
                }
                /* Trim off \n, double leading .'s */
-               standardise(buf);
-
-               outbytes += smtp_write(sock, "%s", buf);
+               leadingdot = standardise(buf, &linestart);
 
+               if (linestart) {
+                       outbytes += smtp_write(sock, "%s", leadingdot ? b : 
buf);
+               } else {
+                       if (log_level > 0) {
+                               log_event(LOG_INFO, "Sent a very long line in 
chunks");
+                       }
+                       if (leadingdot) {
+                               outbytes += fd_puts(sock, b, sizeof(b));
+                       } else {
+                               outbytes += fd_puts(sock, buf, bufsize);
+                       }
+               }
                (void)alarm((unsigned) MEDWAIT);
        }
+       if(feof(stdin) && !linestart) {
+               smtp_write(sock, "");
+       }
        /* End of body */
 
        if (timeout >= MEDWAIT) {

Reply via email to