Several days ago, Michal Zalewski posted this message to BUGTRAQ, outlining two
security bugs in the sendmail package. The Sendmail Organization
worked with Michal to track these bugs down. Here are their
results.
From: Gregory Neil Shapiro Subject: Sendmail 8.8.x/8.9.x bugware Date: Wed, 20 Jan 1999 21:56:57 -0800 To: BUGTRAQ@netspace.org Reply-To: Gregory Neil Shapiro -----BEGIN PGP SIGNED MESSAGE----- Michal> 1. Redirection attack Michal> Due to strange address parsing policy [briefly: if address ends Michal> with local hostname, trim it and parse as any other (even if after Michal> this operation address isn't 'local' anymore], specific message Michal> routing (eg. through internal, protected or external networks) can Michal> be forced, giving an occasion to perform anonymous scanning (or Michal> fakemailing). You could call it 'feature' instead of 'bug', but it Michal> seems to be Sendmail-specific ;> Working with Michal, I believe we have concluded that this is not bug in 8.9.2 but rather a policy decision made by some sites running 8.9.2. Specifically, sendmail includes a method for tuning the anti-relaying rulesets via an access database or other features. Sites can also completely turn off the anti-relaying checks and leave themselves wide open to attacks. Sendmail users should be sure to update their sendmail.cf when upgrading their sendmail binary. The sendmail 8.9.2 binary alone will not stop relaying. The rulesets provide the hooks for stopping relaying. Instructions on building a new sendmail.cf are available in cf/README in the sendmail distribution. Michal> 2. 'Headers prescan' DoS Michal> There are possible DoS attacks due to ineffective headers prescan Michal> algorithm. Two or three medium-size (200 kb) mail messages may Michal> render system unusable for quite long period of time (as headers Michal> are parsed at least twice, on message collection and in Michal> queue). Exploit sold separately :-) While studying the denial of service attack found by Michal Zalewski, we found another related area which Michal's patch did not catch. The patch below extends Michal's patch by limiting both the number of header lines as well as the size of each individual header line. The patch will enforce reasonable limits on these lengths and adds a new configuration item. The new option syntax is similar to that of the MaxMimeHeaderLength option: O MaxHeaderLines=####/#### where the first number is the number of lines that sendmail will accept and the second number is the length of each line. The default is 1000/990. This option can be set in a .mc file (after applying the patch) with: define(confMAX_HEADER_LINES, `1000/990') Also note that the count is per line where before concatenating continuation lines. For example, the following will be counted as 2 lines (even though it is a single header): To: gshapiro, eric The patch is for sendmail 8.9.2. To apply the patch, expand the sendmail distribution (available from ftp://ftp.sendmail.org/pub/sendmail/) and follow these steps: cd sendmail-8.9.2 patch -p0 < patchfile Note that the patch program which ships with Solaris has had problems with patches in the past. Please use GNU patch (ftp://ftp.gnu.org/pub/gnu/) if you are on a Solaris system. To enable the new MaxHeaderLines option, you will need to recompile with '-D_FFR_MAX_HEADER_LINES'. To accomplish this, add the following line to sendmail-8.9.2/BuildTools/Site/site.config.m4: APPENDDEF(`confENVDEF', `-D_FFR_MAX_HEADER_LINES=1') Then recompile with './Build -c' in the src directory: cd sendmail-8.9.2/src ./Build -c Finally, install the new binary and restart your sendmail daemon. sendmail 8.9.3, currently under test, will include this patch. Questions can be mailed to sendmail-questions@sendmail.org. Bugs should be mailed to sendmail-bugs@sendmail.org. Since this message has been PGP signed, some of the lines below will be encapsulated by prepending "- " to lines starting with "-" as specified by RFC 934. Modern versions of patch will properly deal with this encapsulation. If you are using a patch that does not handle this, you may need to hand edit the patch after saving it. *** - Wed Dec 31 16:00:00 1969 - --- src/conf.h Mon Jan 18 15:16:14 1999 *************** *** 65,70 **** - --- 65,76 ---- # else # define MAXMACNAMELEN 20 /* max macro name length */ # endif + # ifndef MAXHDRLINES + # define MAXHDRLINES 1000 /* max lines in a message header */ + # endif + # ifndef MAXHDRLINELEN + # define MAXHDRLINELEN SMTPLINELIM /* max length of a header line */ + # endif /********************************************************************** ** Compilation options. *** - Wed Dec 31 16:00:00 1969 - --- src/collect.c Tue Jan 19 20:27:20 1999 *************** *** 53,58 **** - --- 53,59 ---- #define MS_UFROM 0 /* reading Unix from line */ #define MS_HEADER 1 /* reading message header */ #define MS_BODY 2 /* reading message body */ + #define MS_DISCARD 3 /* discarding rest of message */ void collect(fp, smtpmode, hdrp, e) *************** *** 73,78 **** - --- 74,81 ---- volatile int istate; volatile int mstate; u_char *volatile pbp; + int nhdrlines = 0; + int hdrlinelen = 0; u_char peekbuf[8]; char dfname[MAXQFNAME]; char bufbuf[MAXLINE]; *************** *** 194,199 **** - --- 197,203 ---- switch (istate) { case IS_BOL: + hdrlinelen = 0; if (c == '.') { istate = IS_DOT; *************** *** 258,269 **** bufferchar: if (!headeronly) e->e_msgsize++; ! if (mstate == MS_BODY) { /* just put the character out */ if (MaxMessageSize <= 0 || e->e_msgsize <= MaxMessageSize) putc(c, tf); continue; } - --- 262,278 ---- bufferchar: if (!headeronly) e->e_msgsize++; ! switch (mstate) { + case MS_BODY: /* just put the character out */ if (MaxMessageSize <= 0 || e->e_msgsize <= MaxMessageSize) putc(c, tf); + + /* fall through */ + + case MS_DISCARD: continue; } *************** *** 294,300 **** - --- 303,325 ---- #endif } else if (c != ' ') + { *bp++ = c; + if (MaxHeaderLineLength > 0 && + ++hdrlinelen > MaxHeaderLineLength) + { + sm_syslog(LOG_NOTICE, e->e_id, + "header line too long (%d max) from %s during message collect", + MaxHeaderLineLength, + CurHostName != NULL ? CurHostName : "localhost"); + errno = 0; + e->e_flags |= EF_CLRQUEUE; + e->e_status = "5.6.0"; + usrerr("552 Header line too long (%d max)", + MaxHeaderLineLength); + mstate = MS_DISCARD; + } + } if (istate == IS_BOL) break; } *************** *** 327,332 **** - --- 352,373 ---- goto nextstate; } + if (MaxHeaderLines > 0 && + ++nhdrlines > MaxHeaderLines) + { + sm_syslog(LOG_NOTICE, e->e_id, + "too many header lines (%d max) from %s during message collect", + MaxHeaderLines, + CurHostName != NULL ? CurHostName : "localhost"); + errno = 0; + e->e_flags |= EF_CLRQUEUE; + e->e_status = "5.6.0"; + usrerr("552 Too many header lines (%d max)", + MaxHeaderLines); + mstate = MS_DISCARD; + break; + } + /* check for possible continuation line */ do { *************** *** 346,351 **** - --- 387,393 ---- if (*--bp != 'n' || *--bp != 'r') bp++; *bp = ' '; + if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e))) { mstate = MS_BODY; *** - Wed Dec 31 16:00:00 1969 - --- src/sendmail.h Mon Jan 18 16:41:12 1999 *************** *** 1254,1259 **** - --- 1254,1261 ---- EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */ EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */ EXTERN u_long ConnectOnlyTo; /* override connection address (for testing) */ + EXTERN int MaxHeaderLines; /* max lines of headers per message */ + EXTERN int MaxHeaderLineLength; /* max length of a header line */ #if _FFR_DSN_RRT_OPTION EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */ #endif *** - Wed Dec 31 16:00:00 1969 - --- src/readcf.c Mon Jan 18 19:15:10 1999 *************** *** 1523,1528 **** - --- 1523,1532 ---- #define O_CONTROLSOCKET 0xa9 { "ControlSocketName", O_CONTROLSOCKET, FALSE }, #endif + #if _FFR_MAX_HEADER_LINES + #define O_MAXHDRLINES 0xaa + { "MaxHeaderLines", O_MAXHDRLINES, FALSE }, + #endif { NULL, ' ', FALSE } }; *************** *** 2459,2464 **** - --- 2463,2487 ---- if (ControlSocketName != NULL) free(ControlSocketName); ControlSocketName = newstr(val); + break; + #endif + + #if _FFR_MAX_HEADER_LINES + case O_MAXHDRLINES: + p = strchr(val, '/'); + if (p != NULL) + *p++ = ' '; + MaxHeaderLines = atoi(val); + if (p != NULL && *p != ' ') + MaxHeaderLineLength = atoi(p); + + if (MaxHeaderLines > 0 && + MaxHeaderLines < 50) + printf("Warning: MaxHeaderLines: header line limit set lower than 50n"); + + if (MaxHeaderLineLength > 0 && + MaxHeaderLineLength < MAXHDRLINELEN) + printf("Warning: MaxHeaderLines: header line length limit set lower than %dn", MAXHDRLINELEN); break; #endif *** - Wed Dec 31 16:00:00 1969 - --- src/conf.c Mon Jan 18 15:27:30 1999 *************** *** 280,285 **** - --- 280,287 ---- ColonOkInAddr = TRUE; DontLockReadFiles = TRUE; DoubleBounceAddr = "postmaster"; + MaxHeaderLines = MAXHDRLINES; + MaxHeaderLineLength = MAXHDRLINELEN; snprintf(buf, sizeof buf, "%s%sdead.letter", _PATH_VARTMP, _PATH_VARTMP[sizeof _PATH_VARTMP - 2] == '/' ? "" : "/"); *** - Wed Dec 31 16:00:00 1969 - --- cf/m4/proto.m4 Mon Jan 18 19:28:07 1999 *************** *** 474,479 **** - --- 474,483 ---- `# Maximum MIME header length to protect MUAs O MaxMimeHeaderLength=confMAX_MIME_HEADER_LENGTH ') + ifdef(`confMAX_HEADER_LINES', + `# Maximum number of header lines and header line length limit + O MaxHeaderLines=confMAX_HEADER_LINES + ') ########################### # Message precedences # -----BEGIN PGP SIGNATURE----- Version: 2.6.3a Charset: noconv iQCVAwUBNqa7aXxLZ22gDhVjAQHSMwP+KFbI5hxzkeTdV2gptoqHiAf49AT2KqO+ Mu9cIa/OMuYQSnxoBBJZoZqV6e7BR92x9NJOpeVqemzV7iVZNwYrjgzt/5+K5I6V 9J+g43uXk7Es7r6Y50YktLa+yXXD9qGEQf/VgevYTwT+vqo/VYu1cAPFO5kwy7Er MS4FMoX6SuY= =Yj/b -----END PGP SIGNATURE-----