From 62166824fc4cc3637a47f7c37a186c23acb6e9f2 Mon Sep 17 00:00:00 2001 From: chantra Date: Feb 03 2022 20:59:13 +0000 Subject: support rpmkeys validation --- diff --git a/SOURCES/cow...cow_signvalidation.diff b/SOURCES/cow...cow_signvalidation.diff new file mode 100644 index 0000000..d938e97 --- /dev/null +++ b/SOURCES/cow...cow_signvalidation.diff @@ -0,0 +1,1021 @@ +diff --git a/Makefile.am b/Makefile.am +index 288668819..96542c8c8 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -185,7 +185,7 @@ bin_PROGRAMS += rpmgraph + rpmgraph_SOURCES = tools/rpmgraph.c + rpmgraph_LDADD = lib/librpm.la rpmio/librpmio.la @WITH_POPT_LIB@ + +-dist_bin_SCRIPTS = scripts/gendiff ++dist_bin_SCRIPTS = scripts/gendiff scripts/rpm2extents_dump + + rpmconfig_DATA = rpmrc + rpmrc: $(top_srcdir)/rpmrc.in +diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c +index 40a3ab83f..6164d012c 100644 +--- a/lib/rpmchecksig.c ++++ b/lib/rpmchecksig.c +@@ -24,6 +24,11 @@ + + #include "debug.h" + ++/* magic value at end of file (64 bits) that indicates this is a transcoded ++ * rpm. ++ */ ++#define MAGIC 3472329499408095051 ++ + static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen) + { + char const * const pgpmark = "-----BEGIN PGP "; +@@ -220,6 +225,107 @@ rpmRC rpmpkgRead(struct rpmvs_s *vs, FD_t fd, + return rc; + } + ++static rpmRC isTranscodedRpm(FD_t fd) { ++ rpmRC rc = RPMRC_NOTFOUND; ++ rpm_loff_t current; ++ uint64_t magic; ++ size_t len; ++ ++ // If the file is not seekable, we cannot detect whether or not it is transcoded. ++ if(Fseek(fd, 0, SEEK_CUR) < 0) { ++ return RPMRC_FAIL; ++ } ++ current = Ftell(fd); ++ ++ if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) { ++ rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ len = sizeof(magic); ++ if (Fread(&magic, len, 1, fd) != len) { ++ rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (magic != MAGIC) { ++ rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n")); ++ rc = RPMRC_NOTFOUND; ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ if (Fseek(fd, current, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n")); ++ } ++ return rc; ++} ++ ++static int rpmpkgVerifySigsTranscoded(FD_t fd){ ++ rpm_loff_t current; ++ uint64_t magic; ++ rpm_loff_t offset; ++ int32_t rc; ++ size_t len; ++ uint64_t content_len; ++ char *content = NULL; ++ ++ current = Ftell(fd); ++ ++ if(Fseek(fd, -(sizeof(magic) + 3 * sizeof(offset) ), SEEK_END) < 0) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: failed to seek for offset\n")); ++ rc = -1; ++ goto exit; ++ } ++ ++ len = sizeof(offset); ++ if (Fread(&offset, len, 1, fd) != len) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification offset\n")); ++ rc = -1; ++ goto exit; ++ } ++ ++ if(Fseek(fd, offset, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to seek signature verification offset\n")); ++ rc = -1; ++ goto exit; ++ } ++ len = sizeof(rc); ++ if (Fread(&rc, len, 1, fd) != len) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification RC\n")); ++ rc = -1; ++ goto exit; ++ } ++ ++ len = sizeof(content_len); ++ if (Fread(&content_len, len, 1, fd) != len) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content length\n")); ++ goto exit; ++ } ++ ++ content = malloc(content_len + 1); ++ if(content == NULL) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to allocate memory to read signature content\n")); ++ goto exit; ++ } ++ content[content_len] = 0; ++ if (Fread(content, content_len, 1, fd) != content_len) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content\n")); ++ goto exit; ++ } ++ ++ rpmlog(RPMLOG_NOTICE, "%s", content); ++exit: ++ if(content){ ++ free(content); ++ } ++ if (Fseek(fd, current, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: unable to seek back to original location\n")); ++ } ++ return rc; ++ ++} ++ + static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, + FD_t fd, const char *fn) + { +@@ -229,10 +335,14 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, + .verbose = rpmIsVerbose(), + }; + int rc; +- struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); + + rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : ""); + ++ if(isTranscodedRpm(fd) == RPMRC_OK){ ++ return rpmpkgVerifySigsTranscoded(fd); ++ } ++ struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); ++ + rc = rpmpkgRead(vs, fd, NULL, NULL, &msg); + + if (rc) +@@ -260,6 +370,29 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, + return rc; + } + ++static int rpmpkgVerifySigsFD(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, ++ FD_t fd, rpmsinfoCb cb, void *cbdata) ++{ ++ char *msg = NULL; ++ int rc; ++ struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); ++ ++ rc = rpmpkgRead(vs, fd, NULL, NULL, &msg); ++ ++ if (rc) ++ goto exit; ++ ++ rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata); ++ ++exit: ++ if (rc && msg) ++ rpmlog(RPMLOG_ERR, "%s\n", msg); ++ rpmvsFree(vs); ++ free(msg); ++ return rc; ++} ++ ++ + /* Wrapper around rpmkVerifySigs to preserve API */ + int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn) + { +@@ -304,3 +437,53 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) + rpmKeyringFree(keyring); + return res; + } ++ ++struct vfydatafd_s { ++ size_t len; ++ char msg[BUFSIZ]; ++}; ++ ++ ++static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata) ++{ ++ struct vfydatafd_s *vd = cbdata; ++ char *vmsg, *msg; ++ size_t n; ++ size_t remainder = BUFSIZ - vd->len; ++ ++ vmsg = rpmsinfoMsg(sinfo); ++ rasprintf(&msg, " %s\n", vmsg); ++ n = rstrlcpy(vd->msg + vd->len, msg, remainder); ++ free(vmsg); ++ free(msg); ++ if(n <= remainder){ ++ vd->len += n; ++ } ++ return 1; ++} ++ ++ ++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg) ++{ ++ rpmRC rc = RPMRC_FAIL; ++ rpmKeyring keyring = rpmtsGetKeyring(ts, 1); ++ rpmVSFlags vsflags = rpmtsVfyFlags(ts); ++ int vfylevel = rpmtsVfyLevel(ts); ++ struct vfydatafd_s vd = {.len = 0}; ++ ++ vsflags |= rpmcliVSFlags; ++ if (rpmcliVfyLevelMask) { ++ vfylevel &= ~rpmcliVfyLevelMask; ++ rpmtsSetVfyLevel(ts, vfylevel); ++ } ++ ++ if (!rpmpkgVerifySigsFD(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) { ++ rc = RPMRC_OK; ++ } ++ *msg = strdup(vd.msg); ++ rpmsqPoll(); ++ ++ rpmKeyringFree(keyring); ++ return rc; ++} ++ +diff --git a/lib/rpmcli.h b/lib/rpmcli.h +index 906fe9951..7ff48b37a 100644 +--- a/lib/rpmcli.h ++++ b/lib/rpmcli.h +@@ -411,6 +411,16 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv); + */ + int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv); + ++ ++/** \ingroup rpmcli ++ * Verify package signatures. ++ * @param ts transaction set ++ * @param fd a file descriptor to verify ++ * @param msg a string containing textual information about the verification, similar to rpmcliVerifySignatures output. ++ * @return 0 on success ++ */ ++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd, char **msg); ++ + #ifdef __cplusplus + } + #endif +diff --git a/rpm2extents.c b/rpm2extents.c +index c111be0a2..e316a2834 100644 +--- a/rpm2extents.c ++++ b/rpm2extents.c +@@ -2,7 +2,9 @@ + + #include "system.h" + ++#include + #include /* rpmReadPackageFile .. */ ++#include + #include + #include + #include +@@ -10,6 +12,7 @@ + + #include + #include "lib/rpmlead.h" ++#include "lib/rpmts.h" + #include "lib/signature.h" + #include "lib/header_internal.h" + #include "rpmio/rpmio_internal.h" +@@ -51,38 +54,47 @@ rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit) + return (unit - (pos % unit)) % unit; + } + +-static int digestor( ++static struct poptOption optionsTable[] = { ++ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0, ++ N_("Common options for all rpm modes and executables:"), NULL }, ++ ++ POPT_AUTOALIAS ++ POPT_AUTOHELP ++ POPT_TABLEEND ++}; ++ ++ ++static void FDDigestInit(FD_t fdi, uint8_t algos[], uint32_t algos_len){ ++ int algo; ++ ++ for (algo = 0; algo < algos_len; algo++) { ++ fdInitDigest(fdi, algos[algo], 0); ++ } ++} ++ ++static int FDWriteDigests( + FD_t fdi, + FD_t fdo, +- FD_t validationo, + uint8_t algos[], +- uint32_t algos_len +-) ++ uint32_t algos_len) + { +- ssize_t fdilength; + const char *filedigest, *algo_name; + size_t filedigest_len, len; + uint32_t algo_name_len, algo_digest_len; + int algo; + rpmRC rc = RPMRC_FAIL; + +- for (algo = 0; algo < algos_len; algo++) { +- fdInitDigest(fdi, algos[algo], 0); +- } +- fdilength = ufdCopy(fdi, fdo); +- if (fdilength == -1) { +- fprintf(stderr, _("digest cat failed\n")); +- goto exit; +- } ++ ssize_t fdilength = fdOp(fdi, FDSTAT_READ)->bytes; + + len = sizeof(fdilength); +- if (Fwrite(&fdilength, len, 1, validationo) != len) { ++ if (Fwrite(&fdilength, len, 1, fdo) != len) { + fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); + goto exit; + } + len = sizeof(algos_len); +- if (Fwrite(&algos_len, len, 1, validationo) != len) { +- fprintf(stderr, _("Unable to write number of validation digests\n")); ++ if (Fwrite(&algos_len, len, 1, fdo) != len) { ++ algo_digest_len = (uint32_t)filedigest_len; ++ fprintf(stderr, _("Unable to write number of digests\n")); + goto exit; + } + for (algo = 0; algo < algos_len; algo++) { +@@ -93,24 +105,24 @@ static int digestor( + algo_digest_len = (uint32_t)filedigest_len; + + len = sizeof(algo_name_len); +- if (Fwrite(&algo_name_len, len, 1, validationo) != len) { ++ if (Fwrite(&algo_name_len, len, 1, fdo) != len) { + fprintf(stderr, +- _("Unable to write validation algo name length\n")); ++ _("Unable to write digest algo name length\n")); + goto exit; + } + len = sizeof(algo_digest_len); +- if (Fwrite(&algo_digest_len, len, 1, validationo) != len) { ++ if (Fwrite(&algo_digest_len, len, 1, fdo) != len) { + fprintf(stderr, +- _("Unable to write number of bytes for validation digest\n")); ++ _("Unable to write number of bytes for digest\n")); + goto exit; + } +- if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) { +- fprintf(stderr, _("Unable to write validation algo name\n")); ++ if (Fwrite(algo_name, algo_name_len, 1, fdo) != algo_name_len) { ++ fprintf(stderr, _("Unable to write digest algo name\n")); + goto exit; + } +- if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) { ++ if (Fwrite(filedigest, algo_digest_len, 1, fdo ) != algo_digest_len) { + fprintf(stderr, +- _("Unable to write validation digest value %u, %zu\n"), ++ _("Unable to write digest value %u, %zu\n"), + algo_digest_len, filedigest_len); + goto exit; + } +@@ -120,7 +132,69 @@ static int digestor( + return rc; + } + +-static rpmRC process_package(FD_t fdi, FD_t validationi) ++static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) { ++ size_t len; ++ rpmRC rc = RPMRC_FAIL; ++ ++ if(rpmvsrc){ ++ fprintf(stderr, _("Error verifying package signatures\n")); ++ } ++ ++ len = sizeof(rpmvsrc); ++ if (Fwrite(&rpmvsrc, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write signature verification RC code %d\n"), rpmvsrc); ++ goto exit; ++ } ++ size_t content_len = msg ? strlen(msg) : 0; ++ len = sizeof(content_len); ++ if (Fwrite(&content_len, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write signature verification output length %zd\n"), content_len); ++ goto exit; ++ } ++ if (Fwrite(msg, content_len, 1, fdo) != content_len) { ++ fprintf(stderr, _("Unable to write signature verification output %s\n"), msg); ++ goto exit; ++ } ++ ++ rc = RPMRC_OK; ++exit: ++ ++ return rc; ++} ++ ++static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo, ++ uint8_t algos[], ++ uint32_t algos_len){ ++ int rpmvsrc; ++ rpmRC rc = RPMRC_FAIL; ++ char *msg = NULL; ++ rpmts ts = rpmtsCreate(); ++ ++ rpmtsSetRootDir(ts, rpmcliRootDir); ++ ++ FDDigestInit(fdi, algos, algos_len); ++ ++ rpmvsrc = rpmcliVerifySignaturesFD(ts, fdi, &msg); ++ ++ // Write result of digest computation ++ if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) { ++ fprintf(stderr, _("Failed to write digests")); ++ goto exit; ++ } ++ ++ // Write result of signature validation. ++ if(FDWriteSignaturesValidation(sigo, rpmvsrc, msg)) { ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ if(msg) { ++ free(msg); ++ } ++ return rc; ++} ++ ++static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + { + uint32_t diglen; + /* GNU C extension: can use diglen from outer context */ +@@ -148,7 +222,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + rpm_mode_t mode; + char *rpmio_flags = NULL, *zeros; + const unsigned char *digest; +- rpm_loff_t pos, size, pad, validation_pos; ++ rpm_loff_t pos, size, pad, digest_pos, validation_pos, digest_table_pos; + uint32_t offset_ix = 0; + size_t len; + int next = 0; +@@ -253,6 +327,16 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset), + digestoffsetCmp); + ++ validation_pos = pos; ++ ssize_t validation_len = ufdCopy(validationi, fdo); ++ if (validation_len == -1) { ++ fprintf(stderr, _("validation output ufdCopy failed\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ ++ digest_table_pos = validation_pos + validation_len; ++ + len = sizeof(offset_ix); + if (Fwrite(&offset_ix, len, 1, fdo) != len) { + fprintf(stderr, _("Unable to write length of table\n")); +@@ -278,17 +362,18 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + goto exit; + } + } +- validation_pos = ( +- pos + sizeof(offset_ix) + sizeof(diglen) + ++ digest_pos = ( ++ digest_table_pos + sizeof(offset_ix) + sizeof(diglen) + + offset_ix * (diglen + sizeof(rpm_loff_t)) + ); + +- ssize_t validation_len = ufdCopy(validationi, fdo); +- if (validation_len == -1) { ++ ssize_t digest_len = ufdCopy(digestori, fdo); ++ if (digest_len == -1) { + fprintf(stderr, _("digest table ufdCopy failed\n")); + rc = RPMRC_FAIL; + goto exit; + } ++ + /* add more padding so the last file can be cloned. It doesn't matter that + * the table and validation etc are in this space. In fact, it's pretty + * efficient if it is. +@@ -302,12 +387,17 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + goto exit; + } + zeros = _free(zeros); +- if (Fwrite(&pos, len, 1, fdo) != len) { ++ if (Fwrite(&validation_pos, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write offset of validation output\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (Fwrite(&digest_table_pos, len, 1, fdo) != len) { + fprintf(stderr, _("Unable to write offset of digest table\n")); + rc = RPMRC_FAIL; + goto exit; + } +- if (Fwrite(&validation_pos, len, 1, fdo) != len) { ++ if (Fwrite(&digest_pos, len, 1, fdo) != len) { + fprintf(stderr, _("Unable to write offset of validation table\n")); + rc = RPMRC_FAIL; + goto exit; +@@ -327,104 +417,200 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + return rc; + } + +-int main(int argc, char *argv[]) { +- rpmRC rc; +- int cprc = 0; +- uint8_t algos[argc - 1]; +- int mainpipefd[2]; +- int metapipefd[2]; +- pid_t cpid, w; +- int wstatus; ++static off_t ufdTee(FD_t sfd, FD_t *fds, int len) ++{ ++ char buf[BUFSIZ]; ++ ssize_t rdbytes, wrbytes; ++ off_t total = 0; ++ ++ while (1) { ++ rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd); ++ ++ if (rdbytes > 0) { ++ for(int i=0; i < len; i++) { ++ wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]); ++ if (wrbytes != rdbytes) { ++ fprintf(stderr, "Error wriing to FD %d: %s\n", i, Fstrerror(fds[i])); ++ total = -1; ++ break; ++ } ++ } ++ if(total == -1){ ++ break; ++ } ++ total += wrbytes; ++ } else { ++ if (rdbytes < 0) ++ total = -1; ++ break; ++ } ++ } + +- xsetprogname(argv[0]); /* Portability call -- see system.h */ +- rpmReadConfigFiles(NULL, NULL); ++ return total; ++} + +- if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) { +- fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); +- exit(EXIT_FAILURE); +- } ++static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) { ++ rpmRC rc = RPMRC_FAIL; ++ off_t offt = -1; ++ // tee-ed stdin ++ int processorpipefd[2]; ++ int validatorpipefd[2]; ++ // metadata ++ int meta_digestpipefd[2]; ++ int meta_rpmsignpipefd[2]; ++ ++ pid_t cpids[2], w; ++ int wstatus; ++ FD_t fds[2]; + +- if (argc == 1) { +- fprintf(stderr, +- _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); +- exit(EXIT_FAILURE); ++ if (pipe(processorpipefd) == -1) { ++ fprintf(stderr, _("Processor pipe failure\n")); ++ return RPMRC_FAIL; + } + +- for (int x = 0; x < (argc - 1); x++) { +- if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0) +- { +- fprintf(stderr, +- _("Unable to resolve '%s' as a digest algorithm, exiting\n"), +- argv[x + 1]); +- exit(EXIT_FAILURE); +- } ++ if (pipe(validatorpipefd) == -1) { ++ fprintf(stderr, _("Validator pipe failure\n")); ++ return RPMRC_FAIL; + } + +- +- if (pipe(mainpipefd) == -1) { +- fprintf(stderr, _("Main pipe failure\n")); +- exit(EXIT_FAILURE); ++ if (pipe(meta_digestpipefd) == -1) { ++ fprintf(stderr, _("Meta digest pipe failure\n")); ++ return RPMRC_FAIL; + } +- if (pipe(metapipefd) == -1) { +- fprintf(stderr, _("Meta pipe failure\n")); +- exit(EXIT_FAILURE); ++ ++ if (pipe(meta_rpmsignpipefd) == -1) { ++ fprintf(stderr, _("Meta rpm signature pipe failure\n")); ++ return RPMRC_FAIL; + } +- cpid = fork(); +- if (cpid == 0) { +- /* child: digestor */ +- close(mainpipefd[0]); +- close(metapipefd[0]); +- FD_t fdi = fdDup(STDIN_FILENO); +- FD_t fdo = fdDup(mainpipefd[1]); +- FD_t validationo = fdDup(metapipefd[1]); +- rc = digestor(fdi, fdo, validationo, algos, argc - 1); +- Fclose(validationo); +- Fclose(fdo); ++ ++ cpids[0] = fork(); ++ if (cpids[0] == 0) { ++ /* child: validator */ ++ close(processorpipefd[0]); ++ close(processorpipefd[1]); ++ close(validatorpipefd[1]); ++ close(meta_digestpipefd[0]); ++ close(meta_rpmsignpipefd[0]); ++ FD_t fdi = fdDup(validatorpipefd[0]); ++ FD_t digesto = fdDup(meta_digestpipefd[1]); ++ FD_t sigo = fdDup(meta_rpmsignpipefd[1]); ++ close(meta_digestpipefd[1]); ++ close(meta_rpmsignpipefd[1]); ++ rc = validator(fdi, digesto, sigo, algos, algos_len); ++ if(rc != RPMRC_OK) { ++ fprintf(stderr, _("Validator failed\n")); ++ } + Fclose(fdi); ++ Fclose(digesto); ++ Fclose(sigo); ++ if (rc != RPMRC_OK) { ++ exit(EXIT_FAILURE); ++ } ++ exit(EXIT_SUCCESS); + } else { + /* parent: main program */ +- close(mainpipefd[1]); +- close(metapipefd[1]); +- FD_t fdi = fdDup(mainpipefd[0]); +- FD_t validationi = fdDup(metapipefd[0]); +- rc = process_package(fdi, validationi); +- Fclose(validationi); +- /* fdi is normally closed through the stacked file gzdi in the +- * function. +- * Wait for child process (digestor for stdin) to complete. +- */ +- if (rc != RPMRC_OK) { +- if (kill(cpid, SIGTERM) != 0) { +- fprintf(stderr, +- _("Failed to kill digest process when main process failed: %s\n"), +- strerror(errno)); ++ cpids[1] = fork(); ++ if (cpids[1] == 0) { ++ /* child: process_package */ ++ close(validatorpipefd[0]); ++ close(validatorpipefd[1]); ++ close(processorpipefd[1]); ++ close(meta_digestpipefd[1]); ++ close(meta_rpmsignpipefd[1]); ++ FD_t fdi = fdDup(processorpipefd[0]); ++ close(processorpipefd[0]); ++ FD_t sigi = fdDup(meta_rpmsignpipefd[0]); ++ close(meta_rpmsignpipefd[0]); ++ FD_t digestori = fdDup(meta_digestpipefd[0]); ++ close(meta_digestpipefd[0]); ++ ++ rc = process_package(fdi, digestori, sigi); ++ if(rc != RPMRC_OK) { ++ fprintf(stderr, _("Validator failed\n")); + } +- } +- w = waitpid(cpid, &wstatus, 0); +- if (w == -1) { +- fprintf(stderr, _("waitpid failed\n")); +- cprc = EXIT_FAILURE; +- } else if (WIFEXITED(wstatus)) { +- cprc = WEXITSTATUS(wstatus); +- if (cprc != 0) { +- fprintf(stderr, +- _("Digest process non-zero exit code %d\n"), +- cprc); ++ Fclose(digestori); ++ Fclose(sigi); ++ /* fdi is normally closed through the stacked file gzdi in the ++ * function ++ */ ++ ++ if (rc != RPMRC_OK) { ++ exit(EXIT_FAILURE); + } +- } else if (WIFSIGNALED(wstatus)) { +- fprintf(stderr, +- _("Digest process was terminated with a signal: %d\n"), +- WTERMSIG(wstatus)); +- cprc = EXIT_FAILURE; ++ exit(EXIT_SUCCESS); ++ ++ + } else { +- /* Don't think this can happen, but covering all bases */ +- fprintf(stderr, _("Unhandled circumstance in waitpid\n")); +- cprc = EXIT_FAILURE; ++ /* Actual parent. Read from fdi and write to both processes */ ++ close(processorpipefd[0]); ++ close(validatorpipefd[0]); ++ fds[0] = fdDup(processorpipefd[1]); ++ fds[1] = fdDup(validatorpipefd[1]); ++ close(validatorpipefd[1]); ++ close(processorpipefd[1]); ++ close(meta_digestpipefd[0]); ++ close(meta_digestpipefd[1]); ++ close(meta_rpmsignpipefd[0]); ++ close(meta_rpmsignpipefd[1]); ++ ++ rc = RPMRC_OK; ++ offt = ufdTee(fdi, fds, 2); ++ if(offt == -1){ ++ fprintf(stderr, _("Failed to tee RPM\n")); ++ rc = RPMRC_FAIL; ++ } ++ Fclose(fds[0]); ++ Fclose(fds[1]); ++ w = waitpid(cpids[0], &wstatus, 0); ++ if (w == -1) { ++ fprintf(stderr, _("waitpid cpids[0] failed\n")); ++ rc = RPMRC_FAIL; ++ } ++ w = waitpid(cpids[1], &wstatus, 0); ++ if (w == -1) { ++ fprintf(stderr, _("waitpid cpids[1] failed\n")); ++ rc = RPMRC_FAIL; ++ } + } +- if (cprc != EXIT_SUCCESS) { +- rc = RPMRC_FAIL; ++ } ++ ++ return rc; ++} ++ ++int main(int argc, char *argv[]) { ++ rpmRC rc; ++ poptContext optCon = NULL; ++ const char **args = NULL; ++ int nb_algos = 0; ++ ++ xsetprogname(argv[0]); /* Portability call -- see system.h */ ++ rpmReadConfigFiles(NULL, NULL); ++ optCon = rpmcliInit(argc, argv, optionsTable); ++ poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); ++ ++ if (poptPeekArg(optCon) == NULL) { ++ fprintf(stderr, ++ _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); ++ poptPrintUsage(optCon, stderr, 0); ++ exit(EXIT_FAILURE); ++ } ++ ++ args = poptGetArgs(optCon); ++ ++ for (nb_algos=0; args[nb_algos]; nb_algos++); ++ uint8_t algos[nb_algos]; ++ for (int x = 0; x < nb_algos; x++) { ++ if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0) ++ { ++ fprintf(stderr, ++ _("Unable to resolve '%s' as a digest algorithm, exiting\n"), ++ args[x]); ++ exit(EXIT_FAILURE); + } + } ++ ++ FD_t fdi = fdDup(STDIN_FILENO); ++ rc = teeRpm(fdi, algos, nb_algos); + if (rc != RPMRC_OK) { + /* translate rpmRC into generic failure return code. */ + return EXIT_FAILURE; +diff --git a/scripts/rpm2extents_dump b/scripts/rpm2extents_dump +new file mode 100755 +index 000000000..596a59a49 +--- /dev/null ++++ b/scripts/rpm2extents_dump +@@ -0,0 +1,94 @@ ++#!/usr/bin/env python3 ++ ++import argparse ++import binascii ++import os ++import struct ++import sys ++ ++MAGIC_SIZE = 8 ++MAGIC_STR = b'KWTSH100' ++ ++POS_SIZE = 8 ++ ++def keep_position(func): ++ def wrapper(*args, **kwargs): ++ curr = args[0].tell() ++ res = func(*args, **kwargs) ++ f.seek(curr, os.SEEK_SET) ++ return res ++ return wrapper ++ ++def read_validation_digest(f, validation_offset): ++ digests = [] ++ # validation ++ f.seek(validation_offset, os.SEEK_SET) ++ val_content_len, val_digests_num = struct.unpack('=QI', f.read(8+4)) ++ for i in range(val_digests_num): ++ algo_name_len, digest_len = struct.unpack('=II', f.read(8)) ++ algo_name, digest = struct.unpack(f'{algo_name_len}s{digest_len}s', f.read(algo_name_len+digest_len)) ++ digests.append((algo_name, binascii.hexlify(digest))) ++ return digests ++ ++ ++def read_digests_table(f, digest_offset): ++ digests = [] ++ # validation ++ f.seek(digest_offset, os.SEEK_SET) ++ table_len, digest_len = struct.unpack('=II', f.read(8)) ++ ++ for i in range(table_len): ++ digest, pos = struct.unpack(f'{digest_len}sQ', f.read(digest_len + 8)) ++ digests.append((pos, binascii.hexlify(digest))) ++ return digests ++ ++def read_signature_output(f, signature_offset): ++ f.seek(signature_offset, os.SEEK_SET) ++ signature_rc, signature_output_len = struct.unpack('=IQ', f.read(12)) ++ return signature_rc, f.read(signature_output_len) ++ ++@keep_position ++def parse_file(f): ++ digests = [] ++ pos_table_offset = f.seek(-8 - 3*POS_SIZE, os.SEEK_END) ++ signature_offset, digest_offset, validation_offset = struct.unpack('=QQQ', f.read(3*POS_SIZE)) ++ ++ validation_digests = read_validation_digest(f, validation_offset) ++ digests_table = read_digests_table(f, digest_offset) ++ signature_ouput = read_signature_output(f, signature_offset) ++ ++ return validation_digests, digests_table, signature_ouput ++ ++@keep_position ++def is_transcoded(f): ++ f.seek(-MAGIC_SIZE, os.SEEK_END) ++ magic = f.read(MAGIC_SIZE) ++ return magic == MAGIC_STR ++ ++def arg_parse(): ++ parser = argparse.ArgumentParser() ++ parser.add_argument('--dump-signature', action='store_true') ++ parser.add_argument('--dump-file-digest-table', action='store_true') ++ parser.add_argument('--dump-digests', action='store_true') ++ parser.add_argument('file') ++ ++ return parser.parse_args() ++ ++if __name__ == '__main__': ++ args = arg_parse() ++ f = open(args.file, 'rb') ++ if not is_transcoded(f): ++ sys.exit(1) ++ ++ validation_digests, digests_table, signature_output = parse_file(f) ++ if(args.dump_file_digest_table): ++ for digest in digests_table: ++ print(f"FileDigest {hex(digest[0])}: {digest[1]}") ++ ++ if(args.dump_digests): ++ for validation_digest in validation_digests: ++ print(f"HeaderDigest {validation_digest[0]} {validation_digest[1]}") ++ ++ if(args.dump_signature): ++ print(f"RPMSignOutput RC {signature_output[0]}\nRPMSignOutput Content {signature_output[1].decode()}") ++ +diff --git a/tests/Makefile.am b/tests/Makefile.am +index f78e17c3e..fc8a24a5e 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -36,6 +36,7 @@ TESTSUITE_AT += rpmio.at + TESTSUITE_AT += rpmorder.at + TESTSUITE_AT += rpmvfylevel.at + TESTSUITE_AT += rpmpgp.at ++TESTSUITE_AT += rpm2extents.at + EXTRA_DIST += $(TESTSUITE_AT) + + ## testsuite data +diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at +new file mode 100644 +index 000000000..18accfc75 +--- /dev/null ++++ b/tests/rpm2extents.at +@@ -0,0 +1,96 @@ ++# rpm2extents.at: Some very basic checks ++# ++# Copyright (C) 2022 Manu Bretelle ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ ++AT_BANNER([rpm2extents tests]) ++ ++# ------------------------------ ++ ++# check that transcoder write magic at the end ++AT_SETUP([rpm2extents magic]) ++AT_KEYWORDS([rpm2extents]) ++AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | tail -c8], ++[0], ++[KWTSH100], ++[ignore]) ++AT_CLEANUP ++ ++# Check that transcoder writes checksig return code and content. ++# ++AT_SETUP([rpm2extents signature]) ++AT_KEYWORDS([rpm2extents digest signature]) ++AT_CHECK([ ++RPMDB_INIT ++ ++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null ++rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm ++runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub ++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm ++rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm ++], ++[0], ++[RPMSignOutput RC 2 ++RPMSignOutput Content Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY ++ Header SHA256 digest: OK ++ Header SHA1 digest: OK ++ Payload SHA256 digest: OK ++ V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY ++ MD5 digest: OK ++ ++RPMSignOutput RC 0 ++RPMSignOutput Content Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK ++ Header SHA256 digest: OK ++ Header SHA1 digest: OK ++ Payload SHA256 digest: OK ++ V4 RSA/SHA256 Signature, key ID 1964c5fc: OK ++ MD5 digest: OK ++ ++], ++[]) ++AT_CLEANUP ++ ++AT_SETUP([rpm2extents signature verification]) ++AT_KEYWORDS([rpm2extents digest signature]) ++AT_CHECK([ ++RPMDB_INIT ++ ++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null ++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $? ++runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub ++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm ++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $? ++], ++[0], ++[/tmp/hello-2.0-1.x86_64-signed.rpm: ++ Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY ++ Header SHA256 digest: OK ++ Header SHA1 digest: OK ++ Payload SHA256 digest: OK ++ V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY ++ MD5 digest: OK ++1 ++/tmp/hello-2.0-1.x86_64-signed.rpm: ++ Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK ++ Header SHA256 digest: OK ++ Header SHA1 digest: OK ++ Payload SHA256 digest: OK ++ V4 RSA/SHA256 Signature, key ID 1964c5fc: OK ++ MD5 digest: OK ++0 ++], ++[]) ++AT_CLEANUP +diff --git a/tests/rpmtests.at b/tests/rpmtests.at +index a1adab8e0..205fed6a3 100644 +--- a/tests/rpmtests.at ++++ b/tests/rpmtests.at +@@ -21,3 +21,4 @@ m4_include([rpmreplace.at]) + m4_include([rpmconfig.at]) + m4_include([rpmconfig2.at]) + m4_include([rpmconfig3.at]) ++m4_include([rpm2extents.at]) diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec index 7685441..0c9ae37 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -197,6 +197,8 @@ Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch Provides: rpm(pr1381) Patch9991: https://github.com/chantra/rpm/compare/master...cow.diff Provides: rpm(pr1470) +Patch9992: https://github.com/chantra/rpm/compare/cow...cow_signvalidation.diff +Provides: rpm(pr1470_1) Patch9999: measure.patch # Partially GPL/LGPL dual-licensed and some bits with BSD @@ -724,6 +726,7 @@ make check || cat tests/rpmtests.log %files plugin-reflink %{_bindir}/rpm2extents +%{_bindir}/rpm2extents_dump %{_libdir}/rpm-plugins/reflink.so %files plugin-measure