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) {