static inline int doVerifyRecKSI(FILE *logfp, FILE *sigfp, FILE *nsigfp, /*block_sig_t *bs, */ ksifile ksi, ksierrctx_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; rsksi_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) rsksi_errctxFrstRecInBlk(ectx, line); r = rsksi_vrfy_nextRec(ksi, sigfp, nsigfp, (unsigned char*)line, lenRec, ectx); done: return r; }
/* EXTRACT Loglines Function using KSI API * * Input: logfilename and open file handles */ static int extractKSI(char *name, char *errbuf, char *sigfname, FILE *logfp, FILE *sigfp) { char newsigfname[4096]; FILE *newsigfp = NULL; FILE *newlogfp = NULL; char* lineRec = NULL; size_t stlen = 0; ssize_t ssread; char writeMode[2] = "w"; /* Default = Create new file! */ int iLineNumbers = 0; int* paiLineNumbers = NULL; int r = 0; unsigned int j = 0; int iReturn = 1; /* KSI Signature related variables */ block_sig_t *bs = NULL; block_hdr_t *bh = NULL; tlvrecord_t tlvbhrec; ksifile ksi = NULL; uint8_t bHasRecHashes, bHasIntermedHashes; uint8_t bInBlock; int bInitDone = 0; block_hashchain_t *hashchain = NULL; KSI_DataHash *ksiRootHash = NULL; off_t sigfilerewindPos = 0; /* Helper needed to rewind sigfile */ /* Create default outputfilename if needed*/ if (outputfile == NULL) { int iNameLength = strlen(name); outputfile = malloc( iNameLength + 5 ); memcpy(outputfile, name, iNameLength); memcpy(outputfile+iNameLength, ".out", 4); *(outputfile+iNameLength+4) = '\0'; if (debug) printf("debug: extractKSI:\t\t\t default Outputfile: '%s' \n", outputfile); } else { if (debug) printf("debug: extractKSI:\t\t\t Outputfile: '%s' \n", outputfile); } /* Check/set User output writemode */ if (append > 0 ) { writeMode[0] = 'a'; } /* Count number of linenumbers */ if (strlen(linenumbers) > 0 ) { /* Get count of line numbers */ char* pszTmp = linenumbers; if (*(pszTmp) != ',') iLineNumbers++; else pszTmp++; /*Next Char*/ while(pszTmp != NULL) { pszTmp = strchr(pszTmp, ','); if( pszTmp != NULL) { pszTmp++; if ( *(pszTmp) == ',' ) { /* Invalid number, double ,, - skip char */ pszTmp++; } else { iLineNumbers++; } } } if (iLineNumbers == 0) { fprintf(stderr, "extractKSI:\t\t\t error invalid linenumbers\n"); r = RSGTE_IO; goto done; } if (debug) printf("debug: extractKSI:\t\t\t found '%d' linenumbers\n", iLineNumbers); /* Convert line numbers into int Array */ paiLineNumbers = malloc(iLineNumbers*sizeof(int)); int iNumPos = 0; int iNumLength = 0; char szTmpNum[11]; char* pszBegin = linenumbers; char* pszEnd; while(pszBegin != NULL) { /* Cut number from string */ pszEnd = strchr(pszBegin, ','); if (pszEnd != NULL ) iNumLength = (pszEnd-pszBegin); else /* Rest of string is last number */ iNumLength = strlen(pszBegin); /* Check for valid linenumber */ if ( iNumLength <= 0 || iNumLength > 10 ) { fprintf(stderr, "extractKSI:\t\t\t error invalid linenumbers\n"); r = RSGTE_IO; goto done; } /* Copy into tmp string and terminate buffer */ strncpy(szTmpNum, pszBegin, iNumLength); szTmpNum[iNumLength] = '\0'; /* Process next linenumber */ if (pszEnd != NULL ) { pszBegin = pszEnd+1; } else { pszBegin = NULL; } /* if (debug) fprintf(stderr, "Adding linenumber: '%s' \n", szTmpNum); */ /* try to convert into INT now! */ paiLineNumbers[iNumPos] = atoi(szTmpNum); /* invalid numbers will become 0 and ignored */ if (debug) printf("debug: extractKSI:\t\t\t Adding Linenumber: '%d' \n", paiLineNumbers[iNumPos]); iNumPos++; } } else { fprintf(stderr, "extractKSI:\t\t\t error missing linenumbers to extract\n"); r = RSGTE_IO; goto done; } /* Init KSI library */ ksierrctx_t ectx; rsksi_errctxInit(&ectx); rsksiInit("rsyslog rsksiutil " VERSION); bInitDone = 1; /* Set defaults for KSI Signature extraction */ ectx.verbose = verbose; ectx.fp = stderr; ectx.filename = strdup(sigfname); /* Check for valid file header in sigfile */ if((r = rsksi_chkFileHdr(sigfp, "LOGSIG11", verbose)) != 0) { if (debug) printf("debug: extractKSI:\t\t\t error %d in rsksi_chkFileHdr\n", r); goto done; } sigfilerewindPos = ftello(sigfp); /* Store rewind position */ /* Init KSIFiles for IN and OUT */ ksi = rsksi_vrfyConstruct_gf(); if(ksi == NULL) { if (debug) printf("debug: extractKSI:\t\t\t error initializing signature file structures\n"); r = RSGTE_IO; goto done; } /* Start extracting process */ if (debug) printf("debug: extractKSI:\t\t\t extracting lines(%d) %s from %s now ...\n", iLineNumbers, linenumbers, name); /* Open output logfile for extracted loglines */ if((newlogfp = fopen(outputfile, writeMode)) == NULL) { perror(outputfile); r = RSGTE_IO; goto done; } else { if (debug) printf("debug: extractKSI:\t\t\t Output logfile %s opened with mode: '%s'\n", outputfile, writeMode); } /* Open output signaturefile for extracted signatures */ snprintf(newsigfname, sizeof(newsigfname), "%s.ksisig", outputfile); newsigfname[sizeof(newsigfname)-1] = '\0'; if((newsigfp = fopen(newsigfname, writeMode)) == NULL) { perror(newsigfname); r = RSGTE_IO; goto done; } else { if (debug) printf("debug: extractKSI:\t\t\t Output sigfile %s opened with mode: '%s'\n", newsigfname, writeMode); /* write KSI fileheader */ if (writeMode[0] == 'w') { if(fwrite("RECSIG11", 8, 1, newsigfp) != 1) { perror(newsigfname); r = RSGTE_IO; goto done; } } } /* Variables needed for the logfile extraction process */ int iIndex = 0; int iLineSearch = 0; int iLineMax = 0; int iLineCurrent = 0; int bLogLineFound = 0; /* Helper variable to detect when right HASH is found! */ int bBlockSigWritten = 0; /* Helper variable to verify if block signature is written */ /* Find highest possible linenumber */ for(iIndex = 0; iIndex < iLineNumbers; iIndex++) { if (paiLineNumbers[iIndex] > iLineMax) iLineMax = paiLineNumbers[iIndex]; } /* Main Extraction Loop Starts here */ do { /* Free previous hashchain */ if (hashchain != NULL) { rsksi_objfree(0x0907, hashchain); hashchain = NULL; } /* Init new HashChain */ if((hashchain = calloc(1, sizeof(block_hashchain_t))) == NULL) { r = RSGTE_OOM; goto done; } hashchain->rec_hash.data = NULL; hashchain->stepCount = 0; hashchain->level = 0; /* Get Next linenumber for extraction */ for(iIndex = 0; iIndex < iLineNumbers; iIndex++) { if (paiLineNumbers[iIndex] > iLineSearch) { iLineSearch = paiLineNumbers[iIndex]; break; } } if (debug) printf("debug: extractKSI:\t\t\t Extracting line number '%d'\n", iLineSearch); /* Rewind LOG and SIG File for now */ rewind(logfp); iLineCurrent = 0; fseeko(sigfp, sigfilerewindPos, SEEK_SET); /* Reset Helper variables */ ectx.blkNum = 0; ectx.recNumInFile = 0; bInBlock = 0; bLogLineFound = 0; bBlockSigWritten = 0; do { /* Free memory from last lineRec first*/ if (lineRec != NULL) { free(lineRec); lineRec = NULL; } /* Get next line from file! */ ssread = getline(&lineRec, &stlen, logfp); if (ssread != -1) { iLineCurrent++; /* Increment Current Line */ } else { /* END of file reached */ if (debug) printf("debug: extractKSI:\t\t\t End of file reached.\n"); r = RSGTE_EOF; break; } if (debug) printf("debug: extractKSI:\t\t\t line '%d': %.64s...\n", iLineSearch, lineRec); /* Extract line if correct one */ if (iLineCurrent == iLineSearch) { if (debug) printf("debug: extractKSI:\t\t\t Extracted line '%d': %.64s...\n", iLineSearch, lineRec); /* Write logline into output */ if( (fwrite(lineRec, ssread, 1, newlogfp) != 1) /*|| (fwrite("\n", sizeof(char), 1, newlogfp) != 1)*/ ) { free(lineRec); fprintf(stderr, "extractKSI:\t\t\t error '%d' while writing into output logfile %s\n", ferror(newlogfp), outputfile); r = RSGTE_IO; goto done; } /* Found correct record, need to extract hash in next step! */ bLogLineFound = 1; } /* Remove Linefeed for verification */ if( *(lineRec+ssread-1) == '\n') { *(lineRec+ssread-1) = '\0'; --ssread; rsksi_errctxSetErrRec(&ectx, lineRec); } /* Check if this is a new signature block */ if(bInBlock == 0) { /* Free memory */ if(bs != NULL) { rsksi_objfree(0x0904, bs); bs = NULL; } if(bh != NULL) { rsksi_objfree(0x0901, bh); bh = NULL; } /* Get/Verify Block Paramaters */ if((r = rsksi_getBlockParams(ksi, sigfp, 1, &bs, &bh, &bHasRecHashes, &bHasIntermedHashes)) != 0) { if(ectx.blkNum == 0) { fprintf(stderr, "extractKSI:\t\t\t Error %d before finding any signature block - is the file still open and being written to?\n", r); r = RSGTE_IO; } else { if(verbose) fprintf(stderr, "extractKSI:\t\t\t EOF after signature block %lld\n", (long long unsigned) ectx.blkNum); r = RSGTE_EOF; } perror(sigfname); goto done; } else { if (debug) printf("debug: extractKSI:\t\t\t %ju records in Block \n", bs->recCount); } /* Verify block header */ if ((r = verifyBLOCK_HDRKSI(ksi, sigfp, NULL, &tlvbhrec)) != 0) { perror(sigfname); r = RSGTE_IO; goto done; } /* Init Signature Block */ rsksi_vrfyBlkInit(ksi, bh, bHasRecHashes, bHasIntermedHashes); ectx.recNum = 0; ++ectx.blkNum; } ++ectx.recNum, ++ectx.recNumInFile; /* we need to preserve the first line (record) of each block for * error-reporting purposes (bInBlock==0 meanst start of block) */ if(bInBlock == 0) rsksi_errctxFrstRecInBlk(&ectx, lineRec); /* Verify next record in signature file */ if ((r = rsksi_vrfy_nextRecExtract(ksi, sigfp, NULL, (unsigned char*)lineRec, ssread, &ectx, hashchain, bLogLineFound)) != RSGTE_SUCCESS) { fprintf(stderr, "extractKSI:\t\t\t error %d while verifiying next signature record for logline (%d): '%.64s...'\n", r, iLineCurrent, lineRec); goto done; } if (bLogLineFound == 1) { if (bBlockSigWritten == 0) { /* WRITE BLOCK Signature */ if (debug) printf("debug: extractKSI:\t\t\t rsksi_ExtractBlockSignature #1: \n"); if ((r = rsksi_ExtractBlockSignature(newsigfp, ksi, bs, &ectx, verbose)) != RSGTE_SUCCESS) { fprintf(stderr, "extractKSI:\t\t\t error %d while writing block signature for (%d): '%.64s...'\n", r, iLineCurrent, lineRec); goto done; } bBlockSigWritten = 1; } } if(ectx.recNum == bs->recCount) { if (bBlockSigWritten == 1) { /* We need additional Block Finish handling! */ if((r = verifySigblkFinishChain(ksi, hashchain, &ksiRootHash, &ectx)) != 0) { fprintf(stderr, "extractKSI:\t\t\t error %d while finishing BLOCK signature\n", r); goto done; } } else { /* Finish Block ! */ if((r = verifySigblkFinish(ksi, &ksiRootHash)) != 0) { fprintf(stderr, "extractKSI:\t\t\t error %d while finish signature BLOCK\n", r); goto done; } } /* Verify Block signature */ if((r = verifyBLOCK_SIGKSI(bs, ksi, sigfp, NULL, 0, ksiRootHash, &ectx)) != RSGTE_SUCCESS) { fprintf(stderr, "extractKSI:\t\t\t error %d while verifiying BLOCK signature for logline (%d): '%.64s...'\n", r, iLineCurrent, lineRec); goto done; } /* Reset Block state variables */ bInBlock = 0; if (bLogLineFound == 1 ) { /* Write HashChain now */ if ((r = rsksi_WriteHashChain(newsigfp, hashchain, bs, verbose)) != RSGTE_SUCCESS) { fprintf(stderr, "extractKSI:\t\t\t error %d while starting new hash chain for (%d): '%.64s...'\n", r, iLineCurrent, lineRec); goto done; } /* Abort Loop from here and start over */ break; } } else { bInBlock = 1; } // } while (iLineCurrent < iLineSearch && r != RSGTE_EOF); } while (r != RSGTE_EOF); /* Variables will be resetted at Loop begin */ } while ( (iLineMax > iLineSearch || bInBlock == 1) && r != RSGTE_EOF); done: /* Free mem */ if (lineRec != NULL) free(lineRec); if (paiLineNumbers != NULL) free(paiLineNumbers); /* Free hashchain */ if (hashchain != NULL) { rsksi_objfree(0x0907, hashchain); hashchain = NULL; } /* Free Blockheader / Sig */ if(bs != NULL) rsksi_objfree(0x0904, bs); if(bh != NULL) rsksi_objfree(0x0901, bh); /* Free KSI File helper stuff */ if (ksi != NULL) { rsksiCtxDel(ksi->ctx); free(ksi->IV); rsksiimprintDel(ksi->x_prev); free(ksi); } if(r != RSGTE_EOF) { goto done2; } /* 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 done2; } if (fgetc(sigfp) != EOF) { fprintf(stderr, "There are records missing from the end of the log file.\n"); r = RSGTE_END_OF_LOG; goto done2; } done2: if(r != 0 && r != RSGTE_EOF) { sprintf(errbuf, "extractKSI:\t\t\t error %d (%s) processing file %s\n", r, RSKSIE2String(r), name); iReturn = 0; } else errbuf[0] = '\0'; /* Close file handles */ if(logfp != NULL) { fclose(logfp); } if(sigfp != NULL) { fclose(sigfp); } if(newlogfp != NULL) { fclose(newlogfp); } if(newsigfp != NULL) { fclose(newsigfp); } /* Deinit KSI stuff */ if(bInitDone) rsksi_errctxExit(&ectx); return iReturn; }
/* VERIFY Function using KSI 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 verifyKSI(char *name, char *errbuf, char *sigfname, char *oldsigfname, char *nsigfname, FILE *logfp, FILE *sigfp, FILE *nsigfp) { int FILEMODE = FILEMODE_LOGSIG; /* Default FileMode */ block_sig_t *bs = NULL; block_hdr_t *bh = NULL; ksifile ksi = NULL; uint8_t bHasRecHashes, bHasIntermedHashes; uint8_t bInBlock; int r = 0; int bInitDone = 0; tlvrecord_t tlvrec; ksierrctx_t ectx; /* Helpers for extract verify */ int iCurrentLine = 0; int iMaxLine = 0; char* lineRec = NULL; size_t stlen = 0; ssize_t ssread; /* Init KSI related variables */ rsksi_errctxInit(&ectx); rsksiInit("rsyslog rsksiutil " VERSION); bInitDone = 1; ectx.verbose = verbose; ectx.fp = stderr; ectx.filename = strdup(sigfname); ksi = rsksi_vrfyConstruct_gf(); if(ksi == NULL) { fprintf(stderr, "verifyKSI:\t\t\t Error initializing signature file structure\n"); goto done; } /* Check if we have a logsignature file */ if((r = rsksi_chkFileHdr(sigfp, "LOGSIG11", 0)) == 0) { /* Verify Log signature */ if(debug) printf("debug: verifyKSI:\t\t\t Found log signature file ... \n"); if(mode == MD_EXTEND) { if(fwrite("LOGSIG11", 8, 1, nsigfp) != 1) { perror(nsigfname); r = RSGTE_IO; goto done; } } bInBlock = 0; ectx.blkNum = 0; ectx.recNumInFile = 0; while(!feof(logfp)) { if(bInBlock == 0) { if (bs != NULL) rsksi_objfree(0x0904, bs); if (bh != NULL) rsksi_objfree(0x0901, bh); if((r = rsksi_getBlockParams(ksi, sigfp, 1, &bs, &bh, &bHasRecHashes, &bHasIntermedHashes)) != 0) { if(ectx.blkNum == 0) { fprintf(stderr, "verifyKSI:\t\t\t Error %d before finding any signature block - is the file still open and being written to?\n", r); } else { if(verbose) fprintf(stderr, "verifyKSI:\t\t\t EOF after signature block %lld\n", (long long unsigned) ectx.blkNum); } goto done; } /* Copy block header */ if ((r = verifyBLOCK_HDRKSI(ksi, sigfp, nsigfp, &tlvrec)) != 0) goto done; rsksi_vrfyBlkInit(ksi, bh, bHasRecHashes, bHasIntermedHashes); ectx.recNum = 0; ++ectx.blkNum; } ++ectx.recNum, ++ectx.recNumInFile; if((r = doVerifyRecKSI(logfp, sigfp, nsigfp, ksi, &ectx, bInBlock)) != 0) goto done; if(ectx.recNum == bs->recCount) { /* And Verify Block signature */ if((r = verifyBLOCK_SIGKSI(bs, ksi, sigfp, nsigfp, (mode == MD_EXTEND) ? 1 : 0, NULL, &ectx)) != 0) goto done; bInBlock = 0; } else bInBlock = 1; } } else if((r = rsksi_chkFileHdr(sigfp, "RECSIG11", verbose)) == 0) { /* Verify Log Excerpts */ if(debug) printf("verifyKSI:\t\t\t Found record integrity proof file ... \n"); FILEMODE = FILEMODE_RECSIG; ectx.blkNum = 0; ectx.recNumInFile = 0; while( !feof(logfp) && !feof(sigfp)) { /* Free memory */ if (bs != NULL) rsksi_objfree(0x0905, bs); if (bh != NULL) rsksi_objfree(0x0901, bh); /* Get/Verify Block Paramaters */ if((r = rsksi_getExcerptBlockParams(ksi, sigfp, 1, &bs, &bh)) != 0) { if(ectx.blkNum == 0) { fprintf(stderr, "verifyKSI:\t\t\t Error %d before finding any signature block\n", r); } else { if(verbose) fprintf(stderr, "verifyKSI:\t\t\t EOF after signature block %lld\n", (long long unsigned) ectx.blkNum); } goto done; } ectx.recNum = 0; ++ectx.blkNum; // NEEDED ??? ++ectx.recNum, ++ectx.recNumInFile; /* Verify if records were found */ if (bs->recCount <= 0) { r = RSGTE_INVLD_RECCNT; if(verbose) fprintf(stderr, "verifyKSI:\t\t\t Either signature block or hash chains are missing.\n"); goto done; } /* Init Minimal KSI Helper */ rsksi_vrfyBlkInit(ksi, bh, 1, 0); if(debug) printf("debug: verifyKSI:\t\t\t Verifying %lld hashchains ... \n", (long long unsigned) bs->recCount); /* Set new MAXLINE */ iMaxLine = iCurrentLine + bs->recCount; do { if (debug) printf("debug: verifyKSI:\t\t\t NEXT IN LOOP .\n"); /* Free memory from last lineRec first*/ if (lineRec != NULL) { free(lineRec); lineRec = NULL; } /* Get next line from file! */ ssread = getline(&lineRec, &stlen, logfp); if (ssread != -1) { iCurrentLine++; /* Increment Current Line */ /* Remove Linefeed for verification */ if( *(lineRec+ssread-1) == '\n') { *(lineRec+ssread-1) = '\0'; --ssread; rsksi_errctxSetErrRec(&ectx, lineRec); } } else { /* END of file reached */ if (debug) printf("debug: verifyKSI:\t\t\t End of file reached.\n"); r = RSGTE_EOF; break; } /* we need to preserve the first line (record) of each block for error-reporting purposes */ rsksi_errctxFrstRecInBlk(&ectx, lineRec); if (debug) printf("debug: verifyKSI:\t\t\t Processing line '%d': %.64s... \n", iCurrentLine, lineRec); /* Verify logline record against hash chain */ if ((r = rsksi_vrfy_nextHashChain(ksi, bs, sigfp, (unsigned char*)lineRec, ssread, &ectx)) != RSGTE_SUCCESS) { fprintf(stderr, "verifyKSI:\t\t\t error %d while verifiying hash chain record for logline (%d): '%.64s...'\n", r, iCurrentLine, lineRec); goto done; } } while (iCurrentLine > iMaxLine && r != RSGTE_EOF); if (debug) printf("debug: verifyKSI:\t\t\t DONE with LOOP iCurrentLine=%d > iMaxLine=%d r=%d\n", iCurrentLine, iMaxLine, r); } } else { fprintf(stderr, "verifyKSI:\t\t\t Error %d invalid file header found \n", r); } done: /* Free mem first */ if (lineRec != NULL) { free(lineRec); lineRec = NULL; } 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; } /* Close File handles */ fclose(logfp); logfp = NULL; fclose(sigfp); sigfp = NULL; if(nsigfp != NULL) { fclose(nsigfp); nsigfp = NULL; } /* Check for Extend in LogSig Mode: Everything went fine, so we rename files if we updated them */ if(FILEMODE == FILEMODE_LOGSIG && 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; } } rsksi_errctxExit(&ectx); return 1; err: if(r != 0) sprintf(errbuf, "error %d (%s) processing file %s\n", r, RSKSIE2String(r), name); else errbuf[0] = '\0'; /* Close File handles */ if(logfp != NULL) fclose(logfp); if(sigfp != NULL) fclose(sigfp); if(nsigfp != NULL) { fclose(nsigfp); unlink(nsigfname); } if(bInitDone) { rsksi_errctxExit(&ectx); } return 0; }