diff options
Diffstat (limited to 'src/headers.c')
-rw-r--r-- | src/headers.c | 231 |
1 files changed, 188 insertions, 43 deletions
diff --git a/src/headers.c b/src/headers.c index 857e9c3..6c43dcb 100644 --- a/src/headers.c +++ b/src/headers.c @@ -1,39 +1,17 @@ /* - * Copyright (c) 1983, 1995-1997 Eric P. Allman + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)headers.c 8.115 (Berkeley) 10/22/97"; +static char sccsid[] = "@(#)headers.c 8.136 (Berkeley) 1/26/1999"; #endif /* not lint */ # include <errno.h> @@ -99,6 +77,7 @@ chompheader(line, def, hdrp, e) bool headeronly; STAB *s; struct hdrinfo *hi; + bool nullheader = FALSE; BITMAP mopts; if (tTd(31, 6)) @@ -145,17 +124,22 @@ chompheader(line, def, hdrp, e) return 0; } *fvalue = '\0'; - fvalue = p; /* strip field value on front */ - if (*fvalue == ' ') - fvalue++; + if (*p == ' ') + p++; + fvalue = p; + + /* if the field is null, go ahead and use the default */ + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + nullheader = TRUE; /* security scan: long field names are end-of-header */ if (strlen(fname) > 100) return H_EOH; -#if _FFR_HEADER_RSCHECK /* check to see if it represents a ruleset call */ if (def) { @@ -177,7 +161,6 @@ chompheader(line, def, hdrp, e) return 0; } } -#endif /* see if it is a known type */ s = stab(fname, ST_HEADER, ST_FIND); @@ -206,7 +189,7 @@ chompheader(line, def, hdrp, e) (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e); /* if this means "end of header" quit now */ - if (bitset(H_EOH, hi->hi_flags)) + if (!headeronly && bitset(H_EOH, hi->hi_flags)) return hi->hi_flags; /* @@ -260,6 +243,11 @@ chompheader(line, def, hdrp, e) bitset(H_DEFAULT, h->h_flags) && !bitset(H_FORCE, h->h_flags)) { + if (nullheader) + { + /* user-supplied value was null */ + return 0; + } h->h_value = NULL; if (!cond) { @@ -278,6 +266,10 @@ chompheader(line, def, hdrp, e) bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); *hp = h; h->h_flags = hi->hi_flags; + + /* strip EOH flag if parsing MIME headers */ + if (headeronly) + h->h_flags &= ~H_EOH; if (def) h->h_flags |= H_DEFAULT; if (cond) @@ -463,7 +455,22 @@ eatheader(e, full) /* full name of from person */ p = hvalue("full-name", e->e_header); if (p != NULL) + { + extern bool rfc822_string __P((char *)); + + if (!rfc822_string(p)) + { + extern char *addquotes __P((char *)); + + /* + ** Quote a full name with special characters + ** as a comment so crackaddr() doesn't destroy + ** the name portion of the address. + */ + p = addquotes(p); + } define('x', p, e); + } if (tTd(32, 1)) printf("----- collected header -----\n"); @@ -493,7 +500,7 @@ eatheader(e, full) { if (bitset(H_FROM, h->h_flags)) { - extern char *crackaddr(); + extern char *crackaddr __P((char *)); expand(crackaddr(buf), buf, sizeof buf, e); } @@ -517,15 +524,17 @@ eatheader(e, full) !bitset(H_DEFAULT, h->h_flags) && (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) { +#if 0 int saveflags = e->e_flags; +#endif (void) sendtolist(h->h_value, NULLADDR, &e->e_sendqueue, 0, e); #if 0 /* - ** Change functionality so a fatal error on an - ** address doesn't affect the entire envelope. + ** Change functionality so a fatal error on an + ** address doesn't affect the entire envelope. */ /* delete fatal errors generated by this address */ @@ -1152,6 +1161,7 @@ crackaddr(addr) ** mci -- the connection information. ** h -- the header to put. ** e -- envelope to use. +** flags -- MIME conversion flags. ** ** Returns: ** none. @@ -1168,10 +1178,11 @@ crackaddr(addr) #endif void -putheader(mci, hdr, e) +putheader(mci, hdr, e, flags) register MCI *mci; HDR *hdr; register ENVELOPE *e; + int flags; { register HDR *h; char buf[MAX(MAXLINE,BUFSIZ)]; @@ -1181,11 +1192,19 @@ putheader(mci, hdr, e) printf("--- putheader, mailer = %s ---\n", mci->mci_mailer->m_name); - mci->mci_flags |= MCIF_INHEADER; + /* + ** If we're in MIME mode, we're not really in the header of the + ** message, just the header of one of the parts of the body of + ** the message. Therefore MCIF_INHEADER should not be turned on. + */ + + if (!bitset(MCIF_INMIME, mci->mci_flags)) + mci->mci_flags |= MCIF_INHEADER; + for (h = hdr; h != NULL; h = h->h_link) { register char *p = h->h_value; - extern bool bitintersect(); + extern bool bitintersect __P((BITMAP, BITMAP)); if (tTd(34, 11)) { @@ -1193,9 +1212,69 @@ putheader(mci, hdr, e) xputs(p); } - /* suppress Content-Transfer-Encoding: if we are MIMEing */ +#if _FFR_MAX_MIME_HEADER_LENGTH + /* heuristic shortening of MIME fields to avoid MUA overflows */ + if (MaxMimeFieldLength > 0 && + 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 (MaxMimeHeaderLength > 0 && + wordinclass(h->h_field, + macid("{checkMIMETextHeaders}", NULL))) + { + if (strlen(h->h_value) > MaxMimeHeaderLength) + { + h->h_value[MaxMimeHeaderLength - 1] = '\0'; + 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); + } + } + + if (MaxMimeHeaderLength > 0 && + 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 + ** and we are potentially converting from 8 bit to 7 bit + ** MIME. If converting, add a new CTE header in + ** mime8to7(). + */ if (bitset(H_CTE, h->h_flags) && - bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags)) + bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, + mci->mci_flags) && + !bitset(M87F_NO8TO7, flags)) { if (tTd(34, 11)) printf(" (skipped (content-transfer-encoding))\n"); @@ -1483,7 +1562,7 @@ commaize(h, p, oldstyle, mci, e) else if (e->e_from.q_mailer != NULL && bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags)) { - extern char *udbsender(); + extern char *udbsender __P((char *)); char *q; q = udbsender(name); @@ -1563,3 +1642,69 @@ copyheader(header) 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; +} |