Please see http://www.sendmail.com/sendmail.8.9.1a.html or
ftp://ftp.sendmail.org/pub/sendmail/sendmail.8.9.1a.patch.README for
important information about this patch, including the necessary
steps to install and activate it.

------- headers.c -------
*** -	Wed Dec 31 16:00:00 1969
--- headers.c	Mon Aug 10 18:12:34 1998
***************
*** 1194,1199 ****
--- 1194,1232 ----
  			xputs(p);
  		}
  
+ #if _FFR_MAX_MIME_HEADER_LENGTH
+ 		/* heuristic shortening of MIME fields to avoid MUA overflows */
+ 		if (wordinclass(h->h_field, macid("{checkMIMEFieldHeaders}", NULL)))
+ 		{
+ 			extern bool fix_mime_header __P((char *));
+ 
+ 			if (fix_mime_header(h->h_value))
+ 			{
+ 				sm_syslog(LOG_ALERT, e->e_id,
+ 				  	"Truncated MIME %s header due to field size (possible attack)",
+ 				  	h->h_field);
+ 				if (tTd(34, 11))
+ 				  	printf("  truncated MIME %s header due to field size (possible attack)\n",
+ 					  	h->h_field);
+ 			}
+ 		}
+ 
+ 		if (wordinclass(h->h_field, macid("{checkMIMEHeaders}", NULL)))
+ 		{
+ 			extern bool shorten_rfc822_string __P((char *, int));
+ 
+ 			if (shorten_rfc822_string(h->h_value, MaxMimeHeaderLength))
+ 			{
+ 				sm_syslog(LOG_ALERT, e->e_id,
+ 				  	"Truncated long MIME %s header (possible attack)",
+ 				  	h->h_field);
+ 				if (tTd(34, 11))
+ 				  	printf("  truncated long MIME %s header (possible attack)\n",
+ 					  	h->h_field);
+ 			}
+ 		}
+ #endif
+ 
  		/* suppress Content-Transfer-Encoding: if we are MIMEing */
  		if (bitset(H_CTE, h->h_flags) &&
  		    bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags))
***************
*** 1563,1566 ****
--- 1596,1665 ----
  	*tail = NULL;
  	
  	return ret;
+ }
+ /*
+ **  FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
+ **
+ **	Run through all of the parameters of a MIME header and
+ **	possibly truncate and rebalance the parameter according
+ **	to MaxMimeFieldLength.
+ **
+ **	Parameters:
+ **		string -- the full header
+ **
+ **	Returns:
+ **		TRUE if the header was modified, FALSE otherwise
+ **
+ **	Side Effects:
+ **		string modified in place
+ */
+ 
+ bool
+ fix_mime_header(string)
+ 	char *string;
+ {
+ 	bool modified = FALSE;
+ 	char *begin = string;
+ 	char *end;
+ 	extern char *find_character __P((char *, char));
+ 	extern bool shorten_rfc822_string __P((char *, int));
+ 	
+ 	if (string == NULL || *string == '\0')
+ 		return FALSE;
+ 	
+ 	/* Split on each ';' */
+ 	while ((end = find_character(begin, ';')) != NULL)
+ 	{
+ 		char save = *end;
+ 		char *bp;
+ 		
+ 		*end = '\0';
+ 		
+ 		/* Shorten individual parameter */
+ 		if (shorten_rfc822_string(begin, MaxMimeFieldLength))
+ 			modified = TRUE;
+ 		
+ 		/* Collapse the possibly shortened string with rest */
+ 		bp = begin + strlen(begin);
+ 		if (bp != end)
+ 		{
+ 			char *ep = end;
+ 			
+ 			*end = save;
+ 			end = bp;
+ 			
+ 			/* copy character by character due to overlap */
+ 			while (*ep != '\0')
+ 				*bp++ = *ep++;
+ 			*bp = '\0';
+ 		}
+ 		else
+ 			*end = save;
+ 		if (*end == '\0')
+ 			break;
+ 		
+ 		/* Move past ';' */
+ 		begin = end + 1;
+ 	}
+ 	return modified;
  }

------- main.c -------
*** -	Wed Dec 31 16:00:00 1969
--- main.c	Sun Aug  9 11:45:57 1998
***************
*** 1192,1197 ****
--- 1192,1210 ----
  	setclass('b', "application/octet-stream");
  #endif
  
+ 
+ 	/* MIME headers which have fields to check for overflow */
+ 	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
+ 	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
+ 
+ 	/* MIME headers to check for overflow */
+ 	setclass(macid("{checkMIMEHeaders}", NULL), "content-description");
+ 	setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
+ 	setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
+ 	setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
+ 	setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
+ 	setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
+ 
  	/* operate in queue directory */
  	if (QueueDir == NULL)
  	{

------- conf.h -------

------- sendmail.h -------
*** -	Wed Dec 31 16:00:00 1969
--- sendmail.h	Thu Aug  6 13:48:34 1998
***************
*** 1005,1010 ****
--- 1005,1011 ----
  #define M87F_OUTER		0	/* outer context */
  #define M87F_NO8BIT		0x0001	/* can't have 8-bit in this section */
  #define M87F_DIGEST		0x0002	/* processing multipart/digest */
+ #define M87F_NO8TO7		0x0004	/* don't do 8->7 bit conversions */
  
  
  /*
***************
*** 1282,1287 ****
--- 1283,1291 ----
  EXTERN char	**ExternalEnviron;	/* input environment */
  EXTERN char	*UserEnviron[MAXUSERENVIRON + 1];
  					/* saved user environment */
+ EXTERN int	MaxMimeHeaderLength;	/* maximum MIME header length */
+ EXTERN int	MaxMimeFieldLength;	/* maximum MIME field length */
+ 
  extern int	errno;
  
  /*

------- util.c -------
*** -	Wed Dec 31 16:00:00 1969
--- util.c	Mon Aug 10 18:30:43 1998
***************
*** 170,175 ****
--- 170,351 ----
  		return TRUE;
  }
  /*
+ **  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
+ **
+ **	Arbitratily shorten (in place) an RFC822 string and rebalance
+ **	comments and quotes.
+ **
+ **	Parameters:
+ **		string -- the string to shorten
+ **		length -- the maximum size, 0 if no maximum
+ **
+ **	Returns:
+ **		TRUE if string is changed, FALSE otherwise
+ **
+ **	Side Effects:
+ **		Changes string in place, possibly resulting
+ **		in a shorter string.
+ */
+ 
+ bool
+ shorten_rfc822_string(string, length)
+ 	char *string;
+ 	size_t length;
+ {
+ 	bool backslash = FALSE;
+ 	bool modified = FALSE;
+ 	bool quoted = FALSE;
+ 	size_t slen;
+ 	int parencount = 0;
+ 	char *ptr = string;
+ 	
+ 	/*
+ 	** If have to rebalance an already short enough string,
+ 	** need to do it within allocated space.
+ 	*/
+ 	slen = strlen(string);
+ 	if (length == 0 || slen < length)
+ 		length = slen;
+ 
+ 	while (*ptr != '\0')
+ 	{
+ 		if (backslash)
+ 		{
+ 			backslash = FALSE;
+ 			goto increment;
+ 		}
+ 
+ 		if (*ptr == '\\')
+ 			backslash = TRUE;
+ 		else if (*ptr == '(')
+ 		{
+ 			if (!quoted)
+ 				parencount++;
+ 		}
+ 		else if (*ptr == ')')
+ 		{
+ 			if (--parencount < 0)
+ 				parencount = 0;
+ 		}
+ 		
+ 		/* Inside a comment, quotes don't matter */
+ 		if (parencount <= 0 && *ptr == '"')
+ 			quoted = !quoted;
+ 
+ increment:
+ 		/* Check for sufficient space for next character */
+ 		if (length - (ptr - string) <= ((backslash ? 1 : 0) +
+ 						parencount +
+ 						(quoted ? 1 : 0)))
+ 		{
+ 			/* Not enough, backtrack */
+ 			if (*ptr == '\\')
+ 				backslash = FALSE;
+ 			else if (*ptr == '(' && !quoted)
+ 				parencount--;
+ 			else if (*ptr == '"' && !backslash && parencount == 0)
+ 				quoted = FALSE;
+ 			break;
+ 		}
+ 		ptr++;
+ 	}
+ 
+ 	/* Rebalance */
+ 	while (parencount-- > 0)
+ 	{
+ 		if (*ptr != ')')
+ 		{
+ 			modified = TRUE;
+ 			*ptr = ')';
+ 		}
+ 		ptr++;
+ 	}
+ 	if (quoted)
+ 	{
+ 		if (*ptr != '"')
+ 		{
+ 			modified = TRUE;
+ 			*ptr = '"';
+ 		}
+ 		ptr++;
+ 	}
+ 	if (*ptr != '\0')
+ 	{
+ 		modified = TRUE;
+ 		*ptr = '\0';
+ 	}
+ 	return modified;
+ }
+ /*
+ **  FIND_CHARACTER -- find an unquoted character in an RFC822 string
+ **
+ **	Find an unquoted, non-commented character in an RFC822
+ **	string and return a pointer to its location in the
+ **	string.
+ **
+ **	Parameters:
+ **		string -- the string to search
+ **		character -- the character to find
+ **
+ **	Returns:
+ **		pointer to the character, or
+ **		a pointer to the end of the line if character is not found
+ */
+ 
+ char *
+ find_character(string, character)
+ 	char *string;
+ 	char character;
+ {
+ 	bool backslash = FALSE;
+ 	bool quoted = FALSE;
+ 	int parencount = 0;
+ 		
+ 	while (string != NULL && *string != '\0')
+ 	{
+ 		if (backslash)
+ 		{
+ 			backslash = FALSE;
+ 			if (!quoted && character == '\\' && *string == '\\')
+ 				break;
+ 			string++;
+ 			continue;
+ 		}
+ 		switch (*string)
+ 		{
+ 		  case '\\':
+ 			backslash = TRUE;
+ 			break;
+ 			
+ 		  case '(':
+ 			if (!quoted)
+ 				parencount++;
+ 			break;
+ 			
+ 		  case ')':
+ 			if (--parencount < 0)
+ 				parencount = 0;
+ 			break;
+ 		}
+ 		
+ 		/* Inside a comment, nothing matters */
+ 		if (parencount > 0)
+ 		{
+ 			string++;
+ 			continue;
+ 		}
+ 		
+ 		if (*string == '"')
+ 			quoted = !quoted;
+ 		else if (*string == character && !quoted)
+ 			break;
+ 		string++;
+ 	}
+ 
+ 	/* Return pointer to the character */
+ 	return string;
+ }
+ /*
  **  XALLOC -- Allocate memory and bitch wildly on failure.
  **
  **	THIS IS A CLUDGE.  This should be made to give a proper

------- readcf.c -------
*** -	Wed Dec 31 16:00:00 1969
--- readcf.c	Sun Aug  9 00:10:41 1998
***************
*** 1515,1520 ****
--- 1515,1524 ----
  #define O_TRUSTFILEOWN	0xa7
  	{ "TrustedFileOwner",		O_TRUSTFILEOWN,	FALSE	},
  #endif
+ #if _FFR_MAX_MIME_HEADER_LENGTH
+ #define O_MAXMIMEHDRLEN	0xa8
+ 	{ "MaxMimeHeaderLength",	O_MAXMIMEHDRLEN,	FALSE	},
+ #endif
  
  	{ NULL,				'\0',		FALSE	}
  };
***************
*** 2421,2426 ****
--- 2425,2453 ----
  			TrustedFileUid = 0;
  		}
  #endif
+ 		break;
+ #endif
+ 
+ #if _FFR_MAX_MIME_HEADER_LENGTH
+ 	  case O_MAXMIMEHDRLEN:
+ 		p = strchr(val, '/');
+ 		if (p != NULL)
+ 			*p++ = '\0';
+ 		MaxMimeHeaderLength = atoi(val);
+ 		if (p != NULL && *p != '\0')
+ 			MaxMimeFieldLength = atoi(p);
+ 		else
+ 			MaxMimeFieldLength = MaxMimeHeaderLength / 2;
+ 
+ 		if (MaxMimeHeaderLength < 0)
+ 			MaxMimeHeaderLength = 0;
+ 		else if (MaxMimeHeaderLength < 128)
+ 			printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
+ 
+ 		if (MaxMimeFieldLength < 0)
+ 			MaxMimeFieldLength = 0;
+ 		else if (MaxMimeFieldLength < 40)
+ 			printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
  		break;
  #endif
  

------- deliver.c -------
*** -	Wed Dec 31 16:00:00 1969
--- deliver.c	Fri Aug  7 10:35:44 1998
***************
*** 2878,2883 ****
--- 2878,2884 ----
  	char *separator;
  {
  	char buf[MAXLINE];
+ 	char *boundaries[MAXMIMENESTING + 1];
  
  	/*
  	**  Output the body of the message
***************
*** 2919,2926 ****
  #if MIME8TO7
  	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
  	{
- 		char *boundaries[MAXMIMENESTING + 1];
- 
  		/*
  		**  Do 8 to 7 bit MIME conversion.
  		*/
--- 2920,2925 ----
***************
*** 2948,2953 ****
--- 2947,2959 ----
  		mime7to8(mci, e->e_header, e);
  	}
  # endif
+ 	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
+ 	{
+ 		/* Use mime8to7 to check multipart for MIME header overflows */
+ 		boundaries[0] = NULL;
+ 		mci->mci_flags |= MCIF_INHEADER;
+ 		mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER|M87F_NO8TO7);
+ 	}
  	else
  #endif
  	{
***************
*** 2962,2968 ****
  		size_t eol_len;
  		char peekbuf[10];
  
- 		/* we can pass it through unmodified */
  		if (bitset(MCIF_INHEADER, mci->mci_flags))
  		{
  			putline("", mci);
--- 2968,2973 ----

------- mime.c -------
*** -	Wed Dec 31 16:00:00 1969
--- mime.c	Thu Aug  6 14:13:24 1998
***************
*** 221,227 ****
  	**	Do a recursive descent into the message.
  	*/
  
! 	if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags))
  	{
  		int blen;
  
--- 221,228 ----
  	**	Do a recursive descent into the message.
  	*/
  
! 	if (strcasecmp(type, "multipart") == 0 &&
! 	    (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)))
  	{
  		int blen;
  
***************
*** 375,381 ****
  	*/
  
  	sectionsize = sectionhighbits = 0;
! 	if (!bitset(M87F_NO8BIT, flags))
  	{
  		/* remember where we were */
  		offset = ftell(e->e_dfp);
--- 376,382 ----
  	*/
  
  	sectionsize = sectionhighbits = 0;
! 	if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags))
  	{
  		/* remember where we were */
  		offset = ftell(e->e_dfp);

------- version.c -------
*** -	Thu Jul  2 11:04:58 1998
--- version.c	Mon Aug 10 14:41:30 1998
***************
*** 14,17 ****
  static char sccsid[] = "@(#)version.c	8.9.1.1 (Berkeley) 7/2/98";
  #endif /* not lint */
  
! char	Version[] = "8.9.1";
--- 14,17 ----
  static char sccsid[] = "@(#)version.c	8.9.1.1 (Berkeley) 7/2/98";
  #endif /* not lint */
  
! char	Version[] = "8.9.1a";
