From: Marius Tomaschewski <mt@suse.de>
Date: Tue, 30 Sep 2014 14:08:57 +0200
Subject: [PATCH] Remote PRI DoS vulnerability fix backport (CVE-2014-3683)
References: CVE-2014-3683, CVE-2014-3634, bnc#899756, bnc#897262
Upstream: yes

Adopted v8-stable patch by Rainer Gerhards <rgerhards@adiscon.com>.
---
 grammar/rainerscript.h        |  2 +-
 plugins/imfile/imfile.c       |  4 +--
 plugins/imjournal/imjournal.c |  6 ++---
 plugins/imklog/imklog.c       |  6 ++---
 plugins/imkmsg/imkmsg.c       |  2 +-
 plugins/imsolaris/imsolaris.c |  4 +--
 plugins/imuxsock/imuxsock.c   |  6 ++---
 plugins/omprog/omprog.c       |  1 +
 runtime/msg.c                 | 43 ++++++++++++++++++-----------
 runtime/parser.c              |  8 +++---
 runtime/rsyslog.h             | 63 ++++++++++++++++++++++++++++++++++---------
 runtime/srutils.c             |  1 +
 runtime/syslogd-types.h       |  2 +-
 runtime/typedefs.h            |  4 +++
 tools/rsyslogd.c              |  8 +++---
 15 files changed, 108 insertions(+), 52 deletions(-)

diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h
index 8742c75..453b0f3 100644
--- a/grammar/rainerscript.h
+++ b/grammar/rainerscript.h
@@ -25,7 +25,7 @@
 #include <regex.h>
 #include "typedefs.h"
 
-#define	LOG_NFACILITIES	24	/* current number of syslog facilities */
+#define LOG_NFACILITIES 24+1 /* we copy&paste this as including rsyslog.h gets us in off64_t trouble... :-( */
 #define CNFFUNC_MAX_ARGS 32
 	/**< maximum number of arguments that any function can have (among
 	 *   others, this is used to size data structures).
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index f8272d4..8469d95 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -376,8 +376,8 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
 	MsgSetMSGoffs(pMsg, 0);	/* we do not have a header... */
 	MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
 	MsgSetTAG(pMsg, pInfo->pszTag, pInfo->lenTag);
-	pMsg->iFacility = LOG_FAC(pInfo->iFacility);
-	pMsg->iSeverity = LOG_PRI(pInfo->iSeverity);
+	pMsg->iFacility = pri2fac(pInfo->iFacility);
+	pMsg->iSeverity = pri2sev(pInfo->iSeverity);
 	MsgSetRuleset(pMsg, pInfo->pRuleset);
 	ratelimitAddMsg(pInfo->ratelimiter, &pInfo->multiSub, pMsg);
 finalize_it:
diff --git a/plugins/imjournal/imjournal.c b/plugins/imjournal/imjournal.c
index 51a4aa7..b5b009c 100644
--- a/plugins/imjournal/imjournal.c
+++ b/plugins/imjournal/imjournal.c
@@ -91,8 +91,8 @@ static struct cnfparamblk modpblk =
 	};
 
 #define DFLT_persiststateinterval 10
-#define DFLT_SEVERITY LOG_PRI(LOG_NOTICE)
-#define DFLT_FACILITY LOG_FAC(LOG_USER)
+#define DFLT_SEVERITY pri2fac(LOG_NOTICE)
+#define DFLT_FACILITY pri2sev(LOG_USER)
 
 static int bLegacyCnfModGlobalsPermitted = 1;/* are legacy module-global config parameters permitted? */
 
@@ -125,7 +125,7 @@ static rsRetVal facilityHdlr(uchar **pp, void *pVal)
 			/* noop */;
 		for (c = syslogFacNames; c->c_name; c++) {
 			if (!strncasecmp(p, (char *) c->c_name, len)) {
-				*((int *) pVal) = LOG_FAC(c->c_val);
+				*((int *) pVal) = pri2fac(c->c_val);
 				break;
 			}
 		}
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index 810ac26..4a76340 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -21,7 +21,7 @@
  * To test under Linux:
  * echo test1 > /dev/kmsg
  *
- * Copyright (C) 2008-2012 Adiscon GmbH
+ * Copyright (C) 2008-2014 Adiscon GmbH
  *
  * This file is part of rsyslog.
  *
@@ -247,10 +247,10 @@ rsRetVal Syslog(int priority, uchar *pMsg, struct timeval *tp)
 	/* if we don't get the pri, we use whatever we were supplied */
 
 	/* ignore non-kernel messages if not permitted */
-	if(cs.bPermitNonKernel == 0 && LOG_FAC(priority) != LOG_KERN)
+	if(cs.bPermitNonKernel == 0 && pri2fac(priority) != LOG_KERN)
 		FINALIZE; /* silently ignore */
 
-	iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority), tp);
+	iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", pri2fac(priority), pri2sev(priority), tp);
 
 finalize_it:
 	RETiRet;
diff --git a/plugins/imkmsg/imkmsg.c b/plugins/imkmsg/imkmsg.c
index 2a97f82..8588223 100644
--- a/plugins/imkmsg/imkmsg.c
+++ b/plugins/imkmsg/imkmsg.c
@@ -144,7 +144,7 @@ rsRetVal imkmsgLogIntMsg(int priority, char *fmt, ...)
 rsRetVal Syslog(int priority, uchar *pMsg, struct timeval *tp, struct json_object *json)
 {
 	DEFiRet;
-	iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority), tp, json);
+	iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", pri2fac(priority), pri2sev(priority), tp, json);
 	RETiRet;
 }
 
diff --git a/plugins/imsolaris/imsolaris.c b/plugins/imsolaris/imsolaris.c
index a220e72..36677e7 100644
--- a/plugins/imsolaris/imsolaris.c
+++ b/plugins/imsolaris/imsolaris.c
@@ -209,8 +209,8 @@ readLog(int fd, uchar *pRcv, int iMaxLine)
 		MsgSetInputName(pMsg, pInputName);
 		MsgSetRawMsg(pMsg, (char*)pRcv, strlen((char*)pRcv));
 		MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
-		pMsg->iFacility = LOG_FAC(hdr.pri);
-		pMsg->iSeverity = LOG_PRI(hdr.pri);
+		pMsg->iFacility = pri2fac(hdr.pri);
+		pMsg->iSeverity = pri2sev(hdr.pri);
 		pMsg->msgFlags = NEEDS_PARSING | NO_PRI_IN_RAW;
 		CHKiRet(submitMsg(pMsg));
 	}
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c
index a79fcc5..d99bed8 100644
--- a/plugins/imuxsock/imuxsock.c
+++ b/plugins/imuxsock/imuxsock.c
@@ -6,7 +6,7 @@
  *
  * File begun on 2007-12-20 by RGerhards (extracted from syslogd.c)
  *
- * Copyright 2007-2013 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2014 Rainer Gerhards and Adiscon GmbH.
  *
  * This file is part of rsyslog.
  *
@@ -772,8 +772,8 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct tim
 		++parse;
 		++offs;
 	} 
-	facil = LOG_FAC(pri);
-	sever = LOG_PRI(pri);
+	facil = pri2fac(pri);
+	sever = pri2sev(pri);
 
 	findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */
 
diff --git a/plugins/omprog/omprog.c b/plugins/omprog/omprog.c
index 93dfbb4..2ba0b94 100644
--- a/plugins/omprog/omprog.c
+++ b/plugins/omprog/omprog.c
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "rsyslog.h"
 #include <stdio.h>
+#include <syslog.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/runtime/msg.c b/runtime/msg.c
index 66c3b7b..c994f25 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -125,7 +125,7 @@ static char *years[] = {
 static struct {
 	uchar *pszName;
 	short lenName;
-} syslog_pri_names[192] = {
+} syslog_pri_names[200] = {
 	{ UCHAR_CONSTANT("0"), 3},
 	{ UCHAR_CONSTANT("1"), 3},
 	{ UCHAR_CONSTANT("2"), 3},
@@ -317,22 +317,30 @@ static struct {
 	{ UCHAR_CONSTANT("188"), 5},
 	{ UCHAR_CONSTANT("189"), 5},
 	{ UCHAR_CONSTANT("190"), 5},
-	{ UCHAR_CONSTANT("191"), 5}
+	{ UCHAR_CONSTANT("191"), 5},
+	{ UCHAR_CONSTANT("192"), 5},
+	{ UCHAR_CONSTANT("193"), 5},
+	{ UCHAR_CONSTANT("194"), 5},
+	{ UCHAR_CONSTANT("195"), 5},
+	{ UCHAR_CONSTANT("196"), 5},
+	{ UCHAR_CONSTANT("197"), 5},
+	{ UCHAR_CONSTANT("198"), 5},
+	{ UCHAR_CONSTANT("199"), 5}
 	};
 static char hexdigit[16] =
 	{'0', '1', '2', '3', '4', '5', '6', '7', '8',
 	 '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
 /*syslog facility names (as of RFC5424) */
-static char *syslog_fac_names[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
+static char *syslog_fac_names[LOG_NFACILITIES] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
 			    	      "news", "uucp", "cron", "authpriv", "ftp", "ntp", "audit",
 			    	      "alert", "clock", "local0", "local1", "local2", "local3",
-			    	      "local4", "local5", "local6", "local7" };
+			    	      "local4", "local5", "local6", "local7", "invld" };
 /* length of the facility names string (for optimizatiions) */
-static short len_syslog_fac_names[24] = { 4, 4, 4, 6, 4, 6, 3,
+static short len_syslog_fac_names[LOG_NFACILITIES] = { 4, 4, 4, 6, 4, 6, 3,
 			    	          4, 4, 4, 8, 3, 3, 5,
 			    	          5, 5, 6, 6, 6, 6,
-			    	          6, 6, 6, 6 };
+			    	          6, 6, 6, 6, 5 };
 
 /* table of severity names (in numerical order)*/
 static char *syslog_severity_names[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" };
@@ -342,8 +350,8 @@ static short len_syslog_severity_names[8] = { 5, 5, 4, 3, 7, 6, 4, 5 };
  * and facility values to a numerical string... -- rgerhars, 2009-06-17
  */
 
-static char *syslog_number_names[24] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14",
-					 "15", "16", "17", "18", "19", "20", "21", "22", "23" };
+static char *syslog_number_names[LOG_NFACILITIES] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14",
+					 "15", "16", "17", "18", "19", "20", "21", "22", "23", "24" };
 
 /* global variables */
 #if defined(HAVE_MALLOC_TRIM) && !defined(HAVE_ATOMIC_BUILTINS)
@@ -700,8 +708,8 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis)
 	pM->flowCtlType = 0;
 	pM->bParseSuccess = 0;
 	pM->iRefCount = 1;
-	pM->iSeverity = -1;
-	pM->iFacility = -1;
+	pM->iSeverity = LOG_DEBUG;
+	pM->iFacility = LOG_INVLD;
 	pM->iLenPROGNAME = -1;
 	pM->offAfterPRI = 0;
 	pM->offMSG = -1;
@@ -1544,7 +1552,10 @@ uchar *getMSG(msg_t * const pM)
 /* Get PRI value as integer */
 static int getPRIi(msg_t * const pM)
 {
-	return (pM->iFacility << 3) + (pM->iSeverity);
+	int pri = (pM->iFacility << 3) + (pM->iSeverity);
+	if(pri > 191)
+		pri = LOG_PRI_INVLD;
+	return pri;
 }
 
 
@@ -2626,11 +2637,11 @@ void MsgSetRawMsgWOSize(msg_t * const pMsg, char* pszRawMsg)
 char *textpri(char *pRes, int pri)
 {
 	assert(pRes != NULL);
-	memcpy(pRes, syslog_fac_names[LOG_FAC(pri)], len_syslog_fac_names[LOG_FAC(pri)]);
-	pRes[len_syslog_fac_names[LOG_FAC(pri)]] = '.';
-	memcpy(pRes+len_syslog_fac_names[LOG_FAC(pri)]+1,
-	       syslog_severity_names[LOG_PRI(pri)],
-	       len_syslog_severity_names[LOG_PRI(pri)]+1 /* for \0! */);
+	memcpy(pRes, syslog_fac_names[pri2fac(pri)], len_syslog_fac_names[pri2fac(pri)]);
+	pRes[len_syslog_fac_names[pri2fac(pri)]] = '.';
+	memcpy(pRes+len_syslog_fac_names[pri2fac(pri)]+1,
+	       syslog_severity_names[pri2sev(pri)],
+	       len_syslog_severity_names[pri2sev(pri)]+1 /* for \0! */);
 	return pRes;
 }
 
diff --git a/runtime/parser.c b/runtime/parser.c
index 5607484..1baa502 100644
--- a/runtime/parser.c
+++ b/runtime/parser.c
@@ -620,11 +620,11 @@ ParsePRI(msg_t *pMsg)
 			}
 			if(*msg == '>')
 				++msg;
-			if(pri & ~(LOG_FACMASK|LOG_PRIMASK))
-				pri = DEFUPRI;
+			if(pri > LOG_MAXPRI)
+				pri = LOG_PRI_INVLD;
 		}
-		pMsg->iFacility = LOG_FAC(pri);
-		pMsg->iSeverity = LOG_PRI(pri);
+		pMsg->iFacility = pri2fac(pri);
+		pMsg->iSeverity = pri2sev(pri);
 		MsgSetAfterPRIOffs(pMsg, msg - pMsg->pszRawMsg);
 	}
 	RETiRet;
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 6711bd1..e577fdd 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -77,19 +77,58 @@
  * #                  End Config Settings                      # *
  * ############################################################# */
 
-/* portability: not all platforms have these defines, so we
- * define them here if they are missing. -- rgerhards, 2008-03-04
+/* make sure we uses consistent macros, no matter what the
+ * platform gives us.
  */
-#ifndef LOG_MAKEPRI
-#	define	LOG_MAKEPRI(fac, pri)	(((fac) << 3) | (pri))
-#endif
-#ifndef LOG_PRI
-#	define	LOG_PRI(p)	((p) & LOG_PRIMASK)
-#endif
-#ifndef LOG_FAC
-#	define	LOG_FAC(p)	(((p) & LOG_FACMASK) >> 3)
-#endif
-
+#define LOG_NFACILITIES 24+1 /* plus one for our special "invld" facility! */
+#define LOG_MAXPRI 191	/* highest supported valid PRI value --> RFC3164, RFC5424 */
+#undef LOG_MAKEPRI
+#define LOG_PRI_INVLD	LOG_INVLD|LOG_DEBUG	/* PRI is invalid --> special "invld.=debug" PRI code (rsyslog-specific) */
+
+#define	LOG_EMERG	0	/* system is unusable */
+#define	LOG_ALERT	1	/* action must be taken immediately */
+#define	LOG_CRIT	2	/* critical conditions */
+#define	LOG_ERR		3	/* error conditions */
+#define	LOG_WARNING	4	/* warning conditions */
+#define	LOG_NOTICE	5	/* normal but significant condition */
+#define	LOG_INFO	6	/* informational */
+#define	LOG_DEBUG	7	/* debug-level messages */
+
+#define	LOG_KERN	(0<<3)	/* kernel messages */
+#define	LOG_USER	(1<<3)	/* random user-level messages */
+#define	LOG_MAIL	(2<<3)	/* mail system */
+#define	LOG_DAEMON	(3<<3)	/* system daemons */
+#define	LOG_AUTH	(4<<3)	/* security/authorization messages */
+#define	LOG_SYSLOG	(5<<3)	/* messages generated internally by syslogd */
+#define	LOG_LPR		(6<<3)	/* line printer subsystem */
+#define	LOG_NEWS	(7<<3)	/* network news subsystem */
+#define	LOG_UUCP	(8<<3)	/* UUCP subsystem */
+#define	LOG_CRON	(9<<3)	/* clock daemon */
+#define	LOG_AUTHPRIV	(10<<3)	/* security/authorization messages (private) */
+#define	LOG_FTP		(11<<3)	/* ftp daemon */
+#define	LOG_LOCAL0	(16<<3)	/* reserved for local use */
+#define	LOG_LOCAL1	(17<<3)	/* reserved for local use */
+#define	LOG_LOCAL2	(18<<3)	/* reserved for local use */
+#define	LOG_LOCAL3	(19<<3)	/* reserved for local use */
+#define	LOG_LOCAL4	(20<<3)	/* reserved for local use */
+#define	LOG_LOCAL5	(21<<3)	/* reserved for local use */
+#define	LOG_LOCAL6	(22<<3)	/* reserved for local use */
+#define	LOG_LOCAL7	(23<<3)	/* reserved for local use */
+#define LOG_FAC_INVLD   24
+#define	LOG_INVLD	(LOG_FAC_INVLD<<3)	/* invalid facility/PRI code */
+
+/* we need to use a function to avoid side-effects. This MUST guard
+ * against invalid facility values. rgerhards, 2014-09-16
+ */
+static inline int pri2fac(const int pri)
+{
+	unsigned int fac = pri >> 3;
+	return (fac > 23) ? LOG_FAC_INVLD : fac;
+}
+static inline int pri2sev(const int pri)
+{
+	return pri & 0x07;
+}
 
 /* the rsyslog core provides information about present feature to plugins
  * asking it. Below are feature-test macros which must be used to query
diff --git a/runtime/srutils.c b/runtime/srutils.c
index 6d842ab..d7e71ed 100644
--- a/runtime/srutils.c
+++ b/runtime/srutils.c
@@ -105,6 +105,7 @@ syslogName_t	syslogFacNames[] = {
 	{"local5",       LOG_LOCAL5},
 	{"local6",       LOG_LOCAL6},
 	{"local7",       LOG_LOCAL7},
+	{"invld",        LOG_INVLD},
 	{NULL,           -1},
 };
 
diff --git a/runtime/syslogd-types.h b/runtime/syslogd-types.h
index 4b5d135..2605066 100644
--- a/runtime/syslogd-types.h
+++ b/runtime/syslogd-types.h
@@ -4,7 +4,7 @@
  *
  * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c)
  *
- * Copyright 2007-2012 Adiscon GmbH.
+ * Copyright 2007-2014 Adiscon GmbH.
  *
  * This file is part of the rsyslog runtime library.
  *
diff --git a/runtime/typedefs.h b/runtime/typedefs.h
index f2f5af6..bf92021 100644
--- a/runtime/typedefs.h
+++ b/runtime/typedefs.h
@@ -268,4 +268,8 @@ struct msgPropDescr_s {
 	int nameLen;		/* properties (JSON) */
 };
 
+/* some forward-definitions from the grammar */
+struct nvlst;
+struct cnfobj;
+
 #endif /* multi-include protection */
diff --git a/tools/rsyslogd.c b/tools/rsyslogd.c
index abff2c6..a0fe02c 100644
--- a/tools/rsyslogd.c
+++ b/tools/rsyslogd.c
@@ -491,8 +491,8 @@ logmsgInternalSelf(const int iErr, const int pri, const size_t lenMsg,
 		pszTag[32] = '\0'; /* just to make sure... */
 		MsgSetTAG(pMsg, pszTag, len);
 	}
-	pMsg->iFacility = LOG_FAC(pri);
-	pMsg->iSeverity = LOG_PRI(pri);
+	pMsg->iFacility = pri2fac(pri);
+	pMsg->iSeverity = pri2sev(pri);
 	flags |= INTERNAL_MSG;
 	pMsg->msgFlags  = flags;
 
@@ -540,7 +540,7 @@ logmsgInternal(int iErr, int pri, const uchar *const msg, int flags)
 					   (bufModMsg == NULL) ? (char*)msg : bufModMsg,
 					   flags));
 	} else {
-		stdlog_log(stdlog_hdl, LOG_PRI(pri), "%s",
+		stdlog_log(stdlog_hdl, pri2sev(pri), "%s",
 			   (bufModMsg == NULL) ? (char*)msg : bufModMsg);
 	}
 
@@ -553,7 +553,7 @@ logmsgInternal(int iErr, int pri, const uchar *const msg, int flags)
 	 * supressor statement.
 	 */
 	if(((Debug == DEBUG_FULL || !doFork) && ourConf->globals.bErrMsgToStderr) || iConfigVerify) {
-		if(LOG_PRI(pri) == LOG_ERR)
+		if(pri2sev(pri) == LOG_ERR)
 			fprintf(stderr, "rsyslogd: %s\n", (bufModMsg == NULL) ? (char*)msg : bufModMsg);
 	}
 
-- 
2.1.0

