summaryrefslogtreecommitdiff
path: root/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mmrfc5424addhmac/mmrfc5424addhmac.c')
-rw-r--r--plugins/mmrfc5424addhmac/mmrfc5424addhmac.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c b/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
new file mode 100644
index 0000000..959a8ba
--- /dev/null
+++ b/plugins/mmrfc5424addhmac/mmrfc5424addhmac.c
@@ -0,0 +1,379 @@
+/* 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.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <openssl/hmac.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmrfc5424addhmac")
+
+
+DEFobjCurrIf(errmsg);
+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 {
+ rsconf_t *pConf; /* our overall config object */
+};
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
+
+/* tables for interfacing with the v6 config system */
+/* action (instance) parameters */
+static struct cnfparamdescr actpdescr[] = {
+ { "key", eCmdHdlrString, 1 },
+ { "hashfunction", eCmdHdlrString, 1 },
+ { "sd_id", eCmdHdlrGetWord, 1 }
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+ };
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ENDbeginCnfLoad
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ENDfreeInstance
+
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->key = NULL;
+}
+
+BEGINnewActInst
+ struct cnfparamvals *pvals;
+ char *ciphername;
+ int i;
+CODESTARTnewActInst
+ DBGPRINTF("newActInst (mmrfc5424addhmac)\n");
+ if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ CODE_STD_STRING_REQUESTnewActInst(1)
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
+ CHKiRet(createInstance(&pData));
+ setInstParamDefaults(pData);
+
+ for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "key")) {
+ 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);
+ }
+ }
+
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+
+/* 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)
+{
+ 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];
+ }
+ print[iDst] = '\0';
+}
+
+
+/* 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;
+ }
+ *rootIdx = i;
+}
+
+static inline void
+getSDID(uchar *sdbuf, int sdlen, int *rootIdx, uchar *sdid)
+{
+ int i, j;
+ i = *rootIdx;
+ j = 0;
+
+ if(sdbuf[i] != '[') {
+ ++i;
+ goto done;
+ }
+
+ ++i;
+ 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;
+
+ if(sdbuf[0] == '-') /* RFC: struc data is empty! */
+ goto done;
+
+ 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:
+ 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;
+}
+
+
+BEGINdoAction
+ msg_t *pMsg;
+CODESTARTdoAction
+ pMsg = (msg_t*) ppString[0];
+ 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);
+ }
+ }
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(strncmp((char*) p, ":mmrfc5424addhmac:", sizeof(":mmrfc5424addhmac:") - 1)) {
+ errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED,
+ "mmrfc5424addhmac supports only v6+ config format, use: "
+ "action(type=\"mmrfc5424addhmac\" ...)");
+ }
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
+ EVP_cleanup();
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+ENDqueryEtryPt
+
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *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