static int rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, FILE *nsigfp, GTDataHash *hash, gterrctx_t *ectx) { int r = 0; imprint_t *imp = NULL; if((r = rsgt_tlvrdTreeHash(sigfp, nsigfp, &imp)) != 0) { reportError(r, ectx); goto done; } if(imp->hashID != hashIdentifier(gf->hashAlg)) { reportError(r, ectx); r = RSGTE_INVLD_TREE_HASHID; goto done; } 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; done: if(imp != NULL) rsgt_objfree(0x0901, imp); 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, FILE *nsigfp, uint8_t bExtend, gterrctx_t *ectx) { int r; int gtstate; block_sig_t *file_bs = NULL; GTTimestamp *timestamp = NULL; GTVerificationInfo *vrfyInf; GTDataHash *root = NULL; tlvrecord_t rec; if((r = verifySigblkFinish(gf, &root)) != 0) goto done; if((r = rsgt_tlvrdVrfyBlockSig(sigfp, &file_bs, &rec)) != 0) goto done; if(ectx->recNum != bs->recCount) { r = RSGTE_INVLD_RECCNT; goto done; } gtstate = GTTimestamp_DERDecode(file_bs->sig.der.data, file_bs->sig.der.len, ×tamp); if(gtstate != GT_OK) { r = RSGTE_TS_DERDECODE; ectx->gtstate = gtstate; goto done; } gtstate = GTHTTP_verifyTimestampHash(timestamp, root, NULL, NULL, NULL, rsgt_read_puburl, 0, &vrfyInf); if(! (gtstate == GT_OK && vrfyInf->verification_errors == GT_NO_FAILURES) ) { r = RSGTE_INVLD_TIMESTAMP; ectx->gtstate = gtstate; goto done; } if(rsgt_read_showVerified) reportVerifySuccess(ectx, vrfyInf); if(bExtend) if((r = rsgt_extendSig(timestamp, &rec, ectx)) != 0) goto done; if(nsigfp != NULL) if((r = rsgt_tlvwrite(nsigfp, &rec)) != 0) goto done; r = 0; done: if(file_bs != NULL) rsgt_objfree(0x0902, file_bs); if(r != 0) reportError(r, ectx); if(timestamp != NULL) GTTimestamp_free(timestamp); return r; }
/** * 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; uint8_t bDone = 0; off_t rewindPos = 0; void *obj; tlvrecord_t rec; if(bRewind) rewindPos = ftello(fp); *bHasRecHashes = 0; *bHasIntermedHashes = 0; *bs = NULL; while(!bDone) { /* we will err out on EOF */ if((r = rsgt_tlvrd(fp, &rec, &obj)) != 0) goto done; switch(rec.tlvtype) { case 0x0900: ++nRecs; *bHasRecHashes = 1; break; case 0x0901: *bHasIntermedHashes = 1; break; case 0x0902: *bs = (block_sig_t*) obj; bDone = 1; break; default:fprintf(fp, "unknown tlv record %4.4x\n", rec.tlvtype); break; } if(!bDone) rsgt_objfree(rec.tlvtype, obj); } 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 BLOCK_SIG during verification phase */ static int rsgt_tlvrdVrfyBlockSig(FILE *fp, block_sig_t **bs, tlvrecord_t *rec) { int r; if((r = rsgt_tlvrd(fp, rec, bs)) != 0) goto done; if(rec->tlvtype != 0x0902) { r = RSGTE_MISS_BLOCKSIG; rsgt_objfree(rec->tlvtype, *bs); goto done; } r = 0; done: return r; }
static int rsgt_tlvrdTreeHash(FILE *fp, FILE *outfp, imprint_t **imp) { int r; tlvrecord_t rec; if((r = rsgt_tlvrd(fp, &rec, imp)) != 0) goto done; if(rec.tlvtype != 0x0901) { r = RSGTE_MISS_TREE_HASH; rsgt_objfree(rec.tlvtype, *imp); goto done; } if(outfp != NULL) if((r = rsgt_tlvwrite(outfp, &rec)) != 0) goto done; r = 0; done: return r; }
static void dumpFile(char *name) { FILE *fp; char 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, (uchar*)hdr)) != 0) goto err; if(!strcmp(hdr, "LOGSIG10")) printf("File Header: Version 10 (deprecated) - conversion needed.\n"); else if(!strcmp(hdr, "LOGSIG11")) printf("File Header: Version 11\n"); else 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); }
/* 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) 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); }
/* VERIFY Function using GT API * 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 int verifyGT(char *name, char *errbuf, char *sigfname, char *oldsigfname, char *nsigfname, FILE *logfp, FILE *sigfp, FILE *nsigfp) { block_sig_t *bs = NULL; block_hdr_t *bh = NULL; gtfile gf; uint8_t bHasRecHashes, bHasIntermedHashes; uint8_t bInBlock; int r = 0; int bInitDone = 0; gterrctx_t ectx; rsgt_errctxInit(&ectx); rsgtInit("rsyslog rsgtutil " VERSION); bInitDone = 1; ectx.verbose = verbose; ectx.fp = stderr; ectx.filename = strdup(sigfname); if((r = rsgt_chkFileHdr(sigfp, "LOGSIG11")) != 0) { if (debug) fprintf(stderr, "error %d in rsgt_chkFileHdr\n", r); goto done; } if(mode == MD_EXTEND) { if(fwrite("LOGSIG11", 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(0x0904, bs); if (bh != NULL) rsgt_objfree(0x0901, bh); if((r = rsgt_getBlockParams(sigfp, 1, &bs, &bh, &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; } /* Copy block header */ if ((r = verifyBLOCK_HDR(sigfp, nsigfp)) != 0) goto done; rsgt_vrfyBlkInit(gf, bh, bHasRecHashes, bHasIntermedHashes); ectx.recNum = 0; ++ectx.blkNum; } ++ectx.recNum, ++ectx.recNumInFile; if((r = doVerifyRec(logfp, sigfp, nsigfp, 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; /* Make sure we've reached the end of file in both log and signature file */ if (fgetc(logfp) != EOF) { fprintf(stderr, "There are unsigned records in the end of log.\n"); fprintf(stderr, "Last signed record: %s\n", ectx.errRec); r = RSGTE_END_OF_SIG; goto err; } if (fgetc(sigfp) != EOF) { fprintf(stderr, "There are records missing from the end of the log file.\n"); r = RSGTE_END_OF_LOG; 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 1; err: if(r != 0) sprintf(errbuf, "error %d (%s) processing file %s\n", r, RSGTE2String(r), name); else errbuf[0] = '\0'; if(logfp != NULL) fclose(logfp); if(sigfp != NULL) fclose(sigfp); if(nsigfp != NULL) { fclose(nsigfp); unlink(nsigfname); } if(bInitDone) { rsgtExit(); rsgt_errctxExit(&ectx); } return 0; }