diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2004-11-23 08:51:24 +0000 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2004-11-23 08:51:24 +0000 |
commit | fc45231d62fdb3b75d2466d8b3b8135e17252abe (patch) | |
tree | d7fb00b39fd676f8bd3c20d86b18bb09453d6d0e | |
parent | b6a8efa6014a062dedd205848bc891693562c282 (diff) | |
download | rsyslog-fc45231d62fdb3b75d2466d8b3b8135e17252abe.tar.gz rsyslog-fc45231d62fdb3b75d2466d8b3b8135e17252abe.tar.xz rsyslog-fc45231d62fdb3b75d2466d8b3b8135e17252abe.zip |
now includes sql option in template to escape quote chracters
-rw-r--r-- | syslogd.c | 84 | ||||
-rw-r--r-- | template.c | 52 | ||||
-rw-r--r-- | template.h | 5 | ||||
-rw-r--r-- | test.conf | 26 |
4 files changed, 154 insertions, 13 deletions
@@ -6,6 +6,9 @@ * - check template lins for extra characters and provide * a warning, if they exists * - check that no exit() is used! + * - make sure that syslogd handles the case that NO template + * is specified in an action line (hint: keep action type + * to F_UNUSED unless a proper template could be found) * * \brief This is what will become the rsyslogd daemon. * @@ -2811,6 +2814,82 @@ void logmsg(pri, pMsg, flags) #endif +/* Helper to doSQLEscape. This is called if doSQLEscape + * runs out of memory allocating the escaped string. + * Then we are in trouble. We can + * NOT simply return the unmodified string because this + * may cause SQL injection. But we also can not simply + * abort the run, this would be a DoS. I think an appropriate + * measure is to remove the dangerous \' characters. We + * replace them by \", which will break the message and + * signatures eventually present - but this is the + * best thing we can do now (or does anybody + * have a better idea?). rgerhards 2004-11-23 + */ +void doSQLEmergencyEscape(register char *p) +{ + while(*p) { + if(*p == '\'') + *p = '"'; + ++p; + } +} + + +/* SQL-Escape a string. Single quotes are found and + * replaced by two of them. A new buffer is allocated + * for the provided string and the provided buffer is + * freed. The length is updated. + */ +void doSQLEscape(char **pp, size_t *pLen) +{ + char *p; + int iLen; + sbStrBObj *pStrB; + char *pszGenerated; + + assert(pp != NULL); + assert(*pp != NULL); + assert(pLen != NULL); + + p = *pp; + iLen = *pLen; + if((pStrB = sbStrBConstruct()) == NULL) { + /* oops - no mem ... Do emergency... */ + doSQLEmergencyEscape(p); + return; + } + + while(*p) { + if(*p == '\'') { + if(sbStrBAppendChar(pStrB, '\'') != SR_RET_OK) { + doSQLEmergencyEscape(*pp); + if((pszGenerated = sbStrBFinish(pStrB)) + != NULL) + free(pszGenerated); + return; + iLen++; /* reflect the extra character */ + } + } + if(sbStrBAppendChar(pStrB, *p) != SR_RET_OK) { + doSQLEmergencyEscape(*pp); + if((pszGenerated = sbStrBFinish(pStrB)) + != NULL) + free(pszGenerated); + return; + } + ++p; + } + if((pszGenerated = sbStrBFinish(pStrB)) == NULL) { + doSQLEmergencyEscape(*pp); + return; + } + free(*pp); + *pp = pszGenerated; + *pLen = iLen; +} + + /* create a string from the provided iovec. This can * be called by all functions who need the template * text in a single string. The function takes an @@ -2890,6 +2969,11 @@ void iovCreate(struct filed *f) v->iov_base = MsgGetProp(pMsg, pTpe->data.field.pPropRepl); v->iov_len = strlen(v->iov_base); /* TODO: performance optimize - can we obtain the length? */ + /* we now need to check if we should use SQL option. In this case, + * we must go over the generated string and escape '\'' characters. + */ + if(f->f_pTpl->optFormatForSQL) + doSQLEscape((char**)&v->iov_base, &v->iov_len); ++v; ++iIOVused; } @@ -203,6 +203,8 @@ struct template *tplAddLine(char* pName, char** ppRestOfConfLine) struct template *pTpl; char *p; int bDone; + char optBuf[128]; /* buffer for options - should be more than enough... */ + int i; assert(pName != NULL); assert(ppRestOfConfLine != NULL); @@ -237,11 +239,11 @@ struct template *tplAddLine(char* pName, char** ppRestOfConfLine) } #endif + /* now actually parse the line */ p = *ppRestOfConfLine; assert(p != NULL); - /* skip whitespace */ - while(isspace(*p)) + while(isspace(*p))/* skip whitespace */ ++p; if(*p != '"') { @@ -266,11 +268,50 @@ struct template *tplAddLine(char* pName, char** ppRestOfConfLine) do_Constant(&p, pTpl); break; } - if(*p == '\"') {/* end of template string? */ + if(*p == '"') {/* end of template string? */ ++p; /* eat it! */ bDone = 1; } } + + /* we now have the template - let's look at the options (if any) + * we process options until we reach the end of the string or + * an error occurs - whichever is first. + */ + while(*p) { + while(isspace(*p))/* skip whitespace */ + ++p; + + if(*p != ',') + return(pTpl); + ++p; /* eat ',' */ + + while(isspace(*p))/* skip whitespace */ + ++p; + + /* read option word */ + i = 0; + while(i < sizeof(optBuf) / sizeof(char) - 1 + && *p && *p != '=' && *p !=',' && *p != '\n') { + optBuf[i++] = tolower(*p); + ++p; + } + optBuf[i] = '\0'; + + if(*p == '\n') + ++p; + + /* as of now, the no form is nonsense... but I do include + * it anyhow... ;) rgerhards 2004-11-22 + */ + if(!strcmp(optBuf, "sql")) { + pTpl->optFormatForSQL = 1; + } else if(!strcmp(optBuf, "nosql")) { + pTpl->optFormatForSQL = 0; + } else { + dprintf("Invalid option '%s' ignored.\n", optBuf); + } + } *ppRestOfConfLine = p; return(pTpl); @@ -310,7 +351,10 @@ void tplPrintList(void) pTpl = tplRoot; while(pTpl != NULL) { - dprintf("Template: Name='%s'\n", pTpl->pszName == NULL? "NULL" : pTpl->pszName); + dprintf("Template: Name='%s' ", pTpl->pszName == NULL? "NULL" : pTpl->pszName); + if(pTpl->optFormatForSQL) + dprintf("[SQL-Format] "); + dprintf("\n"); pTpe = pTpl->pEntryRoot; while(pTpe != NULL) { dprintf("\tEntry(%x): type %d, ", (unsigned) pTpe, pTpe->eEntryType); @@ -10,6 +10,11 @@ struct template { int tpenElements; /* number of elements in templateEntry list */ struct templateEntry *pEntryRoot; struct templateEntry *pEntryLast; + /* following are options. All are 0/1 defined (either on or off). + * we use chars because they are faster than bit fields and smaller + * than short... + */ + char optFormatForSQL; /* in text fields, escape quotes by double quotes */ }; enum EntryTypes { UNDEFINED = 0, CONSTANT = 1, FIELD = 2 }; @@ -3,20 +3,27 @@ # as possible. We use "$" to start lines that contain new dirctives. # Set syslogd options -$template Name,"Text %var% Text",<max-length> +#$template Name,"Text %var% Text",<options> +# Template options are case-insensitive. Currently defined are: +# sql - format the string suitable for a SQL statement. This will replace single +# quotes ("'") by two single quotes ("''") inside each field. This option MUST +# be specified when a template is used for writing to a database, otherwise SQL +# injection might occur. The "sql" option is only used for database-bound +# templates. It is ignored for all others. + # To escape: # % = \% # \ = \\ # --> '\' is used to escape (as in C) #$template TraditionalFormat,"%UxTradMsg%\n",1024 -$template MySQLInsert,"insert iut, msg values (1, '%msg') into systemevents" -$template TraditionalFormat,"%timegenerated% %hostname% %syslogtag%%msg%\n" -$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%hostname%,%syslogtag%,%msg%\n",1024 -$template RFC3164fmt,"<%PRI%> %timegenerated% %hostname% %syslogtag%%msg%" -#$template RFC3164fmt,"%syslogpriority%,%syslogfacility% %timegenerated% %hostname% %syslogtag%%msg%",1024 -#$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated::fulltime%,%hostname%,%syslogtag%,%msg%\n",1024 +#$template MySQLInsert,"insert iut, msg values (1, '%msg') into systemevents",SQL,MAXSIZE=1024 +$template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\n" +$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%HOSTNAME%,%syslogtag%,%msg%\n",1024 +$template RFC3164fmt,"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%" +#$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated::fulltime%,%HOSTNAME%,%syslogtag%,%msg%\n",1024 $template usermsg," %syslogtag%%msg%\n\r" -$template wallmsg,"\r\n\7Message from syslogd@%hostname% at %timegenerated% ...\r\n %syslogtag%%msg%\n\r" +$template wallmsg,"\r\n\7Message from syslogd@%HOSTNAME% at %timegenerated% ...\r\n %syslogtag%%msg%\n\r" +$template MySQLInsert,"insert iut, msg values (1, '%msg%') into systemevents", SQL # Selector lines are now modified # The "action" (e.g. file logging) can be followed @@ -24,7 +31,8 @@ $template wallmsg,"\r\n\7Message from syslogd@%hostname% at %timegenerated% ...\ # This is an example: #authpriv.* /var/log/secure,precise #*.* rger;usermsg -*.* *;wallmsg +#*.* *;wallmsg +*.* *;MySQLInsert *.* /home/rger/proj/rsyslog/logfile;precise #*.* /home/rger/proj/rsyslog/logfile;UserMsg *.* /home/rger/proj/rsyslog/tradfile;TraditionalFormat |