summaryrefslogtreecommitdiffstats
path: root/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mmrfc5424addhmac/mmrfc5424addhmac.c')
-rw-r--r--plugins/mmrfc5424addhmac/mmrfc5424addhmac.c266
1 files changed, 164 insertions, 102 deletions
diff --git a/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c b/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
index e5ada8e0..6adad187 100644
--- a/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
+++ b/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
@@ -1,6 +1,30 @@
/* mmrfc5424addhmac.c
* custom module: add hmac to RFC5424 messages
*
+ * Note on important design decision: This module is fully self-contained.
+ * Most importantly, it does not rely on mmpstrucdata to populate the
+ * structured data portion of the messages JSON. There are two reasons
+ * for this:
+ * 1. robustness
+ * - this guard against misconfiguration
+ * - it permits us to be more liberal in regard to malformed
+ * structured data
+ * - it permits us to handle border-cases (like duplicate
+ * SD-IDs) with much less complexity
+ * 2. performance
+ * With being "on the spot" of what we need we can reduce memory
+ * reads and writes. This is a considerable save if the JSON representation
+ * is not otherwise needed.
+ *
+ * Note that the recommended calling sequence if both of these modules
+ * are used is
+ *
+ * 1. mmrfc5424addhmac
+ * 2. mmpstrucdata
+ *
+ * This sequence permits mmpstrucdata to pick up the modifications we
+ * made in this module here.
+ *
* Copyright 2013 Adiscon GmbH.
*
* This file is part of rsyslog.
@@ -30,6 +54,7 @@
#include <errno.h>
#include <unistd.h>
#include <stdint.h>
+#include <openssl/hmac.h>
#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
@@ -48,6 +73,11 @@ DEF_OMOD_STATIC_DATA
/* config variables */
typedef struct _instanceData {
+ uchar *key;
+ int16_t keylen; /* cached length of key, to avoid recomputation */
+ uchar *sdid; /* SD-ID to be used to persist the hmac */
+ int16_t sdidLen;
+ const EVP_MD *algo;
} instanceData;
struct modConfData_s {
@@ -60,9 +90,9 @@ static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current ex
/* tables for interfacing with the v6 config system */
/* action (instance) parameters */
static struct cnfparamdescr actpdescr[] = {
- { "mode", eCmdHdlrGetWord, 0 },
- { "replacementchar", eCmdHdlrGetChar, 0 },
- { "ipv4.bits", eCmdHdlrInt, 0 },
+ { "key", eCmdHdlrString, 1 },
+ { "hashfunction", eCmdHdlrString, 1 },
+ { "sd_id", eCmdHdlrGetWord, 1 }
};
static struct cnfparamblk actpblk =
{ CNFPARAMBLK_VERSION,
@@ -112,11 +142,12 @@ ENDfreeInstance
static inline void
setInstParamDefaults(instanceData *pData)
{
- //pData->replChar = 'x';
+ pData->key = NULL;
}
BEGINnewActInst
struct cnfparamvals *pvals;
+ char *ciphername;
int i;
CODESTARTnewActInst
DBGPRINTF("newActInst (mmrfc5424addhmac)\n");
@@ -133,9 +164,22 @@ CODESTARTnewActInst
if(!pvals[i].bUsed)
continue;
if(!strcmp(actpblk.descr[i].name, "replacementchar")) {
- // pData->replChar = es_getBufAddr(pvals[i].val.d.estr)[0];
- } else if(!strcmp(actpblk.descr[i].name, "ipv4.bits")) {
- // pData->ipv4.bits = (int8_t) pvals[i].val.d.n;
+ pData->key = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ pData->keylen = es_strlen(pvals[i].val.d.estr);
+ } else if(!strcmp(actpblk.descr[i].name, "hashfunction")) {
+ ciphername = es_str2cstr(pvals[i].val.d.estr, NULL);
+ pData->algo = EVP_get_digestbyname(ciphername);
+ if(pData->algo == NULL) {
+ errmsg.LogError(0, RS_RET_CRY_INVLD_ALGO,
+ "hashFunction '%s' unknown to openssl - "
+ "cannot continue", ciphername);
+ free(ciphername);
+ ABORT_FINALIZE(RS_RET_CRY_INVLD_ALGO);
+ }
+ free(ciphername);
+ } else if(!strcmp(actpblk.descr[i].name, "sd_id")) {
+ pData->sdid = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ pData->sdidLen = es_strlen(pvals[i].val.d.estr);
} else {
dbgprintf("mmrfc5424addhmac: program error, non-handled "
"param '%s'\n", actpblk.descr[i].name);
@@ -157,122 +201,138 @@ CODESTARTtryResume
ENDtryResume
-#if 0
-/* write an IP address octet to the output position */
-static int
-writeOctet(uchar *msg, int idx, int *nxtidx, uint8_t octet)
+/* turn the binary data in bin of length len into a
+ * printable hex string. "print" must be 2*len+1 (for \0)
+ */
+static inline void
+hexify(uchar *bin, int len, uchar *print)
{
- if(octet > 99) {
- msg[idx++] = '0' + octet / 100;
- octet = octet % 100;
- }
- if(octet > 9) {
- msg[idx++] = '0' + octet / 10;
- octet = octet % 10;
+ static const char hexchars[16] =
+ {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ int iSrc, iDst;
+
+ for(iSrc = iDst = 0 ; iSrc < len ; ++iSrc) {
+ print[iDst++] = hexchars[bin[iSrc]>>4];
+ print[iDst++] = hexchars[bin[iSrc]&0x0f];
}
- msg[idx++] = '0' + octet;
+ print[iDst] = '\0';
+}
- if(nxtidx != NULL) {
- if(idx + 1 != *nxtidx) {
- /* we got shorter, fix it! */
- msg[idx] = '.';
- *nxtidx = idx + 1;
+
+/* skip to end of current SD-ID. This function can be improved
+ * in regard to fully parsing based on RFC5424, HOWEVER, this would
+ * also reduce performance. So we consider the current implementation
+ * to be superior.
+ */
+static inline void
+skipSDID(uchar *sdbuf, int sdlen, int *rootIdx)
+{
+ int i;
+ i = *rootIdx;
+ while(i < sdlen) {
+ if(sdbuf[i] == ']') {
+ if(i > *rootIdx && sdbuf[i-1] == '\\') {
+ ; /* escaped, nothing to do! */
+ } else {
+ ++i; /* eat ']' */
+ break;
+ }
}
+ ++i;
}
- return idx;
+ *rootIdx = i;
}
-/* currently works for IPv4 only! */
-void
-anonip(instanceData *pData, uchar *msg, int *pLenMsg, int *idx)
+static inline void
+getSDID(uchar *sdbuf, int sdlen, int *rootIdx, uchar *sdid)
{
- int i = *idx;
- int octet;
- uint32_t ipv4addr;
- int ipstart[4];
- int j;
- int endpos;
- int lenMsg = *pLenMsg;
-
- while(i < lenMsg && (msg[i] <= '0' || msg[i] >= '9')) {
- ++i; /* skip to first number */
- }
- if(i >= lenMsg)
+ int i, j;
+ i = *rootIdx;
+ j = 0;
+
+ if(sdbuf[i] != '[') {
+ ++i;
goto done;
+ }
- /* got digit, let's see if ip */
- ipstart[0] = i;
- octet = getnum(msg, lenMsg, &i);
- if(octet > 255 || msg[i] != '.') goto done;
- ipv4addr = octet << 24;
- ++i;
- ipstart[1] = i;
- octet = getnum(msg, lenMsg, &i);
- if(octet > 255 || msg[i] != '.') goto done;
- ipv4addr |= octet << 16;
++i;
- ipstart[2] = i;
- octet = getnum(msg, lenMsg, &i);
- if(octet > 255 || msg[i] != '.') goto done;
- ipv4addr |= octet << 8;
- ++i;
- ipstart[3] = i;
- octet = getnum(msg, lenMsg, &i);
- if(octet > 255 || !(msg[i] == ' ' || msg[i] == ':')) goto done;
- ipv4addr |= octet;
-
- /* OK, we now found an ip address */
- if(pData->mode == SIMPLE_MODE) {
- if(pData->ipv4.bits == 8)
- j = ipstart[3];
- else if(pData->ipv4.bits == 16)
- j = ipstart[2];
- else if(pData->ipv4.bits == 24)
- j = ipstart[1];
- else /* due to our checks, this *must* be 32 */
- j = ipstart[0];
- while(j < i) {
- if(msg[j] != '.')
- msg[j] = pData->replChar;
- ++j;
- }
- } else { /* REWRITE_MODE */
- ipv4addr &= ipv4masks[pData->ipv4.bits];
- if(pData->ipv4.bits > 24)
- writeOctet(msg, ipstart[0], &(ipstart[1]), ipv4addr >> 24);
- if(pData->ipv4.bits > 16)
- writeOctet(msg, ipstart[1], &(ipstart[2]), (ipv4addr >> 16) & 0xff);
- if(pData->ipv4.bits > 8)
- writeOctet(msg, ipstart[2], &(ipstart[3]), (ipv4addr >> 8) & 0xff);
- endpos = writeOctet(msg, ipstart[3], NULL, ipv4addr & 0xff);
- /* if we had truncation, we need to shrink the msg */
- dbgprintf("existing i %d, endpos %d\n", i, endpos);
- if(i - endpos > 0) {
- *pLenMsg = lenMsg - (i - endpos);
- memmove(msg+endpos, msg+i, lenMsg - i + 1);
+ while(i < sdlen && sdbuf[i] != '=' && sdbuf[i] != ' '
+ && sdbuf[i] != ']' && sdbuf[i] != '"') {
+ sdid[j++] = sdbuf[i++];
+ }
+done:
+ sdid[j] = '\0';
+ *rootIdx = i;
+}
+
+/* check if "our" hmac is already present */
+static inline sbool
+isHmacPresent(instanceData *pData, msg_t *pMsg)
+{
+ uchar *sdbuf;
+ rs_size_t sdlen;
+ sbool found;
+ int i;
+ uchar sdid[33]; /* RFC-based size limit */
+
+ MsgGetStructuredData(pMsg, &sdbuf, &sdlen);
+
+ found = 0;
+ i = 0;
+ while(i < sdlen && !found) {
+ getSDID(sdbuf, sdlen, &i, sdid);
+ if(!strcmp((char*)pData->sdid, (char*)sdid)) {
+ found = 1;
+ break;
}
+ skipSDID(sdbuf, sdlen, &i);
}
-done: *idx = i;
- return;
+ return found;
+}
+
+static inline rsRetVal
+hashMsg(instanceData *pData, msg_t *pMsg)
+{
+ uchar *pRawMsg;
+ int lenRawMsg;
+ uchar *sdbuf;
+ rs_size_t sdlen;
+ unsigned int hashlen;
+ uchar hash[EVP_MAX_MD_SIZE];
+ uchar hashPrintable[2*EVP_MAX_MD_SIZE+1];
+ uchar newsd[64*1024]; /* we assume this is sufficient... */
+ int lenNewsd;
+ DEFiRet;
+
+ MsgGetStructuredData(pMsg, &sdbuf, &sdlen);
+ getRawMsg(pMsg, &pRawMsg, &lenRawMsg);
+ HMAC(pData->algo, pData->key, pData->keylen,
+ pRawMsg, lenRawMsg, hash, &hashlen);
+ hexify(hash, hashlen, hashPrintable);
+ lenNewsd = snprintf((char*)newsd, sizeof(newsd), "[%s hash=\"%s\"]",
+ (char*)pData->sdid, (char*)hashPrintable);
+ MsgAddToStructuredData(pMsg, newsd, lenNewsd);
+ RETiRet;
}
-#endif
BEGINdoAction
msg_t *pMsg;
- uchar *msg;
- int lenMsg;
- int i;
CODESTARTdoAction
pMsg = (msg_t*) ppString[0];
- lenMsg = getMSGLen(pMsg);
- msg = getMSG(pMsg);
- for(i = 0 ; i < lenMsg ; ++i) {
- anonip(pData, msg, &lenMsg, &i);
+ if( msgGetProtocolVersion(pMsg) == MSG_RFC5424_PROTOCOL
+ && !isHmacPresent(pData, pMsg)) {
+ hashMsg(pData, pMsg);
+ } else {
+ if(Debug) {
+ uchar *pRawMsg;
+ int lenRawMsg;
+ getRawMsg(pMsg, &pRawMsg, &lenRawMsg);
+ dbgprintf("mmrfc5424addhmac: non-rfc5424 or HMAC already "
+ "present: %.256s\n", pRawMsg);
+ }
}
- if(lenMsg != getMSGLen(pMsg))
- setMSGLen(pMsg, lenMsg);
ENDdoAction
@@ -292,6 +352,7 @@ ENDparseSelectorAct
BEGINmodExit
CODESTARTmodExit
objRelease(errmsg, CORE_COMPONENT);
+ EVP_cleanup();
ENDmodExit
@@ -306,8 +367,9 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
- *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
CODEmodInit_QueryRegCFSLineHdlr
DBGPRINTF("mmrfc5424addhmac: module compiled with rsyslog version %s.\n", VERSION);
+ OpenSSL_add_all_digests();
CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDmodInit