summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--syslogd.c84
-rw-r--r--template.c52
-rw-r--r--template.h5
-rw-r--r--test.conf26
4 files changed, 154 insertions, 13 deletions
diff --git a/syslogd.c b/syslogd.c
index 31fbec30..81300460 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -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;
}
diff --git a/template.c b/template.c
index 9a35b5e2..3ddc8e24 100644
--- a/template.c
+++ b/template.c
@@ -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);
diff --git a/template.h b/template.h
index 604c317b..52869761 100644
--- a/template.h
+++ b/template.h
@@ -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 };
diff --git a/test.conf b/test.conf
index 54ae4739..82cf2150 100644
--- a/test.conf
+++ b/test.conf
@@ -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