summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am22
-rw-r--r--tools/logsigner.c159
-rw-r--r--tools/omfile.c144
-rw-r--r--tools/rsgtutil.c342
-rw-r--r--tools/rsgtutil.rst150
5 files changed, 788 insertions, 29 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 9d9bd352..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
@@ -58,7 +62,19 @@ logctl_SOURCES = logctl.c
logctl_CPPFLAGS = $(LIBMONGO_CLIENT_CFLAGS)
logctl_LDADD = $(LIBMONGO_CLIENT_LIBS)
endif
+if ENABLE_GUARDTIME
+bin_PROGRAMS += rsgtutil
+#bin_PROGRAMS += logsigner rsgtutil
+#logsigner = logsigner.c
+#logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS)
+#logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS)
+rsgtutil = rsgtutil.c
+rsgtutil_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS)
+rsgtutil_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS)
+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/logsigner.c b/tools/logsigner.c
new file mode 100644
index 00000000..f6887696
--- /dev/null
+++ b/tools/logsigner.c
@@ -0,0 +1,159 @@
+/* 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <gt_base.h>
+#include <gt_http.h>
+
+#include "librsgt.h"
+
+
+#if 0
+void
+outputhash(GTDataHash *hash)
+{
+ int i;
+ for(i = 0 ; i < hash->digest_length ; ++i)
+ printf("%2.2x", hash->digest[i]);
+ printf("\n");
+}
+
+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", &timestamp);
+
+ 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, const 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);
+}
+#endif
+
+void
+processFile(char *name)
+{
+ FILE *fp;
+ size_t len;
+ char line[64*1024+1];
+ gtctx ctx = NULL;
+
+ ctx = rsgtCtxNew((unsigned char*)"SIGFILE", GT_HASHALG_SHA256);
+ sigblkInit(ctx);
+ if(!strcmp(name, "-"))
+ fp = stdin;
+ else
+ fp = fopen(name, "r");
+
+ while(1) {
+ if(fgets(line, sizeof(line), fp) == NULL) {
+ if(!feof(fp))
+ perror(name);
+ break;
+ }
+ len = strlen(line);
+ if(line[len-1] == '\n') {
+ --len;
+ line[len] = '\0';
+ }
+ //sign(line, len);
+ sigblkAddRecord(ctx, (unsigned char*)line, len);
+ }
+
+ if(fp != stdin)
+ fclose(fp);
+ sigblkFinish(ctx);
+ rsgtCtxDel(ctx);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ rsgtInit("rsyslog logsigner " VERSION);
+ processFile("-");
+ rsgtExit();
+ return 0;
+}
diff --git a/tools/omfile.c b/tools/omfile.c
index 1c65fc59..faf3c24f 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
@@ -118,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;
@@ -143,6 +145,12 @@ 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 */
+ 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) */
int iDynaFileCacheSize; /* size of file handle cache */
@@ -228,7 +236,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,
@@ -416,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) {
@@ -434,8 +444,10 @@ dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
if(pCache[iEntry]->pStrm != NULL) {
strm.Destruct(&pCache[iEntry]->pStrm);
- 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) {
@@ -460,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;
@@ -481,6 +493,29 @@ static void dynaFileFreeCache(instanceData *pData)
}
+/* close current file */
+static rsRetVal
+closeFile(instanceData *pData)
+{
+ DEFiRet;
+ if(pData->useSigprov) {
+ pData->sigprov.OnFileClose(pData->sigprovFileData);
+ pData->sigprovFileData = NULL;
+ }
+ 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, &pData->sigprovFileData);
+ 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
@@ -563,11 +598,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;
@@ -598,9 +636,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 */
@@ -622,9 +658,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;
@@ -651,7 +689,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 */
@@ -664,14 +702,11 @@ 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)));
}
@@ -694,10 +729,12 @@ 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;
+ 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);
@@ -722,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));
- FINALIZE;
+ if(pData->useSigprov) {
+ CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf));
+ }
}
finalize_it:
@@ -730,10 +769,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)
{
@@ -841,7 +877,14 @@ CODESTARTfreeInstance
if(pData->bDynamicName) {
dynaFileFreeCache(pData);
} else if(pData->pStrm != NULL)
- strm.Destruct(&pData->pStrm);
+ closeFile(pData);
+ if(pData->useSigprov) {
+ pData->sigprov.Destruct(&pData->sigprovData);
+ obj.ReleaseObj(__FILE__, pData->sigprovNameFull+2, pData->sigprovNameFull,
+ (void*) &pData->sigprov);
+ free(pData->sigprovName);
+ free(pData->sigprovNameFull);
+ }
ENDfreeInstance
@@ -907,6 +950,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 +991,48 @@ finalize_it:
RETiRet;
}
+static inline void
+initSigprov(instanceData *pData, struct nvlst *lst)
+{
+ 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;
+ }
+ pData->sigprov.SetCnfParam(pData->sigprovData, lst);
+
+ 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 +1100,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 +1114,10 @@ CODESTARTnewActInst
ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
}
+ if(pData->sigprovName != NULL) {
+ initSigprov(pData, lst);
+ }
+
tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName);
CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS));
@@ -1167,8 +1260,7 @@ CODESTARTdoHUP
dynaFileFreeCacheEntries(pData);
} else {
if(pData->pStrm != NULL) {
- strm.Destruct(&pData->pStrm);
- pData->pStrm = NULL;
+ closeFile(pData);
}
}
ENDdoHUP
diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c
new file mode 100644
index 00000000..9d9f3568
--- /dev/null
+++ b/tools/rsgtutil.c
@@ -0,0 +1,342 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <gt_base.h>
+#include <gt_http.h>
+#include <getopt.h>
+
+#include "librsgt.h"
+
+typedef unsigned char uchar;
+
+static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS,
+ MD_VERIFY
+} 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
+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 Tree 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)
+{
+ 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 if(!strcmp(hdr, "GTSTAT10"))
+ typeName = "rsyslog GuardTime Signature State 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 inline int
+doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf, gterrctx_t *ectx, uint8_t bInBlock)
+{
+ int r;
+ size_t lenRec;
+ char rec[128*1024];
+
+ if(fgets(rec, sizeof(rec), logfp) == NULL) {
+ r = feof(logfp) ? RSGTE_EOF : RSGTE_IO;
+ goto done;
+ }
+ lenRec = strlen(rec);
+ 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, ectx);
+done:
+ 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;
+ char sigfname[4096];
+ gterrctx_t ectx;
+
+ 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;
+ }
+ }
+
+ 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;
+
+ gf = rsgt_vrfyConstruct_gf();
+ if(gf == NULL) {
+ fprintf(stderr, "error initializing signature file structure\n");
+ goto err;
+ }
+
+ 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);
+ ectx.recNum = 0;
+ ++ectx.blkNum;
+ }
+ ++ectx.recNum, ++ectx.recNumInFile;
+ if((r = doVerifyRec(logfp, sigfp, bs, gf, &ectx, bInBlock)) != 0)
+ goto err;
+ 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)
+ fclose(logfp);
+ if(sigfp != NULL)
+ fclose(sigfp);
+ if(r != RSGTE_EOF)
+ fprintf(stderr, "error %d processing file %s\n", r, name);
+ rsgtExit();
+ rsgt_errctxExit(&ectx);
+}
+
+static void
+processFile(char *name)
+{
+ switch(mode) {
+ case MD_DETECT_FILE_TYPE:
+ detectFileType(name);
+ break;
+ case MD_DUMP:
+ dumpFile(name);
+ break;
+ case MD_SHOW_SIGBLK_PARAMS:
+ showSigblkParams(name);
+ break;
+ case MD_VERIFY:
+ verify(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'},
+ {"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}
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int opt;
+
+ while(1) {
+ 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);
+ case 'D':
+ mode = MD_DUMP;
+ break;
+ case 'B':
+ mode = MD_SHOW_SIGBLK_PARAMS;
+ break;
+ case 'P':
+ rsgt_read_puburl = optarg;
+ break;
+ 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);
+ return 1;
+ }
+ }
+
+ if(optind == argc)
+ processFile("-");
+ else {
+ for(i = optind ; i < argc ; ++i)
+ processFile(argv[i]);
+ }
+
+ return 0;
+}
diff --git a/tools/rsgtutil.rst b/tools/rsgtutil.rst
new file mode 100644
index 00000000..c5782c5a
--- /dev/null
+++ b/tools/rsgtutil.rst
@@ -0,0 +1,150 @@
+========
+rsgtutil
+========
+
+-----------------------------------
+Manage (GuardTime) Signed Log Files
+-----------------------------------
+
+:Author: Rainer Gerhards <rgerhards@adiscon.com>
+: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 <URL>, --publications-server <URL>
+ 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
+----
+
+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
+==========
+
+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.