From 37ba1df6e37b2d020001f390ccb2462ae04fc9c1 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 24 Sep 2009 09:48:38 +0200 Subject: bugfix: random data could be appended to message, possibly causing segfaults --- runtime/datetime.c | 114 ++++++++++++++++++++++++++++++++++++----------------- runtime/datetime.h | 4 +- runtime/parser.c | 11 ++++-- runtime/rsyslog.h | 1 + 4 files changed, 88 insertions(+), 42 deletions(-) (limited to 'runtime') diff --git a/runtime/datetime.c b/runtime/datetime.c index 40ab4e9c..fc56b27c 100644 --- a/runtime/datetime.c +++ b/runtime/datetime.c @@ -140,6 +140,7 @@ static void getCurrTime(struct syslogTime *t, time_t *ttSeconds) * DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!! */ + /** * Parse a 32 bit integer number from a string. * @@ -147,17 +148,21 @@ static void getCurrTime(struct syslogTime *t, time_t *ttSeconds) * must be positioned at the first digit. Will be updated * so that on return it points to the first character AFTER * the integer parsed. + * \param pLenStr pointer to string length, decremented on exit by + * characters processed + * Note that if an empty string (len < 1) is passed in, + * the method always returns zero. * \retval The number parsed. */ - -static int srSLMGParseInt32(uchar** ppsz) +static int srSLMGParseInt32(uchar** ppsz, int *pLenStr) { register int i; i = 0; - while(isdigit((int) **ppsz)) { + while(*pLenStr > 0 && isdigit((int) **ppsz)) { i = i * 10 + **ppsz - '0'; ++(*ppsz); + --(*pLenStr); } return i; @@ -169,9 +174,13 @@ static int srSLMGParseInt32(uchar** ppsz) * updates the parse pointer position. The pTime parameter * is guranteed to be updated only if a new valid timestamp * could be obtained (restriction added 2008-09-16 by rgerhards). + * This method now also checks the maximum string length it is passed. + * If a *valid* timestamp is found, the string length is decremented + * by the number of characters processed. If it is not a valid timestamp, + * the length is kept unmodified. -- rgerhards, 2009-09-23 */ static rsRetVal -ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) +ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr) { uchar *pszTS = *ppszTS; /* variables to temporarily hold time information while we parse */ @@ -186,6 +195,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) char OffsetMode; /* UTC offset + or - */ char OffsetHour; /* UTC offset in hours */ int OffsetMinute; /* UTC offset in minutes */ + int lenStr; /* end variables to temporarily hold time information while we parse */ DEFiRet; @@ -193,48 +203,50 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) assert(ppszTS != NULL); assert(pszTS != NULL); - year = srSLMGParseInt32(&pszTS); + lenStr = *pLenStr; + year = srSLMGParseInt32(&pszTS, &lenStr); /* We take the liberty to accept slightly malformed timestamps e.g. in * the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course, * with the current state of affairs, we would never run into this code * here because at postion 11, there is no "T" in such cases ;) */ - if(*pszTS++ != '-') + if(lenStr == 0 || *pszTS++ != '-') ABORT_FINALIZE(RS_RET_INVLD_TIME); - month = srSLMGParseInt32(&pszTS); + month = srSLMGParseInt32(&pszTS, &lenStr); if(month < 1 || month > 12) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != '-') + if(lenStr == 0 || *pszTS++ != '-') ABORT_FINALIZE(RS_RET_INVLD_TIME); - day = srSLMGParseInt32(&pszTS); + day = srSLMGParseInt32(&pszTS, &lenStr); if(day < 1 || day > 31) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != 'T') + if(lenStr == 0 || *pszTS++ != 'T') ABORT_FINALIZE(RS_RET_INVLD_TIME); - hour = srSLMGParseInt32(&pszTS); + hour = srSLMGParseInt32(&pszTS, &lenStr); if(hour < 0 || hour > 23) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != ':') + if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); - minute = srSLMGParseInt32(&pszTS); + minute = srSLMGParseInt32(&pszTS, &lenStr); if(minute < 0 || minute > 59) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != ':') + if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); - second = srSLMGParseInt32(&pszTS); + second = srSLMGParseInt32(&pszTS, &lenStr); if(second < 0 || second > 60) ABORT_FINALIZE(RS_RET_INVLD_TIME); /* Now let's see if we have secfrac */ - if(*pszTS == '.') { + if(lenStr > 0 && *pszTS == '.') { + --lenStr; uchar *pszStart = ++pszTS; - secfrac = srSLMGParseInt32(&pszTS); + secfrac = srSLMGParseInt32(&pszTS, &lenStr); secfracPrecision = (int) (pszTS - pszStart); } else { secfracPrecision = 0; @@ -242,23 +254,27 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) } /* check the timezone */ - if(*pszTS == 'Z') - { + if(lenStr == 0) + ABORT_FINALIZE(RS_RET_INVLD_TIME); + + if(*pszTS == 'Z') { + --lenStr; pszTS++; /* eat Z */ OffsetMode = 'Z'; OffsetHour = 0; OffsetMinute = 0; } else if((*pszTS == '+') || (*pszTS == '-')) { OffsetMode = *pszTS; + --lenStr; pszTS++; - OffsetHour = srSLMGParseInt32(&pszTS); + OffsetHour = srSLMGParseInt32(&pszTS, &lenStr); if(OffsetHour < 0 || OffsetHour > 23) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != ':') + if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); - OffsetMinute = srSLMGParseInt32(&pszTS); + OffsetMinute = srSLMGParseInt32(&pszTS, &lenStr); if(OffsetMinute < 0 || OffsetMinute > 59) ABORT_FINALIZE(RS_RET_INVLD_TIME); } else { @@ -267,10 +283,12 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) } /* OK, we actually have a 3339 timestamp, so let's indicated this */ - if(*pszTS == ' ') + if(lenStr > 0 && *pszTS == ' ') { + --lenStr; ++pszTS; - else + } else { ABORT_FINALIZE(RS_RET_INVLD_TIME); + } /* we had success, so update parse pointer and caller-provided timestamp */ *ppszTS = pszTS; @@ -286,6 +304,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) pTime->OffsetMode = OffsetMode; pTime->OffsetHour = OffsetHour; pTime->OffsetMinute = OffsetMinute; + *pLenStr = lenStr; finalize_it: RETiRet; @@ -304,9 +323,13 @@ finalize_it: * permits us to use a pre-aquired timestamp and thus avoids to do * a (costly) time() call. Thanks to David Lang for insisting on * time() call reduction ;). + * This method now also checks the maximum string length it is passed. + * If a *valid* timestamp is found, the string length is decremented + * by the number of characters processed. If it is not a valid timestamp, + * the length is kept unmodified. -- rgerhards, 2009-09-23 */ static rsRetVal -ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) +ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr) { /* variables to temporarily hold time information while we parse */ int month; @@ -316,6 +339,7 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) int minute; int second; /* end variables to temporarily hold time information while we parse */ + int lenStr; uchar *pszTS; DEFiRet; @@ -323,6 +347,8 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) pszTS = *ppszTS; assert(pszTS != NULL); assert(pTime != NULL); + assert(pLenStr != NULL); + lenStr = *pLenStr; /* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec), * we may see the following character sequences occur: @@ -341,6 +367,9 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) * june, when it first manifested. This also lead to invalid parsing of the rest * of the message, as the time stamp was not detected to be correct. - rgerhards */ + if(lenStr < 3) + ABORT_FINALIZE(RS_RET_INVLD_TIME); + switch(*pszTS++) { case 'J': @@ -455,26 +484,31 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) ABORT_FINALIZE(RS_RET_INVLD_TIME); } + lenStr -= 3; + /* done month */ - if(*pszTS++ != ' ') + if(lenStr == 0 || *pszTS++ != ' ') ABORT_FINALIZE(RS_RET_INVLD_TIME); /* we accept a slightly malformed timestamp when receiving. This is * we accept one-digit days */ - if(*pszTS == ' ') + if(*pszTS == ' ') { + --lenStr; ++pszTS; + } - day = srSLMGParseInt32(&pszTS); + day = srSLMGParseInt32(&pszTS, &lenStr); if(day < 1 || day > 31) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != ' ') + if(lenStr == 0 || *pszTS++ != ' ') ABORT_FINALIZE(RS_RET_INVLD_TIME); + --lenStr; /* time part */ - hour = srSLMGParseInt32(&pszTS); + hour = srSLMGParseInt32(&pszTS, &lenStr); if(hour > 1970 && hour < 2100) { /* if so, we assume this actually is a year. This is a format found * e.g. in Cisco devices. @@ -484,23 +518,26 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) year = hour; /* re-query the hour, this time it must be valid */ - if(*pszTS++ != ' ') + if(lenStr == 0 || *pszTS++ != ' ') ABORT_FINALIZE(RS_RET_INVLD_TIME); - hour = srSLMGParseInt32(&pszTS); + --lenStr; + hour = srSLMGParseInt32(&pszTS, &lenStr); } if(hour < 0 || hour > 23) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != ':') + if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); - minute = srSLMGParseInt32(&pszTS); + --lenStr; + minute = srSLMGParseInt32(&pszTS, &lenStr); if(minute < 0 || minute > 59) ABORT_FINALIZE(RS_RET_INVLD_TIME); - if(*pszTS++ != ':') + if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); - second = srSLMGParseInt32(&pszTS); + --lenStr; + second = srSLMGParseInt32(&pszTS, &lenStr); if(second < 0 || second > 60) ABORT_FINALIZE(RS_RET_INVLD_TIME); @@ -508,8 +545,10 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) * invalid format, it occurs frequently enough (e.g. with Cisco devices) * to permit it as a valid case. -- rgerhards, 2008-09-12 */ - if(*pszTS++ == ':') + if(lenStr == 0 || *pszTS++ == ':') { ++pszTS; /* just skip past it */ + --lenStr; + } /* we had success, so update parse pointer and caller-provided timestamp * fields we do not have are not updated in the caller's timestamp. This @@ -526,6 +565,7 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS) pTime->second = second; pTime->secfracPrecision = 0; pTime->secfrac = 0; + *pLenStr = lenStr; finalize_it: RETiRet; diff --git a/runtime/datetime.h b/runtime/datetime.h index efb0a0af..6377a4a4 100644 --- a/runtime/datetime.h +++ b/runtime/datetime.h @@ -36,8 +36,8 @@ typedef struct datetime_s { /* interfaces */ BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */ void (*getCurrTime)(struct syslogTime *t, time_t *ttSeconds); - rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS); - rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS); + rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS, int *); + rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS, int *); int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst); int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst); int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); diff --git a/runtime/parser.c b/runtime/parser.c index 0b45bfd5..079bcf5e 100644 --- a/runtime/parser.c +++ b/runtime/parser.c @@ -274,6 +274,7 @@ finalize_it: RETiRet; } + /* Parse a received message. The object's rawmsg property is taken and * parsed according to the relevant standards. This can later be * extended to support configured parsers. @@ -284,6 +285,7 @@ rsRetVal parseMsg(msg_t *pMsg) DEFiRet; uchar *msg; int pri; + int lenMsg; int iPriText; CHKiRet(sanitizeMessage(pMsg)); @@ -292,8 +294,11 @@ rsRetVal parseMsg(msg_t *pMsg) DBGPRINTF("msg parser: flags %x, from '%s', msg '%s'\n", pMsg->msgFlags, pMsg->pszRcvFrom, pMsg->pszRawMsg); /* pull PRI */ - pri = DEFUPRI; + lenMsg = pMsg->iLenRawMsg; + if(lenMsg == 0) + ABORT_FINALIZE(RS_RET_EMPTY_MSG); msg = pMsg->pszRawMsg; + pri = DEFUPRI; iPriText = 0; if(*msg == '<') { /* while we process the PRI, we also fill the PRI textual representation @@ -301,7 +306,7 @@ rsRetVal parseMsg(msg_t *pMsg) * but it offers us performance... */ pri = 0; - while(isdigit((int) *++msg)) { + while(--lenMsg > 0 && isdigit((int) *++msg)) { pMsg->bufPRI[iPriText++ % 4] = *msg; /* mod 4 to guard against malformed messages! */ pri = 10 * pri + (*msg - '0'); } @@ -342,7 +347,7 @@ rsRetVal parseMsg(msg_t *pMsg) /* finalize message object */ pMsg->msgFlags &= ~NEEDS_PARSING; /* this message is now parsed */ - MsgPrepareEnqueue(pMsg); /* "historical" name - preparese for multi-threading */ + MsgPrepareEnqueue(pMsg); /* "historical" name - prepare for multi-threading */ finalize_it: RETiRet; diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 32177a9f..13b54731 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -280,6 +280,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_ACTION_FAILED = -2123, /**< action failed and is now suspended (consider this permanent for the time being) */ RS_RET_NONFATAL_CONFIG_ERR = -2124, /**< non-fatal error during config processing */ RS_RET_FILENAME_INVALID = -2140, /**< filename invalid, not found, no access, ... */ + RS_RET_EMPTY_MSG = -2141, /**< provided (raw) MSG is empty */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ -- cgit From 536415cf3dba053b0d2294b7f1dc8e34328e795f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 29 Sep 2009 14:22:11 +0200 Subject: bugfix: invalid handling of zero-sized messages could lead to mis-addressing and potential memory corruption/segfault --- runtime/parser.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/parser.c b/runtime/parser.c index 079bcf5e..7eff0801 100644 --- a/runtime/parser.c +++ b/runtime/parser.c @@ -167,6 +167,7 @@ sanitizeMessage(msg_t *pMsg) size_t iMaxLine; assert(pMsg != NULL); + assert(pMsg->iLenRawMsg > 0); # ifdef USE_NETZIP CHKiRet(uncompressMessage(pMsg)); @@ -288,6 +289,9 @@ rsRetVal parseMsg(msg_t *pMsg) int lenMsg; int iPriText; + if(pMsg->iLenRawMsg == 0) + ABORT_FINALIZE(RS_RET_EMPTY_MSG); + CHKiRet(sanitizeMessage(pMsg)); /* we needed to sanitize first, because we otherwise do not have a C-string we can print... */ @@ -295,8 +299,6 @@ rsRetVal parseMsg(msg_t *pMsg) /* pull PRI */ lenMsg = pMsg->iLenRawMsg; - if(lenMsg == 0) - ABORT_FINALIZE(RS_RET_EMPTY_MSG); msg = pMsg->pszRawMsg; pri = DEFUPRI; iPriText = 0; -- cgit