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 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 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 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 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 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 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 88c62b390eb5504772dd4fbb266054828dc4dc5d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 22 Mar 2013 13:33:18 +0100 Subject: rsgtutil: add new file type to file type detection --- tools/rsgtutil.c | 2 + tools/rsgtutil.rst | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 tools/rsgtutil.rst diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c index d9cce2f7..9d9f3568 100644 --- a/tools/rsgtutil.c +++ b/tools/rsgtutil.c @@ -133,6 +133,8 @@ detectFileType(char *name) if((r = rsgt_tlvrdHeader(fp, (uchar*)hdr)) != 0) goto err; if(!strcmp(hdr, "LOGSIG10")) typeName = "Log Signature File, Version 10"; + else if(!strcmp(hdr, "GTSTAT10")) + typeName = "rsyslog GuardTime Signature State File, Version 10"; else typeName = "unknown"; diff --git a/tools/rsgtutil.rst b/tools/rsgtutil.rst new file mode 100644 index 00000000..f2b097dc --- /dev/null +++ b/tools/rsgtutil.rst @@ -0,0 +1,119 @@ +======== +rsgtutil +======== + +----------------------------------- +Manage (GuardTime) Signed Log Files +----------------------------------- + +:Author: Rainer Gerhards +:Date: 2013-03-22 +:Manual section: 1 + +SYNOPSIS +======== + +:: + + rsgtutil [OPTIONS] [FILE] ... + + +DESCRIPTION +=========== + +This tool performs various maintenance operations on signed log files. +It specifically supports the GuardTime signature provider. + +The *rsgtutil* tool is the primary tool to verify log file signatures, +dump signature file contents and carry out other maintenance operations. +The tool offers different operation modes, which are selected via +command line options. + +The processing of multiple files is permitted. Depending on operation +mode, either the signature file or the base log file must be specified. +Within a single call, only a single operations mode is permitted. To +use different modes on different files, multiple calles, one for each +mode, must be made. + +If no file is specified on the command line, stdin is used instead. Note +that not all operation modes support stdin. + +OPTIONS +======= + +-D, --dump + Select "dump" operations mode. + +-t, --verify + Select "verify" operations mode. + +-T, --detect-file-type + Select "detect-file-type" operations mode. + +-B, --show-sigblock-params + Select "show-sigblock-params" operations mode. + +-s, --show-verified + Prints out information about correctly verified blocks (by default, only + errors are printed). + +-v, --verbose + Select verbose mode. Most importantly, hashes and signatures are printed + in full length (can be **very** lengthy) rather than the usual abbreviation. + +-P , --publications-server + Sets the publications server. If not set but required by the operation a + default server is used. The default server is not necessarily optimal + in regard to performance and reliability. + + +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. + +dump +---- + +This dump a the TLV header. + +EXIT CODES +========== + +The command returns an exit code of 0 if everything went fine, and some +other code in case of failures. + + +EXAMPLES +======== + +:: + + rsgtutil --verify logfile + + This verifies the file "logfile" via its associated signature file + "logfile.gtsig". If errors are detected, these are reported to stderr. + Otherwise, rsgtutil terminates without messages. + + +:: + + rsgtutil --dump logfile.gtsig + + This dumps the content of the signature file "logfile.gtsig". The + actual log file is not being processed and does not even need to be + present. + +SEE ALSO +======== +**rsyslogd(8)** + +COPYRIGHT +========= + +This page is part of the *rsyslog* project, and is available under +LGPLv2. -- cgit v1.2.3 From e25ffb6d1839d35fa890099aef8a78930d90438d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 22 Mar 2013 14:05:25 +0100 Subject: doc: add doc for rsgtutil --- configure.ac | 1 + tools/Makefile.am | 12 ++++++++--- tools/rsgtutil.rst | 59 +++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 650def0f..f444e5eb 100644 --- a/configure.ac +++ b/configure.ac @@ -28,6 +28,7 @@ fi AC_DISABLE_STATIC AC_PROG_LIBTOOL AC_CANONICAL_HOST +AC_PATH_PROG([RST2MAN], [rst2man]) PKG_PROG_PKG_CONFIG diff --git a/tools/Makefile.am b/tools/Makefile.am index 2501331e..21a32868 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -43,6 +43,10 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) 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 +EXTRA_DIST = $(man_MANS) \ + rsgtutil.rst \ + recover_qi.pl + if ENABLE_DIAGTOOLS sbin_PROGRAMS += rsyslog_diag_hostname msggen zpipe rsyslog_diag_hostname_SOURCES = gethostn.c @@ -67,8 +71,10 @@ bin_PROGRAMS += rsgtutil rsgtutil = rsgtutil.c rsgtutil_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) rsgtutil_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) +rsgtutil.1: rsgtutil.rst + $(AM_V_GEN) $(RST2MAN) $< $@ +man1_MANS = rsgtutil.1 +CLEANFILES = rsgtutil.1 +EXTRA_DIST+= rsgtutil.1 endif endif - -EXTRA_DIST = $(man_MANS) \ - recover_qi.pl diff --git a/tools/rsgtutil.rst b/tools/rsgtutil.rst index f2b097dc..c5782c5a 100644 --- a/tools/rsgtutil.rst +++ b/tools/rsgtutil.rst @@ -79,7 +79,43 @@ unpredictable. dump ---- -This dump a the TLV header. +The provided *signature* files are dumped. For each top-level record, the*u +type code is printed as well as q short description. If there is additional +information available, it will be printed in tab-indented lines below the +main record dump. The actual *log* files need not to be present. + +verify +------ + +This mode does not work with stdin. On the command line, the *log* file names +are specified. The corresponding *signature* files (ending on ".gtsig") must also +be preset at the same location as the log file. In verify mode, both the log +and signature file is read and the validity of the log file checked. If verification +errors are detected these are printed and processing of the file aborted. By default, +each file is verified individually, without taking cross-file hash chains into +account (so the order of files on the command line does not matter). + +Note that the actual amount of what can be verified depends on the parameters with +which the signature file was written. If record and tree hashes are present, they +will be verified and thus fine-granular error reporting is possible. If they are +not present, only the block signature itself is verified. + +By default, only errors are printed. To also print successful verifications, use the +**--show-verified** option. + + +detect-file-type +---------------- +This mode is used to detect the type of some well-know files used inside the +signature system. The detection is based on the file header. This mode is +primarily a debug aid. + + +show-sigblock-params +-------------------- +This mode is used to print signature block parameters. It is similar to *dump* +mode, but will ignore everything except signature blocks. Also, some additional +meta information is printed. This mode is primarily a debug aid. EXIT CODES ========== @@ -91,22 +127,17 @@ other code in case of failures. EXAMPLES ======== -:: - - rsgtutil --verify logfile +**rsgtutil --verify logfile** - This verifies the file "logfile" via its associated signature file - "logfile.gtsig". If errors are detected, these are reported to stderr. - Otherwise, rsgtutil terminates without messages. - - -:: +This verifies the file "logfile" via its associated signature file +"logfile.gtsig". If errors are detected, these are reported to stderr. +Otherwise, rsgtutil terminates without messages. - rsgtutil --dump logfile.gtsig +**rsgtutil --dump logfile.gtsig** - This dumps the content of the signature file "logfile.gtsig". The - actual log file is not being processed and does not even need to be - present. +This dumps the content of the signature file "logfile.gtsig". The +actual log file is not being processed and does not even need to be +present. SEE ALSO ======== -- cgit v1.2.3