diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | doc/imtcp.html | 4 | ||||
-rw-r--r-- | doc/rsconf1_dircreatemode.html | 10 | ||||
-rw-r--r-- | plugins/imtcp/imtcp.c | 5 | ||||
-rw-r--r-- | plugins/imudp/imudp.c | 3 | ||||
-rw-r--r-- | plugins/omstdout/omstdout.c | 2 | ||||
-rw-r--r-- | runtime/datetime.c | 119 | ||||
-rw-r--r-- | runtime/datetime.h | 4 | ||||
-rw-r--r-- | runtime/parser.c | 13 | ||||
-rw-r--r-- | runtime/rsyslog.h | 8 | ||||
-rw-r--r-- | tcpsrv.c | 19 | ||||
-rw-r--r-- | tcpsrv.h | 5 | ||||
-rw-r--r-- | tools/syslogd.c | 100 |
13 files changed, 238 insertions, 81 deletions
@@ -1,9 +1,15 @@ --------------------------------------------------------------------------- Version 5.1.6 [v5-beta] (rgerhards), 2009-09-?? +- feature imports from v4.5.6 - bugfixes imported from 4.5.4: * bugfix: potential segfault in stream writer on destruction * bugfix: potential race in object loader (obj.c) during use/release * bugfixes: potential problems in out file zip writer +- included some important fixes from 4.4.2: + * bugfix: invalid handling of zero-sized messages + * bugfix: zero-sized UDP messages are no longer processed + * bugfix: random data could be appended to message + * bugfix: reverse lookup reduction logic in imudp do DNS queries too often --------------------------------------------------------------------------- Version 5.1.5 [v5-beta] (rgerhards), 2009-09-11 - added new config option $ActionWriteAllMarkMessages @@ -116,6 +122,17 @@ increase. - increased ompgsql performance by adapting to new transactional output module interface --------------------------------------------------------------------------- +Version 4.5.6 [v4-beta] (rgerhards), 2009-09-?? +- included some important fixes from v4-stable: + * bugfix: invalid handling of zero-sized messages + * bugfix: zero-sized UDP messages are no longer processed + * bugfix: random data could be appended to message + * bugfix: reverse lookup reduction logic in imudp do DNS queries too often +--------------------------------------------------------------------------- +Version 4.5.5 [v4-beta] (rgerhards), 2009-09-?? +- added $InputTCPServerNotifyOnConnectionClose config directive + see doc for details +--------------------------------------------------------------------------- Version 4.5.4 [v4-beta] (rgerhards), 2009-09-29 - bugfix: potential segfault in stream writer on destruction Most severely affected omfile. The problem was that some buffers were @@ -245,6 +262,15 @@ Version 4.5.0 [DEVEL] (rgerhards), 2009-07-02 an abort --------------------------------------------------------------------------- Version 4.4.2 [v4-stable] (rgerhards), 2009-09-?? +- bugfix: invalid handling of zero-sized messages, could lead to mis- + addressing and potential memory corruption/segfault +- bugfix: zero-sized UDP messages are no longer processed + until now, they were forwarded to processing, but this makes no sense + Also, it looks like the system seems to provide a zero return code + on a UDP recvfrom() from time to time for some internal reasons. These + "receives" are now silently ignored. +- bugfix: random data could be appended to message, possibly causing + segfaults - bugfix: reverse lookup reduction logic in imudp do DNS queries too often A comparison was done between the current and the former source address. However, this was done on the full sockaddr_storage structure and not @@ -1637,6 +1663,7 @@ Version 2.0.8 V2-STABLE (rgerhards), 2008-??-?? connection broke, but not if there was a problem with statement execution. The most probable case for such a case would be invalid sql inside the template, and this is now much easier to diagnose. +- doc bugfix: default for $DirCreateMode incorrectly stated --------------------------------------------------------------------------- Version 2.0.7 V2-STABLE (rgerhards), 2008-04-14 - bugfix: the default for $DirCreateMode was 0644, and as such wrong. diff --git a/doc/imtcp.html b/doc/imtcp.html index 5217634e..0ccdecc7 100644 --- a/doc/imtcp.html +++ b/doc/imtcp.html @@ -41,6 +41,10 @@ very limited interest in fixing this issue. This directive <b>can not</b> fix th That would require much more code changes, which I was unable to do so far. Full details can be found at the <a href="http://www.rsyslog.com/Article321.phtml">Cisco tcp syslog anomaly</a> page. +<li>$InputTCPServerNotifyOnConnectionClose [on/<b>off</b>] (available since 4.5.5)<br> +instructs imtcp to emit a message if the remote peer closes a connection.<br> +<b>Important:</b> This directive is global to all listeners and must be given right +after loading imtcp, otherwise it may have no effect.</li> <li>$InputTCPServerRun <port><br> Starts a TCP server on selected port</li> <li>$InputTCPMaxListeners <number><br> diff --git a/doc/rsconf1_dircreatemode.html b/doc/rsconf1_dircreatemode.html index 9a9c61eb..b22b6c59 100644 --- a/doc/rsconf1_dircreatemode.html +++ b/doc/rsconf1_dircreatemode.html @@ -7,9 +7,13 @@ <h2>$DirCreateMode</h2> <p><b>Type:</b> global configuration directive</p> -<p><b>Default:</b> 0644</p> +<p><b>Default:</b> 0700</p> <p><b>Description:</b></p> <p>This is the same as $FileCreateMode, but for directories automatically generated.</p> +<p>Please visit the +<a target="_blank" href="http://lists.adiscon.net/pipermail/rsyslog/2009-April/001986.html">rsyslog mailing list +archive</a> +to understand why the default is so restrictive.</p> <p><b>Sample:</b></p> <p><code><b></b></code></p> @@ -17,8 +21,8 @@ index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p> <p><font size="2">This documentation is part of the <a href="http://www.rsyslog.com/">rsyslog</a> project.<br> -Copyright © 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and +Copyright © 2007-2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and <a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL -version 2 or higher.</font></p> +version 3 or higher.</font></p> </body> </html> diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c index 0bd064e6..c56593f2 100644 --- a/plugins/imtcp/imtcp.c +++ b/plugins/imtcp/imtcp.c @@ -84,6 +84,7 @@ static permittedPeers_t *pPermPeersRoot = NULL; static int iTCPSessMax = 200; /* max number of sessions */ static int iTCPLstnMax = 20; /* max number of sessions */ static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ +static int bEmitMsgOnClose = 0; /* emit an informational message on close by remote peer */ static int iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; /* addtl frame delimiter, e.g. for netscreen, default none */ static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */ @@ -197,6 +198,7 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose)); CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode)); CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim)); + CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, bEmitMsgOnClose)); /* now set optional params, but only if they were actually configured */ if(pszStrmDrvrAuthMode != NULL) { CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode)); @@ -284,6 +286,7 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus iTCPSessMax = 200; iTCPLstnMax = 20; iStrmDrvrMode = 0; + bEmitMsgOnClose = 0; iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; free(pszInputName); pszInputName = NULL; @@ -321,6 +324,8 @@ CODEmodInit_QueryRegCFSLineHdlr NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxlisteners"), 0, eCmdHdlrInt, NULL, &iTCPLstnMax, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpservernotifyonconnectionclose"), 0, + eCmdHdlrBinary, NULL, &bEmitMsgOnClose, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdrivermode"), 0, eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0, diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c index 0a8920f5..b9db1875 100644 --- a/plugins/imudp/imudp.c +++ b/plugins/imudp/imudp.c @@ -208,6 +208,9 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted, ABORT_FINALIZE(RS_RET_ERR); } + if(lenRcvBuf == 0) + continue; /* this looks a bit strange, but practice shows it happens... */ + /* if we reach this point, we had a good receive and can process the packet received */ /* check if we have a different sender than before, if so, we need to query some new values */ if(net.CmpHost(&frominet, frominetPrev, socklen) != 0) { diff --git a/plugins/omstdout/omstdout.c b/plugins/omstdout/omstdout.c index 584ae458..b3ec6287 100644 --- a/plugins/omstdout/omstdout.c +++ b/plugins/omstdout/omstdout.c @@ -124,7 +124,7 @@ CODESTARTdoAction toWrite = (char*) ppString[0]; } len = strlen(toWrite); - write(1, toWrite, strlen(toWrite)); /* 1 is stdout! */ + write(1, toWrite, len); /* 1 is stdout! */ if(pData->bEnsureLFEnding && toWrite[len-1] != '\n') { write(1, "\n", 1); /* write missing LF */ } diff --git a/runtime/datetime.c b/runtime/datetime.c index dfa56b4f..6160bd7c 100644 --- a/runtime/datetime.c +++ b/runtime/datetime.c @@ -143,6 +143,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. * @@ -150,17 +151,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; @@ -172,9 +177,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 */ @@ -189,6 +198,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; @@ -196,48 +206,55 @@ 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); + --lenStr; + 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); + --lenStr; + 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); + --lenStr; - 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); + --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); /* 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; @@ -245,23 +262,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 { @@ -270,10 +291,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; @@ -289,6 +312,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS) pTime->OffsetMode = OffsetMode; pTime->OffsetHour = OffsetHour; pTime->OffsetMinute = OffsetMinute; + *pLenStr = lenStr; finalize_it: RETiRet; @@ -307,9 +331,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; @@ -319,6 +347,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; @@ -326,6 +355,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: @@ -348,6 +379,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': @@ -470,26 +504,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. @@ -499,23 +538,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); @@ -523,8 +565,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 @@ -541,6 +585,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 58f368e7..8140eb71 100644 --- a/runtime/datetime.h +++ b/runtime/datetime.h @@ -34,8 +34,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); int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst); int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf); diff --git a/runtime/parser.c b/runtime/parser.c index db11ac5b..466066e7 100644 --- a/runtime/parser.c +++ b/runtime/parser.c @@ -167,6 +167,7 @@ sanitizeMessage(msg_t *pMsg) uchar szSanBuf[32*1024]; /* buffer used for sanitizing a string */ assert(pMsg != NULL); + assert(pMsg->iLenRawMsg > 0); # ifdef USE_NETZIP CHKiRet(uncompressMessage(pMsg)); @@ -254,6 +255,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. @@ -264,6 +266,11 @@ rsRetVal parseMsg(msg_t *pMsg) DEFiRet; uchar *msg; int pri; + int lenMsg; + int iPriText; + + if(pMsg->iLenRawMsg == 0) + ABORT_FINALIZE(RS_RET_EMPTY_MSG); CHKiRet(sanitizeMessage(pMsg)); @@ -271,15 +278,17 @@ rsRetVal parseMsg(msg_t *pMsg) DBGPRINTF("msg parser: flags %x, from '%s', msg '%s'\n", pMsg->msgFlags, getRcvFrom(pMsg), pMsg->pszRawMsg); /* pull PRI */ - pri = DEFUPRI; + lenMsg = pMsg->iLenRawMsg; msg = pMsg->pszRawMsg; + pri = DEFUPRI; + iPriText = 0; if(*msg == '<') { /* while we process the PRI, we also fill the PRI textual representation * inside the msg object. This may not be ideal from an OOP point of view, * but it offers us performance... */ pri = 0; - while(isdigit((int) *++msg)) { + while(--lenMsg > 0 && isdigit((int) *++msg)) { pri = 10 * pri + (*msg - '0'); } if(*msg == '>') diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 71cb9cf2..59e8458b 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -381,9 +381,11 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_FILENAME_INVALID = -2140, /**< filename invalid, not found, no access, ... */ RS_RET_ZLIB_ERR = -2141, /**< error during zlib call */ RS_RET_VAR_NOT_FOUND = -2142, /**< variable not found */ - RS_RET_NO_SRCNAME_TPL = -2143, /**< sourcename template was not specified where one was needed (omudpspoof spoof addr) */ - RS_RET_HOST_NOT_SPECIFIED = -2144, /**< (target) host was not specified where it was needed */ - RS_RET_ERR_LIBNET_INIT = -2145, /**< error initializing libnet */ + RS_RET_EMPTY_MSG = -2143, /**< provided (raw) MSG is empty */ + RS_RET_PEER_CLOSED_CONN = -2144, /**< remote peer closed connection (information, no error) */ + RS_RET_NO_SRCNAME_TPL = -2150, /**< sourcename template was not specified where one was needed (omudpspoof spoof addr) */ + RS_RET_HOST_NOT_SPECIFIED = -2151, /**< (target) host was not specified where it was needed */ + RS_RET_ERR_LIBNET_INIT = -2152, /**< error initializing libnet */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ @@ -482,6 +482,14 @@ doReceive(tcpsrv_t *pThis, tcps_sess_t **ppSess) iRet = pThis->pRcvData(*ppSess, buf, sizeof(buf), &iRcvd); switch(iRet) { case RS_RET_CLOSED: + if(pThis->bEmitMsgOnClose) { + uchar *pszPeer; + int lenPeer; + errno = 0; + prop.GetString((*ppSess)->fromHostIP, &pszPeer, &lenPeer); + errmsg.LogError(0, RS_RET_PEER_CLOSED_CONN, "Netstream session %p closed by remote peer %s.\n", + (*ppSess)->pStrm, pszPeer); + } pThis->pOnRegularClose(*ppSess); tcps_sess.Destruct(ppSess); break; @@ -794,6 +802,16 @@ SetRuleset(tcpsrv_t *pThis, ruleset_t *pRuleset) } +/* Set connection close notification */ +static rsRetVal +SetNotificationOnRemoteClose(tcpsrv_t *pThis, int bNewVal) +{ + DEFiRet; + pThis->bEmitMsgOnClose = bNewVal; + RETiRet; +} + + /* here follows a number of methods that shuffle authentication settings down * to the drivers. Drivers not supporting these settings may return an error * state. @@ -910,6 +928,7 @@ CODESTARTobjQueryInterface(tcpsrv) pIf->SetCBOnErrClose = SetCBOnErrClose; pIf->SetOnMsgReceive = SetOnMsgReceive; pIf->SetRuleset = SetRuleset; + pIf->SetNotificationOnRemoteClose = SetNotificationOnRemoteClose; finalize_it: ENDobjQueryInterface(tcpsrv) @@ -54,12 +54,14 @@ struct tcpsrv_s { uchar *pszInputName; /**< value to be used as input name */ ruleset_t *pRuleset; /**< ruleset to bind to */ permittedPeers_t *pPermPeers;/**< driver's permitted peers */ + bool bEmitMsgOnClose; /**< emit an informational message when the remote peer closes connection */ int iLstnCurr; /**< max nbr of listeners currently supported */ netstrm_t **ppLstn; /**< our netstream listners */ tcpLstnPortList_t **ppLstnPort; /**< pointer to relevant listen port description */ int iLstnMax; /**< max number of listners supported */ int iSessMax; /**< max number of sessions supported */ tcpLstnPortList_t *pLstnPorts; /**< head pointer for listen ports */ + int addtlFrameDelim; /**< additional frame delimiter for plain TCP syslog framing (e.g. to handle NetScreen) */ tcps_sess_t **pSessions;/**< array of all of our sessions */ void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/ @@ -114,8 +116,9 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */ rsRetVal (*SetRuleset)(tcpsrv_t *pThis, ruleset_t*); /* 2009-06-12 */ /* added v7 */ rsRetVal (*SetLstnMax)(tcpsrv_t *pThis, int iMaxLstn); /* 2009-08-17 */ + rsRetVal (*SetNotificationOnRemoteClose)(tcpsrv_t *pThis, int bNewVal); /* 2009-10-01 */ ENDinterface(tcpsrv) -#define tcpsrvCURR_IF_VERSION 7 /* increment whenever you change the interface structure! */ +#define tcpsrvCURR_IF_VERSION 8 /* increment whenever you change the interface structure! */ /* change for v4: * - SetAddtlFrameDelim() added -- rgerhards, 2008-12-10 * - SetInputName() added -- rgerhards, 2008-12-10 diff --git a/tools/syslogd.c b/tools/syslogd.c index ff671138..73b76df1 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -954,10 +954,12 @@ msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch) * to after the terminating SP. The caller must ensure that the * provided buffer is large enough to hold the to be extracted value. * Returns 0 if everything is fine or 1 if either the field is not - * SP-terminated or any other error occurs. - * rger, 2005-11-24 + * SP-terminated or any other error occurs. -- rger, 2005-11-24 + * The function now receives the size of the string and makes sure + * that it does not process more than that. The *pLenStr counter is + * updated on exit. -- rgerhards, 2009-09-23 */ -static int parseRFCField(uchar **pp2parse, uchar *pResult) +static int parseRFCField(uchar **pp2parse, uchar *pResult, int *pLenStr) { uchar *p2parse; int iRet = 0; @@ -969,14 +971,17 @@ static int parseRFCField(uchar **pp2parse, uchar *pResult) p2parse = *pp2parse; /* this is the actual parsing loop */ - while(*p2parse && *p2parse != ' ') { + while(*pLenStr > 0 && *p2parse != ' ') { *pResult++ = *p2parse++; + --(*pLenStr); } - if(*p2parse == ' ') + if(*pLenStr > 0 && *p2parse == ' ') { ++p2parse; /* eat SP, but only if not at end of string */ - else + --(*pLenStr); + } else { iRet = 1; /* there MUST be an SP! */ + } *pResult = '\0'; /* set the new parse pointer */ @@ -992,20 +997,24 @@ static int parseRFCField(uchar **pp2parse, uchar *pResult) * to after the terminating SP. The caller must ensure that the * provided buffer is large enough to hold the to be extracted value. * Returns 0 if everything is fine or 1 if either the field is not - * SP-terminated or any other error occurs. - * rger, 2005-11-24 + * SP-terminated or any other error occurs. -- rger, 2005-11-24 + * The function now receives the size of the string and makes sure + * that it does not process more than that. The *pLenStr counter is + * updated on exit. -- rgerhards, 2009-09-23 */ -static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult) +static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult, int *pLenStr) { uchar *p2parse; int bCont = 1; int iRet = 0; + int lenStr; assert(pp2parse != NULL); assert(*pp2parse != NULL); assert(pResult != NULL); p2parse = *pp2parse; + lenStr = *pLenStr; /* this is the actual parsing loop * Remeber: structured data starts with [ and includes any characters @@ -1013,40 +1022,55 @@ static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult) * structured data. There may also be \] inside the structured data, which * do NOT terminate an element. */ - if(*p2parse != '[') + if(lenStr == 0 || *p2parse != '[') return 1; /* this is NOT structured data! */ if(*p2parse == '-') { /* empty structured data? */ *pResult++ = '-'; ++p2parse; + --lenStr; } else { while(bCont) { - if(*p2parse == '\0') { - iRet = 1; /* this is not valid! */ - bCont = 0; + if(lenStr < 2) { + /* we now need to check if we have only structured data */ + if(lenStr > 0 && *p2parse == ']') { + *pResult++ = *p2parse; + p2parse++; + lenStr--; + bCont = 0; + } else { + iRet = 1; /* this is not valid! */ + bCont = 0; + } } else if(*p2parse == '\\' && *(p2parse+1) == ']') { /* this is escaped, need to copy both */ *pResult++ = *p2parse++; *pResult++ = *p2parse++; + lenStr -= 2; } else if(*p2parse == ']' && *(p2parse+1) == ' ') { /* found end, just need to copy the ] and eat the SP */ *pResult++ = *p2parse; p2parse += 2; + lenStr -= 2; bCont = 0; } else { *pResult++ = *p2parse++; + --lenStr; } } } - if(*p2parse == ' ') + if(lenStr > 0 && *p2parse == ' ') { ++p2parse; /* eat SP, but only if not at end of string */ - else + --lenStr; + } else { iRet = 1; /* there MUST be an SP! */ + } *pResult = '\0'; /* set the new parse pointer */ *pp2parse = p2parse; + *pLenStr = lenStr; return 0; } @@ -1071,23 +1095,26 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags) { uchar *p2parse; uchar *pBuf; + int lenMsg; int bContParse = 1; BEGINfunc assert(pMsg != NULL); assert(pMsg->pszRawMsg != NULL); p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */ + lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; - /* do a sanity check on the version and eat it */ + /* do a sanity check on the version and eat it (the caller checked this already) */ assert(p2parse[0] == '1' && p2parse[1] == ' '); p2parse += 2; + lenMsg -= 2; /* Now get us some memory we can use as a work buffer while parsing. * We simply allocated a buffer sufficiently large to hold all of the * message, so we can not run into any troubles. I think this is * more wise then to use individual buffers. */ - if((pBuf = malloc(sizeof(uchar) * ustrlen(p2parse) + 1)) == NULL) + if((pBuf = malloc(sizeof(uchar) * (lenMsg + 1))) == NULL) return 1; /* IMPORTANT NOTE: @@ -1098,7 +1125,7 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags) */ /* TIMESTAMP */ - if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) { + if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) { if(flags & IGNDATE) { /* we need to ignore the msg data, so simply copy over reception date */ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime)); @@ -1110,31 +1137,31 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags) /* HOSTNAME */ if(bContParse) { - parseRFCField(&p2parse, pBuf); + parseRFCField(&p2parse, pBuf, &lenMsg); MsgSetHOSTNAME(pMsg, pBuf, ustrlen(pBuf)); } /* APP-NAME */ if(bContParse) { - parseRFCField(&p2parse, pBuf); + parseRFCField(&p2parse, pBuf, &lenMsg); MsgSetAPPNAME(pMsg, (char*)pBuf); } /* PROCID */ if(bContParse) { - parseRFCField(&p2parse, pBuf); + parseRFCField(&p2parse, pBuf, &lenMsg); MsgSetPROCID(pMsg, (char*)pBuf); } /* MSGID */ if(bContParse) { - parseRFCField(&p2parse, pBuf); + parseRFCField(&p2parse, pBuf, &lenMsg); MsgSetMSGID(pMsg, (char*)pBuf); } /* STRUCTURED-DATA */ if(bContParse) { - parseRFCStructuredData(&p2parse, pBuf); + parseRFCStructuredData(&p2parse, pBuf, &lenMsg); MsgSetStructuredData(pMsg, (char*)pBuf); } @@ -1163,6 +1190,7 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags) int parseLegacySyslogMsg(msg_t *pMsg, int flags) { uchar *p2parse; + int lenMsg; int bTAGCharDetected; int i; /* general index for parsing */ uchar bufParseTAG[CONF_TAG_MAXSIZE]; @@ -1171,27 +1199,32 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags) assert(pMsg != NULL); assert(pMsg->pszRawMsg != NULL); + lenMsg = pMsg->iLenRawMsg - (pMsg->offAfterPRI + 1); +RUNLOG_VAR("%d", pMsg->offAfterPRI); +RUNLOG_VAR("%d", lenMsg); p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */ /* Check to see if msg contains a timestamp. We start by assuming - * that the message timestamp is the time of reciption (which we + * that the message timestamp is the time of reception (which we * generated ourselfs and then try to actually find one inside the * message. There we go from high-to low precison and are done * when we find a matching one. -- rgerhards, 2008-09-16 */ - if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) { + if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) { /* we are done - parse pointer is moved by ParseTIMESTAMP3339 */; - } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) { + } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) { /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */; - } else if(*p2parse == ' ') { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */ + } else if(*p2parse == ' ' && lenMsg > 1) { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */ ++p2parse; /* move over space */ - if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) { + --lenMsg; + if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) { /* indeed, we got it! */ /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */; } else {/* parse pointer needs to be restored, as we moved it off-by-one * for this try. */ --p2parse; + ++lenMsg; } } @@ -1222,12 +1255,13 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags) * that is not a valid hostname. */ bTAGCharDetected = 0; - if(flags & PARSE_HOSTNAME) { + if(lenMsg > 0 && flags & PARSE_HOSTNAME) { i = 0; - while((isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '.' + while(lenMsg > 0 && (isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '.' || p2parse[i] == '_' || p2parse[i] == '-') && i < CONF_TAG_MAXSIZE) { bufParseHOSTNAME[i] = p2parse[i]; ++i; + --lenMsg; } if(i > 0 && p2parse[i] == ' ' && isalnum(p2parse[i-1])) { @@ -1252,11 +1286,13 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags) * outputs so that only 32 characters max are used by default. */ i = 0; - while(*p2parse && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE) { + while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE) { bufParseTAG[i++] = *p2parse++; + --lenMsg; } - if(*p2parse == ':') { + if(lenMsg > 0 && *p2parse == ':') { ++p2parse; + --lenMsg; bufParseTAG[i++] = ':'; } |