From 3e3b746380c610fff39e16d0a73d4a4f3564af91 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 28 Jan 2013 15:36:04 +0100 Subject: doc: add link for set statement --- doc/rsyslog_conf_basic_structure.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/rsyslog_conf_basic_structure.html b/doc/rsyslog_conf_basic_structure.html index fad1b110..412540e3 100644 --- a/doc/rsyslog_conf_basic_structure.html +++ b/doc/rsyslog_conf_basic_structure.html @@ -49,7 +49,8 @@ after the stop statement are never evaluated.

Data Manipulation Statements

-- cgit v1.2.3 From 1deef18daa1b39035e42c8b1db68286727d8f950 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 29 Jan 2013 15:21:26 +0100 Subject: doc: give quick example of ruleset() {} use --- doc/rsyslog_conf_basic_structure.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/rsyslog_conf_basic_structure.html b/doc/rsyslog_conf_basic_structure.html index 412540e3..00a700d4 100644 --- a/doc/rsyslog_conf_basic_structure.html +++ b/doc/rsyslog_conf_basic_structure.html @@ -81,6 +81,14 @@ a message comes in via that input, the "program" (ruleset) bound to it will be e (but not any other!).

There is detail documentation available for rsyslog rulesets. +

For quick reference, rulesets are defined as follows: +

+ruleset(name="rulesetname") {
+	action(type="omfile" file="/path/to/file")
+	action(type="..." ...)
+	/* and so on... */
+}
+

[manual index] [rsyslog.conf] -- cgit v1.2.3 From 3a62484067d28d6b859b7e4b8d87d6de7b3585f8 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Feb 2013 16:52:45 +0100 Subject: logsig: add pseudocodish C skeleton for signature algo --- runtime/lmgt.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 runtime/lmgt.c diff --git a/runtime/lmgt.c b/runtime/lmgt.c new file mode 100644 index 00000000..15a56cfa --- /dev/null +++ b/runtime/lmgt.c @@ -0,0 +1,124 @@ +/* lmgt.c + * + * Regarding the online algorithm for Merkle tree signing. Expected + * calling sequence is: + * + * sigblkConstruct + * for each signature block: + * sigblkInit + * for each record: + * sigblkAddRecord + * sigblkFinish + * sigblkDestruct + * + * Obviously, the next call after sigblkFinsh must either be to + * sigblkInit or sigblkDestruct (if no more signature blocks are + * to be emitted, e.g. on file close). sigblkDestruct saves state + * information (most importantly last block hash) and sigblkConstruct + * reads (or initilizes if not present) it. + * + * 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 + + +/* Max number of roots inside the forest. This permits blocks of up to + * 2^MAX_ROOTS records. We assume that 64 is sufficient for all use + * cases ;) [and 64 is not really a waste of memory, so we do not even + * try to work with reallocs and such... + */ +#define MAX_ROOTS 64 + +/* context for gt calls. All state data is kept here, this permits + * multiple concurrent callers. + */ +struct gtctx_s { + IV; /* initial value for blinding masks (where to do we get it from?) */ + x_prev = NULL; /* last leaf hash (maybe of previous block) --> preserve on term */ + int8_t nroots; + /* algo engineering: roots structure is split into two arrays + * in order to improve cache hits. + */ + char roots_valid[MAX_ROOTS]; + hash_mem roots_hash[MAX_ROOTS]; + +}; +typedef struct gtctx_s *gtctx; + +void +sigblkInit(gtctx ctx) +{ + init ctx->IV; + if(ctx->x_prev == NULL) + alloc & zero-fill x_prev; + memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); + nroots = 0; +} + + +void +sigblkAddRecord(gtctx ctx, char *rec) +{ + auto x; /* current hash */ + hash_mem m, r, t; + int8_t j; + + m = hash(concat(ctx->x_prev, IV)); + r = hash(canonicalize(rec)); + x = hash(concat(m, r, 0)); /* hash leaf */ + /* persists x here if Merkle tree needs to be persisted! */ + /* add x to the forest as new leaf, update roots list */ + t = x; + for(j = 0 ; j < ctx->nRoots ; ++j) { + if(ctx->roots_valid[j] == 0) { + ctx->roots_hash[j] = t; + ctx->roots_valid[j] = 1; + t = NULL; + } else if(t != NULL) { + t = hash(concat(ctx->roots_hash[j], t, j+1)); /* hash interim node */ + ctx->roots_valid[j] = 0; + } + if(t != NULL) { + ctx->roots_hash[ctx->nroots] = t; + ++ctx->roots_hash; + assert(ctx->roots_hash < MAX_ROOTS); + t = NULL; + } + ctx->x_prev = x; /* single var may be sufficient */ +} + +void +sigblkFinish(gtctx ctx) +{ + hash_mem root; + int8_t j; + + root = NULL; + for(j = 0 ; j < ctx->nRoots ; ++j) { + if(root == NULL) { + root = ctx->roots_hash[j]; + ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + } else if(ctx->roots_valid[j]) { + root = hash(concat(ctx->roots_hash[j], root, j+1)); + ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + } + } + /* persist root value here (callback?) */ +} -- cgit v1.2.3 From 3cc89eb96923cb85654558ce623256f4da76b87f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Feb 2013 18:03:56 +0100 Subject: add test/PoC logsigner tool --- configure.ac | 16 +++++ tools/logsigner.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 tools/logsigner.c diff --git a/configure.ac b/configure.ac index e9fce3f7..3f60a5b5 100644 --- a/configure.ac +++ b/configure.ac @@ -908,6 +908,21 @@ fi AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes) +# GuardTime support +AC_ARG_ENABLE(guardtime, + [AS_HELP_STRING([--enable-guardtime],[Enable GuardTime support @<:@default=no@:>@])], + [case "${enableval}" in + yes) enable_guardtime="yes" ;; + no) enable_guardtime="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-guardtime) ;; + esac], + [enable_guardtime=no] +) +if test "x$enable_guardtime" = "xyes"; then + PKG_CHECK_MODULES(GUARDTIME, gt >= 0.3.1) +fi +AM_CONDITIONAL(ENABLE_GUARDTIME, test x$enable_guardtime = xyes) + # RFC 3195 support AC_ARG_ENABLE(rfc3195, [AS_HELP_STRING([--enable-rfc3195],[Enable RFC3195 support @<:@default=no@:>@])], @@ -1348,6 +1363,7 @@ echo " rsyslogd will be built: $enable_rsyslogd" echo " GUI components will be built: $enable_gui" echo " Unlimited select() support enabled: $enable_unlimited_select" echo " uuid support enabled: $enable_uuid" +echo " GuardTime signature support enabled: $enable_guardtime" echo echo "---{ input plugins }---" echo " Klog functionality enabled: $enable_klog ($os_type)" diff --git a/tools/logsigner.c b/tools/logsigner.c new file mode 100644 index 00000000..2aeb2162 --- /dev/null +++ b/tools/logsigner.c @@ -0,0 +1,173 @@ +/* This is a tool for offline signing logfiles via the guardtime API. + * + * NOTE: this currently is only a PoC and WiP! NOT suitable for + * production use! + * + * Current hardcoded timestamper (use this if you do not have an + * idea of which one else to use): + * http://stamper.guardtime.net/gt-signingservice + * Check the GuardTime website for the URLs of nearest public services. + * + * 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 exprs or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include +#include + + +void +outputhash(GTDataHash *hash) +{ + int i; + for(i = 0 ; i < hash->digest_length ; ++i) + printf("%2.2x", hash->digest[i]); + printf("\n"); +} + +void +gtInit() +{ + int r = GT_OK; + + r = GT_init(); + if(r != GT_OK) { + fprintf(stderr, "GT_init() failed: %d (%s)\n", + r, GT_getErrorString(r)); + goto done; + } + r = GTHTTP_init("rsyslog logsigner", 1); + if(r != GT_OK) { + fprintf(stderr, "GTHTTP_init() failed: %d (%s)\n", + r, GTHTTP_getErrorString(r)); + goto done; + } +done: return; +} + +void +gtExit() +{ + GTHTTP_finalize(); + GT_finalize(); +} + +void +timestampIt(GTDataHash *hash) +{ + int r = GT_OK; + GTTimestamp *timestamp = NULL; + unsigned char *der = NULL; + char *sigFile = "logsigner.TIMESTAMP"; + size_t der_len; + + /* Get the timestamp. */ + r = GTHTTP_createTimestampHash(hash, + "http://stamper.guardtime.net/gt-signingservice", ×tamp); + + if(r != GT_OK) { + fprintf(stderr, "GTHTTP_createTimestampHash() failed: %d (%s)\n", + r, GTHTTP_getErrorString(r)); + goto done; + } + + /* Encode timestamp. */ + r = GTTimestamp_getDEREncoded(timestamp, &der, &der_len); + if(r != GT_OK) { + fprintf(stderr, "GTTimestamp_getDEREncoded() failed: %d (%s)\n", + r, GT_getErrorString(r)); + goto done; + } + + /* Save DER-encoded timestamp to file. */ + r = GT_saveFile(sigFile, der, der_len); + if(r != GT_OK) { + fprintf(stderr, "Cannot save timestamp to file %s: %d (%s)\n", + sigFile, r, GT_getErrorString(r)); + if(r == GT_IO_ERROR) { + fprintf(stderr, "\t%d (%s)\n", errno, strerror(errno)); + } + goto done; + } + printf("Timestamping succeeded!\n"); +done: + GT_free(der); + GTTimestamp_free(timestamp); +} + + +void +sign(const char *buf, size_t len) +{ + int r; + GTDataHash *hash = NULL; + + printf("hash for '%s' is ", buf); + r = GTDataHash_create(GT_HASHALG_SHA256, (const unsigned char*)buf, len, &hash); + if(r != GT_OK) { + fprintf(stderr, "GTTDataHash_create() failed: %d (%s)\n", + r, GT_getErrorString(r)); + goto done; + } + outputhash(hash); + timestampIt(hash); /* of course, this needs to be moved to once at end ;) */ +done: GTDataHash_free(hash); +} + +void +processFile(char *name) +{ + FILE *fp; + size_t len; + char line[64*1024+1]; + + if(!strcmp(name, "-")) + fp = stdin; + else + fp = fopen(name, "r"); + + while(1) { + if(fgets(line, sizeof(line), fp) == NULL) { + perror(name); + break; + } + len = strlen(line); + if(line[len-1] == '\n') { + --len; + line[len] = '\0'; + } + sign(line, len); + } + + if(fp != stdout) + fclose(fp); +} + + +int +main(int argc, char *argv[]) +{ + gtInit(); + processFile("-"); + gtExit(); + return 0; +} -- cgit v1.2.3 From 212af677576238bf87deb1bee3e7bcfb170dc668 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 21 Feb 2013 11:29:22 +0100 Subject: logsigner: sign via merkle tree approach (still PoC, non production) --- runtime/Makefile.am | 11 +- runtime/librsgt.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/lmgt.c | 124 --------------------- tools/Makefile.am | 6 + tools/logsigner.c | 54 ++++----- 5 files changed, 345 insertions(+), 159 deletions(-) create mode 100644 runtime/librsgt.c delete mode 100644 runtime/lmgt.c diff --git a/runtime/Makefile.am b/runtime/Makefile.am index fbc92d9c..0f1b580a 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -1,6 +1,6 @@ sbin_PROGRAMS = man_MANS = -noinst_LTLIBRARIES = librsyslog.la +noinst_LTLIBRARIES = librsyslog.la librsgt.la pkglib_LTLIBRARIES = #pkglib_LTLIBRARIES = librsyslog.la @@ -92,6 +92,7 @@ librsyslog_la_SOURCES = \ ../template.h # the files with ../ we need to work on - so that they either become part of the # runtime or will no longer be needed. -- rgerhards, 2008-06-13 +# if WITH_MODDIRS librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools @@ -172,6 +173,14 @@ lmnsd_gtls_la_LDFLAGS = -module -avoid-version lmnsd_gtls_la_LIBADD = $(GNUTLS_LIBS) endif +# +# support library for guardtime +# +if ENABLE_GUARDTIME + librsgt_la_SOURCES = librsgt.c +endif + + update-systemd: curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c > sd-daemon.c curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h > sd-daemon.h diff --git a/runtime/librsgt.c b/runtime/librsgt.c new file mode 100644 index 00000000..4ad4ec26 --- /dev/null +++ b/runtime/librsgt.c @@ -0,0 +1,309 @@ +/* librsgt.c - rsyslog's guardtime support library + * + * Regarding the online algorithm for Merkle tree signing. Expected + * calling sequence is: + * + * sigblkConstruct + * for each signature block: + * sigblkInit + * for each record: + * sigblkAddRecord + * sigblkFinish + * sigblkDestruct + * + * Obviously, the next call after sigblkFinsh must either be to + * sigblkInit or sigblkDestruct (if no more signature blocks are + * to be emitted, e.g. on file close). sigblkDestruct saves state + * information (most importantly last block hash) and sigblkConstruct + * reads (or initilizes if not present) it. + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "librsgt.h" + +typedef unsigned char uchar; +#ifndef VERSION +#define VERSION "no-version" +#endif + +static void +outputhash(GTDataHash *hash) +{ + int i; + for(i = 0 ; i < hash->digest_length ; ++i) + printf("%2.2x", hash->digest[i]); + printf("\n"); +} + + + +void +rsgtInit(char *usragent) +{ + int ret = GT_OK; + + srand(time(NULL) * 7); /* see comments in seedIV() */ + + ret = GT_init(); + if(ret != GT_OK) { + fprintf(stderr, "GT_init() failed: %d (%s)\n", + ret, GT_getErrorString(ret)); + goto done; + } + ret = GTHTTP_init(usragent, 1); + if(ret != GT_OK) { + fprintf(stderr, "GTHTTP_init() failed: %d (%s)\n", + ret, GTHTTP_getErrorString(ret)); + goto done; + } +done: return; +} + +void +rsgtExit(void) +{ + GTHTTP_finalize(); + GT_finalize(); +} + + +void +seedIV(gtctx ctx) +{ + /* FIXME: this currently is "kindergarten cryptography" - use a + * sufficiently strong PRNG instead! Just a PoC so far! Do NOT + * use in production!!! + */ + ctx->IV = rand() * 1000037; +} + +gtctx +rsgtCtxNew(void) +{ + return calloc(1, sizeof(struct gtctx_s)); +} + +void +rsgtCtxDel(gtctx ctx) +{ + if(ctx == NULL) + goto done; + free(ctx); + /* TODO: persist! */ +done: return; +} + +/* new sigblk is initialized, but maybe in existing ctx */ +void +sigblkInit(gtctx ctx) +{ + seedIV(ctx); +// if(ctx->x_prev == NULL) { + ctx->x_prev = NULL; + /* FIXME: do the real thing - or in a later step as currently? */ +// } + memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); + ctx->nRoots = 0; +} + + +/* concat: add IV to buffer */ +static inline void +bufAddIV(gtctx ctx, uchar *buf, size_t *len) +{ + memcpy(buf+*len, &ctx->IV, sizeof(ctx->IV)); + *len += sizeof(ctx->IV); +} + +/* concat: add hash to buffer */ +static inline void +bufAddHash(gtctx ctx, uchar *buf, size_t *len, GTDataHash *hash) +{ + if(hash == NULL) { + memset(buf+*len, 0, 32); /* FIXME: depends on hash function! */ + *len += 32; + } else { + memcpy(buf+*len, hash->digest, hash->digest_length); + *len += hash->digest_length; + } +} +/* concat: add tree level to buffer */ +static inline void +bufAddLevel(gtctx ctx, uchar *buf, size_t *len, int level) +{ + memcpy(buf+*len, &level, sizeof(level)); + *len += sizeof(level); +} + + +static void +hash_m(gtctx ctx, GTDataHash **m) +{ + // m = hash(concat(ctx->x_prev, IV)); + int r; + uchar concatBuf[16*1024]; + size_t len = 0; + + bufAddHash(ctx, concatBuf, &len, ctx->x_prev); + bufAddIV(ctx, concatBuf, &len); + r = GTDataHash_create(GT_HASHALG_SHA256, concatBuf, len, m); +} + +static void +hash_r(gtctx ctx, GTDataHash **r, const uchar *rec, const size_t len) +{ + // r = hash(canonicalize(rec)); + int ret; + + ret = GTDataHash_create(GT_HASHALG_SHA256, rec, len, r); +} + + +static void +hash_node(gtctx ctx, GTDataHash **node, GTDataHash *m, GTDataHash *r, int level) +{ + // x = hash(concat(m, r, 0)); /* hash leaf */ + int ret; + uchar concatBuf[16*1024]; + size_t len = 0; + + bufAddHash(ctx, concatBuf, &len, m); + bufAddHash(ctx, concatBuf, &len, r); + bufAddLevel(ctx, concatBuf, &len, level); + ret = GTDataHash_create(GT_HASHALG_SHA256, concatBuf, len, node); +} +void +sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) +{ + GTDataHash *x; /* current hash */ + GTDataHash *m, *r, *t; + int8_t j; + int ret; + + hash_m(ctx, &m); + hash_r(ctx, &r, rec, len); + hash_node(ctx, &x, m, r, 0); /* hash leaf */ + /* persists x here if Merkle tree needs to be persisted! */ + /* add x to the forest as new leaf, update roots list */ + t = x; + for(j = 0 ; j < ctx->nRoots ; ++j) { + if(ctx->roots_valid[j] == 0) { + GTDataHash_free(ctx->roots_hash[j]); + ctx->roots_hash[j] = t; + ctx->roots_valid[j] = 1; + t = NULL; + } else if(t != NULL) { + /* hash interim node */ + hash_node(ctx, &t, ctx->roots_hash[j], t, j+1); + ctx->roots_valid[j] = 0; + } + } + if(t != NULL) { + /* new level, append "at the top" */ + ctx->roots_hash[ctx->nRoots] = t; + ++ctx->nRoots; + assert(ctx->nRoots < MAX_ROOTS); + t = NULL; + } + ctx->x_prev = x; /* single var may be sufficient */ + + /* cleanup */ + GTDataHash_free(m); + GTDataHash_free(r); +} + +static void +timestampIt(gtctx ctx, GTDataHash *hash) +{ + int r = GT_OK; + GTTimestamp *timestamp = NULL; + unsigned char *der = NULL; + char *sigFile = "logsigner.TIMESTAMP"; + size_t der_len; + + /* Get the timestamp. */ + r = GTHTTP_createTimestampHash(hash, + "http://stamper.guardtime.net/gt-signingservice", ×tamp); + + if(r != GT_OK) { + fprintf(stderr, "GTHTTP_createTimestampHash() failed: %d (%s)\n", + r, GTHTTP_getErrorString(r)); + goto done; + } + + /* Encode timestamp. */ + r = GTTimestamp_getDEREncoded(timestamp, &der, &der_len); + if(r != GT_OK) { + fprintf(stderr, "GTTimestamp_getDEREncoded() failed: %d (%s)\n", + r, GT_getErrorString(r)); + goto done; + } + + /* Save DER-encoded timestamp to file. */ + r = GT_saveFile(sigFile, der, der_len); + if(r != GT_OK) { + fprintf(stderr, "Cannot save timestamp to file %s: %d (%s)\n", + sigFile, r, GT_getErrorString(r)); + if(r == GT_IO_ERROR) { + fprintf(stderr, "\t%d (%s)\n", errno, strerror(errno)); + } + goto done; + } + printf("Timestamping succeeded!\n"); +done: + GT_free(der); + GTTimestamp_free(timestamp); +} + + +void +sigblkFinish(gtctx ctx) +{ + GTDataHash *root, *rootDel; + int8_t j; + + root = NULL; + for(j = 0 ; j < ctx->nRoots ; ++j) { + if(root == NULL) { + root = ctx->roots_hash[j]; + ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + } else if(ctx->roots_valid[j]) { + rootDel = root; + hash_node(ctx, &root, ctx->roots_hash[j], root, j+1); + ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + GTDataHash_free(rootDel); + } + } + /* persist root value here (callback?) */ +printf("root hash is:\n"); outputhash(root); + timestampIt(ctx, root); +} diff --git a/runtime/lmgt.c b/runtime/lmgt.c deleted file mode 100644 index 15a56cfa..00000000 --- a/runtime/lmgt.c +++ /dev/null @@ -1,124 +0,0 @@ -/* lmgt.c - * - * Regarding the online algorithm for Merkle tree signing. Expected - * calling sequence is: - * - * sigblkConstruct - * for each signature block: - * sigblkInit - * for each record: - * sigblkAddRecord - * sigblkFinish - * sigblkDestruct - * - * Obviously, the next call after sigblkFinsh must either be to - * sigblkInit or sigblkDestruct (if no more signature blocks are - * to be emitted, e.g. on file close). sigblkDestruct saves state - * information (most importantly last block hash) and sigblkConstruct - * reads (or initilizes if not present) it. - * - * 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 - - -/* Max number of roots inside the forest. This permits blocks of up to - * 2^MAX_ROOTS records. We assume that 64 is sufficient for all use - * cases ;) [and 64 is not really a waste of memory, so we do not even - * try to work with reallocs and such... - */ -#define MAX_ROOTS 64 - -/* context for gt calls. All state data is kept here, this permits - * multiple concurrent callers. - */ -struct gtctx_s { - IV; /* initial value for blinding masks (where to do we get it from?) */ - x_prev = NULL; /* last leaf hash (maybe of previous block) --> preserve on term */ - int8_t nroots; - /* algo engineering: roots structure is split into two arrays - * in order to improve cache hits. - */ - char roots_valid[MAX_ROOTS]; - hash_mem roots_hash[MAX_ROOTS]; - -}; -typedef struct gtctx_s *gtctx; - -void -sigblkInit(gtctx ctx) -{ - init ctx->IV; - if(ctx->x_prev == NULL) - alloc & zero-fill x_prev; - memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); - nroots = 0; -} - - -void -sigblkAddRecord(gtctx ctx, char *rec) -{ - auto x; /* current hash */ - hash_mem m, r, t; - int8_t j; - - m = hash(concat(ctx->x_prev, IV)); - r = hash(canonicalize(rec)); - x = hash(concat(m, r, 0)); /* hash leaf */ - /* persists x here if Merkle tree needs to be persisted! */ - /* add x to the forest as new leaf, update roots list */ - t = x; - for(j = 0 ; j < ctx->nRoots ; ++j) { - if(ctx->roots_valid[j] == 0) { - ctx->roots_hash[j] = t; - ctx->roots_valid[j] = 1; - t = NULL; - } else if(t != NULL) { - t = hash(concat(ctx->roots_hash[j], t, j+1)); /* hash interim node */ - ctx->roots_valid[j] = 0; - } - if(t != NULL) { - ctx->roots_hash[ctx->nroots] = t; - ++ctx->roots_hash; - assert(ctx->roots_hash < MAX_ROOTS); - t = NULL; - } - ctx->x_prev = x; /* single var may be sufficient */ -} - -void -sigblkFinish(gtctx ctx) -{ - hash_mem root; - int8_t j; - - root = NULL; - for(j = 0 ; j < ctx->nRoots ; ++j) { - if(root == NULL) { - root = ctx->roots_hash[j]; - ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ - } else if(ctx->roots_valid[j]) { - root = hash(concat(ctx->roots_hash[j], root, j+1)); - ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ - } - } - /* persist root value here (callback?) */ -} diff --git a/tools/Makefile.am b/tools/Makefile.am index 9d9bd352..712443c0 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -58,6 +58,12 @@ logctl_SOURCES = logctl.c logctl_CPPFLAGS = $(LIBMONGO_CLIENT_CFLAGS) logctl_LDADD = $(LIBMONGO_CLIENT_LIBS) endif +if ENABLE_GUARDTIME +bin_PROGRAMS += logsigner +logsigner = logsigner.c +logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) +logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) +endif endif EXTRA_DIST = $(man_MANS) \ diff --git a/tools/logsigner.c b/tools/logsigner.c index 2aeb2162..51803be2 100644 --- a/tools/logsigner.c +++ b/tools/logsigner.c @@ -26,15 +26,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include #include #include #include - #include #include +#include "librsgt.h" + +#if 0 void outputhash(GTDataHash *hash) { @@ -44,33 +49,6 @@ outputhash(GTDataHash *hash) printf("\n"); } -void -gtInit() -{ - int r = GT_OK; - - r = GT_init(); - if(r != GT_OK) { - fprintf(stderr, "GT_init() failed: %d (%s)\n", - r, GT_getErrorString(r)); - goto done; - } - r = GTHTTP_init("rsyslog logsigner", 1); - if(r != GT_OK) { - fprintf(stderr, "GTHTTP_init() failed: %d (%s)\n", - r, GTHTTP_getErrorString(r)); - goto done; - } -done: return; -} - -void -gtExit() -{ - GTHTTP_finalize(); - GT_finalize(); -} - void timestampIt(GTDataHash *hash) { @@ -116,7 +94,7 @@ done: void -sign(const char *buf, size_t len) +sign(const char *buf, const size_t len) { int r; GTDataHash *hash = NULL; @@ -132,6 +110,7 @@ sign(const char *buf, size_t len) timestampIt(hash); /* of course, this needs to be moved to once at end ;) */ done: GTDataHash_free(hash); } +#endif void processFile(char *name) @@ -139,7 +118,10 @@ processFile(char *name) FILE *fp; size_t len; char line[64*1024+1]; + gtctx ctx = NULL; + ctx = rsgtCtxNew(); + sigblkInit(ctx); if(!strcmp(name, "-")) fp = stdin; else @@ -147,7 +129,8 @@ processFile(char *name) while(1) { if(fgets(line, sizeof(line), fp) == NULL) { - perror(name); + if(!feof(fp)) + perror(name); break; } len = strlen(line); @@ -155,19 +138,22 @@ processFile(char *name) --len; line[len] = '\0'; } - sign(line, len); + //sign(line, len); + sigblkAddRecord(ctx, line, len); } - if(fp != stdout) + if(fp != stdin) fclose(fp); + sigblkFinish(ctx); + rsgtCtxDel(ctx); } int main(int argc, char *argv[]) { - gtInit(); + rsgtInit("rsyslog logsigner " VERSION); processFile("-"); - gtExit(); + rsgtExit(); return 0; } -- cgit v1.2.3 From b129a96287492685050f6f80f8f0204122b22e77 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 28 Feb 2013 10:02:27 +0100 Subject: bugfix: include files got included in the wrong order This happens if an $IncludeConfig directive was done on multiple files (e.g. the distro default of $IncludeConfig /etc/rsyslog.d/*.conf). In that case, the order of include file processing is reversed, which could lead to all sorts of problems. Thanks to Nathan Stratton Treadway for his great analysis of the problem, which made bug fixing really easy. --- ChangeLog | 7 +++++++ grammar/lexer.l | 8 +++++++- grammar/rainerscript.c | 9 +++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9418d241..fb67f4d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,13 @@ ---------------------------------------------------------------------------- Version 7.2.6 [v7-stable] 2013-01-?? - slightly improved config parser error messages when invalid escapes happen +- bugfix: include files got included in the wrong order + This happens if an $IncludeConfig directive was done on multiple + files (e.g. the distro default of $IncludeConfig /etc/rsyslog.d/*.conf). + In that case, the order of include file processing is reversed, which + could lead to all sorts of problems. + Thanks to Nathan Stratton Treadway for his great analysis of the problem, + which made bug fixing really easy. - bugfix: omelasticsearch failed when authentication data was provided ... at least in most cases it emitted an error message: "snprintf failed when trying to build auth string" diff --git a/grammar/lexer.l b/grammar/lexer.l index e1f5a9c3..237eb2a6 100644 --- a/grammar/lexer.l +++ b/grammar/lexer.l @@ -310,6 +310,7 @@ cnfSetLexFile(char *fname) currbs = bs; cnfcurrfn = bs->fn; yylineno = 1; + dbgprintf("config parser: pushed file %s on top of stack\n", fname); done: if(r != 0) { @@ -337,6 +338,7 @@ popfile(void) * necessary, as otherwise we may provide wrong file name information * at the end of include files as well. -- rgerhards, 2011-07-22 */ + dbgprintf("config parser: reached end of file %s\n", bs->fn); yy_delete_buffer(bs->bs); if(bs->prev != NULL) free(bs->fn); @@ -346,12 +348,16 @@ popfile(void) currbs = bs->prev; free(bs); - if(currbs == NULL) + if(currbs == NULL) { + dbgprintf("config parser: parsing completed\n"); return 1; /* all processed */ + } yy_switch_to_buffer(currbs->bs); yylineno = currbs->lineno; cnfcurrfn = currbs->fn; + dbgprintf("config parser: resume parsing of file %s at line %d\n", + cnfcurrfn, yylineno); return 0; } diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 0584d6a9..20b86c5c 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -2793,7 +2793,7 @@ cnfDoInclude(char *name) { char *cfgFile; char *finalName; - unsigned i; + int i; int result; glob_t cfgFiles; struct stat fileInfo; @@ -2829,7 +2829,12 @@ cnfDoInclude(char *name) return 1; } - for(i = 0; i < cfgFiles.gl_pathc; i++) { + /* note: bison "stacks" the files, so we need to submit them + * in reverse order to the *stack* in order to get the proper + * parsing order. Also see + * http://bugzilla.adiscon.com/show_bug.cgi?id=411 + */ + for(i = cfgFiles.gl_pathc - 1; i >= 0 ; i--) { cfgFile = cfgFiles.gl_pathv[i]; if(stat(cfgFile, &fileInfo) != 0) { char errStr[1024]; -- cgit v1.2.3 From bb0fb0dbd0bcff349e128a740b8bd14f35008deb Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 28 Feb 2013 10:07:03 +0100 Subject: add bug tracker ID to ChangeLog bugfix --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index fb67f4d3..4ab28d67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version 7.2.6 [v7-stable] 2013-01-?? - slightly improved config parser error messages when invalid escapes happen - bugfix: include files got included in the wrong order + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=411 This happens if an $IncludeConfig directive was done on multiple files (e.g. the distro default of $IncludeConfig /etc/rsyslog.d/*.conf). In that case, the order of include file processing is reversed, which -- cgit v1.2.3 From 6867887a424d86fccce3a54ae81da2c1d7f7656f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 4 Mar 2013 18:32:49 +0100 Subject: logsig: PoC for some TLV support functions also first steps at integrating them into rest of PoC --- runtime/librsgt.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 4ad4ec26..89a82f41 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -96,20 +96,110 @@ rsgtExit(void) } + + +static inline void +tlvbufPhysWrite(gtctx ctx) +{ + fprintf(stderr, "emu: writing TLV file!\n"); + ctx->tlvIdx = 0; +} + +static inline void +tlvbufChkWrite(gtctx ctx) +{ + if(ctx->tlvIdx == sizeof(ctx->tlvBuf)) { + tlvbufPhysWrite(ctx); + } +} + + +/* write to TLV file buffer. If buffer is full, an actual call occurs. Else + * output is written only on flush or close. + */ +static inline void +tlvbufAddOctet(gtctx ctx, int8_t octet) +{ + tlvbufChkWrite(ctx); + ctx->tlvBuf[ctx->tlvIdx++] = octet; +} +static inline void +tlvbufAddOctetString(gtctx ctx, int8_t *octet, int size) +{ + int i; + for(i = 0 ; i < size ; ++i) + tlvbufAddOctet(ctx, octet[i]); +} +static inline void +tlvbufAddInteger(gtctx ctx, uint32_t val) +{ + tlvbufAddOctet(ctx, (val >> 24) & 0xff); + tlvbufAddOctet(ctx, (val >> 16) & 0xff); + tlvbufAddOctet(ctx, (val >> 8) & 0xff); + tlvbufAddOctet(ctx, val & 0xff); +} + + +void +tlv8Write(gtctx ctx, int flags, int tlvtype, int len) +{ + tlvbufAddOctet(ctx, (flags << 5)|tlvtype); + tlvbufAddOctet(ctx, len & 0xff); +} + +void +tlv16Write(gtctx ctx, int flags, int tlvtype, uint16_t len) +{ + tlvbufAddOctet(ctx, ((flags|1) << 13)|tlvtype); + tlvbufAddOctet(ctx, (len >> 8) & 0xff); + tlvbufAddOctet(ctx, len & 0xff); +} + +void +tlvFlush(gtctx ctx) +{ + if(ctx->tlvIdx != 0) + tlvbufPhysWrite(ctx); +} + +void tlvClose(gtctx ctx) +{ + tlvFlush(ctx); + fprintf(stderr, "emu: close tlv file\n"); +} + +/* note: if file exists, the last hash for chaining must + * be read from file. + */ +void tlvOpen(gtctx ctx) +{ + fprintf(stderr, "emu: open tlv file\n"); + ctx->tlvIdx = 0; +} + void seedIV(gtctx ctx) { /* FIXME: this currently is "kindergarten cryptography" - use a * sufficiently strong PRNG instead! Just a PoC so far! Do NOT * use in production!!! + * As of some Linux and security expert I spoke to, /dev/urandom + * provides very strong random numbers, even if it runs out of + * entropy. As far as he knew, this is save for all applications + * (and he had good proof that I currently am not permitted to + * reproduce). -- rgerhards, 2013-03-04 */ ctx->IV = rand() * 1000037; } gtctx -rsgtCtxNew(void) +rsgtCtxNew(void, char *logfilename) { - return calloc(1, sizeof(struct gtctx_s)); + gtctx ctx; + ctx = calloc(1, sizeof(struct gtctx_s)); + ctx->logfilename = strdup(logfilename); + tlvOpen(ctx); + return ctx; } void @@ -117,6 +207,9 @@ rsgtCtxDel(gtctx ctx) { if(ctx == NULL) goto done; + + tlvClose(ctx); + free(ctx->logfilename); free(ctx); /* TODO: persist! */ done: return; @@ -133,6 +226,7 @@ sigblkInit(gtctx ctx) // } memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); ctx->nRoots = 0; + ctx->nRecords = 0; } @@ -235,6 +329,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) t = NULL; } ctx->x_prev = x; /* single var may be sufficient */ + ++ctx->nRecords; /* cleanup */ GTDataHash_free(m); -- cgit v1.2.3 From 5aaeb6287ba2c9175dccc4d228e0819a2988a692 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 5 Mar 2013 10:30:22 +0100 Subject: preparing for 7.2.6 release --- ChangeLog | 2 +- configure.ac | 2 +- doc/manual.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4ab28d67..73b2faa2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ ---------------------------------------------------------------------------- -Version 7.2.6 [v7-stable] 2013-01-?? +Version 7.2.6 [v7-stable] 2013-03-05 - slightly improved config parser error messages when invalid escapes happen - bugfix: include files got included in the wrong order closes: http://bugzilla.adiscon.com/show_bug.cgi?id=411 diff --git a/configure.ac b/configure.ac index 71a3fc19..ac6fd43d 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[7.2.5],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.2.6],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/doc/manual.html b/doc/manual.html index 78b5d006..8b54bd0b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ rsyslog support available directly from the source!

Please visit the rsyslog sponsor's page to honor the project sponsors or become one yourself! We are very grateful for any help towards the project goals.

-

This documentation is for version 7.2.5 (v7-stable branch) of rsyslog. +

This documentation is for version 7.2.6 (v7.2-stable branch) of rsyslog. Visit the rsyslog status page to obtain current version information and project status.

If you like rsyslog, you might -- cgit v1.2.3 From 3a68c5cda88336af1fde3755180f4e293735da5d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 5 Mar 2013 11:55:02 +0100 Subject: logsig: build base plumbing for signature multi-provider interface --- runtime/Makefile.am | 7 ++++ runtime/librsgt.c | 3 +- runtime/lmsig_gt.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/lmsig_gt.h | 40 ++++++++++++++++++++ runtime/rsyslog.h | 1 + runtime/sigprov.h | 33 ++++++++++++++++ tools/logsigner.c | 4 +- tools/omfile.c | 68 ++++++++++++++++++++++++++++++++- 8 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 runtime/lmsig_gt.c create mode 100644 runtime/lmsig_gt.h create mode 100644 runtime/sigprov.h diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 0f1b580a..b6009b98 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -17,6 +17,7 @@ librsyslog_la_SOURCES = \ module-template.h \ im-helper.h \ obj-types.h \ + sigprov.h \ nsd.h \ glbl.h \ glbl.c \ @@ -178,6 +179,12 @@ endif # if ENABLE_GUARDTIME librsgt_la_SOURCES = librsgt.c + + pkglib_LTLIBRARIES += lmsig_gt.la + lmsig_gt_la_SOURCES = lmsig_gt.c + lmsig_gt_la_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) + lmsig_gt_la_LDFLAGS = -module -avoid-version + lmsig_gt_la_LIBADD = librsgt.c $(GUARDTIME_LIBS) endif diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 89a82f41..68e0f6d4 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -45,7 +45,6 @@ #include #include -#include #include #include "librsgt.h" @@ -193,7 +192,7 @@ seedIV(gtctx ctx) } gtctx -rsgtCtxNew(void, char *logfilename) +rsgtCtxNew(char *logfilename) { gtctx ctx; ctx = calloc(1, sizeof(struct gtctx_s)); diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c new file mode 100644 index 00000000..c390457a --- /dev/null +++ b/runtime/lmsig_gt.c @@ -0,0 +1,107 @@ +/* lmsig_gt.c + * + * An implementation of the sigprov interface for GuardTime. + * + * Copyright 2013 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * 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 +#include +#include + +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "sigprov.h" +#include "lmsig_gt.h" + +MODULE_TYPE_LIB +MODULE_TYPE_NOKEEP + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) + + +/* Standard-Constructor + */ +BEGINobjConstruct(lmsig_gt) /* be sure to specify the object type also in END macro! */ + dbgprintf("DDDD: lmsig_gt: called construct\n"); +ENDobjConstruct(lmsig_gt) + + +/* destructor for the lmsig_gt object */ +BEGINobjDestruct(lmsig_gt) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(lmsig_gt) + dbgprintf("DDDD: lmsig_gt: called destruct\n"); +ENDobjDestruct(lmsig_gt) + + +BEGINobjQueryInterface(lmsig_gt) +CODESTARTobjQueryInterface(lmsig_gt) + if(pIf->ifVersion != sigprovCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + pIf->Construct = lmsig_gtConstruct; + pIf->Destruct = lmsig_gtDestruct; +finalize_it: +ENDobjQueryInterface(lmsig_gt) + + +BEGINObjClassExit(lmsig_gt, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(lmsig_gt) + /* release objects we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); +ENDObjClassExit(lmsig_gt) + + +BEGINObjClassInit(lmsig_gt, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + /* set our own handlers */ +ENDObjClassInit(lmsig_gt) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + lmsig_gtClassExit(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_LIB_QUERIES +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ + /* Initialize all classes that are in our module - this includes ourselfs */ + CHKiRet(lmsig_gtClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ +ENDmodInit diff --git a/runtime/lmsig_gt.h b/runtime/lmsig_gt.h new file mode 100644 index 00000000..665e6a8e --- /dev/null +++ b/runtime/lmsig_gt.h @@ -0,0 +1,40 @@ +/* An implementation of the sigprov interface for GuardTime. + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * 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. + */ +#ifndef INCLUDED_LMSIG_GT_H +#define INCLUDED_LMSIG_GT_H +#include "sigprov.h" +#include "librsgt.h" + +/* interface is defined in sigprov.h, we just implement it! */ +#define lmsig_gtCURR_IF_VERSION sigprovCURR_IF_VERSION +typedef sigprov_if_t lmsig_gt_if_t; + +/* the lmsig_gt object */ +struct lmsig_gt_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + gtctx ctx; /* librsgt context - contains all we need */ +}; +typedef struct lmsig_gt_s lmsig_gt_t; + +/* prototypes */ +PROTOTYPEObj(lmsig_gt); + +#endif /* #ifndef INCLUDED_LMSIG_GT_H */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 7e3761e0..42c61579 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -399,6 +399,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_DS_PROP_SEQ_ERR = -2308,/**< property sequence error deserializing object */ RS_RET_TPL_INVLD_PROP = -2309,/**< property name error in template (unknown name) */ RS_RET_NO_RULEBASE = -2310,/**< mmnormalize: rulebase can not be found or otherwise invalid */ + RS_RET_SIGPROV_ERR = -2320,/**< error in signature provider */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/runtime/sigprov.h b/runtime/sigprov.h new file mode 100644 index 00000000..f0eb9350 --- /dev/null +++ b/runtime/sigprov.h @@ -0,0 +1,33 @@ +/* The interface definition for (file) signature providers. + * + * This is just an abstract driver interface, which needs to be + * implemented by concrete classes. + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * 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. + */ +#ifndef INCLUDED_SIGPROV_H +#define INCLUDED_SIGPROV_H + +/* interface */ +BEGINinterface(sigprov) /* name must also be changed in ENDinterface macro! */ + rsRetVal (*Construct)(void *ppThis); + rsRetVal (*Destruct)(void **ppThis); +ENDinterface(sigprov) +#define sigprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#endif /* #ifndef INCLUDED_SIGPROV_H */ diff --git a/tools/logsigner.c b/tools/logsigner.c index 51803be2..1a9b9ab5 100644 --- a/tools/logsigner.c +++ b/tools/logsigner.c @@ -120,7 +120,7 @@ processFile(char *name) char line[64*1024+1]; gtctx ctx = NULL; - ctx = rsgtCtxNew(); + ctx = rsgtCtxNew("SIGFILE"); sigblkInit(ctx); if(!strcmp(name, "-")) fp = stdin; @@ -139,7 +139,7 @@ processFile(char *name) line[len] = '\0'; } //sign(line, len); - sigblkAddRecord(ctx, line, len); + sigblkAddRecord(ctx, (unsigned char*)line, len); } if(fp != stdin) diff --git a/tools/omfile.c b/tools/omfile.c index 1c65fc59..27bbe02e 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -17,7 +17,7 @@ * pipes. These have been moved to ompipe, to reduced the entanglement * between the two different functionalities. -- rgerhards * - * Copyright 2007-2012 Adiscon GmbH. + * Copyright 2007-2013 Adiscon GmbH. * * This file is part of rsyslog. * @@ -69,6 +69,7 @@ #include "unicode-helper.h" #include "atomic.h" #include "statsobj.h" +#include "sigprov.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP @@ -143,6 +144,11 @@ typedef struct _instanceData { gid_t fileGID; gid_t dirGID; int bFailOnChown; /* fail creation if chown fails? */ + uchar *sigprovName; /* signature provider */ + uchar *sigprovNameFull;/* full internal signature provider name */ + sigprov_if_t sigprov; /* ptr to signature provider interface */ + void *sigprovData; /* opaque data ptr for provider use */ + sbool useSigprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ int iCurrElt; /* currently active cache element (-1 = none) */ int iCurrCacheSize; /* currently cache size (1-based) */ int iDynaFileCacheSize; /* size of file handle cache */ @@ -228,7 +234,8 @@ static struct cnfparamdescr actpdescr[] = { { "sync", eCmdHdlrBinary, 0 }, /* legacy: actionfileenablesync */ { "file", eCmdHdlrString, 0 }, /* either "file" or ... */ { "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */ - { "template", eCmdHdlrGetWord, 0 }, + { "sig.provider", eCmdHdlrGetWord, 0 }, + { "template", eCmdHdlrGetWord, 0 } }; static struct cnfparamblk actpblk = { CNFPARAMBLK_VERSION, @@ -836,6 +843,14 @@ ENDcreateInstance BEGINfreeInstance CODESTARTfreeInstance + if(pData->useSigprov) { + dbgprintf("DDDD: destructing signature provider %s\n", pData->sigprovNameFull); + pData->sigprov.Destruct(&pData->sigprovData); + obj.ReleaseObj(__FILE__, pData->sigprovNameFull+2, pData->sigprovNameFull, + (void*) &pData->sigprov); + free(pData->sigprovName); + free(pData->sigprovNameFull); + } free(pData->tplName); free(pData->f_fname); if(pData->bDynamicName) { @@ -907,6 +922,8 @@ setInstParamDefaults(instanceData *pData) pData->iIOBufSize = IOBUF_DFLT_SIZE; pData->iFlushInterval = FLUSH_INTRVL_DFLT; pData->bUseAsyncWriter = USE_ASYNCWRITER_DFLT; + pData->sigprovName = NULL; + pData->useSigprov = 0; } @@ -946,6 +963,47 @@ finalize_it: RETiRet; } +static inline void +initSigprov(instanceData *pData) +{ + uchar szDrvrName[1024]; + + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmsig_%s", pData->sigprovName) + == sizeof(szDrvrName)) { + errmsg.LogError(0, RS_RET_ERR, "omfile: signature provider " + "name is too long: '%s' - signatures disabled", + pData->sigprovName); + goto done; + } + pData->sigprovNameFull = ustrdup(szDrvrName); + + pData->sigprov.ifVersion = sigprovCURR_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*) &pData->sigprov) + != RS_RET_OK) { + errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load " + "signature provider '%s' - signatures disabled", + szDrvrName); + goto done; + } + + if(pData->sigprov.Construct(&pData->sigprovData) != RS_RET_OK) { + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "omfile: error constructing " + "signature provider %s dataset - signatures disabled", + szDrvrName); + goto done; + } + + dbgprintf("loaded signature provider %s, data instance at %p\n", + szDrvrName, pData->sigprovData); + pData->useSigprov = 1; +done: return; +} + BEGINnewActInst struct cnfparamvals *pvals; uchar *tplToUse; @@ -1013,6 +1071,8 @@ CODESTARTnewActInst pData->bDynamicName = 1; } else if(!strcmp(actpblk.descr[i].name, "template")) { pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "sig.provider")) { + pData->sigprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else { dbgprintf("omfile: program error, non-handled " "param '%s'\n", actpblk.descr[i].name); @@ -1025,6 +1085,10 @@ CODESTARTnewActInst ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } + if(pData->sigprovName != NULL) { + initSigprov(pData); + } + tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS)); -- cgit v1.2.3 From 8f32f09d7e688091f432e0c0e156d3a9eec78a4b Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 5 Mar 2013 12:52:28 +0100 Subject: logsig: more interface plumbing --- runtime/Makefile.am | 2 +- runtime/lmsig_gt.c | 40 +++++++++++++++++++++++++++++++++++++--- runtime/sigprov.h | 5 ++++- tools/omfile.c | 46 +++++++++++++++++++++++++++++++++++----------- 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/runtime/Makefile.am b/runtime/Makefile.am index b6009b98..20824494 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -184,7 +184,7 @@ if ENABLE_GUARDTIME lmsig_gt_la_SOURCES = lmsig_gt.c lmsig_gt_la_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) lmsig_gt_la_LDFLAGS = -module -avoid-version - lmsig_gt_la_LIBADD = librsgt.c $(GUARDTIME_LIBS) + lmsig_gt_la_LIBADD = librsgt.la $(GUARDTIME_LIBS) endif diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index c390457a..f58d46c3 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -55,14 +55,46 @@ CODESTARTobjDestruct(lmsig_gt) dbgprintf("DDDD: lmsig_gt: called destruct\n"); ENDobjDestruct(lmsig_gt) +static rsRetVal +OnFileOpen(void *pT, uchar *fn) +{ + lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + DEFiRet; +dbgprintf("DDDD: onFileOpen: %s\n", fn); + + RETiRet; +} + +static rsRetVal +OnRecordWrite(void *pT, uchar *rec, rs_size_t lenRec) +{ + lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + DEFiRet; +dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec, rec); + + RETiRet; +} + +static rsRetVal +OnFileClose(void *pT) +{ + lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + DEFiRet; +dbgprintf("DDDD: onFileClose\n"); + + RETiRet; +} BEGINobjQueryInterface(lmsig_gt) CODESTARTobjQueryInterface(lmsig_gt) if(pIf->ifVersion != sigprovCURR_IF_VERSION) {/* check for current version, increment on each change */ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); } - pIf->Construct = lmsig_gtConstruct; - pIf->Destruct = lmsig_gtDestruct; + pIf->Construct = (rsRetVal(*)(void*)) lmsig_gtConstruct; + pIf->Destruct = (rsRetVal(*)(void*)) lmsig_gtDestruct; + pIf->OnFileOpen = OnFileOpen; + pIf->OnRecordWrite = OnRecordWrite; + pIf->OnFileClose = OnFileClose; finalize_it: ENDobjQueryInterface(lmsig_gt) @@ -72,6 +104,8 @@ CODESTARTObjClassExit(lmsig_gt) /* release objects we no longer need */ objRelease(errmsg, CORE_COMPONENT); objRelease(glbl, CORE_COMPONENT); + + rsgtExit(); ENDObjClassExit(lmsig_gt) @@ -80,7 +114,7 @@ BEGINObjClassInit(lmsig_gt, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); - /* set our own handlers */ + rsgtInit("rsyslogd " VERSION); ENDObjClassInit(lmsig_gt) diff --git a/runtime/sigprov.h b/runtime/sigprov.h index f0eb9350..0154a1f4 100644 --- a/runtime/sigprov.h +++ b/runtime/sigprov.h @@ -27,7 +27,10 @@ /* interface */ BEGINinterface(sigprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(void *ppThis); - rsRetVal (*Destruct)(void **ppThis); + rsRetVal (*Destruct)(void *ppThis); + rsRetVal (*OnFileOpen)(void *pThis, uchar *fn); + rsRetVal (*OnRecordWrite)(void *pThis, uchar *rec, rs_size_t lenRec); + rsRetVal (*OnFileClose)(void *pThis); ENDinterface(sigprov) #define sigprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ #endif /* #ifndef INCLUDED_SIGPROV_H */ diff --git a/tools/omfile.c b/tools/omfile.c index 27bbe02e..efdf5e5b 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -441,6 +441,7 @@ dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry) if(pCache[iEntry]->pStrm != NULL) { strm.Destruct(&pCache[iEntry]->pStrm); +#warning add sig capability here if(pCache[iEntry]->pStrm != NULL) /* safety check -- TODO: remove if no longer necessary */ abort(); } @@ -488,6 +489,27 @@ static void dynaFileFreeCache(instanceData *pData) } +/* close current file */ +static rsRetVal +closeFile(instanceData *pData) +{ + DEFiRet; + if(pData->useSigprov) + pData->sigprov.OnFileClose(pData->sigprovData); + strm.Destruct(&pData->pStrm); + RETiRet; +} + + +/* This prepares the signature provider to process a file */ +static rsRetVal +sigprovPrepare(instanceData *pData, uchar *fn) +{ + DEFiRet; + pData->sigprov.OnFileOpen(pData->sigprovData, fn); + RETiRet; +} + /* This is now shared code for all types of files. It simply prepares * file access, which, among others, means the the file wil be opened * and any directories in between will be created (based on config, of @@ -570,11 +592,14 @@ prepareFile(instanceData *pData, uchar *newFileName) if(pData->pszSizeLimitCmd != NULL) CHKiRet(strm.SetpszSizeLimitCmd(pData->pStrm, ustrdup(pData->pszSizeLimitCmd))); CHKiRet(strm.ConstructFinalize(pData->pStrm)); + + if(pData->useSigprov) + sigprovPrepare(pData, szNameBuf); finalize_it: if(iRet != RS_RET_OK) { if(pData->pStrm != NULL) { - strm.Destruct(&pData->pStrm); + closeFile(pData); } } RETiRet; @@ -701,7 +726,7 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) } if((pCache[iFirstFree]->pName = ustrdup(newFileName)) == NULL) { - strm.Destruct(&pData->pStrm); /* need to free failed entry! */ + closeFile(pData); /* need to free failed entry! */ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } pCache[iFirstFree]->pStrm = pData->pStrm; @@ -729,7 +754,7 @@ doWrite(instanceData *pData, uchar *pszBuf, int lenBuf) DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf); if(pData->pStrm != NULL){ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf)); - FINALIZE; + CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovData, pszBuf, lenBuf)); } finalize_it: @@ -843,6 +868,12 @@ ENDcreateInstance BEGINfreeInstance CODESTARTfreeInstance + free(pData->tplName); + free(pData->f_fname); + if(pData->bDynamicName) { + dynaFileFreeCache(pData); + } else if(pData->pStrm != NULL) + closeFile(pData); if(pData->useSigprov) { dbgprintf("DDDD: destructing signature provider %s\n", pData->sigprovNameFull); pData->sigprov.Destruct(&pData->sigprovData); @@ -851,12 +882,6 @@ CODESTARTfreeInstance free(pData->sigprovName); free(pData->sigprovNameFull); } - free(pData->tplName); - free(pData->f_fname); - if(pData->bDynamicName) { - dynaFileFreeCache(pData); - } else if(pData->pStrm != NULL) - strm.Destruct(&pData->pStrm); ENDfreeInstance @@ -1231,8 +1256,7 @@ CODESTARTdoHUP dynaFileFreeCacheEntries(pData); } else { if(pData->pStrm != NULL) { - strm.Destruct(&pData->pStrm); - pData->pStrm = NULL; + closeFile(pData); } } ENDdoHUP -- cgit v1.2.3 From 027441b337a3dc2c0017df6eebf473445f628d52 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 5 Mar 2013 15:10:11 +0100 Subject: logsig: first PoC of actually writing to signature file --- runtime/librsgt.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++------- runtime/lmsig_gt.c | 4 +++ tools/logsigner.c | 2 +- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 68e0f6d4..e1d760c6 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -44,6 +44,11 @@ #include #include #include +#include +#include +#include +#include +#define MAXFNAME 1024 /* TODO: include correct header */ #include @@ -53,11 +58,12 @@ typedef unsigned char uchar; #ifndef VERSION #define VERSION "no-version" #endif +#define LOGSIGHDR "LOGSIG10" static void outputhash(GTDataHash *hash) { - int i; + unsigned i; for(i = 0 ; i < hash->digest_length ; ++i) printf("%2.2x", hash->digest[i]); printf("\n"); @@ -95,12 +101,43 @@ rsgtExit(void) } - - static inline void tlvbufPhysWrite(gtctx ctx) { + ssize_t lenBuf; + ssize_t iTotalWritten; + ssize_t iWritten; + char *pWriteBuf; fprintf(stderr, "emu: writing TLV file!\n"); + + lenBuf = ctx->tlvIdx; + pWriteBuf = ctx->tlvBuf; + iTotalWritten = 0; + do { + iWritten = write(ctx->fd, pWriteBuf, lenBuf); + if(iWritten < 0) { + //char errStr[1024]; + int err = errno; + iWritten = 0; /* we have written NO bytes! */ + /* rs_strerror_r(err, errStr, sizeof(errStr)); + DBGPRINTF("log file (%d) write error %d: %s\n", pThis->fd, err, errStr); + */ + if(err == EINTR) { + /*NO ERROR, just continue */; + } else { + goto finalize_it; //ABORT_FINALIZE(RS_RET_IO_ERROR); + /* FIXME: flag error */ + } + } + /* advance buffer to next write position */ + iTotalWritten += iWritten; + lenBuf -= iWritten; + pWriteBuf += iWritten; + } while(lenBuf > 0); /* Warning: do..while()! */ + + //DBGOPRINT((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, (int) iWritten); + +finalize_it: ctx->tlvIdx = 0; } @@ -161,19 +198,36 @@ tlvFlush(gtctx ctx) tlvbufPhysWrite(ctx); } +void +tlvWriteBlockSig(gtctx ctx, uchar *der, size_t der_len) +{ + //FIXME: flags??? + tlv8Write(ctx, 0x00, 0x00, 1); + tlvbufAddOctet(ctx, 0x02); // TODO: hash identifier (Tab. 2)! + tlv16Write(ctx, 0x00, 0x906, (uint16_t) der_len); + tlvbufAddOctetString(ctx, (int8_t*) der, (int) der_len); +} + void tlvClose(gtctx ctx) { tlvFlush(ctx); fprintf(stderr, "emu: close tlv file\n"); + close(ctx->fd); + ctx->fd = -1; } + /* note: if file exists, the last hash for chaining must * be read from file. */ -void tlvOpen(gtctx ctx) +void tlvOpen(gtctx ctx, char *hdr, unsigned lenHdr) { - fprintf(stderr, "emu: open tlv file\n"); - ctx->tlvIdx = 0; + fprintf(stderr, "emu: open tlv file '%s'\n", ctx->sigfilename); + ctx->fd = open((char*)ctx->sigfilename, + O_WRONLY/*|O_APPEND*/|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); + // FIXME: check fd == -1 + memcpy(ctx->tlvBuf, hdr, lenHdr); + ctx->tlvIdx = lenHdr; } void @@ -192,12 +246,15 @@ seedIV(gtctx ctx) } gtctx -rsgtCtxNew(char *logfilename) +rsgtCtxNew(unsigned char *logfn) { + char fn[MAXFNAME+1]; gtctx ctx; ctx = calloc(1, sizeof(struct gtctx_s)); - ctx->logfilename = strdup(logfilename); - tlvOpen(ctx); + snprintf(fn, sizeof(fn), "%s.gtsig", logfn); + fn[MAXFNAME] = '\0'; /* be on save side */ + ctx->sigfilename = (uchar*) strdup(fn); + tlvOpen(ctx, LOGSIGHDR, sizeof(LOGSIGHDR)-1); return ctx; } @@ -207,8 +264,10 @@ rsgtCtxDel(gtctx ctx) if(ctx == NULL) goto done; + if(ctx->bInBlk) + sigblkFinish(ctx); tlvClose(ctx); - free(ctx->logfilename); + free(ctx->sigfilename); free(ctx); /* TODO: persist! */ done: return; @@ -226,6 +285,7 @@ sigblkInit(gtctx ctx) memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); ctx->nRoots = 0; ctx->nRecords = 0; + ctx->bInBlk = 1; } @@ -300,7 +360,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) GTDataHash *x; /* current hash */ GTDataHash *m, *r, *t; int8_t j; - int ret; + //int ret; hash_m(ctx, &m); hash_r(ctx, &r, rec, len); @@ -362,6 +422,9 @@ timestampIt(gtctx ctx, GTDataHash *hash) goto done; } + tlvWriteBlockSig(ctx, der, der_len); + +#if 0 /* Save DER-encoded timestamp to file. */ r = GT_saveFile(sigFile, der, der_len); if(r != GT_OK) { @@ -373,6 +436,7 @@ timestampIt(gtctx ctx, GTDataHash *hash) goto done; } printf("Timestamping succeeded!\n"); +#endif done: GT_free(der); GTTimestamp_free(timestamp); @@ -400,4 +464,5 @@ sigblkFinish(gtctx ctx) /* persist root value here (callback?) */ printf("root hash is:\n"); outputhash(root); timestampIt(ctx, root); + ctx->bInBlk = 0; } diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index f58d46c3..578e51e5 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -61,6 +61,8 @@ OnFileOpen(void *pT, uchar *fn) lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); + pThis->ctx = rsgtCtxNew(fn); + sigblkInit(pThis->ctx); RETiRet; } @@ -71,6 +73,7 @@ OnRecordWrite(void *pT, uchar *rec, rs_size_t lenRec) lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec, rec); + sigblkAddRecord(pThis->ctx, rec, lenRec); RETiRet; } @@ -81,6 +84,7 @@ OnFileClose(void *pT) lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onFileClose\n"); + rsgtCtxDel(pThis->ctx); RETiRet; } diff --git a/tools/logsigner.c b/tools/logsigner.c index 1a9b9ab5..f11371aa 100644 --- a/tools/logsigner.c +++ b/tools/logsigner.c @@ -120,7 +120,7 @@ processFile(char *name) char line[64*1024+1]; gtctx ctx = NULL; - ctx = rsgtCtxNew("SIGFILE"); + ctx = rsgtCtxNew((unsigned char*)"SIGFILE"); sigblkInit(ctx); if(!strcmp(name, "-")) fp = stdin; -- cgit v1.2.3 From 497d1e09638c6e819f0e02b7f940a8a826b63265 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 6 Mar 2013 15:44:11 +0100 Subject: logsig: write block-sig record also some general improvements, e.g. random data is now gathered correctly --- runtime/librsgt.c | 181 ++++++++++++++++++++++++++++++++++++----------------- runtime/librsgt.h | 91 +++++++++++++++++++++++++++ runtime/lmsig_gt.c | 2 +- tools/logsigner.c | 2 +- 4 files changed, 218 insertions(+), 58 deletions(-) create mode 100644 runtime/librsgt.h diff --git a/runtime/librsgt.c b/runtime/librsgt.c index e1d760c6..8aa80771 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -58,7 +58,45 @@ typedef unsigned char uchar; #ifndef VERSION #define VERSION "no-version" #endif -#define LOGSIGHDR "LOGSIG10" + +static inline uint16_t +hashOutputLengthOctets(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: /* paper: SHA1 */ + return 20; + case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ + return 20; + case GT_HASHALG_SHA224: /* paper: SHA2-224 */ + return 28; + case GT_HASHALG_SHA256: /* paper: SHA2-256 */ + return 32; + case GT_HASHALG_SHA384: /* paper: SHA2-384 */ + return 48; + case GT_HASHALG_SHA512: /* paper: SHA2-512 */ + return 64; + default:return 32; + } +} +static inline uint8_t +hashIdentifier(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: /* paper: SHA1 */ + return 0x00; + case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ + return 0x02; + case GT_HASHALG_SHA224: /* paper: SHA2-224 */ + return 0x03; + case GT_HASHALG_SHA256: /* paper: SHA2-256 */ + return 0x01; + case GT_HASHALG_SHA384: /* paper: SHA2-384 */ + return 0x04; + case GT_HASHALG_SHA512: /* paper: SHA2-512 */ + return 0x05; + default:return 0xff; + } +} static void outputhash(GTDataHash *hash) @@ -76,8 +114,6 @@ rsgtInit(char *usragent) { int ret = GT_OK; - srand(time(NULL) * 7); /* see comments in seedIV() */ - ret = GT_init(); if(ret != GT_OK) { fprintf(stderr, "GT_init() failed: %d (%s)\n", @@ -160,20 +196,26 @@ tlvbufAddOctet(gtctx ctx, int8_t octet) ctx->tlvBuf[ctx->tlvIdx++] = octet; } static inline void -tlvbufAddOctetString(gtctx ctx, int8_t *octet, int size) +tlvbufAddOctetString(gtctx ctx, uint8_t *octet, int size) { int i; for(i = 0 ; i < size ; ++i) tlvbufAddOctet(ctx, octet[i]); } static inline void -tlvbufAddInteger(gtctx ctx, uint32_t val) +tlvbufAddInt32(gtctx ctx, uint32_t val) { tlvbufAddOctet(ctx, (val >> 24) & 0xff); tlvbufAddOctet(ctx, (val >> 16) & 0xff); tlvbufAddOctet(ctx, (val >> 8) & 0xff); tlvbufAddOctet(ctx, val & 0xff); } +static inline void +tlvbufAddInt64(gtctx ctx, uint64_t val) +{ + tlvbufAddInt32(ctx, (val >> 32) & 0xffffffff); + tlvbufAddInt32(ctx, val & 0xffffffff); +} void @@ -199,13 +241,34 @@ tlvFlush(gtctx ctx) } void -tlvWriteBlockSig(gtctx ctx, uchar *der, size_t der_len) +tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) { + unsigned tlvlen; + + tlvlen = 1 + 1 /* hash algo TLV */ + + 1 + hashOutputLengthOctets(ctx->hashAlg) /* iv */ + + 1 + 1 + ctx->lenBlkStrtHash /* last hash */ + + 1 + 8 /* rec-count (64 bit integer) */ + + 2 + lenDer /* rfc-3161 */; + /* write top-level TLV object (block-sig */ + tlv16Write(ctx, 0x00, 0x0902, tlvlen); + /* and now write the children */ //FIXME: flags??? + /* hash-algo */ tlv8Write(ctx, 0x00, 0x00, 1); - tlvbufAddOctet(ctx, 0x02); // TODO: hash identifier (Tab. 2)! - tlv16Write(ctx, 0x00, 0x906, (uint16_t) der_len); - tlvbufAddOctetString(ctx, (int8_t*) der, (int) der_len); + tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); + /* block-iv */ + tlv8Write(ctx, 0x00, 0x01, 1); + tlvbufAddOctetString(ctx, ctx->IV, hashOutputLengthOctets(ctx->hashAlg)); + /* last-hash */ + tlv8Write(ctx, 0x00, 0x02, 1); + tlvbufAddOctetString(ctx, ctx->blkStrtHash, ctx->lenBlkStrtHash); + /* rec-count */ + tlv8Write(ctx, 0x00, 0x03, 1); + tlvbufAddInt64(ctx, ctx->nRecords); + /* rfc-3161 */ + tlv16Write(ctx, 0x00, 0x906, lenDer); + tlvbufAddOctetString(ctx, der, lenDer); } void tlvClose(gtctx ctx) @@ -228,36 +291,62 @@ void tlvOpen(gtctx ctx, char *hdr, unsigned lenHdr) // FIXME: check fd == -1 memcpy(ctx->tlvBuf, hdr, lenHdr); ctx->tlvIdx = lenHdr; + /* we now need to obtain the last previous hash, so that + * we can continue the hash chain. + */ + // ... + /* in case we did not have a previous hash (or could not + * obtain it), we start with zero. + */ + ctx->lenBlkStrtHash = hashOutputLengthOctets(ctx->hashAlg); + ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); } +/* + * As of some Linux and security expert I spoke to, /dev/urandom + * provides very strong random numbers, even if it runs out of + * entropy. As far as he knew, this is save for all applications + * (and he had good proof that I currently am not permitted to + * reproduce). -- rgerhards, 2013-03-04 + */ void seedIV(gtctx ctx) { - /* FIXME: this currently is "kindergarten cryptography" - use a - * sufficiently strong PRNG instead! Just a PoC so far! Do NOT - * use in production!!! - * As of some Linux and security expert I spoke to, /dev/urandom - * provides very strong random numbers, even if it runs out of - * entropy. As far as he knew, this is save for all applications - * (and he had good proof that I currently am not permitted to - * reproduce). -- rgerhards, 2013-03-04 + int hashlen; + int fd; + + hashlen = hashOutputLengthOctets(ctx->hashAlg); + ctx->IV = malloc(hashlen); /* do NOT zero-out! */ + /* if we cannot obtain data from /dev/urandom, we use whatever + * is present at the current memory location as random data. Of + * course, this is very weak and we should consider a different + * option, especially when not running under Linux (for Linux, + * unavailability of /dev/urandom is just a theoretic thing, it + * will always work...). -- TODO -- rgerhards, 2013-03-06 */ - ctx->IV = rand() * 1000037; + if((fd = open("/dev/urandom", O_RDONLY)) > 0) { + read(fd, ctx->IV, hashlen); + close(fd); + } } gtctx -rsgtCtxNew(unsigned char *logfn) +rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg) { char fn[MAXFNAME+1]; gtctx ctx; ctx = calloc(1, sizeof(struct gtctx_s)); + ctx->x_prev = NULL; + ctx->hashAlg = hashAlg; + ctx->timestamper = strdup( + "http://stamper.guardtime.net/gt-signingservice"); snprintf(fn, sizeof(fn), "%s.gtsig", logfn); fn[MAXFNAME] = '\0'; /* be on save side */ ctx->sigfilename = (uchar*) strdup(fn); tlvOpen(ctx, LOGSIGHDR, sizeof(LOGSIGHDR)-1); return ctx; } - + void rsgtCtxDel(gtctx ctx) { @@ -278,10 +367,6 @@ void sigblkInit(gtctx ctx) { seedIV(ctx); -// if(ctx->x_prev == NULL) { - ctx->x_prev = NULL; - /* FIXME: do the real thing - or in a later step as currently? */ -// } memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); ctx->nRoots = 0; ctx->nRecords = 0; @@ -297,13 +382,14 @@ bufAddIV(gtctx ctx, uchar *buf, size_t *len) *len += sizeof(ctx->IV); } + /* concat: add hash to buffer */ static inline void bufAddHash(gtctx ctx, uchar *buf, size_t *len, GTDataHash *hash) { if(hash == NULL) { - memset(buf+*len, 0, 32); /* FIXME: depends on hash function! */ - *len += 32; + memcpy(buf+*len, ctx->blkStrtHash, ctx->lenBlkStrtHash); + *len += ctx->lenBlkStrtHash; } else { memcpy(buf+*len, hash->digest, hash->digest_length); *len += hash->digest_length; @@ -311,7 +397,7 @@ bufAddHash(gtctx ctx, uchar *buf, size_t *len, GTDataHash *hash) } /* concat: add tree level to buffer */ static inline void -bufAddLevel(gtctx ctx, uchar *buf, size_t *len, int level) +bufAddLevel(uchar *buf, size_t *len, int level) { memcpy(buf+*len, &level, sizeof(level)); *len += sizeof(level); @@ -321,23 +407,21 @@ bufAddLevel(gtctx ctx, uchar *buf, size_t *len, int level) static void hash_m(gtctx ctx, GTDataHash **m) { +#warning Overall: check GT API return states! // m = hash(concat(ctx->x_prev, IV)); - int r; uchar concatBuf[16*1024]; size_t len = 0; bufAddHash(ctx, concatBuf, &len, ctx->x_prev); bufAddIV(ctx, concatBuf, &len); - r = GTDataHash_create(GT_HASHALG_SHA256, concatBuf, len, m); + GTDataHash_create(ctx->hashAlg, concatBuf, len, m); } static void hash_r(gtctx ctx, GTDataHash **r, const uchar *rec, const size_t len) { // r = hash(canonicalize(rec)); - int ret; - - ret = GTDataHash_create(GT_HASHALG_SHA256, rec, len, r); + GTDataHash_create(ctx->hashAlg, rec, len, r); } @@ -345,22 +429,21 @@ static void hash_node(gtctx ctx, GTDataHash **node, GTDataHash *m, GTDataHash *r, int level) { // x = hash(concat(m, r, 0)); /* hash leaf */ - int ret; uchar concatBuf[16*1024]; size_t len = 0; bufAddHash(ctx, concatBuf, &len, m); bufAddHash(ctx, concatBuf, &len, r); - bufAddLevel(ctx, concatBuf, &len, level); - ret = GTDataHash_create(GT_HASHALG_SHA256, concatBuf, len, node); + bufAddLevel(concatBuf, &len, level); + GTDataHash_create(ctx->hashAlg, concatBuf, len, node); } + void sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) { GTDataHash *x; /* current hash */ GTDataHash *m, *r, *t; int8_t j; - //int ret; hash_m(ctx, &m); hash_r(ctx, &r, rec, len); @@ -391,6 +474,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) ++ctx->nRecords; /* cleanup */ + /* note: x is freed later as part of roots cleanup */ GTDataHash_free(m); GTDataHash_free(r); } @@ -398,15 +482,13 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) static void timestampIt(gtctx ctx, GTDataHash *hash) { + unsigned char *der; + size_t lenDer; int r = GT_OK; GTTimestamp *timestamp = NULL; - unsigned char *der = NULL; - char *sigFile = "logsigner.TIMESTAMP"; - size_t der_len; /* Get the timestamp. */ - r = GTHTTP_createTimestampHash(hash, - "http://stamper.guardtime.net/gt-signingservice", ×tamp); + r = GTHTTP_createTimestampHash(hash, ctx->timestamper, ×tamp); if(r != GT_OK) { fprintf(stderr, "GTHTTP_createTimestampHash() failed: %d (%s)\n", @@ -415,28 +497,15 @@ timestampIt(gtctx ctx, GTDataHash *hash) } /* Encode timestamp. */ - r = GTTimestamp_getDEREncoded(timestamp, &der, &der_len); + r = GTTimestamp_getDEREncoded(timestamp, &der, &lenDer); if(r != GT_OK) { fprintf(stderr, "GTTimestamp_getDEREncoded() failed: %d (%s)\n", r, GT_getErrorString(r)); goto done; } - tlvWriteBlockSig(ctx, der, der_len); + tlvWriteBlockSig(ctx, der, lenDer); -#if 0 - /* Save DER-encoded timestamp to file. */ - r = GT_saveFile(sigFile, der, der_len); - if(r != GT_OK) { - fprintf(stderr, "Cannot save timestamp to file %s: %d (%s)\n", - sigFile, r, GT_getErrorString(r)); - if(r == GT_IO_ERROR) { - fprintf(stderr, "\t%d (%s)\n", errno, strerror(errno)); - } - goto done; - } - printf("Timestamping succeeded!\n"); -#endif done: GT_free(der); GTTimestamp_free(timestamp); diff --git a/runtime/librsgt.h b/runtime/librsgt.h new file mode 100644 index 00000000..cb1829e9 --- /dev/null +++ b/runtime/librsgt.h @@ -0,0 +1,91 @@ +/* librsgt.h - rsyslog's guardtime support library + * + * 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. + */ +#ifndef INCLUDED_LIBRSGT_H +#define INCLUDED_LIBRSGT_H +#include + +/* Max number of roots inside the forest. This permits blocks of up to + * 2^MAX_ROOTS records. We assume that 64 is sufficient for all use + * cases ;) [and 64 is not really a waste of memory, so we do not even + * try to work with reallocs and such...] + */ +#define MAX_ROOTS 64 +#define LOGSIGHDR "LOGSIG10" + +/* context for gt calls. All state data is kept here, this permits + * multiple concurrent callers. + */ +struct gtctx_s { + enum GTHashAlgorithm hashAlg; + uint8_t *IV; /* initial value for blinding masks (where to do we get it from?) */ + GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ + char *timestamper; + unsigned char *sigfilename; + int fd; + unsigned char *blkStrtHash; /* last hash from previous block */ + uint16_t lenBlkStrtHash; + uint64_t nRecords; /* current number of records in current block */ + uint64_t bInBlk; /* are we currently inside a blk --> need to finish on close */ + int8_t nRoots; + /* algo engineering: roots structure is split into two arrays + * in order to improve cache hits. + */ + int8_t roots_valid[MAX_ROOTS]; + GTDataHash *roots_hash[MAX_ROOTS]; + /* data mambers for the associated TLV file */ + char tlvBuf[4096]; + int tlvIdx; /* current index into tlvBuf */ +}; +typedef struct gtctx_s *gtctx; + +typedef struct imprint_s imprint_t; +typedef struct block_sig_s block_sig_t; + +struct imprint_s { + uint8_t hashID; + int len; + uint8_t *data; +}; + +#define SIGID_RFC3161 0 +struct block_sig_s { + uint8_t hashID; + uint8_t sigID; /* what type of *signature*? */ + uint8_t *iv; + imprint_t lastHash; + uint64_t recCount; + struct { + struct { + uint8_t *data; + size_t len; /* must be size_t due to GT API! */ + } der; + } sig; +}; + +void rsgtInit(char *usragent); +void rsgtExit(void); +gtctx rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg); +void rsgtCtxDel(gtctx ctx); +void sigblkInit(gtctx ctx); +void sigblkAddRecord(gtctx ctx, const unsigned char *rec, const size_t len); +void sigblkFinish(gtctx ctx); +void rsgtCtxSetLogfileName(gtctx ctx, char *logfilename); +#endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index 578e51e5..8be3e045 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -61,7 +61,7 @@ OnFileOpen(void *pT, uchar *fn) lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); - pThis->ctx = rsgtCtxNew(fn); + pThis->ctx = rsgtCtxNew(fn, GT_HASHALG_SHA256); sigblkInit(pThis->ctx); RETiRet; diff --git a/tools/logsigner.c b/tools/logsigner.c index f11371aa..f6887696 100644 --- a/tools/logsigner.c +++ b/tools/logsigner.c @@ -120,7 +120,7 @@ processFile(char *name) char line[64*1024+1]; gtctx ctx = NULL; - ctx = rsgtCtxNew((unsigned char*)"SIGFILE"); + ctx = rsgtCtxNew((unsigned char*)"SIGFILE", GT_HASHALG_SHA256); sigblkInit(ctx); if(!strcmp(name, "-")) fp = stdin; -- cgit v1.2.3 From 4786aa9e9ccacc9715588bed8a5bf16fc47b1717 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 7 Mar 2013 15:45:13 +0100 Subject: logsig: add support to read signature files ... still incomplete ... --- runtime/Makefile.am | 2 +- runtime/librsgt.c | 70 +++------- runtime/librsgt.h | 88 +++++++++++++ runtime/librsgt_read.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 5 +- tools/rsgttlvdump.c | 76 +++++++++++ 6 files changed, 532 insertions(+), 57 deletions(-) create mode 100644 runtime/librsgt_read.c create mode 100644 tools/rsgttlvdump.c diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 20824494..c429394d 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -178,7 +178,7 @@ endif # support library for guardtime # if ENABLE_GUARDTIME - librsgt_la_SOURCES = librsgt.c + librsgt_la_SOURCES = librsgt.c librsgt_read.c pkglib_LTLIBRARIES += lmsig_gt.la lmsig_gt_la_SOURCES = lmsig_gt.c diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 8aa80771..e5187c42 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -48,7 +48,7 @@ #include #include #include -#define MAXFNAME 1024 /* TODO: include correct header */ +#define MAXFNAME 1024 #include @@ -59,45 +59,6 @@ typedef unsigned char uchar; #define VERSION "no-version" #endif -static inline uint16_t -hashOutputLengthOctets(uint8_t hashID) -{ - switch(hashID) { - case GT_HASHALG_SHA1: /* paper: SHA1 */ - return 20; - case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ - return 20; - case GT_HASHALG_SHA224: /* paper: SHA2-224 */ - return 28; - case GT_HASHALG_SHA256: /* paper: SHA2-256 */ - return 32; - case GT_HASHALG_SHA384: /* paper: SHA2-384 */ - return 48; - case GT_HASHALG_SHA512: /* paper: SHA2-512 */ - return 64; - default:return 32; - } -} -static inline uint8_t -hashIdentifier(uint8_t hashID) -{ - switch(hashID) { - case GT_HASHALG_SHA1: /* paper: SHA1 */ - return 0x00; - case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ - return 0x02; - case GT_HASHALG_SHA224: /* paper: SHA2-224 */ - return 0x03; - case GT_HASHALG_SHA256: /* paper: SHA2-256 */ - return 0x01; - case GT_HASHALG_SHA384: /* paper: SHA2-384 */ - return 0x04; - case GT_HASHALG_SHA512: /* paper: SHA2-512 */ - return 0x05; - default:return 0xff; - } -} - static void outputhash(GTDataHash *hash) { @@ -108,7 +69,6 @@ outputhash(GTDataHash *hash) } - void rsgtInit(char *usragent) { @@ -144,7 +104,6 @@ tlvbufPhysWrite(gtctx ctx) ssize_t iTotalWritten; ssize_t iWritten; char *pWriteBuf; - fprintf(stderr, "emu: writing TLV file!\n"); lenBuf = ctx->tlvIdx; pWriteBuf = ctx->tlvBuf; @@ -171,8 +130,6 @@ tlvbufPhysWrite(gtctx ctx) pWriteBuf += iWritten; } while(lenBuf > 0); /* Warning: do..while()! */ - //DBGOPRINT((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, (int) iWritten); - finalize_it: ctx->tlvIdx = 0; } @@ -228,7 +185,10 @@ tlv8Write(gtctx ctx, int flags, int tlvtype, int len) void tlv16Write(gtctx ctx, int flags, int tlvtype, uint16_t len) { - tlvbufAddOctet(ctx, ((flags|1) << 13)|tlvtype); + uint16_t typ; + typ = ((flags|1) << 13)|tlvtype; + tlvbufAddOctet(ctx, typ >> 8); + tlvbufAddOctet(ctx, typ & 0xff); tlvbufAddOctet(ctx, (len >> 8) & 0xff); tlvbufAddOctet(ctx, len & 0xff); } @@ -245,11 +205,12 @@ tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) { unsigned tlvlen; - tlvlen = 1 + 1 /* hash algo TLV */ + - 1 + hashOutputLengthOctets(ctx->hashAlg) /* iv */ + - 1 + 1 + ctx->lenBlkStrtHash /* last hash */ + - 1 + 8 /* rec-count (64 bit integer) */ + - 2 + lenDer /* rfc-3161 */; + tlvlen = 2 + 1 /* hash algo TLV */ + + 2 + hashOutputLengthOctets(ctx->hashAlg) /* iv */ + + 2 + 1 + ctx->lenBlkStrtHash /* last hash */ + + 2 + 8 /* rec-count (64 bit integer) */ + + 4 + lenDer /* rfc-3161 */; +printf("TTTT: tlvlen %u, lenDer %u\n", tlvlen, lenDer); /* write top-level TLV object (block-sig */ tlv16Write(ctx, 0x00, 0x0902, tlvlen); /* and now write the children */ @@ -258,13 +219,14 @@ tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) tlv8Write(ctx, 0x00, 0x00, 1); tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); /* block-iv */ - tlv8Write(ctx, 0x00, 0x01, 1); + tlv8Write(ctx, 0x00, 0x01, hashOutputLengthOctets(ctx->hashAlg)); tlvbufAddOctetString(ctx, ctx->IV, hashOutputLengthOctets(ctx->hashAlg)); /* last-hash */ - tlv8Write(ctx, 0x00, 0x02, 1); + tlv8Write(ctx, 0x00, 0x02, ctx->lenBlkStrtHash+1); + tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); tlvbufAddOctetString(ctx, ctx->blkStrtHash, ctx->lenBlkStrtHash); /* rec-count */ - tlv8Write(ctx, 0x00, 0x03, 1); + tlv8Write(ctx, 0x00, 0x03, 8); tlvbufAddInt64(ctx, ctx->nRecords); /* rfc-3161 */ tlv16Write(ctx, 0x00, 0x906, lenDer); @@ -274,7 +236,6 @@ tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) void tlvClose(gtctx ctx) { tlvFlush(ctx); - fprintf(stderr, "emu: close tlv file\n"); close(ctx->fd); ctx->fd = -1; } @@ -285,7 +246,6 @@ void tlvClose(gtctx ctx) */ void tlvOpen(gtctx ctx, char *hdr, unsigned lenHdr) { - fprintf(stderr, "emu: open tlv file '%s'\n", ctx->sigfilename); ctx->fd = open((char*)ctx->sigfilename, O_WRONLY/*|O_APPEND*/|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); // FIXME: check fd == -1 diff --git a/runtime/librsgt.h b/runtime/librsgt.h index cb1829e9..4ce0be30 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -70,6 +70,8 @@ struct block_sig_s { uint8_t hashID; uint8_t sigID; /* what type of *signature*? */ uint8_t *iv; + // TODO: think about the situation where the last hash is + // different from the current one (e.g. config change!) imprint_t lastHash; uint64_t recCount; struct { @@ -80,6 +82,87 @@ struct block_sig_s { } sig; }; +/* error states */ +#define RSGTE_IO 1 /* any kind of io error, including EOF */ +#define RSGTE_FMT 2 /* data fromat error */ +#define RSGTE_INVLTYP 3 /* invalid TLV type record (unexcpected at this point) */ +#define RSGTE_OOM 4 /* ran out of memory */ +#define RSGTE_LEN 5 /* error related to length records */ + + +static inline uint16_t +hashOutputLengthOctets(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: /* paper: SHA1 */ + return 20; + case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ + return 20; + case GT_HASHALG_SHA224: /* paper: SHA2-224 */ + return 28; + case GT_HASHALG_SHA256: /* paper: SHA2-256 */ + return 32; + case GT_HASHALG_SHA384: /* paper: SHA2-384 */ + return 48; + case GT_HASHALG_SHA512: /* paper: SHA2-512 */ + return 64; + default:return 32; + } +} + +static inline uint8_t +hashIdentifier(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: /* paper: SHA1 */ + return 0x00; + case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ + return 0x02; + case GT_HASHALG_SHA224: /* paper: SHA2-224 */ + return 0x03; + case GT_HASHALG_SHA256: /* paper: SHA2-256 */ + return 0x01; + case GT_HASHALG_SHA384: /* paper: SHA2-384 */ + return 0x04; + case GT_HASHALG_SHA512: /* paper: SHA2-512 */ + return 0x05; + default:return 0xff; + } +} +static inline char * +hashAlgName(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: + return "SHA1"; + case GT_HASHALG_RIPEMD160: + return "RIPEMD-160"; + case GT_HASHALG_SHA224: + return "SHA2-224"; + case GT_HASHALG_SHA256: + return "SHA2-256"; + case GT_HASHALG_SHA384: + return "SHA2-384"; + case GT_HASHALG_SHA512: + return "SHA2-512"; + default:return "[unknown]"; + } +} +static inline char * +sigTypeName(uint8_t sigID) +{ + switch(sigID) { + case SIGID_RFC3161: + return "RFC3161"; + default:return "[unknown]"; + } +} +static inline uint16_t +getIVLen(block_sig_t *bs) +{ + return hashOutputLengthOctets(bs->hashID); +} + void rsgtInit(char *usragent); void rsgtExit(void); gtctx rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg); @@ -88,4 +171,9 @@ void sigblkInit(gtctx ctx); void sigblkAddRecord(gtctx ctx, const unsigned char *rec, const size_t len); void sigblkFinish(gtctx ctx); void rsgtCtxSetLogfileName(gtctx ctx, char *logfilename); +/* reader functions */ +int rsgt_tlvrdHeader(FILE *fp, unsigned char *hdr); +int rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj); +void rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose); + #endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c new file mode 100644 index 00000000..fa489a0f --- /dev/null +++ b/runtime/librsgt_read.c @@ -0,0 +1,348 @@ +/* librsgt_read.c - rsyslog's guardtime support library + * This includes functions used for reading signature (and + * other related) files. + * + * This part of the library uses C stdio and expects that the + * caller will open and close the file to be read itself. + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "librsgt.h" + +typedef unsigned char uchar; +#ifndef VERSION +#define VERSION "no-version" +#endif +#define MAXFNAME 1024 + +static int rsgt_read_debug = 0; + +/* macro to obtain next char from file including error tracking */ +#define NEXTC if((c = fgetc(fp)) == EOF) { \ + r = RSGTE_IO; \ + goto done; \ + } + +/* check return state of operation and abort, if non-OK */ +#define CHKr(code) if((r = code) != 0) goto done + +/** + * Read a header from a binary file. + * @param[in] fp file pointer for processing + * @param[in] hdr buffer for the header. Must be 9 bytes + * (8 for header + NUL byte) + * @returns 0 if ok, something else otherwise + */ +int +rsgt_tlvrdHeader(FILE *fp, uchar *hdr) +{ + int r; + if(fread(hdr, 8, 1, fp) != 1) { + r = RSGTE_IO; + goto done; + } + hdr[8] = '\0'; + r = 0; +done: return r; +} + +/* read type & length */ +static int +rsgt_tlvrdTL(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen) +{ + int r = 1; + int c; + + NEXTC; + *tlvtype = c & 0x1f; + if(c & 0x20) { /* tlv16? */ + NEXTC; + *tlvtype = (*tlvtype << 8) | c; + NEXTC; + *tlvlen = c << 8; + NEXTC; + *tlvlen |= c; + } else { + NEXTC; + *tlvlen = c; + } + if(rsgt_read_debug) + printf("read tlvtype %4.4x, len %u\n", (unsigned) *tlvtype, + (unsigned) *tlvlen); + r = 0; +done: return r; +} + +static int +rsgt_tlvrdOctetString(FILE *fp, uint8_t **data, size_t len) +{ + size_t i; + int c, r = 1; + if((*data = (uint8_t*)malloc(len)) == NULL) {r=RSGTE_OOM;goto done;} + for(i = 0 ; i < len ; ++i) { + NEXTC; + (*data)[i] = c; + } + r = 0; +done: return r; +} +static int +rsgt_tlvrdHASH_ALGO(FILE *fp, uint8_t *hashAlg) +{ + int r = 1; + int c; + uint16_t tlvtype, tlvlen; + + CHKr(rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)); + if(!(tlvtype == 0x00 && tlvlen == 1)) { + r = RSGTE_FMT; + goto done; + } + NEXTC; + *hashAlg = c; + r = 0; +done: return r; +} +static int +rsgt_tlvrdBLOCK_IV(FILE *fp, uint8_t **iv) +{ + int r = 1; + uint16_t tlvtype, tlvlen; + + CHKr(rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)); + if(!(tlvtype == 0x01)) { + r = RSGTE_INVLTYP; + goto done; + } + CHKr(rsgt_tlvrdOctetString(fp, iv, tlvlen)); + r = 0; +done: return r; +} +static int +rsgt_tlvrdLAST_HASH(FILE *fp, imprint_t *imp) +{ + int r = 1; + int c; + uint16_t tlvtype, tlvlen; + + CHKr(rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)); + if(!(tlvtype == 0x02)) { r = RSGTE_INVLTYP; goto done; } + NEXTC; + imp->hashID = c; + imp->len = tlvlen - 1; + CHKr(rsgt_tlvrdOctetString(fp, &imp->data, tlvlen-1)); + r = 0; +done: return r; +} +static int +rsgt_tlvrdREC_COUNT(FILE *fp, uint64_t *cnt, size_t *lenInt) +{ + int r = 1; + int i; + int c; + uint64_t val; + uint16_t tlvtype, tlvlen; + + if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; + if(!(tlvtype == 0x03 && tlvlen <= 8)) { r = RSGTE_INVLTYP; goto done; } + *lenInt = tlvlen; + val = 0; + for(i = 0 ; i < tlvlen ; ++i) { + NEXTC; + val = (val << 8) + c; + } + *cnt = val; + r = 0; +done: return r; +} +static int +rsgt_tlvrdSIG(FILE *fp, block_sig_t *bs) +{ + int r = 1; + uint16_t tlvtype, tlvlen; + + CHKr(rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)); + if(!(tlvtype == 0x0906)) { r = RSGTE_INVLTYP; goto done; } + bs->sig.der.len = tlvlen; + bs->sigID = SIGID_RFC3161; + CHKr(rsgt_tlvrdOctetString(fp, &(bs->sig.der.data), tlvlen)); + r = 0; +done: return r; +} + +static int +rsgt_tlvrdBLOCK_SIG(FILE *fp, block_sig_t **blocksig, uint16_t tlvlen) +{ + int r = 1; + size_t lenInt = 0; + uint16_t sizeRead; + block_sig_t *bs; + if((bs = calloc(1, sizeof(block_sig_t))) == NULL) { + r = RSGTE_OOM; + goto done; + } + CHKr(rsgt_tlvrdHASH_ALGO(fp, &(bs->hashID))); + CHKr(rsgt_tlvrdBLOCK_IV(fp, &(bs->iv))); + CHKr(rsgt_tlvrdLAST_HASH(fp, &(bs->lastHash))); + CHKr(rsgt_tlvrdREC_COUNT(fp, &(bs->recCount), &lenInt)); + CHKr(rsgt_tlvrdSIG(fp, bs)); + sizeRead = 2 + 1 /* hash algo TLV */ + + 2 + getIVLen(bs) /* iv */ + + 2 + 1 + bs->lastHash.len /* last hash */ + + 2 + lenInt /* rec-count */ + + 4 + bs->sig.der.len /* rfc-3161 */; + if(sizeRead != tlvlen) { + printf("lenght record error!\n"); + r = RSGTE_LEN; + goto done; + } + *blocksig = bs; + r = 0; +done: return r; +} + + +/** + * Read the next "object" from file. This usually is + * a single TLV, but may be something larger, for + * example in case of a block-sig TLV record. + * Unknown type records are ignored (or run aborted + * if we are not permitted to skip). + * + * @param[in] fp file pointer for processing + * @param[out] tlvtype type of tlv record (top-level for + * structured objects. + * @param[out] tlvlen length of the tlv record value + * @param[out] obj pointer to object; This is a proper + * tlv record structure, which must be casted + * by the caller according to the reported type. + * The object must be freed by the caller (TODO: better way?) + * + * @returns 0 if ok, something else otherwise + */ +int +rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj) +{ + int r = 1; + + if((r = rsgt_tlvrdTL(fp, tlvtype, tlvlen)) != 0) goto done; + switch(*tlvtype) { + case 0x0902: + r = rsgt_tlvrdBLOCK_SIG(fp, obj, *tlvlen); + if(r != 0) goto done; + break; + } + r = 0; +done: return r; +} + + +/* if verbose==0, only the first and last two octets are shown, + * otherwise everything. + */ +static void +outputHexBlob(uint8_t *blob, uint16_t len, uint8_t verbose) +{ + unsigned i; + if(verbose || len <= 6) { + for(i = 0 ; i < len ; ++i) + printf("%2.2x", blob[i]); + } else { + printf("%2.2x%2.2x[...]%2.2x%2.2x", + blob[0], blob[1], + blob[len-2], blob[len-2]); + } +} + +/* return if a blob is all zero */ +static inline int +blobIsZero(uint8_t *blob, uint16_t len) +{ + int i; + for(i = 0 ; i < len ; ++i) + if(blob[i] != 0) + return 0; + return 1; +} +/** + * Output a human-readable representation of a block_sig_t + * to proviced file pointer. This function is mainly inteded for + * debugging purposes or dumping tlv files. + * + * @param[in] fp file pointer to send output to + * @param[in] bsig ponter to block_sig_t to output + * @param[in] verbose if 0, abbreviate blob hexdump, else complete + */ +void +rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose) +{ + fprintf(fp, "Block Signature Record [0x0902]:\n"); + fprintf(fp, "\tPrevious Block Hash:\n"); + fprintf(fp, "\t Algorithm..: %s\n", hashAlgName(bs->lastHash.hashID)); + fprintf(fp, "\t Hash.......: "); + outputHexBlob(bs->lastHash.data, bs->lastHash.len, verbose); + fputc('\n', fp); + if(blobIsZero(bs->lastHash.data, bs->lastHash.len)) + fprintf(fp, "\t NOTE: New Hash Chain Start!\n"); + fprintf(fp, "\tHash Algorithm: %s\n", hashAlgName(bs->hashID)); + fprintf(fp, "\tIV............: "); + outputHexBlob(bs->iv, getIVLen(bs), verbose); + fputc('\n', fp); + fprintf(fp, "\tRecord Count..: %llu\n", bs->recCount); + fprintf(fp, "\tSignature Type: %s\n", sigTypeName(bs->sigID)); + fprintf(fp, "\tSignature Len.: %u\n", bs->sig.der.len); + fprintf(fp, "\tSignature.....: "); + outputHexBlob(bs->sig.der.data, bs->sig.der.len, verbose); + fputc('\n', fp); +} + + +/** + * Output a human-readable representation of a tlv object. + * + * @param[in] fp file pointer to send output to + * @param[in] tlvtype type of tlv object (record) + * @param[in] verbose if 0, abbreviate blob hexdump, else complete + */ +void +rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose) +{ + switch(tlvtype) { + case 0x0902: + rsgt_printBLOCK_SIG(fp, obj, verbose); + break; + default:fprintf(fp, "unknown tlv record %4.4x\n", tlvtype); + break; + } +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 712443c0..ecdce8ea 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -59,10 +59,13 @@ logctl_CPPFLAGS = $(LIBMONGO_CLIENT_CFLAGS) logctl_LDADD = $(LIBMONGO_CLIENT_LIBS) endif if ENABLE_GUARDTIME -bin_PROGRAMS += logsigner +bin_PROGRAMS += logsigner rsgttlvdump logsigner = logsigner.c logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) +rsgttlvdump = rsgttlvdump.c +rsgttlvdump_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) +rsgttlvdump_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) endif endif diff --git a/tools/rsgttlvdump.c b/tools/rsgttlvdump.c new file mode 100644 index 00000000..af5b1a03 --- /dev/null +++ b/tools/rsgttlvdump.c @@ -0,0 +1,76 @@ +/* This is a tool for dumpoing the content of GuardTime TLV + * files in a (somewhat) human-readable manner. + * + * 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 exprs or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include + +#include "librsgt.h" + +typedef unsigned char uchar; + +void +processFile(char *name) +{ + FILE *fp; + uchar hdr[9]; + uint16_t tlvtype, tlvlen; + void *obj; + + if(!strcmp(name, "-")) + fp = stdin; + else { + printf("Processing file %s:\n", name); + if((fp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } + } + if(rsgt_tlvrdHeader(fp, hdr) != 0) goto err; + printf("File Header: '%s'\n", hdr); + if(rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj) != 0) goto err; + rsgt_tlvprint(stdout, tlvtype, obj, 0); + + if(fp != stdin) + fclose(fp); + return; +err: fprintf(stderr, "error processing file %s\n", name); +} + + +int +main(int argc, char *argv[]) +{ + int i; + if(argc == 1) + processFile("-"); + else { + for(i = 1 ; i < argc ; ++i) + processFile(argv[i]); + } + return 0; +} -- cgit v1.2.3 From 85dd75a54cc9032fce7553f284618cfbbf8508ef Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 7 Mar 2013 17:19:45 +0100 Subject: logsig: preserve necessary state during file closed time --- runtime/librsgt.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++------- runtime/librsgt.h | 14 +++++++-- tools/rsgttlvdump.c | 12 ++++++-- 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index e5187c42..4e457005 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -233,11 +233,68 @@ printf("TTTT: tlvlen %u, lenDer %u\n", tlvlen, lenDer); tlvbufAddOctetString(ctx, der, lenDer); } +/* read rsyslog log state file; if we cannot access it or the + * contents looks invalid, we flag it as non-present (and thus + * begin a new hash chain). + * The context is initialized accordingly. + */ +static void +readStateFile(gtctx ctx) +{ + int fd; + struct rsgtstatefile sf; + int rr; + + fd = open((char*)ctx->statefilename, O_RDONLY|O_NOCTTY|O_CLOEXEC, 0600); + if(fd == -1) goto err; + + if(read(fd, &sf, sizeof(sf)) != sizeof(sf)) goto err; + if(strncmp(sf.hdr, "GTSTAT10", 8)) goto err; + + ctx->lenBlkStrtHash = hashOutputLengthOctets(sf.lenHash); + ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); + if((rr=read(fd, ctx->blkStrtHash, ctx->lenBlkStrtHash)) + != ctx->lenBlkStrtHash) { + free(ctx->blkStrtHash); + goto err; + } +return; + +err: + ctx->lenBlkStrtHash = hashOutputLengthOctets(ctx->hashAlg); + ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); +} + +/* persist all information that we need to re-open and append + * to a log signature file. + */ +static void +writeStateFile(gtctx ctx) +{ + int fd; + struct rsgtstatefile sf; + + fd = open((char*)ctx->statefilename, + O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0600); + if(fd == -1) + goto done; + + memcpy(sf.hdr, "GTSTAT10", 8); + sf.hashID = hashIdentifier(ctx->hashAlg); + sf.lenHash = ctx->x_prev->digest_length; + write(fd, &sf, sizeof(sf)); + write(fd, ctx->x_prev->digest, ctx->x_prev->digest_length); + close(fd); +done: return; +} + + void tlvClose(gtctx ctx) { tlvFlush(ctx); close(ctx->fd); ctx->fd = -1; + writeStateFile(ctx); } @@ -247,19 +304,21 @@ void tlvClose(gtctx ctx) void tlvOpen(gtctx ctx, char *hdr, unsigned lenHdr) { ctx->fd = open((char*)ctx->sigfilename, - O_WRONLY/*|O_APPEND*/|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); - // FIXME: check fd == -1 - memcpy(ctx->tlvBuf, hdr, lenHdr); - ctx->tlvIdx = lenHdr; + O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC, 0600); + if(ctx->fd == -1) { + /* looks like we need to create a new file */ + ctx->fd = open((char*)ctx->sigfilename, + O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); + // FIXME: check fd == -1 + memcpy(ctx->tlvBuf, hdr, lenHdr); + ctx->tlvIdx = lenHdr; + } else { + ctx->tlvIdx = 0; /* header already present! */ + } /* we now need to obtain the last previous hash, so that * we can continue the hash chain. */ - // ... - /* in case we did not have a previous hash (or could not - * obtain it), we start with zero. - */ - ctx->lenBlkStrtHash = hashOutputLengthOctets(ctx->hashAlg); - ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); + readStateFile(ctx); } /* @@ -303,6 +362,9 @@ rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg) snprintf(fn, sizeof(fn), "%s.gtsig", logfn); fn[MAXFNAME] = '\0'; /* be on save side */ ctx->sigfilename = (uchar*) strdup(fn); + snprintf(fn, sizeof(fn), "%s.gtstate", logfn); + fn[MAXFNAME] = '\0'; /* be on save side */ + ctx->statefilename = (uchar*) strdup(fn); tlvOpen(ctx, LOGSIGHDR, sizeof(LOGSIGHDR)-1); return ctx; } diff --git a/runtime/librsgt.h b/runtime/librsgt.h index 4ce0be30..ff35d19b 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -39,6 +39,7 @@ struct gtctx_s { GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ char *timestamper; unsigned char *sigfilename; + unsigned char *statefilename; int fd; unsigned char *blkStrtHash; /* last hash from previous block */ uint16_t lenBlkStrtHash; @@ -70,8 +71,6 @@ struct block_sig_s { uint8_t hashID; uint8_t sigID; /* what type of *signature*? */ uint8_t *iv; - // TODO: think about the situation where the last hash is - // different from the current one (e.g. config change!) imprint_t lastHash; uint64_t recCount; struct { @@ -82,6 +81,17 @@ struct block_sig_s { } sig; }; + +/* the following defines the gtstate file record. Currently, this record + * is fixed, we may change that over time. + */ +struct rsgtstatefile { + char hdr[8]; /* must be "GTSTAT10" */ + uint8_t hashID; + uint8_t lenHash; + /* after that, the hash value is contained within the file */ +}; + /* error states */ #define RSGTE_IO 1 /* any kind of io error, including EOF */ #define RSGTE_FMT 2 /* data fromat error */ diff --git a/tools/rsgttlvdump.c b/tools/rsgttlvdump.c index af5b1a03..256a85b5 100644 --- a/tools/rsgttlvdump.c +++ b/tools/rsgttlvdump.c @@ -52,8 +52,15 @@ processFile(char *name) } if(rsgt_tlvrdHeader(fp, hdr) != 0) goto err; printf("File Header: '%s'\n", hdr); - if(rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj) != 0) goto err; - rsgt_tlvprint(stdout, tlvtype, obj, 0); + while(1) { /* we will err out on EOF */ + if(rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj) != 0) { + if(feof(fp)) + break; + else + goto err; + } + rsgt_tlvprint(stdout, tlvtype, obj, 0); + } if(fp != stdin) fclose(fp); @@ -61,7 +68,6 @@ processFile(char *name) err: fprintf(stderr, "error processing file %s\n", name); } - int main(int argc, char *argv[]) { -- cgit v1.2.3 From 212d4e4fe684a4562b284c5b07d9873b0135b10f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 7 Mar 2013 19:00:51 +0100 Subject: logsig: add config parameters (for omfile) --- runtime/librsgt.c | 38 +++++++++++++++++++++++++++---- runtime/librsgt.h | 28 ++++++++++++++++++++++- runtime/lmsig_gt.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- runtime/sigprov.h | 1 + tools/Makefile.am | 9 ++++---- tools/omfile.c | 5 ++-- 6 files changed, 134 insertions(+), 14 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 4e457005..2f336e2d 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -350,15 +350,21 @@ seedIV(gtctx ctx) } gtctx -rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg) +rsgtCtxNew(void) { - char fn[MAXFNAME+1]; gtctx ctx; - ctx = calloc(1, sizeof(struct gtctx_s)); + ctx = calloc(1, sizeof(struct gtctx_s)); ctx->x_prev = NULL; - ctx->hashAlg = hashAlg; + ctx->hashAlg = GT_HASHALG_SHA256; ctx->timestamper = strdup( "http://stamper.guardtime.net/gt-signingservice"); + return ctx; +} + +int +rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn) +{ + char fn[MAXFNAME+1]; snprintf(fn, sizeof(fn), "%s.gtsig", logfn); fn[MAXFNAME] = '\0'; /* be on save side */ ctx->sigfilename = (uchar*) strdup(fn); @@ -366,9 +372,31 @@ rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg) fn[MAXFNAME] = '\0'; /* be on save side */ ctx->statefilename = (uchar*) strdup(fn); tlvOpen(ctx, LOGSIGHDR, sizeof(LOGSIGHDR)-1); - return ctx; + return 0; } + +/* returns 0 on succes, 1 if algo is unknown */ +int +rsgtSetHashFunction(gtctx ctx, char *algName) +{ + int r = 0; + if(!strcmp(algName, "SHA2-256")) + ctx->hashAlg = GT_HASHALG_SHA256; + else if(!strcmp(algName, "SHA2-384")) + ctx->hashAlg = GT_HASHALG_SHA384; + else if(!strcmp(algName, "SHA2-512")) + ctx->hashAlg = GT_HASHALG_SHA512; + else if(!strcmp(algName, "SHA1")) + ctx->hashAlg = GT_HASHALG_SHA1; + else if(!strcmp(algName, "RIPEMD-160")) + ctx->hashAlg = GT_HASHALG_RIPEMD160; + else if(!strcmp(algName, "SHA2-224")) + ctx->hashAlg = GT_HASHALG_SHA224; + else + r = 1; + return r; +} void rsgtCtxDel(gtctx ctx) { diff --git a/runtime/librsgt.h b/runtime/librsgt.h index ff35d19b..a4ea7cc1 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -37,6 +37,9 @@ struct gtctx_s { enum GTHashAlgorithm hashAlg; uint8_t *IV; /* initial value for blinding masks (where to do we get it from?) */ GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ + uint8_t bKeepRecordHashes; + uint8_t bKeepTreeHashes; + uint64_t blockSizeLimit; char *timestamper; unsigned char *sigfilename; unsigned char *statefilename; @@ -172,10 +175,33 @@ getIVLen(block_sig_t *bs) { return hashOutputLengthOctets(bs->hashID); } +static inline void +rsgtSetTimestamper(gtctx ctx, char *timestamper) +{ + free(ctx->timestamper); + ctx->timestamper = strdup(timestamper); +} +static inline void +rsgtSetBlockSizeLimit(gtctx ctx, uint64_t limit) +{ + ctx->blockSizeLimit = limit; +} +static inline void +rsgtSetKeepRecordHashes(gtctx ctx, int val) +{ + ctx->bKeepRecordHashes = val; +} +static inline void +rsgtSetKeepTreeHashes(gtctx ctx, int val) +{ + ctx->bKeepTreeHashes = val; +} +int rsgtSetHashFunction(gtctx ctx, char *algName); void rsgtInit(char *usragent); void rsgtExit(void); -gtctx rsgtCtxNew(unsigned char *logfn, enum GTHashAlgorithm hashAlg); +gtctx rsgtCtxNew(void); +int rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn); void rsgtCtxDel(gtctx ctx); void sigblkInit(gtctx ctx); void sigblkAddRecord(gtctx ctx, const unsigned char *rec, const size_t len); diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index 8be3e045..474c573f 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -41,11 +41,25 @@ DEFobjStaticHelpers DEFobjCurrIf(errmsg) DEFobjCurrIf(glbl) +/* tables for interfacing with the v6 config system */ +static struct cnfparamdescr cnfpdescr[] = { + { "sig.hashfunction", eCmdHdlrGetWord, 0 }, + { "sig.timestampservice", eCmdHdlrGetWord, 0 }, + { "sig.block.sizelimit", eCmdHdlrSize, 0 }, + { "sig.keeprecordhashes", eCmdHdlrBinary, 0 }, + { "sig.keeptreehashes", eCmdHdlrBinary, 0 } +}; +static struct cnfparamblk pblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfpdescr)/sizeof(struct cnfparamdescr), + cnfpdescr + }; /* Standard-Constructor */ -BEGINobjConstruct(lmsig_gt) /* be sure to specify the object type also in END macro! */ +BEGINobjConstruct(lmsig_gt) dbgprintf("DDDD: lmsig_gt: called construct\n"); + pThis->ctx = rsgtCtxNew(); ENDobjConstruct(lmsig_gt) @@ -55,13 +69,61 @@ CODESTARTobjDestruct(lmsig_gt) dbgprintf("DDDD: lmsig_gt: called destruct\n"); ENDobjDestruct(lmsig_gt) + +/* apply all params from param block to us. This must be called + * after construction, but before the OnFileOpen() entry point. + * Defaults are expected to have been set during construction. + */ +rsRetVal +SetCnfParam(void *pT, struct nvlst *lst) +{ + lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + int i; + uchar *cstr; + struct cnfparamvals *pvals; + pvals = nvlstGetParams(lst, &pblk, NULL); + if(Debug) { + dbgprintf("sig param blk in lmsig_gt:\n"); + cnfparamsPrint(&pblk, pvals); + } + + for(i = 0 ; i < pblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(pblk.descr[i].name, "sig.hashfunction")) { + cstr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + if(rsgtSetHashFunction(pThis->ctx, (char*)cstr) != 0) { + errmsg.LogError(0, RS_RET_ERR, "Hash function " + "'%s' unknown - using default", cstr); + } + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { + cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + rsgtSetTimestamper(pThis->ctx, (char*) cstr); + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.block.sizelimit")) { + rsgtSetBlockSizeLimit(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeprecordhashes")) { + rsgtSetKeepRecordHashes(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeptreehashes")) { + rsgtSetKeepTreeHashes(pThis->ctx, pvals[i].val.d.n); + } else { + DBGPRINTF("lmsig_gt: program error, non-handled " + "param '%s'\n", pblk.descr[i].name); + } + } + cnfparamvalsDestruct(pvals, &pblk); + return RS_RET_OK; +} + + static rsRetVal OnFileOpen(void *pT, uchar *fn) { lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); - pThis->ctx = rsgtCtxNew(fn, GT_HASHALG_SHA256); + rsgtCtxOpenFile(pThis->ctx, fn); sigblkInit(pThis->ctx); RETiRet; @@ -95,6 +157,7 @@ CODESTARTobjQueryInterface(lmsig_gt) ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); } pIf->Construct = (rsRetVal(*)(void*)) lmsig_gtConstruct; + pIf->SetCnfParam = SetCnfParam; pIf->Destruct = (rsRetVal(*)(void*)) lmsig_gtDestruct; pIf->OnFileOpen = OnFileOpen; pIf->OnRecordWrite = OnRecordWrite; diff --git a/runtime/sigprov.h b/runtime/sigprov.h index 0154a1f4..5abfb390 100644 --- a/runtime/sigprov.h +++ b/runtime/sigprov.h @@ -27,6 +27,7 @@ /* interface */ BEGINinterface(sigprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(void *ppThis); + rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst); rsRetVal (*Destruct)(void *ppThis); rsRetVal (*OnFileOpen)(void *pThis, uchar *fn); rsRetVal (*OnRecordWrite)(void *pThis, uchar *rec, rs_size_t lenRec); diff --git a/tools/Makefile.am b/tools/Makefile.am index ecdce8ea..8af86cb4 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -59,10 +59,11 @@ logctl_CPPFLAGS = $(LIBMONGO_CLIENT_CFLAGS) logctl_LDADD = $(LIBMONGO_CLIENT_LIBS) endif if ENABLE_GUARDTIME -bin_PROGRAMS += logsigner rsgttlvdump -logsigner = logsigner.c -logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) -logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) +bin_PROGRAMS += rsgttlvdump +#bin_PROGRAMS += logsigner rsgttlvdump +#logsigner = logsigner.c +#logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) +#logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) rsgttlvdump = rsgttlvdump.c rsgttlvdump_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) rsgttlvdump_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) diff --git a/tools/omfile.c b/tools/omfile.c index efdf5e5b..e439d504 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -989,7 +989,7 @@ finalize_it: } static inline void -initSigprov(instanceData *pData) +initSigprov(instanceData *pData, struct nvlst *lst) { uchar szDrvrName[1024]; @@ -1022,6 +1022,7 @@ initSigprov(instanceData *pData) szDrvrName); goto done; } + pData->sigprov.SetCnfParam(pData->sigprovData, lst); dbgprintf("loaded signature provider %s, data instance at %p\n", szDrvrName, pData->sigprovData); @@ -1111,7 +1112,7 @@ CODESTARTnewActInst } if(pData->sigprovName != NULL) { - initSigprov(pData); + initSigprov(pData, lst); } tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); -- cgit v1.2.3 From 80f2b9f0cba2c55dcb1640fe73ceec55eda80348 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 08:34:57 +0100 Subject: logsig: implement sig.block.sizelimit parameter --- runtime/librsgt.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 2f336e2d..72a1dfeb 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -527,6 +527,11 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) /* note: x is freed later as part of roots cleanup */ GTDataHash_free(m); GTDataHash_free(r); + + if(ctx->nRecords == ctx->blockSizeLimit) { + sigblkFinish(ctx); + sigblkInit(ctx); + } } static void @@ -568,6 +573,9 @@ sigblkFinish(gtctx ctx) GTDataHash *root, *rootDel; int8_t j; + if(ctx->nRecords == 0) + goto done; + root = NULL; for(j = 0 ; j < ctx->nRoots ; ++j) { if(root == NULL) { @@ -580,8 +588,12 @@ sigblkFinish(gtctx ctx) GTDataHash_free(rootDel); } } - /* persist root value here (callback?) */ -printf("root hash is:\n"); outputhash(root); timestampIt(ctx, root); + + free(ctx->blkStrtHash); + ctx->lenBlkStrtHash = ctx->x_prev->digest_length; + ctx->blkStrtHash = malloc(ctx->lenBlkStrtHash); + memcpy(ctx->blkStrtHash, ctx->x_prev->digest, ctx->lenBlkStrtHash); +done: ctx->bInBlk = 0; } -- cgit v1.2.3 From b19a3072bde93afa1ad04c684a38ad0a3177ab22 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 09:03:08 +0100 Subject: logsig: write integers in most compact from to tlv file --- runtime/librsgt.c | 60 ++++++++++++++++++++++++++++++++++------------------- tools/rsgttlvdump.c | 7 ++++--- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 72a1dfeb..b3e23628 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -59,15 +59,6 @@ typedef unsigned char uchar; #define VERSION "no-version" #endif -static void -outputhash(GTDataHash *hash) -{ - unsigned i; - for(i = 0 ; i < hash->digest_length ; ++i) - printf("%2.2x", hash->digest[i]); - printf("\n"); -} - void rsgtInit(char *usragent) @@ -159,19 +150,45 @@ tlvbufAddOctetString(gtctx ctx, uint8_t *octet, int size) for(i = 0 ; i < size ; ++i) tlvbufAddOctet(ctx, octet[i]); } -static inline void -tlvbufAddInt32(gtctx ctx, uint32_t val) -{ - tlvbufAddOctet(ctx, (val >> 24) & 0xff); - tlvbufAddOctet(ctx, (val >> 16) & 0xff); - tlvbufAddOctet(ctx, (val >> 8) & 0xff); - tlvbufAddOctet(ctx, val & 0xff); +/* return the actual length in to-be-written octets of an integer */ +static inline uint8_t +tlvbufGetInt64OctetSize(uint64_t val) +{ + if(val >> 56) + return 8; + if((val >> 48) & 0xff) + return 7; + if((val >> 40) & 0xff) + return 6; + if((val >> 32) & 0xff) + return 5; + if((val >> 24) & 0xff) + return 4; + if((val >> 16) & 0xff) + return 3; + if((val >> 8) & 0xff) + return 2; + return 1; } static inline void tlvbufAddInt64(gtctx ctx, uint64_t val) { - tlvbufAddInt32(ctx, (val >> 32) & 0xffffffff); - tlvbufAddInt32(ctx, val & 0xffffffff); + uint8_t doWrite = 0; + if(val >> 56) + tlvbufAddOctet(ctx, (val >> 56) & 0xff), doWrite = 1; + if(doWrite || ((val >> 48) & 0xff)) + tlvbufAddOctet(ctx, (val >> 48) & 0xff), doWrite = 1; + if(doWrite || ((val >> 40) & 0xff)) + tlvbufAddOctet(ctx, (val >> 40) & 0xff), doWrite = 1; + if(doWrite || ((val >> 32) & 0xff)) + tlvbufAddOctet(ctx, (val >> 32) & 0xff), doWrite = 1; + if(doWrite || ((val >> 24) & 0xff)) + tlvbufAddOctet(ctx, (val >> 24) & 0xff), doWrite = 1; + if(doWrite || ((val >> 16) & 0xff)) + tlvbufAddOctet(ctx, (val >> 16) & 0xff), doWrite = 1; + if(doWrite || ((val >> 8) & 0xff)) + tlvbufAddOctet(ctx, (val >> 8) & 0xff), doWrite = 1; + tlvbufAddOctet(ctx, val & 0xff); } @@ -204,13 +221,14 @@ void tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) { unsigned tlvlen; + uint8_t tlvlenRecords; + tlvlenRecords = tlvbufGetInt64OctetSize(ctx->nRecords); tlvlen = 2 + 1 /* hash algo TLV */ + 2 + hashOutputLengthOctets(ctx->hashAlg) /* iv */ + 2 + 1 + ctx->lenBlkStrtHash /* last hash */ + - 2 + 8 /* rec-count (64 bit integer) */ + + 2 + tlvlenRecords /* rec-count */ + 4 + lenDer /* rfc-3161 */; -printf("TTTT: tlvlen %u, lenDer %u\n", tlvlen, lenDer); /* write top-level TLV object (block-sig */ tlv16Write(ctx, 0x00, 0x0902, tlvlen); /* and now write the children */ @@ -226,7 +244,7 @@ printf("TTTT: tlvlen %u, lenDer %u\n", tlvlen, lenDer); tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); tlvbufAddOctetString(ctx, ctx->blkStrtHash, ctx->lenBlkStrtHash); /* rec-count */ - tlv8Write(ctx, 0x00, 0x03, 8); + tlv8Write(ctx, 0x00, 0x03, tlvlenRecords); tlvbufAddInt64(ctx, ctx->nRecords); /* rfc-3161 */ tlv16Write(ctx, 0x00, 0x906, lenDer); diff --git a/tools/rsgttlvdump.c b/tools/rsgttlvdump.c index 256a85b5..9b536db1 100644 --- a/tools/rsgttlvdump.c +++ b/tools/rsgttlvdump.c @@ -40,6 +40,7 @@ processFile(char *name) uchar hdr[9]; uint16_t tlvtype, tlvlen; void *obj; + int r = -1; if(!strcmp(name, "-")) fp = stdin; @@ -50,10 +51,10 @@ processFile(char *name) goto err; } } - if(rsgt_tlvrdHeader(fp, hdr) != 0) goto err; + if((r = rsgt_tlvrdHeader(fp, hdr)) != 0) goto err; printf("File Header: '%s'\n", hdr); while(1) { /* we will err out on EOF */ - if(rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj) != 0) { + if((r = rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj)) != 0) { if(feof(fp)) break; else @@ -65,7 +66,7 @@ processFile(char *name) if(fp != stdin) fclose(fp); return; -err: fprintf(stderr, "error processing file %s\n", name); +err: fprintf(stderr, "error %d processing file %s\n", r, name); } int -- cgit v1.2.3 From e01e72695022ae50af68a47c4aef2c338eff8c8e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 09:38:26 +0100 Subject: logsig: update hash chain algorithm to match updated paper --- runtime/librsgt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index b3e23628..16dc3995 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -515,7 +515,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) hash_m(ctx, &m); hash_r(ctx, &r, rec, len); - hash_node(ctx, &x, m, r, 0); /* hash leaf */ + hash_node(ctx, &x, m, r, 1); /* hash leaf */ /* persists x here if Merkle tree needs to be persisted! */ /* add x to the forest as new leaf, update roots list */ t = x; @@ -527,7 +527,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) t = NULL; } else if(t != NULL) { /* hash interim node */ - hash_node(ctx, &t, ctx->roots_hash[j], t, j+1); + hash_node(ctx, &t, ctx->roots_hash[j], t, j+2); ctx->roots_valid[j] = 0; } } @@ -601,7 +601,7 @@ sigblkFinish(gtctx ctx) ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ } else if(ctx->roots_valid[j]) { rootDel = root; - hash_node(ctx, &root, ctx->roots_hash[j], root, j+1); + hash_node(ctx, &root, ctx->roots_hash[j], root, j+2); ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ GTDataHash_free(rootDel); } -- cgit v1.2.3 From e34de52833507224f5f0522fd205ee4fae81176e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 10:34:04 +0100 Subject: logsig: update implementation to new concatenation rules from paper - when concatenting hashes, the hash ID must be included (actually the imprint, not just the hash is concatenated) - when concatenting integers, the smallest number of octets must be used (actually, we have just level currently, which always is one octet) --- runtime/librsgt.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 16dc3995..aa37dc84 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -456,16 +456,21 @@ static inline void bufAddHash(gtctx ctx, uchar *buf, size_t *len, GTDataHash *hash) { if(hash == NULL) { + // TODO: how to get the REAL HASH ID? --> add field! + buf[*len] = hashIdentifier(ctx->hashAlg); + ++(*len); memcpy(buf+*len, ctx->blkStrtHash, ctx->lenBlkStrtHash); *len += ctx->lenBlkStrtHash; } else { + buf[*len] = hashIdentifier(ctx->hashAlg); + ++(*len); memcpy(buf+*len, hash->digest, hash->digest_length); *len += hash->digest_length; } } /* concat: add tree level to buffer */ static inline void -bufAddLevel(uchar *buf, size_t *len, int level) +bufAddLevel(uchar *buf, size_t *len, uint8_t level) { memcpy(buf+*len, &level, sizeof(level)); *len += sizeof(level); @@ -494,7 +499,8 @@ hash_r(gtctx ctx, GTDataHash **r, const uchar *rec, const size_t len) static void -hash_node(gtctx ctx, GTDataHash **node, GTDataHash *m, GTDataHash *r, int level) +hash_node(gtctx ctx, GTDataHash **node, GTDataHash *m, GTDataHash *r, + uint8_t level) { // x = hash(concat(m, r, 0)); /* hash leaf */ uchar concatBuf[16*1024]; @@ -511,7 +517,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) { GTDataHash *x; /* current hash */ GTDataHash *m, *r, *t; - int8_t j; + uint8_t j; hash_m(ctx, &m); hash_r(ctx, &r, rec, len); -- cgit v1.2.3 From febd1c619d3774766064294358c885f14c74ed7c Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 14:32:06 +0100 Subject: logsig: add capability to write log signature records --- runtime/librsgt.c | 16 ++++++++++++- runtime/librsgt_read.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index aa37dc84..f00c894d 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -217,6 +217,18 @@ tlvFlush(gtctx ctx) tlvbufPhysWrite(ctx); } +void +tlvWriteRecHash(gtctx ctx, GTDataHash *r) +{ + unsigned tlvlen; + + tlvlen = 1 + r->digest_length; + tlv16Write(ctx, 0x00, 0x0900, tlvlen); + tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); + tlvbufAddOctetString(ctx, r->digest, r->digest_length); +dbgprintf("DDDD: tlvWriteRecHash: tlvlen %u, digest_len %u\n", tlvlen, r->digest_length); +} + void tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) { @@ -490,7 +502,7 @@ hash_m(gtctx ctx, GTDataHash **m) GTDataHash_create(ctx->hashAlg, concatBuf, len, m); } -static void +static inline void hash_r(gtctx ctx, GTDataHash **r, const uchar *rec, const size_t len) { // r = hash(canonicalize(rec)); @@ -521,6 +533,8 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) hash_m(ctx, &m); hash_r(ctx, &r, rec, len); + if(ctx->bKeepRecordHashes) + tlvWriteRecHash(ctx, r); hash_node(ctx, &x, m, r, 1); /* hash leaf */ /* persists x here if Merkle tree needs to be persisted! */ /* add x to the forest as new leaf, update roots list */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index fa489a0f..a1c59509 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -160,6 +160,10 @@ rsgt_tlvrdLAST_HASH(FILE *fp, imprint_t *imp) if(!(tlvtype == 0x02)) { r = RSGTE_INVLTYP; goto done; } NEXTC; imp->hashID = c; + if(tlvlen != 1 + hashOutputLengthOctets(imp->hashID)) { + r = RSGTE_LEN; + goto done; + } imp->len = tlvlen - 1; CHKr(rsgt_tlvrdOctetString(fp, &imp->data, tlvlen-1)); r = 0; @@ -223,7 +227,7 @@ rsgt_tlvrdBLOCK_SIG(FILE *fp, block_sig_t **blocksig, uint16_t tlvlen) 2 + lenInt /* rec-count */ + 4 + bs->sig.der.len /* rfc-3161 */; if(sizeRead != tlvlen) { - printf("lenght record error!\n"); + printf("length record error!\n"); r = RSGTE_LEN; goto done; } @@ -232,8 +236,38 @@ rsgt_tlvrdBLOCK_SIG(FILE *fp, block_sig_t **blocksig, uint16_t tlvlen) done: return r; } +static int +rsgt_tlvrdREC_HASH(FILE *fp, imprint_t **imprint, uint16_t tlvlen) +{ + int r = 1; + imprint_t *imp; + int c; -/** + if((imp = calloc(1, sizeof(imprint_t))) == NULL) { + r = RSGTE_OOM; + goto done; + } + if((imp->data = calloc(1, sizeof(imprint_t))) == NULL) { + r = RSGTE_OOM; + goto done; + } + + NEXTC; + imp->hashID = c; + if(tlvlen != 1 + hashOutputLengthOctets(imp->hashID)) { + r = RSGTE_LEN; + goto done; + } + imp->len = tlvlen - 1; + CHKr(rsgt_tlvrdOctetString(fp, &imp->data, tlvlen-1)); + + *imprint = imp; + r = 0; +done: return r; +} + + +/**; * Read the next "object" from file. This usually is * a single TLV, but may be something larger, for * example in case of a block-sig TLV record. @@ -258,6 +292,10 @@ rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj) if((r = rsgt_tlvrdTL(fp, tlvtype, tlvlen)) != 0) goto done; switch(*tlvtype) { + case 0x0900: + r = rsgt_tlvrdREC_HASH(fp, obj, *tlvlen); + if(r != 0) goto done; + break; case 0x0902: r = rsgt_tlvrdBLOCK_SIG(fp, obj, *tlvlen); if(r != 0) goto done; @@ -295,6 +333,22 @@ blobIsZero(uint8_t *blob, uint16_t len) return 0; return 1; } + +static void +rsgt_printIMPRINT(FILE *fp, char *name, imprint_t *imp, uint8_t verbose) +{ + fprintf(fp, "%s", name); + outputHexBlob(imp->data, imp->len, verbose); + fputc('\n', fp); +} + +static void +rsgt_printREC_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) +{ + rsgt_printIMPRINT(fp, "[0x0900]Record Signature Record: ", + imp, verbose); +} + /** * Output a human-readable representation of a block_sig_t * to proviced file pointer. This function is mainly inteded for @@ -307,7 +361,7 @@ blobIsZero(uint8_t *blob, uint16_t len) void rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose) { - fprintf(fp, "Block Signature Record [0x0902]:\n"); + fprintf(fp, "[0x0902]Block Signature Record:\n"); fprintf(fp, "\tPrevious Block Hash:\n"); fprintf(fp, "\t Algorithm..: %s\n", hashAlgName(bs->lastHash.hashID)); fprintf(fp, "\t Hash.......: "); @@ -339,6 +393,9 @@ void rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose) { switch(tlvtype) { + case 0x0900: + rsgt_printREC_HASH(fp, obj, verbose); + break; case 0x0902: rsgt_printBLOCK_SIG(fp, obj, verbose); break; -- cgit v1.2.3 From 39b91ca4e7b40d4a1ab3fb8ac857cecc084b7308 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 15:13:22 +0100 Subject: logsig: add capability to write interim hash records --- runtime/librsgt.c | 14 +++++++++----- runtime/librsgt_read.c | 20 +++++++++++++++++--- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index f00c894d..aa74f09d 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -218,15 +218,14 @@ tlvFlush(gtctx ctx) } void -tlvWriteRecHash(gtctx ctx, GTDataHash *r) +tlvWriteHash(gtctx ctx, uint16_t tlvtype, GTDataHash *r) { unsigned tlvlen; tlvlen = 1 + r->digest_length; - tlv16Write(ctx, 0x00, 0x0900, tlvlen); + tlv16Write(ctx, 0x00, tlvtype, tlvlen); tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); tlvbufAddOctetString(ctx, r->digest, r->digest_length); -dbgprintf("DDDD: tlvWriteRecHash: tlvlen %u, digest_len %u\n", tlvlen, r->digest_length); } void @@ -281,7 +280,7 @@ readStateFile(gtctx ctx) if(read(fd, &sf, sizeof(sf)) != sizeof(sf)) goto err; if(strncmp(sf.hdr, "GTSTAT10", 8)) goto err; - ctx->lenBlkStrtHash = hashOutputLengthOctets(sf.lenHash); + ctx->lenBlkStrtHash = sf.lenHash; ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); if((rr=read(fd, ctx->blkStrtHash, ctx->lenBlkStrtHash)) != ctx->lenBlkStrtHash) { @@ -534,9 +533,11 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) hash_m(ctx, &m); hash_r(ctx, &r, rec, len); if(ctx->bKeepRecordHashes) - tlvWriteRecHash(ctx, r); + tlvWriteHash(ctx, 0x0900, r); hash_node(ctx, &x, m, r, 1); /* hash leaf */ /* persists x here if Merkle tree needs to be persisted! */ + if(ctx->bKeepTreeHashes) + tlvWriteHash(ctx, 0x0901, x); /* add x to the forest as new leaf, update roots list */ t = x; for(j = 0 ; j < ctx->nRoots ; ++j) { @@ -549,6 +550,9 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) /* hash interim node */ hash_node(ctx, &t, ctx->roots_hash[j], t, j+2); ctx->roots_valid[j] = 0; + // TODO: check if this is correct location (paper!) + if(ctx->bKeepTreeHashes) + tlvWriteHash(ctx, 0x0901, t); } } if(t != NULL) { diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index a1c59509..be78580d 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -237,7 +237,7 @@ done: return r; } static int -rsgt_tlvrdREC_HASH(FILE *fp, imprint_t **imprint, uint16_t tlvlen) +rsgt_tlvrdIMPRINT(FILE *fp, imprint_t **imprint, uint16_t tlvlen) { int r = 1; imprint_t *imp; @@ -293,7 +293,11 @@ rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj) if((r = rsgt_tlvrdTL(fp, tlvtype, tlvlen)) != 0) goto done; switch(*tlvtype) { case 0x0900: - r = rsgt_tlvrdREC_HASH(fp, obj, *tlvlen); + r = rsgt_tlvrdIMPRINT(fp, obj, *tlvlen); + if(r != 0) goto done; + break; + case 0x0901: + r = rsgt_tlvrdIMPRINT(fp, obj, *tlvlen); if(r != 0) goto done; break; case 0x0902: @@ -345,7 +349,14 @@ rsgt_printIMPRINT(FILE *fp, char *name, imprint_t *imp, uint8_t verbose) static void rsgt_printREC_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) { - rsgt_printIMPRINT(fp, "[0x0900]Record Signature Record: ", + rsgt_printIMPRINT(fp, "[0x0900]Record hash................: ", + imp, verbose); +} + +static void +rsgt_printINT_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) +{ + rsgt_printIMPRINT(fp, "[0x0901]Intermediate aggregate hash: ", imp, verbose); } @@ -396,6 +407,9 @@ rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose) case 0x0900: rsgt_printREC_HASH(fp, obj, verbose); break; + case 0x0901: + rsgt_printINT_HASH(fp, obj, verbose); + break; case 0x0902: rsgt_printBLOCK_SIG(fp, obj, verbose); break; -- cgit v1.2.3 From c29b6b6415f668cc3933e7fcc40425fe9f18607f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 17:15:18 +0100 Subject: logsig: fix subroot forest handling --- runtime/librsgt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index aa74f09d..9d112a08 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -542,14 +542,15 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) t = x; for(j = 0 ; j < ctx->nRoots ; ++j) { if(ctx->roots_valid[j] == 0) { - GTDataHash_free(ctx->roots_hash[j]); ctx->roots_hash[j] = t; ctx->roots_valid[j] = 1; t = NULL; + break; } else if(t != NULL) { /* hash interim node */ hash_node(ctx, &t, ctx->roots_hash[j], t, j+2); ctx->roots_valid[j] = 0; + GTDataHash_free(ctx->roots_hash[j]); // TODO: check if this is correct location (paper!) if(ctx->bKeepTreeHashes) tlvWriteHash(ctx, 0x0901, t); @@ -558,6 +559,7 @@ sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) if(t != NULL) { /* new level, append "at the top" */ ctx->roots_hash[ctx->nRoots] = t; + ctx->roots_valid[ctx->nRoots] = 1; ++ctx->nRoots; assert(ctx->nRoots < MAX_ROOTS); t = NULL; -- cgit v1.2.3 From 171482ee70ca74e907ea892f33daf8c07433dcd8 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 8 Mar 2013 17:24:09 +0100 Subject: some cleanup --- runtime/librsgt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 9d112a08..f2ad2727 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -272,7 +272,6 @@ readStateFile(gtctx ctx) { int fd; struct rsgtstatefile sf; - int rr; fd = open((char*)ctx->statefilename, O_RDONLY|O_NOCTTY|O_CLOEXEC, 0600); if(fd == -1) goto err; @@ -282,7 +281,7 @@ readStateFile(gtctx ctx) ctx->lenBlkStrtHash = sf.lenHash; ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); - if((rr=read(fd, ctx->blkStrtHash, ctx->lenBlkStrtHash)) + if(read(fd, ctx->blkStrtHash, ctx->lenBlkStrtHash) != ctx->lenBlkStrtHash) { free(ctx->blkStrtHash); goto err; -- cgit v1.2.3 From d0289157529dd219c16c0c27f0e6580312ae6470 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 11 Mar 2013 12:35:15 +0100 Subject: logsig: support dynafiles --- runtime/librsgt.c | 359 ++++++++++++++++++++++++++++------------------------- runtime/librsgt.h | 36 ++++-- runtime/lmsig_gt.c | 17 ++- runtime/sigprov.h | 6 +- tools/omfile.c | 56 +++++---- 5 files changed, 262 insertions(+), 212 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index f2ad2727..83a2fe05 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -88,19 +88,34 @@ rsgtExit(void) } +static inline gtfile +rsgtfileConstruct(gtctx ctx) +{ + gtfile gf; + if((gf = calloc(1, sizeof(struct gtfile_s))) == NULL) + goto done; + gf->ctx = ctx; + gf->hashAlg = ctx->hashAlg; + gf->bKeepRecordHashes = ctx->bKeepRecordHashes; + gf->bKeepTreeHashes = ctx->bKeepTreeHashes; + gf->x_prev = NULL; + +done: return gf; +} + static inline void -tlvbufPhysWrite(gtctx ctx) +tlvbufPhysWrite(gtfile gf) { ssize_t lenBuf; ssize_t iTotalWritten; ssize_t iWritten; char *pWriteBuf; - lenBuf = ctx->tlvIdx; - pWriteBuf = ctx->tlvBuf; + lenBuf = gf->tlvIdx; + pWriteBuf = gf->tlvBuf; iTotalWritten = 0; do { - iWritten = write(ctx->fd, pWriteBuf, lenBuf); + iWritten = write(gf->fd, pWriteBuf, lenBuf); if(iWritten < 0) { //char errStr[1024]; int err = errno; @@ -122,14 +137,14 @@ tlvbufPhysWrite(gtctx ctx) } while(lenBuf > 0); /* Warning: do..while()! */ finalize_it: - ctx->tlvIdx = 0; + gf->tlvIdx = 0; } static inline void -tlvbufChkWrite(gtctx ctx) +tlvbufChkWrite(gtfile gf) { - if(ctx->tlvIdx == sizeof(ctx->tlvBuf)) { - tlvbufPhysWrite(ctx); + if(gf->tlvIdx == sizeof(gf->tlvBuf)) { + tlvbufPhysWrite(gf); } } @@ -138,17 +153,17 @@ tlvbufChkWrite(gtctx ctx) * output is written only on flush or close. */ static inline void -tlvbufAddOctet(gtctx ctx, int8_t octet) +tlvbufAddOctet(gtfile gf, int8_t octet) { - tlvbufChkWrite(ctx); - ctx->tlvBuf[ctx->tlvIdx++] = octet; + tlvbufChkWrite(gf); + gf->tlvBuf[gf->tlvIdx++] = octet; } static inline void -tlvbufAddOctetString(gtctx ctx, uint8_t *octet, int size) +tlvbufAddOctetString(gtfile gf, uint8_t *octet, int size) { int i; for(i = 0 ; i < size ; ++i) - tlvbufAddOctet(ctx, octet[i]); + tlvbufAddOctet(gf, octet[i]); } /* return the actual length in to-be-written octets of an integer */ static inline uint8_t @@ -171,95 +186,95 @@ tlvbufGetInt64OctetSize(uint64_t val) return 1; } static inline void -tlvbufAddInt64(gtctx ctx, uint64_t val) +tlvbufAddInt64(gtfile gf, uint64_t val) { uint8_t doWrite = 0; if(val >> 56) - tlvbufAddOctet(ctx, (val >> 56) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, (val >> 56) & 0xff), doWrite = 1; if(doWrite || ((val >> 48) & 0xff)) - tlvbufAddOctet(ctx, (val >> 48) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, (val >> 48) & 0xff), doWrite = 1; if(doWrite || ((val >> 40) & 0xff)) - tlvbufAddOctet(ctx, (val >> 40) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, (val >> 40) & 0xff), doWrite = 1; if(doWrite || ((val >> 32) & 0xff)) - tlvbufAddOctet(ctx, (val >> 32) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, (val >> 32) & 0xff), doWrite = 1; if(doWrite || ((val >> 24) & 0xff)) - tlvbufAddOctet(ctx, (val >> 24) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, (val >> 24) & 0xff), doWrite = 1; if(doWrite || ((val >> 16) & 0xff)) - tlvbufAddOctet(ctx, (val >> 16) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, (val >> 16) & 0xff), doWrite = 1; if(doWrite || ((val >> 8) & 0xff)) - tlvbufAddOctet(ctx, (val >> 8) & 0xff), doWrite = 1; - tlvbufAddOctet(ctx, val & 0xff); + tlvbufAddOctet(gf, (val >> 8) & 0xff), doWrite = 1; + tlvbufAddOctet(gf, val & 0xff); } void -tlv8Write(gtctx ctx, int flags, int tlvtype, int len) +tlv8Write(gtfile gf, int flags, int tlvtype, int len) { - tlvbufAddOctet(ctx, (flags << 5)|tlvtype); - tlvbufAddOctet(ctx, len & 0xff); + tlvbufAddOctet(gf, (flags << 5)|tlvtype); + tlvbufAddOctet(gf, len & 0xff); } void -tlv16Write(gtctx ctx, int flags, int tlvtype, uint16_t len) +tlv16Write(gtfile gf, int flags, int tlvtype, uint16_t len) { uint16_t typ; typ = ((flags|1) << 13)|tlvtype; - tlvbufAddOctet(ctx, typ >> 8); - tlvbufAddOctet(ctx, typ & 0xff); - tlvbufAddOctet(ctx, (len >> 8) & 0xff); - tlvbufAddOctet(ctx, len & 0xff); + tlvbufAddOctet(gf, typ >> 8); + tlvbufAddOctet(gf, typ & 0xff); + tlvbufAddOctet(gf, (len >> 8) & 0xff); + tlvbufAddOctet(gf, len & 0xff); } void -tlvFlush(gtctx ctx) +tlvFlush(gtfile gf) { - if(ctx->tlvIdx != 0) - tlvbufPhysWrite(ctx); + if(gf->tlvIdx != 0) + tlvbufPhysWrite(gf); } void -tlvWriteHash(gtctx ctx, uint16_t tlvtype, GTDataHash *r) +tlvWriteHash(gtfile gf, uint16_t tlvtype, GTDataHash *r) { unsigned tlvlen; tlvlen = 1 + r->digest_length; - tlv16Write(ctx, 0x00, tlvtype, tlvlen); - tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); - tlvbufAddOctetString(ctx, r->digest, r->digest_length); + tlv16Write(gf, 0x00, tlvtype, tlvlen); + tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + tlvbufAddOctetString(gf, r->digest, r->digest_length); } void -tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) +tlvWriteBlockSig(gtfile gf, uchar *der, uint16_t lenDer) { unsigned tlvlen; uint8_t tlvlenRecords; - tlvlenRecords = tlvbufGetInt64OctetSize(ctx->nRecords); + tlvlenRecords = tlvbufGetInt64OctetSize(gf->nRecords); tlvlen = 2 + 1 /* hash algo TLV */ + - 2 + hashOutputLengthOctets(ctx->hashAlg) /* iv */ + - 2 + 1 + ctx->lenBlkStrtHash /* last hash */ + + 2 + hashOutputLengthOctets(gf->hashAlg) /* iv */ + + 2 + 1 + gf->lenBlkStrtHash /* last hash */ + 2 + tlvlenRecords /* rec-count */ + 4 + lenDer /* rfc-3161 */; /* write top-level TLV object (block-sig */ - tlv16Write(ctx, 0x00, 0x0902, tlvlen); + tlv16Write(gf, 0x00, 0x0902, tlvlen); /* and now write the children */ //FIXME: flags??? /* hash-algo */ - tlv8Write(ctx, 0x00, 0x00, 1); - tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); + tlv8Write(gf, 0x00, 0x00, 1); + tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); /* block-iv */ - tlv8Write(ctx, 0x00, 0x01, hashOutputLengthOctets(ctx->hashAlg)); - tlvbufAddOctetString(ctx, ctx->IV, hashOutputLengthOctets(ctx->hashAlg)); + tlv8Write(gf, 0x00, 0x01, hashOutputLengthOctets(gf->hashAlg)); + tlvbufAddOctetString(gf, gf->IV, hashOutputLengthOctets(gf->hashAlg)); /* last-hash */ - tlv8Write(ctx, 0x00, 0x02, ctx->lenBlkStrtHash+1); - tlvbufAddOctet(ctx, hashIdentifier(ctx->hashAlg)); - tlvbufAddOctetString(ctx, ctx->blkStrtHash, ctx->lenBlkStrtHash); + tlv8Write(gf, 0x00, 0x02, gf->lenBlkStrtHash+1); + tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + tlvbufAddOctetString(gf, gf->blkStrtHash, gf->lenBlkStrtHash); /* rec-count */ - tlv8Write(ctx, 0x00, 0x03, tlvlenRecords); - tlvbufAddInt64(ctx, ctx->nRecords); + tlv8Write(gf, 0x00, 0x03, tlvlenRecords); + tlvbufAddInt64(gf, gf->nRecords); /* rfc-3161 */ - tlv16Write(ctx, 0x00, 0x906, lenDer); - tlvbufAddOctetString(ctx, der, lenDer); + tlv16Write(gf, 0x00, 0x906, lenDer); + tlvbufAddOctetString(gf, der, lenDer); } /* read rsyslog log state file; if we cannot access it or the @@ -268,85 +283,85 @@ tlvWriteBlockSig(gtctx ctx, uchar *der, uint16_t lenDer) * The context is initialized accordingly. */ static void -readStateFile(gtctx ctx) +readStateFile(gtfile gf) { int fd; struct rsgtstatefile sf; - fd = open((char*)ctx->statefilename, O_RDONLY|O_NOCTTY|O_CLOEXEC, 0600); + fd = open((char*)gf->statefilename, O_RDONLY|O_NOCTTY|O_CLOEXEC, 0600); if(fd == -1) goto err; if(read(fd, &sf, sizeof(sf)) != sizeof(sf)) goto err; if(strncmp(sf.hdr, "GTSTAT10", 8)) goto err; - ctx->lenBlkStrtHash = sf.lenHash; - ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); - if(read(fd, ctx->blkStrtHash, ctx->lenBlkStrtHash) - != ctx->lenBlkStrtHash) { - free(ctx->blkStrtHash); + gf->lenBlkStrtHash = sf.lenHash; + gf->blkStrtHash = calloc(1, gf->lenBlkStrtHash); + if(read(fd, gf->blkStrtHash, gf->lenBlkStrtHash) + != gf->lenBlkStrtHash) { + free(gf->blkStrtHash); goto err; } return; err: - ctx->lenBlkStrtHash = hashOutputLengthOctets(ctx->hashAlg); - ctx->blkStrtHash = calloc(1, ctx->lenBlkStrtHash); + gf->lenBlkStrtHash = hashOutputLengthOctets(gf->hashAlg); + gf->blkStrtHash = calloc(1, gf->lenBlkStrtHash); } /* persist all information that we need to re-open and append * to a log signature file. */ static void -writeStateFile(gtctx ctx) +writeStateFile(gtfile gf) { int fd; struct rsgtstatefile sf; - fd = open((char*)ctx->statefilename, + fd = open((char*)gf->statefilename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0600); if(fd == -1) goto done; memcpy(sf.hdr, "GTSTAT10", 8); - sf.hashID = hashIdentifier(ctx->hashAlg); - sf.lenHash = ctx->x_prev->digest_length; + sf.hashID = hashIdentifier(gf->hashAlg); + sf.lenHash = gf->x_prev->digest_length; write(fd, &sf, sizeof(sf)); - write(fd, ctx->x_prev->digest, ctx->x_prev->digest_length); + write(fd, gf->x_prev->digest, gf->x_prev->digest_length); close(fd); done: return; } -void tlvClose(gtctx ctx) +void tlvClose(gtfile gf) { - tlvFlush(ctx); - close(ctx->fd); - ctx->fd = -1; - writeStateFile(ctx); + tlvFlush(gf); + close(gf->fd); + gf->fd = -1; + writeStateFile(gf); } /* note: if file exists, the last hash for chaining must * be read from file. */ -void tlvOpen(gtctx ctx, char *hdr, unsigned lenHdr) +void tlvOpen(gtfile gf, char *hdr, unsigned lenHdr) { - ctx->fd = open((char*)ctx->sigfilename, + gf->fd = open((char*)gf->sigfilename, O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC, 0600); - if(ctx->fd == -1) { + if(gf->fd == -1) { /* looks like we need to create a new file */ - ctx->fd = open((char*)ctx->sigfilename, + gf->fd = open((char*)gf->sigfilename, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); // FIXME: check fd == -1 - memcpy(ctx->tlvBuf, hdr, lenHdr); - ctx->tlvIdx = lenHdr; + memcpy(gf->tlvBuf, hdr, lenHdr); + gf->tlvIdx = lenHdr; } else { - ctx->tlvIdx = 0; /* header already present! */ + gf->tlvIdx = 0; /* header already present! */ } /* we now need to obtain the last previous hash, so that * we can continue the hash chain. */ - readStateFile(ctx); + readStateFile(gf); } /* @@ -357,13 +372,13 @@ void tlvOpen(gtctx ctx, char *hdr, unsigned lenHdr) * reproduce). -- rgerhards, 2013-03-04 */ void -seedIV(gtctx ctx) +seedIV(gtfile gf) { int hashlen; int fd; - hashlen = hashOutputLengthOctets(ctx->hashAlg); - ctx->IV = malloc(hashlen); /* do NOT zero-out! */ + hashlen = hashOutputLengthOctets(gf->hashAlg); + gf->IV = malloc(hashlen); /* do NOT zero-out! */ /* if we cannot obtain data from /dev/urandom, we use whatever * is present at the current memory location as random data. Of * course, this is very weak and we should consider a different @@ -372,7 +387,7 @@ seedIV(gtctx ctx) * will always work...). -- TODO -- rgerhards, 2013-03-06 */ if((fd = open("/dev/urandom", O_RDONLY)) > 0) { - read(fd, ctx->IV, hashlen); + read(fd, gf->IV, hashlen); close(fd); } } @@ -382,25 +397,30 @@ rsgtCtxNew(void) { gtctx ctx; ctx = calloc(1, sizeof(struct gtctx_s)); - ctx->x_prev = NULL; ctx->hashAlg = GT_HASHALG_SHA256; ctx->timestamper = strdup( "http://stamper.guardtime.net/gt-signingservice"); return ctx; } -int +/* either returns gtfile object or NULL if something went wrong */ +gtfile rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn) { + gtfile gf; char fn[MAXFNAME+1]; + + if((gf = rsgtfileConstruct(ctx)) == NULL) + goto done; + snprintf(fn, sizeof(fn), "%s.gtsig", logfn); fn[MAXFNAME] = '\0'; /* be on save side */ - ctx->sigfilename = (uchar*) strdup(fn); + gf->sigfilename = (uchar*) strdup(fn); snprintf(fn, sizeof(fn), "%s.gtstate", logfn); fn[MAXFNAME] = '\0'; /* be on save side */ - ctx->statefilename = (uchar*) strdup(fn); - tlvOpen(ctx, LOGSIGHDR, sizeof(LOGSIGHDR)-1); - return 0; + gf->statefilename = (uchar*) strdup(fn); + tlvOpen(gf, LOGSIGHDR, sizeof(LOGSIGHDR)-1); +done: return gf; } @@ -425,54 +445,61 @@ rsgtSetHashFunction(gtctx ctx, char *algName) r = 1; return r; } + void -rsgtCtxDel(gtctx ctx) +rsgtfileDestruct(gtfile gf) { - if(ctx == NULL) + if(gf == NULL) goto done; - if(ctx->bInBlk) - sigblkFinish(ctx); - tlvClose(ctx); - free(ctx->sigfilename); - free(ctx); - /* TODO: persist! */ + if(gf->bInBlk) + sigblkFinish(gf); + tlvClose(gf); + free(gf->sigfilename); + free(gf); done: return; } +void +rsgtCtxDel(gtctx ctx) +{ + if(ctx != NULL) + free(ctx); +} + /* new sigblk is initialized, but maybe in existing ctx */ void -sigblkInit(gtctx ctx) +sigblkInit(gtfile gf) { - seedIV(ctx); - memset(ctx->roots_valid, 0, sizeof(ctx->roots_valid)/sizeof(char)); - ctx->nRoots = 0; - ctx->nRecords = 0; - ctx->bInBlk = 1; + seedIV(gf); + memset(gf->roots_valid, 0, sizeof(gf->roots_valid)/sizeof(char)); + gf->nRoots = 0; + gf->nRecords = 0; + gf->bInBlk = 1; } /* concat: add IV to buffer */ static inline void -bufAddIV(gtctx ctx, uchar *buf, size_t *len) +bufAddIV(gtfile gf, uchar *buf, size_t *len) { - memcpy(buf+*len, &ctx->IV, sizeof(ctx->IV)); - *len += sizeof(ctx->IV); + memcpy(buf+*len, &gf->IV, sizeof(gf->IV)); + *len += sizeof(gf->IV); } /* concat: add hash to buffer */ static inline void -bufAddHash(gtctx ctx, uchar *buf, size_t *len, GTDataHash *hash) +bufAddHash(gtfile gf, uchar *buf, size_t *len, GTDataHash *hash) { if(hash == NULL) { // TODO: how to get the REAL HASH ID? --> add field! - buf[*len] = hashIdentifier(ctx->hashAlg); + buf[*len] = hashIdentifier(gf->hashAlg); ++(*len); - memcpy(buf+*len, ctx->blkStrtHash, ctx->lenBlkStrtHash); - *len += ctx->lenBlkStrtHash; + memcpy(buf+*len, gf->blkStrtHash, gf->lenBlkStrtHash); + *len += gf->lenBlkStrtHash; } else { - buf[*len] = hashIdentifier(ctx->hashAlg); + buf[*len] = hashIdentifier(gf->hashAlg); ++(*len); memcpy(buf+*len, hash->digest, hash->digest_length); *len += hash->digest_length; @@ -488,97 +515,97 @@ bufAddLevel(uchar *buf, size_t *len, uint8_t level) static void -hash_m(gtctx ctx, GTDataHash **m) +hash_m(gtfile gf, GTDataHash **m) { #warning Overall: check GT API return states! - // m = hash(concat(ctx->x_prev, IV)); + // m = hash(concat(gf->x_prev, IV)); uchar concatBuf[16*1024]; size_t len = 0; - bufAddHash(ctx, concatBuf, &len, ctx->x_prev); - bufAddIV(ctx, concatBuf, &len); - GTDataHash_create(ctx->hashAlg, concatBuf, len, m); + bufAddHash(gf, concatBuf, &len, gf->x_prev); + bufAddIV(gf, concatBuf, &len); + GTDataHash_create(gf->hashAlg, concatBuf, len, m); } static inline void -hash_r(gtctx ctx, GTDataHash **r, const uchar *rec, const size_t len) +hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len) { // r = hash(canonicalize(rec)); - GTDataHash_create(ctx->hashAlg, rec, len, r); + GTDataHash_create(gf->hashAlg, rec, len, r); } static void -hash_node(gtctx ctx, GTDataHash **node, GTDataHash *m, GTDataHash *r, +hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level) { // x = hash(concat(m, r, 0)); /* hash leaf */ uchar concatBuf[16*1024]; size_t len = 0; - bufAddHash(ctx, concatBuf, &len, m); - bufAddHash(ctx, concatBuf, &len, r); + bufAddHash(gf, concatBuf, &len, m); + bufAddHash(gf, concatBuf, &len, r); bufAddLevel(concatBuf, &len, level); - GTDataHash_create(ctx->hashAlg, concatBuf, len, node); + GTDataHash_create(gf->hashAlg, concatBuf, len, node); } void -sigblkAddRecord(gtctx ctx, const uchar *rec, const size_t len) +sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) { GTDataHash *x; /* current hash */ GTDataHash *m, *r, *t; uint8_t j; - hash_m(ctx, &m); - hash_r(ctx, &r, rec, len); - if(ctx->bKeepRecordHashes) - tlvWriteHash(ctx, 0x0900, r); - hash_node(ctx, &x, m, r, 1); /* hash leaf */ + hash_m(gf, &m); + hash_r(gf, &r, rec, len); + if(gf->bKeepRecordHashes) + tlvWriteHash(gf, 0x0900, r); + hash_node(gf, &x, m, r, 1); /* hash leaf */ /* persists x here if Merkle tree needs to be persisted! */ - if(ctx->bKeepTreeHashes) - tlvWriteHash(ctx, 0x0901, x); + if(gf->bKeepTreeHashes) + tlvWriteHash(gf, 0x0901, x); /* add x to the forest as new leaf, update roots list */ t = x; - for(j = 0 ; j < ctx->nRoots ; ++j) { - if(ctx->roots_valid[j] == 0) { - ctx->roots_hash[j] = t; - ctx->roots_valid[j] = 1; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(gf->roots_valid[j] == 0) { + gf->roots_hash[j] = t; + gf->roots_valid[j] = 1; t = NULL; break; } else if(t != NULL) { /* hash interim node */ - hash_node(ctx, &t, ctx->roots_hash[j], t, j+2); - ctx->roots_valid[j] = 0; - GTDataHash_free(ctx->roots_hash[j]); + hash_node(gf, &t, gf->roots_hash[j], t, j+2); + gf->roots_valid[j] = 0; + GTDataHash_free(gf->roots_hash[j]); // TODO: check if this is correct location (paper!) - if(ctx->bKeepTreeHashes) - tlvWriteHash(ctx, 0x0901, t); + if(gf->bKeepTreeHashes) + tlvWriteHash(gf, 0x0901, t); } } if(t != NULL) { /* new level, append "at the top" */ - ctx->roots_hash[ctx->nRoots] = t; - ctx->roots_valid[ctx->nRoots] = 1; - ++ctx->nRoots; - assert(ctx->nRoots < MAX_ROOTS); + gf->roots_hash[gf->nRoots] = t; + gf->roots_valid[gf->nRoots] = 1; + ++gf->nRoots; + assert(gf->nRoots < MAX_ROOTS); t = NULL; } - ctx->x_prev = x; /* single var may be sufficient */ - ++ctx->nRecords; + gf->x_prev = x; /* single var may be sufficient */ + ++gf->nRecords; /* cleanup */ /* note: x is freed later as part of roots cleanup */ GTDataHash_free(m); GTDataHash_free(r); - if(ctx->nRecords == ctx->blockSizeLimit) { - sigblkFinish(ctx); - sigblkInit(ctx); + if(gf->nRecords == gf->blockSizeLimit) { + sigblkFinish(gf); + sigblkInit(gf); } } static void -timestampIt(gtctx ctx, GTDataHash *hash) +timestampIt(gtfile gf, GTDataHash *hash) { unsigned char *der; size_t lenDer; @@ -586,7 +613,7 @@ timestampIt(gtctx ctx, GTDataHash *hash) GTTimestamp *timestamp = NULL; /* Get the timestamp. */ - r = GTHTTP_createTimestampHash(hash, ctx->timestamper, ×tamp); + r = GTHTTP_createTimestampHash(hash, gf->ctx->timestamper, ×tamp); if(r != GT_OK) { fprintf(stderr, "GTHTTP_createTimestampHash() failed: %d (%s)\n", @@ -602,7 +629,7 @@ timestampIt(gtctx ctx, GTDataHash *hash) goto done; } - tlvWriteBlockSig(ctx, der, lenDer); + tlvWriteBlockSig(gf, der, lenDer); done: GT_free(der); @@ -611,32 +638,32 @@ done: void -sigblkFinish(gtctx ctx) +sigblkFinish(gtfile gf) { GTDataHash *root, *rootDel; int8_t j; - if(ctx->nRecords == 0) + if(gf->nRecords == 0) goto done; root = NULL; - for(j = 0 ; j < ctx->nRoots ; ++j) { + for(j = 0 ; j < gf->nRoots ; ++j) { if(root == NULL) { - root = ctx->roots_hash[j]; - ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ - } else if(ctx->roots_valid[j]) { + root = gf->roots_hash[j]; + gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + } else if(gf->roots_valid[j]) { rootDel = root; - hash_node(ctx, &root, ctx->roots_hash[j], root, j+2); - ctx->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + hash_node(gf, &root, gf->roots_hash[j], root, j+2); + gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ GTDataHash_free(rootDel); } } - timestampIt(ctx, root); + timestampIt(gf, root); - free(ctx->blkStrtHash); - ctx->lenBlkStrtHash = ctx->x_prev->digest_length; - ctx->blkStrtHash = malloc(ctx->lenBlkStrtHash); - memcpy(ctx->blkStrtHash, ctx->x_prev->digest, ctx->lenBlkStrtHash); + free(gf->blkStrtHash); + gf->lenBlkStrtHash = gf->x_prev->digest_length; + gf->blkStrtHash = malloc(gf->lenBlkStrtHash); + memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); done: - ctx->bInBlk = 0; + gf->bInBlk = 0; } diff --git a/runtime/librsgt.h b/runtime/librsgt.h index a4ea7cc1..b2de73bd 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -30,17 +30,31 @@ #define MAX_ROOTS 64 #define LOGSIGHDR "LOGSIG10" -/* context for gt calls. All state data is kept here, this permits - * multiple concurrent callers. +/* context for gt calls. This primarily serves as a container for the + * config settings. The actual file-specific data is kept in gtfile. */ struct gtctx_s { enum GTHashAlgorithm hashAlg; - uint8_t *IV; /* initial value for blinding masks (where to do we get it from?) */ - GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ uint8_t bKeepRecordHashes; uint8_t bKeepTreeHashes; uint64_t blockSizeLimit; char *timestamper; +}; +typedef struct gtctx_s *gtctx; + +/* this describes a file, as far as librsgt is concerned */ +struct gtfile_s { + gtctx ctx; + /* the following data items are mirrored from gtctx to + * increase cache hit ratio (they are frequently accesed). + */ + enum GTHashAlgorithm hashAlg; + uint8_t bKeepRecordHashes; + uint8_t bKeepTreeHashes; + /* end mirrored properties */ + uint64_t blockSizeLimit; + uint8_t *IV; /* initial value for blinding masks */ + GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ unsigned char *sigfilename; unsigned char *statefilename; int fd; @@ -54,11 +68,11 @@ struct gtctx_s { */ int8_t roots_valid[MAX_ROOTS]; GTDataHash *roots_hash[MAX_ROOTS]; - /* data mambers for the associated TLV file */ + /* data members for the associated TLV file */ char tlvBuf[4096]; int tlvIdx; /* current index into tlvBuf */ }; -typedef struct gtctx_s *gtctx; +typedef struct gtfile_s *gtfile; typedef struct imprint_s imprint_t; typedef struct block_sig_s block_sig_t; @@ -201,12 +215,12 @@ int rsgtSetHashFunction(gtctx ctx, char *algName); void rsgtInit(char *usragent); void rsgtExit(void); gtctx rsgtCtxNew(void); -int rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn); +gtfile rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn); +void rsgtfileDestruct(gtfile gf); void rsgtCtxDel(gtctx ctx); -void sigblkInit(gtctx ctx); -void sigblkAddRecord(gtctx ctx, const unsigned char *rec, const size_t len); -void sigblkFinish(gtctx ctx); -void rsgtCtxSetLogfileName(gtctx ctx, char *logfilename); +void sigblkInit(gtfile gf); +void sigblkAddRecord(gtfile gf, const unsigned char *rec, const size_t len); +void sigblkFinish(gtfile gf); /* reader functions */ int rsgt_tlvrdHeader(FILE *fp, unsigned char *hdr); int rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj); diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index 474c573f..021cd9f8 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -118,35 +118,34 @@ SetCnfParam(void *pT, struct nvlst *lst) static rsRetVal -OnFileOpen(void *pT, uchar *fn) +OnFileOpen(void *pT, uchar *fn, gtfile *pgf) { lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); - rsgtCtxOpenFile(pThis->ctx, fn); - sigblkInit(pThis->ctx); + + *pgf = rsgtCtxOpenFile(pThis->ctx, fn); + sigblkInit(*pgf); RETiRet; } static rsRetVal -OnRecordWrite(void *pT, uchar *rec, rs_size_t lenRec) +OnRecordWrite(void *pF, uchar *rec, rs_size_t lenRec) { - lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec, rec); - sigblkAddRecord(pThis->ctx, rec, lenRec); + sigblkAddRecord(pF, rec, lenRec); RETiRet; } static rsRetVal -OnFileClose(void *pT) +OnFileClose(void *pF) { - lmsig_gt_t *pThis = (lmsig_gt_t*) pT; DEFiRet; dbgprintf("DDDD: onFileClose\n"); - rsgtCtxDel(pThis->ctx); + rsgtfileDestruct(pF); RETiRet; } diff --git a/runtime/sigprov.h b/runtime/sigprov.h index 5abfb390..82587b7d 100644 --- a/runtime/sigprov.h +++ b/runtime/sigprov.h @@ -29,9 +29,9 @@ BEGINinterface(sigprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(void *ppThis); rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst); rsRetVal (*Destruct)(void *ppThis); - rsRetVal (*OnFileOpen)(void *pThis, uchar *fn); - rsRetVal (*OnRecordWrite)(void *pThis, uchar *rec, rs_size_t lenRec); - rsRetVal (*OnFileClose)(void *pThis); + rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData); + rsRetVal (*OnRecordWrite)(void *pFileInstData, uchar *rec, rs_size_t lenRec); + rsRetVal (*OnFileClose)(void *pFileInstData); ENDinterface(sigprov) #define sigprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ #endif /* #ifndef INCLUDED_SIGPROV_H */ diff --git a/tools/omfile.c b/tools/omfile.c index e439d504..ba2ef3ae 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -119,6 +119,7 @@ getClockFileAccess(void) struct s_dynaFileCacheEntry { uchar *pName; /* name currently open, if dynamic name */ strm_t *pStrm; /* our output stream */ + void *sigprovFileData; /* opaque data ptr for provider use */ uint64 clkTickAccessed;/* for LRU - based on clockFileAccess */ }; typedef struct s_dynaFileCacheEntry dynaFileCacheEntry; @@ -148,6 +149,7 @@ typedef struct _instanceData { uchar *sigprovNameFull;/* full internal signature provider name */ sigprov_if_t sigprov; /* ptr to signature provider interface */ void *sigprovData; /* opaque data ptr for provider use */ + void *sigprovFileData;/* opaque data ptr for file instance */ sbool useSigprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ int iCurrElt; /* currently active cache element (-1 = none) */ int iCurrCacheSize; /* currently cache size (1-based) */ @@ -423,15 +425,16 @@ finalize_it: * if the entry should be d_free()ed and 0 if not. */ static rsRetVal -dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry) +dynaFileDelCacheEntry(instanceData *pData, int iEntry, int bFreeEntry) { + dynaFileCacheEntry **pCache = pData->dynCache; DEFiRet; ASSERT(pCache != NULL); if(pCache[iEntry] == NULL) FINALIZE; - DBGPRINTF("Removed entry %d for file '%s' from dynaCache.\n", iEntry, + DBGPRINTF("Removing entry %d for file '%s' from dynaCache.\n", iEntry, pCache[iEntry]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[iEntry]->pName); if(pCache[iEntry]->pName != NULL) { @@ -441,9 +444,10 @@ dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry) if(pCache[iEntry]->pStrm != NULL) { strm.Destruct(&pCache[iEntry]->pStrm); -#warning add sig capability here - if(pCache[iEntry]->pStrm != NULL) /* safety check -- TODO: remove if no longer necessary */ - abort(); + if(pData->useSigprov) { + pData->sigprov.OnFileClose(pCache[iEntry]->sigprovFileData); + pCache[iEntry]->sigprovFileData = NULL; + } } if(bFreeEntry) { @@ -468,7 +472,7 @@ dynaFileFreeCacheEntries(instanceData *pData) BEGINfunc; for(i = 0 ; i < pData->iCurrCacheSize ; ++i) { - dynaFileDelCacheEntry(pData->dynCache, i, 1); + dynaFileDelCacheEntry(pData, i, 1); } pData->iCurrElt = -1; /* invalidate current element */ ENDfunc; @@ -494,8 +498,10 @@ static rsRetVal closeFile(instanceData *pData) { DEFiRet; - if(pData->useSigprov) - pData->sigprov.OnFileClose(pData->sigprovData); + if(pData->useSigprov) { + pData->sigprov.OnFileClose(pData->sigprovFileData); + pData->sigprovFileData = NULL; + } strm.Destruct(&pData->pStrm); RETiRet; } @@ -506,7 +512,7 @@ static rsRetVal sigprovPrepare(instanceData *pData, uchar *fn) { DEFiRet; - pData->sigprov.OnFileOpen(pData->sigprovData, fn); + pData->sigprov.OnFileOpen(pData->sigprovData, fn, &pData->sigprovFileData); RETiRet; } @@ -594,7 +600,9 @@ prepareFile(instanceData *pData, uchar *newFileName) CHKiRet(strm.ConstructFinalize(pData->pStrm)); if(pData->useSigprov) +{ dbgprintf("DDDD: prepareFile, call sigprovPrepare\n"); sigprovPrepare(pData, szNameBuf); +} finalize_it: if(iRet != RS_RET_OK) { @@ -630,9 +638,7 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) pCache = pData->dynCache; - /* first check, if we still have the current file - * I *hope* this will be a performance enhancement. - */ + /* first check, if we still have the current file */ if( (pData->iCurrElt != -1) && !ustrcmp(newFileName, pCache[pData->iCurrElt]->pName)) { /* great, we are all set */ @@ -654,9 +660,11 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) if(iFirstFree == -1) iFirstFree = i; } else { /* got an element, let's see if it matches */ - if(!ustrcmp(newFileName, pCache[i]->pName)) { // RG: name == NULL? + if(!ustrcmp(newFileName, pCache[i]->pName)) { /* we found our element! */ pData->pStrm = pCache[i]->pStrm; + if(pData->useSigprov) + pData->sigprovFileData = pCache[i]->sigprovFileData; pData->iCurrElt = i; pCache[i]->clkTickAccessed = getClockFileAccess(); /* update "timestamp" for LRU */ FINALIZE; @@ -683,7 +691,7 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) * but it could be triggered in the common case of a failed open() system call. * rgerhards, 2010-03-22 */ - pData->pStrm = NULL; + pData->pStrm = pData->sigprovFileData = NULL; if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) { /* there is space left, so set it to that index */ @@ -696,19 +704,17 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) * The cache array is only updated after the open was successful. -- rgerhards, 2010-03-21 */ if(iFirstFree == -1) { - dynaFileDelCacheEntry(pCache, iOldest, 0); + dynaFileDelCacheEntry(pData, iOldest, 0); STATSCOUNTER_INC(pData->ctrEvict, pData->mutCtrEvict); iFirstFree = iOldest; /* this one *is* now free ;) */ } else { /* we need to allocate memory for the cache structure */ - /* TODO: performance note: we could alloc all entries on startup, thus saving malloc - * overhead -- this may be something to consider in v5... - */ CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry))); } /* Ok, we finally can open the file */ localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */ +dbgprintf("DDDD: prepareFile returned %d\n", localRet); /* check if we had an error */ if(localRet != RS_RET_OK) { @@ -730,6 +736,8 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } pCache[iFirstFree]->pStrm = pData->pStrm; + if(pData->useSigprov) + pCache[iFirstFree]->sigprovFileData = pData->sigprovFileData; pCache[iFirstFree]->clkTickAccessed = getClockFileAccess(); pData->iCurrElt = iFirstFree; DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName); @@ -751,10 +759,11 @@ doWrite(instanceData *pData, uchar *pszBuf, int lenBuf) ASSERT(pData != NULL); ASSERT(pszBuf != NULL); +dbgprintf("DDDD: pData->sigprov %p\n", pData->sigprov); DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf); if(pData->pStrm != NULL){ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf)); - CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovData, pszBuf, lenBuf)); + CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf)); } finalize_it: @@ -762,10 +771,7 @@ finalize_it: } -/* rgerhards 2004-11-11: write to a file output. This - * will be called for all outputs using file semantics, - * for example also for pipes. - */ +/* rgerhards 2004-11-11: write to a file output. */ static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData) { @@ -773,11 +779,14 @@ writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData) ASSERT(pData != NULL); +dbgprintf("DDDD: enter writeFile, dyn: %d, name: %s\n", pData->bDynamicName, pData->f_fname); /* first check if we have a dynamic file name and, if so, * check if it still is ok or a new file needs to be created */ if(pData->bDynamicName) { +dbgprintf("DDDD: calling prepareDynFile\n"); CHKiRet(prepareDynFile(pData, ppString[1], iMsgOpts)); +dbgprintf("DDDD: done prepareDynFile\n"); } else { /* "regular", non-dynafile */ if(pData->pStrm == NULL) { CHKiRet(prepareFile(pData, pData->f_fname)); @@ -787,6 +796,7 @@ writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData) } } +dbgprintf("DDDD: calling doWrite()\n"); CHKiRet(doWrite(pData, ppString[0], strlen(CHAR_CONVERT(ppString[0])))); finalize_it: -- cgit v1.2.3 From 625480e22019a2b701853b98e34a2a3846279eac Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 11 Mar 2013 12:39:22 +0100 Subject: cleanup --- tools/omfile.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/omfile.c b/tools/omfile.c index ba2ef3ae..4740ed1d 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -600,9 +600,7 @@ prepareFile(instanceData *pData, uchar *newFileName) CHKiRet(strm.ConstructFinalize(pData->pStrm)); if(pData->useSigprov) -{ dbgprintf("DDDD: prepareFile, call sigprovPrepare\n"); sigprovPrepare(pData, szNameBuf); -} finalize_it: if(iRet != RS_RET_OK) { @@ -714,7 +712,6 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) /* Ok, we finally can open the file */ localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */ -dbgprintf("DDDD: prepareFile returned %d\n", localRet); /* check if we had an error */ if(localRet != RS_RET_OK) { @@ -759,7 +756,6 @@ doWrite(instanceData *pData, uchar *pszBuf, int lenBuf) ASSERT(pData != NULL); ASSERT(pszBuf != NULL); -dbgprintf("DDDD: pData->sigprov %p\n", pData->sigprov); DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf); if(pData->pStrm != NULL){ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf)); @@ -779,14 +775,11 @@ writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData) ASSERT(pData != NULL); -dbgprintf("DDDD: enter writeFile, dyn: %d, name: %s\n", pData->bDynamicName, pData->f_fname); /* first check if we have a dynamic file name and, if so, * check if it still is ok or a new file needs to be created */ if(pData->bDynamicName) { -dbgprintf("DDDD: calling prepareDynFile\n"); CHKiRet(prepareDynFile(pData, ppString[1], iMsgOpts)); -dbgprintf("DDDD: done prepareDynFile\n"); } else { /* "regular", non-dynafile */ if(pData->pStrm == NULL) { CHKiRet(prepareFile(pData, pData->f_fname)); @@ -796,7 +789,6 @@ dbgprintf("DDDD: done prepareDynFile\n"); } } -dbgprintf("DDDD: calling doWrite()\n"); CHKiRet(doWrite(pData, ppString[0], strlen(CHAR_CONVERT(ppString[0])))); finalize_it: @@ -885,7 +877,6 @@ CODESTARTfreeInstance } else if(pData->pStrm != NULL) closeFile(pData); if(pData->useSigprov) { - dbgprintf("DDDD: destructing signature provider %s\n", pData->sigprovNameFull); pData->sigprov.Destruct(&pData->sigprovData); obj.ReleaseObj(__FILE__, pData->sigprovNameFull+2, pData->sigprovNameFull, (void*) &pData->sigprov); -- cgit v1.2.3 From 462426844d188e21a402cda78dc33c100e1b394d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 11 Mar 2013 15:19:05 +0100 Subject: doc bugfix: rsyslog.conf man page had invalid file format info closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 --- ChangeLog | 4 ++++ tools/rsyslog.conf.5 | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 73b2faa2..bb546122 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,8 @@ ---------------------------------------------------------------------------- +Version 7.2.7 [v7-stable] 2013-03-?? +- doc bugfix: rsyslog.conf man page had invalid file format info + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 +---------------------------------------------------------------------------- Version 7.2.6 [v7-stable] 2013-03-05 - slightly improved config parser error messages when invalid escapes happen - bugfix: include files got included in the wrong order diff --git a/tools/rsyslog.conf.5 b/tools/rsyslog.conf.5 index fe9e083b..07da6ffd 100644 --- a/tools/rsyslog.conf.5 +++ b/tools/rsyslog.conf.5 @@ -218,7 +218,7 @@ beginning with a slash ('/'). .B Example: .RS -*.* /var/log/traditionalfile.log;RSYSLOG_TraditionalFormat # log to a file in the traditional format +*.* /var/log/traditionalfile.log;RSYSLOG_TraditionalFileFormat # log to a file in the traditional format .RE Note: if you would like to use high-precision timestamps in your log files, -- cgit v1.2.3 From 65734fd32105a574e3fae9e469bac72e1f93e945 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 12 Mar 2013 12:26:00 +0100 Subject: adjust to corrected libgt pkgconfig name --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3f60a5b5..450f06ae 100644 --- a/configure.ac +++ b/configure.ac @@ -919,7 +919,7 @@ AC_ARG_ENABLE(guardtime, [enable_guardtime=no] ) if test "x$enable_guardtime" = "xyes"; then - PKG_CHECK_MODULES(GUARDTIME, gt >= 0.3.1) + PKG_CHECK_MODULES(GUARDTIME, libgt >= 0.3.1) fi AM_CONDITIONAL(ENABLE_GUARDTIME, test x$enable_guardtime = xyes) -- cgit v1.2.3 From a25a4bc02e2a6e9915677600401ef927f9b58013 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 12 Mar 2013 16:48:14 +0100 Subject: prepare 7.3.7 release --- ChangeLog | 2 +- configure.ac | 2 +- doc/manual.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 870d34fa..c8ef7ac6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ --------------------------------------------------------------------------- -Version 7.3.7 [devel] 2013-02-?? +Version 7.3.7 [devel] 2013-03-12 - add support for anonymizing IPv4 addresses - add support for writing to the Linux Journal (omjournal) - imuxsock: add capability to ignore messages from ourselfes diff --git a/configure.ac b/configure.ac index a0c063c2..c3785f48 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[7.3.6],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.3.7],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/doc/manual.html b/doc/manual.html index 05a03e29..b8461b6d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ professional services available directly from the source!

Please visit the rsyslog sponsor's page to honor the project sponsors or become one yourself! We are very grateful for any help towards the project goals.

-

This documentation is for version 7.3.6 (devel branch) of rsyslog. +

This documentation is for version 7.3.7 (devel branch) of rsyslog. Visit the rsyslog status page to obtain current version information and project status.

If you like rsyslog, you might -- cgit v1.2.3 From c469cff2f2e6cfd75ba91dfc106bed0baba99bf8 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 12 Mar 2013 13:56:32 +0100 Subject: bugfix: imuxsock was missing SysSock.ParseTrusted module parameter use that functionality, legacy rsyslog.conf syntax had to be used. Also, the doc was missing information on the "ParseTrusted" set of config directives. --- ChangeLog | 4 ++++ doc/imuxsock.html | 27 +++++++++++++++++++++------ plugins/imuxsock/imuxsock.c | 3 +++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 20599500..a66bbadc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ --------------------------------------------------------------------------- Version 7.3.8 [devel] 2013-03-?? +- bugfix: imuxsock was missing SysSock.ParseTrusted module parameter + To use that functionality, legacy rsyslog.conf syntax had to be used. + Also, the doc was missing information on the "ParseTrusted" set of + config directives. - bugfix: include files got included in the wrong order closes: http://bugzilla.adiscon.com/show_bug.cgi?id=411 This happens if an $IncludeConfig directive was done on multiple diff --git a/doc/imuxsock.html b/doc/imuxsock.html index ee9e2a05..d7a52268 100644 --- a/doc/imuxsock.html +++ b/doc/imuxsock.html @@ -98,6 +98,10 @@ messages that shall be rate-limited.

  • SysSock.Annotate <on/off> turn on annotation/trusted properties for the system log socket.
  • +
  • SysSock.ParseTrusted <on/off> if Annotation is turned on, create +JSON/lumberjack properties out of the trusted properties (which can be accessed +via RainerScript JSON Variables, e.g. "$!pid") instead of adding them to the message. +properties for the system log socket.
  • Input Instance Parameters

    @@ -160,6 +164,8 @@ will only affect the next one and then automatically be reset. This functionalit that the local hostname can be overridden in cases where that is desired.
  • Annotate <on/off> turn on annotation/trusted properties for the non-system log socket in question.
  • +
  • ParseTrusted <on/off> equivalent to the SysSock.ParseTrusted module +parameter, but applies to the input that is being defined. Caveats/Known Bugs:
    @@ -172,12 +178,20 @@ change the array size in imuxsock.c.

    The following sample is the minimum setup required to accept syslog messages from applications running on the local system.

    - + +

    The following sample is similiar to the first one, but enables trusted +properties, which are put into JSON/lumberjack variables. +
    +

    + +

    The following sample is a configuration where rsyslogd pulls logs from two jails, and assigns different hostnames to each of the jails:

    -

    The following sample is used to turn off input rate limiting on the system log socket. -

    The following sample is used activate message annotation and thus trusted properties on the system log socket. - @@ -243,6 +257,7 @@ equivalent to: SysSock.IgnoreTimestamp.

  • $InputUnixListenSocketHostName <hostname> equivalent to: HostName.
  • $InputUnixListenSocketAnnotate <on/off> equivalent to: Annotate.
  • $SystemLogSocketAnnotate <on/off> equivalent to: SysSock.Annotate.
  • +
  • $SystemLogSocketParseTrusted <on/off> equivalent to: SysSock.ParseTrusted.
  • Caveats/Known Bugs:
    @@ -295,7 +310,7 @@ $SystemLogSocketAnnotate on

    This documentation is part of the rsyslog project.
    -Copyright © 2008-2012 by Rainer +Copyright © 2008-2013 by Rainer Gerhards and Adiscon. Released under the GNU GPL version 3 or higher.

    diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index a5360bee..773cb8db 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -233,6 +233,7 @@ static struct cnfparamdescr modpdescr[] = { { "syssock.flowcontrol", eCmdHdlrBinary, 0 }, { "syssock.usesystimestamp", eCmdHdlrBinary, 0 }, { "syssock.annotate", eCmdHdlrBinary, 0 }, + { "syssock.parsetrusted", eCmdHdlrBinary, 0 }, { "syssock.usepidfromsystem", eCmdHdlrBinary, 0 }, { "syssock.ratelimit.interval", eCmdHdlrInt, 0 }, { "syssock.ratelimit.burst", eCmdHdlrInt, 0 }, @@ -1145,6 +1146,8 @@ CODESTARTsetModCnf loadModConf->bUseSysTimeStamp = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "syssock.annotate")) { loadModConf->bAnnotateSysSock = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.parsetrusted")) { + loadModConf->bParseTrusted = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "syssock.usepidfromsystem")) { loadModConf->bWritePidSysSock = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.interval")) { -- cgit v1.2.3 From 06ba977249e6806571795d3257970c5f98fa0d16 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 13 Mar 2013 17:38:04 +0100 Subject: rsgtutil: begin to make rsgttlvdump a generic utility It will support various maintenaince operations, including verification of signatures in the future. To match its new scope, it also has been renamed. --- tools/Makefile.am | 10 ++-- tools/rsgttlvdump.c | 83 -------------------------- tools/rsgtutil.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 88 deletions(-) delete mode 100644 tools/rsgttlvdump.c create mode 100644 tools/rsgtutil.c diff --git a/tools/Makefile.am b/tools/Makefile.am index 8af86cb4..2501331e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -59,14 +59,14 @@ logctl_CPPFLAGS = $(LIBMONGO_CLIENT_CFLAGS) logctl_LDADD = $(LIBMONGO_CLIENT_LIBS) endif if ENABLE_GUARDTIME -bin_PROGRAMS += rsgttlvdump -#bin_PROGRAMS += logsigner rsgttlvdump +bin_PROGRAMS += rsgtutil +#bin_PROGRAMS += logsigner rsgtutil #logsigner = logsigner.c #logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) #logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) -rsgttlvdump = rsgttlvdump.c -rsgttlvdump_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) -rsgttlvdump_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) +rsgtutil = rsgtutil.c +rsgtutil_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) +rsgtutil_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) endif endif diff --git a/tools/rsgttlvdump.c b/tools/rsgttlvdump.c deleted file mode 100644 index 9b536db1..00000000 --- a/tools/rsgttlvdump.c +++ /dev/null @@ -1,83 +0,0 @@ -/* This is a tool for dumpoing the content of GuardTime TLV - * files in a (somewhat) human-readable manner. - * - * 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 exprs or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include -#include -#include -#include -#include -#include - -#include "librsgt.h" - -typedef unsigned char uchar; - -void -processFile(char *name) -{ - FILE *fp; - uchar hdr[9]; - uint16_t tlvtype, tlvlen; - void *obj; - int r = -1; - - if(!strcmp(name, "-")) - fp = stdin; - else { - printf("Processing file %s:\n", name); - if((fp = fopen(name, "r")) == NULL) { - perror(name); - goto err; - } - } - if((r = rsgt_tlvrdHeader(fp, hdr)) != 0) goto err; - printf("File Header: '%s'\n", hdr); - while(1) { /* we will err out on EOF */ - if((r = rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj)) != 0) { - if(feof(fp)) - break; - else - goto err; - } - rsgt_tlvprint(stdout, tlvtype, obj, 0); - } - - if(fp != stdin) - fclose(fp); - return; -err: fprintf(stderr, "error %d processing file %s\n", r, name); -} - -int -main(int argc, char *argv[]) -{ - int i; - if(argc == 1) - processFile("-"); - else { - for(i = 1 ; i < argc ; ++i) - processFile(argv[i]); - } - return 0; -} diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c new file mode 100644 index 00000000..7b70a9a7 --- /dev/null +++ b/tools/rsgtutil.c @@ -0,0 +1,168 @@ +/* This is a tool for dumpoing the content of GuardTime TLV + * files in a (somewhat) human-readable manner. + * + * 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 exprs or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "librsgt.h" + +typedef unsigned char uchar; + +static enum { MD_DUMP, MD_DETECT_FILE_TYPE, +} mode = MD_DUMP; +static int verbose = 0; + +static void +dumpFile(char *name) +{ + FILE *fp; + uchar hdr[9]; + uint16_t tlvtype, tlvlen; + void *obj; + int r = -1; + + if(!strcmp(name, "-")) + fp = stdin; + else { + printf("Processing file %s:\n", name); + if((fp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } + } + if((r = rsgt_tlvrdHeader(fp, hdr)) != 0) goto err; + printf("File Header: '%s'\n", hdr); + while(1) { /* we will err out on EOF */ + if((r = rsgt_tlvrd(fp, &tlvtype, &tlvlen, &obj)) != 0) { + if(feof(fp)) + break; + else + goto err; + } + rsgt_tlvprint(stdout, tlvtype, obj, verbose); + } + + if(fp != stdin) + fclose(fp); + return; +err: fprintf(stderr, "error %d processing file %s\n", r, name); +} + +static void +detectFileType(char *name) +{ + FILE *fp; + char *typeName; + char hdr[9]; + int r = -1; + + if(!strcmp(name, "-")) + fp = stdin; + else { + if((fp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } + } + if((r = rsgt_tlvrdHeader(fp, (uchar*)hdr)) != 0) goto err; + if(!strcmp(hdr, "LOGSIG10")) + typeName = "Log Signature File, Version 10"; + else + typeName = "unknown"; + + printf("%s: %s [%s]\n", name, hdr, typeName); + + if(fp != stdin) + fclose(fp); + return; +err: fprintf(stderr, "error %d processing file %s\n", r, name); +} + +static void +processFile(char *name) +{ + switch(mode) { + case MD_DETECT_FILE_TYPE: + detectFileType(name); + break; + case MD_DUMP: + dumpFile(name); + break; + } +} + + +static struct option long_options[] = +{ + {"dump", no_argument, NULL, 'D'}, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"detect-file-type", no_argument, NULL, 'T'}, + {NULL, 0, NULL, 0} +}; + +int +main(int argc, char *argv[]) +{ + int i; + int opt; + + while(1) { + opt = getopt_long(argc, argv, "v", long_options, NULL); + if(opt == -1) + break; + switch(opt) { + case 'v': + verbose = 1; + break; + case 'V': + fprintf(stderr, "rsgtutil " VERSION "\n"); + exit(0); + case 'D': + mode = MD_DUMP; + break; + case 'T': + mode = MD_DETECT_FILE_TYPE; + break; + case '?': + break; + default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt); + return 1; + } + } + + if(optind == argc) + processFile("-"); + else { + for(i = optind ; i < argc ; ++i) + processFile(argv[i]); + } + + return 0; +} -- cgit v1.2.3 From 44b4922825df794f678cd4ad18d940ff114b943f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 14 Mar 2013 12:32:03 +0100 Subject: rsgtutil: add --show-sigblock-params parameter --- runtime/librsgt.h | 10 +++- runtime/librsgt_read.c | 127 +++++++++++++++++++++++++++++++++++++++++++++---- tools/rsgtutil.c | 48 ++++++++++++++++++- 3 files changed, 175 insertions(+), 10 deletions(-) diff --git a/runtime/librsgt.h b/runtime/librsgt.h index b2de73bd..d9d221ea 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -110,11 +110,16 @@ struct rsgtstatefile { }; /* error states */ -#define RSGTE_IO 1 /* any kind of io error, including EOF */ +#define RSGTE_IO 1 /* any kind of io error */ #define RSGTE_FMT 2 /* data fromat error */ #define RSGTE_INVLTYP 3 /* invalid TLV type record (unexcpected at this point) */ #define RSGTE_OOM 4 /* ran out of memory */ #define RSGTE_LEN 5 /* error related to length records */ +#define RSGTE_NO_BLKSIG 6/* block signature record is missing --> invalid block */ +#define RSGTE_INVLD_RECCNT 7/* mismatch between actual records and records + given in block-sig record */ +#define RSGTE_INVLHDR 8/* invalid file header */ +#define RSGTE_EOF 9 /* specific EOF */ static inline uint16_t @@ -225,5 +230,8 @@ void sigblkFinish(gtfile gf); int rsgt_tlvrdHeader(FILE *fp, unsigned char *hdr); int rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj); void rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose); +void rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose); +int rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, uint8_t *bHasRecHashes, uint8_t *bHasIntermedHashes); +int rsgt_chkFileHdr(FILE *fp, char *expect); #endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index be78580d..961e50c5 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -50,7 +50,7 @@ static int rsgt_read_debug = 0; /* macro to obtain next char from file including error tracking */ #define NEXTC if((c = fgetc(fp)) == EOF) { \ - r = RSGTE_IO; \ + r = feof(fp) ? RSGTE_EOF : RSGTE_IO; \ goto done; \ } @@ -118,6 +118,17 @@ rsgt_tlvrdOctetString(FILE *fp, uint8_t **data, size_t len) done: return r; } static int +rsgt_tlvrdSkipVal(FILE *fp, size_t len) +{ + size_t i; + int c, r = 1; + for(i = 0 ; i < len ; ++i) { + NEXTC; + } + r = 0; +done: return r; +} +static int rsgt_tlvrdHASH_ALGO(FILE *fp, uint8_t *hashAlg) { int r = 1; @@ -314,14 +325,14 @@ done: return r; * otherwise everything. */ static void -outputHexBlob(uint8_t *blob, uint16_t len, uint8_t verbose) +outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) { unsigned i; if(verbose || len <= 6) { for(i = 0 ; i < len ; ++i) - printf("%2.2x", blob[i]); + fprintf(fp, "%2.2x", blob[i]); } else { - printf("%2.2x%2.2x[...]%2.2x%2.2x", + fprintf(fp, "%2.2x%2.2x[...]%2.2x%2.2x", blob[0], blob[1], blob[len-2], blob[len-2]); } @@ -342,7 +353,7 @@ static void rsgt_printIMPRINT(FILE *fp, char *name, imprint_t *imp, uint8_t verbose) { fprintf(fp, "%s", name); - outputHexBlob(imp->data, imp->len, verbose); + outputHexBlob(fp, imp->data, imp->len, verbose); fputc('\n', fp); } @@ -376,19 +387,19 @@ rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose) fprintf(fp, "\tPrevious Block Hash:\n"); fprintf(fp, "\t Algorithm..: %s\n", hashAlgName(bs->lastHash.hashID)); fprintf(fp, "\t Hash.......: "); - outputHexBlob(bs->lastHash.data, bs->lastHash.len, verbose); + outputHexBlob(fp, bs->lastHash.data, bs->lastHash.len, verbose); fputc('\n', fp); if(blobIsZero(bs->lastHash.data, bs->lastHash.len)) fprintf(fp, "\t NOTE: New Hash Chain Start!\n"); fprintf(fp, "\tHash Algorithm: %s\n", hashAlgName(bs->hashID)); fprintf(fp, "\tIV............: "); - outputHexBlob(bs->iv, getIVLen(bs), verbose); + outputHexBlob(fp, bs->iv, getIVLen(bs), verbose); fputc('\n', fp); fprintf(fp, "\tRecord Count..: %llu\n", bs->recCount); fprintf(fp, "\tSignature Type: %s\n", sigTypeName(bs->sigID)); fprintf(fp, "\tSignature Len.: %u\n", bs->sig.der.len); fprintf(fp, "\tSignature.....: "); - outputHexBlob(bs->sig.der.data, bs->sig.der.len, verbose); + outputHexBlob(fp, bs->sig.der.data, bs->sig.der.len, verbose); fputc('\n', fp); } @@ -417,3 +428,103 @@ rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose) break; } } + + +/** + * Read block parameters. This detects if the block contains the + * individual log hashes, the intermediate hashes and the overall + * block paramters (from the signature block). As we do not have any + * begin of block record, we do not know e.g. the hash algorithm or IV + * until reading the block signature record. And because the file is + * purely sequential and variable size, we need to read all records up to + * the next signature record. + * If a caller intends to verify a log file based on the parameters, + * he must re-read the file from the begining (we could keep things + * in memory, but this is impractical for large blocks). In order + * to facitate this, the function permits to rewind to the original + * read location when it is done. + * + * @param[in] fp file pointer of tlv file + * @param[in] bRewind 0 - do not rewind at end of procesing, 1 - do so + * @param[out] bs block signature record + * @param[out] bHasRecHashes 0 if record hashes are present, 1 otherwise + * @param[out] bHasIntermedHashes 0 if intermediate hashes are present, + * 1 otherwise + * + * @returns 0 if ok, something else otherwise + */ +int +rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, + uint8_t *bHasRecHashes, uint8_t *bHasIntermedHashes) +{ + int r; + uint64_t nRecs = 0; + uint16_t tlvtype, tlvlen; + uint8_t bDone = 0; + off_t rewindPos = 0; + + if(bRewind) + rewindPos = ftello(fp); + *bHasRecHashes = 0; + *bHasIntermedHashes = 0; + *bs = NULL; + + while(!bDone) { /* we will err out on EOF */ + if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; + switch(tlvtype) { + case 0x0900: + ++nRecs; + *bHasRecHashes = 1; + rsgt_tlvrdSkipVal(fp, tlvlen); + break; + case 0x0901: + *bHasIntermedHashes = 1; + rsgt_tlvrdSkipVal(fp, tlvlen); + break; + case 0x0902: + r = rsgt_tlvrdBLOCK_SIG(fp, bs, tlvlen); + if(r != 0) goto done; + bDone = 1; + break; + default:fprintf(fp, "unknown tlv record %4.4x\n", tlvtype); + break; + } + } + + if(*bHasRecHashes && (nRecs != (*bs)->recCount)) { + r = RSGTE_INVLD_RECCNT; + goto done; + } + + if(bRewind) { + if(fseeko(fp, rewindPos, SEEK_SET) != 0) { + r = RSGTE_IO; + goto done; + } + } +done: + return r; +} + + +/** + * Read the file header and compare it to the expected value. + * The file pointer is placed right after the header. + * @param[in] fp file pointer of tlv file + * @param[in] excpect expected header (e.g. "LOGSIG10") + * @returns 0 if ok, something else otherwise + */ +int +rsgt_chkFileHdr(FILE *fp, char *expect) +{ + int r; + char hdr[9]; + + if((r = rsgt_tlvrdHeader(fp, (uchar*)hdr)) != 0) goto done; + if(strcmp(hdr, expect)) + r = RSGTE_INVLHDR; + else + r = 0; +done: + return r; +} diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index 7b70a9a7..3286163b 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -34,7 +34,7 @@ typedef unsigned char uchar; -static enum { MD_DUMP, MD_DETECT_FILE_TYPE, +static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS } mode = MD_DUMP; static int verbose = 0; @@ -74,6 +74,45 @@ dumpFile(char *name) err: fprintf(stderr, "error %d processing file %s\n", r, name); } +static void +showSigblkParams(char *name) +{ + FILE *fp; + block_sig_t *bs; + uint8_t bHasRecHashes, bHasIntermedHashes; + uint64_t blkCnt = 0; + int r = -1; + + if(!strcmp(name, "-")) + fp = stdin; + else { + if((fp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } + } + if((r = rsgt_chkFileHdr(fp, "LOGSIG10")) != 0) goto err; + + while(1) { /* we will err out on EOF */ + if((r = rsgt_getBlockParams(fp, 0, &bs, &bHasRecHashes, + &bHasIntermedHashes)) != 0) + goto err; + ++blkCnt; + rsgt_printBLOCK_SIG(stdout, bs, verbose); + printf("\t***META INFORMATION:\n"); + printf("\tBlock Nbr in File......: %llu\n", blkCnt); + printf("\tHas Record Hashes......: %d\n", bHasRecHashes); + printf("\tHas Intermediate Hashes: %d\n", bHasIntermedHashes); + } + + if(fp != stdin) + fclose(fp); + return; +err: + if(r != RSGTE_EOF) + fprintf(stderr, "error %d processing file %s\n", r, name); +} + static void detectFileType(char *name) { @@ -114,6 +153,9 @@ processFile(char *name) case MD_DUMP: dumpFile(name); break; + case MD_SHOW_SIGBLK_PARAMS: + showSigblkParams(name); + break; } } @@ -124,6 +166,7 @@ static struct option long_options[] = {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"detect-file-type", no_argument, NULL, 'T'}, + {"show-sigblock-params", no_argument, NULL, 'B'}, {NULL, 0, NULL, 0} }; @@ -147,6 +190,9 @@ main(int argc, char *argv[]) case 'D': mode = MD_DUMP; break; + case 'B': + mode = MD_SHOW_SIGBLK_PARAMS; + break; case 'T': mode = MD_DETECT_FILE_TYPE; break; -- cgit v1.2.3 From 6345e959d96bd9a3cbeeaea7f99572c33bea3054 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 15 Mar 2013 08:19:14 +0100 Subject: bugfix: imuxsock was missing SysSock.ParseTrusted module parameter To use that functionality, legacy rsyslog.conf syntax had to be used. Also, the doc was missing information on the "ParseTrusted" set of config directives. --- ChangeLog | 4 ++++ doc/imuxsock.html | 27 +++++++++++++++++++++------ plugins/imuxsock/imuxsock.c | 3 +++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index bb546122..c41a89c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ ---------------------------------------------------------------------------- Version 7.2.7 [v7-stable] 2013-03-?? +- bugfix: imuxsock was missing SysSock.ParseTrusted module parameter + To use that functionality, legacy rsyslog.conf syntax had to be used. + Also, the doc was missing information on the "ParseTrusted" set of + config directives. - doc bugfix: rsyslog.conf man page had invalid file format info closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 ---------------------------------------------------------------------------- diff --git a/doc/imuxsock.html b/doc/imuxsock.html index a962f814..d505604a 100644 --- a/doc/imuxsock.html +++ b/doc/imuxsock.html @@ -92,6 +92,10 @@ messages that shall be rate-limited.
  • SysSock.Annotate <on/off> turn on annotation/trusted properties for the system log socket.
  • +
  • SysSock.ParseTrusted <on/off> if Annotation is turned on, create +JSON/lumberjack properties out of the trusted properties (which can be accessed +via RainerScript JSON Variables, e.g. "$!pid") instead of adding them to the message. +properties for the system log socket.
  • Input Instance Parameters

    @@ -148,6 +152,8 @@ will only affect the next one and then automatically be reset. This functionalit that the local hostname can be overridden in cases where that is desired.
  • Annotate <on/off> turn on annotation/trusted properties for the non-system log socket in question.
  • +
  • ParseTrusted <on/off> equivalent to the SysSock.ParseTrusted module +parameter, but applies to the input that is being defined. Caveats/Known Bugs:
    @@ -160,12 +166,20 @@ change the array size in imuxsock.c.

    The following sample is the minimum setup required to accept syslog messages from applications running on the local system.

    - + +

    The following sample is similiar to the first one, but enables trusted +properties, which are put into JSON/lumberjack variables. +
    +

    + +

    The following sample is a configuration where rsyslogd pulls logs from two jails, and assigns different hostnames to each of the jails:

    -

    The following sample is used to turn off input rate limiting on the system log socket. -

    The following sample is used activate message annotation and thus trusted properties on the system log socket. - @@ -231,6 +245,7 @@ equivalent to: SysSock.IgnoreTimestamp.

  • $InputUnixListenSocketHostName <hostname> equivalent to: HostName.
  • $InputUnixListenSocketAnnotate <on/off> equivalent to: Annotate.
  • $SystemLogSocketAnnotate <on/off> equivalent to: SysSock.Annotate.
  • +
  • $SystemLogSocketParseTrusted <on/off> equivalent to: SysSock.ParseTrusted.
  • Caveats/Known Bugs:
    @@ -283,7 +298,7 @@ $SystemLogSocketAnnotate on

    This documentation is part of the rsyslog project.
    -Copyright © 2008-2012 by Rainer +Copyright © 2008-2013 by Rainer Gerhards and Adiscon. Released under the GNU GPL version 3 or higher.

    diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 871a1fa5..637cb133 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -236,6 +236,7 @@ static struct cnfparamdescr modpdescr[] = { { "syssock.flowcontrol", eCmdHdlrBinary, 0 }, { "syssock.usesystimestamp", eCmdHdlrBinary, 0 }, { "syssock.annotate", eCmdHdlrBinary, 0 }, + { "syssock.parsetrusted", eCmdHdlrBinary, 0 }, { "syssock.usepidfromsystem", eCmdHdlrBinary, 0 }, { "syssock.ratelimit.interval", eCmdHdlrInt, 0 }, { "syssock.ratelimit.burst", eCmdHdlrInt, 0 }, @@ -1204,6 +1205,8 @@ CODESTARTsetModCnf loadModConf->bUseSysTimeStamp = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "syssock.annotate")) { loadModConf->bAnnotateSysSock = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.parsetrusted")) { + loadModConf->bParseTrusted = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "syssock.usepidfromsystem")) { loadModConf->bWritePidSysSock = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.interval")) { -- cgit v1.2.3 From 3f4facd593e03d8fcabba04910dfbde46035a445 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 15 Mar 2013 08:41:36 +0100 Subject: doc: typo fix --- doc/imuxsock.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/imuxsock.html b/doc/imuxsock.html index d7a52268..68bbe4b2 100644 --- a/doc/imuxsock.html +++ b/doc/imuxsock.html @@ -101,7 +101,7 @@ properties for the system log socket.
  • SysSock.ParseTrusted <on/off> if Annotation is turned on, create JSON/lumberjack properties out of the trusted properties (which can be accessed via RainerScript JSON Variables, e.g. "$!pid") instead of adding them to the message. -properties for the system log socket.
  • +

    Input Instance Parameters

    -- cgit v1.2.3 From ebda75e574dcb077c0da3ffd8cc4cc6b96c69ed3 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 15 Mar 2013 12:16:57 +0100 Subject: imrelp: now supports listening to IPv4/v6 only instead of always both build now requires librelp 1.0.2 closes: http://bugzilla.adiscon.com/show_bug.cgi?id=378 --- ChangeLog | 3 +++ configure.ac | 2 +- doc/imrelp.html | 2 +- plugins/imrelp/imrelp.c | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 29192537..29cd533c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ --------------------------------------------------------------------------- Version 7.3.8 [devel] 2013-03-?? +- imrelp: now supports listening to IPv4/v6 only instead of always both + build now requires librelp 1.0.2 + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=378 - bugfix: imuxsock was missing SysSock.ParseTrusted module parameter To use that functionality, legacy rsyslog.conf syntax had to be used. Also, the doc was missing information on the "ParseTrusted" set of diff --git a/configure.ac b/configure.ac index c3785f48..0e398307 100644 --- a/configure.ac +++ b/configure.ac @@ -915,7 +915,7 @@ AC_ARG_ENABLE(relp, [enable_relp=no] ) if test "x$enable_relp" = "xyes"; then - PKG_CHECK_MODULES(RELP, relp >= 1.0.1) + PKG_CHECK_MODULES(RELP, relp >= 1.0.2) fi AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes) diff --git a/doc/imrelp.html b/doc/imrelp.html index 856aff82..9f3e4875 100644 --- a/doc/imrelp.html +++ b/doc/imrelp.html @@ -47,7 +47,7 @@ not specific ones. This is due to a currently existing limitation in librelp.

    Sample:

    This sets up a RELP server on port 20514.

    - diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c index dc67f4fe..5e0ae552 100644 --- a/plugins/imrelp/imrelp.c +++ b/plugins/imrelp/imrelp.c @@ -208,6 +208,7 @@ addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst) if(pRelpEngine == NULL) { CHKiRet(relpEngineConstruct(&pRelpEngine)); CHKiRet(relpEngineSetDbgprint(pRelpEngine, dbgprintf)); + CHKiRet(relpEngineSetFamily(pRelpEngine, glbl.GetDefPFFamily())); CHKiRet(relpEngineSetEnableCmd(pRelpEngine, (uchar*) "syslog", eRelpCmdState_Required)); CHKiRet(relpEngineSetSyslogRcv(pRelpEngine, onSyslogRcv)); if (!glbl.GetDisableDNS()) { -- cgit v1.2.3 From 0114b531b38b16db98b04b680017d6c758987fd9 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 15 Mar 2013 14:33:23 +0100 Subject: debug system improvement & bug fix for init system status (see below) - rsyslogd startup information is now properly conveyed back to init when privileges are beging dropped Actually, we have moved termination of the parent in front of the priv drop. So it shall work now in all cases. See code comments in commit for more details. - If forking, the parent now waits for a maximum of 60 seconds for termination by the child - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. --- ChangeLog | 10 ++++++++++ runtime/debug.c | 9 +++++++++ runtime/debug.h | 1 + tools/syslogd.c | 48 ++++++++++++++++++++++++++++-------------------- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index c41a89c0..b143ae81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,16 @@ Version 7.2.7 [v7-stable] 2013-03-?? config directives. - doc bugfix: rsyslog.conf man page had invalid file format info closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 +- rsyslogd startup information is now properly conveyed back to init + when privileges are beging dropped + Actually, we have moved termination of the parent in front of the + priv drop. So it shall work now in all cases. See code comments in + commit for more details. +- If forking, the parent now waits for a maximum of 60 seconds for + termination by the child +- improved debugging support in forked (auto-backgrounding) mode + The rsyslog debug log file is now continued to be written across the + fork. ---------------------------------------------------------------------------- Version 7.2.6 [v7-stable] 2013-03-05 - slightly improved config parser error messages when invalid escapes happen diff --git a/runtime/debug.c b/runtime/debug.c index 1d306dbd..26da31b4 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -1296,6 +1296,15 @@ dbgmalloc(size_t size) } +/* report fd used for debug log. This is needed in case of + * auto-backgrounding, where the debug log shall not be closed. + */ +int +dbgGetDbglogFd(void) +{ + return altdbg; +} + /* read in the runtime options * rgerhards, 2008-02-28 */ diff --git a/runtime/debug.h b/runtime/debug.h index 5bd26bd8..e6971f82 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -104,6 +104,7 @@ void dbgSetExecLocation(int iStackPtr, int line); void dbgSetThrdName(uchar *pszName); void dbgPrintAllDebugInfo(void); void *dbgmalloc(size_t size); +int dbgGetDbglogFd(void); /* macros */ #ifdef DEBUGLESS diff --git a/tools/syslogd.c b/tools/syslogd.c index 62c18e72..0d7aac6c 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -224,7 +224,7 @@ struct queuefilenames_s { int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ -static int NoFork = 0; /* don't fork - don't run in daemon mode - read-only after startup */ +static int doFork = 1; /* fork - run in daemon mode - read-only after startup */ int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode - is available * If the main queue is either not yet ready or not running in * queueing mode (mode DIRECT!), then this is set to 0. @@ -473,7 +473,7 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags) * permits us to process unmodified config files which otherwise contain a * supressor statement. */ - if(((Debug == DEBUG_FULL || NoFork) && ourConf->globals.bErrMsgToStderr) || iConfigVerify) { + if(((Debug == DEBUG_FULL || !doFork) && ourConf->globals.bErrMsgToStderr) || iConfigVerify) { if(LOG_PRI(pri) == LOG_ERR) fprintf(stderr, "rsyslogd: %s\n", msg); } @@ -652,7 +652,6 @@ finalize_it: rsRetVal multiSubmitMsg(multi_submit_t *pMultiSub) { - int i; qqueue_t *pQueue; ruleset_t *pRuleset; DEFiRet; @@ -1693,8 +1692,7 @@ doGlblProcessInit(void) thrdInit(); - if( !(Debug == DEBUG_FULL || NoFork) ) - { + if(doFork) { DBGPRINTF("Checking pidfile '%s'.\n", PidFile); if (!check_pid(PidFile)) { @@ -1706,16 +1704,23 @@ doGlblProcessInit(void) /* stop writing debug messages to stdout (if debugging is on) */ stddbg = -1; + dbgprintf("ready for forking\n"); if (fork()) { /* Parent process */ - sleep(300); - /* Not reached unless something major went wrong. 5 - * minutes should be a fair amount of time to wait. - * Please note that this procedure is important since - * the father must not exit before syslogd isn't - * initialized or the klogd won't be able to flush its - * logs. -Joey + dbgprintf("parent process going to sleep for 60 secs\n"); + sleep(60); + /* Not reached unless something major went wrong. 1 + * minute should be a fair amount of time to wait. + * The parent should not exit before rsyslogd is + * properly initilized (at least almost) or the init + * system may get a wrong impression of our readyness. + * Note that we exit before being completely initialized, + * but at this point it is very, very unlikely that something + * bad can happen. We do this here, because otherwise we would + * need to have much more code to handle priv drop (which we + * don't consider worth for the init system, especially as it + * is going away on the majority of distros). */ exit(1); /* "good" exit - after forking, not diasabling anything */ } @@ -1724,6 +1729,7 @@ doGlblProcessInit(void) close(0); /* we keep stdout and stderr open in case we have to emit something */ i = 3; + dbgprintf("in child, finalizing initialization\n"); /* if (sd_booted()) */ { const char *e; @@ -1757,7 +1763,8 @@ doGlblProcessInit(void) i = SD_LISTEN_FDS_START + sd_fds; } for ( ; i < num_fds; i++) - (void) close(i); + if(i != dbgGetDbglogFd()) + close(i); untty(); } else { @@ -1968,7 +1975,7 @@ int realMain(int argc, char **argv) fprintf(stderr, "rsyslogd: error -m is no longer supported - use immark instead"); break; case 'n': /* don't fork */ - NoFork = 1; + doFork = 0; break; case 'N': /* enable config verify mode */ iConfigVerify = atoi(arg); @@ -2066,17 +2073,18 @@ int realMain(int argc, char **argv) if(!iConfigVerify) CHKiRet(doGlblProcessInit()); + /* Send a signal to the parent so it can terminate. */ + if(myPid != ppid) { + dbgprintf("signaling parent to terminate\n"); + kill(ppid, SIGTERM); + } + CHKiRet(init()); if(Debug && debugging_on) { dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n"); } - /* Send a signal to the parent so it can terminate. */ - if(myPid != ppid) - kill(ppid, SIGTERM); - - /* END OF INTIALIZATION */ DBGPRINTF("initialization completed, transitioning to regular run mode\n"); @@ -2087,7 +2095,7 @@ int realMain(int argc, char **argv) * is still in its infancy (and not really done), we currently accept this issue. * rgerhards, 2009-06-29 */ - if(!(Debug == DEBUG_FULL || NoFork)) { + if(!doFork) { close(1); close(2); ourConf->globals.bErrMsgToStderr = 0; -- cgit v1.2.3 From b097e889a14de1341262e53c70451f67bbe077ab Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 15 Mar 2013 16:59:40 +0100 Subject: bugfix: segfault in expression optimizer closes: http://bugzilla.adiscon.com/show_bug.cgi?id=423 --- ChangeLog | 2 ++ grammar/rainerscript.c | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 29cd533c..ddfed3b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,8 @@ Version 7.3.8 [devel] 2013-03-?? - imrelp: now supports listening to IPv4/v6 only instead of always both build now requires librelp 1.0.2 closes: http://bugzilla.adiscon.com/show_bug.cgi?id=378 +- bugfix: segfault in expression optimizer + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=423 - bugfix: imuxsock was missing SysSock.ParseTrusted module parameter To use that functionality, legacy rsyslog.conf syntax had to be used. Also, the doc was missing information on the "ParseTrusted" set of diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 7ef7bf7f..8a9c9aaa 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -2635,7 +2635,7 @@ cnfexprOptimize_CMP_severity_facility(struct cnfexpr *expr) /* optimize a comparison with a variable as left-hand operand * NOTE: Currently support CMP_EQ, CMP_NE only and code NEEDS - * TO BE CHANGED for other comparisons! + * TO BE CHANGED fgr other comparisons! */ static inline struct cnfexpr* cnfexprOptimize_CMP_var(struct cnfexpr *expr) @@ -2790,10 +2790,10 @@ cnfexprOptimize(struct cnfexpr *expr) expr->l = expr->r; expr->r = exprswap; } - } else if(expr->l->nodetype == 'V') { - expr = cnfexprOptimize_CMP_var(expr); } - if(expr->r->nodetype == 'A') { + if(expr->l->nodetype == 'V') { + expr = cnfexprOptimize_CMP_var(expr); + } else if(expr->r->nodetype == 'A') { cnfexprOptimize_CMPEQ_arr((struct cnfarray *)expr->r); } break; -- cgit v1.2.3 From d2467c38d42f590deecd807741324fc0e5522a8a Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Sun, 17 Mar 2013 13:06:02 +0100 Subject: logsig: milestone/verfier: record hashes are verified --- runtime/librsgt.c | 6 +-- runtime/librsgt.h | 33 +++++++++++++ runtime/librsgt_read.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ runtime/lmsig_gt.c | 12 ++++- tools/rsgtutil.c | 95 ++++++++++++++++++++++++++++++++++++- 5 files changed, 266 insertions(+), 6 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 83a2fe05..e49011f4 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -514,7 +514,7 @@ bufAddLevel(uchar *buf, size_t *len, uint8_t level) } -static void +void hash_m(gtfile gf, GTDataHash **m) { #warning Overall: check GT API return states! @@ -527,7 +527,7 @@ hash_m(gtfile gf, GTDataHash **m) GTDataHash_create(gf->hashAlg, concatBuf, len, m); } -static inline void +void hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len) { // r = hash(canonicalize(rec)); @@ -535,7 +535,7 @@ hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len) } -static void +void hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level) { diff --git a/runtime/librsgt.h b/runtime/librsgt.h index d9d221ea..35ee96b5 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -120,6 +120,12 @@ struct rsgtstatefile { given in block-sig record */ #define RSGTE_INVLHDR 8/* invalid file header */ #define RSGTE_EOF 9 /* specific EOF */ +#define RSGTE_MISS_REC_HASH 10 /* record hash missing when expected */ +#define RSGTE_MISS_TREE_HASH 11 /* tree hash missing when expected */ +#define RSGTE_INVLD_REC_HASH 12 /* invalid record hash (failed verification) */ +#define RSGTE_INVLD_TREE_HASH 13 /* invalid tree hash (failed verification) */ +#define RSGTE_INVLD_REC_HASHID 14 /* invalid record hash ID (failed verification) */ +#define RSGTE_INVLD_TREE_HASHID 15 /* invalid tree hash ID (failed verification) */ static inline uint16_t @@ -180,6 +186,26 @@ hashAlgName(uint8_t hashID) default:return "[unknown]"; } } +static inline enum GTHashAlgorithm +hashID2Alg(uint8_t hashID) +{ + switch(hashID) { + case 0x00: + return GT_HASHALG_SHA1; + case 0x02: + return GT_HASHALG_RIPEMD160; + case 0x03: + return GT_HASHALG_SHA224; + case 0x01: + return GT_HASHALG_SHA256; + case 0x04: + return GT_HASHALG_SHA384; + case 0x05: + return GT_HASHALG_SHA512; + default: + return 0xff; + } +} static inline char * sigTypeName(uint8_t sigID) { @@ -233,5 +259,12 @@ void rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose); void rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose); int rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, uint8_t *bHasRecHashes, uint8_t *bHasIntermedHashes); int rsgt_chkFileHdr(FILE *fp, char *expect); +gtfile rsgt_vrfyConstruct_gf(void); +void rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes); +int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t lenRec); +/* TODO: replace these? */ +void hash_m(gtfile gf, GTDataHash **m); +void hash_r(gtfile gf, GTDataHash **r, const unsigned char *rec, const size_t len); +void hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level); #endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index 961e50c5..ca52cb93 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -277,6 +277,22 @@ rsgt_tlvrdIMPRINT(FILE *fp, imprint_t **imprint, uint16_t tlvlen) done: return r; } +static int +rsgt_tlvrdRecHash(FILE *fp, imprint_t **imp) +{ + int r; + uint16_t tlvtype, tlvlen; + + if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; +printf("read tlvtype %4.4x\n", tlvtype); + if(tlvtype != 0x0900) { + r = RSGTE_MISS_REC_HASH; + goto done; + } + if((r = rsgt_tlvrdIMPRINT(fp, imp, tlvlen)) != 0) goto done; + r = 0; +done: return r; +} /**; * Read the next "object" from file. This usually is @@ -528,3 +544,113 @@ rsgt_chkFileHdr(FILE *fp, char *expect) done: return r; } + +gtfile +rsgt_vrfyConstruct_gf(void) +{ + gtfile gf; + if((gf = calloc(1, sizeof(struct gtfile_s))) == NULL) + goto done; + gf->x_prev = NULL; + +done: return gf; +} + +void +rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes) +{ +printf("bs->hashID %d\n", bs->hashID); + gf->hashAlg = hashID2Alg(bs->hashID); + gf->bKeepRecordHashes = bHasRecHashes; + gf->bKeepTreeHashes = bHasIntermedHashes; + free(gf->IV); + gf->IV = malloc(getIVLen(bs)); + memcpy(gf->IV, bs->iv, getIVLen(bs)); +} + +static int +rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash) +{ + int r = 0; + imprint_t *imp; + + if(!gf->bKeepRecordHashes) + goto done; + if((r = rsgt_tlvrdRecHash(sigfp, &imp)) != 0) + goto done; + if(imp->hashID != hashIdentifier(gf->hashAlg)) { + r = RSGTE_INVLD_REC_HASHID; + goto done; + } +printf("imp hash:"); +outputHexBlob(stdout, imp->data, hashOutputLengthOctets(imp->hashID), 1); +printf("\nrec hash:"); +outputHexBlob(stdout, recHash->digest, hashOutputLengthOctets(imp->hashID), 1); +printf("\n"); + if(memcmp(imp->data, recHash->digest, + hashOutputLengthOctets(imp->hashID))) { + r = RSGTE_INVLD_REC_HASH; + goto done; + } +printf("record hash is OK\n"); + r = 0; +done: + return r; +} + +int +rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, + size_t len) +{ + int r = 0; + GTDataHash *x; /* current hash */ + GTDataHash *m, *recHash, *t; + uint8_t j; + +printf("hasRecHash %d, verify: %s", gf->bKeepRecordHashes, rec); + hash_m(gf, &m); + hash_r(gf, &recHash, rec, len); + if(gf->bKeepRecordHashes) { + r = rsgt_vrfy_chkRecHash(gf, sigfp, recHash); + if(r != 0) goto done; + } + hash_node(gf, &x, m, recHash, 1); /* hash leaf */ + /* persists x here if Merkle tree needs to be persisted! */ + //if(gf->bKeepTreeHashes) + //tlvWriteHash(gf, 0x0901, x); + /* add x to the forest as new leaf, update roots list */ + t = x; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(gf->roots_valid[j] == 0) { + gf->roots_hash[j] = t; + gf->roots_valid[j] = 1; + t = NULL; + break; + } else if(t != NULL) { + /* hash interim node */ + hash_node(gf, &t, gf->roots_hash[j], t, j+2); + gf->roots_valid[j] = 0; + GTDataHash_free(gf->roots_hash[j]); + // TODO: check if this is correct location (paper!) + //if(gf->bKeepTreeHashes) + //tlvWriteHash(gf, 0x0901, t); + } + } + if(t != NULL) { + /* new level, append "at the top" */ + gf->roots_hash[gf->nRoots] = t; + gf->roots_valid[gf->nRoots] = 1; + ++gf->nRoots; + assert(gf->nRoots < MAX_ROOTS); + t = NULL; + } + gf->x_prev = x; /* single var may be sufficient */ + ++gf->nRecords; + + /* cleanup */ + /* note: x is freed later as part of roots cleanup */ + GTDataHash_free(m); + GTDataHash_free(recHash); +done: + return r; +} diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index 021cd9f8..54a795a1 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -130,12 +130,20 @@ dbgprintf("DDDD: onFileOpen: %s\n", fn); RETiRet; } +/* Note: we assume that the record is terminated by a \n. + * As of the GuardTime paper, \n is not part of the signed + * message, so we subtract one from the record size. This + * may cause issues with non-standard formats, but let's + * see how things evolve (the verifier will not work in + * any case when the records are not \n delimited...). + * rgerhards, 2013-03-17 + */ static rsRetVal OnRecordWrite(void *pF, uchar *rec, rs_size_t lenRec) { DEFiRet; -dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec, rec); - sigblkAddRecord(pF, rec, lenRec); +dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec-1, rec); + sigblkAddRecord(pF, rec, lenRec-1); RETiRet; } diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index 3286163b..d94a63cd 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -34,7 +34,8 @@ typedef unsigned char uchar; -static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS +static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS, + MD_VERIFY } mode = MD_DUMP; static int verbose = 0; @@ -143,6 +144,91 @@ detectFileType(char *name) err: fprintf(stderr, "error %d processing file %s\n", r, name); } +static inline int +doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf) +{ + int r; + size_t lenRec; + char rec[128*1024]; + + fgets(rec, sizeof(rec), logfp); + lenRec = strlen(rec); + if(rec[lenRec-1] == '\n') + --lenRec; + + r = rsgt_vrfy_nextRec(bs, gf, sigfp, (unsigned char*)rec, lenRec); + return r; +} + +/* note: here we need to have the LOG file name, not signature! */ +static void +verify(char *name) +{ + FILE *logfp = NULL, *sigfp = NULL; + block_sig_t *bs; + gtfile gf; + uint8_t bHasRecHashes, bHasIntermedHashes; + uint8_t bInBlock; + int r = 0; + uint64_t nRecs; + char sigfname[4096]; + + if(!strcmp(name, "-")) { + fprintf(stderr, "verify mode cannot work on stdin\n"); + goto err; + } else { + snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name); + sigfname[sizeof(sigfname)-1] = '\0'; + if((logfp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } + if((sigfp = fopen(sigfname, "r")) == NULL) { + perror(name); + goto err; + } + } + if((r = rsgt_chkFileHdr(sigfp, "LOGSIG10")) != 0) goto err; + + gf = rsgt_vrfyConstruct_gf(); + if(gf == NULL) { + fprintf(stderr, "error initializing signature file structure\n"); + goto err; + } + + bInBlock = 0; + + while(!feof(logfp)) { + if(bInBlock == 0) { + if((r = rsgt_getBlockParams(sigfp, 1, &bs, &bHasRecHashes, + &bHasIntermedHashes)) != 0) + goto err; + rsgt_vrfyBlkInit(gf, bs, bHasRecHashes, + bHasIntermedHashes); + nRecs = 0; + bInBlock = 1; + } + ++nRecs; + if((r = doVerifyRec(logfp, sigfp, bs, gf)) != 0) + goto err; + if(nRecs == bs->recCount) { + // verifyBLOCK_SIG(bs, gf); + bInBlock = 0; + } + } + + fclose(logfp); + fclose(sigfp); + return; +err: + if(logfp != NULL) + fclose(logfp); + if(sigfp != NULL) + fclose(sigfp); + if(r != RSGTE_EOF) + fprintf(stderr, "error %d processing file %s [%s]\n", r, name, sigfname); +} + static void processFile(char *name) { @@ -156,6 +242,9 @@ processFile(char *name) case MD_SHOW_SIGBLK_PARAMS: showSigblkParams(name); break; + case MD_VERIFY: + verify(name); + break; } } @@ -167,6 +256,7 @@ static struct option long_options[] = {"version", no_argument, NULL, 'V'}, {"detect-file-type", no_argument, NULL, 'T'}, {"show-sigblock-params", no_argument, NULL, 'B'}, + {"verify", no_argument, NULL, 't'}, /* 't' as in "test signatures" */ {NULL, 0, NULL, 0} }; @@ -196,6 +286,9 @@ main(int argc, char *argv[]) case 'T': mode = MD_DETECT_FILE_TYPE; break; + case 't': + mode = MD_VERIFY; + break; case '?': break; default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt); -- cgit v1.2.3 From 03901766c7f452c637ac57ec526b98895da510d5 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Sun, 17 Mar 2013 14:29:17 +0100 Subject: logsig: milestone/verfier: tree hashes are verified; ALSO a bugfix bugfix: the initial vector was used incorrectly during hash computation. --- runtime/librsgt.c | 2 +- runtime/librsgt_read.c | 74 ++++++++++++++++++++++++++++++++++++++++---------- tools/rsgtutil.c | 6 +++- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index e49011f4..92b0d412 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -483,7 +483,7 @@ sigblkInit(gtfile gf) static inline void bufAddIV(gtfile gf, uchar *buf, size_t *len) { - memcpy(buf+*len, &gf->IV, sizeof(gf->IV)); + memcpy(buf+*len, gf->IV, hashOutputLengthOctets(gf->hashAlg)); *len += sizeof(gf->IV); } diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index ca52cb93..8dc6f811 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -294,6 +294,23 @@ printf("read tlvtype %4.4x\n", tlvtype); done: return r; } +static int +rsgt_tlvrdTreeHash(FILE *fp, imprint_t **imp) +{ + int r; + uint16_t tlvtype, tlvlen; + + if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; +printf("read tlvtype %4.4x\n", tlvtype); + if(tlvtype != 0x0901) { + r = RSGTE_MISS_TREE_HASH; + goto done; + } + if((r = rsgt_tlvrdIMPRINT(fp, imp, tlvlen)) != 0) goto done; + r = 0; +done: return r; +} + /**; * Read the next "object" from file. This usually is * a single TLV, but may be something larger, for @@ -350,7 +367,7 @@ outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) } else { fprintf(fp, "%2.2x%2.2x[...]%2.2x%2.2x", blob[0], blob[1], - blob[len-2], blob[len-2]); + blob[len-2], blob[len-1]); } } @@ -559,13 +576,16 @@ done: return gf; void rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes) { -printf("bs->hashID %d\n", bs->hashID); gf->hashAlg = hashID2Alg(bs->hashID); gf->bKeepRecordHashes = bHasRecHashes; gf->bKeepTreeHashes = bHasIntermedHashes; free(gf->IV); gf->IV = malloc(getIVLen(bs)); memcpy(gf->IV, bs->iv, getIVLen(bs)); + free(gf->blkStrtHash); + gf->lenBlkStrtHash = bs->lastHash.len; + gf->blkStrtHash = malloc(gf->lenBlkStrtHash); + memcpy(gf->blkStrtHash, bs->lastHash.data, gf->lenBlkStrtHash); } static int @@ -574,30 +594,52 @@ rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash) int r = 0; imprint_t *imp; - if(!gf->bKeepRecordHashes) - goto done; if((r = rsgt_tlvrdRecHash(sigfp, &imp)) != 0) goto done; if(imp->hashID != hashIdentifier(gf->hashAlg)) { r = RSGTE_INVLD_REC_HASHID; goto done; } -printf("imp hash:"); -outputHexBlob(stdout, imp->data, hashOutputLengthOctets(imp->hashID), 1); -printf("\nrec hash:"); -outputHexBlob(stdout, recHash->digest, hashOutputLengthOctets(imp->hashID), 1); -printf("\n"); if(memcmp(imp->data, recHash->digest, hashOutputLengthOctets(imp->hashID))) { r = RSGTE_INVLD_REC_HASH; goto done; } -printf("record hash is OK\n"); r = 0; done: return r; } +static int +rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, GTDataHash *hash) +{ + int r = 0; + imprint_t *imp; + + if((r = rsgt_tlvrdTreeHash(sigfp, &imp)) != 0) + goto done; + if(imp->hashID != hashIdentifier(gf->hashAlg)) { + r = RSGTE_INVLD_TREE_HASHID; + goto done; + } +//printf("imp hash:"); +//outputHexBlob(stdout, imp->data, hashOutputLengthOctets(imp->hashID), 1); +//printf("\ntree hash:"); +//outputHexBlob(stdout, hash->digest, hashOutputLengthOctets(imp->hashID), 1); +//printf("\nlenBlkStrtHAsh %d\nblkstrt hash:", gf->lenBlkStrtHash); +//outputHexBlob(stdout, gf->blkStrtHash, gf->lenBlkStrtHash, 1); +//printf("\n"); + if(memcmp(imp->data, hash->digest, + hashOutputLengthOctets(imp->hashID))) { + r = RSGTE_INVLD_TREE_HASH; + goto done; + } + r = 0; +printf("Tree hash OK\n"); +done: + return r; +} + int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t len) @@ -616,8 +658,10 @@ printf("hasRecHash %d, verify: %s", gf->bKeepRecordHashes, rec); } hash_node(gf, &x, m, recHash, 1); /* hash leaf */ /* persists x here if Merkle tree needs to be persisted! */ - //if(gf->bKeepTreeHashes) - //tlvWriteHash(gf, 0x0901, x); + if(gf->bKeepTreeHashes) { + r = rsgt_vrfy_chkTreeHash(gf, sigfp, x); + if(r != 0) goto done; + } /* add x to the forest as new leaf, update roots list */ t = x; for(j = 0 ; j < gf->nRoots ; ++j) { @@ -632,8 +676,10 @@ printf("hasRecHash %d, verify: %s", gf->bKeepRecordHashes, rec); gf->roots_valid[j] = 0; GTDataHash_free(gf->roots_hash[j]); // TODO: check if this is correct location (paper!) - //if(gf->bKeepTreeHashes) - //tlvWriteHash(gf, 0x0901, t); + if(gf->bKeepTreeHashes) { + r = rsgt_vrfy_chkTreeHash(gf, sigfp, t); + if(r != 0) goto done; + } } } if(t != NULL) { diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index d94a63cd..d4c58d2d 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -151,12 +151,16 @@ doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf) size_t lenRec; char rec[128*1024]; - fgets(rec, sizeof(rec), logfp); + if(fgets(rec, sizeof(rec), logfp) == NULL) { + r = feof(logfp) ? RSGTE_EOF : RSGTE_IO; + goto done; + } lenRec = strlen(rec); if(rec[lenRec-1] == '\n') --lenRec; r = rsgt_vrfy_nextRec(bs, gf, sigfp, (unsigned char*)rec, lenRec); +done: return r; } -- cgit v1.2.3 From b09d37063fc155ff5ec38430c679da5be5de0dcc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Sun, 17 Mar 2013 15:46:24 +0100 Subject: logsig: milestone/verfier: block timestamp verification almost complete unfortunately, there seems to be a problem with the GuardTime API, so that I need their support before being able to carry on. Once I receive it, it should be fairly quick to complete the function. I am commiting this work as I do not know how long it will take to receive an answer. --- runtime/librsgt.h | 3 ++ runtime/librsgt_read.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++- tools/rsgtutil.c | 2 +- 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/runtime/librsgt.h b/runtime/librsgt.h index 35ee96b5..26eaf8ee 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -126,6 +126,8 @@ struct rsgtstatefile { #define RSGTE_INVLD_TREE_HASH 13 /* invalid tree hash (failed verification) */ #define RSGTE_INVLD_REC_HASHID 14 /* invalid record hash ID (failed verification) */ #define RSGTE_INVLD_TREE_HASHID 15 /* invalid tree hash ID (failed verification) */ +#define RSGTE_MISS_BLOCKSIG 16 /* block signature record missing when expected */ +#define RSGTE_INVLD_TIMESTAMP 17 /* RFC3161 timestamp is invalid */ static inline uint16_t @@ -262,6 +264,7 @@ int rsgt_chkFileHdr(FILE *fp, char *expect); gtfile rsgt_vrfyConstruct_gf(void); void rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes); int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t lenRec); +int verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs); /* TODO: replace these? */ void hash_m(gtfile gf, GTDataHash **m); diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index 8dc6f811..0cc5f492 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -311,6 +311,24 @@ printf("read tlvtype %4.4x\n", tlvtype); done: return r; } +/* read BLOCK_SIG during verification phase */ +static int +rsgt_tlvrdVrfyBlockSig(FILE *fp, block_sig_t **bs) +{ + int r; + uint16_t tlvtype, tlvlen; + + if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; +printf("read tlvtype %4.4x\n", tlvtype); + if(tlvtype != 0x0902) { + r = RSGTE_MISS_BLOCKSIG; + goto done; + } + if((r = rsgt_tlvrdBLOCK_SIG(fp, bs, tlvlen)) != 0) goto done; + r = 0; +done: return r; +} + /**; * Read the next "object" from file. This usually is * a single TLV, but may be something larger, for @@ -649,7 +667,6 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, GTDataHash *m, *recHash, *t; uint8_t j; -printf("hasRecHash %d, verify: %s", gf->bKeepRecordHashes, rec); hash_m(gf, &m); hash_r(gf, &recHash, rec, len); if(gf->bKeepRecordHashes) { @@ -700,3 +717,93 @@ printf("hasRecHash %d, verify: %s", gf->bKeepRecordHashes, rec); done: return r; } + + +static int +verifyTimestamp(gtfile gf, GTDataHash *root) +{ + int r = 0; + printf("in verifyTimestamp\n"); + return r; +} + + +/* TODO: think about merging this with the writer. The + * same applies to the other computation algos. + */ +static int +verifySigblkFinish(gtfile gf) +{ + GTDataHash *root, *rootDel; + int8_t j; + int r; + + if(gf->nRecords == 0) + goto done; + + root = NULL; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(root == NULL) { + root = gf->roots_hash[j]; + gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + } else if(gf->roots_valid[j]) { + rootDel = root; + hash_node(gf, &root, gf->roots_hash[j], root, j+2); + gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + GTDataHash_free(rootDel); + } + } + r = verifyTimestamp(gf, root); + + free(gf->blkStrtHash); + gf->blkStrtHash = NULL; + // We do not need the following as we take this from the block params + // (but I leave it in in order to aid getting to common code) + //gf->lenBlkStrtHash = gf->x_prev->digest_length; + //gf->blkStrtHash = malloc(gf->lenBlkStrtHash); + //memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); +done: + gf->bInBlk = 0; + return r; +} + +/* verify the root hash. This also means we need to compute the + * Merkle tree root for the current block. + */ +int +verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs) +{ + int r; + int gtstate; + block_sig_t *file_bs; + GTTimestamp *timestamp = NULL; + GTVerificationInfo *vrfyInf; + + if((r = verifySigblkFinish(gf)) != 0) + goto done; + if((r = rsgt_tlvrdVrfyBlockSig(sigfp, &file_bs)) != 0) + goto done; +printf("got sig block, now doing checks \n"); + if(nRecs != bs->recCount) { + r = RSGTE_INVLD_RECCNT; + goto done; + } + +printf("len DER timestamp: %d, data %p\n", (int) file_bs->sig.der.len, file_bs->sig.der.data); + gtstate = GTTimestamp_DERDecode(file_bs->sig.der.data, + file_bs->sig.der.len, ×tamp); +printf("result of GTTimestamp_DERDecode: %d\n", gtstate); + gtstate = GTTimestamp_verify(timestamp, 1, &vrfyInf); +printf("result of GTTimestamp_verify: %d, verf_err %d\n", gtstate, vrfyInf->verification_errors ); + if(! (gtstate == GT_OK + && vrfyInf->verification_errors == GT_NO_FAILURES) ) { + r = RSGTE_INVLD_TIMESTAMP; goto done; + } + +printf("root timestamp OK\n"); + r = 0; +done: + if(timestamp != NULL) + GTTimestamp_free(timestamp); + return r; +} diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index d4c58d2d..cc045f8c 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -216,7 +216,7 @@ verify(char *name) if((r = doVerifyRec(logfp, sigfp, bs, gf)) != 0) goto err; if(nRecs == bs->recCount) { - // verifyBLOCK_SIG(bs, gf); + verifyBLOCK_SIG(bs, gf, sigfp, nRecs); bInBlock = 0; } } -- cgit v1.2.3 From ff192e5b3cea37863d3a080ad993ad3233a852b6 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 11:47:14 +0100 Subject: bugfix: mmanon did not build on some platforms (e.g. Ubuntu) --- ChangeLog | 3 ++- plugins/mmanon/mmanon.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index ddfed3b8..fae64d13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,9 @@ --------------------------------------------------------------------------- -Version 7.3.8 [devel] 2013-03-?? +Version 7.3.8 [devel] 2013-03-18 - imrelp: now supports listening to IPv4/v6 only instead of always both build now requires librelp 1.0.2 closes: http://bugzilla.adiscon.com/show_bug.cgi?id=378 +- bugfix: mmanon did not build on some platforms (e.g. Ubuntu) - bugfix: segfault in expression optimizer closes: http://bugzilla.adiscon.com/show_bug.cgi?id=423 - bugfix: imuxsock was missing SysSock.ParseTrusted module parameter diff --git a/plugins/mmanon/mmanon.c b/plugins/mmanon/mmanon.c index fc0c8a03..a1c99d09 100644 --- a/plugins/mmanon/mmanon.c +++ b/plugins/mmanon/mmanon.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "conf.h" #include "syslogd-types.h" #include "srUtils.h" -- cgit v1.2.3 From cc69a17b62599dc5c5a6d1320b6f62ca7632226d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 11:48:16 +0100 Subject: prepare for 7.3.8 release --- configure.ac | 2 +- doc/manual.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0e398307..7a4af19d 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[7.3.7],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.3.8],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/doc/manual.html b/doc/manual.html index b8461b6d..ca54a04a 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ professional services available directly from the source!

    Please visit the rsyslog sponsor's page to honor the project sponsors or become one yourself! We are very grateful for any help towards the project goals.

    -

    This documentation is for version 7.3.7 (devel branch) of rsyslog. +

    This documentation is for version 7.3.8 (devel branch) of rsyslog. Visit the rsyslog status page to obtain current version information and project status.

    If you like rsyslog, you might -- cgit v1.2.3 From 4dce63486fbb197522e11c5644cdf7aef5d7bbbc Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Thu, 14 Mar 2013 11:52:21 +0100 Subject: Fix trailing '-' in kernel messages. modified: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index b771d68a..c531f941 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -89,6 +89,10 @@ submitSyslog(uchar *buf) for (; isdigit(*buf); buf++) { timestamp += (timestamp * 10) + (*buf - '0'); } + + while (*buf != ';') { + buf++; /* skip everything till the first ; */ + } buf++; /* skip ; */ /* get message */ -- cgit v1.2.3 From 07ddb712ba562480a5492e1c8733b28e3a766d1a Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Thu, 14 Mar 2013 11:52:21 +0100 Subject: Fix trailing '-' in kernel messages. modified: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index b771d68a..c531f941 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -89,6 +89,10 @@ submitSyslog(uchar *buf) for (; isdigit(*buf); buf++) { timestamp += (timestamp * 10) + (*buf - '0'); } + + while (*buf != ';') { + buf++; /* skip everything till the first ; */ + } buf++; /* skip ; */ /* get message */ -- cgit v1.2.3 From 68fe67afc88a105575e6be256523a67c40ccff39 Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Mon, 18 Mar 2013 09:06:33 +0100 Subject: Remove unnecessary strlen() call modified: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index c531f941..bc91f989 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -107,7 +107,7 @@ submitSyslog(uchar *buf) if (*buf != '\0') /* message has appended properties, skip \n */ buf++; - while (strlen((char *)buf)) { + while (*buf) { /* get name of the property */ buf++; /* skip ' ' */ offs = 0; -- cgit v1.2.3 From 3c0c1aa425c8f1bbaa61cf93724cae8e0bd72fee Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Mon, 18 Mar 2013 09:15:32 +0100 Subject: Fix message read buffer size modified: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index bc91f989..0117b6ba 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -178,14 +178,14 @@ static void readkmsg(void) { int i; - uchar pRcv[8096+1]; + uchar pRcv[8192+1]; char errmsg[2048]; for (;;) { dbgprintf("imkmsg waiting for kernel log line\n"); /* every read() from the opened device node receives one record of the printk buffer */ - i = read(fklog, pRcv, 8096); + i = read(fklog, pRcv, 8192); if (i > 0) { /* successful read of message of nonzero length */ -- cgit v1.2.3 From bc740ffc8009095dd6cec3e3629fcd2283906fcd Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Mon, 18 Mar 2013 11:12:46 +0100 Subject: Do not exit when messages get overwritten in the circular buffer modified: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index 0117b6ba..f1815f25 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -190,6 +190,10 @@ readkmsg(void) if (i > 0) { /* successful read of message of nonzero length */ pRcv[i] = '\0'; + } else if (i == -EPIPE) { + imkmsgLogIntMsg(LOG_WARNING, + "imkmsg: some messages in circular buffer got overwritten"); + continue; } else { /* something went wrong - error or zero length message */ if (i < 0 && errno != EINTR && errno != EAGAIN) { -- cgit v1.2.3 From ff261830c65f7d094584dba1f9bce669bd6748d4 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 12:16:28 +0100 Subject: doc: mention last patches in ChangeLog --- ChangeLog | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index b143ae81..9acbbf27 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,5 @@ ---------------------------------------------------------------------------- Version 7.2.7 [v7-stable] 2013-03-?? -- bugfix: imuxsock was missing SysSock.ParseTrusted module parameter - To use that functionality, legacy rsyslog.conf syntax had to be used. - Also, the doc was missing information on the "ParseTrusted" set of - config directives. -- doc bugfix: rsyslog.conf man page had invalid file format info - closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 - rsyslogd startup information is now properly conveyed back to init when privileges are beging dropped Actually, we have moved termination of the parent in front of the @@ -16,6 +10,14 @@ Version 7.2.7 [v7-stable] 2013-03-?? - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. +- bugfix: several issues in imkmsg + see bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=421#c8 +- bugfix: imuxsock was missing SysSock.ParseTrusted module parameter + To use that functionality, legacy rsyslog.conf syntax had to be used. + Also, the doc was missing information on the "ParseTrusted" set of + config directives. +- doc bugfix: rsyslog.conf man page had invalid file format info + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 ---------------------------------------------------------------------------- Version 7.2.6 [v7-stable] 2013-03-05 - slightly improved config parser error messages when invalid escapes happen -- cgit v1.2.3 From 0dab9aa0c4f50403436f318e92a65903a956c195 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 13:57:42 +0100 Subject: fix merge problem --- tools/syslogd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/syslogd.c b/tools/syslogd.c index 66adde48..d4fc1c36 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -2001,16 +2001,16 @@ int realMain(int argc, char **argv) if(!iConfigVerify) CHKiRet(doGlblProcessInit()); + /* Send a signal to the parent so it can terminate. */ + if(glblGetOurPid() != ppid) + kill(ppid, SIGTERM); + CHKiRet(init()); if(Debug && debugging_on) { dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n"); } - /* Send a signal to the parent so it can terminate. */ - if(glblGetOurPid() != ppid) - kill(ppid, SIGTERM); - /* END OF INTIALIZATION */ DBGPRINTF("initialization completed, transitioning to regular run mode\n"); -- cgit v1.2.3 From 1125a60d3fcc5b64e0cc7abe54748b6d6ee2fb28 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 14:41:11 +0100 Subject: logsig: implement timestamp verification We now use a higher-level verification function as suggested by GuardTime. --- runtime/librsgt_read.c | 25 +++++++++++-------------- tools/rsgtutil.c | 4 ++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index 0cc5f492..11c43775 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "librsgt.h" @@ -719,20 +720,11 @@ done: } -static int -verifyTimestamp(gtfile gf, GTDataHash *root) -{ - int r = 0; - printf("in verifyTimestamp\n"); - return r; -} - - /* TODO: think about merging this with the writer. The * same applies to the other computation algos. */ static int -verifySigblkFinish(gtfile gf) +verifySigblkFinish(gtfile gf, GTDataHash **pRoot) { GTDataHash *root, *rootDel; int8_t j; @@ -753,15 +745,16 @@ verifySigblkFinish(gtfile gf) GTDataHash_free(rootDel); } } - r = verifyTimestamp(gf, root); free(gf->blkStrtHash); gf->blkStrtHash = NULL; + *pRoot = root; // We do not need the following as we take this from the block params // (but I leave it in in order to aid getting to common code) //gf->lenBlkStrtHash = gf->x_prev->digest_length; //gf->blkStrtHash = malloc(gf->lenBlkStrtHash); //memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); + r = 0; done: gf->bInBlk = 0; return r; @@ -778,8 +771,9 @@ verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs) block_sig_t *file_bs; GTTimestamp *timestamp = NULL; GTVerificationInfo *vrfyInf; + GTDataHash *root = NULL; - if((r = verifySigblkFinish(gf)) != 0) + if((r = verifySigblkFinish(gf, &root)) != 0) goto done; if((r = rsgt_tlvrdVrfyBlockSig(sigfp, &file_bs)) != 0) goto done; @@ -793,8 +787,11 @@ printf("len DER timestamp: %d, data %p\n", (int) file_bs->sig.der.len, file_bs-> gtstate = GTTimestamp_DERDecode(file_bs->sig.der.data, file_bs->sig.der.len, ×tamp); printf("result of GTTimestamp_DERDecode: %d\n", gtstate); - gtstate = GTTimestamp_verify(timestamp, 1, &vrfyInf); -printf("result of GTTimestamp_verify: %d, verf_err %d\n", gtstate, vrfyInf->verification_errors ); + gtstate = GTHTTP_verifyTimestampHash(timestamp, root, NULL, + NULL, NULL, + "http://verify.guardtime.com/gt-controlpublications.bin", + 0, &vrfyInf); +printf("result of HTTP_vrfy: %d: %s\n", gtstate, GTHTTP_getErrorString(gtstate)); if(! (gtstate == GT_OK && vrfyInf->verification_errors == GT_NO_FAILURES) ) { r = RSGTE_INVLD_TIMESTAMP; goto done; diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index cc045f8c..0ccfcd7b 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -192,6 +192,9 @@ verify(char *name) goto err; } } + + rsgtInit("rsyslog rsgtutil " VERSION); + if((r = rsgt_chkFileHdr(sigfp, "LOGSIG10")) != 0) goto err; gf = rsgt_vrfyConstruct_gf(); @@ -231,6 +234,7 @@ err: fclose(sigfp); if(r != RSGTE_EOF) fprintf(stderr, "error %d processing file %s [%s]\n", r, name, sigfname); + rsgtExit(); } static void -- cgit v1.2.3 From cec4c3d3a2cf429999c37d8e987d1186c0755e9b Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 17:34:09 +0100 Subject: logsig: fix abort if no signature provider was configured --- tools/omfile.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/omfile.c b/tools/omfile.c index 4740ed1d..faf3c24f 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -759,7 +759,9 @@ doWrite(instanceData *pData, uchar *pszBuf, int lenBuf) DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf); if(pData->pStrm != NULL){ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf)); - CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf)); + if(pData->useSigprov) { + CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf)); + } } finalize_it: -- cgit v1.2.3 From 9101b00ed29145bde5b816889544eed4638f0925 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 18 Mar 2013 17:36:38 +0100 Subject: logsig: fix calculation of merkle tree at end of signature block --- runtime/librsgt.c | 2 +- runtime/librsgt_read.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 92b0d412..20c8fd79 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -649,7 +649,7 @@ sigblkFinish(gtfile gf) root = NULL; for(j = 0 ; j < gf->nRoots ; ++j) { if(root == NULL) { - root = gf->roots_hash[j]; + root = gf->roots_valid[j] ? gf->roots_hash[j] : NULL; gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ } else if(gf->roots_valid[j]) { rootDel = root; diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index 0cc5f492..32a62c65 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -744,7 +744,7 @@ verifySigblkFinish(gtfile gf) root = NULL; for(j = 0 ; j < gf->nRoots ; ++j) { if(root == NULL) { - root = gf->roots_hash[j]; + root = gf->roots_valid[j] ? gf->roots_hash[j] : NULL; gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ } else if(gf->roots_valid[j]) { rootDel = root; -- cgit v1.2.3 From 785e0c3c8b00614b6f780f902442025f4558a160 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 19 Mar 2013 15:26:21 +0100 Subject: logsig: complete initial version of verifier --- runtime/librsgt.c | 9 +- runtime/librsgt.h | 96 +++++++++++++++++++- runtime/librsgt_read.c | 231 ++++++++++++++++++++++++++++++++++++------------- runtime/lmsig_gt.c | 3 +- tools/rsgtutil.c | 61 +++++++++---- 5 files changed, 315 insertions(+), 85 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 20c8fd79..e24c3769 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -325,8 +325,11 @@ writeStateFile(gtfile gf) memcpy(sf.hdr, "GTSTAT10", 8); sf.hashID = hashIdentifier(gf->hashAlg); sf.lenHash = gf->x_prev->digest_length; - write(fd, &sf, sizeof(sf)); - write(fd, gf->x_prev->digest, gf->x_prev->digest_length); + /* if the write fails, we cannot do anything against that. We check + * the condition just to keep the compiler happy. + */ + if(write(fd, &sf, sizeof(sf))){}; + if(write(fd, gf->x_prev->digest, gf->x_prev->digest_length)){}; close(fd); done: return; } @@ -387,7 +390,7 @@ seedIV(gtfile gf) * will always work...). -- TODO -- rgerhards, 2013-03-06 */ if((fd = open("/dev/urandom", O_RDONLY)) > 0) { - read(fd, gf->IV, hashlen); + if(read(fd, gf->IV, hashlen)) {}; /* keep compiler happy */ close(fd); } } diff --git a/runtime/librsgt.h b/runtime/librsgt.h index 26eaf8ee..98384bb9 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -73,10 +73,39 @@ struct gtfile_s { int tlvIdx; /* current index into tlvBuf */ }; typedef struct gtfile_s *gtfile; - +typedef struct gterrctx_s gterrctx_t; typedef struct imprint_s imprint_t; typedef struct block_sig_s block_sig_t; +/* The following structure describes the "error context" to be used + * for verification and similiar reader functions. While verifying, + * we need some information (like filenames or block numbers) that + * is not readily available from the other objects (or not even known + * to librsgt). In order to provide meaningful error messages, this + * information must be passed in from the external callers. In order + * to centralize information (and make it more manageable), we use + * ths error context here, which contains everything needed to + * generate good error messages. Members of this structure are + * maintained both by library users (the callers) as well as + * the library itself. Who does what simply depends on who has + * the relevant information. + */ +struct gterrctx_s { + FILE *fp; /**< file for error messages */ + char *filename; + uint8_t verbose; + uint64_t recNumInFile; + uint64_t recNum; + uint64_t blkNum; + uint8_t treeLevel; + GTDataHash *computedHash; + GTDataHash *lefthash, *righthash; /* hashes to display if tree hash fails */ + imprint_t *fileHash; + int gtstate; /* status from last relevant GT.*() function call */ + char *errRec; + char *frstRecInBlk; /* This holds the first message seen inside the current block */ +}; + struct imprint_s { uint8_t hashID; int len; @@ -115,7 +144,7 @@ struct rsgtstatefile { #define RSGTE_INVLTYP 3 /* invalid TLV type record (unexcpected at this point) */ #define RSGTE_OOM 4 /* ran out of memory */ #define RSGTE_LEN 5 /* error related to length records */ -#define RSGTE_NO_BLKSIG 6/* block signature record is missing --> invalid block */ +// 6 may be reused! #define RSGTE_INVLD_RECCNT 7/* mismatch between actual records and records given in block-sig record */ #define RSGTE_INVLHDR 8/* invalid file header */ @@ -128,6 +157,57 @@ struct rsgtstatefile { #define RSGTE_INVLD_TREE_HASHID 15 /* invalid tree hash ID (failed verification) */ #define RSGTE_MISS_BLOCKSIG 16 /* block signature record missing when expected */ #define RSGTE_INVLD_TIMESTAMP 17 /* RFC3161 timestamp is invalid */ +#define RSGTE_TS_DERDECODE 18 /* error DER-Decoding a timestamp */ + +/* the following function maps RSGTE_* state to a string - must be updated + * whenever a new state is added. + * Note: it is thread-safe to call this function, as it returns a pointer + * into constant memory pool. + */ +static inline char * +RSGTE2String(int err) +{ + switch(err) { + case 0: + return "success"; + case RSGTE_IO: + return "i/o error"; + case RSGTE_FMT: + return "data format error"; + case RSGTE_INVLTYP: + return "invalid/unexpected tlv record type"; + case RSGTE_OOM: + return "out of memory"; + case RSGTE_LEN: + return "length record problem"; + case RSGTE_INVLD_RECCNT: + return "mismatch between actual record count and number in block signature record"; + case RSGTE_INVLHDR: + return "invalid file header"; + case RSGTE_EOF: + return "EOF"; + case RSGTE_MISS_REC_HASH: + return "record hash missing"; + case RSGTE_MISS_TREE_HASH: + return "tree hash missing"; + case RSGTE_INVLD_REC_HASH: + return "record hash mismatch"; + case RSGTE_INVLD_TREE_HASH: + return "tree hash mismatch"; + case RSGTE_INVLD_REC_HASHID: + return "invalid record hash ID"; + case RSGTE_INVLD_TREE_HASHID: + return "invalid tree hash ID"; + case RSGTE_MISS_BLOCKSIG: + return "missing block signature record"; + case RSGTE_INVLD_TIMESTAMP: + return "RFC3161 timestamp invalid"; + case RSGTE_TS_DERDECODE: + return "error DER-decoding RFC3161 timestamp"; + default: + return "unknown error"; + } +} static inline uint16_t @@ -263,11 +343,19 @@ int rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, uint8_t *bH int rsgt_chkFileHdr(FILE *fp, char *expect); gtfile rsgt_vrfyConstruct_gf(void); void rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes); -int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t lenRec); -int verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs); +int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t lenRec, gterrctx_t *ectx); +int verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, gterrctx_t *ectx); +void rsgt_errctxInit(gterrctx_t *ectx); +void rsgt_errctxExit(gterrctx_t *ectx); +void rsgt_errctxSetErrRec(gterrctx_t *ectx, char *rec); +void rsgt_errctxFrstRecInBlk(gterrctx_t *ectx, char *rec); + /* TODO: replace these? */ void hash_m(gtfile gf, GTDataHash **m); void hash_r(gtfile gf, GTDataHash **r, const unsigned char *rec, const size_t len); void hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level); +extern char *rsgt_read_puburl; /**< url of publication server */ +extern uint8_t rsgt_read_showVerified; + #endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index d756c336..e32ae454 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -48,6 +48,8 @@ typedef unsigned char uchar; #define MAXFNAME 1024 static int rsgt_read_debug = 0; +char *rsgt_read_puburl = "http://verify.guardtime.com/gt-controlpublications.bin"; +uint8_t rsgt_read_showVerified = 0; /* macro to obtain next char from file including error tracking */ #define NEXTC if((c = fgetc(fp)) == EOF) { \ @@ -58,6 +60,130 @@ static int rsgt_read_debug = 0; /* check return state of operation and abort, if non-OK */ #define CHKr(code) if((r = code) != 0) goto done + +/* if verbose==0, only the first and last two octets are shown, + * otherwise everything. + */ +static void +outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) +{ + unsigned i; + if(verbose || len <= 8) { + for(i = 0 ; i < len ; ++i) + fprintf(fp, "%2.2x", blob[i]); + } else { + fprintf(fp, "%2.2x%2.2x%2.2x[...]%2.2x%2.2x%2.2x", + blob[0], blob[1], blob[2], + blob[len-3], blob[len-2], blob[len-1]); + } +} + +static inline void +outputHash(FILE *fp, char *hdr, uint8_t *data, uint16_t len, uint8_t verbose) +{ + fprintf(fp, "%s", hdr); + outputHexBlob(fp, data, len, verbose); + fputc('\n', fp); +} + +void +rsgt_errctxInit(gterrctx_t *ectx) +{ + ectx->fp = NULL; + ectx->filename = NULL; + ectx->recNum = 0; + ectx->recNumInFile = 0; + ectx->blkNum = 0; + ectx->verbose = 0; + ectx->errRec = NULL; + ectx->frstRecInBlk = NULL; +} +void +rsgt_errctxExit(gterrctx_t *ectx) +{ + free(ectx->filename); + free(ectx->frstRecInBlk); +} + +/* note: we do not copy the record, so the caller MUST not destruct + * it before processing of the record is completed. To remove the + * current record without setting a new one, call this function + * with rec==NULL. + */ +void +rsgt_errctxSetErrRec(gterrctx_t *ectx, char *rec) +{ + ectx->errRec = strdup(rec); +} +/* This stores the block's first record. Here we copy the data, + * as the caller will usually not preserve it long enough. + */ +void +rsgt_errctxFrstRecInBlk(gterrctx_t *ectx, char *rec) +{ + free(ectx->frstRecInBlk); + ectx->frstRecInBlk = strdup(rec); +} + +static void +reportError(int errcode, gterrctx_t *ectx) +{ + if(ectx->fp != NULL) { + fprintf(ectx->fp, "%s[%llu:%llu:%llu]: error[%u]: %s\n", + ectx->filename, + (long long unsigned) ectx->blkNum, (long long unsigned) ectx->recNum, + (long long unsigned) ectx->recNumInFile, + errcode, RSGTE2String(errcode)); + if(ectx->frstRecInBlk != NULL) + fprintf(ectx->fp, "\tBlock Start Record.: '%s'\n", ectx->frstRecInBlk); + if(ectx->errRec != NULL) + fprintf(ectx->fp, "\tRecord in Question.: '%s'\n", ectx->errRec); + if(ectx->computedHash != NULL) { + outputHash(ectx->fp, "\tComputed Hash......: ", ectx->computedHash->digest, + ectx->computedHash->digest_length, ectx->verbose); + } + if(ectx->fileHash != NULL) { + outputHash(ectx->fp, "\tSignature File Hash: ", ectx->fileHash->data, + ectx->fileHash->len, ectx->verbose); + } + if(errcode == RSGTE_INVLD_TREE_HASH || + errcode == RSGTE_INVLD_TREE_HASHID) { + fprintf(ectx->fp, "\tTree Level.........: %d\n", (int) ectx->treeLevel); + outputHash(ectx->fp, "\tTree Left Hash.....: ", ectx->lefthash->digest, + ectx->lefthash->digest_length, ectx->verbose); + outputHash(ectx->fp, "\tTree Right Hash....: ", ectx->righthash->digest, + ectx->righthash->digest_length, ectx->verbose); + } + if(errcode == RSGTE_INVLD_TIMESTAMP || + errcode == RSGTE_TS_DERDECODE) { + fprintf(ectx->fp, "\tPublication Server.: %s\n", rsgt_read_puburl); + fprintf(ectx->fp, "\tGT Verify Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } + } +} + +/* obviously, this is not an error-reporting function. We still use + * ectx, as it has most information we need. + */ +static void +reportVerifySuccess(gterrctx_t *ectx) +{ + if(ectx->fp != NULL) { + fprintf(ectx->fp, "%s[%llu:%llu:%llu]: block signature successfully verified\n", + ectx->filename, + (long long unsigned) ectx->blkNum, (long long unsigned) ectx->recNum, + (long long unsigned) ectx->recNumInFile); + if(ectx->frstRecInBlk != NULL) + fprintf(ectx->fp, "\tBlock Start Record.: '%s'\n", ectx->frstRecInBlk); + if(ectx->errRec != NULL) + fprintf(ectx->fp, "\tBlock End Record...: '%s'\n", ectx->errRec); + fprintf(ectx->fp, "\tGT Verify Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } +} + + /** * Read a header from a binary file. * @param[in] fp file pointer for processing @@ -239,7 +365,6 @@ rsgt_tlvrdBLOCK_SIG(FILE *fp, block_sig_t **blocksig, uint16_t tlvlen) 2 + lenInt /* rec-count */ + 4 + bs->sig.der.len /* rfc-3161 */; if(sizeRead != tlvlen) { - printf("length record error!\n"); r = RSGTE_LEN; goto done; } @@ -285,7 +410,6 @@ rsgt_tlvrdRecHash(FILE *fp, imprint_t **imp) uint16_t tlvtype, tlvlen; if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; -printf("read tlvtype %4.4x\n", tlvtype); if(tlvtype != 0x0900) { r = RSGTE_MISS_REC_HASH; goto done; @@ -302,7 +426,6 @@ rsgt_tlvrdTreeHash(FILE *fp, imprint_t **imp) uint16_t tlvtype, tlvlen; if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; -printf("read tlvtype %4.4x\n", tlvtype); if(tlvtype != 0x0901) { r = RSGTE_MISS_TREE_HASH; goto done; @@ -320,7 +443,6 @@ rsgt_tlvrdVrfyBlockSig(FILE *fp, block_sig_t **bs) uint16_t tlvtype, tlvlen; if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; -printf("read tlvtype %4.4x\n", tlvtype); if(tlvtype != 0x0902) { r = RSGTE_MISS_BLOCKSIG; goto done; @@ -372,24 +494,6 @@ rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj) done: return r; } - -/* if verbose==0, only the first and last two octets are shown, - * otherwise everything. - */ -static void -outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) -{ - unsigned i; - if(verbose || len <= 6) { - for(i = 0 ; i < len ; ++i) - fprintf(fp, "%2.2x", blob[i]); - } else { - fprintf(fp, "%2.2x%2.2x[...]%2.2x%2.2x", - blob[0], blob[1], - blob[len-2], blob[len-1]); - } -} - /* return if a blob is all zero */ static inline int blobIsZero(uint8_t *blob, uint16_t len) @@ -412,14 +516,14 @@ rsgt_printIMPRINT(FILE *fp, char *name, imprint_t *imp, uint8_t verbose) static void rsgt_printREC_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) { - rsgt_printIMPRINT(fp, "[0x0900]Record hash................: ", + rsgt_printIMPRINT(fp, "[0x0900]Record hash: ", imp, verbose); } static void rsgt_printINT_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) { - rsgt_printIMPRINT(fp, "[0x0901]Intermediate aggregate hash: ", + rsgt_printIMPRINT(fp, "[0x0901]Tree hash..: ", imp, verbose); } @@ -608,20 +712,26 @@ rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHas } static int -rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash) +rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash, gterrctx_t *ectx) { int r = 0; imprint_t *imp; if((r = rsgt_tlvrdRecHash(sigfp, &imp)) != 0) + reportError(r, ectx); goto done; if(imp->hashID != hashIdentifier(gf->hashAlg)) { + reportError(r, ectx); r = RSGTE_INVLD_REC_HASHID; goto done; } if(memcmp(imp->data, recHash->digest, hashOutputLengthOctets(imp->hashID))) { r = RSGTE_INVLD_REC_HASH; + ectx->computedHash = recHash; + ectx->fileHash = imp; + reportError(r, ectx); + ectx->computedHash = NULL, ectx->fileHash = NULL; goto done; } r = 0; @@ -630,38 +740,37 @@ done: } static int -rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, GTDataHash *hash) +rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, GTDataHash *hash, gterrctx_t *ectx) { int r = 0; imprint_t *imp; - if((r = rsgt_tlvrdTreeHash(sigfp, &imp)) != 0) + if((r = rsgt_tlvrdTreeHash(sigfp, &imp)) != 0) { + reportError(r, ectx); goto done; + } if(imp->hashID != hashIdentifier(gf->hashAlg)) { + reportError(r, ectx); r = RSGTE_INVLD_TREE_HASHID; goto done; } -//printf("imp hash:"); -//outputHexBlob(stdout, imp->data, hashOutputLengthOctets(imp->hashID), 1); -//printf("\ntree hash:"); -//outputHexBlob(stdout, hash->digest, hashOutputLengthOctets(imp->hashID), 1); -//printf("\nlenBlkStrtHAsh %d\nblkstrt hash:", gf->lenBlkStrtHash); -//outputHexBlob(stdout, gf->blkStrtHash, gf->lenBlkStrtHash, 1); -//printf("\n"); if(memcmp(imp->data, hash->digest, hashOutputLengthOctets(imp->hashID))) { r = RSGTE_INVLD_TREE_HASH; + ectx->computedHash = hash; + ectx->fileHash = imp; + reportError(r, ectx); + ectx->computedHash = NULL, ectx->fileHash = NULL; goto done; } r = 0; -printf("Tree hash OK\n"); done: return r; } int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, - size_t len) + size_t len, gterrctx_t *ectx) { int r = 0; GTDataHash *x; /* current hash */ @@ -671,13 +780,15 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, hash_m(gf, &m); hash_r(gf, &recHash, rec, len); if(gf->bKeepRecordHashes) { - r = rsgt_vrfy_chkRecHash(gf, sigfp, recHash); + r = rsgt_vrfy_chkRecHash(gf, sigfp, recHash, ectx); if(r != 0) goto done; } hash_node(gf, &x, m, recHash, 1); /* hash leaf */ - /* persists x here if Merkle tree needs to be persisted! */ if(gf->bKeepTreeHashes) { - r = rsgt_vrfy_chkTreeHash(gf, sigfp, x); + ectx->treeLevel = 0; + ectx->lefthash = m; + ectx->righthash = recHash; + r = rsgt_vrfy_chkTreeHash(gf, sigfp, x, ectx); if(r != 0) goto done; } /* add x to the forest as new leaf, update roots list */ @@ -690,14 +801,16 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, break; } else if(t != NULL) { /* hash interim node */ + ectx->treeLevel = j+1; + ectx->righthash = t; hash_node(gf, &t, gf->roots_hash[j], t, j+2); gf->roots_valid[j] = 0; - GTDataHash_free(gf->roots_hash[j]); - // TODO: check if this is correct location (paper!) if(gf->bKeepTreeHashes) { - r = rsgt_vrfy_chkTreeHash(gf, sigfp, t); - if(r != 0) goto done; + ectx->lefthash = gf->roots_hash[j]; + r = rsgt_vrfy_chkTreeHash(gf, sigfp, t, ectx); + if(r != 0) goto done; /* mem leak ok, we terminate! */ } + GTDataHash_free(gf->roots_hash[j]); } } if(t != NULL) { @@ -749,11 +862,6 @@ verifySigblkFinish(gtfile gf, GTDataHash **pRoot) free(gf->blkStrtHash); gf->blkStrtHash = NULL; *pRoot = root; - // We do not need the following as we take this from the block params - // (but I leave it in in order to aid getting to common code) - //gf->lenBlkStrtHash = gf->x_prev->digest_length; - //gf->blkStrtHash = malloc(gf->lenBlkStrtHash); - //memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); r = 0; done: gf->bInBlk = 0; @@ -764,7 +872,7 @@ done: * Merkle tree root for the current block. */ int -verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs) +verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, gterrctx_t *ectx) { int r; int gtstate; @@ -777,29 +885,34 @@ verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs) goto done; if((r = rsgt_tlvrdVrfyBlockSig(sigfp, &file_bs)) != 0) goto done; -printf("got sig block, now doing checks \n"); - if(nRecs != bs->recCount) { + if(ectx->recNum != bs->recCount) { r = RSGTE_INVLD_RECCNT; goto done; } -printf("len DER timestamp: %d, data %p\n", (int) file_bs->sig.der.len, file_bs->sig.der.data); gtstate = GTTimestamp_DERDecode(file_bs->sig.der.data, file_bs->sig.der.len, ×tamp); -printf("result of GTTimestamp_DERDecode: %d\n", gtstate); + if(gtstate != GT_OK) { + r = RSGTE_TS_DERDECODE; + ectx->gtstate = gtstate; + goto done; + } + gtstate = GTHTTP_verifyTimestampHash(timestamp, root, NULL, - NULL, NULL, - "http://verify.guardtime.com/gt-controlpublications.bin", - 0, &vrfyInf); -printf("result of HTTP_vrfy: %d: %s\n", gtstate, GTHTTP_getErrorString(gtstate)); + NULL, NULL, rsgt_read_puburl, 0, &vrfyInf); if(! (gtstate == GT_OK && vrfyInf->verification_errors == GT_NO_FAILURES) ) { - r = RSGTE_INVLD_TIMESTAMP; goto done; + r = RSGTE_INVLD_TIMESTAMP; + ectx->gtstate = gtstate; + goto done; } -printf("root timestamp OK\n"); r = 0; + if(rsgt_read_showVerified) + reportVerifySuccess(ectx); done: + if(r != 0) + reportError(r, ectx); if(timestamp != NULL) GTTimestamp_free(timestamp); return r; diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index 54a795a1..d61f3a86 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -118,9 +118,10 @@ SetCnfParam(void *pT, struct nvlst *lst) static rsRetVal -OnFileOpen(void *pT, uchar *fn, gtfile *pgf) +OnFileOpen(void *pT, uchar *fn, void *pGF) { lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + gtfile *pgf = (gtfile*) pGF; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index 0ccfcd7b..d9cce2f7 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -101,9 +101,9 @@ showSigblkParams(char *name) ++blkCnt; rsgt_printBLOCK_SIG(stdout, bs, verbose); printf("\t***META INFORMATION:\n"); - printf("\tBlock Nbr in File......: %llu\n", blkCnt); - printf("\tHas Record Hashes......: %d\n", bHasRecHashes); - printf("\tHas Intermediate Hashes: %d\n", bHasIntermedHashes); + printf("\tBlock Nbr in File...: %llu\n", blkCnt); + printf("\tHas Record Hashes...: %d\n", bHasRecHashes); + printf("\tHas Tree Hashes.....: %d\n", bHasIntermedHashes); } if(fp != stdin) @@ -145,7 +145,7 @@ err: fprintf(stderr, "error %d processing file %s\n", r, name); } static inline int -doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf) +doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf, gterrctx_t *ectx, uint8_t bInBlock) { int r; size_t lenRec; @@ -156,10 +156,19 @@ doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf) goto done; } lenRec = strlen(rec); - if(rec[lenRec-1] == '\n') + if(rec[lenRec-1] == '\n') { + rec[lenRec-1] = '\0'; --lenRec; + rsgt_errctxSetErrRec(ectx, rec); + } + + /* we need to preserve the first record of each block for + * error-reporting purposes (bInBlock==0 meanst start of block) + */ + if(bInBlock == 0) + rsgt_errctxFrstRecInBlk(ectx, rec); - r = rsgt_vrfy_nextRec(bs, gf, sigfp, (unsigned char*)rec, lenRec); + r = rsgt_vrfy_nextRec(bs, gf, sigfp, (unsigned char*)rec, lenRec, ectx); done: return r; } @@ -174,8 +183,8 @@ verify(char *name) uint8_t bHasRecHashes, bHasIntermedHashes; uint8_t bInBlock; int r = 0; - uint64_t nRecs; char sigfname[4096]; + gterrctx_t ectx; if(!strcmp(name, "-")) { fprintf(stderr, "verify mode cannot work on stdin\n"); @@ -194,6 +203,10 @@ verify(char *name) } rsgtInit("rsyslog rsgtutil " VERSION); + rsgt_errctxInit(&ectx); + ectx.verbose = verbose; + ectx.fp = stderr; + ectx.filename = strdup(sigfname); if((r = rsgt_chkFileHdr(sigfp, "LOGSIG10")) != 0) goto err; @@ -204,28 +217,31 @@ verify(char *name) } bInBlock = 0; + ectx.blkNum = 0; + ectx.recNumInFile = 0; while(!feof(logfp)) { if(bInBlock == 0) { if((r = rsgt_getBlockParams(sigfp, 1, &bs, &bHasRecHashes, &bHasIntermedHashes)) != 0) goto err; - rsgt_vrfyBlkInit(gf, bs, bHasRecHashes, - bHasIntermedHashes); - nRecs = 0; - bInBlock = 1; + rsgt_vrfyBlkInit(gf, bs, bHasRecHashes, bHasIntermedHashes); + ectx.recNum = 0; + ++ectx.blkNum; } - ++nRecs; - if((r = doVerifyRec(logfp, sigfp, bs, gf)) != 0) + ++ectx.recNum, ++ectx.recNumInFile; + if((r = doVerifyRec(logfp, sigfp, bs, gf, &ectx, bInBlock)) != 0) goto err; - if(nRecs == bs->recCount) { - verifyBLOCK_SIG(bs, gf, sigfp, nRecs); + if(ectx.recNum == bs->recCount) { + verifyBLOCK_SIG(bs, gf, sigfp, &ectx); bInBlock = 0; - } + } else bInBlock = 1; } fclose(logfp); fclose(sigfp); + rsgtExit(); + rsgt_errctxExit(&ectx); return; err: if(logfp != NULL) @@ -233,8 +249,9 @@ err: if(sigfp != NULL) fclose(sigfp); if(r != RSGTE_EOF) - fprintf(stderr, "error %d processing file %s [%s]\n", r, name, sigfname); + fprintf(stderr, "error %d processing file %s\n", r, name); rsgtExit(); + rsgt_errctxExit(&ectx); } static void @@ -265,6 +282,8 @@ static struct option long_options[] = {"detect-file-type", no_argument, NULL, 'T'}, {"show-sigblock-params", no_argument, NULL, 'B'}, {"verify", no_argument, NULL, 't'}, /* 't' as in "test signatures" */ + {"publications-server", optional_argument, NULL, 'P'}, + {"show-verified", no_argument, NULL, 's'}, {NULL, 0, NULL, 0} }; @@ -275,13 +294,16 @@ main(int argc, char *argv[]) int opt; while(1) { - opt = getopt_long(argc, argv, "v", long_options, NULL); + opt = getopt_long(argc, argv, "DvVTBtPs", long_options, NULL); if(opt == -1) break; switch(opt) { case 'v': verbose = 1; break; + case 's': + rsgt_read_showVerified = 1; + break; case 'V': fprintf(stderr, "rsgtutil " VERSION "\n"); exit(0); @@ -291,6 +313,9 @@ main(int argc, char *argv[]) case 'B': mode = MD_SHOW_SIGBLK_PARAMS; break; + case 'P': + rsgt_read_puburl = optarg; + break; case 'T': mode = MD_DETECT_FILE_TYPE; break; -- cgit v1.2.3 From aef0be0c1799fbb20955fc1dc014cb9c9772af88 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 19 Mar 2013 16:20:25 +0100 Subject: bugfix: solve compile problems on non-Linux platforms Thanks to Michael Biebl for alerting us on this issue. --- configure.ac | 1 + runtime/debug.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7a4af19d..68769808 100644 --- a/configure.ac +++ b/configure.ac @@ -122,6 +122,7 @@ AC_CHECK_DECL([SCM_CREDENTIALS], [AC_DEFINE(HAVE_SCM_CREDENTIALS, [1], [set defi #include ]) AC_CHECK_DECL([SO_TIMESTAMP], [AC_DEFINE(HAVE_SO_TIMESTAMP, [1], [set define])], [], [#include #include ]) +AC_CHECK_DECL([SYS_gettid], [AC_DEFINE(HAVE_SYS_gettid, [1], [set define])], [], [#include ]) AC_CHECK_MEMBER([struct sysinfo.uptime], [AC_DEFINE(HAVE_SYSINFO_UPTIME, [1], [set define])], [], [#include ]) # Check for MAXHOSTNAMELEN diff --git a/runtime/debug.c b/runtime/debug.c index 3cb22232..876f61d0 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -303,7 +303,7 @@ static inline void dbgFuncDBRemoveMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_ void dbgOutputTID(char* name) { -# ifdef HAVE_SYSCALL +# if defined(HAVE_SYSCALL) && defined(HAVE_SYS_gettid) if(bOutputTidToStderr) fprintf(stderr, "thread tid %u, name '%s'\n", (unsigned)syscall(SYS_gettid), name); -- cgit v1.2.3 From 83c1920d7a6e675f1f433b047e8ed4e50e1050af Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 19 Mar 2013 16:55:25 +0100 Subject: require newer libestr due to a bug in it --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ac6fd43d..86aaa20d 100644 --- a/configure.ac +++ b/configure.ac @@ -32,7 +32,7 @@ AC_CANONICAL_HOST PKG_PROG_PKG_CONFIG # modules we require -PKG_CHECK_MODULES(LIBESTR, libestr >= 0.1.2) +PKG_CHECK_MODULES(LIBESTR, libestr >= 0.1.5) PKG_CHECK_MODULES(LIBEE, libee >= 0.4.0) PKG_CHECK_MODULES([JSON_C], [json]) -- cgit v1.2.3 From 25b53e72195272e4fbc7a4494b816a34138d5718 Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Wed, 20 Mar 2013 09:27:04 +0100 Subject: bugfix: RainerScript getenv() function caused segfault when var was not found Signed-off-by: Rainer Gerhards --- ChangeLog | 3 +++ grammar/rainerscript.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 9acbbf27..b0e943c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,9 @@ Version 7.2.7 [v7-stable] 2013-03-?? - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. +- bugfix: RainerScript getenv() function caused segfault when var was + not found. + Thanks to Philippe Muller for the patch. - bugfix: several issues in imkmsg see bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=421#c8 - bugfix: imuxsock was missing SysSock.ParseTrusted module parameter diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 20b86c5c..b04e53b5 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -1071,8 +1071,12 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr) estr = var2String(&r[0], &bMustFree); str = (char*) es_str2cstr(estr, NULL); envvar = getenv(str); + if(envvar == NULL) { + ret->d.estr = es_newStr(0); + } else { + ret->d.estr = es_newStrFromCStr(envvar, strlen(envvar)); + } ret->datatype = 'S'; - ret->d.estr = es_newStrFromCStr(envvar, strlen(envvar)); if(bMustFree) es_deleteStr(estr); if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr); free(str); -- cgit v1.2.3 From 619c70d31f33deb8538636ac1302a2d4032a844e Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Wed, 20 Mar 2013 09:27:04 +0100 Subject: bugfix: RainerScript getenv() function caused segfault when var was not found This patch is released under ASL 2.0 as of email conversation from 2013-03-20. Signed-off-by: Rainer Gerhards --- ChangeLog | 3 +++ grammar/rainerscript.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 9acbbf27..b0e943c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,9 @@ Version 7.2.7 [v7-stable] 2013-03-?? - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. +- bugfix: RainerScript getenv() function caused segfault when var was + not found. + Thanks to Philippe Muller for the patch. - bugfix: several issues in imkmsg see bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=421#c8 - bugfix: imuxsock was missing SysSock.ParseTrusted module parameter diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 20b86c5c..b04e53b5 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -1071,8 +1071,12 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr) estr = var2String(&r[0], &bMustFree); str = (char*) es_str2cstr(estr, NULL); envvar = getenv(str); + if(envvar == NULL) { + ret->d.estr = es_newStr(0); + } else { + ret->d.estr = es_newStrFromCStr(envvar, strlen(envvar)); + } ret->datatype = 'S'; - ret->d.estr = es_newStrFromCStr(envvar, strlen(envvar)); if(bMustFree) es_deleteStr(estr); if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr); free(str); -- cgit v1.2.3 From 8059afa9998906ba11d3509beff7af6f7efd9af1 Mon Sep 17 00:00:00 2001 From: Tomas Heinrich Date: Mon, 18 Mar 2013 16:28:47 +0100 Subject: bugfix: get rid of potential endless loop in doGetGID() --- runtime/cfsysline.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c index ad087652..bc5ffa6f 100644 --- a/runtime/cfsysline.c +++ b/runtime/cfsysline.c @@ -349,8 +349,9 @@ static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p struct group gBuf; DEFiRet; uchar szName[256]; - int bufSize = 2048; + int bufSize = 1024; char * stringBuf = NULL; + int err; assert(pp != NULL); assert(*pp != NULL); @@ -360,20 +361,21 @@ static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p ABORT_FINALIZE(RS_RET_NOT_FOUND); } - - CHKmalloc(stringBuf = malloc(bufSize)); - while(pgBuf == NULL) { - errno = 0; - getgrnam_r((char*)szName, &gBuf, stringBuf, bufSize, &pgBuf); - if((pgBuf == NULL) && (errno == ERANGE)) { - /* Increase bufsize and try again.*/ - bufSize *= 2; - CHKmalloc(stringBuf = realloc(stringBuf, bufSize)); - } - } + do { + /* Increase bufsize and try again.*/ + bufSize *= 2; + CHKmalloc(stringBuf = realloc(stringBuf, bufSize)); + err = getgrnam_r((char*)szName, &gBuf, stringBuf, bufSize, &pgBuf); + } while((pgBuf == NULL) && (err == ERANGE)); if(pgBuf == NULL) { - errmsg.LogError(0, RS_RET_NOT_FOUND, "ID for group '%s' could not be found or error", (char*)szName); + if (err != 0) { + rs_strerror_r(err, stringBuf, bufSize); + errmsg.LogError(0, RS_RET_NOT_FOUND, "Query for group '%s' resulted in an error: %s\n", + (char*)szName, stringBuf); + } else { + errmsg.LogError(0, RS_RET_NOT_FOUND, "ID for group '%s' could not be found", (char*)szName); + } iRet = RS_RET_NOT_FOUND; } else { if(pSetHdlr == NULL) { -- cgit v1.2.3 From 2e4e7e819fed64fa2897af01479a9083b081d0c2 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 09:46:59 +0100 Subject: doc: mention merged bugfix in ChangeLog --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index b0e943c2..8286b0a0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,9 @@ Version 7.2.7 [v7-stable] 2013-03-?? - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. +- bugfix: using group resolution could lead to endless loop + Thanks to Tomas Heinrich for the patch. + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=310 - bugfix: RainerScript getenv() function caused segfault when var was not found. Thanks to Philippe Muller for the patch. -- cgit v1.2.3 From 7784b65d8709fba647a7beeb934c18b7cb8cb6f1 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 10:24:36 +0100 Subject: bugfix: build on non-linux platforms failed The previous fix for this was incomplete. --- runtime/wtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/wtp.c b/runtime/wtp.c index f8d3588b..19151e7c 100644 --- a/runtime/wtp.c +++ b/runtime/wtp.c @@ -381,9 +381,9 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in if(prctl(PR_SET_NAME, thrdName, 0, 0, 0) != 0) { DBGPRINTF("prctl failed, not setting thread name for '%s'\n", wtpGetDbgHdr(pThis)); } + dbgOutputTID((char*)thrdName); # endif - dbgOutputTID((char*)thrdName); pthread_cleanup_push(wtpWrkrExecCancelCleanup, pWti); wtiWorker(pWti); pthread_cleanup_pop(0); -- cgit v1.2.3 From 5ebb7d03cf88c86390450b1dd63c9467e9a4f3c7 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 10:25:26 +0100 Subject: add recent bugfix to ChangeLog --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 07cf7844..adc12076 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,7 @@ --------------------------------------------------------------------------- +Version 7.3.9 [devel] 2013-03-?? +- bugfix: build problems on non-Linux platforms +--------------------------------------------------------------------------- Version 7.3.8 [devel] 2013-03-18 - imrelp: now supports listening to IPv4/v6 only instead of always both build now requires librelp 1.0.2 -- cgit v1.2.3 From 5b76d639659fe5582b425b903fa8f5e14624a8e4 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 11:03:41 +0100 Subject: bugfix: $mmnormalizeuseramsg paramter was specified with wrong type Thanks to Renzhong Zhang for alerting us of the problem. closes: http://bugzilla.adiscon.com/show_bug.cgi?id=420 --- ChangeLog | 3 +++ plugins/mmnormalize/mmnormalize.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index b0e943c2..2df963a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,9 @@ Version 7.2.7 [v7-stable] 2013-03-?? - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. +- bugfix: $mmnormalizeuseramsg paramter was specified with wrong type + Thank to Renzhong Zhang for alerting us of the problem. + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=420 - bugfix: RainerScript getenv() function caused segfault when var was not found. Thanks to Philippe Muller for the patch. diff --git a/plugins/mmnormalize/mmnormalize.c b/plugins/mmnormalize/mmnormalize.c index d3fba39b..c366d5fe 100644 --- a/plugins/mmnormalize/mmnormalize.c +++ b/plugins/mmnormalize/mmnormalize.c @@ -281,7 +281,7 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizerulebase", 0, eCmdHdlrGetWord, setRuleBase, NULL, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizeuserawmsg", 0, eCmdHdlrInt, + CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizeuserawmsg", 0, eCmdHdlrBinary, NULL, &cs.bUseRawMsg, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); -- cgit v1.2.3 From 2b69e2aaddb587185b7c0ddb412b36549d848091 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 12:47:53 +0100 Subject: omstdout: added debug message when writing to stdout fails --- plugins/omstdout/omstdout.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/omstdout/omstdout.c b/plugins/omstdout/omstdout.c index 59f9c8bb..a84a7593 100644 --- a/plugins/omstdout/omstdout.c +++ b/plugins/omstdout/omstdout.c @@ -105,6 +105,7 @@ BEGINdoAction int iBuf; char szBuf[65564]; size_t len; + int r; CODESTARTdoAction if(pData->bUseArrayInterface) { /* if we use array passing, we need to put together a string @@ -140,9 +141,15 @@ CODESTARTdoAction * actually intends to use this module in production (why???), this code * needs to be more solid. -- rgerhards, 2012-11-28 */ - if(write(1, toWrite, len)) {}; /* 1 is stdout! */ + if((r = write(1, toWrite, len)) != (int) len) { /* 1 is stdout! */ + DBGPRINTF("omstdout: error %d writing to stdout[%d]: %s\n", + r, len, toWrite); + } if(pData->bEnsureLFEnding && toWrite[len-1] != '\n') { - if(write(1, "\n", 1)) {}; /* write missing LF */ + if((r = write(1, "\n", 1)) != 1) { /* write missing LF */ + DBGPRINTF("omstdout: error %d writing \\n to stdout\n", + r); + } } ENDdoAction -- cgit v1.2.3 From cbe737ed06667b5bcb15f332061ebcde8d50bc8b Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 12:49:35 +0100 Subject: bugfix: stdout/stderr were not closed on forking but were closed when running in the forground - this was just reversed of what it should be. This is a regression of a recent change. --- ChangeLog | 3 +++ tools/syslogd.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 705dcf13..c6a4eb41 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,9 @@ --------------------------------------------------------------------------- Version 7.3.9 [devel] 2013-03-?? - bugfix: build problems on non-Linux platforms +- bugfix: stdout/stderr were not closed on forking + but were closed when running in the forground - this was just reversed + of what it should be. This is a regression of a recent change. --------------------------------------------------------------------------- Version 7.3.8 [devel] 2013-03-18 - imrelp: now supports listening to IPv4/v6 only instead of always both diff --git a/tools/syslogd.c b/tools/syslogd.c index d4fc1c36..e291ba47 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -2021,7 +2021,7 @@ int realMain(int argc, char **argv) * is still in its infancy (and not really done), we currently accept this issue. * rgerhards, 2009-06-29 */ - if(!doFork) { + if(doFork) { close(1); close(2); ourConf->globals.bErrMsgToStderr = 0; -- cgit v1.2.3 From a8e83e494720c3657a74cacb367241f8991d6d73 Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Wed, 20 Mar 2013 13:01:37 +0100 Subject: bugfix: imuxsock rate-limiting could not be configured via legacy conf Signed-off-by: Rainer Gerhards --- ChangeLog | 6 ++++++ plugins/imuxsock/imuxsock.c | 3 +++ 2 files changed, 9 insertions(+) diff --git a/ChangeLog b/ChangeLog index c517cb89..1f6c4bf3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,12 @@ Version 7.2.7 [v7-stable] 2013-03-?? - improved debugging support in forked (auto-backgrounding) mode The rsyslog debug log file is now continued to be written across the fork. +- bugfix: imuxsock rate-limiting could not be configured via legacy conf + Rate-limiting for the system socket could not be configured via legacy + configuration directives. However, the new-style RainerScript config + options worked. + Thanks to Milan Bartos for the patch. + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=390 - bugfix: using group resolution could lead to endless loop Thanks to Tomas Heinrich for the patch. closes: http://bugzilla.adiscon.com/show_bug.cgi?id=310 diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 637cb133..1a2b696e 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -1300,6 +1300,9 @@ CODESTARTendCnfLoad loadModConf->bUseFlowCtl = cs.bUseFlowCtlSysSock; loadModConf->bAnnotateSysSock = cs.bAnnotateSysSock; loadModConf->bParseTrusted = cs.bParseTrusted; + loadModConf->ratelimitIntervalSysSock = cs.ratelimitIntervalSysSock; + loadModConf->ratelimitBurstSysSock = cs.ratelimitBurstSysSock; + loadModConf->ratelimitSeveritySysSock = cs.ratelimitSeveritySysSock; } loadModConf = NULL; /* done loading */ -- cgit v1.2.3 From 144f0858b7250ceffe4828712126146345e0e92a Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 14:21:32 +0100 Subject: cleanup --- tools/pmrfc3164.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pmrfc3164.c b/tools/pmrfc3164.c index bcded428..5dfa74f0 100644 --- a/tools/pmrfc3164.c +++ b/tools/pmrfc3164.c @@ -138,7 +138,7 @@ CODESTARTparse */ if(lenMsg > 0 && pMsg->msgFlags & PARSE_HOSTNAME) { i = 0; - while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '.' + while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '_' || p2parse[i] == '-') && i < (CONF_HOSTNAME_MAXSIZE - 1)) { bufParseHOSTNAME[i] = p2parse[i]; ++i; -- cgit v1.2.3 From 136a9828452486da97578fc5a2e870143279dceb Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Mar 2013 15:36:57 +0100 Subject: slightly improve imfile doc --- doc/imfile.html | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/imfile.html b/doc/imfile.html index 0997e382..de4893ac 100644 --- a/doc/imfile.html +++ b/doc/imfile.html @@ -141,17 +141,17 @@ your distro puts rsyslog's config files). Note that only commands actually needed need to be specified. The second file uses less commands and uses defaults instead.

    - @@ -210,8 +210,7 @@ your distro puts rsyslog's config files). Note that only commands actually needed need to be specified. The second file uses less commands and uses defaults instead.

    - +

    In the next example, we set up three listners at ports 10514, 10515 and 10516 +and assign a listner name of "udp" to it, followed by the port number: +

    + + +

    The next example is almost equal to the previous one, but +now the inputname property will just be set to the port number. +So if a message was received on port 10515, the input name will be +"10515" in this example whereas it was "udp10515" in the previous one. +Note that to do that we set the inputname to the empty string. +

    + + +

    Legacy Configuration Directives:

    Multiple receivers may be configured by specifying $UDPServerRun multiple times. @@ -88,23 +129,20 @@ equivalent to: SchedulingPolicy

  • $IMUDPSchedulingPriority <number> Available since 4.7.4+, 5.7.3+, 6.1.3+.
    equivalent to: SchedulingPriority -Caveats/Known Bugs: -
      -
    • currently none known
    • -

    Sample:

    This sets up an UPD server on port 514:

    - +

    [rsyslog.conf overview] [manual index] [rsyslog site]

    This documentation is part of the rsyslog project.
    -Copyright © 2009 by Rainer -Gerhards and +Copyright © 2009-2013 by +Rainer Gerhards and Adiscon. Released under the GNU GPL version 3 or higher.

    diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c index 7e11a80e..7bf1473a 100644 --- a/plugins/imudp/imudp.c +++ b/plugins/imudp/imudp.c @@ -75,6 +75,7 @@ static struct lstn_s { struct lstn_s *next; int sock; /* socket */ ruleset_t *pRuleset; /* bound ruleset */ + prop_t *pInputName; statsobj_t *stats; /* listener stats */ ratelimit_t *ratelimiter; STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit) @@ -91,7 +92,6 @@ static uchar *pRcvBuf = NULL; /* receive buffer (for a single packet). We use a * it so that we can check available memory in willRun() and request * termination if we can not get it. -- rgerhards, 2007-12-27 */ -static prop_t *pInputName = NULL; /* our inputName currently is always "imudp", and this will hold it */ #define TIME_REQUERY_DFLT 2 #define SCHED_PRIO_UNSET -12345678 /* a value that indicates that the scheduling priority has not been set */ @@ -108,10 +108,12 @@ struct instanceConf_s { uchar *pszBindAddr; /* IP to bind socket to */ uchar *pszBindPort; /* Port to bind socket to */ uchar *pszBindRuleset; /* name of ruleset to bind to */ + uchar *inputname; ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */ int ratelimitInterval; int ratelimitBurst; struct instanceConf_s *next; + sbool bAppendPortToInpname; }; struct modConfData_s { @@ -141,6 +143,8 @@ static struct cnfparamblk modpblk = /* input instance parameters */ static struct cnfparamdescr inppdescr[] = { { "port", eCmdHdlrArray, CNFPARAM_REQUIRED }, /* legacy: InputTCPServerRun */ + { "inputname", eCmdHdlrGetWord, 0 }, + { "inputname.appendport", eCmdHdlrBinary, 0 }, { "address", eCmdHdlrString, 0 }, { "ruleset", eCmdHdlrString, 0 }, { "ratelimit.interval", eCmdHdlrInt, 0 }, @@ -169,6 +173,8 @@ createInstance(instanceConf_t **pinst) inst->pszBindPort = NULL; inst->pszBindAddr = NULL; inst->pszBindRuleset = NULL; + inst->inputname = NULL; + inst->bAppendPortToInpname = 0; inst->ratelimitBurst = 10000; /* arbitrary high limit */ inst->ratelimitInterval = 0; /* off */ @@ -229,7 +235,8 @@ addListner(instanceConf_t *inst) struct lstn_s *newlcnfinfo; uchar *bindName; uchar *port; - uchar dispname[64]; + uchar dispname[64], inpnameBuf[128]; + uchar *inputname; /* check which address to bind to. We could do this more compact, but have not * done so in order to make the code more readable. -- rgerhards, 2007-12-27 @@ -257,6 +264,21 @@ addListner(instanceConf_t *inst) snprintf((char*)dispname, sizeof(dispname), "imudp(%s:%s)", bindName, port); dispname[sizeof(dispname)-1] = '\0'; /* just to be on the save side... */ CHKiRet(ratelimitNew(&newlcnfinfo->ratelimiter, (char*)dispname, NULL)); + if(inst->inputname == NULL) { + inputname = (uchar*)"imudp"; + } else { + inputname = inst->inputname; + } + if(inst->bAppendPortToInpname) { + snprintf((char*)inpnameBuf, sizeof(inpnameBuf), "%s%s", + inputname, port); + inpnameBuf[sizeof(inpnameBuf)-1] = '\0'; + inputname = inpnameBuf; + } + CHKiRet(prop.Construct(&newlcnfinfo->pInputName)); + CHKiRet(prop.SetString(newlcnfinfo->pInputName, + inputname, ustrlen(inputname))); + CHKiRet(prop.ConstructFinalize(newlcnfinfo->pInputName)); ratelimitSetLinuxLike(newlcnfinfo->ratelimiter, inst->ratelimitInterval, inst->ratelimitBurst); /* support statistics gathering */ @@ -390,7 +412,7 @@ processSocket(thrdInfo_t *pThrd, struct lstn_s *lstn, struct sockaddr_storage *f /* we now create our own message object and submit it to the queue */ CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime)); MsgSetRawMsg(pMsg, (char*)pRcvBuf, lenRcvBuf); - MsgSetInputName(pMsg, pInputName); + MsgSetInputName(pMsg, lstn->pInputName); MsgSetRuleset(pMsg, lstn->pRuleset); MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY); pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME | NEEDS_DNSRESOL; @@ -695,6 +717,10 @@ createListner(es_str_t *port, struct cnfparamvals *pvals) continue; if(!strcmp(inppblk.descr[i].name, "port")) { continue; /* array, handled by caller */ + } else if(!strcmp(inppblk.descr[i].name, "inputname")) { + inst->inputname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(inppblk.descr[i].name, "inputname.appendport")) { + inst->bAppendPortToInpname = (int) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "address")) { inst->pszBindAddr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "ruleset")) { @@ -873,6 +899,7 @@ CODESTARTfreeCnf free(inst->pszBindPort); free(inst->pszBindAddr); free(inst->pBindRuleset); + free(inst->inputname); del = inst; inst = inst->next; free(del); @@ -915,6 +942,7 @@ CODESTARTafterRun statsobj.Destruct(&(lstn->stats)); ratelimitDestruct(lstn->ratelimiter); close(lstn->sock); + prop.Destruct(&lstn->pInputName); lstnDel = lstn; lstn = lstn->next; free(lstnDel); @@ -929,9 +957,6 @@ ENDafterRun BEGINmodExit CODESTARTmodExit - if(pInputName != NULL) - prop.Destruct(&pInputName); - /* release what we no longer need */ objRelease(errmsg, CORE_COMPONENT); objRelease(glbl, CORE_COMPONENT); @@ -986,11 +1011,6 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(ruleset, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); - /* we need to create the inputName property (only once during our lifetime) */ - CHKiRet(prop.Construct(&pInputName)); - CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imudp"), sizeof("imudp") - 1)); - CHKiRet(prop.ConstructFinalize(pInputName)); - /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputudpserverbindruleset", 0, eCmdHdlrGetWord, NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 8f969512..c05cc773 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -179,9 +179,8 @@ endif # if ENABLE_GUARDTIME librsgt_la_SOURCES = librsgt.c librsgt_read.c librsgt.h - pkglib_LTLIBRARIES += lmsig_gt.la - lmsig_gt_la_SOURCES = lmsig_gt.c + lmsig_gt_la_SOURCES = lmsig_gt.c lmsig_gt.h lmsig_gt_la_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) lmsig_gt_la_LDFLAGS = -module -avoid-version lmsig_gt_la_LIBADD = librsgt.la $(GUARDTIME_LIBS) -- cgit v1.2.3 From 7e0ab0a1fb8cbbf83ef6e8613b2051b7a0f49803 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 26 Mar 2013 15:33:54 +0100 Subject: logsig: handle error cases --- runtime/librsgt.c | 359 ++++++++++++++++++++++++++++++++++++----------------- runtime/librsgt.h | 23 ++-- runtime/lmsig_gt.c | 21 +++- tools/rsgtutil.c | 4 +- 4 files changed, 281 insertions(+), 126 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index adcc2a8a..50b94b79 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -60,24 +60,50 @@ typedef unsigned char uchar; #endif +static void +reportErr(gtctx ctx, char *errmsg) +{ + if(ctx->errFunc == NULL) + goto done; + ctx->errFunc(ctx->usrptr, (uchar*)errmsg); +done: return; +} + +static void +reportGTAPIErr(gtctx ctx, gtfile gf, char *apiname, int ecode) +{ + char errbuf[4096]; + snprintf(errbuf, sizeof(errbuf), "%s[%s:%d]: %s", + (gf == NULL) ? (uchar*)"" : gf->sigfilename, + apiname, ecode, GT_getErrorString(ecode)); + errbuf[sizeof(errbuf)-1] = '\0'; + reportErr(ctx, errbuf); +} + void +rsgtsetErrFunc(gtctx ctx, void (*func)(void*, uchar *), void *usrptr) +{ + ctx->usrptr = usrptr; + ctx->errFunc = func; +} + +int rsgtInit(char *usragent) { + int r = 0; int ret = GT_OK; ret = GT_init(); if(ret != GT_OK) { - fprintf(stderr, "GT_init() failed: %d (%s)\n", - ret, GT_getErrorString(ret)); + r = 1; goto done; } ret = GTHTTP_init(usragent, 1); if(ret != GT_OK) { - fprintf(stderr, "GTHTTP_init() failed: %d (%s)\n", - ret, GTHTTP_getErrorString(ret)); + r = 1; goto done; } -done: return; +done: return r; } void @@ -103,13 +129,14 @@ rsgtfileConstruct(gtctx ctx) done: return gf; } -static inline void +static inline int tlvbufPhysWrite(gtfile gf) { ssize_t lenBuf; ssize_t iTotalWritten; ssize_t iWritten; char *pWriteBuf; + int r = 0; lenBuf = gf->tlvIdx; pWriteBuf = gf->tlvBuf; @@ -117,17 +144,13 @@ tlvbufPhysWrite(gtfile gf) do { iWritten = write(gf->fd, pWriteBuf, lenBuf); if(iWritten < 0) { - //char errStr[1024]; - int err = errno; iWritten = 0; /* we have written NO bytes! */ - /* rs_strerror_r(err, errStr, sizeof(errStr)); - DBGPRINTF("log file (%d) write error %d: %s\n", pThis->fd, err, errStr); - */ - if(err == EINTR) { + if(errno == EINTR) { /*NO ERROR, just continue */; } else { - goto finalize_it; //ABORT_FINALIZE(RS_RET_IO_ERROR); - /* FIXME: flag error */ + reportErr(gf->ctx, "signature file write error"); + r = RSGTE_IO; + goto finalize_it; } } /* advance buffer to next write position */ @@ -138,32 +161,40 @@ tlvbufPhysWrite(gtfile gf) finalize_it: gf->tlvIdx = 0; + return r; } -static inline void +static inline int tlvbufChkWrite(gtfile gf) { if(gf->tlvIdx == sizeof(gf->tlvBuf)) { - tlvbufPhysWrite(gf); + return tlvbufPhysWrite(gf); } + return 0; } /* write to TLV file buffer. If buffer is full, an actual call occurs. Else * output is written only on flush or close. */ -static inline void +static inline int tlvbufAddOctet(gtfile gf, int8_t octet) { - tlvbufChkWrite(gf); + int r; + r = tlvbufChkWrite(gf); + if(r != 0) goto done; gf->tlvBuf[gf->tlvIdx++] = octet; +done: return r; } -static inline void +static inline int tlvbufAddOctetString(gtfile gf, uint8_t *octet, int size) { - int i; - for(i = 0 ; i < size ; ++i) - tlvbufAddOctet(gf, octet[i]); + int i, r = 0; + for(i = 0 ; i < size ; ++i) { + r = tlvbufAddOctet(gf, octet[i]); + if(r != 0) goto done; + } +done: return r; } /* return the actual length in to-be-written octets of an integer */ static inline uint8_t @@ -185,69 +216,96 @@ tlvbufGetInt64OctetSize(uint64_t val) return 2; return 1; } -static inline void +static inline int tlvbufAddInt64(gtfile gf, uint64_t val) { uint8_t doWrite = 0; - if(val >> 56) - tlvbufAddOctet(gf, (val >> 56) & 0xff), doWrite = 1; - if(doWrite || ((val >> 48) & 0xff)) - tlvbufAddOctet(gf, (val >> 48) & 0xff), doWrite = 1; - if(doWrite || ((val >> 40) & 0xff)) - tlvbufAddOctet(gf, (val >> 40) & 0xff), doWrite = 1; - if(doWrite || ((val >> 32) & 0xff)) - tlvbufAddOctet(gf, (val >> 32) & 0xff), doWrite = 1; - if(doWrite || ((val >> 24) & 0xff)) - tlvbufAddOctet(gf, (val >> 24) & 0xff), doWrite = 1; - if(doWrite || ((val >> 16) & 0xff)) - tlvbufAddOctet(gf, (val >> 16) & 0xff), doWrite = 1; - if(doWrite || ((val >> 8) & 0xff)) - tlvbufAddOctet(gf, (val >> 8) & 0xff), doWrite = 1; - tlvbufAddOctet(gf, val & 0xff); + int r; + if(val >> 56) { + r = tlvbufAddOctet(gf, (val >> 56) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 48) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 48) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 40) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 40) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 32) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 32) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 24) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 24) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 16) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 16) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 8) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 8) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + r = tlvbufAddOctet(gf, val & 0xff); +done: return r; } -void +int tlv8Write(gtfile gf, int flags, int tlvtype, int len) { - tlvbufAddOctet(gf, (flags << 5)|tlvtype); - tlvbufAddOctet(gf, len & 0xff); + int r; + r = tlvbufAddOctet(gf, (flags << 5)|tlvtype); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, len & 0xff); +done: return r; } -void +int tlv16Write(gtfile gf, int flags, int tlvtype, uint16_t len) { uint16_t typ; + int r; typ = ((flags|1) << 13)|tlvtype; - tlvbufAddOctet(gf, typ >> 8); - tlvbufAddOctet(gf, typ & 0xff); - tlvbufAddOctet(gf, (len >> 8) & 0xff); - tlvbufAddOctet(gf, len & 0xff); + r = tlvbufAddOctet(gf, typ >> 8); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, typ & 0xff); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, (len >> 8) & 0xff); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, len & 0xff); +done: return r; } -void +int tlvFlush(gtfile gf) { - if(gf->tlvIdx != 0) - tlvbufPhysWrite(gf); + return (gf->tlvIdx == 0) ? 0 : tlvbufPhysWrite(gf); } -void -tlvWriteHash(gtfile gf, uint16_t tlvtype, GTDataHash *r) +int +tlvWriteHash(gtfile gf, uint16_t tlvtype, GTDataHash *rec) { unsigned tlvlen; - - tlvlen = 1 + r->digest_length; - tlv16Write(gf, 0x00, tlvtype, tlvlen); - tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); - tlvbufAddOctetString(gf, r->digest, r->digest_length); + int r; + tlvlen = 1 + rec->digest_length; + r = tlv16Write(gf, 0x00, tlvtype, tlvlen); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, rec->digest, rec->digest_length); +done: return r; } -void +int tlvWriteBlockSig(gtfile gf, uchar *der, uint16_t lenDer) { unsigned tlvlen; uint8_t tlvlenRecords; + int r; tlvlenRecords = tlvbufGetInt64OctetSize(gf->nRecords); tlvlen = 2 + 1 /* hash algo TLV */ + @@ -256,25 +314,37 @@ tlvWriteBlockSig(gtfile gf, uchar *der, uint16_t lenDer) 2 + tlvlenRecords /* rec-count */ + 4 + lenDer /* rfc-3161 */; /* write top-level TLV object (block-sig */ - tlv16Write(gf, 0x00, 0x0902, tlvlen); + r = tlv16Write(gf, 0x00, 0x0902, tlvlen); + if(r != 0) goto done; /* and now write the children */ //FIXME: flags??? /* hash-algo */ - tlv8Write(gf, 0x00, 0x00, 1); - tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + r = tlv8Write(gf, 0x00, 0x00, 1); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + if(r != 0) goto done; /* block-iv */ - tlv8Write(gf, 0x00, 0x01, hashOutputLengthOctets(gf->hashAlg)); - tlvbufAddOctetString(gf, gf->IV, hashOutputLengthOctets(gf->hashAlg)); + r = tlv8Write(gf, 0x00, 0x01, hashOutputLengthOctets(gf->hashAlg)); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, gf->IV, hashOutputLengthOctets(gf->hashAlg)); + if(r != 0) goto done; /* last-hash */ - tlv8Write(gf, 0x00, 0x02, gf->lenBlkStrtHash+1); - tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); - tlvbufAddOctetString(gf, gf->blkStrtHash, gf->lenBlkStrtHash); + r = tlv8Write(gf, 0x00, 0x02, gf->lenBlkStrtHash+1); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, gf->blkStrtHash, gf->lenBlkStrtHash); + if(r != 0) goto done; /* rec-count */ - tlv8Write(gf, 0x00, 0x03, tlvlenRecords); - tlvbufAddInt64(gf, gf->nRecords); + r = tlv8Write(gf, 0x00, 0x03, tlvlenRecords); + if(r != 0) goto done; + r = tlvbufAddInt64(gf, gf->nRecords); + if(r != 0) goto done; /* rfc-3161 */ - tlv16Write(gf, 0x00, 0x906, lenDer); - tlvbufAddOctetString(gf, der, lenDer); + r = tlv16Write(gf, 0x00, 0x906, lenDer); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, der, lenDer); +done: return r; } /* support for old platforms - graceful degrade */ @@ -339,36 +409,46 @@ done: return; } -void tlvClose(gtfile gf) +int +tlvClose(gtfile gf) { - tlvFlush(gf); + int r; + r = tlvFlush(gf); close(gf->fd); gf->fd = -1; writeStateFile(gf); + return r; } /* note: if file exists, the last hash for chaining must * be read from file. */ -void tlvOpen(gtfile gf, char *hdr, unsigned lenHdr) +int +tlvOpen(gtfile gf, char *hdr, unsigned lenHdr) { + int r = 0; gf->fd = open((char*)gf->sigfilename, O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC, 0600); if(gf->fd == -1) { /* looks like we need to create a new file */ gf->fd = open((char*)gf->sigfilename, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); - // FIXME: check fd == -1 + if(gf->fd == -1) { + r = RSGTE_IO; + goto done; + } memcpy(gf->tlvBuf, hdr, lenHdr); gf->tlvIdx = lenHdr; } else { gf->tlvIdx = 0; /* header already present! */ } /* we now need to obtain the last previous hash, so that - * we can continue the hash chain. + * we can continue the hash chain. We do not check for error + * as a state file error can be recovered by graceful degredation. */ readStateFile(gf); +done: return r; } /* @@ -405,6 +485,8 @@ rsgtCtxNew(void) gtctx ctx; ctx = calloc(1, sizeof(struct gtctx_s)); ctx->hashAlg = GT_HASHALG_SHA256; + ctx->errFunc = NULL; + ctx->usrptr = NULL; ctx->timestamper = strdup( "http://stamper.guardtime.net/gt-signingservice"); return ctx; @@ -426,7 +508,10 @@ rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn) snprintf(fn, sizeof(fn), "%s.gtstate", logfn); fn[MAXFNAME] = '\0'; /* be on save side */ gf->statefilename = (uchar*) strdup(fn); - tlvOpen(gf, LOGSIGHDR, sizeof(LOGSIGHDR)-1); + if(tlvOpen(gf, LOGSIGHDR, sizeof(LOGSIGHDR)-1) != 0) { + reportErr(ctx, "signature file open failed"); + gf = NULL; + } done: return gf; } @@ -453,36 +538,47 @@ rsgtSetHashFunction(gtctx ctx, char *algName) return r; } -void +int rsgtfileDestruct(gtfile gf) { + int r = 0; if(gf == NULL) goto done; - if(gf->bInBlk) - sigblkFinish(gf); - tlvClose(gf); + if(!gf->disabled && gf->bInBlk) { + r = sigblkFinish(gf); + if(r != 0) gf->disabled = 1; + } + if(!gf->disabled) + r = tlvClose(gf); free(gf->sigfilename); + free(gf->statefilename); + free(gf->IV); + free(gf->blkStrtHash); free(gf); -done: return; +done: return r; } void rsgtCtxDel(gtctx ctx) { - if(ctx != NULL) + if(ctx != NULL) { + free(ctx->timestamper); free(ctx); + } } /* new sigblk is initialized, but maybe in existing ctx */ void sigblkInit(gtfile gf) { + if(gf == NULL) goto done; seedIV(gf); memset(gf->roots_valid, 0, sizeof(gf->roots_valid)/sizeof(char)); gf->nRoots = 0; gf->nRecords = 0; gf->bInBlk = 1; +done: return; } @@ -500,7 +596,7 @@ static inline void bufAddHash(gtfile gf, uchar *buf, size_t *len, GTDataHash *hash) { if(hash == NULL) { - // TODO: how to get the REAL HASH ID? --> add field! + /* TODO: how to get the REAL HASH ID? --> add field? */ buf[*len] = hashIdentifier(gf->hashAlg); ++(*len); memcpy(buf+*len, gf->blkStrtHash, gf->lenBlkStrtHash); @@ -521,53 +617,73 @@ bufAddLevel(uchar *buf, size_t *len, uint8_t level) } -void +int hash_m(gtfile gf, GTDataHash **m) { -#warning Overall: check GT API return states! - // m = hash(concat(gf->x_prev, IV)); + int rgt; uchar concatBuf[16*1024]; size_t len = 0; + int r = 0; bufAddHash(gf, concatBuf, &len, gf->x_prev); bufAddIV(gf, concatBuf, &len); - GTDataHash_create(gf->hashAlg, concatBuf, len, m); + rgt = GTDataHash_create(gf->hashAlg, concatBuf, len, m); + if(rgt != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTDataHash_create", rgt); + r = RSGTE_HASH_CREATE; + goto done; + } +done: return r; } -void +int hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len) { - // r = hash(canonicalize(rec)); - GTDataHash_create(gf->hashAlg, rec, len, r); + int ret = 0, rgt; + rgt = GTDataHash_create(gf->hashAlg, rec, len, r); + if(rgt != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTDataHash_create", rgt); + ret = RSGTE_HASH_CREATE; + goto done; + } +done: return ret; } -void -hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, +int +hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *rec, uint8_t level) { - // x = hash(concat(m, r, 0)); /* hash leaf */ + int r = 0, rgt; uchar concatBuf[16*1024]; size_t len = 0; bufAddHash(gf, concatBuf, &len, m); - bufAddHash(gf, concatBuf, &len, r); + bufAddHash(gf, concatBuf, &len, rec); bufAddLevel(concatBuf, &len, level); - GTDataHash_create(gf->hashAlg, concatBuf, len, node); + rgt = GTDataHash_create(gf->hashAlg, concatBuf, len, node); + if(rgt != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTDataHash_create", rgt); + r = RSGTE_HASH_CREATE; + goto done; + } +done: return r; } -void +int sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) { GTDataHash *x; /* current hash */ GTDataHash *m, *r, *t; uint8_t j; + int ret = 0; - hash_m(gf, &m); - hash_r(gf, &r, rec, len); + if(gf == NULL || gf->disabled) goto done; + if((ret = hash_m(gf, &m)) != 0) goto done; + if((ret = hash_r(gf, &r, rec, len)) != 0) goto done; if(gf->bKeepRecordHashes) tlvWriteHash(gf, 0x0900, r); - hash_node(gf, &x, m, r, 1); /* hash leaf */ + if((ret = hash_node(gf, &x, m, r, 1)) != 0) goto done; /* hash leaf */ /* persists x here if Merkle tree needs to be persisted! */ if(gf->bKeepTreeHashes) tlvWriteHash(gf, 0x0901, x); @@ -581,10 +697,10 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) break; } else if(t != NULL) { /* hash interim node */ - hash_node(gf, &t, gf->roots_hash[j], t, j+2); + ret = hash_node(gf, &t, gf->roots_hash[j], t, j+2); + if(ret != 0) goto done; gf->roots_valid[j] = 0; GTDataHash_free(gf->roots_hash[j]); - // TODO: check if this is correct location (paper!) if(gf->bKeepTreeHashes) tlvWriteHash(gf, 0x0901, t); } @@ -606,34 +722,41 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) GTDataHash_free(r); if(gf->nRecords == gf->blockSizeLimit) { - sigblkFinish(gf); + ret = sigblkFinish(gf); + if(ret != 0) goto done; sigblkInit(gf); } +done: + if(ret != 0) { + gf->disabled = 1; +printf("DDDD: disabling ...\n");fflush(stdout); + } + return ret; } -static void +static int timestampIt(gtfile gf, GTDataHash *hash) { unsigned char *der; size_t lenDer; int r = GT_OK; + int ret = 0; GTTimestamp *timestamp = NULL; /* Get the timestamp. */ r = GTHTTP_createTimestampHash(hash, gf->ctx->timestamper, ×tamp); if(r != GT_OK) { - fprintf(stderr, "GTHTTP_createTimestampHash() failed: %d (%s)\n", - r, GTHTTP_getErrorString(r)); + reportGTAPIErr(gf->ctx, gf, "GTHTTP_createTimestampHash", r); + ret = 1; goto done; } /* Encode timestamp. */ r = GTTimestamp_getDEREncoded(timestamp, &der, &lenDer); if(r != GT_OK) { - // TODO: use rsyslog error reporting! - fprintf(stderr, "GTTimestamp_getDEREncoded() failed: %d (%s)\n", - r, GT_getErrorString(r)); + reportGTAPIErr(gf->ctx, gf, "GTTimestamp_getDEREncoded", r); + ret = 1; goto done; } @@ -642,14 +765,16 @@ timestampIt(gtfile gf, GTDataHash *hash) done: GT_free(der); GTTimestamp_free(timestamp); + return ret; } -void +int sigblkFinish(gtfile gf) { GTDataHash *root, *rootDel; int8_t j; + int ret = 0; if(gf->nRecords == 0) goto done; @@ -658,15 +783,22 @@ sigblkFinish(gtfile gf) for(j = 0 ; j < gf->nRoots ; ++j) { if(root == NULL) { root = gf->roots_valid[j] ? gf->roots_hash[j] : NULL; - gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + gf->roots_valid[j] = 0; } else if(gf->roots_valid[j]) { rootDel = root; - hash_node(gf, &root, gf->roots_hash[j], root, j+2); - gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ - GTDataHash_free(rootDel); + ret = hash_node(gf, &root, gf->roots_hash[j], rootDel, j+2); + gf->roots_valid[j] = 0; + GTDataHash_free(gf->roots_hash[j]); + /* there is no API to duplicate a hash, so we need + * to make sure we do not delete one that we still need! + */ + if(rootDel != gf->x_prev) + GTDataHash_free(rootDel); + if(ret != 0) goto done; } } - timestampIt(gf, root); + if((ret = timestampIt(gf, root)) != 0) goto done; + free(gf->blkStrtHash); gf->lenBlkStrtHash = gf->x_prev->digest_length; @@ -674,4 +806,5 @@ sigblkFinish(gtfile gf) memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); done: gf->bInBlk = 0; + return ret; } diff --git a/runtime/librsgt.h b/runtime/librsgt.h index d308d4c3..7eb63c11 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -39,12 +39,13 @@ struct gtctx_s { uint8_t bKeepTreeHashes; uint64_t blockSizeLimit; char *timestamper; + void (*errFunc)(void *, unsigned char*); + void *usrptr; /* for error function */ }; typedef struct gtctx_s *gtctx; /* this describes a file, as far as librsgt is concerned */ struct gtfile_s { - gtctx ctx; /* the following data items are mirrored from gtctx to * increase cache hit ratio (they are frequently accesed). */ @@ -52,6 +53,7 @@ struct gtfile_s { uint8_t bKeepRecordHashes; uint8_t bKeepTreeHashes; /* end mirrored properties */ + uint8_t disabled; /* permits to disable this file --> set to 1 */ uint64_t blockSizeLimit; uint8_t *IV; /* initial value for blinding masks */ GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ @@ -71,6 +73,7 @@ struct gtfile_s { /* data members for the associated TLV file */ char tlvBuf[4096]; int tlvIdx; /* current index into tlvBuf */ + gtctx ctx; }; typedef struct gtfile_s *gtfile; typedef struct gterrctx_s gterrctx_t; @@ -171,6 +174,7 @@ struct rsgtstatefile { #define RSGTE_INVLD_TIMESTAMP 17 /* RFC3161 timestamp is invalid */ #define RSGTE_TS_DERDECODE 18 /* error DER-Decoding a timestamp */ #define RSGTE_TS_DERENCODE 19 /* error DER-Encoding a timestamp */ +#define RSGTE_HASH_CREATE 20 /* error creating a hash */ /* the following function maps RSGTE_* state to a string - must be updated * whenever a new state is added. @@ -221,6 +225,8 @@ RSGTE2String(int err) return "error DER-decoding RFC3161 timestamp"; case RSGTE_TS_DERENCODE: return "error DER-encoding RFC3161 timestamp"; + case RSGTE_HASH_CREATE: + return "error creating hash"; default: return "unknown error"; } @@ -342,15 +348,16 @@ rsgtSetKeepTreeHashes(gtctx ctx, int val) } int rsgtSetHashFunction(gtctx ctx, char *algName); -void rsgtInit(char *usragent); +int rsgtInit(char *usragent); void rsgtExit(void); gtctx rsgtCtxNew(void); +void rsgtsetErrFunc(gtctx ctx, void (*func)(void*, unsigned char *), void *usrptr); gtfile rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn); -void rsgtfileDestruct(gtfile gf); +int rsgtfileDestruct(gtfile gf); void rsgtCtxDel(gtctx ctx); void sigblkInit(gtfile gf); -void sigblkAddRecord(gtfile gf, const unsigned char *rec, const size_t len); -void sigblkFinish(gtfile gf); +int sigblkAddRecord(gtfile gf, const unsigned char *rec, const size_t len); +int sigblkFinish(gtfile gf); /* reader functions */ int rsgt_tlvrdHeader(FILE *fp, unsigned char *hdr); int rsgt_tlvrd(FILE *fp, tlvrecord_t *rec, void *obj); @@ -370,9 +377,9 @@ void rsgt_objfree(uint16_t tlvtype, void *obj); /* TODO: replace these? */ -void hash_m(gtfile gf, GTDataHash **m); -void hash_r(gtfile gf, GTDataHash **r, const unsigned char *rec, const size_t len); -void hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level); +int hash_m(gtfile gf, GTDataHash **m); +int hash_r(gtfile gf, GTDataHash **r, const unsigned char *rec, const size_t len); +int hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level); extern char *rsgt_read_puburl; /**< url of publication server */ extern uint8_t rsgt_read_showVerified; diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index d61f3a86..09691292 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -55,11 +55,20 @@ static struct cnfparamblk pblk = cnfpdescr }; + +static void +errfunc(__attribute__((unused)) void *usrptr, uchar *emsg) +{ + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "Signature Provider" + "Error: %s - disabling signatures", emsg); +} + /* Standard-Constructor */ BEGINobjConstruct(lmsig_gt) dbgprintf("DDDD: lmsig_gt: called construct\n"); pThis->ctx = rsgtCtxNew(); + rsgtsetErrFunc(pThis->ctx, errfunc, NULL); ENDobjConstruct(lmsig_gt) @@ -67,6 +76,7 @@ ENDobjConstruct(lmsig_gt) BEGINobjDestruct(lmsig_gt) /* be sure to specify the object type also in END and CODESTART macros! */ CODESTARTobjDestruct(lmsig_gt) dbgprintf("DDDD: lmsig_gt: called destruct\n"); + rsgtCtxDel(pThis->ctx); ENDobjDestruct(lmsig_gt) @@ -124,10 +134,9 @@ OnFileOpen(void *pT, uchar *fn, void *pGF) gtfile *pgf = (gtfile*) pGF; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); - + /* note: if *pgf is set to NULL, this auto-disables GT functions */ *pgf = rsgtCtxOpenFile(pThis->ctx, fn); sigblkInit(*pgf); - RETiRet; } @@ -161,7 +170,7 @@ dbgprintf("DDDD: onFileClose\n"); BEGINobjQueryInterface(lmsig_gt) CODESTARTobjQueryInterface(lmsig_gt) - if(pIf->ifVersion != sigprovCURR_IF_VERSION) {/* check for current version, increment on each change */ + if(pIf->ifVersion != sigprovCURR_IF_VERSION) {/* check for current version, increment on each change */ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); } pIf->Construct = (rsRetVal(*)(void*)) lmsig_gtConstruct; @@ -189,7 +198,11 @@ BEGINObjClassInit(lmsig_gt, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); - rsgtInit("rsyslogd " VERSION); + if(rsgtInit("rsyslogd " VERSION) != 0) { + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "error initializing " + "signature provider - cannot sign"); + ABORT_FINALIZE(RS_RET_SIGPROV_ERR); + } ENDObjClassInit(lmsig_gt) diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index 45106c00..095b8066 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -282,7 +282,9 @@ done: fclose(logfp); logfp = NULL; fclose(sigfp); sigfp = NULL; - fclose(nsigfp); nsigfp = NULL; + if(nsigfp != NULL) { + fclose(nsigfp); nsigfp = NULL; + } /* everything went fine, so we rename files if we updated them */ if(mode == MD_EXTEND) { -- cgit v1.2.3 From c855e5ce04cbdae4767114b24238c8a9eb15b0ff Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 26 Mar 2013 16:44:42 +0100 Subject: logsig: fix memory leak --- runtime/librsgt.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 50b94b79..f99a8357 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -555,6 +555,7 @@ rsgtfileDestruct(gtfile gf) free(gf->statefilename); free(gf->IV); free(gf->blkStrtHash); + GTDataHash_free(gf->x_prev); free(gf); done: return r; } @@ -670,11 +671,29 @@ hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *rec, done: return r; } + +/* TODO: replace this once libgt provides a native function + * for this! + */ +static inline GTDataHash* +GTDataHash_dup(GTDataHash *in) +{ + GTDataHash *out; + + out = (GTDataHash*) calloc(1, sizeof(GTDataHash)); + if(out == NULL) goto done; + out->algorithm = in->algorithm, + out->digest_length = in->digest_length, + out->digest = malloc(in->digest_length); + memcpy(out->digest, in->digest, in->digest_length); +done: return out; +} + int sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) { GTDataHash *x; /* current hash */ - GTDataHash *m, *r, *t; + GTDataHash *m, *r, *t, *t_del; uint8_t j; int ret = 0; @@ -688,7 +707,7 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) if(gf->bKeepTreeHashes) tlvWriteHash(gf, 0x0901, x); /* add x to the forest as new leaf, update roots list */ - t = x; + t = GTDataHash_dup(x); for(j = 0 ; j < gf->nRoots ; ++j) { if(gf->roots_valid[j] == 0) { gf->roots_hash[j] = t; @@ -697,10 +716,12 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) break; } else if(t != NULL) { /* hash interim node */ - ret = hash_node(gf, &t, gf->roots_hash[j], t, j+2); - if(ret != 0) goto done; + t_del = t; + ret = hash_node(gf, &t, gf->roots_hash[j], t_del, j+2); gf->roots_valid[j] = 0; GTDataHash_free(gf->roots_hash[j]); + GTDataHash_free(t_del); + if(ret != 0) goto done; if(gf->bKeepTreeHashes) tlvWriteHash(gf, 0x0901, t); } @@ -713,6 +734,7 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) assert(gf->nRoots < MAX_ROOTS); t = NULL; } + GTDataHash_free(gf->x_prev); gf->x_prev = x; /* single var may be sufficient */ ++gf->nRecords; @@ -729,7 +751,6 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) done: if(ret != 0) { gf->disabled = 1; -printf("DDDD: disabling ...\n");fflush(stdout); } return ret; } @@ -789,17 +810,12 @@ sigblkFinish(gtfile gf) ret = hash_node(gf, &root, gf->roots_hash[j], rootDel, j+2); gf->roots_valid[j] = 0; GTDataHash_free(gf->roots_hash[j]); - /* there is no API to duplicate a hash, so we need - * to make sure we do not delete one that we still need! - */ - if(rootDel != gf->x_prev) - GTDataHash_free(rootDel); - if(ret != 0) goto done; - } + GTDataHash_free(rootDel); + if(ret != 0) goto done; /* checks hash_node() result! */ } if((ret = timestampIt(gf, root)) != 0) goto done; - + GTDataHash_free(root); free(gf->blkStrtHash); gf->lenBlkStrtHash = gf->x_prev->digest_length; gf->blkStrtHash = malloc(gf->lenBlkStrtHash); -- cgit v1.2.3 From 113c5e20ca6bc0fab6c77c7a4d5d5b2362817b26 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 26 Mar 2013 17:14:41 +0100 Subject: logsig: refactor code to not reley on undocumented libgt parts --- runtime/librsgt.c | 86 +++++++++++++++++++++++++++++++++---------------------- runtime/librsgt.h | 14 ++++----- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index f99a8357..f947dc19 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -87,6 +87,32 @@ rsgtsetErrFunc(gtctx ctx, void (*func)(void*, uchar *), void *usrptr) ctx->errFunc = func; } +static imprint_t * +rsgtImprintFromGTDataHash(GTDataHash *hash) +{ + imprint_t *imp; + + if((imp = calloc(1, sizeof(imprint_t))) == NULL) { + goto done; + } + imp->hashID = hashIdentifier(hash->algorithm), + imp->len = hash->digest_length; + if((imp->data = (uint8_t*)malloc(imp->len)) == NULL) { + free(imp); imp = NULL; goto done; + } + memcpy(imp->data, hash->digest, imp->len); +done: return imp; +} + +static void +rsgtimprintDel(imprint_t *imp) +{ + if(imp != NULL) { + free(imp->data), + free(imp); + } +} + int rsgtInit(char *usragent) { @@ -398,12 +424,12 @@ writeStateFile(gtfile gf) memcpy(sf.hdr, "GTSTAT10", 8); sf.hashID = hashIdentifier(gf->hashAlg); - sf.lenHash = gf->x_prev->digest_length; + sf.lenHash = gf->x_prev->len; /* if the write fails, we cannot do anything against that. We check * the condition just to keep the compiler happy. */ if(write(fd, &sf, sizeof(sf))){}; - if(write(fd, gf->x_prev->digest, gf->x_prev->digest_length)){}; + if(write(fd, gf->x_prev->data, gf->x_prev->len)){}; close(fd); done: return; } @@ -555,7 +581,7 @@ rsgtfileDestruct(gtfile gf) free(gf->statefilename); free(gf->IV); free(gf->blkStrtHash); - GTDataHash_free(gf->x_prev); + rsgtimprintDel(gf->x_prev); free(gf); done: return r; } @@ -592,23 +618,32 @@ bufAddIV(gtfile gf, uchar *buf, size_t *len) } -/* concat: add hash to buffer */ +/* concat: add imprint to buffer */ static inline void -bufAddHash(gtfile gf, uchar *buf, size_t *len, GTDataHash *hash) +bufAddImprint(gtfile gf, uchar *buf, size_t *len, imprint_t *imp) { - if(hash == NULL) { + if(imp == NULL) { /* TODO: how to get the REAL HASH ID? --> add field? */ buf[*len] = hashIdentifier(gf->hashAlg); ++(*len); memcpy(buf+*len, gf->blkStrtHash, gf->lenBlkStrtHash); *len += gf->lenBlkStrtHash; } else { - buf[*len] = hashIdentifier(gf->hashAlg); + buf[*len] = imp->hashID; ++(*len); - memcpy(buf+*len, hash->digest, hash->digest_length); - *len += hash->digest_length; + memcpy(buf+*len, imp->data, imp->len); + *len += imp->len; } } +/* concat: add hash to buffer */ +static inline void +bufAddHash(gtfile gf, uchar *buf, size_t *len, GTDataHash *hash) +{ + buf[*len] = hashIdentifier(gf->hashAlg); + ++(*len); + memcpy(buf+*len, hash->digest, hash->digest_length); + *len += hash->digest_length; +} /* concat: add tree level to buffer */ static inline void bufAddLevel(uchar *buf, size_t *len, uint8_t level) @@ -626,7 +661,7 @@ hash_m(gtfile gf, GTDataHash **m) size_t len = 0; int r = 0; - bufAddHash(gf, concatBuf, &len, gf->x_prev); + bufAddImprint(gf, concatBuf, &len, gf->x_prev); bufAddIV(gf, concatBuf, &len); rgt = GTDataHash_create(gf->hashAlg, concatBuf, len, m); if(rgt != GT_OK) { @@ -672,23 +707,6 @@ done: return r; } -/* TODO: replace this once libgt provides a native function - * for this! - */ -static inline GTDataHash* -GTDataHash_dup(GTDataHash *in) -{ - GTDataHash *out; - - out = (GTDataHash*) calloc(1, sizeof(GTDataHash)); - if(out == NULL) goto done; - out->algorithm = in->algorithm, - out->digest_length = in->digest_length, - out->digest = malloc(in->digest_length); - memcpy(out->digest, in->digest, in->digest_length); -done: return out; -} - int sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) { @@ -706,8 +724,10 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) /* persists x here if Merkle tree needs to be persisted! */ if(gf->bKeepTreeHashes) tlvWriteHash(gf, 0x0901, x); + rsgtimprintDel(gf->x_prev); + gf->x_prev = rsgtImprintFromGTDataHash(x); /* add x to the forest as new leaf, update roots list */ - t = GTDataHash_dup(x); + t = x; for(j = 0 ; j < gf->nRoots ; ++j) { if(gf->roots_valid[j] == 0) { gf->roots_hash[j] = t; @@ -734,12 +754,9 @@ sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) assert(gf->nRoots < MAX_ROOTS); t = NULL; } - GTDataHash_free(gf->x_prev); - gf->x_prev = x; /* single var may be sufficient */ ++gf->nRecords; - /* cleanup */ - /* note: x is freed later as part of roots cleanup */ + /* cleanup (x is cleared as part of the roots array) */ GTDataHash_free(m); GTDataHash_free(r); @@ -812,14 +829,15 @@ sigblkFinish(gtfile gf) GTDataHash_free(gf->roots_hash[j]); GTDataHash_free(rootDel); if(ret != 0) goto done; /* checks hash_node() result! */ + } } if((ret = timestampIt(gf, root)) != 0) goto done; GTDataHash_free(root); free(gf->blkStrtHash); - gf->lenBlkStrtHash = gf->x_prev->digest_length; + gf->lenBlkStrtHash = gf->x_prev->len; gf->blkStrtHash = malloc(gf->lenBlkStrtHash); - memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); + memcpy(gf->blkStrtHash, gf->x_prev->data, gf->x_prev->len); done: gf->bInBlk = 0; return ret; diff --git a/runtime/librsgt.h b/runtime/librsgt.h index 7eb63c11..bc20194f 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -43,6 +43,11 @@ struct gtctx_s { void *usrptr; /* for error function */ }; typedef struct gtctx_s *gtctx; +typedef struct gtfile_s *gtfile; +typedef struct gterrctx_s gterrctx_t; +typedef struct imprint_s imprint_t; +typedef struct block_sig_s block_sig_t; +typedef struct tlvrecord_s tlvrecord_t; /* this describes a file, as far as librsgt is concerned */ struct gtfile_s { @@ -56,7 +61,7 @@ struct gtfile_s { uint8_t disabled; /* permits to disable this file --> set to 1 */ uint64_t blockSizeLimit; uint8_t *IV; /* initial value for blinding masks */ - GTDataHash *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ + imprint_t *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ unsigned char *sigfilename; unsigned char *statefilename; int fd; @@ -75,11 +80,6 @@ struct gtfile_s { int tlvIdx; /* current index into tlvBuf */ gtctx ctx; }; -typedef struct gtfile_s *gtfile; -typedef struct gterrctx_s gterrctx_t; -typedef struct imprint_s imprint_t; -typedef struct block_sig_s block_sig_t; -typedef struct tlvrecord_s tlvrecord_t; struct tlvrecord_s { uint16_t tlvtype; @@ -254,7 +254,7 @@ hashOutputLengthOctets(uint8_t hashID) } static inline uint8_t -hashIdentifier(uint8_t hashID) +hashIdentifier(enum GTHashAlgorithm hashID) { switch(hashID) { case GT_HASHALG_SHA1: /* paper: SHA1 */ -- cgit v1.2.3 From 3a03b9b9213b9cd76b32a5d13f92bd228453322f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 26 Mar 2013 17:27:10 +0100 Subject: logsig: needed to refactor parts of the reader as well the previous refactoring broke that code --- runtime/librsgt.c | 4 ++-- runtime/librsgt.h | 2 ++ runtime/librsgt_read.c | 11 +++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/runtime/librsgt.c b/runtime/librsgt.c index f947dc19..afafe2f2 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -87,7 +87,7 @@ rsgtsetErrFunc(gtctx ctx, void (*func)(void*, uchar *), void *usrptr) ctx->errFunc = func; } -static imprint_t * +imprint_t * rsgtImprintFromGTDataHash(GTDataHash *hash) { imprint_t *imp; @@ -104,7 +104,7 @@ rsgtImprintFromGTDataHash(GTDataHash *hash) done: return imp; } -static void +void rsgtimprintDel(imprint_t *imp) { if(imp != NULL) { diff --git a/runtime/librsgt.h b/runtime/librsgt.h index bc20194f..bfcc4628 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -358,6 +358,8 @@ void rsgtCtxDel(gtctx ctx); void sigblkInit(gtfile gf); int sigblkAddRecord(gtfile gf, const unsigned char *rec, const size_t len); int sigblkFinish(gtfile gf); +imprint_t * rsgtImprintFromGTDataHash(GTDataHash *hash); +void rsgtimprintDel(imprint_t *imp); /* reader functions */ int rsgt_tlvrdHeader(FILE *fp, unsigned char *hdr); int rsgt_tlvrd(FILE *fp, tlvrecord_t *rec, void *obj); diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index 29a07e54..25c0db4d 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -95,6 +95,7 @@ rsgt_errctxInit(gterrctx_t *ectx) ectx->fp = NULL; ectx->filename = NULL; ectx->recNum = 0; + ectx->gtstate = 0; ectx->recNumInFile = 0; ectx->blkNum = 0; ectx->verbose = 0; @@ -864,7 +865,7 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, { int r = 0; GTDataHash *x; /* current hash */ - GTDataHash *m, *recHash = NULL, *t; + GTDataHash *m, *recHash = NULL, *t, *t_del; uint8_t j; hash_m(gf, &m); @@ -881,6 +882,8 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, r = rsgt_vrfy_chkTreeHash(gf, sigfp, nsigfp, x, ectx); if(r != 0) goto done; } + rsgtimprintDel(gf->x_prev); + gf->x_prev = rsgtImprintFromGTDataHash(x); /* add x to the forest as new leaf, update roots list */ t = x; for(j = 0 ; j < gf->nRoots ; ++j) { @@ -893,7 +896,8 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, /* hash interim node */ ectx->treeLevel = j+1; ectx->righthash = t; - hash_node(gf, &t, gf->roots_hash[j], t, j+2); + t_del = t; + hash_node(gf, &t, gf->roots_hash[j], t_del, j+2); gf->roots_valid[j] = 0; if(gf->bKeepTreeHashes) { ectx->lefthash = gf->roots_hash[j]; @@ -901,6 +905,7 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, if(r != 0) goto done; /* mem leak ok, we terminate! */ } GTDataHash_free(gf->roots_hash[j]); + GTDataHash_free(t_del); } } if(t != NULL) { @@ -911,11 +916,9 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, assert(gf->nRoots < MAX_ROOTS); t = NULL; } - gf->x_prev = x; /* single var may be sufficient */ ++gf->nRecords; /* cleanup */ - /* note: x is freed later as part of roots cleanup */ GTDataHash_free(m); done: if(recHash != NULL) -- cgit v1.2.3 From ed775041623e24bd41db50da1e0b099f819b929f Mon Sep 17 00:00:00 2001 From: Martin Carpenter Date: Tue, 26 Mar 2013 17:15:07 +0100 Subject: Check libsocket for getifaddrs() --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f444e5eb..96a9e80f 100644 --- a/configure.ac +++ b/configure.ac @@ -117,7 +117,10 @@ AC_TYPE_SIGNAL AC_FUNC_STAT AC_FUNC_STRERROR_R AC_FUNC_VPRINTF -AC_CHECK_FUNCS([flock basename alarm clock_gettime getifaddrs gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64]) +AC_CHECK_FUNCS([flock basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64]) + +# getifaddrs is in libc (mostly) or in libsocket (eg Solaris 11) or not defined (eg Solaris 10) +AC_SEARCH_LIBS([getifaddrs], [socket], [AC_DEFINE(HAVE_GETIFADDRS, [1], [set define])]) # the check below is probably ugly. If someone knows how to do it in a better way, please # let me know! -- rgerhards, 2010-10-06 -- cgit v1.2.3 From d1b8821be796694f123f3e469eaaedbe228fde66 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 26 Mar 2013 18:39:28 +0100 Subject: logsig: minimal end-user doc --- ChangeLog | 3 +- configure.ac | 2 +- doc/Makefile.am | 1 + doc/manual.html | 2 +- doc/omfile.html | 9 +++-- doc/sigprov_gt.html | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 doc/sigprov_gt.html diff --git a/ChangeLog b/ChangeLog index 6f258fa9..c64fe0f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ --------------------------------------------------------------------------- -Version 7.3.9 [devel] 2013-03-?? +Version 7.3.9 [devel] 2013-03-27 +- support for signing logs added - imudp: now supports user-selectable inputname - omlibdbi: now supports transaction interface if recent enough lbdbi is present diff --git a/configure.ac b/configure.ac index 96a9e80f..0c877eea 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[7.3.8],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.3.9],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/doc/Makefile.am b/doc/Makefile.am index de2e1df5..46afd900 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -41,6 +41,7 @@ html_files = \ omudpspoof.html \ omruleset.html \ omsnmp.html \ + sigprov_gt.html \ ommysql.html \ omoracle.html \ omlibdbi.html \ diff --git a/doc/manual.html b/doc/manual.html index ca54a04a..6fba9a05 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ professional services available directly from the source!

    Please visit the rsyslog sponsor's page to honor the project sponsors or become one yourself! We are very grateful for any help towards the project goals.

    -

    This documentation is for version 7.3.8 (devel branch) of rsyslog. +

    This documentation is for version 7.3.9 (devel branch) of rsyslog. Visit the rsyslog status page to obtain current version information and project status.

    If you like rsyslog, you might diff --git a/doc/omfile.html b/doc/omfile.html index 2c5ab97a..a980d37f 100644 --- a/doc/omfile.html +++ b/doc/omfile.html @@ -13,14 +13,14 @@

    The omfile plug-in provides the core functionality of writing messages to files residing inside the local file system (which may actually be remote if methods like NFS are used). Both files named with static names as well files with names based on message content are supported by this module. It is a built-in module that does not need to be loaded.

     

    -

    Module Configuration Parameters:

    +

    Module Parameters:

    • Template [templateName]
      sets a new default template for file actions.

     

    -

    Action Confguration Parameters:

    +

    Action Parameters:

    • DynaFileCacheSize (not mandatory, default will be used)
      Defines a template to be used for the output.

    • @@ -83,6 +83,11 @@
    • DynaFile
      For each message, the file name is generated based on the given template. Then, this file is opened. As with the ``file'' property, data is appended if the file already exists. If the file does not exist, a new file is created. A cache of recent files is kept. Note that this cache can consume quite some memory (especially if large buffer sizes are used). Files are kept open as long as they stay inside the cache. Currently, files are only evicted from the cache when there is need to do so (due to insufficient cache size). To force-close (and evict) a dynafile from cache, send a HUP signal to rsyslogd.

    • +
    • Sig.Provider [ProviderName]
      + Selects a signature provider for log signing. Currently, + there only is one provider called + "gt".

    • +
    • Template [templateName]
      sets a new default template for file actions.

    • diff --git a/doc/sigprov_gt.html b/doc/sigprov_gt.html new file mode 100644 index 00000000..18b0ed10 --- /dev/null +++ b/doc/sigprov_gt.html @@ -0,0 +1,100 @@ + + + + +GuardTime Log Signature Provider (gt) + + + +back to rsyslog module overview + +

      GuardTime Log Signature Provider (gt)

      +

      Signature Provider Name:    gt

      +

      Author: Rainer Gerhards <rgerhards@adiscon.com>

      +

      Multi-Ruleset Support: since 7.3.9 +

      Description:

      +

      Provides the ability to sign syslog messages via the +GuardTime signature services. +

      + +

      Configuration Parameters:

      +

      Signature providers are loaded by omfile, when the +provider is selected in its "sig.providerName" parameter. +Parameters for the provider are given in the omfile action instance +line. +

      This provider creates a signature file with the same base name but +the extension ".gtsig" for each log file (both for fixed-name files +as well as dynafiles). Both files together form a set. So you need to +archive both in order to prove integrity. +

        +
      • sig.hashFunction <Hash Algorithm>
        +The following hash algorithms are currently supported: +
          +
        • SHA1 +
        • RIPEMD-160 +
        • SHA2-224 +
        • SHA2-256 +
        • SHA2-384 +
        • SHA2-512 +
        +
      • +
      • sig.timestampService <timestamper URL>
        +This provides the URL of the timestamper service. If not selected, +a default server is selected. This may not necessarily be a good +one for your region. +
      • +
      • sig.block.sizeLimit <nbr-records>
        +The maximum number of records inside a single signature block. By +default, there is no size limit, so the signature is only written +on file closure. Note that a signature request typically takes between +one and two seconds. So signing to frequently is probably not a good +idea. +
      • +
      • sig.keepRecordHashes <on/off>
        +Controls if record hashes are written to the .gtsig file. This +enhances the ability to spot the location of a signature breach, +but costs considerable disk space (65 bytes for each log record +for SHA2-512 hashes, for example). +
      • +
      • sig.keepTreeHashes <on/off>
        +Controls if tree (intermediate) hashes are written to the .gtsig file. This +enhances the ability to spot the location of a signature breach, +but costs considerable disk space (a bit mire than the amount +sig.keepRecordHashes requries). Note that both Tree and Record +hashes can be kept inside the signature file. +
      • +
      +Caveats/Known Bugs: +
        +
      • currently none known +
      • +
      +

      Samples:

      +

      This writes a log file with it's associated signature file. Default +parameters are used. +

      + + +

      In the next sample, we use the more secure SHA2-512 hash function, +sign every 10,000 records and Tree and Record hashes are kept. + + + +

      [rsyslog.conf overview] +[manual index] [rsyslog site]

      +

      This documentation is part of the +rsyslog +project.
      +Copyright © 2013 by +Rainer Gerhards and +Adiscon. +Released under the GNU GPL version 3 or higher.

      + -- cgit v1.2.3 From 05478639e36aa588f6033f0664a448aa04f16c32 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 27 Mar 2013 15:14:45 +0100 Subject: bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements --- ChangeLog | 4 ++++ plugins/imuxsock/imuxsock.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index c64fe0f9..01ba0f45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,8 @@ --------------------------------------------------------------------------- +Version 7.3.10 [devel] 2013-04-?? +- bugfix: imuxsock aborted under some conditions + regression from ratelimiting enhancements +--------------------------------------------------------------------------- Version 7.3.9 [devel] 2013-03-27 - support for signing logs added - imudp: now supports user-selectable inputname diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index f15773fc..0f4ded1d 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -570,6 +570,10 @@ findRatelimiter(lstn_t *pLstn, struct ucred *cred, ratelimit_t **prl) FINALIZE; } #endif + if(pLstn->ht == NULL) { + *prl = NULL; + FINALIZE; + } rl = hashtable_search(pLstn->ht, &cred->pid); if(rl == NULL) { -- cgit v1.2.3 From 1b2f93da31d0331244aa7c72068f4ec324e2cb71 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 27 Mar 2013 17:05:26 +0100 Subject: add RainerScript re_extract() function --- ChangeLog | 1 + doc/rainerscript.html | 9 +++++ grammar/rainerscript.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++- grammar/rainerscript.h | 1 + 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 01ba0f45..54aacf0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ --------------------------------------------------------------------------- Version 7.3.10 [devel] 2013-04-?? +- added RainerScript re_extract() function - bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements --------------------------------------------------------------------------- diff --git a/doc/rainerscript.html b/doc/rainerscript.html index d76316ed..7cbbfa9f 100644 --- a/doc/rainerscript.html +++ b/doc/rainerscript.html @@ -66,6 +66,15 @@ variable, if it exists. Returns an empty string if it does not exist.
    • cstr(expr) - converts expr to a string value
    • cnum(expr) - converts expr to a number (integer)
    • re_match(expr, re) - returns 1, if expr matches re, 0 otherwise +
    • re_extract(expr, re, match, submatch, no-found) - extracts +data from a string (property) via a regular expression match. +POSIX ERE regular expressions are used. The variable "match" contains +the number of the match to use. This permits to pick up more than the +first expression match. Submatch is the submatch to match (max 50 supported). +The "no-found" parameter specifies which string is to be returned in case when +the regular expression is not found. Note that match and submatch start with +zero. It currently is not possible to extract more than one submatch with +a single call.
    • field(str, delim, matchnbr) - returns a field-based substring. str is the string to search, delim is the delimiter and matchnbr is the match to search for (the first match starts at 1). This works similar as the field based diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 8ef7429d..00e1e835 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -1228,6 +1228,96 @@ finalize_it: RETiRet; } +static inline void +doFunc_re_extract(struct cnffunc *func, struct var *ret, void* usrptr) +{ + size_t submatchnbr; + short matchnbr; + regmatch_t pmatch[50]; + int bMustFree; + es_str_t *estr; + char *str; + struct var r[CNFFUNC_MAX_ARGS]; + int iLenBuf; + unsigned iOffs; + short iTry = 0; + uchar bFound = 0; + iOffs = 0; + sbool bHadNoMatch = 0; + + cnfexprEval(func->expr[0], &r[0], usrptr); + /* search string is already part of the compiled regex, so we don't + * need it here! + */ + cnfexprEval(func->expr[2], &r[2], usrptr); + cnfexprEval(func->expr[3], &r[3], usrptr); + str = (char*) var2CString(&r[0], &bMustFree); + matchnbr = (short) var2Number(&r[2], NULL); + submatchnbr = (size_t) var2Number(&r[3], NULL); + if(submatchnbr > sizeof(pmatch)/sizeof(regmatch_t)) { + DBGPRINTF("re_extract() submatch %d is too large\n", submatchnbr); + bHadNoMatch = 1; + goto finalize_it; + } + + /* first see if we find a match, iterating through the series of + * potential matches over the string. + */ + while(!bFound) { + int iREstat; + iREstat = regexp.regexec(func->funcdata, (char*)(str + iOffs), + submatchnbr+1, pmatch, 0); + dbgprintf("re_extract: regexec return is %d\n", iREstat); + if(iREstat == 0) { + if(pmatch[0].rm_so == -1) { + dbgprintf("oops ... start offset of successful regexec is -1\n"); + break; + } + if(iTry == matchnbr) { + bFound = 1; + } else { + dbgprintf("re_extract: regex found at offset %d, new offset %d, tries %d\n", + iOffs, (int) (iOffs + pmatch[0].rm_eo), iTry); + iOffs += pmatch[0].rm_eo; + ++iTry; + } + } else { + break; + } + } + dbgprintf("re_extract: regex: end search, found %d\n", bFound); + if(!bFound) { + bHadNoMatch = 1; + goto finalize_it; + } else { + /* Match- but did it match the one we wanted? */ + /* we got no match! */ + if(pmatch[submatchnbr].rm_so == -1) { + bHadNoMatch = 1; + goto finalize_it; + } + /* OK, we have a usable match - we now need to malloc pB */ + iLenBuf = pmatch[submatchnbr].rm_eo - pmatch[submatchnbr].rm_so; + estr = es_newStrFromBuf(str + iOffs + pmatch[submatchnbr].rm_so, + iLenBuf); + } + + if(bMustFree) free(str); + if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr); + if(r[2].datatype == 'S') es_deleteStr(r[2].d.estr); + if(r[3].datatype == 'S') es_deleteStr(r[3].d.estr); +finalize_it: + if(bHadNoMatch) { + cnfexprEval(func->expr[4], &r[4], usrptr); + estr = var2String(&r[4], &bMustFree); + if(r[4].datatype == 'S') es_deleteStr(r[4].d.estr); + } + ret->datatype = 'S'; + ret->d.estr = estr; + return; +} + + /* Perform a function call. This has been moved out of cnfExprEval in order * to keep the code small and easier to maintain. */ @@ -1331,6 +1421,9 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr) if(bMustFree) free(str); if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr); break; + case CNFFUNC_RE_EXTRACT: + doFunc_re_extract(func, ret, usrptr); + break; case CNFFUNC_FIELD: cnfexprEval(func->expr[0], &r[0], usrptr); cnfexprEval(func->expr[1], &r[1], usrptr); @@ -1908,6 +2001,7 @@ cnffuncDestruct(struct cnffunc *func) /* some functions require special destruction */ switch(func->fID) { case CNFFUNC_RE_MATCH: + case CNFFUNC_RE_EXTRACT: if(func->funcdata != NULL) regexp.regfree(func->funcdata); break; @@ -2988,7 +3082,6 @@ cnfstmtOptimize(struct cnfstmt *root) struct cnfstmt *stmt; if(root == NULL) goto done; for(stmt = root ; stmt != NULL ; stmt = stmt->next) { -dbgprintf("RRRR: stmtOptimize: stmt %p, nodetype %u\n", stmt, stmt->nodetype); switch(stmt->nodetype) { case S_IF: cnfstmtOptimizeIf(stmt); @@ -3088,6 +3181,13 @@ funcName2ID(es_str_t *fname, unsigned short nParams) return CNFFUNC_INVALID; } return CNFFUNC_RE_MATCH; + } else if(!es_strbufcmp(fname, (unsigned char*)"re_extract", sizeof("re_extract") - 1)) { + if(nParams != 5) { + parser_errmsg("number of parameters for re_extract() must be five " + "but is %d.", nParams); + return CNFFUNC_INVALID; + } + return CNFFUNC_RE_EXTRACT; } else if(!es_strbufcmp(fname, (unsigned char*)"field", sizeof("field") - 1)) { if(nParams != 3) { parser_errmsg("number of parameters for field() must be three " @@ -3118,7 +3218,7 @@ initFunc_re_match(struct cnffunc *func) func->funcdata = NULL; if(func->expr[1]->nodetype != 'S') { - parser_errmsg("param 2 of re_match() must be a constant string"); + parser_errmsg("param 2 of re_match/extract() must be a constant string"); FINALIZE; } @@ -3196,6 +3296,7 @@ cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst) /* some functions require special initialization */ switch(func->fID) { case CNFFUNC_RE_MATCH: + case CNFFUNC_RE_EXTRACT: /* need to compile the regexp in param 2, so this MUST be a constant */ initFunc_re_match(func); break; diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h index 59ce53f3..4816951b 100644 --- a/grammar/rainerscript.h +++ b/grammar/rainerscript.h @@ -226,6 +226,7 @@ enum cnffuncid { CNFFUNC_CSTR, CNFFUNC_CNUM, CNFFUNC_RE_MATCH, + CNFFUNC_RE_EXTRACT, CNFFUNC_FIELD, CNFFUNC_PRIFILT }; -- cgit v1.2.3 From 474877b9d7912a3d2abc2a350dbc41e4c3b050e4 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 29 Mar 2013 16:04:21 +0100 Subject: permit substring extraction relative to end-of-string in templates --- ChangeLog | 1 + doc/property_replacer.html | 6 ++++++ doc/rsyslog_conf_templates.html | 2 ++ runtime/msg.c | 17 +++++++++++----- template.c | 44 +++++++++++++++++++++++++++++++---------- template.h | 1 + 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 405e8b18..ef67fdfd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +- templates now permit substring extraction relative to end-of-string ---------------------------------------------------------------------------- Version 7.2.7 [v7-stable] 2013-03-?? - rsyslogd startup information is now properly conveyed back to init diff --git a/doc/property_replacer.html b/doc/property_replacer.html index c7624b2d..13ff41c3 100644 --- a/doc/property_replacer.html +++ b/doc/property_replacer.html @@ -413,6 +413,12 @@ option when forwarding to remote hosts - they may treat the date as invalid just the subseconds of a timestamp (always 0 for a low precision timestamp) +pos-end-relative + the from and to position is relative to the end of the string + instead of the usual start of string. (available since rsyslog v7.3.10) + + + ControlCharacters Option values for how to process control characters diff --git a/doc/rsyslog_conf_templates.html b/doc/rsyslog_conf_templates.html index 0c189100..9a6e1619 100644 --- a/doc/rsyslog_conf_templates.html +++ b/doc/rsyslog_conf_templates.html @@ -134,6 +134,8 @@ csv-data is generated, "json", which formats proper json content (but without a header) and "jsonf", which formats as a complete json field.
    • position.from - obtain substring starting from this position (1 is the first position)
    • position.to - obtain substring up to this position +
    • position.relativeToEnd - the from and to position is relative to the end of the string + instead of the usual start of string. (available since rsyslog v7.3.10)
    • field.number - obtain this field match
    • field.delimiter - decimal value of delimiter character for field extraction
    • regex.expression - expression to use diff --git a/runtime/msg.c b/runtime/msg.c index 32a02424..9afd46cf 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -3017,13 +3017,20 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, uchar *pSb; iFrom = pTpe->data.field.iFromPos; iTo = pTpe->data.field.iToPos; - /* need to zero-base to and from (they are 1-based!) */ - if(iFrom > 0) - --iFrom; - if(iTo > 0) - --iTo; if(bufLen == -1) bufLen = ustrlen(pRes); +dbgprintf("DDDD: orginal iFrom %u, iTo %u, len %u\n", iFrom, iTo, bufLen); + if(pTpe->data.field.options.bFromPosEndRelative) { + iFrom = (bufLen < iFrom) ? 0 : bufLen - iFrom; + iTo = (bufLen < iTo)? 0 : bufLen - iTo; +dbgprintf("DDDD: now iFrom %u, iTo %u\n", iFrom, iTo); + } else { + /* need to zero-base to and from (they are 1-based!) */ + if(iFrom > 0) + --iFrom; + if(iTo > 0) + --iTo; + } if(iFrom == 0 && iTo >= bufLen) { /* in this case, the requested string is a superset of what we already have, * so there is no need to do any processing. This is a frequent case for size-limited diff --git a/template.c b/template.c index 8427e85c..09b9612c 100644 --- a/template.c +++ b/template.c @@ -79,6 +79,7 @@ static struct cnfparamdescr cnfparamdescrProperty[] = { { "format", eCmdHdlrString, 0 }, { "position.from", eCmdHdlrInt, 0 }, { "position.to", eCmdHdlrInt, 0 }, + { "position.relativetoend", eCmdHdlrBinary, 0 }, { "field.number", eCmdHdlrInt, 0 }, { "field.delimiter", eCmdHdlrInt, 0 }, { "regex.expression", eCmdHdlrString, 0 }, @@ -707,6 +708,8 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe) pTpe->data.field.options.bSecPathDrop = 1; } else if(!strcmp((char*)Buf, "secpath-replace")) { pTpe->data.field.options.bSecPathReplace = 1; + } else if(!strcmp((char*)Buf, "pos-end-relative")) { + pTpe->data.field.options.bFromPosEndRelative = 1; } else if(!strcmp((char*)Buf, "csv")) { if(pTpe->data.field.options.bJSON || pTpe->data.field.options.bJSONf) { errmsg.LogError(0, NO_ERRCODE, "error: can only specify " @@ -1060,18 +1063,27 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) #endif /* #ifdef FEATURE_REGEXP */ } - if(pTpe->data.field.iToPos < pTpe->data.field.iFromPos) { - iNum = pTpe->data.field.iToPos; - pTpe->data.field.iToPos = pTpe->data.field.iFromPos; - pTpe->data.field.iFromPos = iNum; - } - /* check options */ if(*p == ':') { ++p; /* eat ':' */ doOptions(&p, pTpe); } + if(pTpe->data.field.options.bFromPosEndRelative) { + if(pTpe->data.field.iToPos > pTpe->data.field.iFromPos) { + iNum = pTpe->data.field.iToPos; + pTpe->data.field.iToPos = pTpe->data.field.iFromPos; + pTpe->data.field.iFromPos = iNum; + } + } else { + if(pTpe->data.field.iToPos < pTpe->data.field.iFromPos) { + iNum = pTpe->data.field.iToPos; + pTpe->data.field.iToPos = pTpe->data.field.iFromPos; + pTpe->data.field.iFromPos = iNum; + } + } + + /* check field name */ if(*p == ':') { ++p; /* eat ':' */ @@ -1357,6 +1369,7 @@ createPropertyTpe(struct template *pTpl, struct cnfobj *o) int fielddelim = 9; /* default is HT (USACSII 9) */ int re_matchToUse = 0; int re_submatchToUse = 0; + int bPosRelativeToEnd = 0; char *re_expr = NULL; struct cnfparamvals *pvals = NULL; enum {F_NONE, F_CSV, F_JSON, F_JSONF} formatType = F_NONE; @@ -1392,6 +1405,8 @@ createPropertyTpe(struct template *pTpl, struct cnfobj *o) frompos = pvals[i].val.d.n; } else if(!strcmp(pblkProperty.descr[i].name, "position.to")) { topos = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "position.relativetoend")) { + bPosRelativeToEnd = pvals[i].val.d.n; } else if(!strcmp(pblkProperty.descr[i].name, "field.number")) { fieldnum = pvals[i].val.d.n; } else if(!strcmp(pblkProperty.descr[i].name, "field.delimiter")) { @@ -1523,10 +1538,18 @@ createPropertyTpe(struct template *pTpl, struct cnfobj *o) topos = 2000000000; /* large enough ;) */ if(frompos == -1 && topos != -1) frompos = 0; - if(topos < frompos) { - errmsg.LogError(0, RS_RET_ERR, "position.to=%d is lower than postion.from=%d\n", - topos, frompos); - ABORT_FINALIZE(RS_RET_ERR); + if(bPosRelativeToEnd) { + if(topos > frompos) { + errmsg.LogError(0, RS_RET_ERR, "position.to=%d is higher than postion.from=%d in 'relativeToEnd' mode\n", + topos, frompos); + ABORT_FINALIZE(RS_RET_ERR); + } + } else { + if(topos < frompos) { + errmsg.LogError(0, RS_RET_ERR, "position.to=%d is lower than postion.from=%d\n", + topos, frompos); + ABORT_FINALIZE(RS_RET_ERR); + } } if(fieldnum != -1 && re_expr != NULL) { errmsg.LogError(0, RS_RET_ERR, "both field extraction and regex extraction " @@ -1598,6 +1621,7 @@ createPropertyTpe(struct template *pTpl, struct cnfobj *o) if(frompos != -1) { pTpe->data.field.iFromPos = frompos; pTpe->data.field.iToPos = topos; + pTpe->data.field.options.bFromPosEndRelative = bPosRelativeToEnd; } if(re_expr != NULL) { rsRetVal iRetLocal; diff --git a/template.h b/template.h index 5a35d274..9d27752c 100644 --- a/template.h +++ b/template.h @@ -118,6 +118,7 @@ struct templateEntry { unsigned bJSON: 1; /* format field JSON escaped */ unsigned bJSONf: 1; /* format field JSON *field* (n/v pair) */ unsigned bMandatory: 1; /* mandatory field - emit even if empty */ + unsigned bFromPosEndRelative: 1;/* is From/To-Pos relative to end of string? */ } options; /* options as bit fields */ } field; } data; -- cgit v1.2.3 From f6da383e1c9d3ccfc5742d972c23dc3c718a0b88 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 29 Mar 2013 16:10:50 +0100 Subject: cleanup --- runtime/msg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtime/msg.c b/runtime/msg.c index 1e61e632..a5c52810 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -3232,11 +3232,9 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, iTo = pTpe->data.field.iToPos; if(bufLen == -1) bufLen = ustrlen(pRes); -dbgprintf("DDDD: orginal iFrom %u, iTo %u, len %u\n", iFrom, iTo, bufLen); if(pTpe->data.field.options.bFromPosEndRelative) { iFrom = (bufLen < iFrom) ? 0 : bufLen - iFrom; iTo = (bufLen < iTo)? 0 : bufLen - iTo; -dbgprintf("DDDD: now iFrom %u, iTo %u\n", iFrom, iTo); } else { /* need to zero-base to and from (they are 1-based!) */ if(iFrom > 0) -- cgit v1.2.3 From 0f87c631e7eac2ccbd36cd875d64de29dd7c714c Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 2 Apr 2013 11:31:09 +0200 Subject: add configure check for libgcrypt --- configure.ac | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/configure.ac b/configure.ac index 0c877eea..d6d4c203 100644 --- a/configure.ac +++ b/configure.ac @@ -755,6 +755,38 @@ if test "x$enable_gnutls" = "xyes"; then fi AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes) +# libgcrypt support +AC_ARG_ENABLE(libgcrypt, + [AS_HELP_STRING([--enable-libgcrypt],[Enable libgcrypt support @<:@default=yes@:>@])], + [case "${enableval}" in + yes) enable_libgcrypt="yes" ;; + no) enable_libgcrypt="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-libgcrypt) ;; + esac], + [enable_libgcrypt=yes] +) +if test "x$enable_libgcrypt" = "xyes"; then + AC_CHECK_PROG( + [HAVE_LIBGCRYPT_CONFIG], + [libgcrypt-config], + [yes],,, + ) + if test "x${HAVE_LIBGCRYPT_CONFIG}" != "xyes"; then + AC_MSG_FAILURE([libgcrypt-config not found in PATH]) + fi + AC_CHECK_LIB( + [gcrypt], + [gcry_cipher_open], + [LIBGCRYPT_CFLAGS="`libgcrypt-config --cflags`" + LIBGCRYPT_LIBS="`libgcrypt-config --libs`" + ], + [AC_MSG_FAILURE([libgrypt is missing])], + [`libgcrypt-config --libs --cflags`] + ) + AC_DEFINE([ENABLE_LIBGCRYPT], [1], [Indicator that LIBGCRYPT is present]) +fi +AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes) + # support for building the rsyslogd runtime AC_ARG_ENABLE(rsyslogrt, @@ -1406,6 +1438,7 @@ echo " GUI components will be built: $enable_gui" echo " Unlimited select() support enabled: $enable_unlimited_select" echo " uuid support enabled: $enable_uuid" echo " GuardTime signature support enabled: $enable_guardtime" +echo " libgcrypt support enabled: $enable_libgcrypt" echo " anonymization support enabled: $enable_mmanon" echo echo "---{ input plugins }---" -- cgit v1.2.3 From cbf0ed9541fee2a480dda3533d1604a9df4ae9bb Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 4 Apr 2013 10:07:49 +0200 Subject: bugfix: script == comparison did not work properly on JSON objects backport from 7.3 branch --- ChangeLog | 2 ++ grammar/rainerscript.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/ChangeLog b/ChangeLog index 405e8b18..39b5a8ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Version 7.2.7 [v7-stable] 2013-03-?? The rsyslog debug log file is now continued to be written across the fork. - updated systemd files to match current systemd source +- bugfix: script == comparison did not work properly on JSON objects + [backport from 7.3 branch] - bugfix: imudp scheduling parameters did affect main thread, not imudp closes: http://bugzilla.adiscon.com/show_bug.cgi?id=409 - bugfix: imuxsock rate-limiting could not be configured via legacy conf diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index b04e53b5..c14295af 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -1317,6 +1317,29 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) } if(r.datatype == 'S') es_deleteStr(r.d.estr); } + } else if(l.datatype == 'J') { + estr_l = var2String(&l, &bMustFree); + if(expr->r->nodetype == 'S') { + ret->d.n = !es_strcmp(estr_l, ((struct cnfstringval*)expr->r)->estr); /*CMP*/ + } else if(expr->r->nodetype == 'A') { + ret->d.n = evalStrArrayCmp(estr_l, (struct cnfarray*) expr->r, CMP_EQ); + } else { + cnfexprEval(expr->r, &r, usrptr); + if(r.datatype == 'S') { + ret->d.n = !es_strcmp(estr_l, r.d.estr); /*CMP*/ + } else { + n_l = var2Number(&l, &convok_l); + if(convok_l) { + ret->d.n = (n_l == r.d.n); /*CMP*/ + } else { + estr_r = var2String(&r, &bMustFree); + ret->d.n = !es_strcmp(estr_l, estr_r); /*CMP*/ + if(bMustFree) es_deleteStr(estr_r); + } + } + if(r.datatype == 'S') es_deleteStr(r.d.estr); + } + if(bMustFree) es_deleteStr(estr_l); } else { cnfexprEval(expr->r, &r, usrptr); if(r.datatype == 'S') { -- cgit v1.2.3 From 9b85b24d1323c91a06aeef08bbbde7a96afd46d6 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 4 Apr 2013 16:01:00 +0200 Subject: bugfix: nested if/prifilt conditions did not work properly closes: http://bugzilla.adiscon.com/show_bug.cgi?id=415 --- ChangeLog | 2 + grammar/rainerscript.c | 188 +++++++++++++++++++++++++++++++++---------------- grammar/rainerscript.h | 2 + runtime/ruleset.c | 21 ++++-- 4 files changed, 148 insertions(+), 65 deletions(-) diff --git a/ChangeLog b/ChangeLog index 39b5a8ef..854a14d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Version 7.2.7 [v7-stable] 2013-03-?? The rsyslog debug log file is now continued to be written across the fork. - updated systemd files to match current systemd source +- bugfix: nested if/prifilt conditions did not work properly + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=415 - bugfix: script == comparison did not work properly on JSON objects [backport from 7.3 branch] - bugfix: imudp scheduling parameters did affect main thread, not imudp diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index c14295af..186887b4 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -1285,7 +1285,9 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) int bMustFree, bMustFree2; long long n_r, n_l; - dbgprintf("eval expr %p, type '%c'(%u)\n", expr, expr->nodetype, expr->nodetype); + DBGPRINTF("eval expr %p, type '%c'[%s](%u)\n", expr, expr->nodetype, + + tokenval2str(expr->nodetype), expr->nodetype); switch(expr->nodetype) { /* note: comparison operations are extremely similar. The code can be copyied, only * places flagged with "CMP" need to be changed. @@ -1939,31 +1941,33 @@ cnfexprPrint(struct cnfexpr *expr, int indent) break; } } +/* print only the given stmt + * if "subtree" equals 1, the full statement subtree is printed, else + * really only the statement. + */ void -cnfstmtPrint(struct cnfstmt *root, int indent) +cnfstmtPrintOnly(struct cnfstmt *stmt, int indent, sbool subtree) { - struct cnfstmt *stmt; char *cstr; - //dbgprintf("stmt %p, indent %d, type '%c'\n", expr, indent, expr->nodetype); - for(stmt = root ; stmt != NULL ; stmt = stmt->next) { - switch(stmt->nodetype) { - case S_NOP: - doIndent(indent); dbgprintf("NOP\n"); - break; - case S_STOP: - doIndent(indent); dbgprintf("STOP\n"); - break; - case S_CALL: - cstr = es_str2cstr(stmt->d.s_call.name, NULL); - doIndent(indent); dbgprintf("CALL [%s]\n", cstr); - free(cstr); - break; - case S_ACT: - doIndent(indent); dbgprintf("ACTION %p [%s]\n", stmt->d.act, stmt->printable); - break; - case S_IF: - doIndent(indent); dbgprintf("IF\n"); - cnfexprPrint(stmt->d.s_if.expr, indent+1); + switch(stmt->nodetype) { + case S_NOP: + doIndent(indent); dbgprintf("NOP\n"); + break; + case S_STOP: + doIndent(indent); dbgprintf("STOP\n"); + break; + case S_CALL: + cstr = es_str2cstr(stmt->d.s_call.name, NULL); + doIndent(indent); dbgprintf("CALL [%s]\n", cstr); + free(cstr); + break; + case S_ACT: + doIndent(indent); dbgprintf("ACTION %p [%s]\n", stmt->d.act, stmt->printable); + break; + case S_IF: + doIndent(indent); dbgprintf("IF\n"); + cnfexprPrint(stmt->d.s_if.expr, indent+1); + if(subtree) { doIndent(indent); dbgprintf("THEN\n"); cnfstmtPrint(stmt->d.s_if.t_then, indent+1); if(stmt->d.s_if.t_else != NULL) { @@ -1971,54 +1975,67 @@ cnfstmtPrint(struct cnfstmt *root, int indent) cnfstmtPrint(stmt->d.s_if.t_else, indent+1); } doIndent(indent); dbgprintf("END IF\n"); - break; - case S_SET: - doIndent(indent); dbgprintf("SET %s =\n", - stmt->d.s_set.varname); - cnfexprPrint(stmt->d.s_set.expr, indent+1); - doIndent(indent); dbgprintf("END SET\n"); - break; - case S_UNSET: - doIndent(indent); dbgprintf("UNSET %s\n", - stmt->d.s_unset.varname); - break; - case S_PRIFILT: - doIndent(indent); dbgprintf("PRIFILT '%s'\n", stmt->printable); - pmaskPrint(stmt->d.s_prifilt.pmask, indent); + } + break; + case S_SET: + doIndent(indent); dbgprintf("SET %s =\n", + stmt->d.s_set.varname); + cnfexprPrint(stmt->d.s_set.expr, indent+1); + doIndent(indent); dbgprintf("END SET\n"); + break; + case S_UNSET: + doIndent(indent); dbgprintf("UNSET %s\n", + stmt->d.s_unset.varname); + break; + case S_PRIFILT: + doIndent(indent); dbgprintf("PRIFILT '%s'\n", stmt->printable); + pmaskPrint(stmt->d.s_prifilt.pmask, indent); + if(subtree) { cnfstmtPrint(stmt->d.s_prifilt.t_then, indent+1); if(stmt->d.s_prifilt.t_else != NULL) { doIndent(indent); dbgprintf("ELSE\n"); cnfstmtPrint(stmt->d.s_prifilt.t_else, indent+1); } doIndent(indent); dbgprintf("END PRIFILT\n"); - break; - case S_PROPFILT: - doIndent(indent); dbgprintf("PROPFILT\n"); - doIndent(indent); dbgprintf("\tProperty.: '%s'\n", - propIDToName(stmt->d.s_propfilt.propID)); - if(stmt->d.s_propfilt.propName != NULL) { - cstr = es_str2cstr(stmt->d.s_propfilt.propName, NULL); - doIndent(indent); - dbgprintf("\tCEE-Prop.: '%s'\n", cstr); - free(cstr); - } - doIndent(indent); dbgprintf("\tOperation: "); - if(stmt->d.s_propfilt.isNegated) - dbgprintf("NOT "); - dbgprintf("'%s'\n", getFIOPName(stmt->d.s_propfilt.operation)); - if(stmt->d.s_propfilt.pCSCompValue != NULL) { - doIndent(indent); dbgprintf("\tValue....: '%s'\n", - rsCStrGetSzStrNoNULL(stmt->d.s_propfilt.pCSCompValue)); - } + } + break; + case S_PROPFILT: + doIndent(indent); dbgprintf("PROPFILT\n"); + doIndent(indent); dbgprintf("\tProperty.: '%s'\n", + propIDToName(stmt->d.s_propfilt.propID)); + if(stmt->d.s_propfilt.propName != NULL) { + cstr = es_str2cstr(stmt->d.s_propfilt.propName, NULL); + doIndent(indent); + dbgprintf("\tCEE-Prop.: '%s'\n", cstr); + free(cstr); + } + doIndent(indent); dbgprintf("\tOperation: "); + if(stmt->d.s_propfilt.isNegated) + dbgprintf("NOT "); + dbgprintf("'%s'\n", getFIOPName(stmt->d.s_propfilt.operation)); + if(stmt->d.s_propfilt.pCSCompValue != NULL) { + doIndent(indent); dbgprintf("\tValue....: '%s'\n", + rsCStrGetSzStrNoNULL(stmt->d.s_propfilt.pCSCompValue)); + } + if(subtree) { doIndent(indent); dbgprintf("THEN\n"); cnfstmtPrint(stmt->d.s_propfilt.t_then, indent+1); doIndent(indent); dbgprintf("END PROPFILT\n"); - break; - default: - dbgprintf("error: unknown stmt type %u\n", - (unsigned) stmt->nodetype); - break; } + break; + default: + dbgprintf("error: unknown stmt type %u\n", + (unsigned) stmt->nodetype); + break; + } +} +void +cnfstmtPrint(struct cnfstmt *root, int indent) +{ + struct cnfstmt *stmt; + //dbgprintf("stmt %p, indent %d, type '%c'\n", expr, indent, expr->nodetype); + for(stmt = root ; stmt != NULL ; stmt = stmt->next) { + cnfstmtPrintOnly(stmt, indent, 1); } } @@ -3099,3 +3116,52 @@ unescapeStr(uchar *s, int len) s[iDst] = '\0'; } } + +char * +tokenval2str(int tok) +{ + if(tok < 256) return ""; + switch(tok) { + case NAME: return "NAME"; + case FUNC: return "FUNC"; + case BEGINOBJ: return "BEGINOBJ"; + case ENDOBJ: return "ENDOBJ"; + case BEGIN_ACTION: return "BEGIN_ACTION"; + case BEGIN_PROPERTY: return "BEGIN_PROPERTY"; + case BEGIN_CONSTANT: return "BEGIN_CONSTANT"; + case BEGIN_TPL: return "BEGIN_TPL"; + case BEGIN_RULESET: return "BEGIN_RULESET"; + case STOP: return "STOP"; + case SET: return "SET"; + case UNSET: return "UNSET"; + case CONTINUE: return "CONTINUE"; + case CALL: return "CALL"; + case LEGACY_ACTION: return "LEGACY_ACTION"; + case LEGACY_RULESET: return "LEGACY_RULESET"; + case PRIFILT: return "PRIFILT"; + case PROPFILT: return "PROPFILT"; + case BSD_TAG_SELECTOR: return "BSD_TAG_SELECTOR"; + case BSD_HOST_SELECTOR: return "BSD_HOST_SELECTOR"; + case IF: return "IF"; + case THEN: return "THEN"; + case ELSE: return "ELSE"; + case OR: return "OR"; + case AND: return "AND"; + case NOT: return "NOT"; + case VAR: return "VAR"; + case STRING: return "STRING"; + case NUMBER: return "NUMBER"; + case CMP_EQ: return "CMP_EQ"; + case CMP_NE: return "CMP_NE"; + case CMP_LE: return "CMP_LE"; + case CMP_GE: return "CMP_GE"; + case CMP_LT: return "CMP_LT"; + case CMP_GT: return "CMP_GT"; + case CMP_CONTAINS: return "CMP_CONTAINS"; + case CMP_CONTAINSI: return "CMP_CONTAINSI"; + case CMP_STARTSWITH: return "CMP_STARTSWITH"; + case CMP_STARTSWITHI: return "CMP_STARTSWITHI"; + case UMINUS: return "UMINUS"; + default: return "UNKNOWN TOKEN"; + } +} diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h index 59ce53f3..c9bcbcc9 100644 --- a/grammar/rainerscript.h +++ b/grammar/rainerscript.h @@ -317,6 +317,7 @@ int cnfparamvalsIsSet(struct cnfparamblk *params, struct cnfparamvals *vals); void varDelete(struct var *v); void cnfparamvalsDestruct(struct cnfparamvals *paramvals, struct cnfparamblk *blk); struct cnfstmt * cnfstmtNew(unsigned s_type); +void cnfstmtPrintOnly(struct cnfstmt *stmt, int indent, sbool subtree); void cnfstmtPrint(struct cnfstmt *stmt, int indent); struct cnfstmt* scriptAddStmt(struct cnfstmt *root, struct cnfstmt *s); struct objlst* objlstAdd(struct objlst *root, struct cnfobj *o); @@ -338,6 +339,7 @@ void cnfarrayContentDestruct(struct cnfarray *ar); char* getFIOPName(unsigned iFIOP); rsRetVal initRainerscript(void); void unescapeStr(uchar *s, int len); +char * tokenval2str(int tok); /* debug helper */ void cstrPrint(char *text, es_str_t *estr); diff --git a/runtime/ruleset.c b/runtime/ruleset.c index b74f8ec8..79d2c09d 100644 --- a/runtime/ruleset.c +++ b/runtime/ruleset.c @@ -297,6 +297,7 @@ execIf(struct cnfstmt *stmt, batch_t *pBatch, sbool *active) sbool *newAct; int i; sbool bRet; + sbool allInactive = 1; DEFiRet; newAct = newActive(pBatch); for(i = 0 ; i < batchNumMsgs(pBatch) ; ++i) { @@ -307,12 +308,19 @@ execIf(struct cnfstmt *stmt, batch_t *pBatch, sbool *active) if(active == NULL || active[i]) { bRet = cnfexprEvalBool(stmt->d.s_if.expr, (msg_t*)(pBatch->pElem[i].pUsrp)); + allInactive = 0; } else bRet = 0; newAct[i] = bRet; DBGPRINTF("batch: item %d: expr eval: %d\n", i, bRet); } + if(allInactive) { + DBGPRINTF("execIf: all batch elements are inactive, holding execution\n"); + freeActive(newAct); + FINALIZE; + } + if(stmt->d.s_if.t_then != NULL) { scriptExec(stmt->d.s_if.t_then, pBatch, newAct); } @@ -320,7 +328,8 @@ execIf(struct cnfstmt *stmt, batch_t *pBatch, sbool *active) for(i = 0 ; i < batchNumMsgs(pBatch) ; ++i) { if(*(pBatch->pbShutdownImmediate)) FINALIZE; - if(pBatch->pElem[i].state != BATCH_STATE_DISC) + if(pBatch->pElem[i].state != BATCH_STATE_DISC + && (active == NULL || active[i])) newAct[i] = !newAct[i]; } scriptExec(stmt->d.s_if.t_else, pBatch, newAct); @@ -365,7 +374,8 @@ execPRIFILT(struct cnfstmt *stmt, batch_t *pBatch, sbool *active) for(i = 0 ; i < batchNumMsgs(pBatch) ; ++i) { if(*(pBatch->pbShutdownImmediate)) return; - if(pBatch->pElem[i].state != BATCH_STATE_DISC) + if(pBatch->pElem[i].state != BATCH_STATE_DISC + && (active == NULL || active[i])) newAct[i] = !newAct[i]; } scriptExec(stmt->d.s_prifilt.t_else, pBatch, newAct); @@ -504,7 +514,11 @@ scriptExec(struct cnfstmt *root, batch_t *pBatch, sbool *active) struct cnfstmt *stmt; for(stmt = root ; stmt != NULL ; stmt = stmt->next) { -dbgprintf("RRRR: scriptExec: batch of %d elements, active %p, stmt %p, nodetype %u\n", batchNumMsgs(pBatch), active, stmt, stmt->nodetype); + if(Debug) { + dbgprintf("scriptExec: batch of %d elements, active %p, active[0]:%d\n", + batchNumMsgs(pBatch), active, (active == NULL ? 1 : active[0])); + cnfstmtPrintOnly(stmt, 2, 0); + } switch(stmt->nodetype) { case S_NOP: break; @@ -521,7 +535,6 @@ dbgprintf("RRRR: scriptExec: batch of %d elements, active %p, stmt %p, nodetype execUnset(stmt, pBatch, active); break; case S_CALL: - DBGPRINTF("calling ruleset\n"); // TODO: add Name scriptExec(stmt->d.s_call.stmt, pBatch, active); break; case S_IF: -- cgit v1.2.3 From 0cee769fcdc9716ccb2a60b6473062a60f640bb3 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 5 Apr 2013 12:08:49 +0200 Subject: log encryption: initial PoC implementation rough baseline, needs to be extended for actual use. --- configure.ac | 2 +- runtime/Makefile.am | 22 ++++- runtime/cryprov.h | 41 ++++++++++ runtime/libgcry.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/libgcry.h | 47 +++++++++++ runtime/lmcry_gcry.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/lmcry_gcry.h | 39 +++++++++ runtime/rsyslog.h | 1 + runtime/stream.c | 27 ++++++- runtime/stream.h | 11 ++- tools/Makefile.am | 2 +- tools/omfile.c | 71 +++++++++++++++- 12 files changed, 701 insertions(+), 10 deletions(-) create mode 100644 runtime/cryprov.h create mode 100644 runtime/libgcry.c create mode 100644 runtime/libgcry.h create mode 100644 runtime/lmcry_gcry.c create mode 100644 runtime/lmcry_gcry.h diff --git a/configure.ac b/configure.ac index d6d4c203..0ec29f0d 100644 --- a/configure.ac +++ b/configure.ac @@ -785,7 +785,7 @@ if test "x$enable_libgcrypt" = "xyes"; then ) AC_DEFINE([ENABLE_LIBGCRYPT], [1], [Indicator that LIBGCRYPT is present]) fi -AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes) +AM_CONDITIONAL(ENABLE_LIBGCRYPT, test x$enable_libgcrypt = xyes) # support for building the rsyslogd runtime diff --git a/runtime/Makefile.am b/runtime/Makefile.am index c05cc773..be68ce60 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -96,12 +96,12 @@ librsyslog_la_SOURCES = \ # if WITH_MODDIRS -librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools +librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) $(LIBGCRYPT_CFLAGS) -I\$(top_srcdir)/tools else -librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools -I\$(top_srcdir)/grammar +librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) $(LIBGCRYPT_CFLAGS) -I\$(top_srcdir)/tools -I\$(top_srcdir)/grammar endif #librsyslog_la_LDFLAGS = -module -avoid-version -librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBEE_LIBS) +librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBGCRYPT_LIBS) $(LIBEE_LIBS) # # regular expression support @@ -174,6 +174,22 @@ lmnsd_gtls_la_LDFLAGS = -module -avoid-version lmnsd_gtls_la_LIBADD = $(GNUTLS_LIBS) endif +# +# support library for libgcrypt +# +if ENABLE_LIBGCRYPT +#noinst_LTLIBRARIES += libgcry.la +#libgcry_la_SOURCES = libgcry.c libgcry.h +#libcgry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) + pkglib_LTLIBRARIES += lmcry_gcry.la + lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h libgcry.c libgcry.h + lmcry_gcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) + lmcry_gcry_la_LDFLAGS = -module -avoid-version +#lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) + lmcry_gcry_la_LIBADD = $(LIBGCRYPT_LIBS) +endif + + # # support library for guardtime # diff --git a/runtime/cryprov.h b/runtime/cryprov.h new file mode 100644 index 00000000..c5ee95a0 --- /dev/null +++ b/runtime/cryprov.h @@ -0,0 +1,41 @@ +/* The interface definition for (file) crypto providers. + * + * This is just an abstract driver interface, which needs to be + * implemented by concrete classes. + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * 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. + */ +#ifndef INCLUDED_CRYPROV_H +#define INCLUDED_CRYPROV_H + +#include + +/* interface */ +BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ + rsRetVal (*Construct)(void *ppThis); + rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst); + rsRetVal (*Destruct)(void *ppThis); + rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData); + rsRetVal (*Encrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf); + rsRetVal (*OnFileClose)(void *pFileInstData); +ENDinterface(cryprov) +#define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +rsRetVal initCrypt(int gcry_mode, char * iniVector); +rsRetVal doCrypt(char *buf, size_t *len); +#endif /* #ifndef INCLUDED_CRYPROV_H */ diff --git a/runtime/libgcry.c b/runtime/libgcry.c new file mode 100644 index 00000000..8184c160 --- /dev/null +++ b/runtime/libgcry.c @@ -0,0 +1,224 @@ +/* gcry.c - rsyslog's libgcrypt based crypto provider + * + * 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. + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include + +#include "rsyslog.h" +#include "libgcry.h" + + +static inline gcryfile +gcryfileConstruct(gcryctx ctx) +{ + gcryfile gf; + if((gf = calloc(1, sizeof(struct gcryfile_s))) == NULL) + goto done; + gf->ctx = ctx; +done: return gf; +} +gcryctx +gcryCtxNew(void) +{ + gcryctx ctx; + ctx = calloc(1, sizeof(struct gcryctx_s)); + return ctx; +} + +int +gcryfileDestruct(gcryfile gf) +{ + int r = 0; + if(gf == NULL) + goto done; + + free(gf); +done: return r; +} +void +rsgcryCtxDel(gcryctx ctx) +{ + if(ctx != NULL) { + free(ctx); + } +} + +static inline void +addPadding(gcryfile pF, uchar *buf, size_t *plen) +{ + unsigned i; + size_t nPad; + nPad = (pF->blkLength - *plen % pF->blkLength) % pF->blkLength; + dbgprintf("DDDD: addPadding %d chars, blkLength %d, mod %d, pad %d\n", + *plen, pF->blkLength, *plen % pF->blkLength, nPad); + for(i = 0 ; i < nPad ; ++i) + buf[(*plen)+i] = 0x00; + (*plen)+= nPad; +} + +static inline void +removePadding(char *buf, size_t *plen) +{ + unsigned len = (unsigned) *plen; + unsigned iSrc, iDst; + char *frstNUL; + + frstNUL = strchr(buf, 0x00); + if(frstNUL == NULL) + goto done; + iDst = iSrc = frstNUL - buf; + + while(iSrc < len) { + if(buf[iSrc] != 0x00) + buf[iDst++] = buf[iSrc]; + ++iSrc; + } + + *plen = iDst; +done: return; +} + +rsRetVal +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) +{ + #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable + size_t keyLength; + char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY + gcry_error_t gcryError; + gcryfile gf = NULL; + DEFiRet; + + CHKmalloc(gf = gcryfileConstruct(ctx)); + + gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + + gcryError = gcry_cipher_open( + &gf->chd, // gcry_cipher_hd_t * + GCRY_CIPHER, // int + gcry_mode, // int + 0); // unsigned int + if (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, aesSymKey, keyLength); + if (gcryError) { + dbgprintf("gcry_cipher_setkey failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } + + gcryError = gcry_cipher_setiv(gf->chd, iniVector, gf->blkLength); + if (gcryError) { + dbgprintf("gcry_cipher_setiv failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } + *pgf = gf; +finalize_it: + if(iRet != RS_RET_OK && gf != NULL) + gcryfileDestruct(gf); + RETiRet; +} + +int +rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len) +{ + int gcryError; + DEFiRet; + + if(*len == 0) + FINALIZE; + + addPadding(pF, buf, len); + gcryError = gcry_cipher_encrypt(pF->chd, buf, *len, NULL, 0); + if(gcryError) { + dbgprintf("gcry_cipher_encrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } +finalize_it: + RETiRet; +} + + +/* module-init dummy for potential later use */ +int +rsgcryInit(void) +{ + return 0; +} + +/* module-deinit dummy for potential later use */ +void +rsgcryExit(void) +{ + return; +} + +#if 0 // we use this for the tool, only! +static void +doDeCrypt(FILE *fpin, FILE *fpout) +{ + gcry_error_t gcryError; + char buf[64*1024]; + size_t nRead, nWritten; + size_t nPad; + + while(1) { + nRead = fread(buf, 1, sizeof(buf), fpin); + if(nRead == 0) + break; + nPad = (blkLength - nRead % blkLength) % blkLength; + fprintf(stderr, "read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, + nRead % blkLength, nPad); + gcryError = gcry_cipher_decrypt( + gcryCipherHd, // gcry_cipher_hd_t + buf, // void * + nRead, // size_t + NULL, // const void * + 0); // size_t + if (gcryError) { + fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + return; + } +fprintf(stderr, "in remove pad, %d\n", nRead); + removePadding(buf, &nRead); +fprintf(stderr, "out remove pad %d\n", nRead); + nWritten = fwrite(buf, 1, nRead, fpout); + if(nWritten != nRead) { + perror("fpout"); + return; + } + } +} +#endif diff --git a/runtime/libgcry.h b/runtime/libgcry.h new file mode 100644 index 00000000..0405162f --- /dev/null +++ b/runtime/libgcry.h @@ -0,0 +1,47 @@ +/* libgcry.h - rsyslog's guardtime support library + * + * 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. + */ +#ifndef INCLUDED_LIBGCRY_H +#define INCLUDED_LIBGCRY_H +#include + + +struct gcryctx_s { + void *usrptr; /* for error function */ +}; +typedef struct gcryctx_s *gcryctx; +typedef struct gcryfile_s *gcryfile; + +/* this describes a file, as far as libgcry is concerned */ +struct gcryfile_s { + gcry_cipher_hd_t chd; /* cypher handle */ + size_t blkLength; /* size of low-level crypto block */ + gcryctx ctx; +}; + +int rsgcryInit(void); +void rsgcryExit(void); +gcryctx gcryCtxNew(void); +void rsgcryCtxDel(gcryctx ctx); +int gcryfileDestruct(gcryfile gf); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char * iniVector); +int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); + +#endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c new file mode 100644 index 00000000..6800055d --- /dev/null +++ b/runtime/lmcry_gcry.c @@ -0,0 +1,224 @@ +/* lmcry_gcry.c + * + * An implementation of the cryprov interface for libgcrypt. + * + * Copyright 2013 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * 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 +#include +#include + +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "cryprov.h" +#include "libgcry.h" +#include "lmcry_gcry.h" + +MODULE_TYPE_LIB +MODULE_TYPE_NOKEEP + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) + +/* tables for interfacing with the v6 config system */ +static struct cnfparamdescr cnfpdescr[] = { + { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ + { "cry.algo", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk pblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfpdescr)/sizeof(struct cnfparamdescr), + cnfpdescr + }; + + +#if 0 +static void +errfunc(__attribute__((unused)) void *usrptr, uchar *emsg) +{ + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "Crypto Provider" + "Error: %s - disabling encryption", emsg); +} +#endif + +/* Standard-Constructor + */ +BEGINobjConstruct(lmcry_gcry) + dbgprintf("DDDD: lmcry_gcry: called construct\n"); + pThis->ctx = gcryCtxNew(); +ENDobjConstruct(lmcry_gcry) + + +/* destructor for the lmcry_gcry object */ +BEGINobjDestruct(lmcry_gcry) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(lmcry_gcry) + dbgprintf("DDDD: lmcry_gcry: called destruct\n"); + rsgcryCtxDel(pThis->ctx); +ENDobjDestruct(lmcry_gcry) + + +/* apply all params from param block to us. This must be called + * after construction, but before the OnFileOpen() entry point. + * Defaults are expected to have been set during construction. + */ +rsRetVal +SetCnfParam(void *pT, struct nvlst *lst) +{ + lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; + int i; + uchar *cstr; + struct cnfparamvals *pvals; + pvals = nvlstGetParams(lst, &pblk, NULL); + if(Debug) { + dbgprintf("param blk in lmcry_gcry:\n"); + cnfparamsPrint(&pblk, pvals); + } + + for(i = 0 ; i < pblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; +#if 0 + if(!strcmp(pblk.descr[i].name, "sig.hashfunction")) { + cstr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + if(gcrySetHashFunction(pThis->ctx, (char*)cstr) != 0) { + errmsg.LogError(0, RS_RET_ERR, "Hash function " + "'%s' unknown - using default", cstr); + } + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { + cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + gcrySetTimestamper(pThis->ctx, (char*) cstr); + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.block.sizelimit")) { + gcrySetBlockSizeLimit(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeprecordhashes")) { + gcrySetKeepRecordHashes(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeptreehashes")) { + gcrySetKeepTreeHashes(pThis->ctx, pvals[i].val.d.n); + } else { + DBGPRINTF("lmcry_gcry: program error, non-handled " + "param '%s'\n", pblk.descr[i].name); + } +#endif + } + cnfparamvalsDestruct(pvals, &pblk); + return RS_RET_OK; +} + + +static rsRetVal +OnFileOpen(void *pT, uchar *fn, void *pGF) +{ + lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; + gcryfile *pgf = (gcryfile*) pGF; + DEFiRet; +dbgprintf("DDDD: cry: onFileOpen: %s\n", fn); + /* note: if *pgf is set to NULL, this auto-disables GT functions */ + //*pgf = gcryCtxOpenFile(pThis->ctx, fn); + + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, "TODO: init value")); +finalize_it: + RETiRet; +} + +static rsRetVal +Encrypt(void *pF, uchar *rec, size_t *lenRec) +{ + DEFiRet; +dbgprintf("DDDD: Encrypt (%u): %s\n", *lenRec-1, rec); + iRet = rsgcryEncrypt(pF, rec, lenRec); + + RETiRet; +} + +static rsRetVal +OnFileClose(void *pF) +{ + DEFiRet; +dbgprintf("DDDD: onFileClose\n"); + gcryfileDestruct(pF); + + RETiRet; +} + +BEGINobjQueryInterface(lmcry_gcry) +CODESTARTobjQueryInterface(lmcry_gcry) + if(pIf->ifVersion != cryprovCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + pIf->Construct = (rsRetVal(*)(void*)) lmcry_gcryConstruct; + pIf->SetCnfParam = SetCnfParam; + pIf->Destruct = (rsRetVal(*)(void*)) lmcry_gcryDestruct; + pIf->OnFileOpen = OnFileOpen; + pIf->Encrypt = Encrypt; + pIf->OnFileClose = OnFileClose; +finalize_it: +ENDobjQueryInterface(lmcry_gcry) + + +BEGINObjClassExit(lmcry_gcry, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(lmcry_gcry) + /* release objects we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); + + rsgcryExit(); +ENDObjClassExit(lmcry_gcry) + + +BEGINObjClassInit(lmcry_gcry, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + if(rsgcryInit() != 0) { + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "error initializing " + "crypto provider - cannot encrypt"); + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); + } +ENDObjClassInit(lmcry_gcry) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + lmcry_gcryClassExit(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_LIB_QUERIES +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ + /* Initialize all classes that are in our module - this includes ourselfs */ + CHKiRet(lmcry_gcryClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ +ENDmodInit diff --git a/runtime/lmcry_gcry.h b/runtime/lmcry_gcry.h new file mode 100644 index 00000000..c0205ab9 --- /dev/null +++ b/runtime/lmcry_gcry.h @@ -0,0 +1,39 @@ +/* An implementation of the cryprov interface for libgcrypt. + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * 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. + */ +#ifndef INCLUDED_LMCRY_GCRY_H +#define INCLUDED_LMCRY_GCRY_H +#include "cryprov.h" + +/* interface is defined in cryprov.h, we just implement it! */ +#define lmcry_gcryCURR_IF_VERSION cryprovCURR_IF_VERSION +typedef cryprov_if_t lmcry_gcry_if_t; + +/* the lmcry_gcry object */ +struct lmcry_gcry_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + gcryctx ctx; +}; +typedef struct lmcry_gcry_s lmcry_gcry_t; + +/* prototypes */ +PROTOTYPEObj(lmcry_gcry); + +#endif /* #ifndef INCLUDED_LMCRY_GCRY_H */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 5ba6ede7..cbc0401b 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -403,6 +403,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_INVLD_ANON_BITS = -2312,/**< mmanon: invalid number of bits to anonymize specified */ RS_RET_REPLCHAR_IGNORED = -2313,/**< mmanon: replacementChar parameter is ignored */ RS_RET_SIGPROV_ERR = -2320,/**< error in signature provider */ + RS_RET_CRYPROV_ERR = -2321,/**< error in cryptography encryption provider */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/runtime/stream.c b/runtime/stream.c index 00afcdaa..941fc39d 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -16,7 +16,7 @@ * it turns out to be problematic. Then, we need to quasi-refcount the number of accesses * to the object. * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. + * Copyright 2008-2013 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -56,6 +56,7 @@ #include "stream.h" #include "unicode-helper.h" #include "module-template.h" +#include "cryprov.h" #if HAVE_SYS_PRCTL_H # include #endif @@ -253,6 +254,12 @@ doPhysOpen(strm_t *pThis) pThis->bIsTTY = 0; } +dbgprintf("DDDD: cryprov %p\n", pThis->cryprov); + if(pThis->cryprov != NULL) { + iRet = pThis->cryprov->OnFileOpen(pThis->cryprovData, + pThis->pszCurrFName, &pThis->cryprovFileData); +dbgprintf("DDDD: iREt cryprov->onFileOpen: %d\n", iRet); + } finalize_it: RETiRet; } @@ -405,6 +412,10 @@ static rsRetVal strmCloseFile(strm_t *pThis) close(pThis->fd); pThis->fd = -1; pThis->inode = 0; + if(pThis->cryprov != NULL) { + pThis->cryprov->OnFileClose(pThis->cryprovFileData); + pThis->cryprovFileData = NULL; + } } if(pThis->fdDir != -1) { @@ -1200,10 +1211,18 @@ strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf) DEFiRet; ISOBJ_TYPE_assert(pThis, strm); - DBGPRINTF("strmPhysWrite, stream %p, len %d\n", pThis, (int) lenBuf); + DBGPRINTF("strmPhysWrite, stream %p, len %u\n", pThis, (unsigned)lenBuf); if(pThis->fd == -1) CHKiRet(strmOpenFile(pThis)); + /* here we place our crypto interface */ +dbgprintf("DDDD: doing crypto, len %d\n", lenBuf); + if(pThis->cryprov != NULL) { + pThis->cryprov->Encrypt(pThis->cryprovFileData, pBuf, &lenBuf); + } +dbgprintf("DDDD: done crypto, len %d\n", lenBuf); + /* end crypto */ + iWritten = lenBuf; CHKiRet(doWriteCall(pThis, pBuf, &iWritten)); @@ -1600,6 +1619,8 @@ DEFpropSetMeth(strm, sIOBufSize, size_t) DEFpropSetMeth(strm, iSizeLimit, off_t) DEFpropSetMeth(strm, iFlushInterval, int) DEFpropSetMeth(strm, pszSizeLimitCmd, uchar*) +DEFpropSetMeth(strm, cryprov, cryprov_if_t*) +DEFpropSetMeth(strm, cryprovData, void*) static rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal) { @@ -1935,6 +1956,8 @@ CODESTARTobjQueryInterface(strm) pIf->SetiSizeLimit = strmSetiSizeLimit; pIf->SetiFlushInterval = strmSetiFlushInterval; pIf->SetpszSizeLimitCmd = strmSetpszSizeLimitCmd; + pIf->Setcryprov = strmSetcryprov; + pIf->SetcryprovData = strmSetcryprovData; finalize_it: ENDobjQueryInterface(strm) diff --git a/runtime/stream.h b/runtime/stream.h index b7cc6d36..61d5ede2 100644 --- a/runtime/stream.h +++ b/runtime/stream.h @@ -41,7 +41,7 @@ * deflateInit2(zstrmptr, 6, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY); * -------------------------------------------------------------------------- * - * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH. + * Copyright 2008-2013 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -70,6 +70,7 @@ #include "glbl.h" #include "stream.h" #include "zlibw.h" +#include "cryprov.h" /* stream types */ typedef enum { @@ -134,6 +135,9 @@ typedef struct strm_s { pthread_cond_t isEmpty; unsigned short iEnq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */ unsigned short iDeq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */ + cryprov_if_t *cryprov; /* ptr to crypto provider; NULL = do not encrypt */ + void *cryprovData; /* opaque data ptr for provider use */ + void *cryprovFileData;/* opaque data ptr for file instance */ short iCnt; /* current nbr of elements in buffer */ z_stream zstrm; /* zip stream to use */ struct { @@ -190,8 +194,11 @@ BEGINinterface(strm) /* name must also be changed in ENDinterface macro! */ INTERFACEpropSetMeth(strm, bVeryReliableZip, int); /* v8 added 2013-03-21 */ rsRetVal (*CheckFileChange)(strm_t *pThis); + /* v9 added 2013-04-04 */ + INTERFACEpropSetMeth(strm, cryprov, cryprov_if_t*); + INTERFACEpropSetMeth(strm, cryprovData, void*); ENDinterface(strm) -#define strmCURR_IF_VERSION 8 /* increment whenever you change the interface structure! */ +#define strmCURR_IF_VERSION 9 /* increment whenever you change the interface structure! */ static inline int strmGetCurrFileNum(strm_t *pStrm) { diff --git a/tools/Makefile.am b/tools/Makefile.am index 21a32868..be093957 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -41,7 +41,7 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) # otherwise dependencies are not properly calculated (resulting in a # potentially incomplete build, a problem we had several times...) rsyslogd_LDADD = ../grammar/libgrammar.la ../runtime/librsyslog.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(LIBUUID_LIBS) -rsyslogd_LDFLAGS = -export-dynamic +rsyslogd_LDFLAGS = -export-dynamic `libgcrypt-config --libs` EXTRA_DIST = $(man_MANS) \ rsgtutil.rst \ diff --git a/tools/omfile.c b/tools/omfile.c index faf3c24f..dfd52d80 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -70,6 +70,7 @@ #include "atomic.h" #include "statsobj.h" #include "sigprov.h" +#include "cryprov.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP @@ -151,6 +152,12 @@ typedef struct _instanceData { void *sigprovData; /* opaque data ptr for provider use */ void *sigprovFileData;/* opaque data ptr for file instance */ sbool useSigprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ + uchar *cryprovName; /* crypto provider */ + uchar *cryprovNameFull;/* full internal crypto provider name */ + void *cryprovData; /* opaque data ptr for provider use */ + void *cryprovFileData;/* opaque data ptr for file instance */ + cryprov_if_t cryprov; /* ptr to crypto provider interface */ + sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ int iCurrElt; /* currently active cache element (-1 = none) */ int iCurrCacheSize; /* currently cache size (1-based) */ int iDynaFileCacheSize; /* size of file handle cache */ @@ -237,6 +244,7 @@ static struct cnfparamdescr actpdescr[] = { { "file", eCmdHdlrString, 0 }, /* either "file" or ... */ { "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */ { "sig.provider", eCmdHdlrGetWord, 0 }, + { "cry.provider", eCmdHdlrGetWord, 0 }, { "template", eCmdHdlrGetWord, 0 } }; static struct cnfparamblk actpblk = @@ -589,6 +597,10 @@ prepareFile(instanceData *pData, uchar *newFileName) CHKiRet(strm.SetbSync(pData->pStrm, pData->bSyncFile)); CHKiRet(strm.SetsType(pData->pStrm, STREAMTYPE_FILE_SINGLE)); CHKiRet(strm.SetiSizeLimit(pData->pStrm, pData->iSizeLimit)); + if(pData->useCryprov) { + CHKiRet(strm.Setcryprov(pData->pStrm, &pData->cryprov)); + CHKiRet(strm.SetcryprovData(pData->pStrm, pData->cryprovData)); + } /* set the flush interval only if we actually use it - otherwise it will activate * async processing, which is a real performance waste if we do not do buffered * writes! -- rgerhards, 2009-07-06 @@ -689,7 +701,7 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) * but it could be triggered in the common case of a failed open() system call. * rgerhards, 2010-03-22 */ - pData->pStrm = pData->sigprovFileData = NULL; + pData->pStrm = NULL, pData->sigprovFileData = NULL; if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) { /* there is space left, so set it to that index */ @@ -885,6 +897,13 @@ CODESTARTfreeInstance free(pData->sigprovName); free(pData->sigprovNameFull); } + if(pData->useCryprov) { + pData->cryprov.Destruct(&pData->cryprovData); + obj.ReleaseObj(__FILE__, pData->cryprovNameFull+2, pData->cryprovNameFull, + (void*) &pData->cryprov); + free(pData->cryprovName); + free(pData->cryprovNameFull); + } ENDfreeInstance @@ -951,7 +970,9 @@ setInstParamDefaults(instanceData *pData) pData->iFlushInterval = FLUSH_INTRVL_DFLT; pData->bUseAsyncWriter = USE_ASYNCWRITER_DFLT; pData->sigprovName = NULL; + pData->cryprovName = NULL; pData->useSigprov = 0; + pData->useCryprov = 0; } @@ -1033,6 +1054,48 @@ initSigprov(instanceData *pData, struct nvlst *lst) done: return; } +static inline void +initCryprov(instanceData *pData, struct nvlst *lst) +{ + uchar szDrvrName[1024]; + + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pData->cryprovName) + == sizeof(szDrvrName)) { + errmsg.LogError(0, RS_RET_ERR, "omfile: crypto provider " + "name is too long: '%s' - encryption disabled", + pData->cryprovName); + goto done; + } + pData->cryprovNameFull = ustrdup(szDrvrName); + + pData->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*) &pData->cryprov) + != RS_RET_OK) { + errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load " + "crypto provider '%s' - encryption disabled", + szDrvrName); + goto done; + } + + if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) { + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "omfile: error constructing " + "crypto provider %s dataset - encryption disabled", + szDrvrName); + goto done; + } + pData->cryprov.SetCnfParam(pData->cryprovData, lst); + + dbgprintf("loaded crypto provider %s, data instance at %p\n", + szDrvrName, pData->cryprovData); + pData->useCryprov = 1; +done: return; +} + BEGINnewActInst struct cnfparamvals *pvals; uchar *tplToUse; @@ -1102,6 +1165,8 @@ CODESTARTnewActInst pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(actpblk.descr[i].name, "sig.provider")) { pData->sigprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "cry.provider")) { + pData->cryprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else { dbgprintf("omfile: program error, non-handled " "param '%s'\n", actpblk.descr[i].name); @@ -1118,6 +1183,10 @@ CODESTARTnewActInst initSigprov(pData, lst); } + if(pData->cryprovName != NULL) { + initCryprov(pData, lst); + } + tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS)); -- cgit v1.2.3 From 5242a0b4a351d41ea6f20adc359bbcde8e1b3cfe Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 5 Apr 2013 18:52:52 +0200 Subject: log encryption: add rscrytool to decrypt log files also add test parameter to be able to set key --- runtime/libgcry.c | 27 ++++-- runtime/libgcry.h | 4 +- runtime/lmcry_gcry.c | 31 ++++--- runtime/stream.c | 5 +- tools/Makefile.am | 17 +++- tools/omfile.c | 18 ++-- tools/rscryutil.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/rscryutil.rst | 80 +++++++++++++++++ 8 files changed, 386 insertions(+), 32 deletions(-) create mode 100644 tools/rscryutil.c create mode 100644 tools/rscryutil.rst diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 8184c160..5f1dbf58 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -27,6 +27,7 @@ #include "rsyslog.h" #include "libgcry.h" +#define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable static inline gcryfile gcryfileConstruct(gcryctx ctx) @@ -98,20 +99,34 @@ removePadding(char *buf, size_t *plen) done: return; } +/* returns 0 on succes, positive if key length does not match and key + * of return value size is required. + */ +int +rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) +{ + uint16_t reqKeyLen = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + int r; + + if(keyLen != reqKeyLen) + r = reqKeyLen; + ctx->keyLen = keyLen; + ctx->key = malloc(keyLen); + memcpy(ctx->key, key, keyLen); + r = 0; +done: return r; +} + rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) { - #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable - size_t keyLength; - char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY - gcry_error_t gcryError; + gcry_error_t gcryError; gcryfile gf = NULL; DEFiRet; CHKmalloc(gf = gcryfileConstruct(ctx)); gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); - keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); gcryError = gcry_cipher_open( &gf->chd, // gcry_cipher_hd_t * @@ -125,7 +140,7 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) ABORT_FINALIZE(RS_RET_ERR); } - gcryError = gcry_cipher_setkey(gf->chd, aesSymKey, keyLength); + 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), diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 0405162f..608abd6c 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -24,7 +24,8 @@ struct gcryctx_s { - void *usrptr; /* for error function */ + uchar *key; + size_t keyLen; }; typedef struct gcryctx_s *gcryctx; typedef struct gcryfile_s *gcryfile; @@ -38,6 +39,7 @@ struct gcryfile_s { int rsgcryInit(void); void rsgcryExit(void); +int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); gcryctx gcryCtxNew(void); void rsgcryCtxDel(gcryctx ctx); int gcryfileDestruct(gcryfile gf); diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 6800055d..ce0fef2f 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -44,6 +44,7 @@ DEFobjCurrIf(glbl) /* tables for interfacing with the v6 config system */ static struct cnfparamdescr cnfpdescr[] = { + { "cry.key", eCmdHdlrGetWord, 0 }, { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ { "cry.algo", eCmdHdlrGetWord, 0 } }; @@ -83,12 +84,13 @@ ENDobjDestruct(lmcry_gcry) * after construction, but before the OnFileOpen() entry point. * Defaults are expected to have been set during construction. */ -rsRetVal +static rsRetVal SetCnfParam(void *pT, struct nvlst *lst) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; - int i; + int i, r; uchar *cstr; + uchar *key = NULL; struct cnfparamvals *pvals; pvals = nvlstGetParams(lst, &pblk, NULL); if(Debug) { @@ -99,14 +101,9 @@ SetCnfParam(void *pT, struct nvlst *lst) for(i = 0 ; i < pblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; + if(!strcmp(pblk.descr[i].name, "cry.key")) { + key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); #if 0 - if(!strcmp(pblk.descr[i].name, "sig.hashfunction")) { - cstr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); - if(gcrySetHashFunction(pThis->ctx, (char*)cstr) != 0) { - errmsg.LogError(0, RS_RET_ERR, "Hash function " - "'%s' unknown - using default", cstr); - } - free(cstr); } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); gcrySetTimestamper(pThis->ctx, (char*) cstr); @@ -120,10 +117,24 @@ SetCnfParam(void *pT, struct nvlst *lst) } else { DBGPRINTF("lmcry_gcry: program error, non-handled " "param '%s'\n", pblk.descr[i].name); - } #endif + } } + if(key != NULL) { + errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the " + "config file is highly insecure - DO NOT USE FOR PRODUCTION"); + r = rsgcrySetKey(pThis->ctx, key, strlen((char*)key)); + if(r > 0) { + errmsg.LogError(0, RS_RET_ERR, "Key length %d expected, but " + "key of length %d given", r, strlen((char*)key)); + } + } + cnfparamvalsDestruct(pvals, &pblk); + if(key != NULL) { + memset(key, 0, strlen((char*)key)); + free(key); + } return RS_RET_OK; } diff --git a/runtime/stream.c b/runtime/stream.c index 941fc39d..b31520b0 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -256,9 +256,8 @@ doPhysOpen(strm_t *pThis) dbgprintf("DDDD: cryprov %p\n", pThis->cryprov); if(pThis->cryprov != NULL) { - iRet = pThis->cryprov->OnFileOpen(pThis->cryprovData, - pThis->pszCurrFName, &pThis->cryprovFileData); -dbgprintf("DDDD: iREt cryprov->onFileOpen: %d\n", iRet); + CHKiRet(pThis->cryprov->OnFileOpen(pThis->cryprovData, + pThis->pszCurrFName, &pThis->cryprovFileData)); } finalize_it: RETiRet; diff --git a/tools/Makefile.am b/tools/Makefile.am index be093957..8957d713 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -42,6 +42,7 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) # potentially incomplete build, a problem we had several times...) rsyslogd_LDADD = ../grammar/libgrammar.la ../runtime/librsyslog.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(LIBUUID_LIBS) rsyslogd_LDFLAGS = -export-dynamic `libgcrypt-config --libs` +#rsyslogd_LDFLAGS = -export-dynamic $(LIBGCRYPT_LIBS) EXTRA_DIST = $(man_MANS) \ rsgtutil.rst \ @@ -64,10 +65,6 @@ logctl_LDADD = $(LIBMONGO_CLIENT_LIBS) endif if ENABLE_GUARDTIME bin_PROGRAMS += rsgtutil -#bin_PROGRAMS += logsigner rsgtutil -#logsigner = logsigner.c -#logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) -#logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) rsgtutil = rsgtutil.c rsgtutil_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) rsgtutil_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) @@ -77,4 +74,16 @@ man1_MANS = rsgtutil.1 CLEANFILES = rsgtutil.1 EXTRA_DIST+= rsgtutil.1 endif +if ENABLE_LIBGCRYPT +bin_PROGRAMS += rscryutil +rscryutil = rscryutil.c +rscryutil_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) +rscryutil_LDFLAGS = `libgcrypt-config --libs` +#rscryutil_LDFLAGS = $(LIBGCRYPT_LIBS) +rscryutil.1: rscryutil.rst + $(AM_V_GEN) $(RST2MAN) $< $@ +man1_MANS = rscryutil.1 +CLEANFILES = rscryutil.1 +EXTRA_DIST+= rscryutil.1 +endif endif diff --git a/tools/omfile.c b/tools/omfile.c index dfd52d80..46d882bf 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -1054,17 +1054,18 @@ initSigprov(instanceData *pData, struct nvlst *lst) done: return; } -static inline void +static inline rsRetVal initCryprov(instanceData *pData, struct nvlst *lst) { uchar szDrvrName[1024]; + DEFiRet; if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pData->cryprovName) == sizeof(szDrvrName)) { errmsg.LogError(0, RS_RET_ERR, "omfile: crypto provider " "name is too long: '%s' - encryption disabled", pData->cryprovName); - goto done; + ABORT_FINALIZE(RS_RET_ERR); } pData->cryprovNameFull = ustrdup(szDrvrName); @@ -1079,21 +1080,22 @@ initCryprov(instanceData *pData, struct nvlst *lst) errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load " "crypto provider '%s' - encryption disabled", szDrvrName); - goto done; + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); } if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) { - errmsg.LogError(0, RS_RET_SIGPROV_ERR, "omfile: error constructing " + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "omfile: error constructing " "crypto provider %s dataset - encryption disabled", szDrvrName); - goto done; + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); } - pData->cryprov.SetCnfParam(pData->cryprovData, lst); + CHKiRet(pData->cryprov.SetCnfParam(pData->cryprovData, lst)); dbgprintf("loaded crypto provider %s, data instance at %p\n", szDrvrName, pData->cryprovData); pData->useCryprov = 1; -done: return; +finalize_it: + RETiRet; } BEGINnewActInst @@ -1184,7 +1186,7 @@ CODESTARTnewActInst } if(pData->cryprovName != NULL) { - initCryprov(pData, lst); + CHKiRet(initCryprov(pData, lst)); } tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); diff --git a/tools/rscryutil.c b/tools/rscryutil.c new file mode 100644 index 00000000..755371f2 --- /dev/null +++ b/tools/rscryutil.c @@ -0,0 +1,236 @@ +/* This is a tool for dumpoing the content of GuardTime TLV + * files in a (somewhat) human-readable manner. + * + * 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 exprs or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +static enum { MD_DECRYPT +} mode = MD_DECRYPT; +static int verbose = 0; +static gcry_cipher_hd_t gcry_chd; +static size_t blkLength; + +static int +initCrypt(int gcry_mode, char *iv, char *key) +{ + #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable + int r = 0; + gcry_error_t gcryError; + + blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + if(strlen(key) != keyLength) { + fprintf(stderr, "invalid key lengtjh; key is %u characters, but " + "exactly %u characters are required\n", strlen(key), + keyLength); + r = 1; goto done; + } + + gcryError = gcry_cipher_open(&gcry_chd, GCRY_CIPHER, gcry_mode, 0); + if (gcryError) { + printf("gcry_cipher_open failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } + + gcryError = gcry_cipher_setkey(gcry_chd, key, keyLength); + if (gcryError) { + printf("gcry_cipher_setkey failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } + + gcryError = gcry_cipher_setiv(gcry_chd, iv, blkLength); + if (gcryError) { + printf("gcry_cipher_setiv failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } +done: return r; +} + +static inline void +removePadding(char *buf, size_t *plen) +{ + unsigned len = (unsigned) *plen; + unsigned iSrc, iDst; + char *frstNUL; + + frstNUL = strchr(buf, 0x00); + if(frstNUL == NULL) + goto done; + iDst = iSrc = frstNUL - buf; + + while(iSrc < len) { + if(buf[iSrc] != 0x00) + buf[iDst++] = buf[iSrc]; + ++iSrc; + } + + *plen = iDst; +done: return; +} + +static void +doDeCrypt(FILE *fpin, FILE *fpout) +{ + gcry_error_t gcryError; + char buf[64*1024]; + size_t nRead, nWritten; + size_t nPad; + + while(1) { + nRead = fread(buf, 1, sizeof(buf), fpin); + if(nRead == 0) + break; + nPad = (blkLength - nRead % blkLength) % blkLength; + fprintf(stderr, "--->read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, + nRead % blkLength, nPad); + gcryError = gcry_cipher_decrypt( + gcry_chd, // gcry_cipher_hd_t + buf, // void * + nRead, // size_t + NULL, // const void * + 0); // size_t + if (gcryError) { + fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + return; + } + removePadding(buf, &nRead); + nWritten = fwrite(buf, 1, nRead, fpout); + if(nWritten != nRead) { + perror("fpout"); + return; + } + } +} + + +static void +decrypt(char *name, char *key) +{ + FILE *logfp = NULL; + //, *sigfp = NULL; + int r = 0; + //char sigfname[4096]; + + if(!strcmp(name, "-")) { + fprintf(stderr, "decrypt mode cannot work on stdin\n"); + goto err; + } else { + if((logfp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } +#if 0 + snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name); + sigfname[sizeof(sigfname)-1] = '\0'; + if((sigfp = fopen(sigfname, "r")) == NULL) { + perror(sigfname); + goto err; + } +#endif + } + + if(initCrypt(GCRY_CIPHER_MODE_CBC, "TODO: init value", key) != 0) + goto err; + doDeCrypt(logfp, stdout); + gcry_cipher_close(gcry_chd); + fclose(logfp); logfp = NULL; + return; + +err: + fprintf(stderr, "error %d processing file %s\n", r, name); + if(logfp != NULL) + fclose(logfp); +} + + +static struct option long_options[] = +{ + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"decrypt", no_argument, NULL, 'd'}, + {"key", required_argument, NULL, 'k'}, + {NULL, 0, NULL, 0} +}; + +int +main(int argc, char *argv[]) +{ + int i; + int opt; + char *key = ""; + + while(1) { + opt = getopt_long(argc, argv, "dk:vV", long_options, NULL); + if(opt == -1) + break; + switch(opt) { + case 'd': + mode = MD_DECRYPT; + break; + case 'k': + fprintf(stderr, "WARNING: specifying the actual key " + "via the command line is highly insecure\n" + "Do NOT use this for PRODUCTION use.\n"); + key = optarg; + break; + case 'v': + verbose = 1; + break; + case 'V': + fprintf(stderr, "rsgtutil " VERSION "\n"); + exit(0); + break; + case '?': + break; + default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt); + return 1; + } + } + + if(optind == argc) + decrypt("-", key); + else { + for(i = optind ; i < argc ; ++i) + decrypt(argv[i], key); /* currently only mode ;) */ + } + + memset(key, 0, strlen(key)); /* zero-out key store */ + return 0; +} + //char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst new file mode 100644 index 00000000..7e3ab5b4 --- /dev/null +++ b/tools/rscryutil.rst @@ -0,0 +1,80 @@ +========= +rscryutil +========= + +-------------------------- +Manage Encrypted Log Files +-------------------------- + +:Author: Rainer Gerhards +:Date: 2013-04-08 +:Manual section: 1 + +SYNOPSIS +======== + +:: + + rscryutil [OPTIONS] [FILE] ... + + +DESCRIPTION +=========== + +This tool performs various operations on encrypted log files. +Most importantly, it provides the ability to decrypt them. + + +OPTIONS +======= + +-d, --decrypt + Select decryption mode. This is the default mode. + +-v, --verbose + Select verbose mode. + +-k, --key + TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified + on the command line. This is the actual key, and as such this mode + is highly insecure. However, it can be useful for intial testing + steps. This option may be removed in the future. + + +OPERATION MODES +=============== + +The operation mode specifies what exactly the tool does with the provided +files. The default operation mode is "dump", but this may change in the future. +Thus, it is recommended to always set the operations mode explicitely. If +multiple operations mode are set on the command line, results are +unpredictable. + +decrypt +------- + +The provided log files are decrypted. + +EXIT CODES +========== + +The command returns an exit code of 0 if everything went fine, and some +other code in case of failures. + + +EXAMPLES +======== + +**rscryutil logfile** + +Decrypts "logfile" and sends data to stdout. + +SEE ALSO +======== +**rsyslogd(8)** + +COPYRIGHT +========= + +This page is part of the *rsyslog* project, and is available under +LGPLv2. -- cgit v1.2.3 From 2de4a04b7e728f533382c9839345a89cbf73db89 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Sun, 7 Apr 2013 18:48:48 +0200 Subject: bugfix: failover/action suspend did not work correctly This was experienced if the retry action took more than one second to complete. For suspending, a cached timestamp was used, and if the retry took longer, that timestamp was already in the past. As a result, the action never was kept in suspended state, and as such no failover happened. The suspend functionalit now does no longer use the cached timestamp (should not have any performance implication, as action suspend occurs very infrequently). Also added some better debug logging for the action engine. --- ChangeLog | 8 ++++++++ action.c | 30 ++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 854a14d3..c4546db6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,14 @@ Version 7.2.7 [v7-stable] 2013-03-?? The rsyslog debug log file is now continued to be written across the fork. - updated systemd files to match current systemd source +- bugfix: failover/action suspend did not work correctly + This was experienced if the retry action took more than one second + to complete. For suspending, a cached timestamp was used, and if the + retry took longer, that timestamp was already in the past. As a + result, the action never was kept in suspended state, and as such + no failover happened. The suspend functionalit now does no longer use + the cached timestamp (should not have any performance implication, as + action suspend occurs very infrequently). - bugfix: nested if/prifilt conditions did not work properly closes: http://bugzilla.adiscon.com/show_bug.cgi?id=415 - bugfix: script == comparison did not work properly on JSON objects diff --git a/action.c b/action.c index fd8727ee..8fc92e56 100644 --- a/action.c +++ b/action.c @@ -613,13 +613,17 @@ static void actionDisable(action_t *pThis) * CPU time. TODO: maybe a config option for that? * rgerhards, 2007-08-02 */ -static inline void actionSuspend(action_t *pThis, time_t ttNow) +static inline void actionSuspend(action_t *pThis) { - if(ttNow == NO_TIME_PROVIDED) - datetime.GetTime(&ttNow); + time_t ttNow; + + /* note: we can NOT use a cached timestamp, as time may have evolved + * since caching, and this would break logic (and it actually did so!) + */ + datetime.GetTime(&ttNow); pThis->ttResumeRtry = ttNow + pThis->iResumeInterval * (pThis->iNbrResRtry / 10 + 1); actionSetState(pThis, ACT_STATE_SUSP); - DBGPRINTF("earliest retry=%d\n", (int) pThis->ttResumeRtry); + DBGPRINTF("action suspended, earliest retry=%d\n", (int) pThis->ttResumeRtry); } @@ -639,7 +643,7 @@ static inline void actionSuspend(action_t *pThis, time_t ttNow) * of its inability to recover. -- rgerhards, 2010-04-26. */ static inline rsRetVal -actionDoRetry(action_t *pThis, time_t ttNow, int *pbShutdownImmediate) +actionDoRetry(action_t *pThis, int *pbShutdownImmediate) { int iRetries; int iSleepPeriod; @@ -650,7 +654,9 @@ actionDoRetry(action_t *pThis, time_t ttNow, int *pbShutdownImmediate) iRetries = 0; while((*pbShutdownImmediate == 0) && pThis->eState == ACT_STATE_RTRY) { + DBGPRINTF("actionDoRetry: enter loop, iRetries=%d\n", iRetries); iRet = pThis->pMod->tryResume(pThis->pModData); + DBGPRINTF("actionDoRetry: action->tryResume returned %d\n", iRet); if((pThis->iResumeOKinRow > 9) && (pThis->iResumeOKinRow % 10 == 0)) { bTreatOKasSusp = 1; pThis->iResumeOKinRow = 0; @@ -658,16 +664,18 @@ actionDoRetry(action_t *pThis, time_t ttNow, int *pbShutdownImmediate) bTreatOKasSusp = 0; } if((iRet == RS_RET_OK) && (!bTreatOKasSusp)) { + DBGPRINTF("actionDoRetry: had success RDY again (iRet=%d)\n", iRet); actionSetState(pThis, ACT_STATE_RDY); } else if(iRet == RS_RET_SUSPENDED || bTreatOKasSusp) { /* max retries reached? */ + DBGPRINTF("actionDoRetry: check for max retries, iResumeRetryCount %d, iRetries %d\n", + pThis->iResumeRetryCount, iRetries); if((pThis->iResumeRetryCount != -1 && iRetries >= pThis->iResumeRetryCount)) { - actionSuspend(pThis, ttNow); + actionSuspend(pThis); } else { ++pThis->iNbrResRtry; ++iRetries; iSleepPeriod = pThis->iResumeInterval; - ttNow += iSleepPeriod; /* not truly exact, but sufficiently... */ srSleep(iSleepPeriod, 0); if(*pbShutdownImmediate) { ABORT_FINALIZE(RS_RET_FORCE_TERM); @@ -714,7 +722,7 @@ static rsRetVal actionTryResume(action_t *pThis, int *pbShutdownImmediate) if(pThis->eState == ACT_STATE_RTRY) { if(ttNow == NO_TIME_PROVIDED) /* use cached result if we have it */ datetime.GetTime(&ttNow); - CHKiRet(actionDoRetry(pThis, ttNow, pbShutdownImmediate)); + CHKiRet(actionDoRetry(pThis, pbShutdownImmediate)); } if(Debug && (pThis->eState == ACT_STATE_RTRY ||pThis->eState == ACT_STATE_SUSP)) { @@ -1125,6 +1133,7 @@ submitBatch(action_t *pAction, batch_t *pBatch, int nElem) assert(pBatch != NULL); + DBGPRINTF("submitBatch: enter, nElem %d\n", nElem); wasDoneTo = pBatch->iDoneUpTo; bDone = 0; do { @@ -1146,7 +1155,8 @@ submitBatch(action_t *pAction, batch_t *pBatch, int nElem) || localRet == RS_RET_DEFER_COMMIT) { bDone = 1; } else if(localRet == RS_RET_SUSPENDED) { - ; /* do nothing, this will retry the full batch */ + DBGPRINTF("action ret RS_RET_SUSPENDED - retry full batch\n"); + /* do nothing, this will retry the full batch */ } else if(localRet == RS_RET_ACTION_FAILED) { /* in this case, everything not yet committed is BAD */ for(i = pBatch->iDoneUpTo ; i < wasDoneTo + nElem ; ++i) { @@ -1974,7 +1984,7 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, pAction->eState = ACT_STATE_RDY; /* action is enabled */ if(bSuspended) - actionSuspend(pAction, datetime.GetTime(NULL)); /* "good" time call, only during init and unavoidable */ + actionSuspend(pAction); CHKiRet(actionConstructFinalize(pAction, queueParams)); -- cgit v1.2.3 From 610137b49d5e4f8a0d448d8d361c042cf451a550 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 8 Apr 2013 09:37:10 +0200 Subject: omrelp: add support for RainerScript-based configuration --- ChangeLog | 1 + doc/omrelp.html | 37 ++++++++++---- plugins/omrelp/omrelp.c | 132 +++++++++++++++++++++++++++++++++++------------- runtime/rsyslog.h | 4 +- 4 files changed, 128 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index c4546db6..e10f6c5d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +- omrelp: added support for RainerScript-based configuration ---------------------------------------------------------------------------- Version 7.2.7 [v7-stable] 2013-03-?? - rsyslogd startup information is now properly conveyed back to init diff --git a/doc/omrelp.html b/doc/omrelp.html index 22e6845f..4b28bdda 100644 --- a/doc/omrelp.html +++ b/doc/omrelp.html @@ -16,10 +16,30 @@ RELP protocol. For RELP's advantages over plain tcp syslog, please see the documentation for imrelp (the server counterpart). 

      Setup -

      Please note the librelp +

      Please note that librelp is required for imrelp (it provides the core relp protocol implementation).

      -

      Configuration Directives:

      +

      Action Configuration Parameters:

      +

      This module supports RainerScript configuration starting with +rsyslog 7.3.10. For older versions, legacy configuration directives +must be used. +

        +
      • target (mandatory)
        + The target server to connect to. +
      • +
      • template (not mandatory, default "RSYSLOG_ForwardFormat")
        + Defines the template to be used for the output. +
      • +
      +

      Sample:

      +

      The following sample sends all messages to the central server +"centralserv" at port 2514 (note that that server must run imrelp on +port 2514). +

      + +

      Legacy Configuration Directives:

      This module uses old-style action configuration to keep consistent with the forwarding rule. So far, no additional configuration directives can be specified. To send a message via RELP, @@ -33,18 +53,15 @@ use

      Caveats/Known Bugs:

      See imrelp, which documents them. 

      -

      Sample:

      +

      Legacy Sample:

      The following sample sends all messages to the central server "centralserv" at port 2514 (note that that server must run imrelp on -port 2514). Rsyslog's high-precision timestamp format is used, thus the -special "RSYSLOG_ForwardFormat" (case sensitive!) template is used.
      +port 2514).

      - -Note: to use IPv6 addresses, encode them in [::1] format. +

      Note: to use IPv6 addresses, encode them in [::1] format.

      [rsyslog.conf overview] [manual index] [rsyslog site]

      This documentation is part of the diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index e55836c5..0c296673 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -7,7 +7,7 @@ * * File begun on 2008-03-13 by RGerhards * - * Copyright 2008-2012 Adiscon GmbH. + * Copyright 2008-2013 Adiscon GmbH. * * This file is part of rsyslog. * @@ -43,6 +43,7 @@ #include "glbl.h" #include "errmsg.h" #include "debug.h" +#include "unicode-helper.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP @@ -57,12 +58,13 @@ DEFobjCurrIf(glbl) static relpEngine_t *pRelpEngine; /* our relp engine */ typedef struct _instanceData { - char *f_hname; + uchar *target; int compressionLevel; /* 0 - no compression, else level for zlib */ - char *port; + uchar *port; int bInitialConnect; /* is this the initial connection request of our module? (0-no, 1-yes) */ int bIsConnected; /* currently connected to server? 0 - no, 1 - yes */ relpClt_t *pRelpClt; /* relp client for this instance */ + uchar *tplName; } instanceData; typedef struct configSettings_s { @@ -70,56 +72,119 @@ typedef struct configSettings_s { } configSettings_t; static configSettings_t __attribute__((unused)) cs; + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "target", eCmdHdlrGetWord, 0 }, + { "port", eCmdHdlrGetWord, 0 }, + { "template", eCmdHdlrGetWord, 1 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars ENDinitConfVars -/* get the syslog forward port from selector_t. The passed in - * struct must be one that is setup for forwarding. - * rgerhards, 2007-06-28 - * We may change the implementation to try to lookup the port - * if it is unspecified. So far, we use the IANA default auf 514. +/* We may change the implementation to try to lookup the port + * if it is unspecified. So far, we use 514 as default (what probably + * is not a really bright idea, but kept for backward compatibility). */ -static char *getRelpPt(instanceData *pData) +static uchar *getRelpPt(instanceData *pData) { assert(pData != NULL); if(pData->port == NULL) - return("514"); + return((uchar*)"514"); else return(pData->port); } +static inline rsRetVal +doCreateRelpClient(instanceData *pData) +{ + DEFiRet; + if(relpEngineCltConstruct(pRelpEngine, &pData->pRelpClt) != RELP_RET_OK) + ABORT_FINALIZE(RS_RET_RELP_ERR); +finalize_it: + RETiRet; +} + + BEGINcreateInstance CODESTARTcreateInstance pData->bInitialConnect = 1; ENDcreateInstance - -BEGINisCompatibleWithFeature -CODESTARTisCompatibleWithFeature - if(eFeat == sFEATURERepeatedMsgReduction) - iRet = RS_RET_OK; -ENDisCompatibleWithFeature - - BEGINfreeInstance CODESTARTfreeInstance - if(pData->port != NULL) - free(pData->port); - - /* final cleanup */ if(pData->pRelpClt != NULL) relpEngineCltDestruct(pRelpEngine, &pData->pRelpClt); + free(pData->target); + free(pData->port); + free(pData->tplName); +ENDfreeInstance - if(pData->f_hname != NULL) - free(pData->f_hname); +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->target = NULL; + pData->port = NULL; + pData->tplName = NULL; +} -ENDfreeInstance + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "target")) { + pData->target = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "port")) { + pData->port = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("omrelp: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + CODE_STD_STRING_REQUESTnewActInst(1) + + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*)strdup((pData->tplName == NULL) ? + "RSYSLOG_ForwardFormat" : (char*)pData->tplName), + OMSR_NO_RQD_TPL_OPTS)); + + CHKiRet(doCreateRelpClient(pData)); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature BEGINdbgPrintInstInfo CODESTARTdbgPrintInstInfo - printf("RELP/%s", pData->f_hname); + dbgprintf("RELP/%s", pData->target); ENDdbgPrintInstInfo @@ -131,7 +196,7 @@ static rsRetVal doConnect(instanceData *pData) DEFiRet; if(pData->bInitialConnect) { - iRet = relpCltConnect(pData->pRelpClt, glbl.GetDefPFFamily(), (uchar*) pData->port, (uchar*) pData->f_hname); + iRet = relpCltConnect(pData->pRelpClt, glbl.GetDefPFFamily(), pData->port, pData->target); if(iRet == RELP_RET_OK) pData->bInitialConnect = 0; } else { @@ -160,7 +225,7 @@ BEGINdoAction size_t lenMsg; relpRetVal ret; CODESTARTdoAction - dbgprintf(" %s:%s/RELP\n", pData->f_hname, getRelpPt(pData)); + dbgprintf(" %s:%s/RELP\n", pData->target, getRelpPt(pData)); if(!pData->bIsConnected) { CHKiRet(doConnect(pData)); @@ -309,21 +374,17 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* TODO: make this if go away! */ if(*p == ';') { *p = '\0'; /* trick to obtain hostname (later)! */ - CHKmalloc(pData->f_hname = strdup((char*) q)); + CHKmalloc(pData->target = ustrdup(q)); *p = ';'; } else { - CHKmalloc(pData->f_hname = strdup((char*) q)); + CHKmalloc(pData->target = ustrdup(q)); } /* process template */ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) "RSYSLOG_ForwardFormat")); - /* create our relp client */ - CHKiRet(relpEngineCltConstruct(pRelpEngine, &pData->pRelpClt)); /* we use CHKiRet as librelp has a similar return value range */ + CHKiRet(doCreateRelpClient(pData)); - /* TODO: do we need to call freeInstance if we failed - this is a general question for - * all output modules. I'll address it later as the interface evolves. rgerhards, 2007-07-25 - */ CODE_STD_FINALIZERparseSelectorAct ENDparseSelectorAct @@ -342,6 +403,7 @@ BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 07d58d68..53eb1edb 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -384,7 +384,9 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_MOD_NO_INPUT_STMT = -2224, /**< (input) module does not support input() statement */ RS_RET_NO_CEE_MSG = -2225, /**< the message being processed is NOT CEE-enhanced */ - /**** up to 2300 is reserved for v6 use ****/ + /**** up to 2290 is reserved for v6 use ****/ + RS_RET_RELP_ERR = -2291, /**<< error in RELP processing */ + /**** up to 3000 is reserved for c7 use ****/ RS_RET_JNAME_NO_ROOT = -2301, /**< root element is missing in JSON path */ RS_RET_JNAME_INVALID = -2302, /**< JSON path is invalid */ RS_RET_JSON_PARSE_ERR = -2303, /**< we had a problem parsing JSON (or extra data) */ -- cgit v1.2.3 From ba592431e279b3dafb74b93824a1478ff0d9724f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 8 Apr 2013 14:20:34 +0200 Subject: omrelp: add ability to specify session timeout --- ChangeLog | 1 + configure.ac | 2 +- doc/omrelp.html | 4 ++++ plugins/omrelp/omrelp.c | 11 +++++++++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index e10f6c5d..1cd37361 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ - omrelp: added support for RainerScript-based configuration +- omrelp: added ability to specify session timeout ---------------------------------------------------------------------------- Version 7.2.7 [v7-stable] 2013-03-?? - rsyslogd startup information is now properly conveyed back to init diff --git a/configure.ac b/configure.ac index 5c1fa991..e33a3d85 100644 --- a/configure.ac +++ b/configure.ac @@ -905,7 +905,7 @@ AC_ARG_ENABLE(relp, [enable_relp=no] ) if test "x$enable_relp" = "xyes"; then - PKG_CHECK_MODULES(RELP, relp >= 1.0.1) + PKG_CHECK_MODULES(RELP, relp >= 1.0.3) fi AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes) diff --git a/doc/omrelp.html b/doc/omrelp.html index 4b28bdda..8858f884 100644 --- a/doc/omrelp.html +++ b/doc/omrelp.html @@ -30,6 +30,10 @@ must be used.

    • template (not mandatory, default "RSYSLOG_ForwardFormat")
      Defines the template to be used for the output.
    • +
    • timeout (not mandatory, default 90)
      + Timeout for relp sessions. If set too low, valid sessions + may be considered dead and tried to recover. +

    Sample:

    The following sample sends all messages to the central server diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index 0c296673..7d536cbc 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -63,7 +63,8 @@ typedef struct _instanceData { uchar *port; int bInitialConnect; /* is this the initial connection request of our module? (0-no, 1-yes) */ int bIsConnected; /* currently connected to server? 0 - no, 1 - yes */ - relpClt_t *pRelpClt; /* relp client for this instance */ + unsigned timeout; + relpClt_t *pRelpClt; /* relp client for this instance */ uchar *tplName; } instanceData; @@ -76,8 +77,9 @@ static configSettings_t __attribute__((unused)) cs; /* tables for interfacing with the v6 config system */ /* action (instance) parameters */ static struct cnfparamdescr actpdescr[] = { - { "target", eCmdHdlrGetWord, 0 }, + { "target", eCmdHdlrGetWord, 1 }, { "port", eCmdHdlrGetWord, 0 }, + { "timeout", eCmdHdlrInt, 0 }, { "template", eCmdHdlrGetWord, 1 } }; static struct cnfparamblk actpblk = @@ -109,6 +111,8 @@ doCreateRelpClient(instanceData *pData) DEFiRet; if(relpEngineCltConstruct(pRelpEngine, &pData->pRelpClt) != RELP_RET_OK) ABORT_FINALIZE(RS_RET_RELP_ERR); + if(relpCltSetTimeout(pData->pRelpClt, pData->timeout) != RELP_RET_OK) + ABORT_FINALIZE(RS_RET_RELP_ERR); finalize_it: RETiRet; } @@ -134,6 +138,7 @@ setInstParamDefaults(instanceData *pData) pData->target = NULL; pData->port = NULL; pData->tplName = NULL; + pData->timeout = 90; } @@ -157,6 +162,8 @@ CODESTARTnewActInst pData->port = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(actpblk.descr[i].name, "template")) { pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "timeout")) { + pData->timeout = (unsigned) pvals[i].val.d.n; } else { dbgprintf("omrelp: program error, non-handled " "param '%s'\n", actpblk.descr[i].name); -- cgit v1.2.3 From d9cde56eb8532bd660d6feb2249562afac0c40f6 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 8 Apr 2013 17:55:52 +0200 Subject: add output module interface to facilitate cooperative shutdown ... in more complex cases (where receiving SIGTTIN is not sufficient). See also: http://blog.gerhards.net/2013/04/rsyslog-output-plugin-wrangling.html --- action.c | 2 ++ plugins/omrelp/omrelp.c | 6 ++++++ runtime/module-template.h | 22 ++++++++++++++++++++++ runtime/modules.c | 4 ++++ runtime/modules.h | 1 + runtime/queue.c | 2 -- 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/action.c b/action.c index 8fc92e56..42d0e0ad 100644 --- a/action.c +++ b/action.c @@ -975,6 +975,8 @@ actionProcessMessage(action_t *pThis, msg_t *pMsg, void *actParams, int *pbShutd ISOBJ_TYPE_assert(pMsg, msg); CHKiRet(actionPrepare(pThis, pbShutdownImmediate)); + if(pThis->pMod->mod.om.SetShutdownImmdtPtr != NULL) + pThis->pMod->mod.om.SetShutdownImmdtPtr(pThis->pModData, pbShutdownImmediate); if(pThis->eState == ACT_STATE_ITX) CHKiRet(actionCallDoAction(pThis, pMsg, actParams)); diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index 7d536cbc..0374b697 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -188,6 +188,11 @@ CODESTARTisCompatibleWithFeature iRet = RS_RET_OK; ENDisCompatibleWithFeature +BEGINSetShutdownImmdtPtr +CODESTARTSetShutdownImmdtPtr + DBGPRINTF("omrelp: shutdownImmediate ptr now is %p\n", pPtr); +ENDSetShutdownImmdtPtr + BEGINdbgPrintInstInfo CODESTARTdbgPrintInstInfo @@ -411,6 +416,7 @@ CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +CODEqueryEtryPt_SetShutdownImmdtPtr ENDqueryEtryPt diff --git a/runtime/module-template.h b/runtime/module-template.h index fe74bac9..8a958f90 100644 --- a/runtime/module-template.h +++ b/runtime/module-template.h @@ -938,6 +938,28 @@ static rsRetVal doHUP(instanceData __attribute__((unused)) *pData)\ } +/* SetShutdownImmdtPtr() + * This function is optional. If defined by an output plugin, it is called + * each time the action is invoked to set the "ShutdownImmediate" pointer, + * which is used during termination to indicate the action should shutdown + * as quickly as possible. + */ +#define CODEqueryEtryPt_SetShutdownImmdtPtr \ + else if(!strcmp((char*) name, "SetShutdownImmdtPtr")) {\ + *pEtryPoint = SetShutdownImmdtPtr;\ + } +#define BEGINSetShutdownImmdtPtr \ +static rsRetVal SetShutdownImmdtPtr(instanceData __attribute__((unused)) *pData, int *pPtr)\ +{\ + DEFiRet; + +#define CODESTARTSetShutdownImmdtPtr + +#define ENDSetShutdownImmdtPtr \ + RETiRet;\ +} + + /* parse() - main entry point of parser modules */ #define BEGINparse \ diff --git a/runtime/modules.c b/runtime/modules.c index 9f7ff31c..e9d8d959 100644 --- a/runtime/modules.c +++ b/runtime/modules.c @@ -657,6 +657,10 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ if(localRet != RS_RET_OK && localRet != RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) ABORT_FINALIZE(localRet); + localRet = (*pNew->modQueryEtryPt)((uchar*)"SetShutdownImmdtPtr", &pNew->mod.om.SetShutdownImmdtPtr); + if(localRet != RS_RET_OK && localRet != RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) + ABORT_FINALIZE(localRet); + localRet = (*pNew->modQueryEtryPt)((uchar*)"beginTransaction", &pNew->mod.om.beginTransaction); if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) pNew->mod.om.beginTransaction = dummyBeginTransaction; diff --git a/runtime/modules.h b/runtime/modules.h index e42d19e1..64644be2 100644 --- a/runtime/modules.h +++ b/runtime/modules.h @@ -142,6 +142,7 @@ struct modInfo_s { rsRetVal (*endTransaction)(void*); rsRetVal (*parseSelectorAct)(uchar**, void**,omodStringRequest_t**); rsRetVal (*newActInst)(uchar *modName, struct nvlst *lst, void **, omodStringRequest_t **); + rsRetVal (*SetShutdownImmdtPtr)(void *pData, void *pPtr); } om; struct { /* data for library modules */ char dummy; diff --git a/runtime/queue.c b/runtime/queue.c index 8d8d8e0a..a464c2d7 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -1332,8 +1332,6 @@ finalize_it: RETiRet; } - - /* Constructor for the queue object * This constructs the data structure, but does not yet start the queue. That * is done by queueStart(). The reason is that we want to give the caller a chance -- cgit v1.2.3 From 86e34c6985da29c62f13ab83e44548f1fd21849d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 9 Apr 2013 10:54:04 +0200 Subject: make imrelp properly terminate on system shutdown it didn't do so if it was inside a retry loop --- action.c | 10 +++++++--- plugins/omrelp/omrelp.c | 1 + runtime/queue.c | 5 ++++- tools/syslogd.c | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/action.c b/action.c index 42d0e0ad..4e1a8713 100644 --- a/action.c +++ b/action.c @@ -1284,8 +1284,11 @@ processBatchMain(action_t *pAction, batch_t *pBatch, int *pbShutdownImmediate) assert(pBatch != NULL); - pbShutdownImmdtSave = pBatch->pbShutdownImmediate; - pBatch->pbShutdownImmediate = pbShutdownImmediate; + if(pbShutdownImmediate != NULL) { + pbShutdownImmdtSave = pBatch->pbShutdownImmediate; + pBatch->pbShutdownImmediate = pbShutdownImmediate; +dbgprintf("DDDD: processBatchMain ShutdownImmediate is %p, was %p\n", pBatch->pbShutdownImmediate, pbShutdownImmdtSave); + } CHKiRet(prepareBatch(pAction, pBatch, &activeSave, &bMustRestoreActivePtr)); /* We now must guard the output module against execution by multiple threads. The @@ -1316,7 +1319,8 @@ processBatchMain(action_t *pAction, batch_t *pBatch, int *pbShutdownImmediate) } finalize_it: - pBatch->pbShutdownImmediate = pbShutdownImmdtSave; + if(pbShutdownImmediate != NULL) + pBatch->pbShutdownImmediate = pbShutdownImmdtSave; RETiRet; } #pragma GCC diagnostic warning "-Wempty-body" diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index 0374b697..c9e32444 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -190,6 +190,7 @@ ENDisCompatibleWithFeature BEGINSetShutdownImmdtPtr CODESTARTSetShutdownImmdtPtr + relpEngineSetShutdownImmdtPtr(pRelpEngine, pPtr); DBGPRINTF("omrelp: shutdownImmediate ptr now is %p\n", pPtr); ENDSetShutdownImmdtPtr diff --git a/runtime/queue.c b/runtime/queue.c index a464c2d7..5c736d2c 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -1032,7 +1032,8 @@ rsRetVal qqueueEnqObjDirectBatch(qqueue_t *pThis, batch_t *pBatch) * We use our knowledge about the batch_t structure below, but without that, we * pay a too-large performance toll... -- rgerhards, 2009-04-22 */ - iRet = pThis->pConsumer(pThis->pUsr, pBatch, &pThis->bShutdownImmediate); +dbgprintf("DDDD: qqueueEnqObjDirectBatch\n"); + iRet = pThis->pConsumer(pThis->pUsr, pBatch, NULL); RETiRet; } @@ -1191,6 +1192,7 @@ RUNLOG_STR("trying to shutdown workers within Action Timeout"); /* instruct workers to finish ASAP, even if still work exists */ DBGOPRINT((obj_t*) pThis, "setting EnqOnly mode\n"); pThis->bEnqOnly = 1; +dbgprintf("DDDD: setting shutdownImmediate mode, ptr %p!\n", &pThis->bShutdownImmediate); pThis->bShutdownImmediate = 1; /* now DA queue */ if(pThis->bIsDA) { @@ -1872,6 +1874,7 @@ ConsumerReg(qqueue_t *pThis, wti_t *pWti) /* at this spot, we may be cancelled */ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &iCancelStateSave); +dbgprintf("DDDD: calling consumer with shutdownImmeditate ptr %p\n", &pThis->bShutdownImmediate); CHKiRet(pThis->pConsumer(pThis->pUsr, &pWti->batch, &pThis->bShutdownImmediate)); /* we now need to check if we should deliberately delay processing a bit diff --git a/tools/syslogd.c b/tools/syslogd.c index e2776c11..a0c01bf7 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -604,6 +604,7 @@ msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch, int *pbShu assert(pBatch != NULL); pBatch->pbShutdownImmediate = pbShutdownImmediate; /* TODO: move this to batch creation! */ preprocessBatch(pBatch); +dbgprintf("DDDD: batches ShutdownImmediate is %p\n", pBatch->pbShutdownImmediate); ruleset.ProcessBatch(pBatch); //TODO: the BATCH_STATE_COMM must be set somewhere down the road, but we //do not have this yet and so we emulate -- 2010-06-10 -- cgit v1.2.3 From c020cf51d14a9b80fbde9296ed4beb6d4c66e55e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 9 Apr 2013 16:03:14 +0200 Subject: bugfix in testbench I initially thought the testbench had found some regression, but it turned out that a bugfix fixed a situation that the testbench had (invalidly) relied on. I have now adapted the testbench to how things really need to be. --- tests/queue-persist-drvr.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/queue-persist-drvr.sh b/tests/queue-persist-drvr.sh index de597308..823fed6c 100755 --- a/tests/queue-persist-drvr.sh +++ b/tests/queue-persist-drvr.sh @@ -26,5 +26,10 @@ source $srcdir/diag.sh startup queue-persist.conf source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages ./msleep 500 $srcdir/diag.sh wait-shutdown -source $srcdir/diag.sh seq-check 0 4999 +# note: we need to permit duplicate messages, as due to the forced +# shutdown some messages may be flagged as "unprocessed" while they +# actually were processed. This is inline with rsyslog's philosophy +# to better duplicate than loose messages. Duplicate messages are +# permitted by the -d seq-check option. +source $srcdir/diag.sh seq-check 0 4999 -d source $srcdir/diag.sh exit -- cgit v1.2.3 From 9959f80ee64c0beb86338f94f41ef8a873ab38d1 Mon Sep 17 00:00:00 2001 From: Tomas Heinrich Date: Wed, 20 Mar 2013 19:13:59 +0100 Subject: Add new string functions that take a printf-style fmt --- runtime/stringbuf.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/stringbuf.h | 7 ++++++ 2 files changed, 79 insertions(+) diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c index d9f80231..75d2eac4 100644 --- a/runtime/stringbuf.c +++ b/runtime/stringbuf.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include "rsyslog.h" @@ -104,6 +105,56 @@ finalize_it: } +/* a helper function for rsCStr*Strf() + */ +static rsRetVal rsCStrConstructFromszStrv(cstr_t **ppThis, uchar *fmt, va_list ap) +{ + DEFiRet; + cstr_t *pThis; + va_list ap2; + uchar *sz; + int len; + + assert(ppThis != NULL); + + va_copy(ap2, ap); + len = vsnprintf(NULL, 0, fmt, ap2); + va_end(ap2); + + if(len < 0) + ABORT_FINALIZE(RS_RET_ERR); + + CHKiRet(rsCStrConstruct(&pThis)); + + pThis->iBufSize = pThis->iStrLen = len; + len++; /* account for the \0 written by vsnprintf */ + if((pThis->pBuf = (uchar*) MALLOC(sizeof(uchar) * len)) == NULL) { + RSFREEOBJ(pThis); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + vsnprintf(pThis->pBuf, len, fmt, ap); + *ppThis = pThis; +finalize_it: + RETiRet; +} + + +/* construct from a printf-style formated string + */ +rsRetVal rsCStrConstructFromszStrf(cstr_t **ppThis, uchar *fmt, ...) +{ + DEFiRet; + va_list ap; + + va_start(ap, fmt); + iRet = rsCStrConstructFromszStrv(ppThis, fmt, ap); + va_end(ap); + + RETiRet; +} + + /* construct from es_str_t string * rgerhards 2010-12-03 */ @@ -256,6 +307,27 @@ rsRetVal cstrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend) } +/* append a printf-style formated string + */ +rsRetVal rsCStrAppendStrf(cstr_t *pThis, uchar *fmt, ...) +{ + DEFiRet; + va_list ap; + cstr_t *pStr; + + va_start(ap, fmt); + iRet = rsCStrConstructFromszStrv(&pStr, fmt, ap); + va_end(ap); + + CHKiRet(iRet); + + iRet = cstrAppendCStr(pThis, pStr); + rsCStrDestruct(pStr); +finalize_it: + RETiRet; +} + + rsRetVal rsCStrAppendInt(cstr_t *pThis, long i) { DEFiRet; diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h index bba004a0..b301f4b9 100644 --- a/runtime/stringbuf.h +++ b/runtime/stringbuf.h @@ -58,6 +58,7 @@ rsRetVal cstrConstruct(cstr_t **ppThis); rsRetVal cstrConstructFromESStr(cstr_t **ppThis, es_str_t *str); rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz); rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom); +rsRetVal rsCStrConstructFromszStrf(cstr_t **ppThis, uchar *fmt, ...); /** * Destruct the string buffer object. @@ -173,6 +174,12 @@ rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz); */ rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen); +/** + * Append a printf-style formated string to the buffer. + * + * \param fmt pointer to the format string (see man 3 printf for details). Must not be NULL. + */ +rsRetVal rsCStrAppendStrf(cstr_t *pThis, uchar *fmt, ...); /** * Append an integer to the string. No special formatting is -- cgit v1.2.3 From 6923b9a4a7f86f6196d28b96e2ef5e8d1c2dceee Mon Sep 17 00:00:00 2001 From: Tomas Heinrich Date: Wed, 20 Mar 2013 16:40:01 +0100 Subject: Check return code from gnutls_x509_crt_get_*_dn Enlarge the supplied buffer if necessary. --- runtime/nsd_gtls.c | 55 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index 0f66c766..cd1b336c 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -259,9 +259,10 @@ gtlsClientCertCallback(gnutls_session session, static rsRetVal gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) { - char dn[128]; uchar lnBuf[256]; - size_t size; + uchar szBufA[1024]; + uchar *szBuf = szBufA; + size_t szBufLen = sizeof(szBufA), tmp; unsigned int algo, bits; time_t expiration_time, activation_time; const gnutls_datum *cert_list; @@ -271,8 +272,6 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) int gnuRet; DEFiRet; unsigned iAltName; - size_t szAltNameLen; - char szAltName[1024]; /* this is sufficient for the DNSNAME... */ assert(ppStr != NULL); ISOBJ_TYPE_assert(pThis, nsd_gtls); @@ -296,14 +295,14 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) expiration_time = gnutls_x509_crt_get_expiration_time(cert); activation_time = gnutls_x509_crt_get_activation_time(cert); - ctime_r(&activation_time, dn); - dn[strlen(dn) - 1] = '\0'; /* strip linefeed */ - snprintf((char*)lnBuf, sizeof(lnBuf), "certificate valid from %s ", dn); + ctime_r(&activation_time, szBuf); + szBuf[strlen(szBuf) - 1] = '\0'; /* strip linefeed */ + snprintf((char*)lnBuf, sizeof(lnBuf), "certificate valid from %s ", szBuf); CHKiRet(rsCStrAppendStr(pStr, lnBuf)); - ctime_r(&expiration_time, dn); - dn[strlen(dn) - 1] = '\0'; /* strip linefeed */ - snprintf((char*)lnBuf, sizeof(lnBuf), "to %s; ", dn); + ctime_r(&expiration_time, szBuf); + szBuf[strlen(szBuf) - 1] = '\0'; /* strip linefeed */ + snprintf((char*)lnBuf, sizeof(lnBuf), "to %s; ", szBuf); CHKiRet(rsCStrAppendStr(pStr, lnBuf)); /* Extract some of the public key algorithm's parameters */ @@ -314,27 +313,41 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) CHKiRet(rsCStrAppendStr(pStr, lnBuf)); /* names */ - size = sizeof(dn); - gnutls_x509_crt_get_dn(cert, dn, &size); - snprintf((char*)lnBuf, sizeof(lnBuf), "DN: %s; ", dn); + tmp = szBufLen; + if(gnutls_x509_crt_get_dn(cert, szBuf, &tmp) + == GNUTLS_E_SHORT_MEMORY_BUFFER) { + szBufLen = tmp; + szBuf = malloc(tmp); + gnutls_x509_crt_get_dn(cert, szBuf, &tmp); + } + snprintf((char*)lnBuf, sizeof(lnBuf), "DN: %s; ", szBuf); CHKiRet(rsCStrAppendStr(pStr, lnBuf)); - size = sizeof(dn); - gnutls_x509_crt_get_issuer_dn(cert, dn, &size); - snprintf((char*)lnBuf, sizeof(lnBuf), "Issuer DN: %s; ", dn); + tmp = szBufLen; + if(gnutls_x509_crt_get_issuer_dn(cert, szBuf, &tmp) + == GNUTLS_E_SHORT_MEMORY_BUFFER) { + szBufLen = tmp; + szBuf = realloc((szBuf == szBufA) ? NULL : szBuf, tmp); + gnutls_x509_crt_get_issuer_dn(cert, szBuf, &tmp); + } + snprintf((char*)lnBuf, sizeof(lnBuf), "Issuer DN: %s; ", szBuf); CHKiRet(rsCStrAppendStr(pStr, lnBuf)); /* dNSName alt name */ iAltName = 0; while(1) { /* loop broken below */ - szAltNameLen = sizeof(szAltName); + tmp = szBufLen; gnuRet = gnutls_x509_crt_get_subject_alt_name(cert, iAltName, - szAltName, &szAltNameLen, NULL); - if(gnuRet < 0) + szBuf, &tmp, NULL); + if(gnuRet == GNUTLS_E_SHORT_MEMORY_BUFFER) { + szBufLen = tmp; + szBuf = realloc((szBuf == szBufA) ? NULL : szBuf, tmp); + continue; + } else if(gnuRet < 0) break; else if(gnuRet == GNUTLS_SAN_DNSNAME) { /* we found it! */ - snprintf((char*)lnBuf, sizeof(lnBuf), "SAN:DNSname: %s; ", szAltName); + snprintf((char*)lnBuf, sizeof(lnBuf), "SAN:DNSname: %s; ", szBuf); CHKiRet(rsCStrAppendStr(pStr, lnBuf)); /* do NOT break, because there may be multiple dNSName's! */ } @@ -352,6 +365,8 @@ finalize_it: if(pStr != NULL) rsCStrDestruct(&pStr); } + if(szBuf != szBufA) + free(szBuf); RETiRet; } -- cgit v1.2.3 From 4d831391925f29f82464aa0223e6f40ac8e1f9b1 Mon Sep 17 00:00:00 2001 From: Tomas Heinrich Date: Thu, 21 Mar 2013 10:04:59 +0100 Subject: Utilize the new rsCStr*Strf() functions --- runtime/nsd_gtls.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index cd1b336c..6ef4feba 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -259,7 +259,6 @@ gtlsClientCertCallback(gnutls_session session, static rsRetVal gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) { - uchar lnBuf[256]; uchar szBufA[1024]; uchar *szBuf = szBufA; size_t szBufLen = sizeof(szBufA), tmp; @@ -280,37 +279,27 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) return RS_RET_TLS_CERT_ERR; cert_list = gnutls_certificate_get_peers(pThis->sess, &cert_list_size); - - CHKiRet(rsCStrConstruct(&pStr)); - - snprintf((char*)lnBuf, sizeof(lnBuf), "peer provided %d certificate(s). ", cert_list_size); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(rsCStrConstructFromszStrf(&pStr, "peer provided %d certificate(s). ", cert_list_size)); if(cert_list_size > 0) { /* we only print information about the first certificate */ CHKgnutls(gnutls_x509_crt_init(&cert)); CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)); - CHKiRet(rsCStrAppendStr(pStr, (uchar*)"Certificate 1 info: ")); - expiration_time = gnutls_x509_crt_get_expiration_time(cert); activation_time = gnutls_x509_crt_get_activation_time(cert); ctime_r(&activation_time, szBuf); szBuf[strlen(szBuf) - 1] = '\0'; /* strip linefeed */ - snprintf((char*)lnBuf, sizeof(lnBuf), "certificate valid from %s ", szBuf); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); - + CHKiRet(rsCStrAppendStrf(pStr, (uchar*)"Certificate 1 info: " + "certificate valid from %s ", szBuf)); ctime_r(&expiration_time, szBuf); szBuf[strlen(szBuf) - 1] = '\0'; /* strip linefeed */ - snprintf((char*)lnBuf, sizeof(lnBuf), "to %s; ", szBuf); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(rsCStrAppendStrf(pStr, "to %s; ", szBuf)); /* Extract some of the public key algorithm's parameters */ algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); - - snprintf((char*)lnBuf, sizeof(lnBuf), "Certificate public key: %s; ", - gnutls_pk_algorithm_get_name(algo)); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(rsCStrAppendStrf(pStr, "Certificate public key: %s; ", + gnutls_pk_algorithm_get_name(algo))); /* names */ tmp = szBufLen; @@ -320,8 +309,7 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) szBuf = malloc(tmp); gnutls_x509_crt_get_dn(cert, szBuf, &tmp); } - snprintf((char*)lnBuf, sizeof(lnBuf), "DN: %s; ", szBuf); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(rsCStrAppendStrf(pStr, "DN: %s; ", szBuf)); tmp = szBufLen; if(gnutls_x509_crt_get_issuer_dn(cert, szBuf, &tmp) @@ -330,8 +318,7 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) szBuf = realloc((szBuf == szBufA) ? NULL : szBuf, tmp); gnutls_x509_crt_get_issuer_dn(cert, szBuf, &tmp); } - snprintf((char*)lnBuf, sizeof(lnBuf), "Issuer DN: %s; ", szBuf); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(rsCStrAppendStrf(pStr, "Issuer DN: %s; ", szBuf)); /* dNSName alt name */ iAltName = 0; @@ -347,8 +334,7 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) break; else if(gnuRet == GNUTLS_SAN_DNSNAME) { /* we found it! */ - snprintf((char*)lnBuf, sizeof(lnBuf), "SAN:DNSname: %s; ", szBuf); - CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(rsCStrAppendStrf(pStr, "SAN:DNSname: %s; ", szBuf)); /* do NOT break, because there may be multiple dNSName's! */ } ++iAltName; -- cgit v1.2.3 From d27e726d6b0e071fc9077e18a795025b3571e032 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 9 Apr 2013 17:30:12 +0200 Subject: doc: add recent patch to ChangeLog --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3c532059..c28e2dbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Version 7.3.10 [devel] 2013-04-?? no failover happened. The suspend functionalit now does no longer use the cached timestamp (should not have any performance implication, as action suspend occurs very infrequently). +- bugfix: gnutls RFC5425 driver had some undersized buffers + Thanks to Tomas Heinrich for the patch. - bugfix: nested if/prifilt conditions did not work properly closes: http://bugzilla.adiscon.com/show_bug.cgi?id=415 - templates now permit substring extraction relative to end-of-string -- cgit v1.2.3 From 08a63f4987b7bfd694a85b53b3385af3bda3ad2f Mon Sep 17 00:00:00 2001 From: Martin Carpenter Date: Wed, 27 Mar 2013 11:12:50 +0100 Subject: Set pkgconfig variables for libuuid on Solaris --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index 5c1fa991..1df9e7d4 100644 --- a/configure.ac +++ b/configure.ac @@ -55,6 +55,10 @@ case "${host}" in AC_DEFINE([OS_SOLARIS], [1], [Indicator for a Solaris OS]) AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], [1], [Use POSIX pthread semantics]) SOL_LIBS="-lsocket -lnsl" + # Solaris libuuid does not ship with a pkgconfig file so override the appropriate + # variables (but only if they have not been set by the user). + LIBUUID_CFLAGS=${LIBUUID_CFLAGS:= } + LIBUUID_LIBS=${LIBUUID_LIBS:=-luuid} AC_SUBST(SOL_LIBS) ;; esac -- cgit v1.2.3 From e523107a96f569825879d5713ed4c5a56180eb2f Mon Sep 17 00:00:00 2001 From: Martin Carpenter Date: Wed, 27 Mar 2013 12:07:33 +0100 Subject: Update u_int8_t to uint8_t --- plugins/omudpspoof/omudpspoof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c index 4f37fd8c..9c4c80ba 100644 --- a/plugins/omudpspoof/omudpspoof.c +++ b/plugins/omudpspoof/omudpspoof.c @@ -481,7 +481,7 @@ UDPSend(instanceData *pData, uchar *pszSourcename, char *msg, size_t len) 0, /* checksum */ source_ip.sin_addr.s_addr, tempaddr->sin_addr.s_addr, - (u_int8_t*)(msg+msgOffs), /* payload */ + (uint8_t*)(msg+msgOffs), /* payload */ pktLen, /* payload size */ pData->libnet_handle, /* libnet handle */ ip); /* libnet id */ -- cgit v1.2.3 From 980523a7de83f6a492e633648467118c84adbe12 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 10 Apr 2013 08:57:37 +0200 Subject: doc: complete ChangeLog --- ChangeLog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index c28e2dbb..670a39a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ Version 7.3.10 [devel] 2013-04-?? - added RainerScript re_extract() function - omrelp: added support for RainerScript-based configuration - omrelp: added ability to specify session timeout +- templates now permit substring extraction relative to end-of-string - bugfix: failover/action suspend did not work correctly This was experienced if the retry action took more than one second to complete. For suspending, a cached timestamp was used, and if the @@ -15,9 +16,10 @@ Version 7.3.10 [devel] 2013-04-?? Thanks to Tomas Heinrich for the patch. - bugfix: nested if/prifilt conditions did not work properly closes: http://bugzilla.adiscon.com/show_bug.cgi?id=415 -- templates now permit substring extraction relative to end-of-string - bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements +- bugfix: build problems on Solaris + Thanks to Martin Carpenter for the patches. --------------------------------------------------------------------------- Version 7.3.9 [devel] 2013-03-27 - support for signing logs added -- cgit v1.2.3 From a5d33140aae708f71939bcde5f4db5655856e7dc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 10 Apr 2013 10:05:49 +0200 Subject: prepare for 7.3.10 release --- ChangeLog | 2 +- configure.ac | 2 +- doc/manual.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 670a39a6..84c5db7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ --------------------------------------------------------------------------- -Version 7.3.10 [devel] 2013-04-?? +Version 7.3.10 [devel] 2013-04-10 - added RainerScript re_extract() function - omrelp: added support for RainerScript-based configuration - omrelp: added ability to specify session timeout diff --git a/configure.ac b/configure.ac index 915821f0..c0712a5e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[7.3.9],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.3.10],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/doc/manual.html b/doc/manual.html index 6fba9a05..a1e39c9d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ professional services available directly from the source!

    Please visit the rsyslog sponsor's page to honor the project sponsors or become one yourself! We are very grateful for any help towards the project goals.

    -

    This documentation is for version 7.3.9 (devel branch) of rsyslog. +

    This documentation is for version 7.3.10 (devel branch) of rsyslog. Visit the rsyslog status page to obtain current version information and project status.

    If you like rsyslog, you might -- cgit v1.2.3 From 078b010af6317ad325a9f10a901f8a279c8d0899 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 11 Apr 2013 09:57:18 +0200 Subject: log encryption: check length of "key" parameter --- runtime/Makefile.am | 2 +- runtime/libgcry.c | 4 +++- runtime/lmcry_gcry.c | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/runtime/Makefile.am b/runtime/Makefile.am index be68ce60..e1f0673c 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -184,7 +184,7 @@ if ENABLE_LIBGCRYPT pkglib_LTLIBRARIES += lmcry_gcry.la lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h libgcry.c libgcry.h lmcry_gcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) - lmcry_gcry_la_LDFLAGS = -module -avoid-version + lmcry_gcry_la_LDFLAGS = -module -avoid-version `libgcrypt-config --libs` #lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) lmcry_gcry_la_LIBADD = $(LIBGCRYPT_LIBS) endif diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 5f1dbf58..c1ab3abf 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -108,8 +108,10 @@ rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) uint16_t reqKeyLen = gcry_cipher_get_algo_keylen(GCRY_CIPHER); int r; - if(keyLen != reqKeyLen) + if(keyLen != reqKeyLen) { r = reqKeyLen; + goto done; + } ctx->keyLen = keyLen; ctx->key = malloc(keyLen); memcpy(ctx->key, key, keyLen); diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index ce0fef2f..31e648fc 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -92,6 +92,8 @@ SetCnfParam(void *pT, struct nvlst *lst) uchar *cstr; uchar *key = NULL; struct cnfparamvals *pvals; + DEFiRet; + pvals = nvlstGetParams(lst, &pblk, NULL); if(Debug) { dbgprintf("param blk in lmcry_gcry:\n"); @@ -125,8 +127,9 @@ SetCnfParam(void *pT, struct nvlst *lst) "config file is highly insecure - DO NOT USE FOR PRODUCTION"); r = rsgcrySetKey(pThis->ctx, key, strlen((char*)key)); if(r > 0) { - errmsg.LogError(0, RS_RET_ERR, "Key length %d expected, but " + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Key length %d expected, but " "key of length %d given", r, strlen((char*)key)); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } } @@ -135,7 +138,8 @@ SetCnfParam(void *pT, struct nvlst *lst) memset(key, 0, strlen((char*)key)); free(key); } - return RS_RET_OK; +finalize_it: + RETiRet; } -- cgit v1.2.3 From 7ab02dce5c60453c454dd8645a63a5ef33f6a546 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 11 Apr 2013 10:08:05 +0200 Subject: cleanup --- runtime/libgcry.c | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/runtime/libgcry.c b/runtime/libgcry.c index c1ab3abf..af70e581 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -199,43 +199,3 @@ rsgcryExit(void) { return; } - -#if 0 // we use this for the tool, only! -static void -doDeCrypt(FILE *fpin, FILE *fpout) -{ - gcry_error_t gcryError; - char buf[64*1024]; - size_t nRead, nWritten; - size_t nPad; - - while(1) { - nRead = fread(buf, 1, sizeof(buf), fpin); - if(nRead == 0) - break; - nPad = (blkLength - nRead % blkLength) % blkLength; - fprintf(stderr, "read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, - nRead % blkLength, nPad); - gcryError = gcry_cipher_decrypt( - gcryCipherHd, // gcry_cipher_hd_t - buf, // void * - nRead, // size_t - NULL, // const void * - 0); // size_t - if (gcryError) { - fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", - gcry_strsource(gcryError), - gcry_strerror(gcryError)); - return; - } -fprintf(stderr, "in remove pad, %d\n", nRead); - removePadding(buf, &nRead); -fprintf(stderr, "out remove pad %d\n", nRead); - nWritten = fwrite(buf, 1, nRead, fpout); - if(nWritten != nRead) { - perror("fpout"); - return; - } - } -} -#endif -- cgit v1.2.3 From 5f72832b3d6c2acd076b9ebfd3484877fc506ebc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 11 Apr 2013 14:49:06 +0200 Subject: logenc: milestone: rsyslog writes .encinfo side files --- runtime/cryprov.h | 2 +- runtime/libgcry.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++--- runtime/libgcry.h | 12 ++- runtime/lmcry_gcry.c | 12 +-- runtime/rsyslog.h | 4 + runtime/stream.c | 5 +- 6 files changed, 229 insertions(+), 19 deletions(-) diff --git a/runtime/cryprov.h b/runtime/cryprov.h index c5ee95a0..5b694f46 100644 --- a/runtime/cryprov.h +++ b/runtime/cryprov.h @@ -33,7 +33,7 @@ BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Destruct)(void *ppThis); rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData); rsRetVal (*Encrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf); - rsRetVal (*OnFileClose)(void *pFileInstData); + rsRetVal (*OnFileClose)(void *pFileInstData, off64_t offsLogfile); ENDinterface(cryprov) #define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ rsRetVal initCrypt(int gcry_mode, char * iniVector); diff --git a/runtime/libgcry.c b/runtime/libgcry.c index af70e581..94e087ac 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -2,6 +2,22 @@ * * Copyright 2013 Adiscon GmbH. * + * We need to store some additional information in support of encryption. + * For this, we create a side-file, which is named like the actual log + * file, but with the suffix ".encinfo" appended. It contains the following + * records: + * IV: The initial vector used at block start. Also indicates start + * start of block. + * END: The end offset of the block, as uint64_t in decimal notation. + * This is used during encryption to know when the current + * encryption block ends. + * For the current implementation, there must always be an IV record + * followed by an END record. Each records is LF-terminated. Record + * types can simply be extended in the future by specifying new + * keywords (like "IV") before the colon. + * To identify a file as rsyslog encryption info file, it must start with + * the line "FILETYPE:rsyslog-enrcyption-info" + * * This file is part of rsyslog. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,21 +39,166 @@ #endif #include #include +#include +#include +#include +#include #include "rsyslog.h" #include "libgcry.h" #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable -static inline gcryfile -gcryfileConstruct(gcryctx ctx) + +static rsRetVal +eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf) +{ + struct iovec iov[3]; + ssize_t nwritten, towrite; + DEFiRet; + + iov[0].iov_base = recHdr; + iov[0].iov_len = lenRecHdr; + iov[1].iov_base = buf; + iov[1].iov_len = lenBuf; + iov[2].iov_base = "\n"; + iov[2].iov_len = 1; + towrite = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; + nwritten = writev(gf->fd, iov, sizeof(iov)/sizeof(struct iovec)); + if(nwritten != towrite) { + DBGPRINTF("eiWrite%s: error writing file, towrite %d, " + "nwritten %d\n", recHdr, (int) towrite, (int) nwritten); + ABORT_FINALIZE(RS_RET_EI_WR_ERR); + } + DBGPRINTF("encryption info file %s: written %s, len %d\n", + recHdr, gf->eiName, (int) nwritten); +finalize_it: + RETiRet; +} + +static rsRetVal +eiOpenRead(gcryfile gf) +{ + DEFiRet; + gf->fd = open((char*)gf->eiName, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if(gf->fd == -1) { + ABORT_FINALIZE(errno == ENOENT ? RS_RET_EI_NO_EXISTS : RS_RET_EI_OPN_ERR); + } +finalize_it: + RETiRet; +} + + +static rsRetVal +eiCheckFiletype(gcryfile gf) +{ + char hdrBuf[128]; + size_t toRead, didRead; + DEFiRet; + + 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); + DBGPRINTF("eiCheckFiletype read %d bytes: '%s'\n", didRead, hdrBuf); + if( didRead != toRead + || strncmp(hdrBuf, "FILETYPE:" RSGCRY_FILETYPE_NAME "\n", toRead)) + iRet = RS_RET_EI_INVLD_FILE; +finalize_it: + RETiRet; +} + +static rsRetVal +eiOpenAppend(gcryfile gf) +{ + rsRetVal localRet; + DEFiRet; + localRet = eiCheckFiletype(gf); + if(localRet == RS_RET_OK) { + gf->fd = open((char*)gf->eiName, + O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC, 0600); + if(gf->fd == -1) { + ABORT_FINALIZE(RS_RET_EI_OPN_ERR); + } + } else if(localRet == RS_RET_EI_NO_EXISTS) { + /* looks like we need to create a new file */ + gf->fd = open((char*)gf->eiName, + O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); + if(gf->fd == -1) { + ABORT_FINALIZE(RS_RET_EI_OPN_ERR); + } + CHKiRet(eiWriteRec(gf, "FILETYPE:", 9, RSGCRY_FILETYPE_NAME, + sizeof(RSGCRY_FILETYPE_NAME)-1)); + } else { + gf->fd = -1; + ABORT_FINALIZE(localRet); + } + DBGPRINTF("encryption info file %s: opened as #%d\n", + gf->eiName, gf->fd); +finalize_it: + RETiRet; +} + +static rsRetVal +eiWriteIV(gcryfile gf, uchar *iv) { + static const char hexchars[16] = + {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + unsigned iSrc, iDst; + char hex[4096]; + DEFiRet; + + if(gf->blkLength > sizeof(hex)/2) { + DBGPRINTF("eiWriteIV: crypto block len way too large, aborting " + "write"); + ABORT_FINALIZE(RS_RET_ERR); + } + + for(iSrc = iDst = 0 ; iSrc < gf->blkLength ; ++iSrc) { + hex[iDst++] = hexchars[iv[iSrc]>>4]; + hex[iDst++] = hexchars[iv[iSrc]&0x0f]; + } + + iRet = eiWriteRec(gf, "IV:", 3, hex, gf->blkLength*2); +finalize_it: + RETiRet; +} + +/* we do not return an error state, as we MUST close the file, + * no matter what happens. + */ +static void +eiClose(gcryfile gf, off64_t offsLogfile) +{ + char offs[21]; + 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); + close(gf->fd); + DBGPRINTF("encryption info file %s: closed\n", gf->eiName); +} + +static rsRetVal +gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn) +{ + char fn[MAXFNAME+1]; gcryfile gf; - if((gf = calloc(1, sizeof(struct gcryfile_s))) == NULL) - goto done; + DEFiRet; + + CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s))); gf->ctx = ctx; -done: return gf; + snprintf(fn, sizeof(fn), "%s.encinfo", logfn); + fn[MAXFNAME] = '\0'; /* be on save side */ + gf->eiName = (uchar*) strdup(fn); + *pgf = gf; +finalize_it: + RETiRet; } + gcryctx gcryCtxNew(void) { @@ -47,12 +208,14 @@ gcryCtxNew(void) } int -gcryfileDestruct(gcryfile gf) +gcryfileDestruct(gcryfile gf, off64_t offsLogfile) { int r = 0; if(gf == NULL) goto done; + eiClose(gf, offsLogfile); + free(gf->eiName); free(gf); done: return r; } @@ -119,14 +282,40 @@ rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) done: return r; } +/* As of some Linux and security expert I spoke to, /dev/urandom + * provides very strong random numbers, even if it runs out of + * entropy. As far as he knew, this is save for all applications + * (and he had good proof that I currently am not permitted to + * reproduce). -- rgerhards, 2013-03-04 + */ +void +seedIV(gcryfile gf, uchar **iv) +{ + int fd; + + *iv = malloc(gf->blkLength); /* do NOT zero-out! */ + /* if we cannot obtain data from /dev/urandom, we use whatever + * is present at the current memory location as random data. Of + * course, this is very weak and we should consider a different + * option, especially when not running under Linux (for Linux, + * unavailability of /dev/urandom is just a theoretic thing, it + * will always work...). -- TODO -- rgerhards, 2013-03-06 + */ + if((fd = open("/dev/urandom", O_RDONLY)) > 0) { + if(read(fd, *iv, gf->blkLength)) {}; /* keep compiler happy */ + close(fd); + } +} + rsRetVal -rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname) { gcry_error_t gcryError; gcryfile gf = NULL; + uchar *iv = NULL; DEFiRet; - CHKmalloc(gf = gcryfileConstruct(ctx)); + CHKiRet(gcryfileConstruct(ctx, &gf, fname)); gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); @@ -150,17 +339,21 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) ABORT_FINALIZE(RS_RET_ERR); } - gcryError = gcry_cipher_setiv(gf->chd, iniVector, gf->blkLength); + 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)); ABORT_FINALIZE(RS_RET_ERR); } + CHKiRet(eiOpenAppend(gf)); + CHKiRet(eiWriteIV(gf, iv)); *pgf = gf; finalize_it: + free(iv); if(iRet != RS_RET_OK && gf != NULL) - gcryfileDestruct(gf); + gcryfileDestruct(gf, -1); RETiRet; } diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 608abd6c..6e677130 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -34,6 +34,8 @@ typedef struct gcryfile_s *gcryfile; struct gcryfile_s { gcry_cipher_hd_t chd; /* cypher handle */ 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) */ gcryctx ctx; }; @@ -42,8 +44,14 @@ void rsgcryExit(void); int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); gcryctx gcryCtxNew(void); void rsgcryCtxDel(gcryctx ctx); -int gcryfileDestruct(gcryfile gf); -rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char * iniVector); +int gcryfileDestruct(gcryfile gf, off64_t offsLogfile); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname); int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); +/* error states */ +#define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */ +#define RSGCRYE_OOM 4 /* ran out of memory */ + +#define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info" + #endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 31e648fc..881d047d 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -150,11 +150,13 @@ OnFileOpen(void *pT, uchar *fn, void *pGF) gcryfile *pgf = (gcryfile*) pGF; DEFiRet; dbgprintf("DDDD: cry: onFileOpen: %s\n", fn); - /* note: if *pgf is set to NULL, this auto-disables GT functions */ - //*pgf = gcryCtxOpenFile(pThis->ctx, fn); - CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, "TODO: init value")); + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, fn)); finalize_it: + /* TODO: enable this error message (need to cleanup loop first ;)) + errmsg.LogError(0, iRet, "Encryption Provider" + "Error: cannot open .encinfo file - disabling log file"); + */ RETiRet; } @@ -169,11 +171,11 @@ dbgprintf("DDDD: Encrypt (%u): %s\n", *lenRec-1, rec); } static rsRetVal -OnFileClose(void *pF) +OnFileClose(void *pF, off64_t offsLogfile) { DEFiRet; dbgprintf("DDDD: onFileClose\n"); - gcryfileDestruct(pF); + gcryfileDestruct(pF, offsLogfile); RETiRet; } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index cbc0401b..ab57eace 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -404,6 +404,10 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_REPLCHAR_IGNORED = -2313,/**< mmanon: replacementChar parameter is ignored */ RS_RET_SIGPROV_ERR = -2320,/**< error in signature provider */ RS_RET_CRYPROV_ERR = -2321,/**< error in cryptography encryption provider */ + RS_RET_EI_OPN_ERR = -2322,/**< error opening an .encinfo file */ + RS_RET_EI_NO_EXISTS = -2323,/**< .encinfo file does not exist (status, not necessarily error!)*/ + RS_RET_EI_WR_ERR = -2324,/**< error writing an .encinfo file */ + RS_RET_EI_INVLD_FILE = -2325,/**< header indicates the file is no .encinfo file */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/runtime/stream.c b/runtime/stream.c index b31520b0..b0df8418 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -45,6 +45,7 @@ #include #include #include +#include #include /* required for HP UX */ #include #include @@ -388,6 +389,7 @@ strmWaitAsyncWriterDone(strm_t *pThis) */ static rsRetVal strmCloseFile(strm_t *pThis) { + off64_t currOffs; DEFiRet; ASSERT(pThis != NULL); @@ -408,11 +410,12 @@ static rsRetVal strmCloseFile(strm_t *pThis) * against this. -- rgerhards, 2010-03-19 */ if(pThis->fd != -1) { + currOffs = lseek64(pThis->fd, 0, SEEK_CUR); close(pThis->fd); pThis->fd = -1; pThis->inode = 0; if(pThis->cryprov != NULL) { - pThis->cryprov->OnFileClose(pThis->cryprovFileData); + pThis->cryprov->OnFileClose(pThis->cryprovFileData, currOffs); pThis->cryprovFileData = NULL; } } -- cgit v1.2.3 From 2679dd4af107290845711c4e265ed1e8b0c051a8 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 08:49:04 +0200 Subject: logenc: rscryutil can now decrypt multi-block logfiles --- runtime/cryprov.h | 2 - runtime/libgcry.c | 6 +- runtime/libgcry.h | 3 + tools/Makefile.am | 2 +- tools/rscryutil.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 173 insertions(+), 29 deletions(-) diff --git a/runtime/cryprov.h b/runtime/cryprov.h index 5b694f46..8496b745 100644 --- a/runtime/cryprov.h +++ b/runtime/cryprov.h @@ -36,6 +36,4 @@ BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*OnFileClose)(void *pFileInstData, off64_t offsLogfile); ENDinterface(cryprov) #define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ -rsRetVal initCrypt(int gcry_mode, char * iniVector); -rsRetVal doCrypt(char *buf, size_t *len); #endif /* #ifndef INCLUDED_CRYPROV_H */ diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 94e087ac..5fd55360 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -14,9 +14,11 @@ * For the current implementation, there must always be an IV record * followed by an END record. Each records is LF-terminated. Record * types can simply be extended in the future by specifying new - * keywords (like "IV") before the colon. + * types (like "IV") before the colon. * To identify a file as rsyslog encryption info file, it must start with * the line "FILETYPE:rsyslog-enrcyption-info" + * There are some size constraints: the recordtype must be 31 bytes at + * most and the actual value (between : and LF) must be 1023 bytes at most. * * This file is part of rsyslog. * @@ -191,7 +193,7 @@ gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn) CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s))); gf->ctx = ctx; - snprintf(fn, sizeof(fn), "%s.encinfo", logfn); + snprintf(fn, sizeof(fn), "%s%s", logfn, ENCINFO_SUFFIX); fn[MAXFNAME] = '\0'; /* be on save side */ gf->eiName = (uchar*) strdup(fn); *pgf = gf; diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 6e677130..857d2352 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -52,6 +52,9 @@ int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); #define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */ #define RSGCRYE_OOM 4 /* ran out of memory */ +#define EIF_MAX_RECTYPE_LEN 31 /* max length of record types */ +#define EIF_MAX_VALUE_LEN 1023 /* max length of value types */ #define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info" +#define ENCINFO_SUFFIX ".encinfo" #endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/tools/Makefile.am b/tools/Makefile.am index 8957d713..0f2bb57e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -77,7 +77,7 @@ endif if ENABLE_LIBGCRYPT bin_PROGRAMS += rscryutil rscryutil = rscryutil.c -rscryutil_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) +rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) rscryutil_LDFLAGS = `libgcrypt-config --libs` #rscryutil_LDFLAGS = $(LIBGCRYPT_LIBS) rscryutil.1: rscryutil.rst diff --git a/tools/rscryutil.c b/tools/rscryutil.c index 755371f2..e57eb625 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -1,5 +1,4 @@ -/* This is a tool for dumpoing the content of GuardTime TLV - * files in a (somewhat) human-readable manner. +/* This is a tool for processing rsyslog encrypted log files. * * Copyright 2013 Adiscon GmbH * @@ -31,6 +30,9 @@ #include #include +#include "rsyslog.h" +#include "libgcry.h" + static enum { MD_DECRYPT } mode = MD_DECRYPT; @@ -38,17 +40,135 @@ static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; + +/* rectype/value must be EIF_MAX_*_LEN+1 long! + * returns 0 on success or something else on error/EOF + */ +static int +eiGetRecord(FILE *eifp, char *rectype, char *value) +{ + int r; + unsigned short i, j; + char buf[EIF_MAX_RECTYPE_LEN+EIF_MAX_VALUE_LEN+128]; + /* large enough for any valid record */ + + if(fgets(buf, sizeof(buf), eifp) == NULL) { + r = 1; goto done; + } + + for(i = 0 ; i < EIF_MAX_RECTYPE_LEN && buf[i] != ':' ; ++i) + if(buf[i] == '\0') { + r = 2; goto done; + } else + rectype[i] = buf[i]; + rectype[i] = '\0'; + j = 0; + for(++i ; i < EIF_MAX_VALUE_LEN && buf[i] != '\n' ; ++i, ++j) + if(buf[i] == '\0') { + r = 3; goto done; + } else + value[j] = buf[i]; + value[j] = '\0'; + r = 0; +done: return r; +} + +static int +eiCheckFiletype(FILE *eifp) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + int r; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "FILETYPE") || strcmp(value, RSGCRY_FILETYPE_NAME)) { + fprintf(stderr, "invalid filetype \"cookie\" in encryption " + "info file\n"); + fprintf(stderr, "\trectype: '%s', value: '%s'\n", rectype, value); + r = 1; goto done; + } + r = 0; +done: return r; +} + static int -initCrypt(int gcry_mode, char *iv, char *key) +eiGetIV(FILE *eifp, char *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; + int r; + unsigned char nibble; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "IV")) { + fprintf(stderr, "no IV record found when expected, record type " + "seen is '%s'\n", rectype); + r = 1; goto done; + } + valueLen = strlen(value); + if(valueLen/2 != leniv) { + fprintf(stderr, "length of IV is %d, expected %d\n", + valueLen/2, leniv); + r = 1; goto done; + } + + 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 { + fprintf(stderr, "invalid IV '%s'\n", value); + r = 1; goto done; + } + if(i % 2 == 0) + iv[j] = nibble << 4; + else + iv[j++] |= nibble; + } + r = 0; +done: return r; +} + +static int +eiGetEND(FILE *eifp, off64_t *offs) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + int r; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "END")) { + fprintf(stderr, "no END record found when expected, record type " + "seen is '%s'\n", rectype); + r = 1; goto done; + } + *offs = atoll(value); + r = 0; +done: return r; +} + +static int +initCrypt(FILE *eifp, int gcry_mode, char *key) { #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable int r = 0; gcry_error_t gcryError; + char iv[4096]; blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + if(blkLength > sizeof(iv)) { + fprintf(stderr, "internal error[%s:%d]: block length %d too large for " + "iv buffer\n", __FILE__, __LINE__, blkLength); + r = 1; goto done; + } + if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done; + size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); if(strlen(key) != keyLength) { - fprintf(stderr, "invalid key lengtjh; key is %u characters, but " + fprintf(stderr, "invalid key length; key is %u characters, but " "exactly %u characters are required\n", strlen(key), keyLength); r = 1; goto done; @@ -87,7 +207,7 @@ removePadding(char *buf, size_t *plen) unsigned iSrc, iDst; char *frstNUL; - frstNUL = strchr(buf, 0x00); + frstNUL = memchr(buf, 0x00, *plen); if(frstNUL == NULL) goto done; iDst = iSrc = frstNUL - buf; @@ -103,20 +223,24 @@ done: return; } static void -doDeCrypt(FILE *fpin, FILE *fpout) +decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) { gcry_error_t gcryError; - char buf[64*1024]; - size_t nRead, nWritten; + size_t nRead, nWritten; + size_t toRead; size_t nPad; + size_t leftTillBlkEnd; + char buf[64*1024]; + leftTillBlkEnd = blkEnd - *pCurrOffs; while(1) { - nRead = fread(buf, 1, sizeof(buf), fpin); + toRead = sizeof(buf) <= leftTillBlkEnd ? sizeof(buf) : leftTillBlkEnd; + toRead = toRead - toRead % blkLength; + nRead = fread(buf, 1, toRead, fpin); if(nRead == 0) break; + leftTillBlkEnd -= nRead, *pCurrOffs += nRead; nPad = (blkLength - nRead % blkLength) % blkLength; - fprintf(stderr, "--->read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, - nRead % blkLength, nPad); gcryError = gcry_cipher_decrypt( gcry_chd, // gcry_cipher_hd_t buf, // void * @@ -139,13 +263,31 @@ doDeCrypt(FILE *fpin, FILE *fpout) } +static int +doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp, char *key) +{ + off64_t blkEnd; + off64_t currOffs = 0; + int r; + + while(1) { + /* process block */ + if(initCrypt(eifp, GCRY_CIPHER_MODE_CBC, key) != 0) + goto done; + if((r = eiGetEND(eifp, &blkEnd)) != 0) goto done; + decryptBlock(logfp, outfp, blkEnd, &currOffs); + gcry_cipher_close(gcry_chd); + } + r = 0; +done: return r; +} + static void decrypt(char *name, char *key) { - FILE *logfp = NULL; - //, *sigfp = NULL; + FILE *logfp = NULL, *eifp = NULL; int r = 0; - //char sigfname[4096]; + char eifname[4096]; if(!strcmp(name, "-")) { fprintf(stderr, "decrypt mode cannot work on stdin\n"); @@ -155,21 +297,20 @@ decrypt(char *name, char *key) perror(name); goto err; } -#if 0 - snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name); - sigfname[sizeof(sigfname)-1] = '\0'; - if((sigfp = fopen(sigfname, "r")) == NULL) { - perror(sigfname); + snprintf(eifname, sizeof(eifname), "%s%s", name, ENCINFO_SUFFIX); + eifname[sizeof(eifname)-1] = '\0'; + if((eifp = fopen(eifname, "r")) == NULL) { + perror(eifname); goto err; } -#endif + if(eiCheckFiletype(eifp) != 0) + goto err; } - if(initCrypt(GCRY_CIPHER_MODE_CBC, "TODO: init value", key) != 0) - goto err; - doDeCrypt(logfp, stdout); - gcry_cipher_close(gcry_chd); + doDecrypt(logfp, eifp, stdout, key); + fclose(logfp); logfp = NULL; + fclose(eifp); eifp = NULL; return; err: -- cgit v1.2.3 From 6773add66a5b369c33740b794f1a81b1a3bd1aa2 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 09:12:05 +0200 Subject: bugfix: parameter action.execOnlyWhenPreviousIsSuspended was accidently of integer-type For obvious reasons, it needs to be boolean. Note that this change can break existing configurations if they circumvented the problem by using 0/1 values. --- ChangeLog | 4 ++++ action.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index c4546db6..0fe084ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,6 +46,10 @@ Version 7.2.7 [v7-stable] 2013-03-?? To use that functionality, legacy rsyslog.conf syntax had to be used. Also, the doc was missing information on the "ParseTrusted" set of config directives. +- bugfix: parameter action.execOnlyWhenPreviousIsSuspended was accidently + of integer-type. For obvious reasons, it needs to be boolean. Note + that this change can break existing configurations if they circumvented + the problem by using 0/1 values. - doc bugfix: rsyslog.conf man page had invalid file format info closes: http://bugzilla.adiscon.com/show_bug.cgi?id=418 ---------------------------------------------------------------------------- diff --git a/action.c b/action.c index 8fc92e56..7e228767 100644 --- a/action.c +++ b/action.c @@ -188,7 +188,7 @@ static struct cnfparamdescr cnfparamdescr[] = { { "action.execonlyeverynthtime", eCmdHdlrInt, 0 }, /* legacy: actionexeconlyeverynthtime */ { "action.execonlyeverynthtimetimeout", eCmdHdlrInt, 0 }, /* legacy: actionexeconlyeverynthtimetimeout */ { "action.execonlyonceeveryinterval", eCmdHdlrInt, 0 }, /* legacy: actionexeconlyonceeveryinterval */ - { "action.execonlywhenpreviousissuspended", eCmdHdlrInt, 0 }, /* legacy: actionexeconlywhenpreviousissuspended */ + { "action.execonlywhenpreviousissuspended", eCmdHdlrBinary, 0 }, /* legacy: actionexeconlywhenpreviousissuspended */ { "action.repeatedmsgcontainsoriginalmsg", eCmdHdlrBinary, 0 }, /* legacy: repeatedmsgcontainsoriginalmsg */ { "action.resumeretrycount", eCmdHdlrInt, 0 }, /* legacy: actionresumeretrycount */ { "action.resumeinterval", eCmdHdlrInt, 0 } -- cgit v1.2.3 From 815bae1f35c67ff7b8caf7b446a9e4cf1c870aa3 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 11:22:04 +0200 Subject: logenc: add parameters to select cipher algo and mode --- runtime/libgcry.c | 47 ++++++++++++++++++++++++++++-------- runtime/libgcry.h | 40 ++++++++++++++++++++++++++++++- runtime/lmcry_gcry.c | 38 ++++++++++++++++++----------- runtime/rsyslog.h | 2 ++ tools/rscryutil.c | 67 +++++++++++++++++++++++++++++++++------------------- tools/rscryutil.rst | 43 +++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 49 deletions(-) diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 5fd55360..ef94e8ac 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -49,8 +49,6 @@ #include "rsyslog.h" #include "libgcry.h" -#define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable - static rsRetVal eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf) @@ -206,6 +204,8 @@ gcryCtxNew(void) { gcryctx ctx; ctx = calloc(1, sizeof(struct gcryctx_s)); + ctx->algo = GCRY_CIPHER_AES128; + ctx->mode = GCRY_CIPHER_MODE_CBC; return ctx; } @@ -270,9 +270,10 @@ done: return; int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) { - uint16_t reqKeyLen = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + uint16_t reqKeyLen; int r; + reqKeyLen = gcry_cipher_get_algo_keylen(ctx->algo); if(keyLen != reqKeyLen) { r = reqKeyLen; goto done; @@ -284,6 +285,36 @@ rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) done: return r; } +rsRetVal +rsgcrySetMode(gcryctx ctx, uchar *modename) +{ + int mode; + DEFiRet; + + mode = rsgcryModename2Mode((char *)modename); + if(mode == GCRY_CIPHER_MODE_NONE) { + ABORT_FINALIZE(RS_RET_CRY_INVLD_MODE); + } + ctx->mode = mode; +finalize_it: + RETiRet; +} + +rsRetVal +rsgcrySetAlgo(gcryctx ctx, uchar *algoname) +{ + int algo; + DEFiRet; + + algo = rsgcryAlgoname2Algo((char *)algoname); + if(algo == GCRY_CIPHER_NONE) { + ABORT_FINALIZE(RS_RET_CRY_INVLD_ALGO); + } + ctx->algo = algo; +finalize_it: + RETiRet; +} + /* As of some Linux and security expert I spoke to, /dev/urandom * provides very strong random numbers, even if it runs out of * entropy. As far as he knew, this is save for all applications @@ -310,7 +341,7 @@ seedIV(gcryfile gf, uchar **iv) } rsRetVal -rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname) +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname) { gcry_error_t gcryError; gcryfile gf = NULL; @@ -319,13 +350,9 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname) CHKiRet(gcryfileConstruct(ctx, &gf, fname)); - gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo); - gcryError = gcry_cipher_open( - &gf->chd, // gcry_cipher_hd_t * - GCRY_CIPHER, // int - gcry_mode, // int - 0); // unsigned int + gcryError = gcry_cipher_open(&gf->chd, ctx->algo, ctx->mode, 0); if (gcryError) { dbgprintf("gcry_cipher_open failed: %s/%s\n", gcry_strsource(gcryError), diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 857d2352..d699124d 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -26,6 +26,8 @@ struct gcryctx_s { uchar *key; size_t keyLen; + int algo; + int mode; }; typedef struct gcryctx_s *gcryctx; typedef struct gcryfile_s *gcryfile; @@ -42,10 +44,12 @@ struct gcryfile_s { int rsgcryInit(void); void rsgcryExit(void); int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); +rsRetVal rsgcrySetMode(gcryctx ctx, uchar *algoname); +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, int gcry_mode, uchar *fname); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname); int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); /* error states */ @@ -57,4 +61,38 @@ int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); #define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info" #define ENCINFO_SUFFIX ".encinfo" +static inline int +rsgcryAlgoname2Algo(char *algoname) { + if(!strcmp((char*)algoname, "3DES")) return GCRY_CIPHER_3DES; + if(!strcmp((char*)algoname, "CAST5")) return GCRY_CIPHER_CAST5; + if(!strcmp((char*)algoname, "BLOWFISH")) return GCRY_CIPHER_BLOWFISH; + if(!strcmp((char*)algoname, "AES128")) return GCRY_CIPHER_AES128; + if(!strcmp((char*)algoname, "AES192")) return GCRY_CIPHER_AES192; + if(!strcmp((char*)algoname, "AES256")) return GCRY_CIPHER_AES256; + if(!strcmp((char*)algoname, "TWOFISH")) return GCRY_CIPHER_TWOFISH; + if(!strcmp((char*)algoname, "TWOFISH128")) return GCRY_CIPHER_TWOFISH128; + if(!strcmp((char*)algoname, "ARCFOUR")) return GCRY_CIPHER_ARCFOUR; + if(!strcmp((char*)algoname, "DES")) return GCRY_CIPHER_DES; + if(!strcmp((char*)algoname, "SERPENT128")) return GCRY_CIPHER_SERPENT128; + if(!strcmp((char*)algoname, "SERPENT192")) return GCRY_CIPHER_SERPENT192; + if(!strcmp((char*)algoname, "SERPENT256")) return GCRY_CIPHER_SERPENT256; + if(!strcmp((char*)algoname, "RFC2268_40")) return GCRY_CIPHER_RFC2268_40; + if(!strcmp((char*)algoname, "SEED")) return GCRY_CIPHER_SEED; + if(!strcmp((char*)algoname, "CAMELLIA128")) return GCRY_CIPHER_CAMELLIA128; + if(!strcmp((char*)algoname, "CAMELLIA192")) return GCRY_CIPHER_CAMELLIA192; + if(!strcmp((char*)algoname, "CAMELLIA256")) return GCRY_CIPHER_CAMELLIA256; + return GCRY_CIPHER_NONE; +} + +static inline int +rsgcryModename2Mode(char *modename) { + if(!strcmp((char*)modename, "ECB")) return GCRY_CIPHER_MODE_ECB; + if(!strcmp((char*)modename, "CFB")) return GCRY_CIPHER_MODE_CFB; + if(!strcmp((char*)modename, "CBC")) return GCRY_CIPHER_MODE_CBC; + if(!strcmp((char*)modename, "STREAM")) return GCRY_CIPHER_MODE_STREAM; + if(!strcmp((char*)modename, "OFB")) return GCRY_CIPHER_MODE_OFB; + if(!strcmp((char*)modename, "CTR")) return GCRY_CIPHER_MODE_CTR; + if(!strcmp((char*)modename, "AESWRAP")) return GCRY_CIPHER_MODE_AESWRAP; + return GCRY_CIPHER_MODE_NONE; +} #endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 881d047d..cc65051f 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -89,8 +89,9 @@ SetCnfParam(void *pT, struct nvlst *lst) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; int i, r; - uchar *cstr; uchar *key = NULL; + uchar *algo = NULL; + uchar *mode = NULL; struct cnfparamvals *pvals; DEFiRet; @@ -105,23 +106,30 @@ SetCnfParam(void *pT, struct nvlst *lst) continue; if(!strcmp(pblk.descr[i].name, "cry.key")) { key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); -#if 0 - } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { - cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); - gcrySetTimestamper(pThis->ctx, (char*) cstr); - free(cstr); - } else if(!strcmp(pblk.descr[i].name, "sig.block.sizelimit")) { - gcrySetBlockSizeLimit(pThis->ctx, pvals[i].val.d.n); - } else if(!strcmp(pblk.descr[i].name, "sig.keeprecordhashes")) { - gcrySetKeepRecordHashes(pThis->ctx, pvals[i].val.d.n); - } else if(!strcmp(pblk.descr[i].name, "sig.keeptreehashes")) { - gcrySetKeepTreeHashes(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "cry.mode")) { + mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "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); -#endif } } + if(algo != NULL) { + iRet = rsgcrySetAlgo(pThis->ctx, algo); + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "cry.algo '%s' is not know/supported", algo); + FINALIZE; + } + } + if(mode != NULL) { + iRet = rsgcrySetMode(pThis->ctx, mode); + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "cry.mode '%s' is not know/supported", mode); + FINALIZE; + } + } + /* note: key must be set AFTER algo/mode is set (as it depends on them) */ if(key != NULL) { errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the " "config file is highly insecure - DO NOT USE FOR PRODUCTION"); @@ -138,6 +146,8 @@ SetCnfParam(void *pT, struct nvlst *lst) memset(key, 0, strlen((char*)key)); free(key); } + free(algo); + free(mode); finalize_it: RETiRet; } @@ -151,7 +161,7 @@ OnFileOpen(void *pT, uchar *fn, void *pGF) DEFiRet; dbgprintf("DDDD: cry: onFileOpen: %s\n", fn); - CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, fn)); + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, fn)); finalize_it: /* TODO: enable this error message (need to cleanup loop first ;)) errmsg.LogError(0, iRet, "Encryption Provider" diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index ab57eace..4cdd1c1e 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -408,6 +408,8 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_EI_NO_EXISTS = -2323,/**< .encinfo file does not exist (status, not necessarily error!)*/ RS_RET_EI_WR_ERR = -2324,/**< error writing an .encinfo file */ RS_RET_EI_INVLD_FILE = -2325,/**< header indicates the file is no .encinfo file */ + RS_RET_CRY_INVLD_ALGO = -2326,/**< user specified invalid (unkonwn) crypto algorithm */ + RS_RET_CRY_INVLD_MODE = -2327,/**< user specified invalid (unkonwn) crypto mode */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/tools/rscryutil.c b/tools/rscryutil.c index e57eb625..e1e900a7 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -40,6 +40,9 @@ static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; +static char *cry_key = NULL; +static int cry_algo = GCRY_CIPHER_AES128; +static int cry_mode = GCRY_CIPHER_MODE_CBC; /* rectype/value must be EIF_MAX_*_LEN+1 long! * returns 0 on success or something else on error/EOF @@ -151,14 +154,13 @@ done: return r; } static int -initCrypt(FILE *eifp, int gcry_mode, char *key) +initCrypt(FILE *eifp) { - #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable int r = 0; - gcry_error_t gcryError; + gcry_error_t gcryError; char iv[4096]; - blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + blkLength = gcry_cipher_get_algo_blklen(cry_algo); if(blkLength > sizeof(iv)) { fprintf(stderr, "internal error[%s:%d]: block length %d too large for " "iv buffer\n", __FILE__, __LINE__, blkLength); @@ -166,15 +168,15 @@ initCrypt(FILE *eifp, int gcry_mode, char *key) } if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done; - size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); - if(strlen(key) != keyLength) { + size_t keyLength = gcry_cipher_get_algo_keylen(cry_algo); + if(strlen(cry_key) != keyLength) { fprintf(stderr, "invalid key length; key is %u characters, but " - "exactly %u characters are required\n", strlen(key), + "exactly %u characters are required\n", strlen(cry_key), keyLength); r = 1; goto done; } - gcryError = gcry_cipher_open(&gcry_chd, GCRY_CIPHER, gcry_mode, 0); + gcryError = gcry_cipher_open(&gcry_chd, cry_algo, cry_mode, 0); if (gcryError) { printf("gcry_cipher_open failed: %s/%s\n", gcry_strsource(gcryError), @@ -182,7 +184,7 @@ initCrypt(FILE *eifp, int gcry_mode, char *key) r = 1; goto done; } - gcryError = gcry_cipher_setkey(gcry_chd, key, keyLength); + gcryError = gcry_cipher_setkey(gcry_chd, cry_key, keyLength); if (gcryError) { printf("gcry_cipher_setkey failed: %s/%s\n", gcry_strsource(gcryError), @@ -225,10 +227,9 @@ done: return; static void decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) { - gcry_error_t gcryError; + gcry_error_t gcryError; size_t nRead, nWritten; size_t toRead; - size_t nPad; size_t leftTillBlkEnd; char buf[64*1024]; @@ -240,7 +241,6 @@ decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) if(nRead == 0) break; leftTillBlkEnd -= nRead, *pCurrOffs += nRead; - nPad = (blkLength - nRead % blkLength) % blkLength; gcryError = gcry_cipher_decrypt( gcry_chd, // gcry_cipher_hd_t buf, // void * @@ -248,7 +248,7 @@ decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) NULL, // const void * 0); // size_t if (gcryError) { - fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", + fprintf(stderr, "gcry_cipher_decrypt failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; @@ -264,7 +264,7 @@ decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) static int -doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp, char *key) +doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp) { off64_t blkEnd; off64_t currOffs = 0; @@ -272,7 +272,7 @@ doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp, char *key) while(1) { /* process block */ - if(initCrypt(eifp, GCRY_CIPHER_MODE_CBC, key) != 0) + if(initCrypt(eifp) != 0) goto done; if((r = eiGetEND(eifp, &blkEnd)) != 0) goto done; decryptBlock(logfp, outfp, blkEnd, &currOffs); @@ -283,7 +283,7 @@ done: return r; } static void -decrypt(char *name, char *key) +decrypt(char *name) { FILE *logfp = NULL, *eifp = NULL; int r = 0; @@ -307,7 +307,7 @@ decrypt(char *name, char *key) goto err; } - doDecrypt(logfp, eifp, stdout, key); + doDecrypt(logfp, eifp, stdout); fclose(logfp); logfp = NULL; fclose(eifp); eifp = NULL; @@ -326,6 +326,8 @@ static struct option long_options[] = {"version", no_argument, NULL, 'V'}, {"decrypt", no_argument, NULL, 'd'}, {"key", required_argument, NULL, 'k'}, + {"algo", required_argument, NULL, 'a'}, + {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; @@ -334,10 +336,10 @@ main(int argc, char *argv[]) { int i; int opt; - char *key = ""; + int temp; while(1) { - opt = getopt_long(argc, argv, "dk:vV", long_options, NULL); + opt = getopt_long(argc, argv, "a:dk:m:vV", long_options, NULL); if(opt == -1) break; switch(opt) { @@ -348,7 +350,25 @@ main(int argc, char *argv[]) fprintf(stderr, "WARNING: specifying the actual key " "via the command line is highly insecure\n" "Do NOT use this for PRODUCTION use.\n"); - key = optarg; + cry_key = optarg; + break; + case 'a': + temp = rsgcryAlgoname2Algo(optarg); + if(temp == GCRY_CIPHER_NONE) { + fprintf(stderr, "ERROR: algorithm \"%s\" is not " + "kown/supported\n", optarg); + exit(1); + } + cry_algo = temp; + break; + case 'm': + temp = rsgcryModename2Mode(optarg); + if(temp == GCRY_CIPHER_MODE_NONE) { + fprintf(stderr, "ERROR: cipher mode \"%s\" is not " + "kown/supported\n", optarg); + exit(1); + } + cry_mode = temp; break; case 'v': verbose = 1; @@ -365,13 +385,12 @@ main(int argc, char *argv[]) } if(optind == argc) - decrypt("-", key); + decrypt("-"); else { for(i = optind ; i < argc ; ++i) - decrypt(argv[i], key); /* currently only mode ;) */ + decrypt(argv[i]); /* currently only mode ;) */ } - memset(key, 0, strlen(key)); /* zero-out key store */ + memset(cry_key, 0, strlen(cry_key)); /* zero-out key store */ return 0; } - //char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index 7e3ab5b4..3cc54f57 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -40,6 +40,13 @@ OPTIONS is highly insecure. However, it can be useful for intial testing steps. This option may be removed in the future. +-a, --algo + Sets the encryption algorightm (cipher) to be used. See below + for supported algorithms. The default is "AES128". + +-m, --mode + Sets the ciphermode to be used. See below for supported modes. + The default is "CBC". OPERATION MODES =============== @@ -62,6 +69,42 @@ The command returns an exit code of 0 if everything went fine, and some other code in case of failures. +SUPPORTED ALGORITHMS +==================== + +We basically support what libgcrypt supports. This is: + 3DES + CAST5 + BLOWFISH + AES128 + AES192 + AES256 + TWOFISH + TWOFISH128 + ARCFOUR + DES + SERPENT128 + SERPENT192 + SERPENT256 + RFC2268_40 + SEED + CAMELLIA128 + CAMELLIA192 + CAMELLIA256 + + +SUPPORTED CIPHER MODES +====================== + +We basically support what libgcrypt supports. This is: + ECB + CFB + CBC + STREAM + OFB + CTR + AESWRAP + EXAMPLES ======== -- cgit v1.2.3 From 7a62ef673f3aea7b0ad34e27a4cfaa5ba6e9efd1 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 12:52:59 +0200 Subject: logenc: support keyfiles in rscryutil --- runtime/Makefile.am | 14 ++++----- runtime/libgcry.c | 3 ++ runtime/libgcry.h | 1 + runtime/libgcry_common.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 2 +- tools/rscryutil.c | 81 ++++++++++++++++++++++++++++++++++++++++++------ tools/rscryutil.rst | 4 +++ 7 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 runtime/libgcry_common.c diff --git a/runtime/Makefile.am b/runtime/Makefile.am index e1f0673c..ee5a3ef2 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -1,6 +1,6 @@ sbin_PROGRAMS = man_MANS = -noinst_LTLIBRARIES = librsyslog.la librsgt.la +noinst_LTLIBRARIES = librsyslog.la pkglib_LTLIBRARIES = #pkglib_LTLIBRARIES = librsyslog.la @@ -178,15 +178,14 @@ endif # support library for libgcrypt # if ENABLE_LIBGCRYPT -#noinst_LTLIBRARIES += libgcry.la -#libgcry_la_SOURCES = libgcry.c libgcry.h -#libcgry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) + noinst_LTLIBRARIES += libgcry.la + libgcry_la_SOURCES = libgcry.c libgcry_common.c libgcry.h + libgcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) pkglib_LTLIBRARIES += lmcry_gcry.la - lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h libgcry.c libgcry.h + lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h lmcry_gcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) lmcry_gcry_la_LDFLAGS = -module -avoid-version `libgcrypt-config --libs` -#lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) - lmcry_gcry_la_LIBADD = $(LIBGCRYPT_LIBS) + lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) endif @@ -194,6 +193,7 @@ endif # support library for guardtime # if ENABLE_GUARDTIME + noinst_LTLIBRARIES += librsgt.la librsgt_la_SOURCES = librsgt.c librsgt_read.c librsgt.h pkglib_LTLIBRARIES += lmsig_gt.la lmsig_gt_la_SOURCES = lmsig_gt.c lmsig_gt.h diff --git a/runtime/libgcry.c b/runtime/libgcry.c index ef94e8ac..e57ee8bc 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include @@ -199,6 +201,7 @@ finalize_it: RETiRet; } + gcryctx gcryCtxNew(void) { diff --git a/runtime/libgcry.h b/runtime/libgcry.h index d699124d..5dde1576 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -41,6 +41,7 @@ struct gcryfile_s { gcryctx ctx; }; +int gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen); int rsgcryInit(void); void rsgcryExit(void); int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); diff --git a/runtime/libgcry_common.c b/runtime/libgcry_common.c new file mode 100644 index 00000000..49a0e669 --- /dev/null +++ b/runtime/libgcry_common.c @@ -0,0 +1,77 @@ +/* libgcry_common.c + * This file hosts functions both being used by the rsyslog runtime as + * well as tools who do not use the runtime (so we can maintain the + * code at a single place). + * + * 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. + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rsyslog.h" /* we need data typedefs */ +#include "libgcry.h" + + +/* read a key from a key file + * @param[out] key - key buffer, must be freed by caller + * @param[out] keylen - length of buffer + * @returns 0 if OK, something else otherwise (we do not use + * iRet as this is also called from non-rsyslog w/o runtime) + * The key length is limited to 64KiB to prevent DoS. + * Note well: key is a blob, not a C string (NUL may be present!) + */ +int +gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen) +{ + struct stat sb; + int fd; + int r; + + if(stat(fn, &sb) == -1) { + r = 1; goto done; + } + if((sb.st_mode & S_IFMT) != S_IFREG) { + r = 2; goto done; + } + if(sb.st_size > 64*1024) { + r = 3; goto done; + } + if((*key = malloc(sb.st_size)) == NULL) { + r = -1; goto done; + } + if((fd = open(fn, O_RDONLY)) < 0) { + r = 4; goto done; + } + if(read(fd, *key, sb.st_size) != sb.st_size) { + r = 5; goto done; + } + *keylen = sb.st_size; + close(fd); + r = 0; +done: return r; +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 0f2bb57e..938782f7 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -79,7 +79,7 @@ bin_PROGRAMS += rscryutil rscryutil = rscryutil.c rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) rscryutil_LDFLAGS = `libgcrypt-config --libs` -#rscryutil_LDFLAGS = $(LIBGCRYPT_LIBS) +rscryutil_LDADD = ../runtime/libgcry.la $(LIBGCRYPT_LIBS) rscryutil.1: rscryutil.rst $(AM_V_GEN) $(RST2MAN) $< $@ man1_MANS = rscryutil.1 diff --git a/tools/rscryutil.c b/tools/rscryutil.c index e1e900a7..9290db4d 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -34,13 +34,14 @@ #include "libgcry.h" -static enum { MD_DECRYPT +static enum { MD_DECRYPT, MD_WRITE_KEYFILE } mode = MD_DECRYPT; static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; static char *cry_key = NULL; +static unsigned cry_keylen = 0; static int cry_algo = GCRY_CIPHER_AES128; static int cry_mode = GCRY_CIPHER_MODE_CBC; @@ -171,7 +172,7 @@ initCrypt(FILE *eifp) size_t keyLength = gcry_cipher_get_algo_keylen(cry_algo); if(strlen(cry_key) != keyLength) { fprintf(stderr, "invalid key length; key is %u characters, but " - "exactly %u characters are required\n", strlen(cry_key), + "exactly %u characters are required\n", cry_keylen, keyLength); r = 1; goto done; } @@ -319,38 +320,82 @@ err: fclose(logfp); } +static void +write_keyfile(char *keyfile) +{ + FILE *fp; + + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); + exit(1); + } + if(keyfile == NULL) { + fprintf(stderr, "ERROR: keyfile must be set\n"); + exit(1); + } + if((fp = fopen(keyfile, "w")) == NULL) { + perror(keyfile); + exit(1); + } + if(fwrite(cry_key, cry_keylen, 1, fp) != 1) { + perror(keyfile); + exit(1); + } + fclose(fp); +} static struct option long_options[] = { {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"decrypt", no_argument, NULL, 'd'}, - {"key", required_argument, NULL, 'k'}, + {"write-keyfile", no_argument, NULL, 'W'}, + {"key", required_argument, NULL, 'K'}, + {"keyfile", required_argument, NULL, 'k'}, {"algo", required_argument, NULL, 'a'}, {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; +static void +getKeyFromFile(char *fn) +{ + int r; + r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); + if(r != 0) { + fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn); + exit(1); + } +} + int main(int argc, char *argv[]) { int i; int opt; int temp; + char *keyfile = NULL; while(1) { - opt = getopt_long(argc, argv, "a:dk:m:vV", long_options, NULL); + opt = getopt_long(argc, argv, "a:dk:K:m:vVW", long_options, NULL); if(opt == -1) break; switch(opt) { case 'd': mode = MD_DECRYPT; break; + case 'W': + mode = MD_WRITE_KEYFILE; + break; case 'k': + keyfile = optarg; + break; + case 'K': fprintf(stderr, "WARNING: specifying the actual key " "via the command line is highly insecure\n" "Do NOT use this for PRODUCTION use.\n"); cry_key = optarg; + cry_keylen = strlen(cry_key); break; case 'a': temp = rsgcryAlgoname2Algo(optarg); @@ -384,13 +429,29 @@ main(int argc, char *argv[]) } } - if(optind == argc) - decrypt("-"); - else { - for(i = optind ; i < argc ; ++i) - decrypt(argv[i]); /* currently only mode ;) */ + if(mode == MD_WRITE_KEYFILE) { + if(optind != argc) { + fprintf(stderr, "ERROR: no file parameters permitted in " + "--write-keyfile mode\n"); + exit(1); + } + write_keyfile(keyfile); + } else { + if(keyfile != NULL) + getKeyFromFile(keyfile); + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); + exit(1); + } + if(optind == argc) + decrypt("-"); + else { + for(i = optind ; i < argc ; ++i) + decrypt(argv[i]); + } } - memset(cry_key, 0, strlen(cry_key)); /* zero-out key store */ + memset(cry_key, 0, cry_keylen); /* zero-out key store */ + cry_keylen = 0; return 0; } diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index 3cc54f57..d6381011 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -31,6 +31,10 @@ OPTIONS -d, --decrypt Select decryption mode. This is the default mode. +-W, --write-keyfile + Utility function to write a key to a keyfile. The key can be obtained + via any method (except via a keyfile for obvious reasons). + -v, --verbose Select verbose mode. -- cgit v1.2.3 From 97cbbdac13c0e2a08a8f0cb716b0cdce9a2bb3cf Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 15:24:51 +0200 Subject: logenc: full support for keyfiles including their generation via rscrytool --- ChangeLog | 1 + runtime/lmcry_gcry.c | 30 ++++++++++++-- tools/rscryutil.c | 114 ++++++++++++++++++++++++++++++++++++--------------- tools/rscryutil.rst | 77 +++++++++++++++++++++++++++++++--- 4 files changed, 179 insertions(+), 43 deletions(-) diff --git a/ChangeLog b/ChangeLog index 97114227..a8f6b475 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version 7.3.10 [devel] 2013-04-?? - added RainerScript re_extract() function - templates now permit substring extraction relative to end-of-string +- added support for encrypting log files - bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements --------------------------------------------------------------------------- diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index cc65051f..bcc001fc 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -45,6 +45,7 @@ DEFobjCurrIf(glbl) /* tables for interfacing with the v6 config system */ static struct cnfparamdescr cnfpdescr[] = { { "cry.key", eCmdHdlrGetWord, 0 }, + { "cry.keyfile", eCmdHdlrGetWord, 0 }, { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ { "cry.algo", eCmdHdlrGetWord, 0 } }; @@ -89,7 +90,9 @@ SetCnfParam(void *pT, struct nvlst *lst) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; int i, r; + unsigned keylen; uchar *key = NULL; + uchar *keyfile = NULL; uchar *algo = NULL; uchar *mode = NULL; struct cnfparamvals *pvals; @@ -106,6 +109,8 @@ SetCnfParam(void *pT, struct nvlst *lst) continue; if(!strcmp(pblk.descr[i].name, "cry.key")) { key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "cry.keyfile")) { + keyfile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(pblk.descr[i].name, "cry.mode")) { mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(pblk.descr[i].name, "cry.algo")) { @@ -130,22 +135,39 @@ SetCnfParam(void *pT, struct nvlst *lst) } } /* note: key must be set AFTER algo/mode is set (as it depends on them) */ + if(key != NULL && keyfile != NULL) { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "only one of the following " + "parameters can be specified: cry.key, cry.keyfile\n"); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } if(key != NULL) { errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the " "config file is highly insecure - DO NOT USE FOR PRODUCTION"); - r = rsgcrySetKey(pThis->ctx, key, strlen((char*)key)); - if(r > 0) { - errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Key length %d expected, but " - "key of length %d given", r, strlen((char*)key)); + keylen = strlen((char*)key); + } + if(keyfile != NULL) { + r = gcryGetKeyFromFile((char*)keyfile, (char**)&key, &keylen); + if(r != 0) { + errmsg.LogError(0, RS_RET_ERR, "error %d reading keyfile %s\n", + r, keyfile); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } } + /* if we reach this point, we have a valid key */ + r = rsgcrySetKey(pThis->ctx, key, keylen); + if(r > 0) { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Key length %d expected, but " + "key of length %d given", r, keylen); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } + cnfparamvalsDestruct(pvals, &pblk); if(key != NULL) { memset(key, 0, strlen((char*)key)); free(key); } + free(keyfile); free(algo); free(mode); finalize_it: diff --git a/tools/rscryutil.c b/tools/rscryutil.c index 9290db4d..be14cde9 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include "rsyslog.h" @@ -40,10 +43,13 @@ static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; +static char *keyfile = NULL; +static int randomKeyLen = -1; static char *cry_key = NULL; static unsigned cry_keylen = 0; static int cry_algo = GCRY_CIPHER_AES128; static int cry_mode = GCRY_CIPHER_MODE_CBC; +static int optionForce = 0; /* rectype/value must be EIF_MAX_*_LEN+1 long! * returns 0 on success or something else on error/EOF @@ -321,27 +327,70 @@ err: } static void -write_keyfile(char *keyfile) +write_keyfile(char *fn) { - FILE *fp; - - if(cry_key == NULL) { - fprintf(stderr, "ERROR: key must be set via some method\n"); + int fd; + int r; + mode_t fmode; + + fmode = O_WRONLY|O_CREAT; + if(!optionForce) + fmode |= O_EXCL; + if((fd = open(fn, fmode, S_IRUSR)) == -1) { + fprintf(stderr, "error opening keyfile "); + perror(fn); exit(1); } - if(keyfile == NULL) { - fprintf(stderr, "ERROR: keyfile must be set\n"); + if((r = write(fd, cry_key, cry_keylen)) != (ssize_t)cry_keylen) { + fprintf(stderr, "error writing keyfile (ret=%d) ", r); + perror(fn); exit(1); } - if((fp = fopen(keyfile, "w")) == NULL) { - perror(keyfile); + close(fd); +} + +static void +getKeyFromFile(char *fn) +{ + int r; + r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); + if(r != 0) { + fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn); exit(1); } - if(fwrite(cry_key, cry_keylen, 1, fp) != 1) { - perror(keyfile); +} + +static void +getRandomKey(void) +{ + int fd; + cry_keylen = randomKeyLen; + cry_key = malloc(randomKeyLen); /* do NOT zero-out! */ + /* if we cannot obtain data from /dev/urandom, we use whatever + * is present at the current memory location as random data. Of + * course, this is very weak and we should consider a different + * option, especially when not running under Linux (for Linux, + * unavailability of /dev/urandom is just a theoretic thing, it + * will always work...). -- TODO -- rgerhards, 2013-03-06 + */ + if((fd = open("/dev/urandom", O_RDONLY)) > 0) { + if(read(fd, cry_key, randomKeyLen)) {}; /* keep compiler happy */ + close(fd); + } +} + + +static void +setKey() +{ + if(randomKeyLen != -1) + getRandomKey(); + else if(keyfile != NULL) + getKeyFromFile(keyfile); + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); exit(1); } - fclose(fp); } static struct option long_options[] = @@ -349,35 +398,26 @@ static struct option long_options[] = {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"decrypt", no_argument, NULL, 'd'}, - {"write-keyfile", no_argument, NULL, 'W'}, + {"force", no_argument, NULL, 'f'}, + {"write-keyfile", required_argument, NULL, 'W'}, {"key", required_argument, NULL, 'K'}, + {"generate-random-key", required_argument, NULL, 'r'}, {"keyfile", required_argument, NULL, 'k'}, {"algo", required_argument, NULL, 'a'}, {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; -static void -getKeyFromFile(char *fn) -{ - int r; - r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); - if(r != 0) { - fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn); - exit(1); - } -} - int main(int argc, char *argv[]) { int i; int opt; int temp; - char *keyfile = NULL; + char *newKeyFile = NULL; while(1) { - opt = getopt_long(argc, argv, "a:dk:K:m:vVW", long_options, NULL); + opt = getopt_long(argc, argv, "a:dfk:K:m:r:vVW:", long_options, NULL); if(opt == -1) break; switch(opt) { @@ -386,10 +426,22 @@ main(int argc, char *argv[]) break; case 'W': mode = MD_WRITE_KEYFILE; + newKeyFile = optarg; break; case 'k': keyfile = optarg; break; + case 'f': + optionForce = 1; + break; + case 'r': + randomKeyLen = atoi(optarg); + if(randomKeyLen > 64*1024) { + fprintf(stderr, "ERROR: keys larger than 64KiB are " + "not supported\n"); + exit(1); + } + break; case 'K': fprintf(stderr, "WARNING: specifying the actual key " "via the command line is highly insecure\n" @@ -429,20 +481,16 @@ main(int argc, char *argv[]) } } + setKey(); + if(mode == MD_WRITE_KEYFILE) { if(optind != argc) { fprintf(stderr, "ERROR: no file parameters permitted in " "--write-keyfile mode\n"); exit(1); } - write_keyfile(keyfile); + write_keyfile(newKeyFile); } else { - if(keyfile != NULL) - getKeyFromFile(keyfile); - if(cry_key == NULL) { - fprintf(stderr, "ERROR: key must be set via some method\n"); - exit(1); - } if(optind == argc) decrypt("-"); else { diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index d6381011..c546d855 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -7,7 +7,7 @@ Manage Encrypted Log Files -------------------------- :Author: Rainer Gerhards -:Date: 2013-04-08 +:Date: 2013-04-15 :Manual section: 1 SYNOPSIS @@ -31,14 +31,22 @@ OPTIONS -d, --decrypt Select decryption mode. This is the default mode. --W, --write-keyfile +-W, --write-keyfile Utility function to write a key to a keyfile. The key can be obtained - via any method (except via a keyfile for obvious reasons). + via any method. -v, --verbose Select verbose mode. --k, --key +-f, --force + Forces operations that otherwise would fail. + +-k, --keyfile + Reads the key from . File _must_ contain the key, only, no headers + or other meta information. Keyfiles can be generated via the + *--write-keyfile* option. + +-K, --key TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified on the command line. This is the actual key, and as such this mode is highly insecure. However, it can be useful for intial testing @@ -52,6 +60,11 @@ OPTIONS Sets the ciphermode to be used. See below for supported modes. The default is "CBC". +-r, --generate-random-key + Generates a random key of length . This option is + meant to be used together with *--write-keyfile* (and it is hard + to envision any other valid use for it). + OPERATION MODES =============== @@ -64,7 +77,25 @@ unpredictable. decrypt ------- -The provided log files are decrypted. +The provided log files are decrypted. Note that the *.encinfo* side files +must exist and be accessible in order for decryption to to work. + +write-keyfile +------------- + +In this mode no log files are processed; thus it is an error to specify +any on the command line. The specified keyfile is written. The key itself +is obtained via the usual key commands. If *--keyfile* is used, that +file is effectively copied. + +For security reasons, existing key files are _not_ overwritten. To permit +this, specify the *--force* option. When doing so, keep in mind that lost +keys cannot be recovered and data encrypted with them may also be considered +lost. + +Keyfiles are always created with 0400 permission, that is read access for only +the user. An exception is when an existing file is overwritten via the +*--force* option, in which case the former permissions still apply. EXIT CODES ========== @@ -77,6 +108,7 @@ SUPPORTED ALGORITHMS ==================== We basically support what libgcrypt supports. This is: + 3DES CAST5 BLOWFISH @@ -101,6 +133,7 @@ SUPPORTED CIPHER MODES ====================== We basically support what libgcrypt supports. This is: + ECB CFB CBC @@ -116,9 +149,41 @@ EXAMPLES Decrypts "logfile" and sends data to stdout. + +**rscryutil --generate-random-key 16 --keyfile /some/secured/path/keyfile** + +Generates random key and stores it in the specified keyfile. + +LOG SIGNATURES +============== + +Encrypted log files can be used together with signing. To verify such a file, +it must be decrypted first, and the verification tool **rsgtutil(1)** must be +run on the decrypted file. + +SECURITY CONSIDERATIONS +======================= + +Specifying keys directly on the command line (*--key* option) is very +insecure and should +not be done, except for testing purposes with test keys. Even then it is +recommended to use keyfiles, which are also easy to handle during testing. +Keep in mind that command history is usally be kept by bash and can also +easily be monitored. + +Local keyfiles are also a security risk. At a minimum, they should be +used with very restrictive file permissions. For this reason, +the *rscryutil* tool creates them with read permissions for the user, +only, no matter what umask is set to. + +When selecting cipher algorithms and modes, care needs to be taken. The +defaults should be reasonable safe to use, but this tends to change over +time. Keep up with the most current crypto recommendations. + + SEE ALSO ======== -**rsyslogd(8)** +**rsgtutil(1)**, **rsyslogd(8)** COPYRIGHT ========= -- cgit v1.2.3 From 64102e8cc352ffc542dca1dfcdd50f5ae776dc1f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 17:50:08 +0200 Subject: logenc: add capability to use key generation program to rscryutil --- runtime/libgcry_common.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ tools/rscryutil.c | 9 +++- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/runtime/libgcry_common.c b/runtime/libgcry_common.c index 49a0e669..63b5e5d5 100644 --- a/runtime/libgcry_common.c +++ b/runtime/libgcry_common.c @@ -75,3 +75,132 @@ gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen) r = 0; done: return r; } + + +/* execute the child process (must be called in child context + * after fork). + */ + +static void +execKeyScript(char *cmd, int pipefd[]) +{ + char *newargv[] = { NULL }; + char *newenviron[] = { NULL }; + + dup2(pipefd[0], STDIN_FILENO); + dup2(pipefd[1], STDOUT_FILENO); + + /* finally exec child */ +fprintf(stderr, "pre execve: %s\n", cmd); + execve(cmd, newargv, newenviron); + /* switch to? + execlp((char*)program, (char*) program, (char*)arg, NULL); + */ + + /* we should never reach this point, but if we do, we terminate */ +done: return; +} + + +static int +openPipe(char *cmd, int *fd) +{ + int pipefd[2]; + pid_t cpid; + int r; + + if(pipe(pipefd) == -1) { + r = 1; goto done; + } + + cpid = fork(); + if(cpid == -1) { + r = 1; goto done; + } + + if(cpid == 0) { + /* we are the child */ + execKeyScript(cmd, pipefd); + exit(1); + } + + close(pipefd[1]); + *fd = pipefd[0]; + r = 0; +done: return r; +} + + +/* Read a character from the program's output. */ +// TODO: highly unoptimized version, should be used in buffered +// mode +static int +readProgChar(int fd, char *c) +{ + int r; + if(read(fd, c, 1) != 1) { + r = 1; goto done; + } + r = 0; +done: return r; +} + +/* Read a line from the script. Line is terminated by LF, which + * is NOT put into the buffer. + * buf must be 64KiB + */ +static int +readProgLine(int fd, char *buf) +{ + char c; + int r; + unsigned i; + + for(i = 0 ; i < 64*1024 ; ++i) { + if((r = readProgChar(fd, &c)) != 0) goto done; + if(c == '\n') + break; + buf[i] = c; + }; + if(i >= 64*1024) { + r = 1; goto done; + } + buf[i] = '\0'; + r = 0; +done: return r; +} +static int +readProgKey(int fd, char *buf, unsigned keylen) +{ + char c; + int r; + unsigned i; + + for(i = 0 ; i < keylen ; ++i) { + if((r = readProgChar(fd, &c)) != 0) goto done; + buf[i] = c; + }; + r = 0; +done: return r; +} + +int +gcryGetKeyFromProg(char *cmd, char **key, unsigned *keylen) +{ + int r; + int fd; + char rcvBuf[64*1024]; + + if((r = openPipe(cmd, &fd)) != 0) goto done; + if((r = readProgLine(fd, rcvBuf)) != 0) goto done; + if(strcmp(rcvBuf, "RSYSLOG-KEY-PROVIDER:0")) { + r = 2; goto done; + } + if((r = readProgLine(fd, rcvBuf)) != 0) goto done; + *keylen = atoi(rcvBuf); + if((*key = malloc(*keylen)) == NULL) { + r = -1; goto done; + } + if((r = readProgKey(fd, *key, *keylen)) != 0) goto done; +done: return r; +} diff --git a/tools/rscryutil.c b/tools/rscryutil.c index be14cde9..2591b2cc 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -44,6 +44,7 @@ static gcry_cipher_hd_t gcry_chd; static size_t blkLength; static char *keyfile = NULL; +static char *keyprog = NULL; static int randomKeyLen = -1; static char *cry_key = NULL; static unsigned cry_keylen = 0; @@ -387,6 +388,8 @@ setKey() getRandomKey(); else if(keyfile != NULL) getKeyFromFile(keyfile); + else if(keyprog != NULL) + gcryGetKeyFromProg(keyprog, &cry_key, &cry_keylen); if(cry_key == NULL) { fprintf(stderr, "ERROR: key must be set via some method\n"); exit(1); @@ -403,6 +406,7 @@ static struct option long_options[] = {"key", required_argument, NULL, 'K'}, {"generate-random-key", required_argument, NULL, 'r'}, {"keyfile", required_argument, NULL, 'k'}, + {"key-program", required_argument, NULL, 'p'}, {"algo", required_argument, NULL, 'a'}, {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} @@ -417,7 +421,7 @@ main(int argc, char *argv[]) char *newKeyFile = NULL; while(1) { - opt = getopt_long(argc, argv, "a:dfk:K:m:r:vVW:", long_options, NULL); + opt = getopt_long(argc, argv, "a:dfk:K:m:p:r:vVW:", long_options, NULL); if(opt == -1) break; switch(opt) { @@ -431,6 +435,9 @@ main(int argc, char *argv[]) case 'k': keyfile = optarg; break; + case 'p': + keyprog = optarg; + break; case 'f': optionForce = 1; break; -- cgit v1.2.3 From 4e0c339586c5d6741434266810f7f75e632b03ce Mon Sep 17 00:00:00 2001 From: Tomas Heinrich Date: Fri, 12 Apr 2013 10:09:33 +0200 Subject: bugfix: prevent a segfault if ratelimit condition is not met Move the severity-check logic inside the ratelimiter and add a new function ratelimitSetSeverity() to manipulate the treshold. Currently only utilized by the imuxsock module. --- plugins/imuxsock/imuxsock.c | 9 +++++---- runtime/ratelimit.c | 14 +++++++++++++- runtime/ratelimit.h | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 0f4ded1d..95537475 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -412,6 +412,8 @@ addListner(instanceConf_t *inst) ratelimitSetLinuxLike(listeners[nfd].dflt_ratelimiter, listeners[nfd].ratelimitInterval, listeners[nfd].ratelimitBurst); + ratelimitSetSeverity(listeners[nfd].dflt_ratelimiter, + listeners[nfd].ratelimitSev); nfd++; } else { errmsg.LogError(0, NO_ERRCODE, "Out of unix socket name descriptors, ignoring %s\n", @@ -586,6 +588,7 @@ findRatelimiter(lstn_t *pLstn, struct ucred *cred, ratelimit_t **prl) pidbuf[sizeof(pidbuf)-1] = '\0'; /* to be on safe side */ CHKiRet(ratelimitNew(&rl, "imuxsock", pidbuf)); ratelimitSetLinuxLike(rl, pLstn->ratelimitInterval, pLstn->ratelimitBurst); + ratelimitSetSeverity(rl, pLstn->ratelimitSev); CHKmalloc(keybuf = malloc(sizeof(pid_t))); *keybuf = cred->pid; r = hashtable_insert(pLstn->ht, keybuf, rl); @@ -775,10 +778,7 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct tim facil = LOG_FAC(pri); sever = LOG_PRI(pri); - if(sever >= pLstn->ratelimitSev) { - /* note: if cred == NULL, then ratelimiter == NULL as well! */ - findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */ - } + findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */ if(ts == NULL) { datetime.getCurrTime(&st, &tt); @@ -1075,6 +1075,7 @@ activateListeners() ratelimitSetLinuxLike(listeners[0].dflt_ratelimiter, listeners[0].ratelimitInterval, listeners[0].ratelimitBurst); + ratelimitSetSeverity(listeners[0].dflt_ratelimiter,listeners[0].ratelimitSev); sd_fds = sd_listen_fds(0); if(sd_fds < 0) { diff --git a/runtime/ratelimit.c b/runtime/ratelimit.c index 4b618fb5..d83da2dd 100644 --- a/runtime/ratelimit.c +++ b/runtime/ratelimit.c @@ -202,7 +202,9 @@ ratelimitMsg(ratelimit_t *ratelimit, msg_t *pMsg, msg_t **ppRepMsg) DEFiRet; *ppRepMsg = NULL; - if(ratelimit->interval) { + /* Only the messages having severity level at or below the + * treshold (the value is >=) are subject to ratelimiting. */ + if(ratelimit->interval && (pMsg->iSeverity >= ratelimit->severity)) { if(withinRatelimit(ratelimit, pMsg->ttGenTime) == 0) { msgDestruct(&pMsg); ABORT_FINALIZE(RS_RET_DISCARDMSG); @@ -284,6 +286,7 @@ ratelimitNew(ratelimit_t **ppThis, char *modname, char *dynname) namebuf[sizeof(namebuf)-1] = '\0'; /* to be on safe side */ pThis->name = strdup(namebuf); } + /* pThis->severity == 0 - all messages are ratelimited */ pThis->bReduceRepeatMsgs = loadConf->globals.bReduceRepeatMsgs; *ppThis = pThis; finalize_it: @@ -316,6 +319,15 @@ ratelimitSetThreadSafe(ratelimit_t *ratelimit) pthread_mutex_init(&ratelimit->mut, NULL); } +/* Severity level determines which messages are subject to + * ratelimiting. Default (no value set) is all messages. + */ +void +ratelimitSetSeverity(ratelimit_t *ratelimit, intTiny severity) +{ + ratelimit->severity = severity; +} + void ratelimitDestruct(ratelimit_t *ratelimit) { diff --git a/runtime/ratelimit.h b/runtime/ratelimit.h index 820817bc..a058b069 100644 --- a/runtime/ratelimit.h +++ b/runtime/ratelimit.h @@ -26,6 +26,7 @@ struct ratelimit_s { /* support for Linux kernel-type ratelimiting */ unsigned short interval; unsigned short burst; + intTiny severity; /**< ratelimit only equal or lower severity levels (eq or higher values) */ unsigned done; unsigned missed; time_t begin; @@ -41,6 +42,7 @@ struct ratelimit_s { rsRetVal ratelimitNew(ratelimit_t **ppThis, char *modname, char *dynname); void ratelimitSetThreadSafe(ratelimit_t *ratelimit); void ratelimitSetLinuxLike(ratelimit_t *ratelimit, unsigned short interval, unsigned short burst); +void ratelimitSetSeverity(ratelimit_t *ratelimit, intTiny severity); rsRetVal ratelimitMsg(ratelimit_t *ratelimit, msg_t *pMsg, msg_t **ppRep); rsRetVal ratelimitAddMsg(ratelimit_t *ratelimit, multi_submit_t *pMultiSub, msg_t *pMsg); void ratelimitDestruct(ratelimit_t *pThis); -- cgit v1.2.3 From 0b4ed2c46a9b65e60bc0fb0f2532631a21917467 Mon Sep 17 00:00:00 2001 From: Tomas Heinrich Date: Fri, 12 Apr 2013 14:20:56 +0200 Subject: bugfix: set correct default value --- plugins/imuxsock/imuxsock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 95537475..c503852c 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -290,7 +290,7 @@ createInstance(instanceConf_t **pinst) inst->sockName = NULL; inst->pLogHostName = NULL; inst->ratelimitInterval = DFLT_ratelimitInterval; - inst->ratelimitBurst = DFLT_ratelimitSeverity; + inst->ratelimitBurst = DFLT_ratelimitBurst; inst->ratelimitSeverity = DFLT_ratelimitSeverity; inst->bUseFlowCtl = 0; inst->bIgnoreTimestamp = 1; -- cgit v1.2.3 From 2c704e09431de23c245d11d7627127c09a8efee5 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Sat, 13 Apr 2013 11:28:42 +0200 Subject: doc: add recent patch to ChangeLog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 85ac344b..1d04020e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,8 @@ --------------------------------------------------------------------------- +Version 7.3.11 [devel] 2013-04-?? +- bugfix: imuxsock aborted due to problem in ratelimiting code + Thanks to Tomas Heinrich for the patch. +--------------------------------------------------------------------------- Version 7.3.10 [devel] 2013-04-10 - added RainerScript re_extract() function - omrelp: added support for RainerScript-based configuration -- cgit v1.2.3 From fc0babb27d9021c103b05eaae0ccc6caef12137e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 15 Apr 2013 09:40:34 +0200 Subject: logenc: add key-program support to rsyslog crypto provider --- runtime/lmcry_gcry.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index bcc001fc..2e4cfff3 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -46,6 +46,7 @@ DEFobjCurrIf(glbl) static struct cnfparamdescr cnfpdescr[] = { { "cry.key", eCmdHdlrGetWord, 0 }, { "cry.keyfile", eCmdHdlrGetWord, 0 }, + { "cry.keyprogram", eCmdHdlrGetWord, 0 }, { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ { "cry.algo", eCmdHdlrGetWord, 0 } }; @@ -93,11 +94,14 @@ SetCnfParam(void *pT, struct nvlst *lst) unsigned keylen; uchar *key = NULL; uchar *keyfile = NULL; + uchar *keyprogram = NULL; uchar *algo = NULL; uchar *mode = NULL; + int nKeys; /* number of keys (actually methods) specified */ struct cnfparamvals *pvals; DEFiRet; + nKeys = 0; pvals = nvlstGetParams(lst, &pblk, NULL); if(Debug) { dbgprintf("param blk in lmcry_gcry:\n"); @@ -109,8 +113,13 @@ SetCnfParam(void *pT, struct nvlst *lst) continue; if(!strcmp(pblk.descr[i].name, "cry.key")) { key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + ++nKeys; } else if(!strcmp(pblk.descr[i].name, "cry.keyfile")) { keyfile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + ++nKeys; + } else if(!strcmp(pblk.descr[i].name, "cry.keyprogram")) { + keyprogram = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + ++nKeys; } else if(!strcmp(pblk.descr[i].name, "cry.mode")) { mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(pblk.descr[i].name, "cry.algo")) { @@ -135,9 +144,9 @@ SetCnfParam(void *pT, struct nvlst *lst) } } /* note: key must be set AFTER algo/mode is set (as it depends on them) */ - if(key != NULL && keyfile != NULL) { - errmsg.LogError(0, RS_RET_INVALID_PARAMS, "only one of the following " - "parameters can be specified: cry.key, cry.keyfile\n"); + if(nKeys != 1) { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "excactly one of the following " + "parameters can be specified: cry.key, cry.keyfile, cry.keyprogram\n"); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } if(key != NULL) { @@ -153,6 +162,14 @@ SetCnfParam(void *pT, struct nvlst *lst) ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } } + if(keyprogram != NULL) { + r = gcryGetKeyFromProg((char*)keyprogram, (char**)&key, &keylen); + if(r != 0) { + errmsg.LogError(0, RS_RET_ERR, "error %d obtaining key from program %s\n", + r, keyprogram); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } + } /* if we reach this point, we have a valid key */ r = rsgcrySetKey(pThis->ctx, key, keylen); -- cgit v1.2.3 From 15fc65f35079bdac64c48e9ae37833785a6d379d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 15 Apr 2013 18:40:06 +0200 Subject: add basic doc for encryption capability --- doc/cryprov_gcry.html | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/omfile.html | 7 ++- doc/sigprov_gt.html | 2 +- tools/rscryutil.rst | 7 +++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 doc/cryprov_gcry.html diff --git a/doc/cryprov_gcry.html b/doc/cryprov_gcry.html new file mode 100644 index 00000000..2568add9 --- /dev/null +++ b/doc/cryprov_gcry.html @@ -0,0 +1,121 @@ + + + + +libgcryt Log Crypto Provider (gcry) + + + +back to rsyslog module overview + +

    libgcrypt Log Crypto Provider (gcry)

    +

    Signature Provider Name:    gt

    +

    Author: Rainer Gerhards <rgerhards@adiscon.com>

    +

    Supported Since: since 7.3.10 +

    Description:

    +

    Provides encryption support to rsyslog. +

    + +

    Configuration Parameters:

    +

    Crypto providers are loaded by omfile, when the +provider is selected in its "cry.providerName" parameter. +Parameters for the provider are given in the omfile action instance +line. +

    This provider creates an encryption information file with the same base name but +the extension ".encinfo" for each log file (both for fixed-name files +as well as dynafiles). Both files together form a set. So you need to +archive both in order to prove integrity. +

      +
    • cry.algo <Encryption Algorithm>
      +The algorithm (cipher) to be used for encryption. +The default algorithm is "AES128". +
      Currently, the following Algorithms are supported: +
        +
      • 3DES +
      • CAST5 +
      • BLOWFISH +
      • AES128 +
      • AES192 +
      • AES256 +
      • TWOFISH +
      • TWOFISH128 +
      • ARCFOUR +
      • DES +
      • SERPENT128 +
      • SERPENT192 +
      • SERPENT256 +
      • RFC2268_40 +
      • SEED +
      • CAMELLIA128 +
      • CAMELLIA192 +
      • CAMELLIA256 +
      +
      + The actual availability of an algorithms depends on which ones + are compiled into libgcrypt. Note that some versions of libgcrypt + simply abort the process (rsyslogd in this case!) if a supported + algorithm is select but not available due to libgcrypt build + settings. There is nothing rsyslog can do against this. So in + order to avoid production downtime, always check carefully when + you change the algorithm. +
    • +
    • cry.mode <Algorithm Mode>
      +The encryption mode to be used. Default ist Cipher Block Chaining (CBC). +Note that not all encryption modes can be used together with all +algorithms. +
      Currently, the following modes are supported: +
        +
      • ECB +
      • CFB +
      • CBC +
      • STREAM +
      • OFB +
      • CTR +
      • AESWRAP +
      +
    • cry.key <encryption key>
      + TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified + inside rsyslog.conf. This is the actual key, and as such this mode + is highly insecure. However, it can be useful for intial testing + steps. This option may be removed in the future. +
    • +
    • cry.keyfile <filename>
      + Reads the key from the specified file. The file must contain the key, only, + no headers or other meta information. Keyfiles can be generated via the + rscrytool utility. +
    • +
    • cry.keyprogram <path to program>
      + If given, the key is provided by a so-called "key program". This program + is executed and must return the key to (as well as some meta information) + via stdout. The core idea of key programs is that using this interface the + user can implement as complex (and secure) method to obtain keys as + desired, all without the need to make modifications to rsyslog. +
    • +
    +Caveats/Known Bugs: +
      +
    • currently none known +
    • +
    +

    Samples:

    +

    This encrypts a log file. Default parameters are used, they key is +provided via a keyfile. +

    + +Note that the keyfile can be generated via the rscrytool utility (see its +documentation for how to actually do that). + + +

    [rsyslog.conf overview] +[manual index] [rsyslog site]

    +

    This documentation is part of the +rsyslog +project.
    +Copyright © 2013 by +Rainer Gerhards and +Adiscon. +Released under the GNU GPL version 3 or higher.

    + diff --git a/doc/omfile.html b/doc/omfile.html index a980d37f..06b738bb 100644 --- a/doc/omfile.html +++ b/doc/omfile.html @@ -83,11 +83,16 @@
  • DynaFile
    For each message, the file name is generated based on the given template. Then, this file is opened. As with the ``file'' property, data is appended if the file already exists. If the file does not exist, a new file is created. A cache of recent files is kept. Note that this cache can consume quite some memory (especially if large buffer sizes are used). Files are kept open as long as they stay inside the cache. Currently, files are only evicted from the cache when there is need to do so (due to insufficient cache size). To force-close (and evict) a dynafile from cache, send a HUP signal to rsyslogd.

  • -
  • Sig.Provider [ProviderName]
    +
  • Sig.Provider [ProviderName]
    Selects a signature provider for log signing. Currently, there only is one provider called "gt".

  • +
  • Cry.Provider [ProviderName]
    + Selects a crypto provider for log encryption. Currently, + there only is one provider called + "gcry".

  • +
  • Template [templateName]
    sets a new default template for file actions.

  • diff --git a/doc/sigprov_gt.html b/doc/sigprov_gt.html index 18b0ed10..caeee116 100644 --- a/doc/sigprov_gt.html +++ b/doc/sigprov_gt.html @@ -11,7 +11,7 @@

    GuardTime Log Signature Provider (gt)

    Signature Provider Name:    gt

    Author: Rainer Gerhards <rgerhards@adiscon.com>

    -

    Multi-Ruleset Support: since 7.3.9 +

    Supported Since: since 7.3.9

    Description:

    Provides the ability to sign syslog messages via the GuardTime signature services. diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index c546d855..dfd447d2 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -46,6 +46,13 @@ OPTIONS or other meta information. Keyfiles can be generated via the *--write-keyfile* option. +-p, --key-program + In this mode, the key is provided by a so-called "key program". This program + is executed and must return the key to (as well as some meta information) + via stdout. The core idea of key programs is that using this interface the + user can implement as complex (and secure) method to obtain keys as + desired, all without the need to make modifications to rsyslog. + -K, --key TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified on the command line. This is the actual key, and as such this mode -- cgit v1.2.3 From 89ace1e401dc179a20ec283c2f7c2f80becf4700 Mon Sep 17 00:00:00 2001 From: Louis Bouchard Date: Wed, 17 Apr 2013 08:52:17 +0200 Subject: bugfix: $PreserveFQDN is not properly working closes: http://bugzilla.adiscon.com/show_bug.cgi?id=426 --- ChangeLog | 3 +++ tools/syslogd.c | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index a8f6b475..b8027636 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,9 @@ Version 7.3.10 [devel] 2013-04-?? - added support for encrypting log files - bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements +- bugfix: $PreserveFQDN is not properly working + Thanks to Louis Bouchard for the patch + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=426 --------------------------------------------------------------------------- Version 7.3.9 [devel] 2013-03-27 - support for signing logs added diff --git a/tools/syslogd.c b/tools/syslogd.c index e291ba47..fe1205dd 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -1528,6 +1528,13 @@ queryLocalHostname(void) */ glbl.SetLocalHostName(LocalHostName); glbl.SetLocalDomain(LocalDomain); + + if ( strlen((char*)LocalDomain) ) { + CHKmalloc(LocalFQDNName = (uchar*)malloc(strlen((char*)LocalDomain)+strlen((char*)LocalHostName)+1)); + if ( sprintf((char*)LocalFQDNName,"%s.%s",(char*)LocalHostName,(char*)LocalDomain) ) + glbl.SetLocalFQDNName(LocalFQDNName); + } + glbl.GenerateLocalHostNameProperty(); /* must be redone after conf processing, FQDN setting may have changed */ finalize_it: RETiRet; -- cgit v1.2.3 From 8b42f06d338b425f164cb3c534fb2dffe474221c Mon Sep 17 00:00:00 2001 From: Florian Riedl Date: Wed, 17 Apr 2013 09:12:55 +0200 Subject: doc: improve action doc --- doc/rsyslog_conf_actions.html | 118 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/doc/rsyslog_conf_actions.html b/doc/rsyslog_conf_actions.html index 0c7705f8..a468a403 100644 --- a/doc/rsyslog_conf_actions.html +++ b/doc/rsyslog_conf_actions.html @@ -3,6 +3,123 @@

    This is a part of the rsyslog.conf documentation.

    back +

    Actions

    +The action describes what to do with a log message. In general, +message content is written to a kind of "logfile". +But also other actions might be done, like writing to a database +table or forwarding to another host.
    +
    +There is a certain base format to configure a action that needs to follow +either a selector or a filter. If no selector or filter is configured +before the action line begins, all messages will be used automatically.
    +
    +A action line could look like this

    +

    *.auth action(type="omfwd" file="/var/log/auth")

    +

    Or a little simpler

    +

    action(type="omfwd" file="/var/log/auth")

    +

    without a selector, which will execute the action for all messages. +The command to initiate a action is always action(). +In the brackets you determine the parameters for the action, like type, +destination or special processing parameters. There are two types of action +parameters. The first are the general action parameters described below. +The second type are the module instance parameters. They are described in +the documentation of the respective module.

    +

    General Action Parameters

    +
      +
    • name word +
      used primarily for documentation, e.g. when generating a configuration graph.
    • +
    • type string +
      Mandatory parameter for every action. The name of the module that should be used.
    • +
    • action.writeallmarkmessages on/off +
      [available since 5.1.5] - Normally, mark messages are written to actions only if the action was not recently executed (by default, recently means within the past 20 minutes). If this setting is switched to "on", mark messages are always sent to actions, no matter how recently they have been executed. In this mode, mark messages can be used as a kind of heartbeat. Note that this option auto-resets to "off", so if you intend to use it with multiple actions, it must be specified in front off all selector lines that should provide this functionality.
    • +
    • action.execonlyeverynthtime integer +
      If configured, the next action will only be executed every n-th time. For example, if configured to 3, the first two messages that go into the action will be dropped, the 3rd will actually cause the action to execute, the 4th and 5th will be dropped, the 6th executed under the action, ... and so on. Note: this setting is automatically re-set when the actual action is defined.
    • +
    • action.execonlyeverynthtimeout integer +
      Has a meaning only if Action.ExecOnlyEveryNthTime is also configured for the same action. If so, the timeout setting specifies after which period the counting of "previous actions" expires and a new action count is begun. Specify 0 (the default) to disable timeouts. +Why is this option needed? Consider this case: a message comes in at, eg., 10am. That's count 1. Then, nothing happens for the next 10 hours. At 8pm, the next one occurs. That's count 2. Another 5 hours later, the next message occurs, bringing the total count to 3. Thus, this message now triggers the rule. +The question is if this is desired behavior? Or should the rule only be triggered if the messages occur within an e.g. 20 minute window? If the later is the case, you need a +
      Action.ExecOnlyEveryNthTimeTimeout="1200" +
      This directive will timeout previous messages seen if they are older than 20 minutes. In the example above, the count would now be always 1 and consequently no rule would ever be triggered.
    • +
    • action.execonlyonceeveryinterval integer +
      Execute action only if the last execute is at last seconds in the past (more info in ommail, but may be used with any action)
    • +
    • action.execonlywhenpreviousissuspended on/off +
      This directive allows to specify if actions should always be executed ("off," the default) or only if the previous action is suspended ("on"). This directive works hand-in-hand with the multiple actions per selector feature. It can be used, for example, to create rules that automatically switch destination servers or databases to a (set of) backup(s), if the primary server fails. Note that this feature depends on proper implementation of the suspend feature in the output module. All built-in output modules properly support it (most importantly the database write and the syslog message forwarder).
    • +
    • action.repeatedmsgcontainsoriginalmsg on/off +
      "last message repeated n times" messages, if generated, have a different format that contains the message that is being repeated. Note that only the first "n" characters are included, with n to be at least 80 characters, most probably more (this may change from version to version, thus no specific limit is given). The bottom line is that n is large enough to get a good idea which message was repeated but it is not necessarily large enough for the whole message. (Introduced with 4.1.5). Once set, it affects all following actions.
    • +
    • action.resumeretrycount integer +
      [default 0, -1 means eternal]
    • +
    • action.resumeinterval integer +
      Sets the ActionResumeInterval for the action. The interval provided is always in seconds. Thus, multiply by 60 if you need minutes and 3,600 if you need hours (not recommended). +When an action is suspended (e.g. destination can not be connected), the action is resumed for the configured interval. Thereafter, it is retried. If multiple retires fail, the interval is automatically extended. This is to prevent excessive ressource use for retires. After each 10 retries, the interval is extended by itself. To be precise, the actual interval is (numRetries / 10 + 1) * Action.ResumeInterval. so after the 10th try, it by default is 60 and after the 100th try it is 330.
    • +
    +

    Action Queues

    +

    For some types of action it would make sense to have a queue ready which +will "store" log messages on a interim basis to prevent message loss. This +commonly applies to the forwarding kind of actions. Here is a list of Parameters +that are used for a action queue. Queues are described in the respective article.

    +
      +
    • Action.QueueCheckpointInterval number
    • +
    • ActionQueueDequeueBatchSize number (default 16)
    • +
    • ActionQueueDequeueSlowdown number +
      number is timeout in microseconds (1000000us is 1sec!), default 0 (no delay). Simple rate-limiting!
    • +
    • ActionQueueDiscardMark number (default 9750)
    • +
    • ActionQueueDiscardSeverity number +
      *numerical* severity! default 8 (nothing discarded)
    • +
    • ActionQueueFileName name
    • +
    • ActionQueueHighWaterMark number (default 8000)
    • +
    • ActionQueueImmediateShutdown on/off
    • +
    • ActionQueueSize number
    • +
    • ActionQueueLowWaterMark number (default 2000)
    • +
    • ActionQueueMaxFileSize size_nbr (default 1m)
    • +
    • ActionQueueTimeoutActionCompletion number +
      number is timeout in ms (1000ms is 1sec!), default 1000, 0 means immediate!
    • +
    • ActionQueueTimeoutEnqueue number +
      number is timeout in ms (1000ms is 1sec!), default 2000, 0 means indefinite
    • +
    • ActionQueueTimeoutShutdown number +
      number is timeout in ms (1000ms is 1sec!), default 0 (indefinite)
    • +
    • ActionQueueWorkerTimeoutThreadShutdown number +
      number is timeout in ms (1000ms is 1sec!), default 60000 (1 minute)
    • +
    • ActionQueueType FixedArray/LinkedList/Direct/Disk
    • +
    • ActionQueueSaveOnShutdown on/off
    • +
    • ActionQueueWorkerThreads number +
      number of worker threads, default 1, recommended 1
    • +
    • ActionQueueWorkerThreadMinumumMessages number (default 100)
    • +
    +

    Multiple Actions

    +

    You can have multiple actions for a single selector (or more precisely +a single filter of such a selector line). Each action must be called with +it's own action() statement and must all be enclosed by square brackets. +An example would be

    +

    *.=crit[ +
    action(type="omfwd" target="10.10.10.1" port="514" transport="udp") +
    action(type="omusrmsg" user="rger") +
    action(type="omfile" file="/var/log/critmsgs") +
    ]

    +

    These three lines send critical messages to a host via UDP, the user rger +and also store them in /var/log/critmsgs. Using multiple actions per selector +is convenient and also offers a performance benefit. As the filter needs to +be evaluated only once, there is less computation required to process +the directive

    +

    Stop/Discard

    +

    The stop action is no real action. It is basically a trigger to stop +processing of the message. Though, it is basically used instead of action() +in a selector line. For obvious reasons, the result of "stop" is depending +on where in the configuration it is used. Please note, that once processing +of a message has been stopped, there is no way to retrieve it in later +configuration file lines.
    +
    +Stop can be highly effective if you want to filter out some annoying +messages that otherwise would fill your log files. To do that, place the +discard actions early in your log files. This often plays well with +property-based filters, giving you great freedom in specifying what you +do not want.
    +
    +It can be used like this:

    +

    Stop

    +

    Here, processing for all messages will be stopped and the messages will +be discarded. Though, using it this way is obviously stupid, especially at +the beginning of the configuration.

    +

    Actions (legacy format)

    The action field of a rule describes what to do with the message. In general, message content is written to a kind of "logfile". @@ -331,7 +448,6 @@ one template name for each given action. The default template is specific to each action. For a description of what a template is and what you can do with it, see "TEMPLATES" at the top of this document.

    -

    [manual index] [rsyslog.conf] [rsyslog site]

    -- cgit v1.2.3 From 1f3b2e2c74dad8ac0eb554372e4128b65ae8e772 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 17 Apr 2013 09:42:05 +0200 Subject: doc: further improve action doc --- doc/rsyslog_conf_actions.html | 167 +++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 107 deletions(-) diff --git a/doc/rsyslog_conf_actions.html b/doc/rsyslog_conf_actions.html index a468a403..fa240d97 100644 --- a/doc/rsyslog_conf_actions.html +++ b/doc/rsyslog_conf_actions.html @@ -4,129 +4,82 @@

    This is a part of the rsyslog.conf documentation.

    back

    Actions

    -The action describes what to do with a log message. In general, -message content is written to a kind of "logfile". -But also other actions might be done, like writing to a database -table or forwarding to another host.
    -
    -There is a certain base format to configure a action that needs to follow -either a selector or a filter. If no selector or filter is configured -before the action line begins, all messages will be used automatically.
    -
    -A action line could look like this

    -

    *.auth action(type="omfwd" file="/var/log/auth")

    -

    Or a little simpler

    -

    action(type="omfwd" file="/var/log/auth")

    -

    without a selector, which will execute the action for all messages. -The command to initiate a action is always action(). -In the brackets you determine the parameters for the action, like type, -destination or special processing parameters. There are two types of action -parameters. The first are the general action parameters described below. -The second type are the module instance parameters. They are described in -the documentation of the respective module.

    +Action object describe what is to be done with a message. They are +implemented via outpout modules. +

    The action object has different parameters: +

      +
    • those that apply to all actions and are action specific. These + are documented below. +
    • parameters for the action queue. While they also apply to + all parameters, they are queue-specific, not action-specific (they + are the same that are used in rulesets, for example). +
    • action-specific parameters. These are specific to a certain + type of actions. They are documented by the output module + in question. +

    General Action Parameters

      -
    • name word -
      used primarily for documentation, e.g. when generating a configuration graph.
    • -
    • type string +
    • name word +
      used for statistics gathering and documentation +
    • type string
      Mandatory parameter for every action. The name of the module that should be used.
    • -
    • action.writeallmarkmessages on/off -
      [available since 5.1.5] - Normally, mark messages are written to actions only if the action was not recently executed (by default, recently means within the past 20 minutes). If this setting is switched to "on", mark messages are always sent to actions, no matter how recently they have been executed. In this mode, mark messages can be used as a kind of heartbeat. Note that this option auto-resets to "off", so if you intend to use it with multiple actions, it must be specified in front off all selector lines that should provide this functionality.
    • -
    • action.execonlyeverynthtime integer +
    • action.writeAllMarkMessages on/off +
      Normally, mark messages are written to actions only if the action was not recently executed (by default, recently means within the past 20 minutes). If this setting is switched to "on", mark messages are always sent to actions, no matter how recently they have been executed. In this mode, mark messages can be used as a kind of heartbeat. Note that this option auto-resets to "off", so if you intend to use it with multiple actions, it must be specified in front off all selector lines that should provide this functionality.
    • +
    • action.execOnlyEveryNthTime integer
      If configured, the next action will only be executed every n-th time. For example, if configured to 3, the first two messages that go into the action will be dropped, the 3rd will actually cause the action to execute, the 4th and 5th will be dropped, the 6th executed under the action, ... and so on. Note: this setting is automatically re-set when the actual action is defined.
    • -
    • action.execonlyeverynthtimeout integer +
    • action.execOnlyEveryNthTimeout integer
      Has a meaning only if Action.ExecOnlyEveryNthTime is also configured for the same action. If so, the timeout setting specifies after which period the counting of "previous actions" expires and a new action count is begun. Specify 0 (the default) to disable timeouts. Why is this option needed? Consider this case: a message comes in at, eg., 10am. That's count 1. Then, nothing happens for the next 10 hours. At 8pm, the next one occurs. That's count 2. Another 5 hours later, the next message occurs, bringing the total count to 3. Thus, this message now triggers the rule. The question is if this is desired behavior? Or should the rule only be triggered if the messages occur within an e.g. 20 minute window? If the later is the case, you need a
      Action.ExecOnlyEveryNthTimeTimeout="1200"
      This directive will timeout previous messages seen if they are older than 20 minutes. In the example above, the count would now be always 1 and consequently no rule would ever be triggered.
    • -
    • action.execonlyonceeveryinterval integer +
    • action.execOnlyOnceEveryInterval integer
      Execute action only if the last execute is at last seconds in the past (more info in ommail, but may be used with any action)
    • -
    • action.execonlywhenpreviousissuspended on/off +
    • action.execOnlyWhenpReviousIsSuspended on/off
      This directive allows to specify if actions should always be executed ("off," the default) or only if the previous action is suspended ("on"). This directive works hand-in-hand with the multiple actions per selector feature. It can be used, for example, to create rules that automatically switch destination servers or databases to a (set of) backup(s), if the primary server fails. Note that this feature depends on proper implementation of the suspend feature in the output module. All built-in output modules properly support it (most importantly the database write and the syslog message forwarder).
    • -
    • action.repeatedmsgcontainsoriginalmsg on/off +
    • action.repeatedmsgcontainsoriginalmsg on/off
      "last message repeated n times" messages, if generated, have a different format that contains the message that is being repeated. Note that only the first "n" characters are included, with n to be at least 80 characters, most probably more (this may change from version to version, thus no specific limit is given). The bottom line is that n is large enough to get a good idea which message was repeated but it is not necessarily large enough for the whole message. (Introduced with 4.1.5). Once set, it affects all following actions.
    • -
    • action.resumeretrycount integer +
    • action.resumeRetryCount integer
      [default 0, -1 means eternal]
    • -
    • action.resumeinterval integer +
    • action.resumeInterval integer
      Sets the ActionResumeInterval for the action. The interval provided is always in seconds. Thus, multiply by 60 if you need minutes and 3,600 if you need hours (not recommended). When an action is suspended (e.g. destination can not be connected), the action is resumed for the configured interval. Thereafter, it is retried. If multiple retires fail, the interval is automatically extended. This is to prevent excessive ressource use for retires. After each 10 retries, the interval is extended by itself. To be precise, the actual interval is (numRetries / 10 + 1) * Action.ResumeInterval. so after the 10th try, it by default is 60 and after the 100th try it is 330.
    -

    Action Queues

    -

    For some types of action it would make sense to have a queue ready which -will "store" log messages on a interim basis to prevent message loss. This -commonly applies to the forwarding kind of actions. Here is a list of Parameters -that are used for a action queue. Queues are described in the respective article.

    -
      -
    • Action.QueueCheckpointInterval number
    • -
    • ActionQueueDequeueBatchSize number (default 16)
    • -
    • ActionQueueDequeueSlowdown number -
      number is timeout in microseconds (1000000us is 1sec!), default 0 (no delay). Simple rate-limiting!
    • -
    • ActionQueueDiscardMark number (default 9750)
    • -
    • ActionQueueDiscardSeverity number -
      *numerical* severity! default 8 (nothing discarded)
    • -
    • ActionQueueFileName name
    • -
    • ActionQueueHighWaterMark number (default 8000)
    • -
    • ActionQueueImmediateShutdown on/off
    • -
    • ActionQueueSize number
    • -
    • ActionQueueLowWaterMark number (default 2000)
    • -
    • ActionQueueMaxFileSize size_nbr (default 1m)
    • -
    • ActionQueueTimeoutActionCompletion number -
      number is timeout in ms (1000ms is 1sec!), default 1000, 0 means immediate!
    • -
    • ActionQueueTimeoutEnqueue number -
      number is timeout in ms (1000ms is 1sec!), default 2000, 0 means indefinite
    • -
    • ActionQueueTimeoutShutdown number -
      number is timeout in ms (1000ms is 1sec!), default 0 (indefinite)
    • -
    • ActionQueueWorkerTimeoutThreadShutdown number -
      number is timeout in ms (1000ms is 1sec!), default 60000 (1 minute)
    • -
    • ActionQueueType FixedArray/LinkedList/Direct/Disk
    • -
    • ActionQueueSaveOnShutdown on/off
    • -
    • ActionQueueWorkerThreads number -
      number of worker threads, default 1, recommended 1
    • -
    • ActionQueueWorkerThreadMinumumMessages number (default 100)
    • -
    -

    Multiple Actions

    -

    You can have multiple actions for a single selector (or more precisely -a single filter of such a selector line). Each action must be called with -it's own action() statement and must all be enclosed by square brackets. -An example would be

    -

    *.=crit[ -
    action(type="omfwd" target="10.10.10.1" port="514" transport="udp") -
    action(type="omusrmsg" user="rger") -
    action(type="omfile" file="/var/log/critmsgs") -
    ]

    -

    These three lines send critical messages to a host via UDP, the user rger -and also store them in /var/log/critmsgs. Using multiple actions per selector -is convenient and also offers a performance benefit. As the filter needs to -be evaluated only once, there is less computation required to process -the directive

    -

    Stop/Discard

    -

    The stop action is no real action. It is basically a trigger to stop -processing of the message. Though, it is basically used instead of action() -in a selector line. For obvious reasons, the result of "stop" is depending -on where in the configuration it is used. Please note, that once processing -of a message has been stopped, there is no way to retrieve it in later -configuration file lines.
    -
    -Stop can be highly effective if you want to filter out some annoying -messages that otherwise would fill your log files. To do that, place the -discard actions early in your log files. This often plays well with -property-based filters, giving you great freedom in specifying what you -do not want.
    -
    -It can be used like this:

    -

    Stop

    -

    Here, processing for all messages will be stopped and the messages will -be discarded. Though, using it this way is obviously stupid, especially at -the beginning of the configuration.

    -

    Actions (legacy format)

    -

    The action field of a rule describes what to do with the -message. In general, message content is written to a kind of "logfile". -But also other actions might be done, like writing to a database table -or forwarding to another host.
    -
    -Templates can be used with all actions. If used, the specified template + +

    Legacy Format

    +

    Be warned that legacy action format is hard to get right. It is +recommended to use RainerScript-Style action format whenever possible! +A key problem with legacy format is that a single action is defined via +multiple configurations lines, which may be spread all across rsyslog.conf. +Even the definition of multiple actions may be intermixed (often not +intentional!). If legacy actions format needs to be used (e.g. some modules +may not yet implement the RainerScript format), it is strongly recommended +to place all configuration statements pertaining to a single action +closely together. +

    Please also note that legacy action parameters do not affect +RainerScript action objects. So if you define for example: + +

    +$actionResumeRetryCount 10
    +action(type="omfwd" target="server1.example.net")
    +@@server2.example.net
    +
    + +server1's "action.resumeRetryCount" parameter is not set, instead +server2's is! +

    A goal of the new RainerScript action format was to avoid confusion +which parameters are actually used. As such, it would be counter-productive +to honor legacy action parameters inside a RainerScript definition. As +result, both types of action definitions are strictly (and nicely) +separated from each other. The bottom line is that if RainerScript actions +are used, one does not need to care about which legacy action parameters may +(still...) be in effect. +

    +

    Note that not all modules necessarily support legacy action format. +Especially newer modules are recommended to NOT support it. +

    Legacy Description

    +

    Templates can be used with many actions. If used, the specified template is used to generate the message content (instead of the default template). To specify a template, write a semicolon after the action value immediately followed by the template name.
    @@ -453,7 +406,7 @@ what you can do with it, see "TEMPLATES" at the top of this document.

    [rsyslog site]

    This documentation is part of the rsyslog project.
    -Copyright © 2008-2011 by Rainer Gerhards and +Copyright © 2008-2013 by Rainer Gerhards and Adiscon. Released under the GNU GPL version 2 or higher.

    -- cgit v1.2.3 From adc8d626535c7c79d723ea2a84146b6c1856316f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 17 Apr 2013 10:49:09 +0200 Subject: prepare for 7.2.7 release --- ChangeLog | 2 +- configure.ac | 2 +- doc/manual.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0fe084ee..36187cdd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ ---------------------------------------------------------------------------- -Version 7.2.7 [v7-stable] 2013-03-?? +Version 7.2.7 [v7-stable] 2013-04-17 - rsyslogd startup information is now properly conveyed back to init when privileges are beging dropped Actually, we have moved termination of the parent in front of the diff --git a/configure.ac b/configure.ac index 1df9e7d4..3be549e3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[7.2.6],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.2.7],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/doc/manual.html b/doc/manual.html index 8b54bd0b..fc14031f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ rsyslog support available directly from the source!

    Please visit the rsyslog sponsor's page to honor the project sponsors or become one yourself! We are very grateful for any help towards the project goals.

    -

    This documentation is for version 7.2.6 (v7.2-stable branch) of rsyslog. +

    This documentation is for version 7.2.7 (v7.2-stable branch) of rsyslog. Visit the rsyslog status page to obtain current version information and project status.

    If you like rsyslog, you might -- cgit v1.2.3