aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory Neil Shapiro <gshapiro@FreeBSD.org>2003-03-03 17:23:11 +0000
committerGregory Neil Shapiro <gshapiro@FreeBSD.org>2003-03-03 17:23:11 +0000
commit6d01de1c0e385b6886ee1a0180fe664bb8e5d8a4 (patch)
tree6b42668ddd6f347b206c74b702a8f5a1e6edeaf3
parent745db300174eed2dfc0bd5c67daa482ca9543f19 (diff)
downloadsrc-6d01de1c0e385b6886ee1a0180fe664bb8e5d8a4.tar.gz
src-6d01de1c0e385b6886ee1a0180fe664bb8e5d8a4.zip
FreeBSD-SA-03:04.sendmail: sendmail header parsing buffer overflow
Approved by: security-officer (nectar)
Notes
Notes: svn path=/releng/4.6/; revision=111828
-rw-r--r--UPDATING3
-rw-r--r--contrib/sendmail/src/daemon.c2
-rw-r--r--contrib/sendmail/src/headers.c215
-rw-r--r--contrib/sendmail/src/main.c2
-rw-r--r--contrib/sendmail/src/parseaddr.c2
-rw-r--r--contrib/sendmail/src/sendmail.h2
-rw-r--r--sys/conf/newvers.sh2
7 files changed, 138 insertions, 90 deletions
diff --git a/UPDATING b/UPDATING
index 4a4ff8a152d1..f82d1ede68b2 100644
--- a/UPDATING
+++ b/UPDATING
@@ -17,6 +17,9 @@ minimal number of processes, if possible, for that patch. For those
updates that don't have an advisory, or to be safe, you can do a full
build and install as described in the COMMON ITEMS section.
+20030303: p10 FreeBSD-SA-03:04.sendmail
+ sendmail header parsing buffer overflow, ident parsing bug.
+
20030223: p9 FreeBSD-SA-03:03.syncookies
Make brute force attacks on syncookies much more difficult.
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c
index 8409066dafc3..b2e38ea12bfd 100644
--- a/contrib/sendmail/src/daemon.c
+++ b/contrib/sendmail/src/daemon.c
@@ -3459,7 +3459,7 @@ getauthinfo(fd, may_be_forged)
if (i < 0 || p == &ibuf[0])
goto noident;
- if (*--p == '\n' && *--p == '\r')
+ if (p >= &ibuf[2] && *--p == '\n' && *--p == '\r')
p--;
*++p = '\0';
diff --git a/contrib/sendmail/src/headers.c b/contrib/sendmail/src/headers.c
index efb4f3c821b7..25000c5dce35 100644
--- a/contrib/sendmail/src/headers.c
+++ b/contrib/sendmail/src/headers.c
@@ -674,8 +674,8 @@ eatheader(e, full, log)
if (buf[0] != '\0')
{
if (bitset(H_FROM, h->h_flags))
- expand(crackaddr(buf), buf, sizeof buf,
- e);
+ expand(crackaddr(buf, e),
+ buf, sizeof buf, e);
h->h_value = sm_rpool_strdup_x(e->e_rpool, buf);
h->h_flags &= ~H_DEFAULT;
}
@@ -996,7 +996,11 @@ priencode(p)
** it and replaces it with "$g". The parse is totally ad hoc
** and isn't even guaranteed to leave something syntactically
** identical to what it started with. However, it does leave
-** something semantically identical.
+** something semantically identical if possible, else at least
+** syntactically correct.
+**
+** For example, it changes "Real Name <real@example.com> (Comment)"
+** to "Real Name <$g> (Comment)".
**
** This algorithm has been cleaned up to handle a wider range
** of cases -- notably quoted and backslash escaped strings.
@@ -1005,6 +1009,7 @@ priencode(p)
**
** Parameters:
** addr -- the address to be cracked.
+** e -- the current envelope.
**
** Returns:
** a pointer to the new version.
@@ -1017,28 +1022,50 @@ priencode(p)
** be copied if it is to be reused.
*/
+#define SM_HAVE_ROOM ((bp < buflim) && (buflim <= bufend))
+
+/*
+** Append a character to bp if we have room.
+** If not, punt and return $g.
+*/
+
+#define SM_APPEND_CHAR(c) \
+ do \
+ { \
+ if (SM_HAVE_ROOM) \
+ *bp++ = (c); \
+ else \
+ goto returng; \
+ } while (0)
+
+#if MAXNAME < 10
+ERROR MAXNAME must be at least 10
+#endif /* MAXNAME < 10 */
+
char *
-crackaddr(addr)
+crackaddr(addr, e)
register char *addr;
+ ENVELOPE *e;
{
register char *p;
register char c;
- int cmtlev;
- int realcmtlev;
- int anglelev, realanglelev;
- int copylev;
- int bracklev;
- bool qmode;
- bool realqmode;
- bool skipping;
- bool putgmac = false;
- bool quoteit = false;
- bool gotangle = false;
- bool gotcolon = false;
+ int cmtlev; /* comment level in input string */
+ int realcmtlev; /* comment level in output string */
+ int anglelev; /* angle level in input string */
+ int copylev; /* 0 == in address, >0 copying */
+ int bracklev; /* bracket level for IPv6 addr check */
+ bool addangle; /* put closing angle in output */
+ bool qmode; /* quoting in original string? */
+ bool realqmode; /* quoting in output string? */
+ bool putgmac = false; /* already wrote $g */
+ bool quoteit = false; /* need to quote next character */
+ bool gotangle = false; /* found first '<' */
+ bool gotcolon = false; /* found a ':' */
register char *bp;
char *buflim;
char *bufhead;
char *addrhead;
+ char *bufend;
static char buf[MAXNAME + 1];
if (tTd(33, 1))
@@ -1053,25 +1080,22 @@ crackaddr(addr)
** adjusted later if we find them.
*/
+ buflim = bufend = &buf[sizeof(buf) - 1];
bp = bufhead = buf;
- buflim = &buf[sizeof buf - 7];
p = addrhead = addr;
- copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0;
+ copylev = anglelev = cmtlev = realcmtlev = 0;
bracklev = 0;
- qmode = realqmode = false;
+ qmode = realqmode = addangle = false;
while ((c = *p++) != '\0')
{
/*
- ** If the buffer is overful, go into a special "skipping"
- ** mode that tries to keep legal syntax but doesn't actually
- ** output things.
+ ** Try to keep legal syntax using spare buffer space
+ ** (maintained by buflim).
*/
- skipping = bp >= buflim;
-
- if (copylev > 0 && !skipping)
- *bp++ = c;
+ if (copylev > 0)
+ SM_APPEND_CHAR(c);
/* check for backslash escapes */
if (c == '\\')
@@ -1086,8 +1110,8 @@ crackaddr(addr)
p--;
goto putg;
}
- if (copylev > 0 && !skipping)
- *bp++ = c;
+ if (copylev > 0)
+ SM_APPEND_CHAR(c);
goto putg;
}
@@ -1095,8 +1119,14 @@ crackaddr(addr)
if (c == '"' && cmtlev <= 0)
{
qmode = !qmode;
- if (copylev > 0 && !skipping)
+ if (copylev > 0 && SM_HAVE_ROOM)
+ {
+ if (realqmode)
+ buflim--;
+ else
+ buflim++;
realqmode = !realqmode;
+ }
continue;
}
if (qmode)
@@ -1108,15 +1138,15 @@ crackaddr(addr)
cmtlev++;
/* allow space for closing paren */
- if (!skipping)
+ if (SM_HAVE_ROOM)
{
buflim--;
realcmtlev++;
if (copylev++ <= 0)
{
if (bp != bufhead)
- *bp++ = ' ';
- *bp++ = c;
+ SM_APPEND_CHAR(' ');
+ SM_APPEND_CHAR(c);
}
}
}
@@ -1126,7 +1156,7 @@ crackaddr(addr)
{
cmtlev--;
copylev--;
- if (!skipping)
+ if (SM_HAVE_ROOM)
{
realcmtlev--;
buflim++;
@@ -1137,7 +1167,7 @@ crackaddr(addr)
else if (c == ')')
{
/* syntax error: unmatched ) */
- if (copylev > 0 && !skipping)
+ if (copylev > 0 && SM_HAVE_ROOM)
bp--;
}
@@ -1155,7 +1185,7 @@ crackaddr(addr)
/*
** Check for DECnet phase IV ``::'' (host::user)
- ** or ** DECnet phase V ``:.'' syntaxes. The latter
+ ** or DECnet phase V ``:.'' syntaxes. The latter
** covers ``user@DEC:.tay.myhost'' and
** ``DEC:.tay.myhost::user'' syntaxes (bletch).
*/
@@ -1164,10 +1194,10 @@ crackaddr(addr)
{
if (cmtlev <= 0 && !qmode)
quoteit = true;
- if (copylev > 0 && !skipping)
+ if (copylev > 0)
{
- *bp++ = c;
- *bp++ = *p;
+ SM_APPEND_CHAR(c);
+ SM_APPEND_CHAR(*p);
}
p++;
goto putg;
@@ -1178,41 +1208,43 @@ crackaddr(addr)
bp = bufhead;
if (quoteit)
{
- *bp++ = '"';
+ SM_APPEND_CHAR('"');
/* back up over the ':' and any spaces */
--p;
- while (isascii(*--p) && isspace(*p))
+ while (p > addr &&
+ isascii(*--p) && isspace(*p))
continue;
p++;
}
for (q = addrhead; q < p; )
{
c = *q++;
- if (bp < buflim)
+ if (quoteit && c == '"')
{
- if (quoteit && c == '"')
- *bp++ = '\\';
- *bp++ = c;
+ SM_APPEND_CHAR('\\');
+ SM_APPEND_CHAR(c);
}
+ else
+ SM_APPEND_CHAR(c);
}
if (quoteit)
{
if (bp == &bufhead[1])
bp--;
else
- *bp++ = '"';
+ SM_APPEND_CHAR('"');
while ((c = *p++) != ':')
- {
- if (bp < buflim)
- *bp++ = c;
- }
- *bp++ = c;
+ SM_APPEND_CHAR(c);
+ SM_APPEND_CHAR(c);
}
/* any trailing white space is part of group: */
- while (isascii(*p) && isspace(*p) && bp < buflim)
- *bp++ = *p++;
+ while (isascii(*p) && isspace(*p))
+ {
+ SM_APPEND_CHAR(*p);
+ p++;
+ }
copylev = 0;
putgmac = quoteit = false;
bufhead = bp;
@@ -1221,10 +1253,7 @@ crackaddr(addr)
}
if (c == ';' && copylev <= 0 && !ColonOkInAddr)
- {
- if (bp < buflim)
- *bp++ = c;
- }
+ SM_APPEND_CHAR(c);
/* check for characters that may have to be quoted */
if (strchr(MustQuoteChars, c) != NULL)
@@ -1252,42 +1281,45 @@ crackaddr(addr)
/* oops -- have to change our mind */
anglelev = 1;
- if (!skipping)
- realanglelev = 1;
+ if (SM_HAVE_ROOM)
+ {
+ if (!addangle)
+ buflim--;
+ addangle = true;
+ }
bp = bufhead;
if (quoteit)
{
- *bp++ = '"';
+ SM_APPEND_CHAR('"');
/* back up over the '<' and any spaces */
--p;
- while (isascii(*--p) && isspace(*p))
+ while (p > addr &&
+ isascii(*--p) && isspace(*p))
continue;
p++;
}
for (q = addrhead; q < p; )
{
c = *q++;
- if (bp < buflim)
+ if (quoteit && c == '"')
{
- if (quoteit && c == '"')
- *bp++ = '\\';
- *bp++ = c;
+ SM_APPEND_CHAR('\\');
+ SM_APPEND_CHAR(c);
}
+ else
+ SM_APPEND_CHAR(c);
}
if (quoteit)
{
if (bp == &buf[1])
bp--;
else
- *bp++ = '"';
+ SM_APPEND_CHAR('"');
while ((c = *p++) != '<')
- {
- if (bp < buflim)
- *bp++ = c;
- }
- *bp++ = c;
+ SM_APPEND_CHAR(c);
+ SM_APPEND_CHAR(c);
}
copylev = 0;
putgmac = quoteit = false;
@@ -1299,13 +1331,14 @@ crackaddr(addr)
if (anglelev > 0)
{
anglelev--;
- if (!skipping)
+ if (SM_HAVE_ROOM)
{
- realanglelev--;
- buflim++;
+ if (addangle)
+ buflim++;
+ addangle = false;
}
}
- else if (!skipping)
+ else if (SM_HAVE_ROOM)
{
/* syntax error: unmatched > */
if (copylev > 0)
@@ -1314,7 +1347,7 @@ crackaddr(addr)
continue;
}
if (copylev++ <= 0)
- *bp++ = c;
+ SM_APPEND_CHAR(c);
continue;
}
@@ -1322,30 +1355,42 @@ crackaddr(addr)
putg:
if (copylev <= 0 && !putgmac)
{
- if (bp > bufhead && bp[-1] == ')')
- *bp++ = ' ';
- *bp++ = MACROEXPAND;
- *bp++ = 'g';
+ if (bp > buf && bp[-1] == ')')
+ SM_APPEND_CHAR(' ');
+ SM_APPEND_CHAR(MACROEXPAND);
+ SM_APPEND_CHAR('g');
putgmac = true;
}
}
/* repair any syntactic damage */
- if (realqmode)
+ if (realqmode && bp < bufend)
*bp++ = '"';
- while (realcmtlev-- > 0)
+ while (realcmtlev-- > 0 && bp < bufend)
*bp++ = ')';
- while (realanglelev-- > 0)
+ if (addangle && bp < bufend)
*bp++ = '>';
- *bp++ = '\0';
-
+ *bp = '\0';
+ if (bp < bufend)
+ goto success;
+
+ returng:
+ /* String too long, punt */
+ buf[0] = '<';
+ buf[1] = MACROEXPAND;
+ buf[2]= 'g';
+ buf[3] = '>';
+ buf[4]= '\0';
+ sm_syslog(LOG_ALERT, e->e_id,
+ "Dropped invalid comments from header address");
+
+ success:
if (tTd(33, 1))
{
sm_dprintf("crackaddr=>`");
xputs(buf);
sm_dprintf("'\n");
}
-
return buf;
}
/*
diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c
index 4e8252d1259e..c5be7c723447 100644
--- a/contrib/sendmail/src/main.c
+++ b/contrib/sendmail/src/main.c
@@ -4209,7 +4209,7 @@ testmodeline(line, e)
"Usage: /parse address\n");
return;
}
- q = crackaddr(p);
+ q = crackaddr(p, e);
(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
"Cracked address = ");
xputs(q);
diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c
index aa0e31d1b969..075e79c6048e 100644
--- a/contrib/sendmail/src/parseaddr.c
+++ b/contrib/sendmail/src/parseaddr.c
@@ -2508,7 +2508,7 @@ remotename(name, m, flags, pstat, e)
if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
fancy = "\201g";
else
- fancy = crackaddr(name);
+ fancy = crackaddr(name, e);
/*
** Turn the name into canonical form.
diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h
index 0bbddc438e35..19e640b6595f 100644
--- a/contrib/sendmail/src/sendmail.h
+++ b/contrib/sendmail/src/sendmail.h
@@ -316,7 +316,7 @@ extern ADDRESS NullAddress; /* a null (template) address [main.c] */
/* functions */
extern void cataddr __P((char **, char **, char *, int, int));
-extern char *crackaddr __P((char *));
+extern char *crackaddr __P((char *, ENVELOPE *));
extern bool emptyaddr __P((ADDRESS *));
extern ADDRESS *getctladdr __P((ADDRESS *));
extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *));
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index fc764c34256c..bae35ef71538 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -36,7 +36,7 @@
TYPE="FreeBSD"
REVISION="4.6.2"
-BRANCH="RELEASE-p9"
+BRANCH="RELEASE-p10"
RELEASE="${REVISION}-${BRANCH}"
VERSION="${TYPE} ${RELEASE}"