summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/cryprov.h17
-rw-r--r--runtime/glbl.c53
-rw-r--r--runtime/glbl.h5
-rw-r--r--runtime/libgcry.c350
-rw-r--r--runtime/libgcry.h24
-rw-r--r--runtime/lmcry_gcry.c86
-rw-r--r--runtime/msg.c225
-rw-r--r--runtime/msg.h7
-rw-r--r--runtime/net.c35
-rw-r--r--runtime/net.h2
-rw-r--r--runtime/queue.c135
-rw-r--r--runtime/queue.h11
-rw-r--r--runtime/rsconf.c27
-rw-r--r--runtime/rsyslog.h12
-rw-r--r--runtime/ruleset.c16
-rw-r--r--runtime/srutils.c2
-rw-r--r--runtime/stream.c73
17 files changed, 930 insertions, 150 deletions
diff --git a/runtime/cryprov.h b/runtime/cryprov.h
index 8496b745..5690904d 100644
--- a/runtime/cryprov.h
+++ b/runtime/cryprov.h
@@ -26,14 +26,25 @@
#include <gcrypt.h>
+/* we unfortunately need to have two different param names depending on the
+ * context in which parameters are set. Other than (re/over)engineering the core
+ * interface, we just define some values to keep track of that.
+ */
+#define CRYPROV_PARAMTYPE_REGULAR 0
+#define CRYPROV_PARAMTYPE_DISK 1
+
/* interface */
BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */
rsRetVal (*Construct)(void *ppThis);
- rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst);
+ rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst, int paramType);
rsRetVal (*Destruct)(void *ppThis);
- rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData);
+ rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData, char openMode);
rsRetVal (*Encrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf);
+ rsRetVal (*Decrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf);
rsRetVal (*OnFileClose)(void *pFileInstData, off64_t offsLogfile);
+ rsRetVal (*DeleteStateFiles)(uchar *logfn);
+ rsRetVal (*GetBytesLeftInBlock)(void *pFileInstData, ssize_t *left);
+ void (*SetDeleteOnClose)(void *pFileInstData, int val);
ENDinterface(cryprov)
-#define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define cryprovCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
#endif /* #ifndef INCLUDED_CRYPROV_H */
diff --git a/runtime/glbl.c b/runtime/glbl.c
index b3fe3a1d..cbb9b20f 100644
--- a/runtime/glbl.c
+++ b/runtime/glbl.c
@@ -60,6 +60,7 @@ DEFobjCurrIf(net)
* For this object, these variables are obviously what makes the "meat" of the
* class...
*/
+static struct cnfobj *mainqCnfObj = NULL;/* main queue object, to be used later in startup sequence */
static uchar *pszWorkDir = NULL;
static int bOptimizeUniProc = 1; /* enable uniprocessor optimizations */
static int bParseHOSTNAMEandTAG = 1; /* parser modification (based on startup params!) */
@@ -89,6 +90,7 @@ static DEF_ATOMIC_HELPER_MUT(mutTerminateInputs);
#ifdef USE_UNLIMITED_SELECT
static int iFdSetSize = howmany(FD_SETSIZE, __NFDBITS) * sizeof (fd_mask); /* size of select() bitmask in bytes */
#endif
+static uchar *SourceIPofLocalClient = NULL; /* [ar] Source IP for local client to be used on multihomed host */
/* tables for interfacing with the v6 config system */
@@ -136,6 +138,7 @@ static dataType Get##nameFunc(void) \
SIMP_PROP(ParseHOSTNAMEandTAG, bParseHOSTNAMEandTAG, int)
SIMP_PROP(OptimizeUniProc, bOptimizeUniProc, int)
SIMP_PROP(PreserveFQDN, bPreserveFQDN, int)
+SIMP_PROP(mainqCnfObj, mainqCnfObj, struct cnfobj *)
SIMP_PROP(MaxLine, iMaxLine, int)
SIMP_PROP(DefPFFamily, iDefPFFamily, int) /* note that in the future we may check the family argument */
SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int)
@@ -478,6 +481,23 @@ GetDfltNetstrmDrvrCertFile(void)
}
+/* [ar] Source IP for local client to be used on multihomed host */
+static rsRetVal
+SetSourceIPofLocalClient(uchar *newname)
+{
+ if(SourceIPofLocalClient != NULL) {
+ free(SourceIPofLocalClient); }
+ SourceIPofLocalClient = newname;
+ return RS_RET_OK;
+}
+
+static uchar*
+GetSourceIPofLocalClient(void)
+{
+ return(SourceIPofLocalClient);
+}
+
+
/* queryInterface function
* rgerhards, 2008-02-21
*/
@@ -498,6 +518,8 @@ CODESTARTobjQueryInterface(glbl)
pIf->GetLocalHostIP = GetLocalHostIP;
pIf->SetGlobalInputTermination = SetGlobalInputTermination;
pIf->GetGlobalInputTermState = GetGlobalInputTermState;
+ pIf->GetSourceIPofLocalClient = GetSourceIPofLocalClient; /* [ar] */
+ pIf->SetSourceIPofLocalClient = SetSourceIPofLocalClient; /* [ar] */
#define SIMP_PROP(name) \
pIf->Get##name = Get##name; \
pIf->Set##name = Set##name;
@@ -509,6 +531,7 @@ CODESTARTobjQueryInterface(glbl)
SIMP_PROP(DropMalPTRMsgs);
SIMP_PROP(Option_DisallowWarning);
SIMP_PROP(DisableDNS);
+ SIMP_PROP(mainqCnfObj);
SIMP_PROP(LocalFQDNName)
SIMP_PROP(LocalHostName)
SIMP_PROP(LocalDomain)
@@ -559,6 +582,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
void
glblPrepCnf(void)
{
+ free(mainqCnfObj);
+ mainqCnfObj = NULL;
free(cnfparamvals);
cnfparamvals = NULL;
}
@@ -576,6 +601,34 @@ glblProcessCnf(struct cnfobj *o)
cnfparamsPrint(&paramblk, cnfparamvals);
}
+/* Set mainq parameters. Note that when this is not called, we'll use the
+ * legacy parameter config. mainq parameters can only be set once.
+ */
+void
+glblProcessMainQCnf(struct cnfobj *o)
+{
+ if(mainqCnfObj == NULL) {
+ mainqCnfObj = o;
+ } else {
+ errmsg.LogError(0, RS_RET_ERR, "main_queue() object can only be specified "
+ "once - all but first ignored\n");
+ }
+}
+
+/* destruct the main q cnf object after it is no longer needed. This is
+ * also used to do some final checks.
+ */
+void
+glblDestructMainqCnfObj()
+{
+ /* Only destruct if not NULL! */
+ if (mainqCnfObj != NULL) {
+ nvlstChkUnused(mainqCnfObj->nvlst);
+ }
+ cnfobjDestruct(mainqCnfObj);
+ mainqCnfObj = NULL;
+}
+
void
glblDoneLoadCnf(void)
{
diff --git a/runtime/glbl.h b/runtime/glbl.h
index e95e48f7..4b1cd9f8 100644
--- a/runtime/glbl.h
+++ b/runtime/glbl.h
@@ -52,6 +52,7 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
SIMP_PROP(Option_DisallowWarning, int)
SIMP_PROP(DisableDNS, int)
SIMP_PROP(LocalFQDNName, uchar*)
+ SIMP_PROP(mainqCnfObj, struct cnfobj*)
SIMP_PROP(LocalHostName, uchar*)
SIMP_PROP(LocalDomain, uchar*)
SIMP_PROP(StripDomains, char**)
@@ -81,6 +82,8 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
/* next change is v9! */
/* v8 - 2012-03-21 */
prop_t* (*GetLocalHostIP)(void);
+ uchar* (*GetSourceIPofLocalClient)(void); /* [ar] */
+ rsRetVal (*SetSourceIPofLocalClient)(uchar*); /* [ar] */
#undef SIMP_PROP
ENDinterface(glbl)
#define glblCURR_IF_VERSION 7 /* increment whenever you change the interface structure! */
@@ -94,6 +97,8 @@ static inline void glblSetOurPid(pid_t pid) { glbl_ourpid = pid; }
void glblPrepCnf(void);
void glblProcessCnf(struct cnfobj *o);
+void glblProcessMainQCnf(struct cnfobj *o);
+void glblDestructMainqCnfObj();
void glblDoneLoadCnf(void);
#endif /* #ifndef GLBL_H_INCLUDED */
diff --git a/runtime/libgcry.c b/runtime/libgcry.c
index 51c10af4..4772cf47 100644
--- a/runtime/libgcry.c
+++ b/runtime/libgcry.c
@@ -49,8 +49,12 @@
#include <errno.h>
#include "rsyslog.h"
+#include "srUtils.h"
#include "libgcry.h"
+#define READBUF_SIZE 4096 /* size of the read buffer */
+
+static rsRetVal rsgcryBlkBegin(gcryfile gf);
static rsRetVal
eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf)
@@ -90,19 +94,66 @@ finalize_it:
RETiRet;
}
+static rsRetVal
+eiRead(gcryfile gf)
+{
+ ssize_t nRead;
+ DEFiRet;
+
+ if(gf->readBuf == NULL) {
+ CHKmalloc(gf->readBuf = malloc(READBUF_SIZE));
+ }
+
+ nRead = read(gf->fd, gf->readBuf, READBUF_SIZE);
+ if(nRead <= 0) { /* TODO: provide specific EOF case? */
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ gf->readBufMaxIdx = (int16_t) nRead;
+ gf->readBufIdx = 0;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* returns EOF on any kind of error */
+static int
+eiReadChar(gcryfile gf)
+{
+ int c;
+
+ if(gf->readBufIdx >= gf->readBufMaxIdx) {
+ if(eiRead(gf) != RS_RET_OK) {
+ c = EOF;
+ goto finalize_it;
+ }
+ }
+ c = gf->readBuf[gf->readBufIdx++];
+finalize_it:
+ return c;
+}
+
static rsRetVal
eiCheckFiletype(gcryfile gf)
{
char hdrBuf[128];
size_t toRead, didRead;
+ sbool bNeedClose = 0;
DEFiRet;
- CHKiRet(eiOpenRead(gf));
+ if(gf->fd == -1) {
+ bNeedClose = 1;
+ CHKiRet(eiOpenRead(gf));
+ }
+
if(Debug) memset(hdrBuf, 0, sizeof(hdrBuf)); /* for dbgprintf below! */
toRead = sizeof("FILETYPE:")-1 + sizeof(RSGCRY_FILETYPE_NAME)-1 + 1;
didRead = read(gf->fd, hdrBuf, toRead);
- close(gf->fd);
+ if(bNeedClose) {
+ close(gf->fd);
+ gf->fd = -1;
+ }
DBGPRINTF("eiCheckFiletype read %d bytes: '%s'\n", didRead, hdrBuf);
if( didRead != toRead
|| strncmp(hdrBuf, "FILETYPE:" RSGCRY_FILETYPE_NAME "\n", toRead))
@@ -111,6 +162,98 @@ finalize_it:
RETiRet;
}
+/* rectype/value must be EIF_MAX_*_LEN+1 long!
+ * returns 0 on success or something else on error/EOF
+ */
+static rsRetVal
+eiGetRecord(gcryfile gf, char *rectype, char *value)
+{
+ unsigned short i, j;
+ int c;
+ DEFiRet;
+
+ c = eiReadChar(gf);
+ if(c == EOF) { ABORT_FINALIZE(RS_RET_NO_DATA); }
+ for(i = 0 ; i < EIF_MAX_RECTYPE_LEN ; ++i) {
+ if(c == ':' || c == EOF)
+ break;
+ rectype[i] = c;
+ c = eiReadChar(gf);
+ }
+ if(c != ':') { ABORT_FINALIZE(RS_RET_ERR); }
+ rectype[i] = '\0';
+ j = 0;
+ for(++i ; i < EIF_MAX_VALUE_LEN ; ++i, ++j) {
+ c = eiReadChar(gf);
+ if(c == '\n' || c == EOF)
+ break;
+ value[j] = c;
+ }
+ if(c != '\n') { ABORT_FINALIZE(RS_RET_ERR); }
+ value[j] = '\0';
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+eiGetIV(gcryfile gf, uchar *iv, size_t leniv)
+{
+ char rectype[EIF_MAX_RECTYPE_LEN+1];
+ char value[EIF_MAX_VALUE_LEN+1];
+ size_t valueLen;
+ unsigned short i, j;
+ unsigned char nibble;
+ DEFiRet;
+
+ CHKiRet(eiGetRecord(gf, rectype, value));
+ if(strcmp(rectype, "IV")) {
+ DBGPRINTF("no IV record found when expected, record type "
+ "seen is '%s'\n", rectype);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ valueLen = strlen(value);
+ if(valueLen/2 != leniv) {
+ DBGPRINTF("length of IV is %d, expected %d\n",
+ valueLen/2, leniv);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ for(i = j = 0 ; i < valueLen ; ++i) {
+ if(value[i] >= '0' && value[i] <= '9')
+ nibble = value[i] - '0';
+ else if(value[i] >= 'a' && value[i] <= 'f')
+ nibble = value[i] - 'a' + 10;
+ else {
+ DBGPRINTF("invalid IV '%s'\n", value);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ if(i % 2 == 0)
+ iv[j] = nibble << 4;
+ else
+ iv[j++] |= nibble;
+ }
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+eiGetEND(gcryfile gf, off64_t *offs)
+{
+ char rectype[EIF_MAX_RECTYPE_LEN+1];
+ char value[EIF_MAX_VALUE_LEN+1];
+ DEFiRet;
+
+ CHKiRet(eiGetRecord(gf, rectype, value));
+ if(strcmp(rectype, "END")) {
+ DBGPRINTF("no END record found when expected, record type "
+ "seen is '%s'\n", rectype);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ *offs = atoll(value);
+finalize_it:
+ RETiRet;
+}
+
static rsRetVal
eiOpenAppend(gcryfile gf)
{
@@ -177,13 +320,55 @@ eiClose(gcryfile gf, off64_t offsLogfile)
size_t len;
if(gf->fd == -1)
return;
- /* 2^64 is 20 digits, so the snprintf buffer is large enough */
- len = snprintf(offs, sizeof(offs), "%lld", offsLogfile);
- eiWriteRec(gf, "END:", 4, offs, len);
+ if(gf->openMode == 'w') {
+ /* 2^64 is 20 digits, so the snprintf buffer is large enough */
+ len = snprintf(offs, sizeof(offs), "%lld", offsLogfile);
+ eiWriteRec(gf, "END:", 4, offs, len);
+ }
+ gcry_cipher_close(gf->chd);
+ free(gf->readBuf);
close(gf->fd);
+ gf->fd = -1;
DBGPRINTF("encryption info file %s: closed\n", gf->eiName);
}
+/* this returns the number of bytes left inside the block or -1, if the block
+ * size is unbounded. The function automatically handles end-of-block and begins
+ * to read the next block in this case.
+ */
+rsRetVal
+gcryfileGetBytesLeftInBlock(gcryfile gf, ssize_t *left)
+{
+ DEFiRet;
+ if(gf->bytesToBlkEnd == 0) {
+ DBGPRINTF("libgcry: end of current crypto block\n");
+ gcry_cipher_close(gf->chd);
+ CHKiRet(rsgcryBlkBegin(gf));
+ }
+ *left = gf->bytesToBlkEnd;
+finalize_it:
+ // TODO: remove once this code is sufficiently well-proven
+ DBGPRINTF("gcryfileGetBytesLeftInBlock returns %lld, iRet %d\n", (long long) *left, iRet);
+ RETiRet;
+}
+
+/* this is a special functon for use by the rsyslog disk queue subsystem. It
+ * needs to have the capability to delete state when a queue file is rolled
+ * over. This simply generates the file name and deletes it. It must take care
+ * of "all" state files, which currently happens to be a single one.
+ */
+rsRetVal
+gcryfileDeleteState(uchar *logfn)
+{
+ char fn[MAXFNAME+1];
+ DEFiRet;
+ snprintf(fn, sizeof(fn), "%s%s", logfn, ENCINFO_SUFFIX);
+ fn[MAXFNAME] = '\0'; /* be on save side */
+ DBGPRINTF("crypto provider deletes state file '%s' on request\n", fn);
+ unlink(fn);
+ RETiRet;
+}
+
static rsRetVal
gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn)
{
@@ -193,6 +378,7 @@ gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn)
CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s)));
gf->ctx = ctx;
+ gf->fd = -1;
snprintf(fn, sizeof(fn), "%s%s", logfn, ENCINFO_SUFFIX);
fn[MAXFNAME] = '\0'; /* be on save side */
gf->eiName = (uchar*) strdup(fn);
@@ -219,7 +405,12 @@ gcryfileDestruct(gcryfile gf, off64_t offsLogfile)
if(gf == NULL)
goto done;
+ DBGPRINTF("libgcry: close file %s\n", gf->eiName);
eiClose(gf, offsLogfile);
+ if(gf->bDeleteOnClose) {
+ DBGPRINTF("unlink file '%s' due to bDeleteOnClose set\n", gf->eiName);
+ unlink((char*)gf->eiName);
+ }
free(gf->eiName);
free(gf);
done: return r;
@@ -246,13 +437,13 @@ addPadding(gcryfile pF, uchar *buf, size_t *plen)
}
static inline void
-removePadding(char *buf, size_t *plen)
+removePadding(uchar *buf, size_t *plen)
{
unsigned len = (unsigned) *plen;
unsigned iSrc, iDst;
- char *frstNUL;
+ uchar *frstNUL;
- frstNUL = strchr(buf, 0x00);
+ frstNUL = (uchar*)strchr((char*)buf, 0x00);
if(frstNUL == NULL)
goto done;
iDst = iSrc = frstNUL - buf;
@@ -343,53 +534,123 @@ seedIV(gcryfile gf, uchar **iv)
}
}
-rsRetVal
-rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname)
+static inline rsRetVal
+readIV(gcryfile gf, uchar **iv)
{
- gcry_error_t gcryError;
- gcryfile gf = NULL;
- uchar *iv = NULL;
+ rsRetVal localRet;
DEFiRet;
- CHKiRet(gcryfileConstruct(ctx, &gf, fname));
+ if(gf->fd == -1) {
+ while(gf->fd == -1) {
+ localRet = eiOpenRead(gf);
+ if(localRet == RS_RET_EI_NO_EXISTS) {
+ /* wait until it is created */
+ srSleep(0, 10000);
+ } else {
+ CHKiRet(localRet);
+ }
+ }
+ CHKiRet(eiCheckFiletype(gf));
+ }
+ *iv = malloc(gf->blkLength); /* do NOT zero-out! */
+ CHKiRet(eiGetIV(gf, *iv, (size_t) gf->blkLength));
+finalize_it:
+ RETiRet;
+}
+
+/* this tries to read the END record. HOWEVER, no such record may be
+ * present, which is the case if we handle a currently-written to queue
+ * file. On the other hand, the queue file may contain multiple blocks. So
+ * what we do is try to see if there is a block end or not - and set the
+ * status accordingly. Note that once we found no end-of-block, we will never
+ * retry. This is because that case can never happen under current queue
+ * implementations. -- gerhards, 2013-05-16
+ */
+static inline rsRetVal
+readBlkEnd(gcryfile gf)
+{
+ off64_t blkEnd;
+ DEFiRet;
+
+ iRet = eiGetEND(gf, &blkEnd);
+ if(iRet == RS_RET_OK) {
+ gf->bytesToBlkEnd = (ssize_t) blkEnd;
+ } else if(iRet == RS_RET_NO_DATA) {
+ gf->bytesToBlkEnd = -1;
+ } else {
+ FINALIZE;
+ }
+
+finalize_it:
+ RETiRet;
+}
- gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo);
- gcryError = gcry_cipher_open(&gf->chd, ctx->algo, ctx->mode, 0);
+/* Read the block begin metadata and set our state variables accordingly. Can also
+ * be used to init the first block in write case.
+ */
+static rsRetVal
+rsgcryBlkBegin(gcryfile gf)
+{
+ gcry_error_t gcryError;
+ uchar *iv = NULL;
+ DEFiRet;
+
+ gcryError = gcry_cipher_open(&gf->chd, gf->ctx->algo, gf->ctx->mode, 0);
if (gcryError) {
- dbgprintf("gcry_cipher_open failed: %s/%s\n",
- gcry_strsource(gcryError),
- gcry_strerror(gcryError));
+ DBGPRINTF("gcry_cipher_open failed: %s/%s\n",
+ gcry_strsource(gcryError), gcry_strerror(gcryError));
ABORT_FINALIZE(RS_RET_ERR);
}
gcryError = gcry_cipher_setkey(gf->chd, gf->ctx->key, gf->ctx->keyLen);
if (gcryError) {
- dbgprintf("gcry_cipher_setkey failed: %s/%s\n",
- gcry_strsource(gcryError),
- gcry_strerror(gcryError));
+ DBGPRINTF("gcry_cipher_setkey failed: %s/%s\n",
+ gcry_strsource(gcryError), gcry_strerror(gcryError));
ABORT_FINALIZE(RS_RET_ERR);
}
- seedIV(gf, &iv);
+ if(gf->openMode == 'r') {
+ readIV(gf, &iv);
+ readBlkEnd(gf);
+ } else {
+ seedIV(gf, &iv);
+ }
+
gcryError = gcry_cipher_setiv(gf->chd, iv, gf->blkLength);
if (gcryError) {
- dbgprintf("gcry_cipher_setiv failed: %s/%s\n",
- gcry_strsource(gcryError),
- gcry_strerror(gcryError));
+ DBGPRINTF("gcry_cipher_setiv failed: %s/%s\n",
+ gcry_strsource(gcryError), gcry_strerror(gcryError));
ABORT_FINALIZE(RS_RET_ERR);
}
- CHKiRet(eiOpenAppend(gf));
- CHKiRet(eiWriteIV(gf, iv));
- *pgf = gf;
+
+ if(gf->openMode == 'w') {
+ CHKiRet(eiOpenAppend(gf));
+ CHKiRet(eiWriteIV(gf, iv));
+ }
finalize_it:
free(iv);
+ RETiRet;
+}
+
+rsRetVal
+rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname, char openMode)
+{
+ gcryfile gf = NULL;
+ DEFiRet;
+
+ CHKiRet(gcryfileConstruct(ctx, &gf, fname));
+ gf->openMode = openMode;
+ gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo);
+ CHKiRet(rsgcryBlkBegin(gf));
+ *pgf = gf;
+finalize_it:
if(iRet != RS_RET_OK && gf != NULL)
gcryfileDestruct(gf, -1);
RETiRet;
}
-int
+rsRetVal
rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len)
{
int gcryError;
@@ -410,6 +671,35 @@ finalize_it:
RETiRet;
}
+/* TODO: handle multiple blocks
+ * test-read END record; if present, store offset, else unbounded (current active block)
+ * when decrypting, check if bound is reached. If yes, split into two blocks, get new IV for
+ * second one.
+ */
+rsRetVal
+rsgcryDecrypt(gcryfile pF, uchar *buf, size_t *len)
+{
+ gcry_error_t gcryError;
+ DEFiRet;
+
+ if(pF->bytesToBlkEnd != -1)
+ pF->bytesToBlkEnd -= *len;
+ gcryError = gcry_cipher_decrypt(pF->chd, buf, *len, NULL, 0);
+ if(gcryError) {
+ DBGPRINTF("gcry_cipher_decrypt failed: %s/%s\n",
+ gcry_strsource(gcryError),
+ gcry_strerror(gcryError));
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ removePadding(buf, len);
+ // TODO: remove dbgprintf once things are sufficently stable -- rgerhards, 2013-05-16
+ dbgprintf("libgcry: decrypted, bytesToBlkEnd %lld, buffer is now '%50.50s'\n", (long long) pF->bytesToBlkEnd, buf);
+
+finalize_it:
+ RETiRet;
+}
+
+
/* module-init dummy for potential later use */
int
diff --git a/runtime/libgcry.h b/runtime/libgcry.h
index b77b0f9e..2f700554 100644
--- a/runtime/libgcry.h
+++ b/runtime/libgcry.h
@@ -38,7 +38,15 @@ struct gcryfile_s {
size_t blkLength; /* size of low-level crypto block */
uchar *eiName; /* name of .encinfo file */
int fd; /* descriptor of .encinfo file (-1 if not open) */
+ char openMode; /* 'r': read, 'w': write */
gcryctx ctx;
+ uchar *readBuf;
+ int16_t readBufIdx;
+ int16_t readBufMaxIdx;
+ int8_t bDeleteOnClose; /* for queue support, similar to stream subsys */
+ ssize_t bytesToBlkEnd; /* number of bytes remaining in current crypto block
+ -1 means -> no end (still being writen to, queue files),
+ 0 means -> end of block, new one must be started. */
};
int gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen);
@@ -50,8 +58,12 @@ rsRetVal rsgcrySetAlgo(gcryctx ctx, uchar *modename);
gcryctx gcryCtxNew(void);
void rsgcryCtxDel(gcryctx ctx);
int gcryfileDestruct(gcryfile gf, off64_t offsLogfile);
-rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname);
-int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len);
+rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname, char openMode);
+rsRetVal rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len);
+rsRetVal rsgcryDecrypt(gcryfile pF, uchar *buf, size_t *len);
+int gcryGetKeyFromProg(char *cmd, char **key, unsigned *keylen);
+rsRetVal gcryfileDeleteState(uchar *fn);
+rsRetVal gcryfileGetBytesLeftInBlock(gcryfile gf, ssize_t *left);
/* error states */
#define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */
@@ -62,6 +74,14 @@ int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len);
#define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info"
#define ENCINFO_SUFFIX ".encinfo"
+/* Note: gf may validly be NULL, e.g. if file has not yet been opened! */
+static inline void
+gcryfileSetDeleteOnClose(gcryfile gf, int val)
+{
+ if(gf != NULL)
+ gf->bDeleteOnClose = val;
+}
+
static inline int
rsgcryAlgoname2Algo(char *algoname) {
if(!strcmp((char*)algoname, "3DES")) return GCRY_CIPHER_3DES;
diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c
index 0a9b94bc..9a0c0072 100644
--- a/runtime/lmcry_gcry.c
+++ b/runtime/lmcry_gcry.c
@@ -43,17 +43,30 @@ DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
/* tables for interfacing with the v6 config system */
-static struct cnfparamdescr cnfpdescr[] = {
+static struct cnfparamdescr cnfpdescrRegular[] = {
{ "cry.key", eCmdHdlrGetWord, 0 },
{ "cry.keyfile", eCmdHdlrGetWord, 0 },
{ "cry.keyprogram", eCmdHdlrGetWord, 0 },
{ "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */
{ "cry.algo", eCmdHdlrGetWord, 0 }
};
-static struct cnfparamblk pblk =
+static struct cnfparamblk pblkRegular =
{ CNFPARAMBLK_VERSION,
- sizeof(cnfpdescr)/sizeof(struct cnfparamdescr),
- cnfpdescr
+ sizeof(cnfpdescrRegular)/sizeof(struct cnfparamdescr),
+ cnfpdescrRegular
+ };
+
+static struct cnfparamdescr cnfpdescrQueue[] = {
+ { "queue.cry.key", eCmdHdlrGetWord, 0 },
+ { "queue.cry.keyfile", eCmdHdlrGetWord, 0 },
+ { "queue.cry.keyprogram", eCmdHdlrGetWord, 0 },
+ { "queue.cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */
+ { "queue.cry.algo", eCmdHdlrGetWord, 0 }
+};
+static struct cnfparamblk pblkQueue =
+ { CNFPARAMBLK_VERSION,
+ sizeof(cnfpdescrQueue)/sizeof(struct cnfparamdescr),
+ cnfpdescrQueue
};
@@ -85,7 +98,7 @@ ENDobjDestruct(lmcry_gcry)
* Defaults are expected to have been set during construction.
*/
static rsRetVal
-SetCnfParam(void *pT, struct nvlst *lst)
+SetCnfParam(void *pT, struct nvlst *lst, int paramType)
{
lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT;
int i, r;
@@ -97,34 +110,41 @@ SetCnfParam(void *pT, struct nvlst *lst)
uchar *mode = NULL;
int nKeys; /* number of keys (actually methods) specified */
struct cnfparamvals *pvals;
+ struct cnfparamblk *pblk;
DEFiRet;
+ pblk = (paramType == CRYPROV_PARAMTYPE_REGULAR ) ? &pblkRegular : &pblkQueue;
nKeys = 0;
- pvals = nvlstGetParams(lst, &pblk, NULL);
+ pvals = nvlstGetParams(lst, pblk, NULL);
if(Debug) {
dbgprintf("param blk in lmcry_gcry:\n");
- cnfparamsPrint(&pblk, pvals);
+ cnfparamsPrint(pblk, pvals);
}
- for(i = 0 ; i < pblk.nParams ; ++i) {
+ for(i = 0 ; i < pblk->nParams ; ++i) {
if(!pvals[i].bUsed)
continue;
- if(!strcmp(pblk.descr[i].name, "cry.key")) {
+ if(!strcmp(pblk->descr[i].name, "cry.key") ||
+ !strcmp(pblk->descr[i].name, "queue.cry.key")) {
key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
++nKeys;
- } else if(!strcmp(pblk.descr[i].name, "cry.keyfile")) {
+ } else if(!strcmp(pblk->descr[i].name, "cry.keyfile") ||
+ !strcmp(pblk->descr[i].name, "queue.cry.keyfile")) {
keyfile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
++nKeys;
- } else if(!strcmp(pblk.descr[i].name, "cry.keyprogram")) {
+ } else if(!strcmp(pblk->descr[i].name, "cry.keyprogram") ||
+ !strcmp(pblk->descr[i].name, "queue.cry.keyprogram")) {
keyprogram = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
++nKeys;
- } else if(!strcmp(pblk.descr[i].name, "cry.mode")) {
+ } else if(!strcmp(pblk->descr[i].name, "cry.mode") ||
+ !strcmp(pblk->descr[i].name, "queue.cry.mode")) {
mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
- } else if(!strcmp(pblk.descr[i].name, "cry.algo")) {
+ } else if(!strcmp(pblk->descr[i].name, "cry.algo") ||
+ !strcmp(pblk->descr[i].name, "queue.cry.algo")) {
algo = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
} else {
DBGPRINTF("lmcry_gcry: program error, non-handled "
- "param '%s'\n", pblk.descr[i].name);
+ "param '%s'\n", pblk->descr[i].name);
}
}
if(algo != NULL) {
@@ -177,7 +197,7 @@ SetCnfParam(void *pT, struct nvlst *lst)
ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
}
- cnfparamvalsDestruct(pvals, &pblk);
+ cnfparamvalsDestruct(pvals, pblk);
if(key != NULL) {
memset(key, 0, strlen((char*)key));
free(key);
@@ -189,15 +209,33 @@ finalize_it:
RETiRet;
}
+static void
+SetDeleteOnClose(void *pF, int val)
+{
+ gcryfileSetDeleteOnClose(pF, val);
+}
+
+static rsRetVal
+GetBytesLeftInBlock(void *pF, ssize_t *left)
+{
+ return gcryfileGetBytesLeftInBlock((gcryfile) pF, left);
+}
static rsRetVal
-OnFileOpen(void *pT, uchar *fn, void *pGF)
+DeleteStateFiles(uchar *logfn)
+{
+ return gcryfileDeleteState(logfn);
+}
+
+static rsRetVal
+OnFileOpen(void *pT, uchar *fn, void *pGF, char openMode)
{
lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT;
gcryfile *pgf = (gcryfile*) pGF;
DEFiRet;
+ DBGPRINTF("lmcry_gcry: open file '%s', mode '%c'\n", fn, openMode);
- CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, fn));
+ CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, fn, openMode));
finalize_it:
/* TODO: enable this error message (need to cleanup loop first ;))
errmsg.LogError(0, iRet, "Encryption Provider"
@@ -207,6 +245,16 @@ finalize_it:
}
static rsRetVal
+Decrypt(void *pF, uchar *rec, size_t *lenRec)
+{
+ DEFiRet;
+ iRet = rsgcryDecrypt(pF, rec, lenRec);
+
+ RETiRet;
+}
+
+
+static rsRetVal
Encrypt(void *pF, uchar *rec, size_t *lenRec)
{
DEFiRet;
@@ -231,10 +279,14 @@ CODESTARTobjQueryInterface(lmcry_gcry)
}
pIf->Construct = (rsRetVal(*)(void*)) lmcry_gcryConstruct;
pIf->SetCnfParam = SetCnfParam;
+ pIf->SetDeleteOnClose = SetDeleteOnClose;
pIf->Destruct = (rsRetVal(*)(void*)) lmcry_gcryDestruct;
pIf->OnFileOpen = OnFileOpen;
pIf->Encrypt = Encrypt;
+ pIf->Decrypt = Decrypt;
pIf->OnFileClose = OnFileClose;
+ pIf->DeleteStateFiles = DeleteStateFiles;
+ pIf->GetBytesLeftInBlock = GetBytesLeftInBlock;
finalize_it:
ENDobjQueryInterface(lmcry_gcry)
diff --git a/runtime/msg.c b/runtime/msg.c
index 67d957d1..c7cd9406 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -66,6 +66,12 @@
#include "var.h"
#include "rsconf.h"
+/* TODO: move the global variable root to the config object - had no time to to it
+ * right now before vacation -- rgerhards, 2013-07-22
+ */
+static pthread_rwlock_t glblVars_rwlock;
+struct json_object *global_var_root = NULL;
+
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(datetime)
@@ -317,9 +323,10 @@ static pthread_mutex_t mutTrimCtr; /* mutex to handle malloc trim */
/* some forward declarations */
static int getAPPNAMELen(msg_t *pM, sbool bLockMutex);
-static rsRetVal jsonPathFindParent(msg_t *pM, uchar *name, uchar *leaf, struct json_object **parent, int bCreate);
+static rsRetVal jsonPathFindParent(struct json_object *jroot, uchar *name, uchar *leaf, struct json_object **parent, int bCreate);
static uchar * jsonPathGetLeaf(uchar *name, int lenName);
static struct json_object *jsonDeepCopy(struct json_object *src);
+static rsRetVal msgAddJSONObj(msg_t *pM, uchar *name, struct json_object *json, struct json_object **pjroot);
/* the locking and unlocking implementations: */
@@ -536,11 +543,16 @@ propNameStrToID(uchar *pName, propid_t *pPropID)
*pPropID = PROP_CEE_ALL_JSON;
} else if(!strncmp((char*) pName, "$!", 2)) {
*pPropID = PROP_CEE;
+ } else if(!strncmp((char*) pName, "$.", 2)) {
+ *pPropID = PROP_LOCAL_VAR;
+ } else if(!strncmp((char*) pName, "$/", 2)) {
+ *pPropID = PROP_GLOBAL_VAR;
} else if(!strcmp((char*) pName, "$bom")) {
*pPropID = PROP_SYS_BOM;
} else if(!strcmp((char*) pName, "$uptime")) {
*pPropID = PROP_SYS_UPTIME;
} else {
+ DBGPRINTF("PROP_INVALID for name '%s'\n", pName);
*pPropID = PROP_INVALID;
iRet = RS_RET_VAR_NOT_FOUND;
}
@@ -634,6 +646,10 @@ uchar *propIDToName(propid_t propID)
return UCHAR_CONSTANT("$MYHOSTNAME");
case PROP_CEE:
return UCHAR_CONSTANT("*CEE-based property*");
+ case PROP_LOCAL_VAR:
+ return UCHAR_CONSTANT("*LOCAL_VARIABLE*");
+ case PROP_GLOBAL_VAR:
+ return UCHAR_CONSTANT("*GLOBAL_VARIABLE*");
case PROP_CEE_ALL_JSON:
return UCHAR_CONSTANT("$!all-json");
case PROP_SYS_BOM:
@@ -708,6 +724,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis)
pM->rcvFrom.pRcvFrom = NULL;
pM->pRuleset = NULL;
pM->json = NULL;
+ pM->localvars = NULL;
memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt));
memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP));
pM->TAG.pszTAG = NULL;
@@ -853,6 +870,8 @@ CODESTARTobjDestruct(msg)
rsCStrDestruct(&pThis->pCSMSGID);
if(pThis->json != NULL)
json_object_put(pThis->json);
+ if(pThis->localvars != NULL)
+ json_object_put(pThis->localvars);
if(pThis->pszUUID != NULL)
free(pThis->pszUUID);
# ifndef HAVE_ATOMIC_BUILTINS
@@ -1002,6 +1021,8 @@ msg_t* MsgDup(msg_t* pOld)
if(pOld->json != NULL)
pNew->json = jsonDeepCopy(pOld->json);
+ if(pOld->localvars != NULL)
+ pNew->localvars = jsonDeepCopy(pOld->localvars);
/* we do not copy all other cache properties, as we do not even know
* if they are needed once again. So we let them re-create if needed.
@@ -1060,6 +1081,10 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
psz = (uchar*) json_object_get_string(pThis->json);
CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("json"), PROPTYPE_PSZ, (void*) psz));
}
+ if(pThis->localvars != NULL) {
+ psz = (uchar*) json_object_get_string(pThis->localvars);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("localvars"), PROPTYPE_PSZ, (void*) psz));
+ }
objSerializePTR(pStrm, pCSStrucData, CSTR);
objSerializePTR(pStrm, pCSAPPNAME, CSTR);
@@ -1202,6 +1227,14 @@ MsgDeserialize(msg_t *pMsg, strm_t *pStrm)
reinitVar(pVar);
CHKiRet(objDeserializeProperty(pVar, pStrm));
}
+ if(isProp("localvars")) {
+ tokener = json_tokener_new();
+ pMsg->localvars = json_tokener_parse_ex(tokener, (char*)rsCStrGetSzStrNoNULL(pVar->val.pStr),
+ cstrLen(pVar->val.pStr));
+ json_tokener_free(tokener);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
if(isProp("pCSStrucData")) {
MsgSetStructuredData(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr));
reinitVar(pVar);
@@ -2510,9 +2543,9 @@ static uchar *getNOW(eNOWType eNow, struct syslogTime *t)
#undef tmpBUFSIZE /* clean up */
-/* Get a CEE-Property as string value*/
-rsRetVal
-getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed)
+/* Get a JSON-Property as string value (used for various types of JSON-based vars) */
+static rsRetVal
+getJSONPropVal(struct json_object *jroot, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed)
{
uchar *name = NULL;
uchar *leaf;
@@ -2524,14 +2557,14 @@ getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, un
free(*pRes);
*pRes = NULL;
// TODO: mutex?
- if(pM->json == NULL) goto finalize_it;
+ if(jroot == NULL) goto finalize_it;
if(!es_strbufcmp(propName, (uchar*)"!", 1)) {
- field = pM->json;
+ field = jroot;
} else {
name = (uchar*)es_str2cstr(propName, NULL);
leaf = jsonPathGetLeaf(name, ustrlen(name));
- CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
+ CHKiRet(jsonPathFindParent(jroot, name, leaf, &parent, 1));
field = json_object_object_get(parent, (char*)leaf);
}
if(field != NULL) {
@@ -2550,11 +2583,32 @@ finalize_it:
RETiRet;
}
+rsRetVal
+getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed)
+{
+ return getJSONPropVal(pM->json, propName, pRes, buflen, pbMustBeFreed);
+}
-/* Get a CEE-Property as native json object
- */
rsRetVal
-msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson)
+getLocalVarPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed)
+{
+ return getJSONPropVal(pM->localvars, propName, pRes, buflen, pbMustBeFreed);
+}
+
+rsRetVal
+getGlobalVarPropVal( es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed)
+{
+ DEFiRet;
+ pthread_rwlock_rdlock(&glblVars_rwlock);
+ iRet = getJSONPropVal(global_var_root, propName, pRes, buflen, pbMustBeFreed);
+ pthread_rwlock_unlock(&glblVars_rwlock);
+ RETiRet;
+}
+
+
+/* Get a JSON-based-variable as native json object */
+rsRetVal
+msgGetJSONPropJSON(struct json_object *jroot, es_str_t *propName, struct json_object **pjson)
{
uchar *name = NULL;
uchar *leaf;
@@ -2562,17 +2616,17 @@ msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson)
DEFiRet;
// TODO: mutex?
- if(pM->json == NULL) {
+ if(jroot == NULL) {
ABORT_FINALIZE(RS_RET_NOT_FOUND);
}
if(!es_strbufcmp(propName, (uchar*)"!", 1)) {
- *pjson = pM->json;
+ *pjson = jroot;
FINALIZE;
}
name = (uchar*)es_str2cstr(propName, NULL);
leaf = jsonPathGetLeaf(name, ustrlen(name));
- CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
+ CHKiRet(jsonPathFindParent(jroot, name, leaf, &parent, 1));
*pjson = json_object_object_get(parent, (char*)leaf);
if(*pjson == NULL) {
ABORT_FINALIZE(RS_RET_NOT_FOUND);
@@ -2583,6 +2637,27 @@ finalize_it:
RETiRet;
}
+rsRetVal
+msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson)
+{
+ return msgGetJSONPropJSON(pM->json, propName, pjson);
+}
+
+rsRetVal
+msgGetLocalVarJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson)
+{
+ return msgGetJSONPropJSON(pM->localvars, propName, pjson);
+}
+
+rsRetVal
+msgGetGlobalVarJSON(es_str_t *propName, struct json_object **pjson)
+{
+ DEFiRet;
+ pthread_rwlock_rdlock(&glblVars_rwlock);
+ iRet = msgGetJSONPropJSON(global_var_root, propName, pjson);
+ pthread_rwlock_unlock(&glblVars_rwlock);
+ RETiRet;
+}
/* Encode a JSON value and add it to provided string. Note that
* the string object may be NULL. In this case, it is created
@@ -2981,6 +3056,12 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
case PROP_CEE:
getCEEPropVal(pMsg, propName, &pRes, &bufLen, pbMustBeFreed);
break;
+ case PROP_LOCAL_VAR:
+ getLocalVarPropVal(pMsg, propName, &pRes, &bufLen, pbMustBeFreed);
+ break;
+ case PROP_GLOBAL_VAR:
+ getGlobalVarPropVal(propName, &pRes, &bufLen, pbMustBeFreed);
+ break;
case PROP_SYS_BOM:
if(*pbMustBeFreed == 1)
free(pRes);
@@ -3648,15 +3729,15 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
}
-/* The function returns a cee variable suitable for use with RainerScript.
+/* The function returns a json variable suitable for use with RainerScript.
* Note: caller must free the returned string.
* Note that we need to do a lot of conversions between es_str_t and cstr -- this will go away once
* we have moved larger parts of rsyslog to es_str_t. Acceptable for the moment, especially as we intend
* to rewrite the script engine as well!
* rgerhards, 2010-12-03
*/
-es_str_t*
-msgGetCEEVarNew(msg_t *pMsg, char *name)
+static es_str_t*
+msgGetJSONVarNew(msg_t *pMsg, struct json_object *jroot, char *name)
{
uchar *leaf;
char *val;
@@ -3665,12 +3746,12 @@ msgGetCEEVarNew(msg_t *pMsg, char *name)
ISOBJ_TYPE_assert(pMsg, msg);
- if(pMsg->json == NULL) {
+ if(jroot == NULL) {
estr = es_newStr(1);
goto done;
}
leaf = jsonPathGetLeaf((uchar*)name, strlen(name));
- if(jsonPathFindParent(pMsg, (uchar*)name, leaf, &parent, 1) != RS_RET_OK) {
+ if(jsonPathFindParent(jroot, (uchar*)name, leaf, &parent, 1) != RS_RET_OK) {
estr = es_newStr(1);
goto done;
}
@@ -3681,6 +3762,17 @@ done:
return estr;
}
+es_str_t*
+msgGetCEEVarNew(msg_t *pMsg, char *name)
+{
+ return msgGetJSONVarNew(pMsg, pMsg->json, name);
+}
+
+es_str_t*
+msgGetLocalVarNew(msg_t *pMsg, char *name)
+{
+ return msgGetJSONVarNew(pMsg, pMsg->localvars, name);
+}
/* Return an es_str_t for given message property.
*/
@@ -3692,13 +3784,16 @@ msgGetMsgVarNew(msg_t *pThis, uchar *name)
propid_t propid;
unsigned short bMustBeFreed = 0;
es_str_t *estr;
+ es_str_t *propName;
ISOBJ_TYPE_assert(pThis, msg);
/* always call MsgGetProp() without a template specifier */
/* TODO: optimize propNameToID() call -- rgerhards, 2009-06-26 */
propNameStrToID(name, &propid);
- pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, NULL, &propLen, &bMustBeFreed, NULL);
+ propName = es_newStrFromCStr((char*)name+1, ustrlen(name)-1); // TODO: optimize!
+ pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, propName, &propLen, &bMustBeFreed, NULL);
+ es_deleteStr(propName);
estr = es_newStrFromCStr((char*)pszProp, propLen);
if(bMustBeFreed)
@@ -3809,16 +3904,22 @@ static uchar *
jsonPathGetLeaf(uchar *name, int lenName)
{
int i;
- for(i = lenName ; name[i] != '!' && i >= 0 ; --i)
- /* just skip */;
- if(name[i] == '!')
+ for(i = lenName ; i >= 0 ; --i)
+ if(i == 0) {
+ if(name[0] == '!' || name[0] == '.' || name[0] == '/')
+ break;
+ } else {
+ if(name[i] == '!')
+ break;
+ }
+ if(name[i] == '!' || name[i] == '.' || name[i] == '/')
++i;
return name + i;
}
static rsRetVal
-jsonPathFindNext(struct json_object *root, uchar **name, uchar *leaf,
+jsonPathFindNext(struct json_object *root, uchar *namestart, uchar **name, uchar *leaf,
struct json_object **found, int bCreate)
{
uchar namebuf[1024];
@@ -3827,9 +3928,9 @@ jsonPathFindNext(struct json_object *root, uchar **name, uchar *leaf,
uchar *p = *name;
DEFiRet;
- if(*p == '!')
+ if(*p == '!' || (*name == namestart && (*p == '.' || *p == '/')))
++p;
- for(i = 0 ; *p && *p != '!' && p != leaf && i < sizeof(namebuf)-1 ; ++i, ++p)
+ for(i = 0 ; *p && !(p == namestart && (*p == '.' || *p == '/')) && *p != '!' && p != leaf && i < sizeof(namebuf)-1 ; ++i, ++p)
namebuf[i] = *p;
if(i > 0) {
namebuf[i] = '\0';
@@ -3853,12 +3954,14 @@ finalize_it:
}
static rsRetVal
-jsonPathFindParent(msg_t *pM, uchar *name, uchar *leaf, struct json_object **parent, int bCreate)
+jsonPathFindParent(struct json_object *jroot, uchar *name, uchar *leaf, struct json_object **parent, int bCreate)
{
+ uchar *namestart;
DEFiRet;
- *parent = pM->json;
+ namestart = name;
+ *parent = jroot;
while(name < leaf-1) {
- jsonPathFindNext(*parent, &name, leaf, parent, bCreate);
+ jsonPathFindNext(*parent, namestart, &name, leaf, parent, bCreate);
}
RETiRet;
}
@@ -3885,7 +3988,7 @@ DBGPRINTF("AAAA jsonMerge adds '%s'\n", it.key);
/* find a JSON structure element (field or container doesn't matter). */
rsRetVal
-jsonFind(msg_t *pM, es_str_t *propName, struct json_object **jsonres)
+jsonFind(struct json_object *jroot, es_str_t *propName, struct json_object **jsonres)
{
uchar *name = NULL;
uchar *leaf;
@@ -3893,17 +3996,17 @@ jsonFind(msg_t *pM, es_str_t *propName, struct json_object **jsonres)
struct json_object *field;
DEFiRet;
- if(pM->json == NULL) {
+ if(jroot == NULL) {
field = NULL;
goto finalize_it;
}
if(!es_strbufcmp(propName, (uchar*)"!", 1)) {
- field = pM->json;
+ field = jroot;
} else {
name = (uchar*)es_str2cstr(propName, NULL);
leaf = jsonPathGetLeaf(name, ustrlen(name));
- CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 0));
+ CHKiRet(jsonPathFindParent(jroot, name, leaf, &parent, 0));
field = json_object_object_get(parent, (char*)leaf);
}
*jsonres = field;
@@ -3913,8 +4016,8 @@ finalize_it:
RETiRet;
}
-rsRetVal
-msgAddJSON(msg_t *pM, uchar *name, struct json_object *json)
+static rsRetVal
+msgAddJSONObj(msg_t *pM, uchar *name, struct json_object *json, struct json_object **pjroot)
{
/* TODO: error checks! This is a quick&dirty PoC! */
struct json_object *parent, *leafnode;
@@ -3922,24 +4025,24 @@ msgAddJSON(msg_t *pM, uchar *name, struct json_object *json)
DEFiRet;
MsgLock(pM);
- if(name[0] == '!' && name[1] == '\0') {
- if(pM->json == NULL)
- pM->json = json;
+ if((name[0] == '!' || name[0] == '.' || name[0] == '/') && name[1] == '\0') {
+ if(*pjroot == NULL)
+ *pjroot = json;
else
- CHKiRet(jsonMerge(pM->json, json));
+ CHKiRet(jsonMerge(*pjroot, json));
} else {
- if(pM->json == NULL) {
+ if(*pjroot == NULL) {
/* now we need a root obj */
- pM->json = json_object_new_object();
+ *pjroot = json_object_new_object();
}
leaf = jsonPathGetLeaf(name, ustrlen(name));
- CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
+ CHKiRet(jsonPathFindParent(*pjroot, name, leaf, &parent, 1));
leafnode = json_object_object_get(parent, (char*)leaf);
if(leafnode == NULL) {
json_object_object_add(parent, (char*)leaf, json);
} else {
if(json_object_get_type(json) == json_type_object) {
- CHKiRet(jsonMerge(pM->json, json));
+ CHKiRet(jsonMerge(*pjroot, json));
} else {
//dbgprintf("AAAA: leafnode already exists, type is %d, update with %d\n", (int)json_object_get_type(leafnode), (int)json_object_get_type(json));
/* TODO: improve the code below, however, the current
@@ -3970,7 +4073,12 @@ finalize_it:
}
rsRetVal
-msgDelJSON(msg_t *pM, uchar *name)
+msgAddJSON(msg_t *pM, uchar *name, struct json_object *json) {
+ return msgAddJSONObj(pM, name, json, &pM->json);
+}
+
+rsRetVal
+msgDelJSONVar(msg_t *pM, struct json_object **jroot, uchar *name)
{
struct json_object *parent, *leafnode;
uchar *leaf;
@@ -3978,20 +4086,20 @@ msgDelJSON(msg_t *pM, uchar *name)
dbgprintf("AAAA: unset variable '%s'\n", name);
MsgLock(pM);
- if(name[0] == '!' && name[1] == '\0') {
+ if((name[0] == '!' || name[0] == '.' || name[0] == '/') && name[1] == '\0') {
/* strange, but I think we should permit this. After all,
* we trust rsyslog.conf to be written by the admin.
*/
DBGPRINTF("unsetting JSON root object\n");
- json_object_put(pM->json);
- pM->json = NULL;
+ json_object_put(*jroot);
+ *jroot = NULL;
} else {
- if(pM->json == NULL) {
+ if(*jroot == NULL) {
/* now we need a root obj */
- pM->json = json_object_new_object();
+ *jroot = json_object_new_object();
}
leaf = jsonPathGetLeaf(name, ustrlen(name));
- CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
+ CHKiRet(jsonPathFindParent(*jroot, name, leaf, &parent, 1));
leafnode = json_object_object_get(parent, (char*)leaf);
DBGPRINTF("AAAA: unset found JSON value path '%s', " "leaf '%s', leafnode %p\n", name, leaf, leafnode);
if(leafnode == NULL) {
@@ -4010,6 +4118,12 @@ finalize_it:
RETiRet;
}
+rsRetVal
+msgDelJSON(msg_t *pM, uchar *name)
+{
+ return msgDelJSONVar(pM, &pM->json, name);
+}
+
static struct json_object *
jsonDeepCopy(struct json_object *src)
{
@@ -4079,7 +4193,16 @@ msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *v)
v->datatype);
ABORT_FINALIZE(RS_RET_ERR);
}
- msgAddJSON(pMsg, varname+1, json);
+ /* we always know strlen(varname) > 2 */
+ if(varname[1] == '!')
+ msgAddJSONObj(pMsg, varname+1, json, &pMsg->json);
+ else if(varname[1] == '.')
+ msgAddJSONObj(pMsg, varname+1, json, &pMsg->localvars);
+ else { /* global - '/' */
+ pthread_rwlock_wrlock(&glblVars_rwlock);
+ msgAddJSONObj(pMsg, varname+1, json, &global_var_root);
+ pthread_rwlock_unlock(&glblVars_rwlock);
+ }
finalize_it:
RETiRet;
}
@@ -4092,6 +4215,8 @@ rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
* rgerhards, 2008-01-04
*/
BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE)
+ pthread_rwlock_init(&glblVars_rwlock, NULL);
+
/* request objects we use */
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
diff --git a/runtime/msg.h b/runtime/msg.h
index 6faf066a..6b85042d 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -108,6 +108,7 @@ struct msg {
struct syslogTime tRcvdAt;/* time the message entered this program */
struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */
struct json_object *json;
+ struct json_object *localvars;
/* some fixed-size buffers to save malloc()/free() for frequently used fields (from the default templates) */
uchar szRawMsg[CONF_RAWMSG_BUFSIZE]; /* most messages are small, and these are stored here (without malloc/free!) */
uchar szHOSTNAME[CONF_HOSTNAME_BUFSIZE];
@@ -186,6 +187,7 @@ char *getPRI(msg_t *pMsg);
void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen);
rsRetVal msgGetCEEVar(msg_t *pThis, cstr_t *propName, var_t **ppVar);
es_str_t* msgGetCEEVarNew(msg_t *pMsg, char *name);
+es_str_t* msgGetLocalVarNew(msg_t *pMsg, char *name);
rsRetVal msgAddJSON(msg_t *pM, uchar *name, struct json_object *json);
rsRetVal getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed);
rsRetVal MsgGetSeverity(msg_t *pThis, int *piSeverity);
@@ -206,9 +208,12 @@ uchar *getRcvFrom(msg_t *pM);
rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID);
uchar *propIDToName(propid_t propID);
rsRetVal msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson);
+rsRetVal getGlobalVarPropVal( es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed);
+rsRetVal msgGetLocalVarJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson);
+rsRetVal msgGetGlobalVarJSON(es_str_t *propName, struct json_object **pjson);
rsRetVal msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *var);
rsRetVal msgDelJSON(msg_t *pMsg, uchar *varname);
-rsRetVal jsonFind(msg_t *pM, es_str_t *propName, struct json_object **jsonres);
+rsRetVal jsonFind(struct json_object *jroot, es_str_t *propName, struct json_object **jsonres);
static inline rsRetVal
msgUnsetJSON(msg_t *pMsg, uchar *varname) {
diff --git a/runtime/net.c b/runtime/net.c
index 13391cc0..7c180b19 100644
--- a/runtime/net.c
+++ b/runtime/net.c
@@ -1189,12 +1189,16 @@ void closeUDPListenSockets(int *pSockArr)
* hostname and/or pszPort may be NULL, but not both!
* bIsServer indicates if a server socket should be created
* 1 - server, 0 - client
+ * param rcvbuf indicates desired rcvbuf size; 0 means OS default
*/
-int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer)
+int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbuf)
{
struct addrinfo hints, *res, *r;
int error, maxs, *s, *socks, on = 1;
int sockflags;
+ int actrcvbuf;
+ socklen_t optlen;
+ char errStr[1024];
assert(!((pszPort == NULL) && (hostname == NULL)));
memset(&hints, 0, sizeof(hints));
@@ -1297,6 +1301,35 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer)
continue;
}
+ if(rcvbuf != 0) {
+# if defined(SO_RCVBUFFORCE)
+ if(setsockopt(*s, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, sizeof(rcvbuf)) < 0)
+# endif
+ {
+ /* if we fail, try to do it the regular way. Experiments show that at
+ * least some platforms do not return an error here, but silently set
+ * it to the max permitted value. So we do our error check a bit
+ * differently by querying the size below.
+ */
+ setsockopt(*s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+ }
+ }
+
+ if(Debug || rcvbuf != 0) {
+ optlen = sizeof(actrcvbuf);
+ if(getsockopt(*s, SOL_SOCKET, SO_RCVBUF, &actrcvbuf, &optlen) == 0) {
+ dbgprintf("socket %d, actual rcvbuf size %d\n", *s, actrcvbuf);
+ if(rcvbuf != 0 && actrcvbuf/2 != rcvbuf) {
+ errmsg.LogError(errno, NO_ERRCODE,
+ "cannot set rcvbuf size %d for socket %d, value now is %d",
+ rcvbuf, *s, actrcvbuf/2);
+ }
+ } else {
+ dbgprintf("could not obtain rcvbuf size for socket %d: %s\n",
+ *s, rs_strerror_r(errno, errStr, sizeof(errStr)));
+ }
+ }
+
if(bIsServer) {
/* rgerhards, 2007-06-22: if we run on a kernel that does not support
* the IPV6_V6ONLY socket option, we need to use a work-around. On such
diff --git a/runtime/net.h b/runtime/net.h
index b196116b..d7a7b512 100644
--- a/runtime/net.h
+++ b/runtime/net.h
@@ -137,7 +137,7 @@ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */
void (*PrintAllowedSenders)(int iListToPrint);
void (*clearAllowedSenders)(uchar*);
void (*debugListenInfo)(int fd, char *type);
- int *(*create_udp_socket)(uchar *hostname, uchar *LogPort, int bIsServer);
+ int *(*create_udp_socket)(uchar *hostname, uchar *LogPort, int bIsServer, int rcvbuf);
void (*closeUDPListenSockets)(int *finet);
int (*isAllowedSender)(uchar *pszType, struct sockaddr *pFrom, const char *pszFromHost); /* deprecated! */
rsRetVal (*getLocalHostname)(uchar**);
diff --git a/runtime/queue.c b/runtime/queue.c
index 935a8106..5666c465 100644
--- a/runtime/queue.c
+++ b/runtime/queue.c
@@ -12,7 +12,7 @@
* function names - this makes it really hard to read and does not provide much
* benefit, at least I (now) think so...
*
- * Copyright 2008-2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008-2013 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -118,6 +118,7 @@ static struct cnfparamdescr cnfpdescr[] = {
{ "queue.dequeueslowdown", eCmdHdlrInt, 0 },
{ "queue.dequeuetimebegin", eCmdHdlrInt, 0 },
{ "queue.dequeuetimeend", eCmdHdlrInt, 0 },
+ { "queue.cry.provider", eCmdHdlrGetWord, 0 }
};
static struct cnfparamblk pblk =
{ CNFPARAMBLK_VERSION,
@@ -776,11 +777,19 @@ qqueueTryLoadPersistedInfo(qqueue_t *pThis)
(rsRetVal(*)(obj_t*,void*))qqueueLoadPersStrmInfoFixup, pThis));
CHKiRet(obj.Deserialize(&pThis->tVars.disk.pReadDel, (uchar*) "strm", psQIF,
(rsRetVal(*)(obj_t*,void*))qqueueLoadPersStrmInfoFixup, pThis));
-
/* create a duplicate for the read "pointer". */
CHKiRet(strm.Dup(pThis->tVars.disk.pReadDel, &pThis->tVars.disk.pReadDeq));
CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDeq, 0)); /* deq must NOT delete the files! */
CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDeq));
+ /* if we use a crypto provider, we need to amend the objects with it's info */
+ if(pThis->useCryprov) {
+ CHKiRet(strm.Setcryprov(pThis->tVars.disk.pWrite, &pThis->cryprov));
+ CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pWrite, pThis->cryprovData));
+ CHKiRet(strm.Setcryprov(pThis->tVars.disk.pReadDeq, &pThis->cryprov));
+ CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pReadDeq, pThis->cryprovData));
+ CHKiRet(strm.Setcryprov(pThis->tVars.disk.pReadDel, &pThis->cryprov));
+ CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pReadDel, pThis->cryprovData));
+ }
CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pWrite));
CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pReadDel));
@@ -834,6 +843,10 @@ static rsRetVal qConstructDisk(qqueue_t *pThis)
CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pWrite, 10000000));
CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE));
CHKiRet(strm.SetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR));
+ if(pThis->useCryprov) {
+ CHKiRet(strm.Setcryprov(pThis->tVars.disk.pWrite, &pThis->cryprov));
+ CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pWrite, pThis->cryprovData));
+ }
CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pWrite));
CHKiRet(strm.Construct(&pThis->tVars.disk.pReadDeq));
@@ -842,6 +855,10 @@ static rsRetVal qConstructDisk(qqueue_t *pThis)
CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pReadDeq, 10000000));
CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pReadDeq, STREAMMODE_READ));
CHKiRet(strm.SetsType(pThis->tVars.disk.pReadDeq, STREAMTYPE_FILE_CIRCULAR));
+ if(pThis->useCryprov) {
+ CHKiRet(strm.Setcryprov(pThis->tVars.disk.pReadDeq, &pThis->cryprov));
+ CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pReadDeq, pThis->cryprovData));
+ }
CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDeq));
CHKiRet(strm.Construct(&pThis->tVars.disk.pReadDel));
@@ -851,6 +868,10 @@ static rsRetVal qConstructDisk(qqueue_t *pThis)
CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pReadDel, 10000000));
CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pReadDel, STREAMMODE_READ));
CHKiRet(strm.SetsType(pThis->tVars.disk.pReadDel, STREAMTYPE_FILE_CIRCULAR));
+ if(pThis->useCryprov) {
+ CHKiRet(strm.Setcryprov(pThis->tVars.disk.pReadDel, &pThis->cryprov));
+ CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pReadDel, pThis->cryprovData));
+ }
CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDel));
CHKiRet(strm.SetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix));
@@ -1321,6 +1342,7 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread
pThis->iMaxFileSize = 1024 * 1024; /* default is 1 MiB */
pThis->iQueueSize = 0;
pThis->nLogDeq = 0;
+ pThis->useCryprov = 0;
pThis->iMaxQueueSize = iMaxQueueSize;
pThis->pConsumer = pConsumer;
pThis->iNumWorkerThreads = iWorkerThreads;
@@ -2426,6 +2448,13 @@ CODESTARTobjDestruct(qqueue)
free(pThis->pszFilePrefix);
free(pThis->pszSpoolDir);
+ if(pThis->useCryprov) {
+ pThis->cryprov.Destruct(&pThis->cryprovData);
+ obj.ReleaseObj(__FILE__, pThis->cryprovNameFull+2, pThis->cryprovNameFull,
+ (void*) &pThis->cryprov);
+ free(pThis->cryprovName);
+ free(pThis->cryprovNameFull);
+ }
/* some queues do not provide stats and thus have no statsobj! */
if(pThis->statsobj != NULL)
@@ -2711,27 +2740,67 @@ finalize_it:
}
-/* take v6 config list and extract the queue params out of it. Hand the
- * param values back to the caller. Caller is responsible for destructing
- * them when no longer needed. Caller can use this param block to configure
- * all parameters for a newly created queue with one call to qqueueSetParams().
- * rgerhards, 2011-07-22
+/* are any queue params set at all? 1 - yes, 0 - no
+ * We need to evaluate the param block for this function, which is somewhat
+ * inefficient. HOWEVER, this is only done during config load, so we really
+ * don't care... -- rgerhards, 2013-05-10
*/
-rsRetVal
-qqueueDoCnfParams(struct nvlst *lst, struct cnfparamvals **ppvals)
+int
+queueCnfParamsSet(struct nvlst *lst)
{
- *ppvals = nvlstGetParams(lst, &pblk, NULL);
- return RS_RET_OK;
+ int r;
+ struct cnfparamvals *pvals;
+
+ pvals = nvlstGetParams(lst, &pblk, NULL);
+ r = cnfparamvalsIsSet(&pblk, pvals);
+ cnfparamvalsDestruct(pvals, &pblk);
+ return r;
}
-/* are any queue params set at all? 1 - yes, 0 - no */
-int
-queueCnfParamsSet(struct cnfparamvals *pvals)
+static inline rsRetVal
+initCryprov(qqueue_t *pThis, struct nvlst *lst)
{
- return cnfparamvalsIsSet(&pblk, pvals);
-}
+ uchar szDrvrName[1024];
+ DEFiRet;
+ if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pThis->cryprovName)
+ == sizeof(szDrvrName)) {
+ errmsg.LogError(0, RS_RET_ERR, "queue: crypto provider "
+ "name is too long: '%s' - encryption disabled",
+ pThis->cryprovName);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ pThis->cryprovNameFull = ustrdup(szDrvrName);
+
+ pThis->cryprov.ifVersion = cryprovCURR_IF_VERSION;
+ /* The pDrvrName+2 below is a hack to obtain the object name. It
+ * safes us to have yet another variable with the name without "lm" in
+ * front of it. If we change the module load interface, we may re-think
+ * about this hack, but for the time being it is efficient and clean enough.
+ */
+ if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pThis->cryprov)
+ != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_LOAD_ERROR, "queue: could not load "
+ "crypto provider '%s' - encryption disabled",
+ szDrvrName);
+ ABORT_FINALIZE(RS_RET_CRYPROV_ERR);
+ }
+
+ if(pThis->cryprov.Construct(&pThis->cryprovData) != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_CRYPROV_ERR, "queue: error constructing "
+ "crypto provider %s dataset - encryption disabled",
+ szDrvrName);
+ ABORT_FINALIZE(RS_RET_CRYPROV_ERR);
+ }
+ CHKiRet(pThis->cryprov.SetCnfParam(pThis->cryprovData, lst, CRYPROV_PARAMTYPE_DISK));
+
+ dbgprintf("loaded crypto provider %s, data instance at %p\n",
+ szDrvrName, pThis->cryprovData);
+ pThis->useCryprov = 1;
+finalize_it:
+ RETiRet;
+}
/* apply all params from param block to queue. Must be called before
* finalizing. This supports the v6 config system. Defaults were already
@@ -2739,15 +2808,24 @@ queueCnfParamsSet(struct cnfparamvals *pvals)
* function.
*/
rsRetVal
-qqueueApplyCnfParam(qqueue_t *pThis, struct cnfparamvals *pvals)
+qqueueApplyCnfParam(qqueue_t *pThis, struct nvlst *lst)
{
int i;
+ struct cnfparamvals *pvals;
+
+ pvals = nvlstGetParams(lst, &pblk, NULL);
+ if(Debug) {
+ dbgprintf("queue param blk:\n");
+ cnfparamsPrint(&pblk, pvals);
+ }
for(i = 0 ; i < pblk.nParams ; ++i) {
if(!pvals[i].bUsed)
continue;
if(!strcmp(pblk.descr[i].name, "queue.filename")) {
pThis->pszFilePrefix = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
pThis->lenFilePrefix = es_strlen(pvals[i].val.d.estr);
+ } else if(!strcmp(pblk.descr[i].name, "queue.cry.provider")) {
+ pThis->cryprovName = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
} else if(!strcmp(pblk.descr[i].name, "queue.size")) {
pThis->iMaxQueueSize = pvals[i].val.d.n;
} else if(!strcmp(pblk.descr[i].name, "queue.dequeuebatchsize")) {
@@ -2799,12 +2877,27 @@ qqueueApplyCnfParam(qqueue_t *pThis, struct cnfparamvals *pvals)
"param '%s'\n", pblk.descr[i].name);
}
}
- if(pThis->qType == QUEUETYPE_DISK && pThis->pszFilePrefix == NULL) {
- errmsg.LogError(0, RS_RET_QUEUE_DISK_NO_FN, "error on queue '%s', disk mode selected, but "
- "no queue file name given; queue type changed to 'linkedList'",
+ if(pThis->qType == QUEUETYPE_DISK) {
+ if(pThis->pszFilePrefix == NULL) {
+ errmsg.LogError(0, RS_RET_QUEUE_DISK_NO_FN, "error on queue '%s', disk mode selected, but "
+ "no queue file name given; queue type changed to 'linkedList'",
+ obj.GetName((obj_t*) pThis));
+ pThis->qType = QUEUETYPE_LINKEDLIST;
+ }
+ }
+
+ if(pThis->pszFilePrefix == NULL && pThis->cryprovName != NULL) {
+ errmsg.LogError(0, RS_RET_QUEUE_CRY_DISK_ONLY, "error on queue '%s', crypto provider can "
+ "only be set for disk or disk assisted queue - ignored",
obj.GetName((obj_t*) pThis));
- pThis->qType = QUEUETYPE_LINKEDLIST;
+ free(pThis->cryprovName);
+ pThis->cryprovName = NULL;
+ }
+
+ if(pThis->cryprovName != NULL) {
+ initCryprov(pThis, lst);
}
+
cnfparamvalsDestruct(pvals, &pblk);
return RS_RET_OK;
}
diff --git a/runtime/queue.h b/runtime/queue.h
index 886fac8d..844523ad 100644
--- a/runtime/queue.h
+++ b/runtime/queue.h
@@ -30,6 +30,7 @@
#include "batch.h"
#include "stream.h"
#include "statsobj.h"
+#include "cryprov.h"
/* support for the toDelete list */
typedef struct toDeleteLst_s toDeleteLst_t;
@@ -168,6 +169,11 @@ struct queue_s {
strm_t *pReadDel; /* current file for deleting */
} disk;
} tVars;
+ sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */
+ uchar *cryprovName; /* crypto provider to use */
+ cryprov_if_t cryprov; /* ptr to crypto provider interface */
+ void *cryprovData; /* opaque data ptr for provider use */
+ uchar *cryprovNameFull;/* full internal crypto provider name */
DEF_ATOMIC_HELPER_MUT(mutQueueSize);
DEF_ATOMIC_HELPER_MUT(mutLogDeq);
/* for statistics subsystem */
@@ -197,9 +203,8 @@ rsRetVal qqueueSetFilePrefix(qqueue_t *pThis, uchar *pszPrefix, size_t iLenPrefi
rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThreads,
int iMaxQueueSize, rsRetVal (*pConsumer)(void*,batch_t*, int*));
rsRetVal qqueueEnqObjDirectBatch(qqueue_t *pThis, batch_t *pBatch);
-rsRetVal qqueueDoCnfParams(struct nvlst *lst, struct cnfparamvals **ppvals);
-int queueCnfParamsSet(struct cnfparamvals *pvals);
-rsRetVal qqueueApplyCnfParam(qqueue_t *pThis, struct cnfparamvals *pvals);
+int queueCnfParamsSet(struct nvlst *lst);
+rsRetVal qqueueApplyCnfParam(qqueue_t *pThis, struct nvlst *lst);
void qqueueSetDefaultsRulesetQueue(qqueue_t *pThis);
void qqueueSetDefaultsActionQueue(qqueue_t *pThis);
void qqueueDbgPrint(qqueue_t *pThis);
diff --git a/runtime/rsconf.c b/runtime/rsconf.c
index d8b81f1b..1c0e33c3 100644
--- a/runtime/rsconf.c
+++ b/runtime/rsconf.c
@@ -83,7 +83,7 @@ rsconf_t *runConf = NULL;/* the currently running config */
rsconf_t *loadConf = NULL;/* the config currently being loaded (no concurrent config load supported!) */
/* hardcoded standard templates (used for defaults) */
-static uchar template_DebugFormat[] = "\"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%', HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\ninputname: %inputname% rawmsg: '%rawmsg%'\n\n\"";
+static uchar template_DebugFormat[] = "\"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%', HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\ninputname: %inputname% rawmsg: '%rawmsg%'\n$!:%$!%\n$.:%$.%\n$/:%$/%\n\n\"";
static uchar template_SyslogProtocol23Format[] = "\"<%PRI%>1 %TIMESTAMP:::date-rfc3339% %HOSTNAME% %APP-NAME% %PROCID% %MSGID% %STRUCTURED-DATA% %msg%\n\"";
static uchar template_TraditionalFileFormat[] = "=RSYSLOG_TraditionalFileFormat";
static uchar template_FileFormat[] = "=RSYSLOG_FileFormat";
@@ -399,6 +399,7 @@ yyerror(char *s)
}
void cnfDoObj(struct cnfobj *o)
{
+ int bDestructObj = 1;
int bChkUnuse = 1;
dbgprintf("cnf:global:obj: ");
@@ -407,6 +408,10 @@ void cnfDoObj(struct cnfobj *o)
case CNFOBJ_GLOBAL:
glblProcessCnf(o);
break;
+ case CNFOBJ_MAINQ:
+ glblProcessMainQCnf(o);
+ bDestructObj = 0;
+ break;
case CNFOBJ_MODULE:
modulesProcessCnf(o);
break;
@@ -430,9 +435,11 @@ void cnfDoObj(struct cnfobj *o)
o->objType);
break;
}
- if(bChkUnuse)
- nvlstChkUnused(o->nvlst);
- cnfobjDestruct(o);
+ if(bDestructObj) {
+ if(bChkUnuse)
+ nvlstChkUnused(o->nvlst);
+ cnfobjDestruct(o);
+ }
}
void cnfDoScript(struct cnfstmt *script)
@@ -479,10 +486,10 @@ cnfGetVar(char *name, void *usrptr)
else if(name[1] == '!')
estr = msgGetCEEVarNew((msg_t*) usrptr, name+2);
else
- estr = msgGetMsgVarNew((msg_t*) usrptr, (uchar*)name+1);
+ estr = msgGetMsgVarNew((msg_t*) usrptr, (uchar*)name);
} else { /* if this happens, we have a program logic error */
estr = es_newStrFromCStr("err: var must start with $",
- strlen("err: var must start with $"));
+ sizeof("err: var must start with $")-1);
}
if(Debug) {
char *s;
@@ -757,9 +764,14 @@ startInputModules(void)
static inline rsRetVal
activateMainQueue()
{
+ struct cnfobj *mainqCnfObj;
DEFiRet;
+
+ mainqCnfObj = glbl.GetmainqCnfObj();
+ DBGPRINTF("activateMainQueue: mainq cnf obj ptr is %p\n", mainqCnfObj);
/* create message queue */
- CHKiRet_Hdlr(createMainQueue(&pMsgQueue, UCHAR_CONSTANT("main Q"), NULL)) {
+ CHKiRet_Hdlr(createMainQueue(&pMsgQueue, UCHAR_CONSTANT("main Q"),
+ (mainqCnfObj == NULL) ? NULL : mainqCnfObj->nvlst)) {
/* no queue is fatal, we need to give up in that case... */
fprintf(stderr, "fatal error %d: could not create message queue - rsyslogd can not run!\n", iRet);
FINALIZE;
@@ -768,6 +780,7 @@ activateMainQueue()
bHaveMainQueue = (ourConf->globals.mainQ.MainMsgQueType == QUEUETYPE_DIRECT) ? 0 : 1;
DBGPRINTF("Main processing queue is initialized and running\n");
finalize_it:
+ glblDestructMainqCnfObj();
RETiRet;
}
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 47b34783..e51f11bd 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -140,11 +140,13 @@ typedef uintTiny propid_t;
#define PROP_SYS_QHOUR 156
#define PROP_SYS_MINUTE 157
#define PROP_SYS_MYHOSTNAME 158
-#define PROP_CEE 200
-#define PROP_CEE_ALL_JSON 201
#define PROP_SYS_BOM 159
#define PROP_SYS_UPTIME 160
-#define PROP_UUID 161
+#define PROP_UUID 161
+#define PROP_CEE 200
+#define PROP_CEE_ALL_JSON 201
+#define PROP_LOCAL_VAR 202
+#define PROP_GLOBAL_VAR 203
/* The error codes below are orginally "borrowed" from
@@ -413,6 +415,10 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_CRY_INVLD_ALGO = -2326,/**< user specified invalid (unkonwn) crypto algorithm */
RS_RET_CRY_INVLD_MODE = -2327,/**< user specified invalid (unkonwn) crypto mode */
RS_RET_QUEUE_DISK_NO_FN = -2328,/**< disk queue configured, but filename not set */
+ /* up to 2350 reserved for 7.4 */
+ RS_RET_QUEUE_CRY_DISK_ONLY = -2351,/**< crypto provider only supported for disk-associated queues */
+ RS_RET_NO_DATA = -2352,/**< file has no data; more a state than a real error */
+ RS_RET_RELP_AUTH_FAIL = -2353,/**< RELP peer authentication failed */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/runtime/ruleset.c b/runtime/ruleset.c
index 5bf7ac03..dae5bbaa 100644
--- a/runtime/ruleset.c
+++ b/runtime/ruleset.c
@@ -447,6 +447,16 @@ evalPROPFILT(struct cnfstmt *stmt, msg_t *pMsg)
DBGPRINTF("Filter: check for CEE property '%s' (value '%s') ",
cstr, pszPropVal);
free(cstr);
+ } else if(stmt->d.s_propfilt.propID == PROP_LOCAL_VAR) {
+ cstr = es_str2cstr(stmt->d.s_propfilt.propName, NULL);
+ DBGPRINTF("Filter: check for local var '%s' (value '%s') ",
+ cstr, pszPropVal);
+ free(cstr);
+ } else if(stmt->d.s_propfilt.propID == PROP_GLOBAL_VAR) {
+ cstr = es_str2cstr(stmt->d.s_propfilt.propName, NULL);
+ DBGPRINTF("Filter: check for global var '%s' (value '%s') ",
+ cstr, pszPropVal);
+ free(cstr);
} else {
DBGPRINTF("Filter: check for property '%s' (value '%s') ",
propIDToName(stmt->d.s_propfilt.propID), pszPropVal);
@@ -928,7 +938,6 @@ rsRetVal
rulesetProcessCnf(struct cnfobj *o)
{
struct cnfparamvals *pvals;
- struct cnfparamvals *queueParams;
rsRetVal localRet;
uchar *rsName = NULL;
uchar *parserName;
@@ -974,11 +983,10 @@ rulesetProcessCnf(struct cnfobj *o)
}
/* pick up ruleset queue parameters */
- qqueueDoCnfParams(o->nvlst, &queueParams);
- if(queueCnfParamsSet(queueParams)) {
+ if(queueCnfParamsSet(o->nvlst)) {
rsname = (pRuleset->pszName == NULL) ? (uchar*) "[ruleset]" : pRuleset->pszName;
DBGPRINTF("adding a ruleset-specific \"main\" queue for ruleset '%s'\n", rsname);
- CHKiRet(createMainQueue(&pRuleset->pQueue, rsname, queueParams));
+ CHKiRet(createMainQueue(&pRuleset->pQueue, rsname, o->nvlst));
}
finalize_it:
diff --git a/runtime/srutils.c b/runtime/srutils.c
index 6a509b4a..8eb2459c 100644
--- a/runtime/srutils.c
+++ b/runtime/srutils.c
@@ -86,6 +86,7 @@ syslogName_t syslogFacNames[] = {
{"mark", LOG_MARK}, /* INTERNAL */
{"news", LOG_NEWS},
{"security", LOG_AUTH}, /* DEPRECATED */
+ {"bsd_security", (13<<3) }, /* BSD-specific, unfortunatly with duplicate name... */
{"syslog", LOG_SYSLOG},
{"user", LOG_USER},
{"uucp", LOG_UUCP},
@@ -95,6 +96,7 @@ syslogName_t syslogFacNames[] = {
#if defined(LOG_AUDIT)
{"audit", LOG_AUDIT},
#endif
+ {"console", (14 << 3)}, /* BSD-specific priority */
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
diff --git a/runtime/stream.c b/runtime/stream.c
index 94fc0ca7..444a33ff 100644
--- a/runtime/stream.c
+++ b/runtime/stream.c
@@ -256,7 +256,9 @@ doPhysOpen(strm_t *pThis)
if(pThis->cryprov != NULL) {
CHKiRet(pThis->cryprov->OnFileOpen(pThis->cryprovData,
- pThis->pszCurrFName, &pThis->cryprovFileData));
+ pThis->pszCurrFName, &pThis->cryprovFileData,
+ (pThis->tOperationsMode == STREAMMODE_READ) ? 'r' : 'w'));
+ pThis->cryprov->SetDeleteOnClose(pThis->cryprovFileData, pThis->bDeleteOnClose);
}
finalize_it:
RETiRet;
@@ -404,6 +406,12 @@ static rsRetVal strmCloseFile(strm_t *pThis)
}
}
+ /* if we have a signature provider, we must make sure that the crypto
+ * state files are opened and proper close processing happens. */
+ if(pThis->cryprov != NULL && pThis->fd == -1) {
+ strmOpenFile(pThis);
+ }
+
/* the file may already be closed (or never have opened), so guard
* against this. -- rgerhards, 2010-03-19
*/
@@ -550,11 +558,14 @@ finalize_it:
* rgerhards, 2008-02-13
*/
static rsRetVal
-strmReadBuf(strm_t *pThis)
+strmReadBuf(strm_t *pThis, int *padBytes)
{
DEFiRet;
int bRun;
long iLenRead;
+ size_t actualDataLen;
+ size_t toRead;
+ ssize_t bytesLeft;
ISOBJ_TYPE_assert(pThis, strm);
/* We need to try read at least twice because we may run into EOF and need to switch files. */
@@ -565,13 +576,35 @@ strmReadBuf(strm_t *pThis)
* rgerhards, 2008-02-13
*/
CHKiRet(strmOpenFile(pThis));
- iLenRead = read(pThis->fd, pThis->pIOBuf, pThis->sIOBufSize);
+ if(pThis->cryprov == NULL) {
+ toRead = pThis->sIOBufSize;
+ } else {
+ CHKiRet(pThis->cryprov->GetBytesLeftInBlock(pThis->cryprovFileData, &bytesLeft));
+ if(bytesLeft == -1 || bytesLeft > (ssize_t) pThis->sIOBufSize) {
+ toRead = pThis->sIOBufSize;
+ } else {
+ toRead = (size_t) bytesLeft;
+ }
+ }
+ iLenRead = read(pThis->fd, pThis->pIOBuf, toRead);
DBGOPRINT((obj_t*) pThis, "file %d read %ld bytes\n", pThis->fd, iLenRead);
+ /* end crypto */
if(iLenRead == 0) {
CHKiRet(strmHandleEOF(pThis));
} else if(iLenRead < 0)
ABORT_FINALIZE(RS_RET_IO_ERROR);
else { /* good read */
+ /* here we place our crypto interface */
+ if(pThis->cryprov != NULL) {
+ actualDataLen = iLenRead;
+ pThis->cryprov->Decrypt(pThis->cryprovFileData, pThis->pIOBuf, &actualDataLen);
+ *padBytes = iLenRead - actualDataLen;
+ iLenRead = actualDataLen;
+ DBGOPRINT((obj_t*) pThis, "encrypted file %d pad bytes %d, actual "
+ "data %ld\n", pThis->fd, *padBytes, iLenRead);
+ } else {
+ *padBytes = 0;
+ }
pThis->iBufPtrMax = iLenRead;
bRun = 0; /* exit loop */
}
@@ -593,6 +626,7 @@ finalize_it:
*/
static rsRetVal strmReadChar(strm_t *pThis, uchar *pC)
{
+ int padBytes = 0; /* in crypto mode, we may have some padding (non-data) bytes */
DEFiRet;
ASSERT(pThis != NULL);
@@ -608,8 +642,9 @@ static rsRetVal strmReadChar(strm_t *pThis, uchar *pC)
/* do we need to obtain a new buffer? */
if(pThis->iBufPtr >= pThis->iBufPtrMax) {
- CHKiRet(strmReadBuf(pThis));
+ CHKiRet(strmReadBuf(pThis, &padBytes));
}
+ pThis->iCurrOffs += padBytes;
/* if we reach this point, we have data available in the buffer */
@@ -1454,6 +1489,8 @@ strmMultiFileSeek(strm_t *pThis, int FNum, off64_t offs, off64_t *bytesDel)
"deleting '%s' (%lld bytes)\n", pThis->iCurrFNum, FNum,
pThis->pszCurrFName, (long long) *bytesDel);
unlink((char*)pThis->pszCurrFName);
+ if(pThis->cryprov != NULL)
+ pThis->cryprov->DeleteStateFiles(pThis->pszCurrFName);
free(pThis->pszCurrFName);
pThis->pszCurrFName = NULL;
pThis->iCurrFNum = FNum;
@@ -1467,17 +1504,31 @@ finalize_it:
}
-
/* seek to current offset. This is primarily a helper to readjust the OS file
* pointer after a strm object has been deserialized.
*/
static rsRetVal strmSeekCurrOffs(strm_t *pThis)
{
+ off64_t targetOffs;
+ uchar c;
DEFiRet;
ISOBJ_TYPE_assert(pThis, strm);
- iRet = strmSeek(pThis, pThis->iCurrOffs);
+ if(pThis->cryprov == NULL || pThis->tOperationsMode != STREAMMODE_READ) {
+ iRet = strmSeek(pThis, pThis->iCurrOffs);
+ FINALIZE;
+ }
+
+ /* As the cryprov may use CBC or similiar things, we need to read skip data */
+ targetOffs = pThis->iCurrOffs;
+ pThis->iCurrOffs = 0;
+ DBGOPRINT((obj_t*) pThis, "encrypted, doing skip read of %lld bytes\n",
+ (long long) targetOffs);
+ while(targetOffs != pThis->iCurrOffs) {
+ CHKiRet(strmReadChar(pThis, &c));
+ }
+finalize_it:
RETiRet;
}
@@ -1604,7 +1655,6 @@ finalize_it:
/* property set methods */
/* simple ones first */
-DEFpropSetMeth(strm, bDeleteOnClose, int)
DEFpropSetMeth(strm, iMaxFileSize, int64)
DEFpropSetMeth(strm, iFileNumDigits, int)
DEFpropSetMeth(strm, tOperationsMode, int)
@@ -1620,6 +1670,15 @@ DEFpropSetMeth(strm, pszSizeLimitCmd, uchar*)
DEFpropSetMeth(strm, cryprov, cryprov_if_t*)
DEFpropSetMeth(strm, cryprovData, void*)
+static rsRetVal strmSetbDeleteOnClose(strm_t *pThis, int val)
+{
+ pThis->bDeleteOnClose = val;
+ if(pThis->cryprov != NULL) {
+ pThis->cryprov->SetDeleteOnClose(pThis->cryprovFileData, pThis->bDeleteOnClose);
+ }
+ return RS_RET_OK;
+}
+
static rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal)
{
pThis->iMaxFiles = iNewVal;