diff --git a/0001-Store-configurable-digest-s-on-packages-from-verific.patch b/0001-Store-configurable-digest-s-on-packages-from-verific.patch new file mode 100644 index 0000000..8fe4515 --- /dev/null +++ b/0001-Store-configurable-digest-s-on-packages-from-verific.patch @@ -0,0 +1,672 @@ +From fdf88aa9cae15bb21d533d103cb8201caafaefbc Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 9 Apr 2025 09:58:40 +0300 +Subject: [PATCH] Store configurable digest(s) on packages from verification + into the rpmdb + +It can be hard to reliably map packages in repositories to installed +packages because the common repodata format only stores package-level +checksums, whereas packages themselves cannot contain such a checksum +for obvious reasons. The NEVRA information is nowhere near enough to +uniquely identify a package. Technically of course, the repodata could +be extended to carry header checksums but it seems that format is next +to impossible to change, so... + +Having rpm calculate and store a configurable set of hashes has the +benefit of serving as a cross-check that the package we installed was +bit-per-bit identical to what was in the repository, even after the fact. + +Backported from commits: +c0d84d40a94478e94943271dcc6c1213275dcf10 +cdf8ac7f07d75b941df90484684e87366e50cfe5 +49844e90bc352372b31035934fa51ff438c89a00 +a41a2c30cc9b0cc67d4d87c600cfdcec68751784 +f08348247203105cc40679ed7aef1f0c4d1d1770 +55d22df4c8d7cf703d431ff6ebaa442275129e06 + +Fixes: RHEL-84062 +--- + docs/man/rpm.8.md | 4 ++ + docs/manual/tags.md | 2 + + include/rpm/rpmtag.h | 2 + + include/rpm/rpmtd.h | 1 + + lib/formats.c | 10 +++++ + lib/psm.c | 21 +++++++++ + lib/rpmte.c | 13 ++++++ + lib/rpmte_internal.h | 3 ++ + lib/transaction.c | 105 +++++++++++++++++++++++++++++++------------ + macros.in | 17 +++++++ + tests/CMakeLists.txt | 1 + + tests/mktree.common | 1 + + tests/prpm.py | 80 +++++++++++++++++++++++++++++++++ + tests/rpmgeneral.at | 2 + + tests/rpmi.at | 1 + + tests/rpmpython2.at | 41 +++++++++++++++++ + tests/rpmquery.at | 15 +++++++ + tests/rpmsigdig.at | 36 +++++++++++++++ + tests/rpmtests.at | 1 + + 19 files changed, 328 insertions(+), 28 deletions(-) + create mode 100755 tests/prpm.py + create mode 100644 tests/rpmpython2.at + +diff --git a/docs/man/rpm.8.md b/docs/man/rpm.8.md +index 8ce06cf3e..c9c4473fd 100644 +--- a/docs/man/rpm.8.md ++++ b/docs/man/rpm.8.md +@@ -534,6 +534,10 @@ Alternate output formats may be requested by following the tag with + + : Format file verify status. + ++**:hashalgo** ++ ++: Display hash algorithm name. ++ + **:hex** + + : Format in hexadecimal. +diff --git a/docs/manual/tags.md b/docs/manual/tags.md +index 86ddbf876..fef69ab64 100644 +--- a/docs/manual/tags.md ++++ b/docs/manual/tags.md +@@ -331,6 +331,8 @@ Instprefixes | 1099 | string array + Origbasenames | 1120 | string array | Original Basenames (relocated packages only) + Origdirindexes | 1119 | int32 array | Original Dirindexes (relocated packages only) + Origdirnames | 1121 | string array | Original Dirnames (relocated packages only) ++Packagedigests | 5118 | string array | Package digests calculated during verification ++Packagedigestalgos | 5119 | int32 | Algorithms used for Packagedigests + + + ## Source packages +diff --git a/include/rpm/rpmtag.h b/include/rpm/rpmtag.h +index dec9c9244..8da7cf518 100644 +--- a/include/rpm/rpmtag.h ++++ b/include/rpm/rpmtag.h +@@ -386,6 +386,8 @@ typedef enum rpmTag_e { + RPMTAG_PREUNTRANSFLAGS = 5107, /* i */ + RPMTAG_POSTUNTRANSFLAGS = 5108, /* i */ + RPMTAG_SYSUSERS = 5109, /* s[] extension */ ++ RPMTAG_PACKAGEDIGESTS = 5118, /* s[] */ ++ RPMTAG_PACKAGEDIGESTALGOS = 5119, /* i[] */ + + RPMTAG_FIRSTFREE_TAG /*!< internal */ + } rpmTag; +diff --git a/include/rpm/rpmtd.h b/include/rpm/rpmtd.h +index c592905fa..99dce6a83 100644 +--- a/include/rpm/rpmtd.h ++++ b/include/rpm/rpmtd.h +@@ -253,6 +253,7 @@ typedef enum rpmtdFormats_e { + RPMTD_FORMAT_HUMANIEC = 21, /* human readable value, K = 1024 (int types) */ + RPMTD_FORMAT_TAGNAME = 22, /* tag name (any type) */ + RPMTD_FORMAT_TAGNUM = 23, /* tag number (any type) */ ++ RPMTD_FORMAT_HASHALGO = 25, /* digest algorithm name (int types) */ + } rpmtdFormats; + + /** \ingroup rpmtd +diff --git a/lib/formats.c b/lib/formats.c +index 156812165..303c12cbb 100644 +--- a/lib/formats.c ++++ b/lib/formats.c +@@ -494,6 +494,14 @@ static char *tagnumFormat(rpmtd td, char **emsg) + return num; + } + ++static char *hashalgoFormat(rpmtd td, char **emsg) ++{ ++ const char *alg = pgpValString(PGPVAL_HASHALGO, rpmtdGetNumber(td)); ++ if (rstreqn(alg, "Unknown", 7)) ++ alg = "Unknown"; ++ return rstrdup(alg); ++} ++ + static const struct headerFmt_s rpmHeaderFormats[] = { + { RPMTD_FORMAT_STRING, "string", + RPM_ANY_CLASS, stringFormat }, +@@ -545,6 +553,8 @@ static const struct headerFmt_s rpmHeaderFormats[] = { + RPM_ANY_CLASS, tagnameFormat }, + { RPMTD_FORMAT_TAGNUM, "tagnum", + RPM_ANY_CLASS, tagnumFormat }, ++ { RPMTD_FORMAT_HASHALGO, "hashalgo", ++ RPM_NUMERIC_CLASS, hashalgoFormat }, + { -1, NULL, 0, NULL } + }; + +diff --git a/lib/psm.c b/lib/psm.c +index ddc339357..39261b9f4 100644 +--- a/lib/psm.c ++++ b/lib/psm.c +@@ -716,9 +716,26 @@ static void markReplacedInstance(rpmts ts, rpmte te) + rpmdbFreeIterator(mi); + } + ++static void mergeAux(Header auxh, Header h) ++{ ++ struct rpmtd_s td; ++ HeaderIterator hi = headerInitIterator(auxh); ++ while (headerNext(hi, &td)) { ++ /* Don't allow overwriting package data from aux */ ++ if (headerIsEntry(h, td.tag)) ++ continue; ++ if (rpmtdCount(&td) > 0) { ++ (void) headerPut(h, &td, HEADERPUT_DEFAULT); ++ } ++ rpmtdFreeData(&td); ++ } ++ headerFreeIterator(hi); ++} ++ + static rpmRC dbAdd(rpmts ts, rpmte te) + { + Header h = rpmteHeader(te); ++ Header auxh = rpmteHeaderAux(te, 0); + rpm_time_t installTime = rpmtsGetTime(ts, 1); + rpmfs fs = rpmteGetFileStates(te); + rpm_count_t fc = rpmfsFC(fs); +@@ -735,6 +752,9 @@ static rpmRC dbAdd(rpmts ts, rpmte te) + headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1); + headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1); + ++ if (auxh) ++ mergeAux(auxh, h); ++ + (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0); + rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL; + (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0); +@@ -744,6 +764,7 @@ static rpmRC dbAdd(rpmts ts, rpmte te) + packageHashAddEntry(ts->members->installedPackages, + headerGetInstance(h), te); + } ++ headerFree(auxh); + headerFree(h); + return rc; + } +diff --git a/lib/rpmte.c b/lib/rpmte.c +index d31152a43..657b4e11d 100644 +--- a/lib/rpmte.c ++++ b/lib/rpmte.c +@@ -32,6 +32,7 @@ struct rpmte_s { + void *userdata; /*!< Application private user data. */ + + Header h; /*!< Package header. */ ++ Header auxh; /*!< Auxiliary data (from install) */ + char * NEVR; /*!< Package name-version-release. */ + char * NEVRA; /*!< Package name-version-release.arch. */ + char * name; /*!< Name: */ +@@ -252,6 +253,7 @@ rpmte rpmteFree(rpmte te) + fdFree(te->fd); + rpmfilesFree(te->files); + headerFree(te->h); ++ headerFree(te->auxh); + rpmfsFree(te->fs); + rpmpsFree(te->probs); + rpmteCleanDS(te); +@@ -279,6 +281,17 @@ rpmte rpmteNew(rpmts ts, Header h, rpmElementType type, fnpyKey key, + return p; + } + ++Header rpmteHeaderAux(rpmte te, int init) ++{ ++ Header auxh = NULL; ++ if (te != NULL) { ++ if (te->auxh == NULL && init == 1) ++ te->auxh = headerNew(); ++ auxh = headerLink(te->auxh); ++ } ++ return auxh; ++} ++ + unsigned int rpmteDBInstance(rpmte te) + { + return (te != NULL ? te->db_instance : 0); +diff --git a/lib/rpmte_internal.h b/lib/rpmte_internal.h +index 99b548fcc..e616c0561 100644 +--- a/lib/rpmte_internal.h ++++ b/lib/rpmte_internal.h +@@ -101,6 +101,9 @@ rpmfs rpmteGetFileStates(rpmte te); + RPM_GNUC_INTERNAL + void rpmteSetVerified(rpmte te, int verified); + ++RPM_GNUC_INTERNAL ++Header rpmteHeaderAux(rpmte te, int init); ++ + /** \ingroup rpmte + * Retrieve size in bytes of package header. + * @param te transaction element +diff --git a/lib/transaction.c b/lib/transaction.c +index 70d2587ac..b828fe395 100644 +--- a/lib/transaction.c ++++ b/lib/transaction.c +@@ -35,6 +35,7 @@ + #include "rpmlock.h" + #include "rpmds_internal.h" + #include "rpmfi_internal.h" /* only internal apis */ ++#include "rpmio_internal.h" + #include "rpmte_internal.h" /* only internal apis */ + #include "rpmts_internal.h" + #include "rpmvs.h" +@@ -1265,6 +1266,81 @@ static int vfyCb(struct rpmsinfo_s *sinfo, void *cbdata) + return (sinfo->rc == 0); + } + ++static ARGI_t initPkgDigests(FD_t fd) ++{ ++ ARGI_t ids = NULL; ++ char *digests = rpmExpand("%{?_pkgverify_digests}", NULL); ++ ARGV_t vals = argvSplitString(digests, ":", 0); ++ ++ for (ARGV_t v = vals; v && *v; v++) { ++ uint32_t alg = atoi(*v); ++ if (alg) { ++ /* Try to ensure unique ids for the digests */ ++ uint32_t id = (RPMTAG_PACKAGEDIGESTS << 16) | alg; ++ fdInitDigestID(fd, alg, id, 0); ++ argiAdd(&ids, -1, id); ++ } ++ } ++ ++ argvFree(vals); ++ free(digests); ++ return ids; ++} ++ ++static void finiPkgDigests(FD_t fd, ARGI_t ids, Header auxh) ++{ ++ for (int i = 0; i < argiCount(ids); i++) { ++ char *pkgdig = NULL; ++ uint32_t id = argiData(ids)[i]; ++ fdFiniDigest(fd, id, (void **)&pkgdig, NULL, 1); ++ if (pkgdig) { ++ uint32_t alg = 0xffff & id; ++ headerPutString(auxh, RPMTAG_PACKAGEDIGESTS, pkgdig); ++ headerPutUint32(auxh, RPMTAG_PACKAGEDIGESTALGOS, &alg, 1); ++ free(pkgdig); ++ } ++ } ++ argiFree(ids); ++} ++ ++static int verifyPackage(rpmts ts, rpmte p, struct rpmvs_s *vs, int vfylevel) ++{ ++ struct vfydata_s vd = { ++ .msg = NULL, ++ .type = { -1, -1, -1, }, ++ .vfylevel = vfylevel, ++ }; ++ int verified = 0; ++ rpmRC prc = RPMRC_FAIL; ++ Header auxh = rpmteHeaderAux(p, 1); ++ ++ FD_t fd = rpmtsNotify(ts, p, RPMCALLBACK_INST_OPEN_FILE, 0, 0); ++ if (fd != NULL) { ++ ARGI_t ids = initPkgDigests(fd); ++ prc = rpmpkgRead(vs, fd, NULL, NULL, &vd.msg); ++ int test = rpmtsFlags(ts) & RPMTRANS_FLAG_TEST; ++ finiPkgDigests(fd, ids, (test || prc) ? NULL : auxh); ++ rpmtsNotify(ts, p, RPMCALLBACK_INST_CLOSE_FILE, 0, 0); ++ } ++ ++ if (prc == RPMRC_OK) ++ prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd); ++ ++ /* Record verify result */ ++ if (vd.type[RPMSIG_SIGNATURE_TYPE] == RPMRC_OK) ++ verified |= RPMSIG_SIGNATURE_TYPE; ++ if (vd.type[RPMSIG_DIGEST_TYPE] == RPMRC_OK) ++ verified |= RPMSIG_DIGEST_TYPE; ++ rpmteSetVerified(p, verified); ++ ++ if (prc) ++ rpmteAddProblem(p, RPMPROB_VERIFY, NULL, vd.msg, 0); ++ ++ vd.msg = _free(vd.msg); ++ headerFree(auxh); ++ return prc; ++} ++ + static int verifyPackageFiles(rpmts ts, rpm_loff_t total) + { + int rc = 0; +@@ -1282,35 +1358,8 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total) + pi = rpmtsiInit(ts); + while ((p = rpmtsiNext(pi, TR_ADDED))) { + struct rpmvs_s *vs = rpmvsCreate(vfylevel, vsflags, keyring); +- struct vfydata_s vd = { +- .msg = NULL, +- .type = { -1, -1, -1, }, +- .vfylevel = vfylevel, +- }; +- int verified = 0; +- rpmRC prc = RPMRC_FAIL; +- + rpmtsNotify(ts, p, RPMCALLBACK_VERIFY_PROGRESS, oc++, total); +- FD_t fd = rpmtsNotify(ts, p, RPMCALLBACK_INST_OPEN_FILE, 0, 0); +- if (fd != NULL) { +- prc = rpmpkgRead(vs, fd, NULL, NULL, &vd.msg); +- rpmtsNotify(ts, p, RPMCALLBACK_INST_CLOSE_FILE, 0, 0); +- } +- +- if (prc == RPMRC_OK) +- prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd); +- +- /* Record verify result */ +- if (vd.type[RPMSIG_SIGNATURE_TYPE] == RPMRC_OK) +- verified |= RPMSIG_SIGNATURE_TYPE; +- if (vd.type[RPMSIG_DIGEST_TYPE] == RPMRC_OK) +- verified |= RPMSIG_DIGEST_TYPE; +- rpmteSetVerified(p, verified); +- +- if (prc) +- rpmteAddProblem(p, RPMPROB_VERIFY, NULL, vd.msg, 0); +- +- vd.msg = _free(vd.msg); ++ verifyPackage(ts, p, vs, vfylevel); + rpmvsFree(vs); + } + rpmtsNotify(ts, NULL, RPMCALLBACK_VERIFY_STOP, total, total); +diff --git a/macros.in b/macros.in +index f2a28fa57..8240e9613 100644 +--- a/macros.in ++++ b/macros.in +@@ -679,6 +679,23 @@ Supplements: (%{name} = %{version}-%{release} and langpacks-%{1})\ + # Disabler flags for package verification (similar to vsflags) + %_pkgverify_flags 0x0 + ++# A colon separated list of hash algorithms to calculate digests on the entire ++# package files during verification. The calculated digests are stored in the ++# Packagedigests tag of packages in the rpmdb, and the corresponding algorithms ++# in in the Packagedigestalgos tag. No package digests are calculated or stored ++# if --noverify is used during package installation. ++# ++# The following hash algorithms are known to rpm, but availability can vary ++# depending how rpm and it's underlying libraries have been built: ++# ++# 1 MD5 (obsolete) ++# 2 SHA1 (obsolete) ++# 8 SHA256 ++# 9 SHA384 ++# 10 SHA512 ++# ++%_pkgverify_digests 8:10 ++ + # Minimize writes during transactions (at the cost of more reads) to + # conserve eg SSD disks (EXPERIMENTAL). + # 1 enable +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index 8e3a6f3c5..6f26a8fa8 100644 +--- a/tests/CMakeLists.txt ++++ b/tests/CMakeLists.txt +@@ -42,6 +42,7 @@ set(TESTSUITE_AT + rpmreplace.at + rpmmacro.at + rpmpython.at ++ rpmpython2.at + rpmdepmatch.at + rpmscript.at + rpmsigdig.at +diff --git a/tests/mktree.common b/tests/mktree.common +index 80c359f7e..e6ecfca87 100644 +--- a/tests/mktree.common ++++ b/tests/mktree.common +@@ -19,6 +19,7 @@ make_install() + mkdir -p $DESTDIR/$script_dir + cp rpmtests atlocal mktree.common $DESTDIR/$script_dir/ + cp @CMAKE_CURRENT_SOURCE_DIR@/rpmtests.sh $DESTDIR/$script_dir/ ++ cp @CMAKE_CURRENT_SOURCE_DIR@/prpm.py $DESTDIR/@CMAKE_INSTALL_BINDIR@ + + mkdir -p $DESTDIR/build + ln -sf ../data/SOURCES $DESTDIR/build/ +diff --git a/tests/prpm.py b/tests/prpm.py +new file mode 100755 +index 000000000..b11297675 +--- /dev/null ++++ b/tests/prpm.py +@@ -0,0 +1,80 @@ ++#!/usr/bin/python3 ++ ++import rpm ++import argparse ++ ++class transCb: ++ fds = {} ++ ++ def __call__(self, what, amount, total, key, data): ++ if what == rpm.RPMCALLBACK_INST_OPEN_FILE: ++ f = open(key, 'rb') ++ self.fds[key] = f ++ return f.fileno() ++ elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE: ++ del self.fds[key] ++ ++def doprobs(ts): ++ for p in ts.problems(): ++ print(p) ++ ++def runts(ts): ++ probs = ts.run(transCb(), "") ++ if probs: ++ doprobs(ts) ++ return 1 ++ return 0 ++ ++if __name__ == '__main__': ++ parser = argparse.ArgumentParser() ++ parser.add_argument('-i', '--install', nargs='+', default=[]) ++ parser.add_argument('-u', '--upgrade', nargs='+', default=[]) ++ parser.add_argument('-e', '--erase', nargs='+', default=[]) ++ parser.add_argument('-r', '--reinstall', nargs='+', default=[]) ++ parser.add_argument('-R', '--restore', nargs='+', default=[]) ++ parser.add_argument('--root', default='/', action='store') ++ parser.add_argument('-n', '--dry-run', action='store_true') ++ parser.add_argument('--nosignature', action='store_true') ++ parser.add_argument('--nodeps', action='store_true') ++ parser.add_argument('--noorder', action='store_true') ++ parser.add_argument('-v', '--verbose', action='store_true') ++ args = parser.parse_args() ++ ++ ts = rpm.ts(args.root) ++ ovflags = ts.setVSFlags(ts.getVSFlags() | rpm.RPMVSF_MASK_NOSIGNATURES) ++ for arg in args.install: ++ ts.addInstall(arg, arg, 'i') ++ for arg in args.upgrade: ++ ts.addInstall(arg, arg, 'u') ++ for arg in args.reinstall: ++ ts.addReinstall(arg, arg) ++ for arg in args.restore: ++ ts.addRestore(arg, arg) ++ for arg in args.erase: ++ ts.addErase(arg) ++ ts.setVSFlags(ovflags) ++ ++ if args.nosignature: ++ ts.setVfyLevel(rpm.RPMSIG_DIGEST_TYPE) ++ ++ if not args.noorder: ++ ts.order() ++ ++ if not args.nodeps: ++ rc = ts.check() ++ if rc: ++ doprobs(ts) ++ exit(rc) ++ ++ oflags = ts.setFlags(rpm.RPMTRANS_FLAG_TEST) ++ if args.verbose: ++ print("Testing") ++ rc = runts(ts) ++ ts.setFlags(oflags) ++ ++ if rc == 0 and not args.dry_run: ++ if args.verbose: ++ print("Committing") ++ rc = runts(ts) ++ ++ exit(rc) +diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at +index 1951d2f9f..a1342eff8 100644 +--- a/tests/rpmgeneral.at ++++ b/tests/rpmgeneral.at +@@ -199,6 +199,8 @@ ORIGDIRNAMES + ORIGFILENAMES + OS + P ++PACKAGEDIGESTALGOS ++PACKAGEDIGESTS + PACKAGER + PATCH + PATCHESFLAGS +diff --git a/tests/rpmi.at b/tests/rpmi.at +index 2a571184e..87eb4fbf9 100644 +--- a/tests/rpmi.at ++++ b/tests/rpmi.at +@@ -44,6 +44,7 @@ runroot rpm -U --ignorearch --ignoreos --nodeps \ + [0], + [], + []) ++ + RPMTEST_CLEANUP + + AT_SETUP([rpm -U ]) +diff --git a/tests/rpmpython2.at b/tests/rpmpython2.at +new file mode 100644 +index 000000000..fe6315a43 +--- /dev/null ++++ b/tests/rpmpython2.at +@@ -0,0 +1,41 @@ ++ ++AT_SETUP([prpm install]) ++AT_KEYWORDS([install python]) ++AT_SKIP_IF([$PYTHON_DISABLED]) ++RPMDB_INIT ++ ++RPMTEST_CHECK([ ++runroot_other prpm.py --nodeps --nosignature \ ++ -i /data/RPMS/hello-2.0-1.x86_64.rpm ++], ++[0], ++[], ++[]) ++ ++RPMTEST_CHECK([[ ++runroot rpm -qa --qf "[%{packagedigestalgos:hashalgo} %{packagedigests}\n]" ++]], ++[0], ++[SHA256 e05a5191e214b1f05ae2448ebe493e55c6313ab68eaf040b83baa80e25f15d54 ++SHA512 5e0a11bf9c4f353b9197446d722e66cc322030e164929356e3fb669201597be77f3a44b4bd6f4fddf8746768809b43dae28f4fad1de315ef42a78e130847eb05 ++], ++[]) ++ ++RPMTEST_CHECK([ ++runroot_other prpm.py --nodeps --nosignature \ ++ -i /data/RPMS/hlinktest-1.0-1.noarch.rpm \ ++ -e hello ++], ++[0], ++[], ++[]) ++ ++RPMTEST_CHECK([ ++runroot rpm -qa ++], ++[0], ++[hlinktest-1.0-1.noarch ++], ++[]) ++ ++RPMTEST_CLEANUP +diff --git a/tests/rpmquery.at b/tests/rpmquery.at +index 8263fe9ba..7d604ad6e 100644 +--- a/tests/rpmquery.at ++++ b/tests/rpmquery.at +@@ -420,6 +420,21 @@ runroot rpm \ + ]) + RPMTEST_CLEANUP + ++AT_SETUP([hashalgo extension]) ++AT_KEYWORDS([query digest]) ++RPMTEST_CHECK([ ++rpm -q \ ++ --qf "%{filedigestalgo:hashalgo}\n" \ ++ --qf "%{longsize:hashalgo}\n" \ ++ "${RPMTEST}"/data/RPMS/hello-2.0-1.x86_64.rpm ++], ++[0], ++[SHA256 ++Unknown ++], ++[]) ++RPMTEST_CLEANUP ++ + # ------------------------------ + AT_SETUP([integer array perms format query]) + AT_KEYWORDS([query]) +diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at +index a9c55a654..9111befe0 100644 +--- a/tests/rpmsigdig.at ++++ b/tests/rpmsigdig.at +@@ -1192,3 +1192,39 @@ rpm -qp --qf "[%{filenames}:%{filesignatures}\n]" hello-2.0-1.x86_64-badima.rpm + ], + []) + RPMTEST_CLEANUP ++ ++AT_SETUP([package verification digest]) ++AT_KEYWORDS([install digest verify]) ++ ++RPMTEST_CHECK([ ++RPMDB_INIT ++runroot rpm -U \ ++ --define "_pkgverify_digests aa:bb:zz" \ ++ --ignorearch --ignoreos --nodeps --nosignature --justdb \ ++ /data/RPMS/hello-2.0-1.{i686,x86_64}.rpm ++ ++runroot rpm -qa --qf "[[%{packagedigestalgos} %{packagedigests}\n]]" | sort -n ++], ++[0], ++[], ++[]) ++ ++RPMTEST_CHECK([ ++RPMDB_INIT ++runroot rpm -U \ ++ --define "_pkgverify_digests 2:8:12345:10" \ ++ --ignorearch --ignoreos --nodeps --nosignature --justdb \ ++ /data/RPMS/hello-2.0-1.{i686,x86_64}.rpm ++ ++runroot rpm -qa --qf "[[%{packagedigestalgos:hashalgo} %{packagedigests}\n]]" | sort -n ++], ++[0], ++[SHA1 70d8bfc198823acdec9b3d793770e12ead6ae047 ++SHA1 7299fad790a49e571a7ec4d60bef5d51597085fa ++SHA256 3328b90a578d18dba45abc584395795115c6c024abd561ce533ec175619925ff ++SHA256 e05a5191e214b1f05ae2448ebe493e55c6313ab68eaf040b83baa80e25f15d54 ++SHA512 4db194ba2cb8b5e5cbb6f8d0dc1ec50be15cb20cdefa74b2b14a74032789b7411f4365dc4e69c7e3e37882f549d5f6e91b863eb51629e6ab72438e54b8eeedf5 ++SHA512 5e0a11bf9c4f353b9197446d722e66cc322030e164929356e3fb669201597be77f3a44b4bd6f4fddf8746768809b43dae28f4fad1de315ef42a78e130847eb05 ++], ++[]) ++RPMTEST_CLEANUP +diff --git a/tests/rpmtests.at b/tests/rpmtests.at +index d675452e4..4fbc70fc6 100644 +--- a/tests/rpmtests.at ++++ b/tests/rpmtests.at +@@ -4,6 +4,7 @@ m4_include([rpmvercmp.at]) + m4_include([rpmmacro.at]) + m4_include([rpmdevel.at]) + m4_include([rpmpython.at]) ++m4_include([rpmpython2.at]) + m4_include([rpmdepmatch.at]) + m4_include([rpmquery.at]) + m4_include([rpmspec.at]) +-- +2.49.0 + +diff -up rpm-4.19.1.1/docs/man/rpm.8.orig rpm-4.19.1.1/docs/man/rpm.8 +--- rpm-4.19.1.1/docs/man/rpm.8.orig 2025-04-24 14:18:03.652976012 +0200 ++++ rpm-4.19.1.1/docs/man/rpm.8 2025-04-24 14:18:22.279864710 +0200 +@@ -479,6 +479,9 @@ Format file state. + \f[B]:fstatus\f[R] + Format file verify status. + .TP ++\f[B]:hashalgo\f[R] ++Display hash algorithm name. ++.TP + \f[B]:hex\f[R] + Format in hexadecimal. + .TP diff --git a/rpm.spec b/rpm.spec index e9ae6da..01e0323 100644 --- a/rpm.spec +++ b/rpm.spec @@ -158,6 +158,7 @@ rpm-4.18.90-weak-user-group.patch 0003-Fix-memory-leak-in-runGPG.patch 0004-Talk-about-rpmsign-in-the-rpmsign-man-page.patch 0005-Revert-Drop-redundant-argument-from-rpmcliTransactio.patch +0001-Store-configurable-digest-s-on-packages-from-verific.patch # These are not yet upstream rpm-4.7.1-geode-i686.patch @@ -646,6 +647,7 @@ fi %changelog * Thu Apr 24 2025 Michal Domonkos - 4.19.1.1-14 +- Store configurable digest(s) on packages in rpmdb (RHEL-84062) - Fix command references in rpmsign(8) man page, take II (RHEL-73173) * Tue Apr 22 2025 Michal Domonkos - 4.19.1.1-13