/* 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 #include "librsgt.h" typedef unsigned char uchar; static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS, MD_VERIFY, MD_EXTEND } mode = MD_DUMP; static int verbose = 0; static void dumpFile(char *name) { FILE *fp; uchar hdr[9]; void *obj; tlvrecord_t rec; 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, &rec, &obj)) != 0) { if(feof(fp)) break; else goto err; } rsgt_tlvprint(stdout, rec.tlvtype, obj, verbose); rsgt_objfree(rec.tlvtype, obj); } if(fp != stdin) fclose(fp); return; err: fprintf(stderr, "error %d (%s) processing file %s\n", r, RSGTE2String(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", (long long unsigned) 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 (%s) processing file %s\n", r, RSGTE2String(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 (%s) processing file %s\n", r, RSGTE2String(r), name); } static inline int doVerifyRec(FILE *logfp, FILE *sigfp, FILE *nsigfp, block_sig_t *bs, gtfile gf, gterrctx_t *ectx, uint8_t bInBlock) { int r; size_t lenRec; char line[128*1024]; if(fgets(line, sizeof(line), logfp) == NULL) { if(feof(logfp)) { r = RSGTE_EOF; } else { perror("log file input"); r = RSGTE_IO; } goto done; } lenRec = strlen(line); if(line[lenRec-1] == '\n') { line[lenRec-1] = '\0'; --lenRec; rsgt_errctxSetErrRec(ectx, line); } /* we need to preserve the first line (record) of each block for * error-reporting purposes (bInBlock==0 meanst start of block) */ if(bInBlock == 0) rsgt_errctxFrstRecInBlk(ectx, line); r = rsgt_vrfy_nextRec(bs, gf, sigfp, nsigfp, (unsigned char*)line, lenRec, ectx); done: return r; } /* We handle both verify and extend with the same function as they * are very similiar. * * note: here we need to have the LOG file name, not signature! */ static void verify(char *name) { FILE *logfp = NULL, *sigfp = NULL, *nsigfp = NULL; block_sig_t *bs = NULL; gtfile gf; uint8_t bHasRecHashes, bHasIntermedHashes; uint8_t bInBlock; int r = 0; char sigfname[4096]; char oldsigfname[4096]; char nsigfname[4096]; gterrctx_t ectx; if(!strcmp(name, "-")) { fprintf(stderr, "%s mode cannot work on stdin\n", mode == MD_VERIFY ? "verify" : "extend"); 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(sigfname); goto err; } if(mode == MD_EXTEND) { snprintf(nsigfname, sizeof(nsigfname), "%s.gtsig.new", name); nsigfname[sizeof(nsigfname)-1] = '\0'; if((nsigfp = fopen(nsigfname, "w")) == NULL) { perror(nsigfname); goto err; } snprintf(oldsigfname, sizeof(oldsigfname), "%s.gtsig.old", name); oldsigfname[sizeof(oldsigfname)-1] = '\0'; } } 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 done; if(mode == MD_EXTEND) { if(fwrite("LOGSIG10", 8, 1, nsigfp) != 1) { perror(nsigfname); r = RSGTE_IO; goto done; } } gf = rsgt_vrfyConstruct_gf(); if(gf == NULL) { fprintf(stderr, "error initializing signature file structure\n"); goto done; } bInBlock = 0; ectx.blkNum = 0; ectx.recNumInFile = 0; while(!feof(logfp)) { if(bInBlock == 0) { if(bs != NULL) rsgt_objfree(0x0902, bs); if((r = rsgt_getBlockParams(sigfp, 1, &bs, &bHasRecHashes, &bHasIntermedHashes)) != 0) { if(ectx.blkNum == 0) { fprintf(stderr, "EOF before finding any signature block - " "is the file still open and being written to?\n"); } else { if(verbose) fprintf(stderr, "EOF after signature block %lld\n", (long long unsigned) ectx.blkNum); } goto done; } rsgt_vrfyBlkInit(gf, bs, bHasRecHashes, bHasIntermedHashes); ectx.recNum = 0; ++ectx.blkNum; } ++ectx.recNum, ++ectx.recNumInFile; if((r = doVerifyRec(logfp, sigfp, nsigfp, bs, gf, &ectx, bInBlock)) != 0) goto done; if(ectx.recNum == bs->recCount) { if((r = verifyBLOCK_SIG(bs, gf, sigfp, nsigfp, (mode == MD_EXTEND) ? 1 : 0, &ectx)) != 0) goto done; bInBlock = 0; } else bInBlock = 1; } done: if(r != RSGTE_EOF) goto err; fclose(logfp); logfp = NULL; fclose(sigfp); sigfp = NULL; if(nsigfp != NULL) { fclose(nsigfp); nsigfp = NULL; } /* everything went fine, so we rename files if we updated them */ if(mode == MD_EXTEND) { if(unlink(oldsigfname) != 0) { if(errno != ENOENT) { perror("unlink oldsig"); r = RSGTE_IO; goto err; } } if(link(sigfname, oldsigfname) != 0) { perror("link oldsig"); r = RSGTE_IO; goto err; } if(unlink(sigfname) != 0) { perror("unlink cursig"); r = RSGTE_IO; goto err; } if(link(nsigfname, sigfname) != 0) { perror("link newsig"); fprintf(stderr, "WARNING: current sig file has been " "renamed to %s - you need to manually recover " "it.\n", oldsigfname); r = RSGTE_IO; goto err; } if(unlink(nsigfname) != 0) { perror("unlink newsig"); fprintf(stderr, "WARNING: current sig file has been " "renamed to %s - you need to manually recover " "it.\n", oldsigfname); r = RSGTE_IO; goto err; } } rsgtExit(); rsgt_errctxExit(&ectx); return; err: fprintf(stderr, "error %d (%s) processing file %s\n", r, RSGTE2String(r), name); if(logfp != NULL) fclose(logfp); if(sigfp != NULL) fclose(sigfp); if(nsigfp != NULL) { fclose(nsigfp); unlink(nsigfname); } 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: case MD_EXTEND: 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" */ {"extend", no_argument, NULL, 'e'}, {"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 'e': mode = MD_EXTEND; 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; }