I found an integer overflow in syslogd which can be triggered by
compiling and running:
#include <err.h>
#include <string.h>
#include <sys/types.h>
int main( int argc, char ** argv ) {
const char * msg = "<999999999999> hello";
return sendsyslog( msg, strlen( msg ) );
}
The problematic code is a hand-rolled int parser in printline/printsys:
pri = 0;
while (isdigit((unsigned char)*++p))
pri = 10 * pri + (*p - '0');
if (*p == '>')
++p;
I've attached a patch which converts the loop to strtonum, but doesn't
exactly mirror the behaviour of the old code in funny cases, like
sendsyslog("<1no closing angle bracket").
Mike
Index: syslogd.c
===================================================================
RCS file: /cvs/src/usr.sbin/syslogd/syslogd.c,v
retrieving revision 1.177
diff -u -p -r1.177 syslogd.c
--- syslogd.c 20 Jul 2015 19:49:33 -0000 1.177
+++ syslogd.c 11 Feb 2016 20:31:55 -0000
@@ -1331,18 +1331,23 @@ usage(void)
void
printline(char *hname, char *msg)
{
- int pri;
- char *p, *q, line[MAXLINE + 4 + 1]; /* message, encoding, NUL */
+ int pri, possiblepri;
+ char *p, *q, line[MAXLINE + 4 + 1], *gt; /* message, encoding, NUL */
+ const char *errstr;
/* test for special codes */
pri = DEFUPRI;
p = msg;
if (*p == '<') {
- pri = 0;
- while (isdigit((unsigned char)*++p))
- pri = 10 * pri + (*p - '0');
- if (*p == '>')
- ++p;
+ gt = strchr(p, '>');
+ if (gt) {
+ *gt = '\0';
+ possiblepri = strtonum(p + 1, 0, INT_MAX, &errstr);
+ if (!errstr)
+ pri = possiblepri;
+ *gt = '>';
+ p = gt + 1;
+ }
}
if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
pri = DEFUPRI;
@@ -1372,8 +1377,9 @@ printline(char *hname, char *msg)
void
printsys(char *msg)
{
- int c, pri, flags;
- char *lp, *p, *q, line[MAXLINE + 1];
+ int c, pri, flags, possiblepri;
+ char *lp, *p, *q, line[MAXLINE + 1], *gt;
+ const char *errstr;
(void)snprintf(line, sizeof line, "%s: ", _PATH_UNIX);
lp = line + strlen(line);
@@ -1381,11 +1387,15 @@ printsys(char *msg)
flags = SYNC_FILE | ADDDATE; /* fsync file after write */
pri = DEFSPRI;
if (*p == '<') {
- pri = 0;
- while (isdigit((unsigned char)*++p))
- pri = 10 * pri + (*p - '0');
- if (*p == '>')
- ++p;
+ gt = strchr(p, '>');
+ if (gt) {
+ *gt = '\0';
+ possiblepri = strtonum(p + 1, 0, INT_MAX,
&errstr);
+ if (!errstr)
+ pri = possiblepri;
+ *gt = '>';
+ p = gt + 1;
+ }
} else {
/* kernel printf's come out on console */
flags |= IGN_CONS;