diff --git a/SOURCES/0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch b/SOURCES/0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch new file mode 100644 index 0000000..178a328 --- /dev/null +++ b/SOURCES/0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch @@ -0,0 +1,32 @@ +From 2043b3f0e079b8efe97f8ccc0a140e4ad4e0db4f Mon Sep 17 00:00:00 2001 +From: "Vladimir D. Seleznev" +Date: Tue, 13 Mar 2018 00:04:45 +0300 +Subject: [PATCH 01/33] Add RPMTAG_AUTOINSTALLED reservation + +This tag is needed to track automatically installed packages with rpmdb. +Zero value means that a package was installed manually, other values +mean that the package was installed automatically as some else package +dependency. + +This tag is reserved for ALT Linux Team and marked as unimplemented. + +Signed-off-by: Vladimir D. Seleznev +--- + lib/rpmtag.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 57b10a706..664561156 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -368,6 +368,7 @@ typedef enum rpmTag_e { + RPMTAG_FILESIGNATURELENGTH = 5091, /* i */ + RPMTAG_PAYLOADDIGEST = 5092, /* s[] */ + RPMTAG_PAYLOADDIGESTALGO = 5093, /* i */ ++ RPMTAG_AUTOINSTALLED = 5094, /* i reservation (unimplemented) */ + RPMTAG_MODULARITYLABEL = 5096, /* s */ + + RPMTAG_FIRSTFREE_TAG /*!< internal */ +-- +2.13.5 + diff --git a/SOURCES/0002-Add-RPMTAG_IDENTITY-reservation.patch b/SOURCES/0002-Add-RPMTAG_IDENTITY-reservation.patch new file mode 100644 index 0000000..5d7c727 --- /dev/null +++ b/SOURCES/0002-Add-RPMTAG_IDENTITY-reservation.patch @@ -0,0 +1,34 @@ +From e4c6197c5d81be1c1a9adc31c47c110e623a66a0 Mon Sep 17 00:00:00 2001 +From: "Vladimir D. Seleznev" +Date: Tue, 13 Mar 2018 00:04:46 +0300 +Subject: [PATCH 02/33] Add RPMTAG_IDENTITY reservation + +This tag represents binary package build characteristic: if two binary +packages have equal RPMTAG_IDENTITY values, it means that these packages +have no significant differences. + +One of the applications of RPMTAG_IDENTITY is reproducible build +verification. + +This tag is reserved for ALT Linux Team and marked as unimplemented. + +Signed-off-by: Vladimir D. Seleznev +--- + lib/rpmtag.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 664561156..002492d20 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -369,6 +369,7 @@ typedef enum rpmTag_e { + RPMTAG_PAYLOADDIGEST = 5092, /* s[] */ + RPMTAG_PAYLOADDIGESTALGO = 5093, /* i */ + RPMTAG_AUTOINSTALLED = 5094, /* i reservation (unimplemented) */ ++ RPMTAG_IDENTITY = 5095, /* s reservation (unimplemented) */ + RPMTAG_MODULARITYLABEL = 5096, /* s */ + + RPMTAG_FIRSTFREE_TAG /*!< internal */ +-- +2.13.5 + diff --git a/SOURCES/0003-Implement-support-for-alternative-uncompressed-paylo.patch b/SOURCES/0003-Implement-support-for-alternative-uncompressed-paylo.patch new file mode 100644 index 0000000..84faa88 --- /dev/null +++ b/SOURCES/0003-Implement-support-for-alternative-uncompressed-paylo.patch @@ -0,0 +1,248 @@ +From 6052efe94992ee591b72b495b209777fc71d0f6a Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 3 Oct 2019 14:58:11 +0300 +Subject: [PATCH 03/33] Implement support for alternative (uncompressed) + payload digest + +During build, also calculate a digest for the uncompressed payload data +and add to packages. On verify side this is equivalent to the existing +payload digest (through sharing the same disabler), which allows +either one to be used for verification. This means deltarpm and similar +don't need to recompress the data which is both expensive and error-prone +due to minor differences in compressed stream despite the actual data +being identical. + +Add a testcase for the basic behavior and update other test output +expectations where necessary. +--- + build/pack.c | 16 ++++++++++++---- + lib/rpmchecksig.c | 1 + + lib/rpmtag.h | 1 + + lib/rpmvs.c | 4 ++++ + tests/rpmgeneral.at | 1 + + tests/rpmsigdig.at | 32 ++++++++++++++++++++++++++++++++ + tests/rpmvfylevel.at | 2 ++ + 7 files changed, 53 insertions(+), 4 deletions(-) + +diff --git a/build/pack.c b/build/pack.c +index c7c1d8f46..9293e98e4 100644 +--- a/build/pack.c ++++ b/build/pack.c +@@ -70,7 +70,8 @@ static int rpmPackageFilesArchive(rpmfiles fi, int isSrc, + * @todo Create transaction set *much* earlier. + */ + static rpmRC cpio_doio(FD_t fdo, Package pkg, const char * fmodeMacro, +- rpm_loff_t *archiveSize) ++ int pld_algo, ++ rpm_loff_t *archiveSize, char ** pldig) + { + char *failedFile = NULL; + FD_t cfd; +@@ -81,9 +82,12 @@ static rpmRC cpio_doio(FD_t fdo, Package pkg, const char * fmodeMacro, + if (cfd == NULL) + return RPMRC_FAIL; + ++ /* Calculate alternative (uncompressed) payload digest while writing */ ++ fdInitDigestID(cfd, pld_algo, RPMTAG_PAYLOADDIGESTALT, 0); + fsmrc = rpmPackageFilesArchive(pkg->cpioList, headerIsSource(pkg->header), + cfd, pkg->dpaths, + archiveSize, &failedFile); ++ fdFiniDigest(cfd, RPMTAG_PAYLOADDIGESTALT, (void **)pldig, NULL, 1); + + if (fsmrc) { + char *emsg = rpmfileStrerror(fsmrc); +@@ -512,6 +516,7 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, + char * SHA256 = NULL; + uint8_t * MD5 = NULL; + char * pld = NULL; ++ char * upld = NULL; + uint32_t pld_algo = PGPHASHALGO_SHA256; /* TODO: macro configuration */ + rpmRC rc = RPMRC_FAIL; /* assume failure */ + rpm_loff_t archiveSize = 0; +@@ -532,10 +537,11 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, + headerPutString(pkg->header, RPMTAG_COOKIE, *cookie); + } + +- /* Create a dummy payload digest to get the header size right */ ++ /* Create a dummy payload digests to get the header size right */ + pld = nullDigest(pld_algo, 1); + headerPutUint32(pkg->header, RPMTAG_PAYLOADDIGESTALGO, &pld_algo, 1); + headerPutString(pkg->header, RPMTAG_PAYLOADDIGEST, pld); ++ headerPutString(pkg->header, RPMTAG_PAYLOADDIGESTALT, pld); + pld = _free(pld); + + /* Check for UTF-8 encoding of string tags, add encoding tag if all good */ +@@ -576,7 +582,7 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, + + /* Write payload section (cpio archive) */ + payloadStart = Ftell(fd); +- if (cpio_doio(fd, pkg, rpmio_flags, &archiveSize)) ++ if (cpio_doio(fd, pkg, rpmio_flags, pld_algo, &archiveSize, &upld)) + goto exit; + payloadEnd = Ftell(fd); + +@@ -586,9 +592,11 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, + goto exit; + fdFiniDigest(fd, RPMTAG_PAYLOADDIGEST, (void **)&pld, NULL, 1); + +- /* Insert the payload digest in main header */ ++ /* Insert the payload digests in main header */ + headerDel(pkg->header, RPMTAG_PAYLOADDIGEST); + headerPutString(pkg->header, RPMTAG_PAYLOADDIGEST, pld); ++ headerDel(pkg->header, RPMTAG_PAYLOADDIGESTALT); ++ headerPutString(pkg->header, RPMTAG_PAYLOADDIGESTALT, upld); + pld = _free(pld); + + /* Write the final header */ +diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c +index beca0e7b2..c4986b99c 100644 +--- a/lib/rpmchecksig.c ++++ b/lib/rpmchecksig.c +@@ -189,6 +189,7 @@ rpmRC rpmpkgRead(struct rpmvs_s *vs, FD_t fd, + + /* Fish interesting tags from the main header. This is a bit hacky... */ + rpmvsAppendTag(vs, blob, RPMTAG_PAYLOADDIGEST); ++ rpmvsAppendTag(vs, blob, RPMTAG_PAYLOADDIGESTALT); + + /* If needed and not explicitly disabled, read the payload as well. */ + if (rpmvsRange(vs) & RPMSIG_PAYLOAD) { +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 002492d20..8bdf34405 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -371,6 +371,7 @@ typedef enum rpmTag_e { + RPMTAG_AUTOINSTALLED = 5094, /* i reservation (unimplemented) */ + RPMTAG_IDENTITY = 5095, /* s reservation (unimplemented) */ + RPMTAG_MODULARITYLABEL = 5096, /* s */ ++ RPMTAG_PAYLOADDIGESTALT = 5097, /* s[] */ + + RPMTAG_FIRSTFREE_TAG /*!< internal */ + } rpmTag; +diff --git a/lib/rpmvs.c b/lib/rpmvs.c +index 0d475af86..9a90e2eb8 100644 +--- a/lib/rpmvs.c ++++ b/lib/rpmvs.c +@@ -40,6 +40,7 @@ static const struct vfytag_s rpmvfytags[] = { + { RPMSIGTAG_LONGARCHIVESIZE, RPM_INT64_TYPE, 1, 8, }, + { RPMTAG_SHA256HEADER, RPM_STRING_TYPE, 1, 65, }, + { RPMTAG_PAYLOADDIGEST, RPM_STRING_ARRAY_TYPE, 0, 0, }, ++ { RPMTAG_PAYLOADDIGESTALT, RPM_STRING_ARRAY_TYPE, 0, 0, }, + { 0 } /* sentinel */ + }; + +@@ -89,6 +90,9 @@ static const struct vfyinfo_s rpmvfyitems[] = { + { RPMTAG_PAYLOADDIGEST, 0, + { RPMSIG_DIGEST_TYPE, RPMVSF_NOPAYLOAD, + (RPMSIG_PAYLOAD), PGPHASHALGO_SHA256, 0, }, }, ++ { RPMTAG_PAYLOADDIGESTALT, 0, ++ { RPMSIG_DIGEST_TYPE, RPMVSF_NOPAYLOAD, ++ (RPMSIG_PAYLOAD), PGPHASHALGO_SHA256, 0, 1, }, }, + { 0 } /* sentinel */ + }; + +diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at +index 45d38698b..7f100774c 100644 +--- a/tests/rpmgeneral.at ++++ b/tests/rpmgeneral.at +@@ -192,6 +192,7 @@ PATCHESVERSION + PAYLOADCOMPRESSOR + PAYLOADDIGEST + PAYLOADDIGESTALGO ++PAYLOADDIGESTALT + PAYLOADFLAGS + PAYLOADFORMAT + PKGID +diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at +index 880e5edd0..f6ad72589 100644 +--- a/tests/rpmsigdig.at ++++ b/tests/rpmsigdig.at +@@ -26,6 +26,34 @@ runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64.rpm /data/RPMS/hello-1.0-1.i38 + []) + AT_CLEANUP + ++AT_SETUP([rpmkeys -Kv 1]) ++AT_KEYWORDS([rpmkeys digest]) ++AT_CHECK([ ++RPMDB_CLEAR ++RPMDB_INIT ++rm -rf "${TOPDIR}" ++ ++cp "${RPMTEST}"/data/misc/hello.intro "${RPMTEST}"/data/misc/hello.payload . ++gzip -cd < hello.payload > hello.uc-payload ++cat hello.intro hello.payload > "${RPMTEST}"/tmp/hello-c.rpm ++cat hello.intro hello.uc-payload > "${RPMTEST}"/tmp/hello-uc.rpm ++runroot rpmkeys -Kv /tmp/hello-c.rpm /tmp/hello-uc.rpm ++], ++[1], ++[/tmp/hello-c.rpm: ++ Header SHA256 digest: OK ++ Header SHA1 digest: OK ++ Payload SHA256 digest: OK ++ MD5 digest: OK ++/tmp/hello-uc.rpm: ++ Header SHA256 digest: OK ++ Header SHA1 digest: OK ++ Payload SHA256 ALT digest: OK ++ MD5 digest: BAD (Expected 055607c4dee6464b9415ae726e7d81a7 != 839d24c30e5188e0b83599fbe3865919) ++], ++[]) ++AT_CLEANUP ++ + # ------------------------------ + # Test corrupted package verification (corrupted signature) + AT_SETUP([rpmkeys -Kv 1]) +@@ -96,6 +124,7 @@ runroot rpmkeys -Kv /tmp/${pkg} + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: BAD (Expected 84a7338287bf19715c4eed0243f5cdb447eeb0ade37b2af718d4060aefca2f7c != bea903609dceac36e1f26a983c493c98064d320fdfeb423034ed63d649b2c8dc) ++ Payload SHA256 ALT digest: NOTFOUND + MD5 digest: BAD (Expected 137ca1d8b35cca02a1854ba301c5432e != d662cd0d81601a7107312684ad1ddf38) + ], + []) +@@ -145,6 +174,7 @@ runroot rpmkeys -Kv /build/RPMS/noarch/attrtest-1.0-1.noarch.rpm + [/build/RPMS/noarch/attrtest-1.0-1.noarch.rpm: + Header SHA256 digest: OK + Header SHA1 digest: OK ++ Payload SHA256 ALT digest: OK + Payload SHA256 digest: OK + MD5 digest: OK + ], +@@ -376,6 +406,7 @@ runroot rpmkeys -Kv /tmp/${pkg} + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: BAD (Expected 84a7338287bf19715c4eed0243f5cdb447eeb0ade37b2af718d4060aefca2f7c != bea903609dceac36e1f26a983c493c98064d320fdfeb423034ed63d649b2c8dc) ++ Payload SHA256 ALT digest: NOTFOUND + V4 RSA/SHA256 Signature, key ID 1964c5fc: BAD + MD5 digest: BAD (Expected 137ca1d8b35cca02a1854ba301c5432e != d662cd0d81601a7107312684ad1ddf38) + /tmp/hello-2.0-1.x86_64-signed.rpm: +@@ -383,6 +414,7 @@ runroot rpmkeys -Kv /tmp/${pkg} + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: BAD (Expected 84a7338287bf19715c4eed0243f5cdb447eeb0ade37b2af718d4060aefca2f7c != bea903609dceac36e1f26a983c493c98064d320fdfeb423034ed63d649b2c8dc) ++ Payload SHA256 ALT digest: NOTFOUND + V4 RSA/SHA256 Signature, key ID 1964c5fc: BAD + MD5 digest: BAD (Expected 137ca1d8b35cca02a1854ba301c5432e != d662cd0d81601a7107312684ad1ddf38) + ], +diff --git a/tests/rpmvfylevel.at b/tests/rpmvfylevel.at +index 17531b0fe..cb5e48ad7 100644 +--- a/tests/rpmvfylevel.at ++++ b/tests/rpmvfylevel.at +@@ -117,6 +117,7 @@ nopl + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: NOTFOUND ++ Payload SHA256 ALT digest: NOTFOUND + MD5 digest: NOTFOUND + 1 + nosha1 +@@ -338,6 +339,7 @@ noplds + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: NOTFOUND ++ Payload SHA256 ALT digest: NOTFOUND + RSA signature: NOTFOUND + DSA signature: NOTFOUND + MD5 digest: OK +-- +2.13.5 + diff --git a/SOURCES/0004-Use-lower-level-headerPut-for-file-signing.patch b/SOURCES/0004-Use-lower-level-headerPut-for-file-signing.patch new file mode 100644 index 0000000..158681d --- /dev/null +++ b/SOURCES/0004-Use-lower-level-headerPut-for-file-signing.patch @@ -0,0 +1,58 @@ +From fa25bcaab93cd9f1f90d5ebb0111655b265386dc Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 11:07:38 +0300 +Subject: [PATCH 04/33] Use lower-level headerPut() for file signing + +Not supposed to affect behavior at all, but we'll need this in the +next step. +--- + sign/rpmsignfiles.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c +index 61b73bd40..1fc127cb1 100644 +--- a/sign/rpmsignfiles.c ++++ b/sign/rpmsignfiles.c +@@ -82,7 +82,7 @@ char *keypass) + + rpmRC rpmSignFiles(Header h, const char *key, char *keypass) + { +- struct rpmtd_s digests; ++ struct rpmtd_s digests, td; + int algo; + int diglen; + uint32_t siglen; +@@ -110,7 +110,19 @@ rpmRC rpmSignFiles(Header h, const char *key, char *keypass) + headerDel(h, RPMTAG_FILESIGNATURELENGTH); + headerDel(h, RPMTAG_FILESIGNATURES); + siglen = signatureLength(algoname, diglen, key, keypass); +- headerPutUint32(h, RPMTAG_FILESIGNATURELENGTH, &siglen, 1); ++ ++ rpmtdReset(&td); ++ td.tag = RPMTAG_FILESIGNATURELENGTH; ++ td.type = RPM_INT32_TYPE; ++ td.data = &siglen; ++ td.count = 1; ++ headerPut(h, &td, HEADERPUT_DEFAULT); ++ ++ rpmtdReset(&td); ++ td.tag = RPMTAG_FILESIGNATURES; ++ td.type = RPM_STRING_ARRAY_TYPE; ++ td.data = NULL; /* set in the loop below */ ++ td.count = 1; + + headerGet(h, RPMTAG_FILEDIGESTS, &digests, HEADERGET_MINMEM); + while ((digest = rpmtdNextString(&digests))) { +@@ -120,7 +132,8 @@ rpmRC rpmSignFiles(Header h, const char *key, char *keypass) + rc = RPMRC_FAIL; + goto exit; + } +- if (!headerPutString(h, RPMTAG_FILESIGNATURES, signature)) { ++ td.data = &signature; ++ if (!headerPut(h, &td, HEADERPUT_APPEND)) { + free(signature); + rpmlog(RPMLOG_ERR, _("headerPutString failed\n")); + rc = RPMRC_FAIL; +-- +2.13.5 + diff --git a/SOURCES/0005-Place-file-signatures-into-the-signature-header-wher.patch b/SOURCES/0005-Place-file-signatures-into-the-signature-header-wher.patch new file mode 100644 index 0000000..6918f3c --- /dev/null +++ b/SOURCES/0005-Place-file-signatures-into-the-signature-header-wher.patch @@ -0,0 +1,370 @@ +From d343c335fbf451ff1eebe3b347954c401c22ebf1 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 11:44:10 +0300 +Subject: [PATCH 05/33] Place file signatures into the signature header where + they belong + +The original file signing puts the file signatures into the main header +immutable region, invalidating all previous signatures and digests so +the package no longer appears to be what it was when it came out of the +assembly line. Which is bad. Doing that also requires recalculating +everything again which is just added complexity, and since it adds +stuff to different place from the rest of the signing, it requires yet +complexity to deal with that. Moving the file signatures into the +signature header solves all that and allows removing a big pile of +now unnecessary code. + +Because this means retrofitting tags bass-ackwards into the signature +header, the tag definitions are backwards to everything else. Other +options would certainly be possible, but this makes things look more +normal on the signature header side. "Users" only ever see the +unchanged file signature tags as they have always been. + +This also means the signature header can be MUCH bigger than ever before, +so bump up the limit (to 64MB, arbitrary something for now), and +permit string array types to be migrated from the signature header +on package read. + +Caveats: +This loses the check for identical existing signatures to keep the +complexity down, it's hardly a critical thing and can be added back later. +While file signing could now be done separately to other signing, that +is not handled here. +--- + lib/header.c | 2 +- + lib/package.c | 4 +- + lib/rpmtag.h | 4 ++ + sign/rpmgensig.c | 161 +++------------------------------------------------- + sign/rpmsignfiles.c | 14 ++--- + sign/rpmsignfiles.h | 5 +- + 6 files changed, 26 insertions(+), 164 deletions(-) + +diff --git a/lib/header.c b/lib/header.c +index b05fda196..ab96da309 100644 +--- a/lib/header.c ++++ b/lib/header.c +@@ -1910,7 +1910,7 @@ rpmRC hdrblobRead(FD_t fd, int magic, int exact_size, rpmTagVal regionTag, hdrbl + + if (regionTag == RPMTAG_HEADERSIGNATURES) { + il_max = 32; +- dl_max = 8192; ++ dl_max = 64 * 1024; + } + + memset(block, 0, sizeof(block)); +diff --git a/lib/package.c b/lib/package.c +index d3fc37324..8ee0e9e7c 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -69,6 +69,8 @@ void headerMergeLegacySigs(Header h, Header sigh) + case RPMSIGTAG_SHA256: + case RPMSIGTAG_DSA: + case RPMSIGTAG_RSA: ++ case RPMSIGTAG_FILESIGNATURELENGTH: ++ case RPMSIGTAG_FILESIGNATURES: + default: + if (!(td.tag >= HEADER_SIGBASE && td.tag < HEADER_TAGBASE)) + continue; +@@ -88,11 +90,11 @@ void headerMergeLegacySigs(Header h, Header sigh) + continue; + break; + case RPM_STRING_TYPE: ++ case RPM_STRING_ARRAY_TYPE: + case RPM_BIN_TYPE: + if (td.count >= 16*1024) + continue; + break; +- case RPM_STRING_ARRAY_TYPE: + case RPM_I18NSTRING_TYPE: + continue; + break; +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 8bdf34405..7a9092a6e 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -65,6 +65,8 @@ typedef enum rpmTag_e { + RPMTAG_LONGARCHIVESIZE = RPMTAG_SIG_BASE+15, /* l */ + /* RPMTAG_SIG_BASE+16 reserved */ + RPMTAG_SHA256HEADER = RPMTAG_SIG_BASE+17, /* s */ ++ /* RPMTAG_SIG_BASE+18 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ ++ /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURES */ + + RPMTAG_NAME = 1000, /* s */ + #define RPMTAG_N RPMTAG_NAME /* s */ +@@ -426,6 +428,8 @@ typedef enum rpmSigTag_e { + RPMSIGTAG_LONGSIZE = RPMTAG_LONGSIGSIZE, /*!< internal Header+Payload size (64bit) in bytes. */ + RPMSIGTAG_LONGARCHIVESIZE = RPMTAG_LONGARCHIVESIZE, /*!< internal uncompressed payload size (64bit) in bytes. */ + RPMSIGTAG_SHA256 = RPMTAG_SHA256HEADER, ++ RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 18, ++ RPMSIGTAG_FILESIGNATURES = RPMTAG_SIG_BASE + 19, + } rpmSigTag; + + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index e20cbdb12..875b96078 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -410,74 +410,12 @@ static void unloadImmutableRegion(Header *hdrp, rpmTagVal tag) + } + } + +-#ifdef WITH_IMAEVM +-static rpmRC replaceSigDigests(FD_t fd, const char *rpm, Header *sigp, +- off_t sigStart, off_t sigTargetSize, +- char *SHA256, char *SHA1, uint8_t *MD5) +-{ +- off_t archiveSize; +- rpmRC rc = RPMRC_OK; +- +- if (Fseek(fd, sigStart, SEEK_SET) < 0) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), +- rpm, Fstrerror(fd)); +- goto exit; +- } +- +- /* Get payload size from signature tag */ +- archiveSize = headerGetNumber(*sigp, RPMSIGTAG_PAYLOADSIZE); +- if (!archiveSize) { +- archiveSize = headerGetNumber(*sigp, RPMSIGTAG_LONGARCHIVESIZE); +- } +- +- /* Set reserved space to 0 */ +- rpmPushMacro(NULL, "__gpg_reserved_space", NULL, 0, RMIL_GLOBAL); +- +- /* Replace old digests in sigh */ +- rc = rpmGenerateSignature(SHA256, SHA1, MD5, sigTargetSize, archiveSize, fd); +- if (rc != RPMRC_OK) { +- rpmlog(RPMLOG_ERR, _("generateSignature failed\n")); +- goto exit; +- } +- +- if (Fseek(fd, sigStart, SEEK_SET) < 0) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), +- rpm, Fstrerror(fd)); +- goto exit; +- } +- +- headerFree(*sigp); +- rc = rpmReadSignature(fd, sigp, NULL); +- if (rc != RPMRC_OK) { +- rpmlog(RPMLOG_ERR, _("rpmReadSignature failed\n")); +- goto exit; +- } +- +-exit: +- return rc; +-} +-#endif +- +-static rpmRC includeFileSignatures(FD_t fd, const char *rpm, +- Header *sigp, Header *hdrp, +- off_t sigStart, off_t headerStart) ++static rpmRC includeFileSignatures(Header *sigp, Header *hdrp) + { + #ifdef WITH_IMAEVM +- FD_t ofd = NULL; +- char *trpm = NULL; ++ rpmRC rc; + char *key; + char *keypass; +- char *SHA1 = NULL; +- char *SHA256 = NULL; +- uint8_t *MD5 = NULL; +- off_t sigTargetSize; +- rpmRC rc = RPMRC_OK; +- struct rpmtd_s osigtd; +- char *o_sha1 = NULL; +- +- unloadImmutableRegion(hdrp, RPMTAG_HEADERIMMUTABLE); + + key = rpmExpand("%{?_file_signing_key}", NULL); + +@@ -487,94 +425,10 @@ static rpmRC includeFileSignatures(FD_t fd, const char *rpm, + keypass = NULL; + } + +- rc = rpmSignFiles(*hdrp, key, keypass); +- if (rc != RPMRC_OK) { +- goto exit; +- } +- +- *hdrp = headerReload(*hdrp, RPMTAG_HEADERIMMUTABLE); +- if (*hdrp == NULL) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("headerReload failed\n")); +- goto exit; +- } +- +- ofd = rpmMkTempFile(NULL, &trpm); +- if (ofd == NULL || Ferror(ofd)) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n")); +- goto exit; +- } +- +- /* Copy archive to temp file */ +- if (copyFile(&fd, rpm, &ofd, trpm)) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("copyFile failed\n")); +- goto exit; +- } +- +- if (Fseek(fd, headerStart, SEEK_SET) < 0) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), +- rpm, Fstrerror(fd)); +- goto exit; +- } ++ rc = rpmSignFiles(*sigp, *hdrp, key, keypass); + +- /* Start MD5 calculation */ +- fdInitDigestID(fd, PGPHASHALGO_MD5, RPMSIGTAG_MD5, 0); +- +- /* Write header to rpm and recalculate digests */ +- fdInitDigestID(fd, PGPHASHALGO_SHA1, RPMSIGTAG_SHA1, 0); +- fdInitDigestID(fd, PGPHASHALGO_SHA256, RPMSIGTAG_SHA256, 0); +- rc = headerWrite(fd, *hdrp, HEADER_MAGIC_YES); +- if (rc != RPMRC_OK) { +- rpmlog(RPMLOG_ERR, _("headerWrite failed\n")); +- goto exit; +- } +- fdFiniDigest(fd, RPMSIGTAG_SHA1, (void **)&SHA1, NULL, 1); +- /* Only add SHA256 if it was there to begin with */ +- if (headerIsEntry(*sigp, RPMSIGTAG_SHA256)) +- fdFiniDigest(fd, RPMSIGTAG_SHA256, (void **)&SHA256, NULL, 1); +- +- /* Copy archive from temp file */ +- if (Fseek(ofd, 0, SEEK_SET) < 0) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), +- rpm, Fstrerror(fd)); +- goto exit; +- } +- if (copyFile(&ofd, trpm, &fd, rpm)) { +- rc = RPMRC_FAIL; +- rpmlog(RPMLOG_ERR, _("copyFile failed\n")); +- goto exit; +- } +- unlink(trpm); +- +- sigTargetSize = Ftell(fd) - headerStart; +- fdFiniDigest(fd, RPMSIGTAG_MD5, (void **)&MD5, NULL, 0); +- +- if (headerGet(*sigp, RPMSIGTAG_SHA1, &osigtd, HEADERGET_DEFAULT)) { +- o_sha1 = xstrdup(osigtd.data); +- rpmtdFreeData(&osigtd); +- } +- +- if (strcmp(SHA1, o_sha1) == 0) +- rpmlog(RPMLOG_WARNING, +- _("%s already contains identical file signatures\n"), +- rpm); +- else +- replaceSigDigests(fd, rpm, sigp, sigStart, sigTargetSize, SHA256, SHA1, MD5); +- +-exit: +- free(trpm); +- free(MD5); +- free(SHA1); +- free(SHA256); +- free(o_sha1); + free(keypass); + free(key); +- if (ofd) +- (void) closeFile(&ofd); + return rc; + #else + rpmlog(RPMLOG_ERR, _("file signing support not built in\n")); +@@ -667,13 +521,14 @@ static int rpmSign(const char *rpm, int deleting, int signfiles) + goto exit; + } + +- if (signfiles) { +- includeFileSignatures(fd, rpm, &sigh, &h, sigStart, headerStart); +- } +- + unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES); + origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES); + ++ if (signfiles) { ++ if (includeFileSignatures(&sigh, &h)) ++ goto exit; ++ } ++ + if (deleting) { /* Nuke all the signature tags. */ + deleteSigs(sigh); + } else { +diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c +index 1fc127cb1..2dcc50400 100644 +--- a/sign/rpmsignfiles.c ++++ b/sign/rpmsignfiles.c +@@ -80,7 +80,7 @@ char *keypass) + return siglen + 1; + } + +-rpmRC rpmSignFiles(Header h, const char *key, char *keypass) ++rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + { + struct rpmtd_s digests, td; + int algo; +@@ -107,19 +107,19 @@ rpmRC rpmSignFiles(Header h, const char *key, char *keypass) + return RPMRC_FAIL; + } + +- headerDel(h, RPMTAG_FILESIGNATURELENGTH); +- headerDel(h, RPMTAG_FILESIGNATURES); ++ headerDel(sigh, RPMTAG_FILESIGNATURELENGTH); ++ headerDel(sigh, RPMTAG_FILESIGNATURES); + siglen = signatureLength(algoname, diglen, key, keypass); + + rpmtdReset(&td); +- td.tag = RPMTAG_FILESIGNATURELENGTH; ++ td.tag = RPMSIGTAG_FILESIGNATURELENGTH; + td.type = RPM_INT32_TYPE; + td.data = &siglen; + td.count = 1; +- headerPut(h, &td, HEADERPUT_DEFAULT); ++ headerPut(sigh, &td, HEADERPUT_DEFAULT); + + rpmtdReset(&td); +- td.tag = RPMTAG_FILESIGNATURES; ++ td.tag = RPMSIGTAG_FILESIGNATURES; + td.type = RPM_STRING_ARRAY_TYPE; + td.data = NULL; /* set in the loop below */ + td.count = 1; +@@ -133,7 +133,7 @@ rpmRC rpmSignFiles(Header h, const char *key, char *keypass) + goto exit; + } + td.data = &signature; +- if (!headerPut(h, &td, HEADERPUT_APPEND)) { ++ if (!headerPut(sigh, &td, HEADERPUT_APPEND)) { + free(signature); + rpmlog(RPMLOG_ERR, _("headerPutString failed\n")); + rc = RPMRC_FAIL; +diff --git a/sign/rpmsignfiles.h b/sign/rpmsignfiles.h +index 4163fafde..2ff623cdf 100644 +--- a/sign/rpmsignfiles.h ++++ b/sign/rpmsignfiles.h +@@ -9,14 +9,15 @@ extern "C" { + #endif + + /** +- * Sign file digests in header and store the signatures in header ++ * Sign file digests in header into signature header ++ * @param sigh package signature header + * @param h package header + * @param key signing key + * @param keypass signing key password + * @return RPMRC_OK on success + */ + RPM_GNUC_INTERNAL +-rpmRC rpmSignFiles(Header h, const char *key, char *keypass); ++rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass); + + #ifdef _cplusplus + } +-- +2.13.5 + diff --git a/SOURCES/0006-Unbreak-file-signing-from-previous-commit.patch b/SOURCES/0006-Unbreak-file-signing-from-previous-commit.patch new file mode 100644 index 0000000..610d305 --- /dev/null +++ b/SOURCES/0006-Unbreak-file-signing-from-previous-commit.patch @@ -0,0 +1,40 @@ +From 0b989774f356a43c8a795d0548ae2c76132c8e60 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 14:40:05 +0300 +Subject: [PATCH 06/33] Unbreak file signing from previous commit + +Commit f558e886050c4e98f6cdde391df679a411b3f62c essentially broke +file signing because signatures never get migrated into the package +header. This is what happens when you play around with too many +variants of the same thing and forget to test what ultimately got +committed, which was subtly different from anything else so far... :( +--- + lib/package.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/lib/package.c b/lib/package.c +index 8ee0e9e7c..db70d13f8 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -65,12 +65,16 @@ void headerMergeLegacySigs(Header h, Header sigh) + case RPMSIGTAG_PAYLOADSIZE: + td.tag = RPMTAG_ARCHIVESIZE; + break; ++ case RPMSIGTAG_FILESIGNATURES: ++ td.tag = RPMTAG_FILESIGNATURES; ++ break; ++ case RPMSIGTAG_FILESIGNATURELENGTH: ++ td.tag = RPMTAG_FILESIGNATURELENGTH; ++ break; + case RPMSIGTAG_SHA1: + case RPMSIGTAG_SHA256: + case RPMSIGTAG_DSA: + case RPMSIGTAG_RSA: +- case RPMSIGTAG_FILESIGNATURELENGTH: +- case RPMSIGTAG_FILESIGNATURES: + default: + if (!(td.tag >= HEADER_SIGBASE && td.tag < HEADER_TAGBASE)) + continue; +-- +2.13.5 + diff --git a/SOURCES/0007-Assume-failure-in-rpmSignFiles.patch b/SOURCES/0007-Assume-failure-in-rpmSignFiles.patch new file mode 100644 index 0000000..49bdb01 --- /dev/null +++ b/SOURCES/0007-Assume-failure-in-rpmSignFiles.patch @@ -0,0 +1,71 @@ +From 83d623cd684ae424cf0c804d7a141981f283f5c4 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 14:43:58 +0300 +Subject: [PATCH 07/33] Assume failure in rpmSignFiles() + +Doesn't make it any shorter yet, but makes more sense in the next steps. +Just refactoring. +--- + sign/rpmsignfiles.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c +index 2dcc50400..c1d227a07 100644 +--- a/sign/rpmsignfiles.c ++++ b/sign/rpmsignfiles.c +@@ -88,23 +88,24 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + uint32_t siglen; + const char *algoname; + const char *digest; +- char *signature; +- rpmRC rc = RPMRC_OK; ++ char *signature = NULL; ++ rpmRC rc = RPMRC_FAIL; + ++ rpmtdReset(&digests); + algo = headerGetNumber(h, RPMTAG_FILEDIGESTALGO); + if (!algo) { + /* use default algorithm */ + algo = PGPHASHALGO_MD5; + } else if (algo < 0 || algo >= ARRAY_SIZE(hash_algo_name)) { + rpmlog(RPMLOG_ERR, _("File digest algorithm id is invalid")); +- return RPMRC_FAIL; ++ goto exit; + } + + diglen = rpmDigestLength(algo); + algoname = hash_algo_name[algo]; + if (!algoname) { + rpmlog(RPMLOG_ERR, _("hash_algo_name failed\n")); +- return RPMRC_FAIL; ++ goto exit; + } + + headerDel(sigh, RPMTAG_FILESIGNATURELENGTH); +@@ -129,20 +130,19 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + signature = signFile(algoname, digest, diglen, key, keypass); + if (!signature) { + rpmlog(RPMLOG_ERR, _("signFile failed\n")); +- rc = RPMRC_FAIL; + goto exit; + } + td.data = &signature; + if (!headerPut(sigh, &td, HEADERPUT_APPEND)) { +- free(signature); + rpmlog(RPMLOG_ERR, _("headerPutString failed\n")); +- rc = RPMRC_FAIL; + goto exit; + } +- free(signature); ++ signature = _free(signature); + } ++ rc = RPMRC_OK; + + exit: ++ free(signature); + rpmtdFreeData(&digests); + return rc; + } +-- +2.13.5 + diff --git a/SOURCES/0008-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch b/SOURCES/0008-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch new file mode 100644 index 0000000..5a8bdca --- /dev/null +++ b/SOURCES/0008-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch @@ -0,0 +1,108 @@ +From 63cddf8526eeb54086e587aca0cec4bbe35bfb10 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 14:44:18 +0300 +Subject: [PATCH 08/33] Use rpm file info sets instead of header for retrieving + file data + +Simplifies the code a little, but more imporantly it avoids duplicating +code and special knowledge like the default digest algo and converting +hex to binary. As a side-effect, this fixes RPMTAG_FILESIGNATURELENGTH +inadvertly getting added into packages that have no files at all. +--- + sign/rpmsignfiles.c | 36 +++++++++++++++++------------------- + 1 file changed, 17 insertions(+), 19 deletions(-) + +diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c +index c1d227a07..de7a73cfd 100644 +--- a/sign/rpmsignfiles.c ++++ b/sign/rpmsignfiles.c +@@ -8,7 +8,7 @@ + #include "imaevm.h" + + #include /* rpmlog */ +-#include /* rnibble */ ++#include + #include /* rpmDigestLength */ + #include "lib/header.h" /* HEADERGET_MINMEM */ + #include "lib/rpmtypes.h" /* rpmRC */ +@@ -32,7 +32,7 @@ static const char *hash_algo_name[] = { + + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +-static char *signFile(const char *algo, const char *fdigest, int diglen, ++static char *signFile(const char *algo, const uint8_t *fdigest, int diglen, + const char *key, char *keypass) + { + char *fsignature; +@@ -40,15 +40,11 @@ const char *key, char *keypass) + unsigned char signature[MAX_SIGNATURE_LENGTH]; + int siglen; + +- /* convert file digest hex to binary */ +- memset(digest, 0, diglen); + /* some entries don't have a digest - we return an empty signature */ +- if (strlen(fdigest) != diglen * 2) ++ memset(digest, 0, diglen); ++ if (memcmp(digest, fdigest, diglen) == 0) + return strdup(""); + +- for (int i = 0; i < diglen; ++i, fdigest += 2) +- digest[i] = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]); +- + /* prepare file signature */ + memset(signature, 0, MAX_SIGNATURE_LENGTH); + signature[0] = '\x03'; +@@ -82,21 +78,23 @@ char *keypass) + + rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + { +- struct rpmtd_s digests, td; ++ struct rpmtd_s td; + int algo; + int diglen; + uint32_t siglen; + const char *algoname; +- const char *digest; ++ const uint8_t *digest; + char *signature = NULL; + rpmRC rc = RPMRC_FAIL; ++ rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY); ++ ++ if (rpmfiFC(fi) == 0) { ++ rc = RPMRC_OK; ++ goto exit; ++ } + +- rpmtdReset(&digests); +- algo = headerGetNumber(h, RPMTAG_FILEDIGESTALGO); +- if (!algo) { +- /* use default algorithm */ +- algo = PGPHASHALGO_MD5; +- } else if (algo < 0 || algo >= ARRAY_SIZE(hash_algo_name)) { ++ algo = rpmfiDigestAlgo(fi); ++ if (algo >= ARRAY_SIZE(hash_algo_name)) { + rpmlog(RPMLOG_ERR, _("File digest algorithm id is invalid")); + goto exit; + } +@@ -125,8 +123,8 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + td.data = NULL; /* set in the loop below */ + td.count = 1; + +- headerGet(h, RPMTAG_FILEDIGESTS, &digests, HEADERGET_MINMEM); +- while ((digest = rpmtdNextString(&digests))) { ++ while (rpmfiNext(fi) >= 0) { ++ digest = rpmfiFDigest(fi, NULL, NULL); + signature = signFile(algoname, digest, diglen, key, keypass); + if (!signature) { + rpmlog(RPMLOG_ERR, _("signFile failed\n")); +@@ -143,6 +141,6 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + + exit: + free(signature); +- rpmtdFreeData(&digests); ++ rpmfiFree(fi); + return rc; + } +-- +2.13.5 + diff --git a/SOURCES/0009-Eliminate-redundant-signature-length-calculation-fun.patch b/SOURCES/0009-Eliminate-redundant-signature-length-calculation-fun.patch new file mode 100644 index 0000000..05af112 --- /dev/null +++ b/SOURCES/0009-Eliminate-redundant-signature-length-calculation-fun.patch @@ -0,0 +1,105 @@ +From e6d91a00f0782f7551552d851f38c9ef188f7308 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 15:04:38 +0300 +Subject: [PATCH 09/33] Eliminate redundant signature length calculation + function + +The actual signing function knows the length already, we just need to +return it and then we can insert it if there was anything at all +to sign. +--- + sign/rpmsignfiles.c | 40 ++++++++++++++-------------------------- + 1 file changed, 14 insertions(+), 26 deletions(-) + +diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c +index de7a73cfd..9fe6e6d41 100644 +--- a/sign/rpmsignfiles.c ++++ b/sign/rpmsignfiles.c +@@ -33,7 +33,7 @@ static const char *hash_algo_name[] = { + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + + static char *signFile(const char *algo, const uint8_t *fdigest, int diglen, +-const char *key, char *keypass) ++const char *key, char *keypass, uint32_t *siglenp) + { + char *fsignature; + unsigned char digest[diglen]; +@@ -56,32 +56,18 @@ const char *key, char *keypass) + return NULL; + } + ++ *siglenp = siglen + 1; + /* convert file signature binary to hex */ + fsignature = pgpHexStr(signature, siglen+1); + return fsignature; + } + +-static uint32_t signatureLength(const char *algo, int diglen, const char *key, +-char *keypass) +-{ +- unsigned char digest[diglen]; +- unsigned char signature[MAX_SIGNATURE_LENGTH]; +- +- memset(digest, 0, diglen); +- memset(signature, 0, MAX_SIGNATURE_LENGTH); +- signature[0] = '\x03'; +- +- uint32_t siglen = sign_hash(algo, digest, diglen, key, keypass, +- signature+1); +- return siglen + 1; +-} +- + rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + { + struct rpmtd_s td; + int algo; + int diglen; +- uint32_t siglen; ++ uint32_t siglen = 0; + const char *algoname; + const uint8_t *digest; + char *signature = NULL; +@@ -108,14 +94,6 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + + headerDel(sigh, RPMTAG_FILESIGNATURELENGTH); + headerDel(sigh, RPMTAG_FILESIGNATURES); +- siglen = signatureLength(algoname, diglen, key, keypass); +- +- rpmtdReset(&td); +- td.tag = RPMSIGTAG_FILESIGNATURELENGTH; +- td.type = RPM_INT32_TYPE; +- td.data = &siglen; +- td.count = 1; +- headerPut(sigh, &td, HEADERPUT_DEFAULT); + + rpmtdReset(&td); + td.tag = RPMSIGTAG_FILESIGNATURES; +@@ -125,7 +103,7 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + + while (rpmfiNext(fi) >= 0) { + digest = rpmfiFDigest(fi, NULL, NULL); +- signature = signFile(algoname, digest, diglen, key, keypass); ++ signature = signFile(algoname, digest, diglen, key, keypass, &siglen); + if (!signature) { + rpmlog(RPMLOG_ERR, _("signFile failed\n")); + goto exit; +@@ -137,6 +115,16 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + } + signature = _free(signature); + } ++ ++ if (siglen > 0) { ++ rpmtdReset(&td); ++ td.tag = RPMSIGTAG_FILESIGNATURELENGTH; ++ td.type = RPM_INT32_TYPE; ++ td.data = &siglen; ++ td.count = 1; ++ headerPut(sigh, &td, HEADERPUT_DEFAULT); ++ } ++ + rc = RPMRC_OK; + + exit: +-- +2.13.5 + diff --git a/SOURCES/0010-Drop-redundant-check-on-hash-algo-name.patch b/SOURCES/0010-Drop-redundant-check-on-hash-algo-name.patch new file mode 100644 index 0000000..098e61b --- /dev/null +++ b/SOURCES/0010-Drop-redundant-check-on-hash-algo-name.patch @@ -0,0 +1,29 @@ +From 17643af15665beecfc2f37d9eb0832ec4bcc4f59 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 15:20:16 +0300 +Subject: [PATCH 10/33] Drop redundant check on hash algo name + +The array is already size-validated, me thinks we can safely +assume the array to be populated with non-NULL values. +--- + sign/rpmsignfiles.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c +index 9fe6e6d41..4876f66f2 100644 +--- a/sign/rpmsignfiles.c ++++ b/sign/rpmsignfiles.c +@@ -87,10 +87,6 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass) + + diglen = rpmDigestLength(algo); + algoname = hash_algo_name[algo]; +- if (!algoname) { +- rpmlog(RPMLOG_ERR, _("hash_algo_name failed\n")); +- goto exit; +- } + + headerDel(sigh, RPMTAG_FILESIGNATURELENGTH); + headerDel(sigh, RPMTAG_FILESIGNATURES); +-- +2.13.5 + diff --git a/SOURCES/0011-Drop-redundant-check-on-hash-algo-name.patch b/SOURCES/0011-Drop-redundant-check-on-hash-algo-name.patch new file mode 100644 index 0000000..56fe97e --- /dev/null +++ b/SOURCES/0011-Drop-redundant-check-on-hash-algo-name.patch @@ -0,0 +1,40 @@ +From 87d64d1a2bf734385f86a3a7f51897616966c418 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 15:20:16 +0300 +Subject: [PATCH 11/33] Drop redundant check on hash algo name + +The array is already size-validated, me thinks we can safely +assume the array to be populated with non-NULL values. +--- + lib/rpmtag.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 7a9092a6e..7b58e4c41 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -65,8 +65,8 @@ typedef enum rpmTag_e { + RPMTAG_LONGARCHIVESIZE = RPMTAG_SIG_BASE+15, /* l */ + /* RPMTAG_SIG_BASE+16 reserved */ + RPMTAG_SHA256HEADER = RPMTAG_SIG_BASE+17, /* s */ +- /* RPMTAG_SIG_BASE+18 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ +- /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURES */ ++ /* RPMTAG_SIG_BASE+18 reserved for RPMSIGTAG_FILESIGNATURES */ ++ /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ + + RPMTAG_NAME = 1000, /* s */ + #define RPMTAG_N RPMTAG_NAME /* s */ +@@ -428,8 +428,8 @@ typedef enum rpmSigTag_e { + RPMSIGTAG_LONGSIZE = RPMTAG_LONGSIGSIZE, /*!< internal Header+Payload size (64bit) in bytes. */ + RPMSIGTAG_LONGARCHIVESIZE = RPMTAG_LONGARCHIVESIZE, /*!< internal uncompressed payload size (64bit) in bytes. */ + RPMSIGTAG_SHA256 = RPMTAG_SHA256HEADER, +- RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 18, +- RPMSIGTAG_FILESIGNATURES = RPMTAG_SIG_BASE + 19, ++ RPMSIGTAG_FILESIGNATURES = RPMTAG_SIG_BASE + 18, ++ RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19, + } rpmSigTag; + + +-- +2.13.5 + diff --git a/SOURCES/0012-Generalize-file-signing-to-use-a-generic-flags-field.patch b/SOURCES/0012-Generalize-file-signing-to-use-a-generic-flags-field.patch new file mode 100644 index 0000000..760fbc3 --- /dev/null +++ b/SOURCES/0012-Generalize-file-signing-to-use-a-generic-flags-field.patch @@ -0,0 +1,129 @@ +From 1afb1b5f5711e70a0806d041c57d35ac2456d6de Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 2 Mar 2020 13:56:33 +0200 +Subject: [PATCH 12/33] Generalize file signing to use a generic flags field in + signing arguments + +There will be any number of signing flags in the future, and we don't +want to break the ABI for every single one of them by adding new +fields to the sign argument struct. Replace the signfiles field +with a bitfield in the common rpm style. No functional changes. + +This is an API change of course, but we'll have to bump the soname for +the next release anyway so might as well do it now. +--- + rpmsign.c | 11 ++++++----- + sign/rpmgensig.c | 8 ++++---- + sign/rpmsign.h | 8 +++++++- + 3 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/rpmsign.c b/rpmsign.c +index 1a5cd59c2..57cb36919 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -19,7 +19,7 @@ enum modes { + static int mode = MODE_NONE; + + #ifdef WITH_IMAEVM +-static int signfiles = 0, fskpass = 0; ++static int fskpass = 0; + static char * fileSigningKey = NULL; + #endif + +@@ -33,7 +33,8 @@ static struct poptOption signOptsTable[] = { + { "delsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELSIGN, + N_("delete package signatures"), NULL }, + #ifdef WITH_IMAEVM +- { "signfiles", '\0', POPT_ARG_NONE, &signfiles, 0, ++ { "signfiles", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), ++ &sargs.signflags, RPMSIGN_FLAG_IMA, + N_("sign package(s) files"), NULL}, + { "fskpath", '\0', POPT_ARG_STRING, &fileSigningKey, 0, + N_("use file signing key "), +@@ -107,7 +108,7 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + rpmPushMacro(NULL, "_file_signing_key", NULL, fileSigningKey, RMIL_GLOBAL); + } + +- if (signfiles) { ++ if (sargs->signflags & RPMSIGN_FLAG_IMA) { + char *fileSigningKeyPassword = NULL; + char *key = rpmExpand("%{?_file_signing_key}", NULL); + if (rstreq(key, "")) { +@@ -126,7 +127,7 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + free(fileSigningKeyPassword); + } + +- sargs->signfiles = 1; ++ sargs->signflags |= RPMSIGN_FLAG_IMA; + free(key); + } + #endif +@@ -163,7 +164,7 @@ int main(int argc, char *argv[]) + } + + #ifdef WITH_IMAEVM +- if (fileSigningKey && !signfiles) { ++ if (fileSigningKey && !(sargs.signflags & RPMSIGN_FLAG_IMA)) { + argerror(_("--fskpath may only be specified when signing files")); + } + #endif +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 875b96078..6ab8c23fa 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -465,10 +465,10 @@ static int checkPkg(FD_t fd, char **msg) + * Create/modify elements in signature header. + * @param rpm path to package + * @param deleting adding or deleting signature? +- * @param signfiles sign files if non-zero ++ * @param flags + * @return 0 on success, -1 on error + */ +-static int rpmSign(const char *rpm, int deleting, int signfiles) ++static int rpmSign(const char *rpm, int deleting, int flags) + { + FD_t fd = NULL; + FD_t ofd = NULL; +@@ -524,7 +524,7 @@ static int rpmSign(const char *rpm, int deleting, int signfiles) + unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES); + origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES); + +- if (signfiles) { ++ if (flags & RPMSIGN_FLAG_IMA) { + if (includeFileSignatures(&sigh, &h)) + goto exit; + } +@@ -675,7 +675,7 @@ int rpmPkgSign(const char *path, const struct rpmSignArgs * args) + } + } + +- rc = rpmSign(path, 0, args ? args->signfiles : 0); ++ rc = rpmSign(path, 0, args ? args->signflags : 0); + + if (args) { + if (args->hashalgo) { +diff --git a/sign/rpmsign.h b/sign/rpmsign.h +index bed8d6245..545e80d2d 100644 +--- a/sign/rpmsign.h ++++ b/sign/rpmsign.h +@@ -13,10 +13,16 @@ + extern "C" { + #endif + ++enum rpmSignFlags_e { ++ RPMSIGN_FLAG_NONE = 0, ++ RPMSIGN_FLAG_IMA = (1 << 0), ++}; ++typedef rpmFlags rpmSignFlags; ++ + struct rpmSignArgs { + char *keyid; + pgpHashAlgo hashalgo; +- int signfiles; ++ rpmSignFlags signflags; + /* ... what else? */ + }; + +-- +2.13.5 + diff --git a/SOURCES/0013-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch b/SOURCES/0013-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch new file mode 100644 index 0000000..b8ba4c1 --- /dev/null +++ b/SOURCES/0013-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch @@ -0,0 +1,201 @@ +From 4276d324bebf442299a90b5a9c4679829ab96385 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 2 Mar 2020 14:47:26 +0200 +Subject: [PATCH 13/33] Stop adding rpm v3 header+payload signatures by default + where not needed + +On packages where a separate payload digest exists (ie those built with +rpm >= 4.14), rpm v3 header+payload signatures are nothing but expensive +legacy baggage, as the payload digest will be signed by a header-only +signature already, without having to recalculate the entire file. + +Automatically detect the payload digest presence and only add V3 +signatures on packages that need it, but also add an override switch +to force their addition if needed for compatibility or so. A particular +use-case would be ability to signature-level verify the entire package +on rpm older than 4.14. + +Fixes: #863 +--- + doc/rpmsign.8 | 9 +++++++++ + rpmsign.c | 3 +++ + sign/rpmgensig.c | 24 +++++++++++++++++------- + sign/rpmsign.h | 1 + + tests/rpmsigdig.at | 36 ++++++++++++++++++++++++++++++++++-- + 5 files changed, 64 insertions(+), 9 deletions(-) + +diff --git a/doc/rpmsign.8 b/doc/rpmsign.8 +index d895a3b8c..f7ceae89b 100644 +--- a/doc/rpmsign.8 ++++ b/doc/rpmsign.8 +@@ -11,6 +11,7 @@ rpmsign \- RPM Package Signing + + .SS "rpmsign-options" + .PP ++[\fb--rpmv3\fR] + [\fb--fskpath \fIKEY\fb\fR] [\fB--signfiles\fR] + + .SH DESCRIPTION +@@ -32,6 +33,14 @@ Delete all signatures from each package \fIPACKAGE_FILE\fR given. + .SS "SIGN OPTIONS" + .PP + .TP ++\fB--rpmv3\fR ++Force RPM V3 header+payload signature addition. ++These are expensive and redundant baggage on packages where a separate ++payload digest exists (packages built with rpm >= 4.14). Rpm will ++automatically detect the need for V3 signatures, but this option can be ++used to force their creation if the packages must be fully ++signature verifiable with rpm < 4.14 or other interoperability reasons. ++.TP + \fB--fskpath \fIKEY\fB\fR + Used with \fB--signfiles\fR, use file signing key \fIKey\fR. + .TP +diff --git a/rpmsign.c b/rpmsign.c +index 57cb36919..a74948ba8 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -32,6 +32,9 @@ static struct poptOption signOptsTable[] = { + N_("sign package(s) (identical to --addsign)"), NULL }, + { "delsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELSIGN, + N_("delete package signatures"), NULL }, ++ { "rpmv3", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), ++ &sargs.signflags, RPMSIGN_FLAG_RPMV3, ++ N_("create rpm v3 header+payload signatures") }, + #ifdef WITH_IMAEVM + { "signfiles", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_IMA, +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 6ab8c23fa..0c6646d85 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -377,14 +377,17 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + + if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) + goto exit; +- rpmtdFree(sigtd); + +- /* Assume the same signature test holds for v3 signature too */ +- if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL) +- goto exit; ++ if (sigt_v3) { ++ rpmtdFree(sigtd); + +- if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) +- goto exit; ++ /* Assume the same signature test holds for v3 signature too */ ++ if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL) ++ goto exit; ++ ++ if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) ++ goto exit; ++ } + + rc = 0; + exit: +@@ -521,6 +524,12 @@ static int rpmSign(const char *rpm, int deleting, int flags) + goto exit; + } + ++ /* Always add V3 signatures if no payload digest present */ ++ if (!(headerIsEntry(h, RPMTAG_PAYLOADDIGEST) || ++ headerIsEntry(h, RPMTAG_PAYLOADDIGESTALT))) { ++ flags |= RPMSIGN_FLAG_RPMV3; ++ } ++ + unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES); + origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES); + +@@ -533,6 +542,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + deleteSigs(sigh); + } else { + /* Signature target containing header + payload */ ++ int v3 = (flags & RPMSIGN_FLAG_RPMV3); + sigt_v3.fd = fd; + sigt_v3.start = headerStart; + sigt_v3.fileName = rpm; +@@ -542,7 +552,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + sigt_v4 = sigt_v3; + sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES); + +- res = replaceSignature(sigh, &sigt_v3, &sigt_v4); ++ res = replaceSignature(sigh, v3 ? &sigt_v3 : NULL, &sigt_v4); + if (res != 0) { + if (res == 1) { + rpmlog(RPMLOG_WARNING, +diff --git a/sign/rpmsign.h b/sign/rpmsign.h +index 545e80d2d..7a770d879 100644 +--- a/sign/rpmsign.h ++++ b/sign/rpmsign.h +@@ -16,6 +16,7 @@ extern "C" { + enum rpmSignFlags_e { + RPMSIGN_FLAG_NONE = 0, + RPMSIGN_FLAG_IMA = (1 << 0), ++ RPMSIGN_FLAG_RPMV3 = (1 << 1), + }; + typedef rpmFlags rpmSignFlags; + +diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at +index f6ad72589..c6f95e997 100644 +--- a/tests/rpmsigdig.at ++++ b/tests/rpmsigdig.at +@@ -423,7 +423,7 @@ AT_CLEANUP + + # ------------------------------ + # Test --addsign +-AT_SETUP([rpmsign --addsign ]) ++AT_SETUP([rpmsign --addsign --rpmv3 ]) + AT_KEYWORDS([rpmsign signature]) + AT_CHECK([ + RPMDB_CLEAR +@@ -431,7 +431,7 @@ RPMDB_INIT + rm -rf "${TOPDIR}" + + cp "${RPMTEST}"/data/RPMS/hello-2.0-1.x86_64.rpm "${RPMTEST}"/tmp/ +-run rpmsign --key-id 1964C5FC --addsign "${RPMTEST}"/tmp/hello-2.0-1.x86_64.rpm > /dev/null ++run rpmsign --key-id 1964C5FC --rpmv3 --addsign "${RPMTEST}"/tmp/hello-2.0-1.x86_64.rpm > /dev/null + echo PRE-IMPORT + runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm|grep -v digest + echo POST-IMPORT +@@ -456,6 +456,38 @@ POST-DELSIGN + []) + AT_CLEANUP + ++# Test --addsign ++AT_SETUP([rpmsign --addsign ]) ++AT_KEYWORDS([rpmsign signature]) ++AT_CHECK([ ++RPMDB_CLEAR ++RPMDB_INIT ++rm -rf "${TOPDIR}" ++ ++cp "${RPMTEST}"/data/RPMS/hello-2.0-1.x86_64.rpm "${RPMTEST}"/tmp/ ++run rpmsign --key-id 1964C5FC --addsign "${RPMTEST}"/tmp/hello-2.0-1.x86_64.rpm > /dev/null ++echo PRE-IMPORT ++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm|grep -v digest ++echo POST-IMPORT ++runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub ++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm|grep -v digest ++run rpmsign --delsign "${RPMTEST}"/tmp/hello-2.0-1.x86_64.rpm > /dev/null ++echo POST-DELSIGN ++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64.rpm|grep -v digest ++], ++[0], ++[PRE-IMPORT ++/tmp/hello-2.0-1.x86_64.rpm: ++ Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY ++POST-IMPORT ++/tmp/hello-2.0-1.x86_64.rpm: ++ Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK ++POST-DELSIGN ++/tmp/hello-2.0-1.x86_64.rpm: ++], ++[]) ++AT_CLEANUP ++ + # ------------------------------ + # Test --delsign + AT_SETUP([rpmsign --delsign ]) +-- +2.13.5 + diff --git a/SOURCES/0014-Drop-support-for-dmalloc.patch b/SOURCES/0014-Drop-support-for-dmalloc.patch new file mode 100644 index 0000000..a754a7b --- /dev/null +++ b/SOURCES/0014-Drop-support-for-dmalloc.patch @@ -0,0 +1,48 @@ +From ce27d2d35d5a0dfe89525c1cf3dd19df5f276032 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 27 Mar 2020 15:09:25 +0200 +Subject: [PATCH 14/33] Drop support for dmalloc + +Last dmalloc release is from 2007, and these days there are plenty of +other, maintained tools for debugging memory issues. +--- + configure.ac | 7 ------- + debug.h | 4 ---- + 2 files changed, 11 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 2dddf53e6..57a3eb299 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1022,13 +1022,6 @@ AS_IF([test "$enable_plugins" != no],[ + ]) + AM_CONDITIONAL(IMA, [test "x$ac_cv_func_lsetxattr" = xyes]) + +-with_dmalloc=no +-AC_ARG_WITH(dmalloc, [AS_HELP_STRING([--with-dmalloc],[build with dmalloc debugging support])]) +-if test "$with_dmalloc" = yes ; then +- AC_DEFINE(DMALLOC, 1, [Build with dmalloc support?]) +- LIBS="$LIBS -ldmalloc" +-fi +- + user_with_uid0=$(awk -F: '$3==0 {print $1;exit}' /etc/passwd) + group_with_gid0=$(awk -F: '$3==0 {print $1;exit}' /etc/group) + AC_DEFINE_UNQUOTED([UID_0_USER],["$user_with_uid0"],[Get the user name having userid 0]) +diff --git a/debug.h b/debug.h +index 3d34ea010..db7ea1df9 100644 +--- a/debug.h ++++ b/debug.h +@@ -6,10 +6,6 @@ + + #include + +-#ifdef DMALLOC +-#include +-#endif +- + #define RPMDBG_TOSTR(a) RPMDBG_TOSTR_ARG(a) + #define RPMDBG_TOSTR_ARG(a) #a + +-- +2.13.5 + diff --git a/SOURCES/0015-Fix-bump-up-the-limit-of-signature-header-to-64MB.patch b/SOURCES/0015-Fix-bump-up-the-limit-of-signature-header-to-64MB.patch new file mode 100644 index 0000000..7e5f320 --- /dev/null +++ b/SOURCES/0015-Fix-bump-up-the-limit-of-signature-header-to-64MB.patch @@ -0,0 +1,61 @@ +From 90c0aac93590ff2c6d38e4c43f055e5a777d82c6 Mon Sep 17 00:00:00 2001 +From: Hongxu Jia +Date: Wed, 3 Jun 2020 10:25:24 +0800 +Subject: [PATCH 15/33] Fix: bump up the limit of signature header to 64MB + +Since commits [Place file signatures into the signature header where they +belong][1] applied, run `rpm -Kv **.rpm' failed if signature header +is larger than 64KB. Here are steps: + +1) A unsigned rpm package, the size is 227560 bytes +$ ls -al xz-src-5.2.5-r0.corei7_64.rpm +-rw-------. 1 mockbuild 1000 227560 Jun 3 09:59 + +2) Sign the rpm package +$ rpmsign --addsign ... xz-src-5.2.5-r0.corei7_64.rpm + +3) The size of signed rpm is 312208 bytes +$ ls -al xz-src-5.2.5-r0.corei7_64.rpm +-rw-------. 1 mockbuild 1000 312208 Jun 3 09:48 + +4) Run `rpm -Kv' failed with signature hdr data out of range +$ rpm -Kv xz-src-5.2.5-r0.corei7_64.rpm +xz-src-5.2.5-r0.corei7_64.rpm: +error: xz-src-5.2.5-r0.corei7_64.rpm: signature hdr data: BAD, no. of +bytes(88864) out of range + +From 1) and 3), the size of signed rpm package increased +312208 - 227560 = 84648, so the check of dl_max (64KB,65536) +is not enough. + +As [1] said: + + This also means the signature header can be MUCH bigger than ever + before,so bump up the limit (to 64MB, arbitrary something for now) + +So [1] missed to multiply by 1024. + +[1] https://github.com/rpm-software-management/rpm/commit/f558e886050c4e98f6cdde391df679a411b3f62c + +Signed-off-by: Hongxu Jia +(cherry picked from commit 486579912381ede82172dc6d0ff3941a6d0536b5) +--- + lib/header.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/header.c b/lib/header.c +index ab96da309..a12439a40 100644 +--- a/lib/header.c ++++ b/lib/header.c +@@ -1910,7 +1910,7 @@ rpmRC hdrblobRead(FD_t fd, int magic, int exact_size, rpmTagVal regionTag, hdrbl + + if (regionTag == RPMTAG_HEADERSIGNATURES) { + il_max = 32; +- dl_max = 64 * 1024; ++ dl_max = 64 * 1024 * 1024; + } + + memset(block, 0, sizeof(block)); +-- +2.13.5 + diff --git a/SOURCES/0016-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch b/SOURCES/0016-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch new file mode 100644 index 0000000..ebba8e3 --- /dev/null +++ b/SOURCES/0016-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch @@ -0,0 +1,28 @@ +From 649c63140362f18103711520a99942654d49a28f Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 11:22:15 -0400 +Subject: [PATCH 16/33] rpmsign: RPMSIGN_FLAG_IMA is already set + +There is no need to set RPMSIGN_FLAG_IMA since it was already set to +get here. + +Signed-off-by: Jes Sorensen +--- + rpmsign.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/rpmsign.c b/rpmsign.c +index a74948ba8..e1d207da5 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -130,7 +130,6 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + free(fileSigningKeyPassword); + } + +- sargs->signflags |= RPMSIGN_FLAG_IMA; + free(key); + } + #endif +-- +2.13.5 + diff --git a/SOURCES/0017-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch b/SOURCES/0017-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch new file mode 100644 index 0000000..7730479 --- /dev/null +++ b/SOURCES/0017-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch @@ -0,0 +1,149 @@ +From b24880763a3c5ea9c9943f6c4554a9faf4cf2f6f Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 27 Mar 2020 18:31:36 -0400 +Subject: [PATCH 17/33] Add basic autoconf and framework for fsverity support + +Use the same signing key argument as is used for IMA file signing. + +Signed-off-by: Jes Sorensen +--- + Makefile.am | 1 + + configure.ac | 19 +++++++++++++++++++ + rpmsign.c | 20 ++++++++++++++------ + sign/Makefile.am | 5 +++++ + sign/rpmsign.h | 1 + + 5 files changed, 40 insertions(+), 6 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 1f20f05b7..2a1dfed7b 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -16,6 +16,7 @@ DISTCHECK_CONFIGURE_FLAGS = \ + --with-selinux \ + --with-imaevm \ + --with-crypto=openssl \ ++ --with-fsverity \ + --disable-dependency-tracking + + include $(top_srcdir)/rpm.am +diff --git a/configure.ac b/configure.ac +index 57a3eb299..699d8de13 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -919,6 +919,25 @@ fi + AM_CONDITIONAL(WITH_IMAEVM,[test "$with_imaevm" = yes]) + AC_SUBST(WITH_IMAEVM_LIB) + ++# fsverity ++AC_ARG_WITH([fsverity], [AS_HELP_STRING([--with-fsverity],[build with fsverity support])],[],[with_fsverity=no]) ++if test "$with_fsverity" = yes ; then ++ AC_MSG_CHECKING([libfsverity]) ++ AC_COMPILE_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[#include ]], ++ [[return libfsverity_sign_digest(NULL, NULL, NULL, NULL);]] ++ )], ++ [AC_MSG_RESULT(yes) ++ AC_DEFINE(WITH_FSVERITY, 1, [Build with fsverity support?]) ++ WITH_FSVERITY_LIB="-lfsverity" ++ ], ++ [AC_MSG_ERROR([--with-fsverity given, libfsverity or libfsverity.h missing])] ++ ) ++fi ++AM_CONDITIONAL(WITH_FSVERITY,[test "$with_fsverity" = yes]) ++AC_SUBST(WITH_FSVERITY_LIB) ++ + # libcap + WITH_CAP_LIB= + AC_ARG_WITH(cap, [AS_HELP_STRING([--with-cap],[build with capability support])], +diff --git a/rpmsign.c b/rpmsign.c +index e1d207da5..8861c2c59 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -18,7 +18,7 @@ enum modes { + + static int mode = MODE_NONE; + +-#ifdef WITH_IMAEVM ++#if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) + static int fskpass = 0; + static char * fileSigningKey = NULL; + #endif +@@ -39,6 +39,13 @@ static struct poptOption signOptsTable[] = { + { "signfiles", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_IMA, + N_("sign package(s) files"), NULL}, ++#endif ++#ifdef WITH_FSVERITY ++ { "signverity", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), ++ &sargs.signflags, RPMSIGN_FLAG_FSVERITY, ++ N_("generate fsverity signatures for package(s) files"), NULL}, ++#endif ++#if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) + { "fskpath", '\0', POPT_ARG_STRING, &fileSigningKey, 0, + N_("use file signing key "), + N_("") }, +@@ -59,7 +66,7 @@ static struct poptOption optionsTable[] = { + POPT_TABLEEND + }; + +-#ifdef WITH_IMAEVM ++#if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) + static char *get_fskpass(void) + { + struct termios flags, tmp_flags; +@@ -106,12 +113,12 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + goto exit; + } + +-#ifdef WITH_IMAEVM ++#if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) + if (fileSigningKey) { + rpmPushMacro(NULL, "_file_signing_key", NULL, fileSigningKey, RMIL_GLOBAL); + } + +- if (sargs->signflags & RPMSIGN_FLAG_IMA) { ++ if (sargs->signflags & (RPMSIGN_FLAG_IMA | RPMSIGN_FLAG_FSVERITY)) { + char *fileSigningKeyPassword = NULL; + char *key = rpmExpand("%{?_file_signing_key}", NULL); + if (rstreq(key, "")) { +@@ -165,8 +172,9 @@ int main(int argc, char *argv[]) + argerror(_("no arguments given")); + } + +-#ifdef WITH_IMAEVM +- if (fileSigningKey && !(sargs.signflags & RPMSIGN_FLAG_IMA)) { ++#if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) ++ if (fileSigningKey && ++ !(sargs.signflags & (RPMSIGN_FLAG_IMA | RPMSIGN_FLAG_FSVERITY))) { + argerror(_("--fskpath may only be specified when signing files")); + } + #endif +diff --git a/sign/Makefile.am b/sign/Makefile.am +index db774de0e..8d372915a 100644 +--- a/sign/Makefile.am ++++ b/sign/Makefile.am +@@ -24,3 +24,8 @@ if WITH_IMAEVM + librpmsign_la_SOURCES += rpmsignfiles.c rpmsignfiles.h + librpmsign_la_LIBADD += @WITH_IMAEVM_LIB@ + endif ++ ++if WITH_FSVERITY ++librpmsign_la_SOURCES += rpmsignverity.c rpmsignverity.h ++librpmsign_la_LIBADD += @WITH_FSVERITY_LIB@ ++endif +diff --git a/sign/rpmsign.h b/sign/rpmsign.h +index 7a770d879..2b8a10a1a 100644 +--- a/sign/rpmsign.h ++++ b/sign/rpmsign.h +@@ -17,6 +17,7 @@ enum rpmSignFlags_e { + RPMSIGN_FLAG_NONE = 0, + RPMSIGN_FLAG_IMA = (1 << 0), + RPMSIGN_FLAG_RPMV3 = (1 << 1), ++ RPMSIGN_FLAG_FSVERITY = (1 << 2), + }; + typedef rpmFlags rpmSignFlags; + +-- +2.13.5 + diff --git a/SOURCES/0018-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch b/SOURCES/0018-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch new file mode 100644 index 0000000..09f182a --- /dev/null +++ b/SOURCES/0018-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch @@ -0,0 +1,51 @@ +From 929cdd919cdd67bbc57794b3a57276a4a4f40630 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Wed, 27 May 2020 16:49:03 -0400 +Subject: [PATCH 18/33] rpmsign: Add helper to indicate file signing enabled + +Helper function returning true if either IMA or VERITY signatures are +to be applied. This simplifies the code and makes it easier to read. + +Signed-off-by: Jes Sorensen +--- + rpmsign.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/rpmsign.c b/rpmsign.c +index 8861c2c59..94cbf1d1a 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -67,6 +67,11 @@ static struct poptOption optionsTable[] = { + }; + + #if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) ++static int flags_sign_files(int flags) ++{ ++ return (flags & (RPMSIGN_FLAG_IMA | RPMSIGN_FLAG_FSVERITY) ? 1 : 0); ++} ++ + static char *get_fskpass(void) + { + struct termios flags, tmp_flags; +@@ -118,7 +123,7 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + rpmPushMacro(NULL, "_file_signing_key", NULL, fileSigningKey, RMIL_GLOBAL); + } + +- if (sargs->signflags & (RPMSIGN_FLAG_IMA | RPMSIGN_FLAG_FSVERITY)) { ++ if (flags_sign_files(sargs->signflags)) { + char *fileSigningKeyPassword = NULL; + char *key = rpmExpand("%{?_file_signing_key}", NULL); + if (rstreq(key, "")) { +@@ -173,8 +178,7 @@ int main(int argc, char *argv[]) + } + + #if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) +- if (fileSigningKey && +- !(sargs.signflags & (RPMSIGN_FLAG_IMA | RPMSIGN_FLAG_FSVERITY))) { ++ if (fileSigningKey && !(flags_sign_files(sargs.signflags))) { + argerror(_("--fskpath may only be specified when signing files")); + } + #endif +-- +2.13.5 + diff --git a/SOURCES/0019-rpmsign-Handle-certpath-for-signing-certificate.patch b/SOURCES/0019-rpmsign-Handle-certpath-for-signing-certificate.patch new file mode 100644 index 0000000..edbbbd2 --- /dev/null +++ b/SOURCES/0019-rpmsign-Handle-certpath-for-signing-certificate.patch @@ -0,0 +1,52 @@ +From 2d349e915aa7a26d5d41c3dcdea597195ed26948 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 3 Apr 2020 16:26:06 -0400 +Subject: [PATCH 19/33] rpmsign: Handle --certpath for signing certificate + +fsverirty needs a certificate for signing, in addition to the signing key. + +Signed-off-by: Jes Sorensen +--- + rpmsign.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/rpmsign.c b/rpmsign.c +index 94cbf1d1a..074dd8b13 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -22,6 +22,9 @@ static int mode = MODE_NONE; + static int fskpass = 0; + static char * fileSigningKey = NULL; + #endif ++#ifdef WITH_FSVERITY ++static char * fileSigningCert = NULL; ++#endif + + static struct rpmSignArgs sargs = {NULL, 0, 0}; + +@@ -44,6 +47,9 @@ static struct poptOption signOptsTable[] = { + { "signverity", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_FSVERITY, + N_("generate fsverity signatures for package(s) files"), NULL}, ++ { "certpath", '\0', POPT_ARG_STRING, &fileSigningCert, 0, ++ N_("use file signing cert "), ++ N_("") }, + #endif + #if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) + { "fskpath", '\0', POPT_ARG_STRING, &fileSigningKey, 0, +@@ -123,6 +129,12 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + rpmPushMacro(NULL, "_file_signing_key", NULL, fileSigningKey, RMIL_GLOBAL); + } + ++#ifdef WITH_FSVERITY ++ if (fileSigningCert) { ++ rpmPushMacro(NULL, "_file_signing_cert", NULL, fileSigningCert, RMIL_GLOBAL); ++ } ++#endif ++ + if (flags_sign_files(sargs->signflags)) { + char *fileSigningKeyPassword = NULL; + char *key = rpmExpand("%{?_file_signing_key}", NULL); +-- +2.13.5 + diff --git a/SOURCES/0020-Implement-rpmSignVerity.patch b/SOURCES/0020-Implement-rpmSignVerity.patch new file mode 100644 index 0000000..6110e58 --- /dev/null +++ b/SOURCES/0020-Implement-rpmSignVerity.patch @@ -0,0 +1,243 @@ +From 77bfd446e345fb65b9d1695116a82eb37cfba640 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 3 Apr 2020 16:38:08 -0400 +Subject: [PATCH 20/33] Implement rpmSignVerity() + +This generates the root Merkle tree hash and signs it using the +specified key and certificate. + +Signed-off-by: Jes Sorensen +--- + sign/rpmgensig.c | 36 +++++++++++++++ + sign/rpmsignverity.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ + sign/rpmsignverity.h | 29 ++++++++++++ + 3 files changed, 186 insertions(+) + create mode 100644 sign/rpmsignverity.c + create mode 100644 sign/rpmsignverity.h + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 0c6646d85..78da1347b 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -22,6 +22,7 @@ + #include "lib/signature.h" + #include "lib/rpmvs.h" + #include "sign/rpmsignfiles.h" ++#include "sign/rpmsignverity.h" + + #include "debug.h" + +@@ -439,6 +440,36 @@ static rpmRC includeFileSignatures(Header *sigp, Header *hdrp) + #endif + } + ++static rpmRC includeVeritySignatures(FD_t fd, Header *sigp, Header *hdrp) ++{ ++#ifdef WITH_FSVERITY ++ rpmRC rc; ++ char *key = rpmExpand("%{?_file_signing_key}", NULL); ++ char *keypass = rpmExpand("%{?_file_signing_key_password}", NULL); ++ char *cert = rpmExpand("%{?_file_signing_cert}", NULL); ++ ++ if (rstreq(keypass, "")) { ++ free(keypass); ++ keypass = NULL; ++ } ++ ++ if (key && cert) { ++ rc = rpmSignVerity(fd, *sigp, *hdrp, key, keypass, cert); ++ } else { ++ rpmlog(RPMLOG_ERR, _("fsverity signatures requires a key and a cert\n")); ++ rc = RPMRC_FAIL; ++ } ++ ++ free(keypass); ++ free(key); ++ free(cert); ++ return rc; ++#else ++ rpmlog(RPMLOG_ERR, _("fsverity signing support not built in\n")); ++ return RPMRC_FAIL; ++#endif ++} ++ + static int msgCb(struct rpmsinfo_s *sinfo, void *cbdata) + { + char **msg = cbdata; +@@ -538,6 +569,11 @@ static int rpmSign(const char *rpm, int deleting, int flags) + goto exit; + } + ++ if (flags & RPMSIGN_FLAG_FSVERITY) { ++ if (includeVeritySignatures(fd, &sigh, &h)) ++ goto exit; ++ } ++ + if (deleting) { /* Nuke all the signature tags. */ + deleteSigs(sigh); + } else { +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +new file mode 100644 +index 000000000..5346c3bc8 +--- /dev/null ++++ b/sign/rpmsignverity.c +@@ -0,0 +1,121 @@ ++/** ++ * Copyright (C) 2020 Facebook ++ * ++ * Author: Jes Sorensen ++ */ ++ ++#include "system.h" ++ ++#include /* RPMSIGTAG & related */ ++#include /* rpmlog */ ++#include ++#include /* rpmDigestLength */ ++#include "lib/header.h" /* HEADERGET_MINMEM */ ++#include "lib/header_internal.h" ++#include "lib/rpmtypes.h" /* rpmRC */ ++#include ++#include "rpmio/rpmio_internal.h" ++#include "lib/rpmvs.h" ++ ++#include "sign/rpmsignverity.h" ++ ++#define MAX_SIGNATURE_LENGTH 1024 ++ ++static int rpmVerityRead(void *opaque, void *buf, size_t size) ++{ ++ int retval; ++ rpmfi fi = (rpmfi)opaque; ++ ++ retval = rpmfiArchiveRead(fi, buf, size); ++ ++ if (retval > 0) ++ retval = 0; ++ return retval; ++} ++ ++rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, ++ char *keypass, char *cert) ++{ ++ int rc, status; ++ FD_t gzdi; ++ rpmfiles files = NULL; ++ rpmfi fi = NULL; ++ rpmts ts = rpmtsCreate(); ++ struct libfsverity_digest *digest = NULL; ++ struct libfsverity_merkle_tree_params params; ++ struct libfsverity_signature_params sig_params; ++ rpm_loff_t file_size; ++ off_t offset = Ftell(fd); ++ const char *compr; ++ char *rpmio_flags = NULL; ++ char *digest_hex; ++ uint8_t *sig; ++ size_t sig_size; ++ ++ Fseek(fd, 0, SEEK_SET); ++ rpmtsSetVSFlags(ts, RPMVSF_MASK_NODIGESTS | RPMVSF_MASK_NOSIGNATURES | ++ RPMVSF_NOHDRCHK); ++ rc = rpmReadPackageFile(ts, fd, "fsverity", &h); ++ if (rc != RPMRC_OK) { ++ rpmlog(RPMLOG_DEBUG, _("%s: rpmReadPackageFile returned %i\n"), ++ __func__, rc); ++ goto out; ++ } ++ ++ rpmlog(RPMLOG_DEBUG, _("key: %s\n"), key); ++ rpmlog(RPMLOG_DEBUG, _("cert: %s\n"), cert); ++ ++ compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); ++ rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); ++ ++ gzdi = Fdopen(fdDup(Fileno(fd)), rpmio_flags); ++ free(rpmio_flags); ++ ++ files = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY); ++ fi = rpmfiNewArchiveReader(gzdi, files, ++ RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS); ++ ++ while (rpmfiNext(fi) >= 0) { ++ if (!S_ISREG(rpmfiFMode(fi))) ++ continue; ++ file_size = rpmfiFSize(fi); ++ rpmlog(RPMLOG_DEBUG, _("file: %s, (size %li)\n"), ++ rpmfiFN(fi), file_size); ++ ++ memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params)); ++ params.version = 1; ++ params.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; ++ params.block_size = sysconf(_SC_PAGESIZE); ++ params.salt_size = 0 /* salt_size */; ++ params.salt = NULL /* salt */; ++ params.file_size = file_size; ++ status = libfsverity_compute_digest(fi, rpmVerityRead, ++ ¶ms, &digest); ++ if (!status) { ++ digest_hex = pgpHexStr(digest->digest, digest->digest_size); ++ rpmlog(RPMLOG_DEBUG, _("digest(%i): %s\n"), ++ digest->digest_size, digest_hex); ++ free(digest_hex); ++ } ++ memset(&sig_params, 0, sizeof(struct libfsverity_signature_params)); ++ sig_params.keyfile = key; ++ sig_params.certfile = cert; ++ if (libfsverity_sign_digest(digest, &sig_params, &sig, &sig_size)) { ++ rpmlog(RPMLOG_DEBUG, _("failed to sign digest\n")); ++ rc = RPMRC_FAIL; ++ goto out; ++ } ++ rpmlog(RPMLOG_DEBUG, _("digest signing success\n")); ++ ++ free(digest); ++ free(sig); ++ } ++ ++out: ++ Fseek(fd, offset, SEEK_SET); ++ ++ rpmfilesFree(files); ++ rpmfiFree(fi); ++ rpmtsFree(ts); ++ return rc; ++} +diff --git a/sign/rpmsignverity.h b/sign/rpmsignverity.h +new file mode 100644 +index 000000000..f3ad3bb18 +--- /dev/null ++++ b/sign/rpmsignverity.h +@@ -0,0 +1,29 @@ ++#ifndef H_RPMSIGNVERITY ++#define H_RPMSIGNVERITY ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * Sign file digests in header into signature header ++ * @param fd file descriptor of RPM ++ * @param sigh package signature header ++ * @param h package header ++ * @param key signing key ++ * @param keypass signing key password ++ * @param cert signing cert ++ * @return RPMRC_OK on success ++ */ ++RPM_GNUC_INTERNAL ++rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, ++ char *keypass, char *cert); ++ ++#ifdef _cplusplus ++} ++#endif ++ ++#endif /* H_RPMSIGNVERITY */ +-- +2.13.5 + diff --git a/SOURCES/0021-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch b/SOURCES/0021-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch new file mode 100644 index 0000000..0e14f8a --- /dev/null +++ b/SOURCES/0021-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch @@ -0,0 +1,95 @@ +From 02083dd42f21c17b7ebd8d1fb49c01d9c01b7c2b Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Thu, 28 May 2020 17:48:23 -0400 +Subject: [PATCH 21/33] Introduce base2bin() - a helper to convert tag array of + base64 strings + +This will convert a tag of base64 strings to a binary array, similar +to how hex2bin() works. It supports variable sized strings, and will +determine the maximum string length and build the output array based +on that. + +Signed-off-by: Jes Sorensen +--- + lib/rpmfi.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/lib/rpmfi.c b/lib/rpmfi.c +index 689ead2c5..8c69d3e40 100644 +--- a/lib/rpmfi.c ++++ b/lib/rpmfi.c +@@ -19,6 +19,7 @@ + #include "lib/fsm.h" /* rpmpsm stuff for now */ + #include "lib/rpmug.h" + #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ ++#include "rpmio/rpmbase64.h" + + #include "debug.h" + +@@ -1520,6 +1521,63 @@ static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len) + return bin; + } + ++/* ++ * Convert a tag of base64 strings to binary presentation. ++ * This handles variable length strings by finding the longest string ++ * before building the output array. Dummy strings in the tag should be ++ * added as '\0' ++ */ ++static uint8_t *base2bin(Header h, rpmTagVal tag, rpm_count_t num, int *len) ++{ ++ struct rpmtd_s td; ++ uint8_t *bin = NULL, *t = NULL; ++ size_t maxlen = 0; ++ int status, i= 0; ++ void **arr = xmalloc(num * sizeof(void *)); ++ size_t *lengths = xcalloc(num, sizeof(size_t)); ++ const char *s; ++ ++ if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) != num) ++ goto out; ++ ++ while ((s = rpmtdNextString(&td))) { ++ /* Insert a dummy entry for empty strings */ ++ if (*s == '\0') { ++ arr[i++] = NULL; ++ continue; ++ } ++ status = rpmBase64Decode(s, &arr[i], &lengths[i]); ++ if (lengths[i] > maxlen) ++ maxlen = lengths[i]; ++ if (status) { ++ rpmlog(RPMLOG_DEBUG, _("%s: base64 decode failed, len %li\n"), ++ __func__, lengths[i]); ++ goto out; ++ } ++ i++; ++ } ++ ++ if (maxlen) { ++ rpmlog(RPMLOG_DEBUG, _("%s: base64 decode success, len %li\n"), ++ __func__, maxlen); ++ ++ t = bin = xcalloc(num, maxlen); ++ ++ for (i = 0; i < num; i++) { ++ memcpy(t, arr[i], lengths[i]); ++ free(arr[i]); ++ t += maxlen; ++ } ++ *len = maxlen; ++ } ++ out: ++ free(arr); ++ free(lengths); ++ rpmtdFreeData(&td); ++ ++ return bin; ++} ++ + static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags) + { + headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ? +-- +2.13.5 + diff --git a/SOURCES/0022-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch b/SOURCES/0022-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch new file mode 100644 index 0000000..644596c --- /dev/null +++ b/SOURCES/0022-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch @@ -0,0 +1,210 @@ +From e0a5f4e8875daa9317387015f8e04cb7f0673472 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Thu, 9 Apr 2020 12:58:17 -0400 +Subject: [PATCH 22/33] rpmsignverity: Add verity signature headers to the + package + +This adds the array of verity signatures, and a signature length +header. We use 4K block for the Merkle tree, and rely on the kernel +doing the right thing. + +Signed-off-by: Jes Sorensen +--- + lib/rpmtag.h | 6 ++- + sign/rpmsignverity.c | 112 ++++++++++++++++++++++++++++++++++----------------- + sign/rpmsignverity.h | 7 ++++ + 3 files changed, 87 insertions(+), 38 deletions(-) + +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 7b58e4c41..f27786c3e 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -67,6 +67,7 @@ typedef enum rpmTag_e { + RPMTAG_SHA256HEADER = RPMTAG_SIG_BASE+17, /* s */ + /* RPMTAG_SIG_BASE+18 reserved for RPMSIGTAG_FILESIGNATURES */ + /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ ++ RPMTAG_VERITYSIGNATURES = RPMTAG_SIG_BASE+20, /* s[] */ + + RPMTAG_NAME = 1000, /* s */ + #define RPMTAG_N RPMTAG_NAME /* s */ +@@ -428,8 +429,9 @@ typedef enum rpmSigTag_e { + RPMSIGTAG_LONGSIZE = RPMTAG_LONGSIGSIZE, /*!< internal Header+Payload size (64bit) in bytes. */ + RPMSIGTAG_LONGARCHIVESIZE = RPMTAG_LONGARCHIVESIZE, /*!< internal uncompressed payload size (64bit) in bytes. */ + RPMSIGTAG_SHA256 = RPMTAG_SHA256HEADER, +- RPMSIGTAG_FILESIGNATURES = RPMTAG_SIG_BASE + 18, +- RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19, ++ RPMSIGTAG_FILESIGNATURES = RPMTAG_SIG_BASE + 18, ++ RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19, ++ RPMSIGTAG_VERITYSIGNATURES = RPMTAG_VERITYSIGNATURES, + } rpmSigTag; + + +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index 5346c3bc8..a9818bd08 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -33,23 +33,66 @@ static int rpmVerityRead(void *opaque, void *buf, size_t size) + return retval; + } + ++static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, ++ char *keypass, char *cert) ++{ ++ struct libfsverity_merkle_tree_params params; ++ struct libfsverity_signature_params sig_params; ++ struct libfsverity_digest *digest = NULL; ++ rpm_loff_t file_size; ++ char *digest_hex, *sig_hex = NULL; ++ uint8_t *sig; ++ int status; ++ ++ file_size = rpmfiFSize(fi); ++ ++ memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params)); ++ params.version = 1; ++ params.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; ++ params.block_size = RPM_FSVERITY_BLKSZ; ++ params.salt_size = 0 /* salt_size */; ++ params.salt = NULL /* salt */; ++ params.file_size = file_size; ++ status = libfsverity_compute_digest(fi, rpmVerityRead, ¶ms, &digest); ++ if (status) { ++ rpmlog(RPMLOG_DEBUG, _("failed to compute digest\n")); ++ goto out; ++ } ++ ++ digest_hex = pgpHexStr(digest->digest, digest->digest_size); ++ rpmlog(RPMLOG_DEBUG, _("digest(%i): %s\n"), ++ digest->digest_size, digest_hex); ++ free(digest_hex); ++ ++ memset(&sig_params, 0, sizeof(struct libfsverity_signature_params)); ++ sig_params.keyfile = key; ++ sig_params.certfile = cert; ++ if (libfsverity_sign_digest(digest, &sig_params, &sig, sig_size)) { ++ rpmlog(RPMLOG_DEBUG, _("failed to sign digest\n")); ++ goto out; ++ } ++ ++ sig_hex = pgpHexStr(sig, *sig_size + 1); ++ out: ++ free(digest); ++ free(sig); ++ return sig_hex; ++} ++ + rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + char *keypass, char *cert) + { +- int rc, status; ++ int rc; + FD_t gzdi; + rpmfiles files = NULL; + rpmfi fi = NULL; + rpmts ts = rpmtsCreate(); +- struct libfsverity_digest *digest = NULL; +- struct libfsverity_merkle_tree_params params; +- struct libfsverity_signature_params sig_params; ++ struct rpmtd_s td; + rpm_loff_t file_size; + off_t offset = Ftell(fd); + const char *compr; + char *rpmio_flags = NULL; +- char *digest_hex; +- uint8_t *sig; ++ char *sig_hex; + size_t sig_size; + + Fseek(fd, 0, SEEK_SET); +@@ -75,43 +118,40 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + fi = rpmfiNewArchiveReader(gzdi, files, + RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS); + ++ /* ++ * Should this be sigh from the cloned fd or the sigh we received? ++ */ ++ headerDel(sigh, RPMSIGTAG_VERITYSIGNATURES); ++ ++ rpmtdReset(&td); ++ td.tag = RPMSIGTAG_VERITYSIGNATURES; ++ td.type = RPM_STRING_ARRAY_TYPE; ++ td.count = 1; ++ + while (rpmfiNext(fi) >= 0) { +- if (!S_ISREG(rpmfiFMode(fi))) +- continue; + file_size = rpmfiFSize(fi); +- rpmlog(RPMLOG_DEBUG, _("file: %s, (size %li)\n"), +- rpmfiFN(fi), file_size); +- +- memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params)); +- params.version = 1; +- params.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; +- params.block_size = sysconf(_SC_PAGESIZE); +- params.salt_size = 0 /* salt_size */; +- params.salt = NULL /* salt */; +- params.file_size = file_size; +- status = libfsverity_compute_digest(fi, rpmVerityRead, +- ¶ms, &digest); +- if (!status) { +- digest_hex = pgpHexStr(digest->digest, digest->digest_size); +- rpmlog(RPMLOG_DEBUG, _("digest(%i): %s\n"), +- digest->digest_size, digest_hex); +- free(digest_hex); +- } +- memset(&sig_params, 0, sizeof(struct libfsverity_signature_params)); +- sig_params.keyfile = key; +- sig_params.certfile = cert; +- if (libfsverity_sign_digest(digest, &sig_params, &sig, &sig_size)) { +- rpmlog(RPMLOG_DEBUG, _("failed to sign digest\n")); ++ ++ rpmlog(RPMLOG_DEBUG, _("file: %s, (size %li, link %s, idx %i)\n"), ++ rpmfiFN(fi), file_size, rpmfiFLink(fi), rpmfiFX(fi)); ++ ++ sig_hex = rpmVeritySignFile(fi, &sig_size, key, keypass, cert); ++ td.data = &sig_hex; ++ rpmlog(RPMLOG_DEBUG, _("digest signed, len: %li\n"), sig_size); ++#if 0 ++ rpmlog(RPMLOG_DEBUG, _("digest: %s\n"), (char *)sig_hex); ++#endif ++ if (!headerPut(sigh, &td, HEADERPUT_APPEND)) { ++ rpmlog(RPMLOG_ERR, _("headerPutString failed\n")); + rc = RPMRC_FAIL; + goto out; + } +- rpmlog(RPMLOG_DEBUG, _("digest signing success\n")); +- +- free(digest); +- free(sig); ++ free(sig_hex); + } + +-out: ++ rpmlog(RPMLOG_DEBUG, _("sigh size: %i\n"), headerSizeof(sigh, 0)); ++ ++ rc = RPMRC_OK; ++ out: + Fseek(fd, offset, SEEK_SET); + + rpmfilesFree(files); +diff --git a/sign/rpmsignverity.h b/sign/rpmsignverity.h +index f3ad3bb18..69bbaf7f7 100644 +--- a/sign/rpmsignverity.h ++++ b/sign/rpmsignverity.h +@@ -8,6 +8,13 @@ + extern "C" { + #endif + ++/* ++ * Block size used to generate the Merkle tree for fsverity. For now ++ * we only support 4K blocks, if we ever decide to support different ++ * block sizes, we will need a tag to indicate this. ++ */ ++#define RPM_FSVERITY_BLKSZ 4096 ++ + /** + * Sign file digests in header into signature header + * @param fd file descriptor of RPM +-- +2.13.5 + diff --git a/SOURCES/0023-rpmSignVerity-Generate-signatures-for-files-not-pres.patch b/SOURCES/0023-rpmSignVerity-Generate-signatures-for-files-not-pres.patch new file mode 100644 index 0000000..2fda498 --- /dev/null +++ b/SOURCES/0023-rpmSignVerity-Generate-signatures-for-files-not-pres.patch @@ -0,0 +1,162 @@ +From 1c4485ea80e5c2d7292c837dbc8728fc34875928 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 13 Apr 2020 18:14:15 -0400 +Subject: [PATCH 23/33] rpmSignVerity: Generate signatures for files not + present in archive + +This generates signatures for all files in the archive, then picks up +ghost files from the header metadata and generates signatures for them +as well. It finally submits them to RPMTAG_VERITYSIGNATURES in header +file order as we cannot rely on archive order and header order being +the same. + +Signed-off-by: Jes Sorensen +--- + lib/package.c | 1 + + sign/rpmsignverity.c | 55 ++++++++++++++++++++++++++++++++++++++++------------ + 2 files changed, 44 insertions(+), 12 deletions(-) + +diff --git a/lib/package.c b/lib/package.c +index db70d13f8..fec0ccfaf 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -71,6 +71,7 @@ void headerMergeLegacySigs(Header h, Header sigh) + case RPMSIGTAG_FILESIGNATURELENGTH: + td.tag = RPMTAG_FILESIGNATURELENGTH; + break; ++ case RPMSIGTAG_VERITYSIGNATURES: + case RPMSIGTAG_SHA1: + case RPMSIGTAG_SHA256: + case RPMSIGTAG_DSA: +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index a9818bd08..3bb23a18d 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -41,7 +41,7 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + struct libfsverity_digest *digest = NULL; + rpm_loff_t file_size; + char *digest_hex, *sig_hex = NULL; +- uint8_t *sig; ++ uint8_t *sig = NULL; + int status; + + file_size = rpmfiFSize(fi); +@@ -72,7 +72,7 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + goto out; + } + +- sig_hex = pgpHexStr(sig, *sig_size + 1); ++ sig_hex = pgpHexStr(sig, *sig_size); + out: + free(digest); + free(sig); +@@ -86,6 +86,7 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + FD_t gzdi; + rpmfiles files = NULL; + rpmfi fi = NULL; ++ rpmfi hfi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY); + rpmts ts = rpmtsCreate(); + struct rpmtd_s td; + rpm_loff_t file_size; +@@ -93,11 +94,14 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + const char *compr; + char *rpmio_flags = NULL; + char *sig_hex; ++ char **signatures = NULL; + size_t sig_size; ++ int nr_files, idx; + + Fseek(fd, 0, SEEK_SET); + rpmtsSetVSFlags(ts, RPMVSF_MASK_NODIGESTS | RPMVSF_MASK_NOSIGNATURES | + RPMVSF_NOHDRCHK); ++ + rc = rpmReadPackageFile(ts, fd, "fsverity", &h); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_DEBUG, _("%s: rpmReadPackageFile returned %i\n"), +@@ -113,6 +117,8 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + + gzdi = Fdopen(fdDup(Fileno(fd)), rpmio_flags); + free(rpmio_flags); ++ if (!gzdi) ++ rpmlog(RPMLOG_DEBUG, _("Fdopen() failed\n")); + + files = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY); + fi = rpmfiNewArchiveReader(gzdi, files, +@@ -123,39 +129,64 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + */ + headerDel(sigh, RPMSIGTAG_VERITYSIGNATURES); + +- rpmtdReset(&td); +- td.tag = RPMSIGTAG_VERITYSIGNATURES; +- td.type = RPM_STRING_ARRAY_TYPE; +- td.count = 1; ++ /* ++ * The payload doesn't include special files, like ghost files, and ++ * we cannot rely on the file order in the payload to match that of ++ * the header. Instead we allocate an array of pointers and populate ++ * it as we go along. Then walk the header fi and account for the ++ * special files. Last we walk the array and populate the header. ++ */ ++ nr_files = rpmfiFC(hfi); ++ signatures = xcalloc(nr_files, sizeof(char *)); ++ ++ rpmlog(RPMLOG_DEBUG, _("file count - header: %i, payload %i\n"), ++ nr_files, rpmfiFC(fi)); + + while (rpmfiNext(fi) >= 0) { + file_size = rpmfiFSize(fi); ++ idx = rpmfiFX(fi); + + rpmlog(RPMLOG_DEBUG, _("file: %s, (size %li, link %s, idx %i)\n"), + rpmfiFN(fi), file_size, rpmfiFLink(fi), rpmfiFX(fi)); + +- sig_hex = rpmVeritySignFile(fi, &sig_size, key, keypass, cert); ++ signatures[idx] = rpmVeritySignFile(fi, &sig_size, key, keypass, cert); ++ } ++ ++ while (rpmfiNext(hfi) >= 0) { ++ idx = rpmfiFX(hfi); ++ if (signatures[idx]) ++ continue; ++ signatures[idx] = rpmVeritySignFile(hfi, &sig_size, key, keypass, cert); ++ } ++ ++ rpmtdReset(&td); ++ td.tag = RPMSIGTAG_VERITYSIGNATURES; ++ td.type = RPM_STRING_ARRAY_TYPE; ++ td.count = 1; ++ for (idx = 0; idx < nr_files; idx++) { ++ sig_hex = signatures[idx]; + td.data = &sig_hex; +- rpmlog(RPMLOG_DEBUG, _("digest signed, len: %li\n"), sig_size); +-#if 0 +- rpmlog(RPMLOG_DEBUG, _("digest: %s\n"), (char *)sig_hex); +-#endif + if (!headerPut(sigh, &td, HEADERPUT_APPEND)) { + rpmlog(RPMLOG_ERR, _("headerPutString failed\n")); + rc = RPMRC_FAIL; + goto out; + } +- free(sig_hex); ++ rpmlog(RPMLOG_DEBUG, _("signature: %s\n"), signatures[idx]); ++ rpmlog(RPMLOG_DEBUG, _("digest signed, len: %li\n"), sig_size); ++ free(signatures[idx]); ++ signatures[idx] = NULL; + } + + rpmlog(RPMLOG_DEBUG, _("sigh size: %i\n"), headerSizeof(sigh, 0)); + + rc = RPMRC_OK; + out: ++ signatures = _free(signatures); + Fseek(fd, offset, SEEK_SET); + + rpmfilesFree(files); + rpmfiFree(fi); ++ rpmfiFree(hfi); + rpmtsFree(ts); + return rc; + } +-- +2.13.5 + diff --git a/SOURCES/0024-Process-verity-tag-on-package-read.patch b/SOURCES/0024-Process-verity-tag-on-package-read.patch new file mode 100644 index 0000000..7d6540f --- /dev/null +++ b/SOURCES/0024-Process-verity-tag-on-package-read.patch @@ -0,0 +1,189 @@ +From 72b2875ca72ecf9ff3618126618cd59a94fd87df Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 13 Apr 2020 18:21:36 -0400 +Subject: [PATCH 24/33] Process verity tag on package read + +This processes verity signature tags on package read, and provides +accessor functions to access them. + +Signed-off-by: Jes Sorensen +--- + lib/rpmfi.c | 27 +++++++++++++++++++++++++++ + lib/rpmfi.h | 8 ++++++++ + lib/rpmfiles.h | 10 ++++++++++ + sign/rpmsignverity.c | 20 ++++++++++++++++---- + 4 files changed, 61 insertions(+), 4 deletions(-) + +diff --git a/lib/rpmfi.c b/lib/rpmfi.c +index 8c69d3e40..5fdbe02a2 100644 +--- a/lib/rpmfi.c ++++ b/lib/rpmfi.c +@@ -116,8 +116,10 @@ struct rpmfiles_s { + + int digestalgo; /*!< File digest algorithm */ + int signaturelength; /*!< File signature length */ ++ int veritysiglength; /*!< Verity signature length */ + unsigned char * digests; /*!< File digests in binary. */ + unsigned char * signatures; /*!< File signatures in binary. */ ++ unsigned char * veritysigs; /*!< Verity signatures in binary. */ + + struct nlinkHash_s * nlinks;/*!< Files connected by hardlinks */ + rpm_off_t * replacedSizes; /*!< (TR_ADDED) */ +@@ -582,6 +584,19 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len) + return signature; + } + ++const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len) ++{ ++ const unsigned char *vsignature = NULL; ++ ++ if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { ++ if (fi->veritysigs != NULL) ++ vsignature = fi->veritysigs + (fi->veritysiglength * ix); ++ if (len) ++ *len = fi->veritysiglength; ++ } ++ return vsignature; ++} ++ + const char * rpmfilesFLink(rpmfiles fi, int ix) + { + const char * flink = NULL; +@@ -1268,6 +1283,7 @@ rpmfiles rpmfilesFree(rpmfiles fi) + fi->flangs = _free(fi->flangs); + fi->digests = _free(fi->digests); + fi->signatures = _free(fi->signatures); ++ fi->veritysigs = _free(fi->veritysigs); + fi->fcaps = _free(fi->fcaps); + + fi->cdict = _free(fi->cdict); +@@ -1649,6 +1665,12 @@ static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags) + totalfc, fi->signaturelength); + } + ++ fi->veritysigs = NULL; ++ if (!(flags & RPMFI_NOVERITYSIGNATURES)) { ++ fi->veritysigs = base2bin(h, RPMTAG_VERITYSIGNATURES, ++ totalfc, &fi->veritysiglength); ++ } ++ + /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */ + if (!(flags & RPMFI_NOFILEMTIMES)) + _hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes); +@@ -1939,6 +1961,11 @@ const unsigned char * rpmfiFSignature(rpmfi fi, size_t *len) + return rpmfilesFSignature(fi->files, fi ? fi->i : -1, len); + } + ++const unsigned char * rpmfiVSignature(rpmfi fi, size_t *len) ++{ ++ return rpmfilesVSignature(fi->files, fi ? fi->i : -1, len); ++} ++ + uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp) + { + return rpmfilesFDepends(fi->files, fi ? fi->i : -1, fddictp); +diff --git a/lib/rpmfi.h b/lib/rpmfi.h +index 6ef70cd28..fcb9d3acd 100644 +--- a/lib/rpmfi.h ++++ b/lib/rpmfi.h +@@ -191,6 +191,14 @@ char * rpmfiFDigestHex(rpmfi fi, int *algo); + const unsigned char * rpmfiFSignature(rpmfi fi, size_t *siglen); + + /** \ingroup rpmfi ++ * Return current verity (binary) signature of file info set iterator. ++ * @param fi file info set iterator ++ * @retval siglen signature length (pass NULL to ignore) ++ * @return current verity signature, NULL on invalid ++ */ ++const unsigned char * rpmfiVSignature(rpmfi fi, size_t *siglen); ++ ++/** \ingroup rpmfi + * Return current file linkto (i.e. symlink(2) target) from file info set iterator. + * @param fi file info set iterator + * @return current file linkto, NULL on invalid +diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h +index daf572cf4..81b3d01a1 100644 +--- a/lib/rpmfiles.h ++++ b/lib/rpmfiles.h +@@ -119,6 +119,7 @@ enum rpmfiFlags_e { + RPMFI_NOFILEVERIFYFLAGS = (1 << 16), + RPMFI_NOFILEFLAGS = (1 << 17), + RPMFI_NOFILESIGNATURES = (1 << 18), ++ RPMFI_NOVERITYSIGNATURES = (1 << 19), + }; + + typedef rpmFlags rpmfiFlags; +@@ -443,6 +444,15 @@ const unsigned char * rpmfilesFDigest(rpmfiles fi, int ix, int *algo, size_t *le + const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len); + + /** \ingroup rpmfiles ++ * Return file verity signature (binary) ++ * @param fi file info set ++ * @param ix file index ++ * @retval len signature length (pass NULL to ignore) ++ * @return verity signature, NULL on invalid ++ */ ++const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len); ++ ++/** \ingroup rpmfiles + * Return file rdev from file info set. + * @param fi file info set + * @param ix file index +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index 3bb23a18d..177561957 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -15,6 +15,7 @@ + #include "lib/rpmtypes.h" /* rpmRC */ + #include + #include "rpmio/rpmio_internal.h" ++#include "rpmio/rpmbase64.h" + #include "lib/rpmvs.h" + + #include "sign/rpmsignverity.h" +@@ -40,7 +41,7 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + struct libfsverity_signature_params sig_params; + struct libfsverity_digest *digest = NULL; + rpm_loff_t file_size; +- char *digest_hex, *sig_hex = NULL; ++ char *digest_hex, *digest_base64, *sig_base64 = NULL, *sig_hex = NULL; + uint8_t *sig = NULL; + int status; + +@@ -60,8 +61,14 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + } + + digest_hex = pgpHexStr(digest->digest, digest->digest_size); +- rpmlog(RPMLOG_DEBUG, _("digest(%i): %s\n"), +- digest->digest_size, digest_hex); ++ digest_base64 = rpmBase64Encode(digest->digest, digest->digest_size, -1); ++ rpmlog(RPMLOG_DEBUG, _("file(size %li): %s: digest(%i): %s, idx %i\n"), ++ file_size, rpmfiFN(fi), digest->digest_size, digest_hex, ++ rpmfiFX(fi)); ++ rpmlog(RPMLOG_DEBUG, _("file(size %li): %s: digest sz (%i): base64 sz (%li), %s, idx %i\n"), ++ file_size, rpmfiFN(fi), digest->digest_size, strlen(digest_base64), ++ digest_base64, rpmfiFX(fi)); ++ + free(digest_hex); + + memset(&sig_params, 0, sizeof(struct libfsverity_signature_params)); +@@ -73,10 +80,15 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + } + + sig_hex = pgpHexStr(sig, *sig_size); ++ sig_base64 = rpmBase64Encode(sig, *sig_size, -1); ++ rpmlog(RPMLOG_DEBUG, _("%s: sig_size(%li), base64_size(%li), idx %i: signature:\n%s\n"), ++ rpmfiFN(fi), *sig_size, strlen(sig_base64), rpmfiFX(fi), sig_hex); + out: ++ free(sig_hex); ++ + free(digest); + free(sig); +- return sig_hex; ++ return sig_base64; + } + + rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, +-- +2.13.5 + diff --git a/SOURCES/0025-Generate-a-zero-length-signature-for-symlinks.patch b/SOURCES/0025-Generate-a-zero-length-signature-for-symlinks.patch new file mode 100644 index 0000000..bea6282 --- /dev/null +++ b/SOURCES/0025-Generate-a-zero-length-signature-for-symlinks.patch @@ -0,0 +1,33 @@ +From 813e39af6ee4abea3bacb5c9f5af85757e52e73f Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Tue, 14 Apr 2020 10:33:32 -0400 +Subject: [PATCH 25/33] Generate a zero-length signature for symlinks + +The fsverity utility follows the symlink when generating a signature. +Since we don't want to sign the same file twice, we need to skip these +links, and instead just generate a dummy zero-length signature here. + +Signed-off-by: Jes Sorensen +--- + sign/rpmsignverity.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index 177561957..2c7d21620 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -45,7 +45,10 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + uint8_t *sig = NULL; + int status; + +- file_size = rpmfiFSize(fi); ++ if (S_ISLNK(rpmfiFMode(fi))) ++ file_size = 0; ++ else ++ file_size = rpmfiFSize(fi); + + memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params)); + params.version = 1; +-- +2.13.5 + diff --git a/SOURCES/0026-rpmsignverity.c-Clean-up-debug-logging.patch b/SOURCES/0026-rpmsignverity.c-Clean-up-debug-logging.patch new file mode 100644 index 0000000..fc55ae2 --- /dev/null +++ b/SOURCES/0026-rpmsignverity.c-Clean-up-debug-logging.patch @@ -0,0 +1,40 @@ +From e529459f2da13e33e112b9420dcf76f711179d6e Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Tue, 14 Apr 2020 12:08:09 -0400 +Subject: [PATCH 26/33] rpmsignverity.c: Clean up debug logging + +Put most logging in one place and avoid printing the same info twice. + +Signed-off-by: Jes Sorensen +--- + sign/rpmsignverity.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index 2c7d21620..445e1197c 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -104,7 +104,6 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + rpmfi hfi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY); + rpmts ts = rpmtsCreate(); + struct rpmtd_s td; +- rpm_loff_t file_size; + off_t offset = Ftell(fd); + const char *compr; + char *rpmio_flags = NULL; +@@ -158,12 +157,8 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + nr_files, rpmfiFC(fi)); + + while (rpmfiNext(fi) >= 0) { +- file_size = rpmfiFSize(fi); + idx = rpmfiFX(fi); + +- rpmlog(RPMLOG_DEBUG, _("file: %s, (size %li, link %s, idx %i)\n"), +- rpmfiFN(fi), file_size, rpmfiFLink(fi), rpmfiFX(fi)); +- + signatures[idx] = rpmVeritySignFile(fi, &sig_size, key, keypass, cert); + } + +-- +2.13.5 + diff --git a/SOURCES/0027-fsverity-add-tag-for-fsverity-algorithm.patch b/SOURCES/0027-fsverity-add-tag-for-fsverity-algorithm.patch new file mode 100644 index 0000000..cac014b --- /dev/null +++ b/SOURCES/0027-fsverity-add-tag-for-fsverity-algorithm.patch @@ -0,0 +1,161 @@ +From 21b537cea09636c81b19727676ed9c5e6e0421a0 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 13:40:26 -0400 +Subject: [PATCH 27/33] fsverity - add tag for fsverity algorithm + +The default algorith is SHA256, but fsverity allows for other +algorithms, so add a tag to handle this. + +Signed-off-by: Jes Sorensen +--- + lib/package.c | 1 + + lib/rpmfi.c | 2 ++ + lib/rpmtag.h | 2 ++ + sign/rpmsignverity.c | 32 ++++++++++++++++++++++++++++---- + 4 files changed, 33 insertions(+), 4 deletions(-) + +diff --git a/lib/package.c b/lib/package.c +index fec0ccfaf..c74184854 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -72,6 +72,7 @@ void headerMergeLegacySigs(Header h, Header sigh) + td.tag = RPMTAG_FILESIGNATURELENGTH; + break; + case RPMSIGTAG_VERITYSIGNATURES: ++ case RPMSIGTAG_VERITYSIGNATUREALGO: + case RPMSIGTAG_SHA1: + case RPMSIGTAG_SHA256: + case RPMSIGTAG_DSA: +diff --git a/lib/rpmfi.c b/lib/rpmfi.c +index 5fdbe02a2..70f05f509 100644 +--- a/lib/rpmfi.c ++++ b/lib/rpmfi.c +@@ -117,6 +117,7 @@ struct rpmfiles_s { + int digestalgo; /*!< File digest algorithm */ + int signaturelength; /*!< File signature length */ + int veritysiglength; /*!< Verity signature length */ ++ uint16_t verityalgo; /*!< Verity algorithm */ + unsigned char * digests; /*!< File digests in binary. */ + unsigned char * signatures; /*!< File signatures in binary. */ + unsigned char * veritysigs; /*!< Verity signatures in binary. */ +@@ -1667,6 +1668,7 @@ static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags) + + fi->veritysigs = NULL; + if (!(flags & RPMFI_NOVERITYSIGNATURES)) { ++ fi->verityalgo = headerGetNumber(h, RPMTAG_VERITYSIGNATUREALGO); + fi->veritysigs = base2bin(h, RPMTAG_VERITYSIGNATURES, + totalfc, &fi->veritysiglength); + } +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index f27786c3e..67b7f5a37 100644 +--- a/lib/rpmtag.h ++++ b/lib/rpmtag.h +@@ -68,6 +68,7 @@ typedef enum rpmTag_e { + /* RPMTAG_SIG_BASE+18 reserved for RPMSIGTAG_FILESIGNATURES */ + /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ + RPMTAG_VERITYSIGNATURES = RPMTAG_SIG_BASE+20, /* s[] */ ++ RPMTAG_VERITYSIGNATUREALGO = RPMTAG_SIG_BASE+21, /* i */ + + RPMTAG_NAME = 1000, /* s */ + #define RPMTAG_N RPMTAG_NAME /* s */ +@@ -432,6 +433,7 @@ typedef enum rpmSigTag_e { + RPMSIGTAG_FILESIGNATURES = RPMTAG_SIG_BASE + 18, + RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19, + RPMSIGTAG_VERITYSIGNATURES = RPMTAG_VERITYSIGNATURES, ++ RPMSIGTAG_VERITYSIGNATUREALGO = RPMTAG_VERITYSIGNATUREALGO, + } rpmSigTag; + + +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index 445e1197c..55096e732 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -35,7 +35,7 @@ static int rpmVerityRead(void *opaque, void *buf, size_t size) + } + + static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, +- char *keypass, char *cert) ++ char *keypass, char *cert, uint16_t algo) + { + struct libfsverity_merkle_tree_params params; + struct libfsverity_signature_params sig_params; +@@ -52,7 +52,7 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + + memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params)); + params.version = 1; +- params.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; ++ params.hash_algorithm = algo; + params.block_size = RPM_FSVERITY_BLKSZ; + params.salt_size = 0 /* salt_size */; + params.salt = NULL /* salt */; +@@ -111,6 +111,8 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + char **signatures = NULL; + size_t sig_size; + int nr_files, idx; ++ uint16_t algo; ++ uint32_t algo32; + + Fseek(fd, 0, SEEK_SET); + rpmtsSetVSFlags(ts, RPMVSF_MASK_NODIGESTS | RPMVSF_MASK_NOSIGNATURES | +@@ -142,6 +144,7 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + * Should this be sigh from the cloned fd or the sigh we received? + */ + headerDel(sigh, RPMSIGTAG_VERITYSIGNATURES); ++ headerDel(sigh, RPMSIGTAG_VERITYSIGNATUREALGO); + + /* + * The payload doesn't include special files, like ghost files, and +@@ -153,20 +156,24 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + nr_files = rpmfiFC(hfi); + signatures = xcalloc(nr_files, sizeof(char *)); + ++ algo = FS_VERITY_HASH_ALG_SHA256; ++ + rpmlog(RPMLOG_DEBUG, _("file count - header: %i, payload %i\n"), + nr_files, rpmfiFC(fi)); + + while (rpmfiNext(fi) >= 0) { + idx = rpmfiFX(fi); + +- signatures[idx] = rpmVeritySignFile(fi, &sig_size, key, keypass, cert); ++ signatures[idx] = rpmVeritySignFile(fi, &sig_size, key, keypass, cert, ++ algo); + } + + while (rpmfiNext(hfi) >= 0) { + idx = rpmfiFX(hfi); + if (signatures[idx]) + continue; +- signatures[idx] = rpmVeritySignFile(hfi, &sig_size, key, keypass, cert); ++ signatures[idx] = rpmVeritySignFile(hfi, &sig_size, key, keypass, cert, ++ algo); + } + + rpmtdReset(&td); +@@ -187,6 +194,23 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + signatures[idx] = NULL; + } + ++ if (sig_size == 0) { ++ rpmlog(RPMLOG_ERR, _("Zero length fsverity signature\n")); ++ rc = RPMRC_FAIL; ++ goto out; ++ } ++ ++ rpmtdReset(&td); ++ ++ /* RPM doesn't like new 16 bit types, so use a 32 bit tag */ ++ algo32 = algo; ++ rpmtdReset(&td); ++ td.tag = RPMSIGTAG_VERITYSIGNATUREALGO; ++ td.type = RPM_INT32_TYPE; ++ td.data = &algo32; ++ td.count = 1; ++ headerPut(sigh, &td, HEADERPUT_DEFAULT); ++ + rpmlog(RPMLOG_DEBUG, _("sigh size: %i\n"), headerSizeof(sigh, 0)); + + rc = RPMRC_OK; +-- +2.13.5 + diff --git a/SOURCES/0028-plugins-fsverity-Install-fsverity-signatures.patch b/SOURCES/0028-plugins-fsverity-Install-fsverity-signatures.patch new file mode 100644 index 0000000..d0502e2 --- /dev/null +++ b/SOURCES/0028-plugins-fsverity-Install-fsverity-signatures.patch @@ -0,0 +1,256 @@ +From 77e0c68c383904c390e6d99bc747dcfc20a1521c Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 11:11:25 -0400 +Subject: [PATCH 28/33] plugins/fsverity: Install fsverity signatures + +This plugin installs fsverity signatures for regular files, when a signature +is found in the RPM. It tries to enable them unconditionally, but fails +gracefully if fsverity isn't supported or enabled. + +Signed-off-by: Jes Sorensen +--- + configure.ac | 5 ++ + macros.in | 4 ++ + plugins/Makefile.am | 6 ++ + plugins/fsverity.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 192 insertions(+) + create mode 100644 plugins/fsverity.c + +diff --git a/configure.ac b/configure.ac +index 699d8de13..242cb8804 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1041,6 +1041,11 @@ AS_IF([test "$enable_plugins" != no],[ + ]) + AM_CONDITIONAL(IMA, [test "x$ac_cv_func_lsetxattr" = xyes]) + ++AS_IF([test "$enable_plugins" != no],[ ++AC_CHECK_HEADERS([linux/fsverity.h],[FSVERITY_IOCTL="yes"]) ++]) ++AM_CONDITIONAL(FSVERITY_IOCTL,[test "x$FSVERITY_IOCTL" = xyes]) ++ + user_with_uid0=$(awk -F: '$3==0 {print $1;exit}' /etc/passwd) + group_with_gid0=$(awk -F: '$3==0 {print $1;exit}' /etc/group) + AC_DEFINE_UNQUOTED([UID_0_USER],["$user_with_uid0"],[Get the user name having userid 0]) +diff --git a/macros.in b/macros.in +index 8dcb26a9b..317f91d96 100644 +--- a/macros.in ++++ b/macros.in +@@ -767,6 +767,9 @@ package or when debugging this package.\ + # performance for rotational disks) + #%_flush_io 0 + ++# Set to 1 to have fsverity signatures written for %config files. ++#%_fsverity_sign_config_files 0 ++ + # + # Default output format string for rpm -qa + # +@@ -1184,6 +1187,7 @@ package or when debugging this package.\ + %__transaction_selinux %{__plugindir}/selinux.so + %__transaction_syslog %{__plugindir}/syslog.so + %__transaction_ima %{__plugindir}/ima.so ++%__transaction_fsverity %{__plugindir}/fsverity.so + %__transaction_prioreset %{__plugindir}/prioreset.so + + #------------------------------------------------------------------------------ +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index ab4eee34f..22285be97 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -42,3 +42,9 @@ ima_la_sources = ima.c + ima_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la + plugins_LTLIBRARIES += ima.la + endif ++ ++if FSVERITY_IOCTL ++fsverity_la_sources = fsverity.c ++fsverity_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la ++plugins_LTLIBRARIES += fsverity.la ++endif +diff --git a/plugins/fsverity.c b/plugins/fsverity.c +new file mode 100644 +index 000000000..15ddcf33e +--- /dev/null ++++ b/plugins/fsverity.c +@@ -0,0 +1,177 @@ ++/** ++ * Copyright (C) 2020 Facebook ++ * ++ * Author: Jes Sorensen ++ */ ++ ++#include "system.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/rpmfs.h" ++#include "lib/rpmplugin.h" ++#include "lib/rpmte_internal.h" ++ ++#include "sign/rpmsignverity.h" ++ ++static int sign_config_files = 0; ++ ++/* ++ * This unconditionally tries to apply the fsverity signature to a file, ++ * but fails gracefully if the file system doesn't support it or the ++ * verity feature flag isn't enabled in the file system (ext4). ++ */ ++static rpmRC fsverity_fsm_file_prepare(rpmPlugin plugin, rpmfi fi, ++ const char *path, const char *dest, ++ mode_t file_mode, rpmFsmOp op) ++{ ++ struct fsverity_enable_arg arg; ++ const unsigned char * signature = NULL; ++ size_t len; ++ int rc = RPMRC_OK; ++ int fd; ++ rpmFileAction action = XFO_ACTION(op); ++ char *buffer; ++ ++ /* Ignore skipped files and unowned directories */ ++ if (XFA_SKIPPING(action) || (op & FAF_UNOWNED)) { ++ rpmlog(RPMLOG_DEBUG, "fsverity skipping early: path %s dest %s\n", ++ path, dest); ++ goto exit; ++ } ++ ++ /* ++ * Do not install signatures for config files unless the ++ * user explicitly asks for it. ++ */ ++ if (rpmfiFFlags(fi) & RPMFILE_CONFIG) { ++ if (!(rpmfiFMode(fi) & (S_IXUSR|S_IXGRP|S_IXOTH)) && ++ !sign_config_files) { ++ rpmlog(RPMLOG_DEBUG, "fsverity skipping: path %s dest %s\n", ++ path, dest); ++ ++ goto exit; ++ } ++ } ++ ++ /* ++ * Right now fsverity doesn't deal with symlinks or directories, so do ++ * not try to install signatures for non regular files. ++ */ ++ if (!S_ISREG(rpmfiFMode(fi))) { ++ rpmlog(RPMLOG_DEBUG, "fsverity skipping non regular: path %s dest %s\n", ++ path, dest); ++ goto exit; ++ } ++ ++ signature = rpmfiVSignature(fi, &len); ++ if (!signature || !len) { ++ rpmlog(RPMLOG_DEBUG, "fsverity no signature for: path %s dest %s\n", ++ path, dest); ++ goto exit; ++ } ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.version = 1; ++ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; ++ arg.block_size = RPM_FSVERITY_BLKSZ; ++ arg.sig_ptr = (uintptr_t)signature; ++ arg.sig_size = len; ++ ++ buffer = pgpHexStr(signature, arg.sig_size); ++ rpmlog(RPMLOG_DEBUG, "applying signature: %s\n", buffer); ++ free(buffer); ++ ++ fd = open(path, O_RDONLY); ++ if (fd < 0) { ++ rpmlog(RPMLOG_ERR, "failed to open path %s\n", path); ++ goto exit; ++ } ++ ++ /* ++ * Enable fsverity on the file. ++ * fsverity not supported by file system (ENOTTY) and fsverity not ++ * enabled on file system are expected and not considered ++ * errors. Every other non-zero error code will result in the ++ * installation failing. ++ */ ++ if (ioctl(fd, FS_IOC_ENABLE_VERITY, &arg) != 0) { ++ switch(errno) { ++ case EBADMSG: ++ rpmlog(RPMLOG_DEBUG, "invalid or malformed fsverity signature for %s\n", path); ++ rc = RPMRC_FAIL; ++ break; ++ case EEXIST: ++ rpmlog(RPMLOG_DEBUG, "fsverity signature already enabled %s\n", ++ path); ++ rc = RPMRC_FAIL; ++ break; ++ case EINVAL: ++ rpmlog(RPMLOG_DEBUG, "invalid arguments for ioctl %s\n", path); ++ rc = RPMRC_FAIL; ++ break; ++ case EKEYREJECTED: ++ rpmlog(RPMLOG_DEBUG, "signature doesn't match file %s\n", path); ++ rc = RPMRC_FAIL; ++ break; ++ case EMSGSIZE: ++ rpmlog(RPMLOG_DEBUG, "invalid signature size for %s\n", path); ++ rc = RPMRC_FAIL; ++ break; ++ case ENOPKG: ++ rpmlog(RPMLOG_DEBUG, "unsupported signature algoritm (%i) for %s\n", ++ arg.hash_algorithm, path); ++ rc = RPMRC_FAIL; ++ break; ++ case ETXTBSY: ++ rpmlog(RPMLOG_DEBUG, "file is open by other process %s\n", ++ path); ++ rc = RPMRC_FAIL; ++ break; ++ case ENOTTY: ++ rpmlog(RPMLOG_DEBUG, "fsverity not supported by file system for %s\n", ++ path); ++ break; ++ case EOPNOTSUPP: ++ rpmlog(RPMLOG_DEBUG, "fsverity not enabled on file system for %s\n", ++ path); ++ break; ++ default: ++ rpmlog(RPMLOG_DEBUG, "failed to enable verity (errno %i) for %s\n", ++ errno, path); ++ rc = RPMRC_FAIL; ++ break; ++ } ++ } ++ ++ rpmlog(RPMLOG_DEBUG, "fsverity enabled signature for: path %s dest %s\n", ++ path, dest); ++ close(fd); ++exit: ++ return rc; ++} ++ ++static rpmRC fsverity_init(rpmPlugin plugin, rpmts ts) ++{ ++ sign_config_files = rpmExpandNumeric("%{?_fsverity_sign_config_files}"); ++ ++ rpmlog(RPMLOG_DEBUG, "fsverity_init\n"); ++ ++ return RPMRC_OK; ++} ++ ++struct rpmPluginHooks_s fsverity_hooks = { ++ .init = fsverity_init, ++ .fsm_file_prepare = fsverity_fsm_file_prepare, ++}; +-- +2.13.5 + diff --git a/SOURCES/0029-fsverity-plugin-Use-tag-for-algorithm.patch b/SOURCES/0029-fsverity-plugin-Use-tag-for-algorithm.patch new file mode 100644 index 0000000..1a4b515 --- /dev/null +++ b/SOURCES/0029-fsverity-plugin-Use-tag-for-algorithm.patch @@ -0,0 +1,116 @@ +From 2ea354b4c6c442400e6d06c3b9041bc4603f9784 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 14:13:51 -0400 +Subject: [PATCH 29/33] fsverity plugin: Use tag for algorithm + +This uses the algorithm from the tag, if available. Fallback is SHA256. + +Signed-off-by: Jes Sorensen +--- + lib/rpmfi.c | 9 ++++++--- + lib/rpmfi.h | 3 ++- + lib/rpmfiles.h | 3 ++- + plugins/fsverity.c | 8 ++++++-- + 4 files changed, 16 insertions(+), 7 deletions(-) + +diff --git a/lib/rpmfi.c b/lib/rpmfi.c +index 70f05f509..3e2b4e676 100644 +--- a/lib/rpmfi.c ++++ b/lib/rpmfi.c +@@ -585,7 +585,8 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len) + return signature; + } + +-const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len) ++const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len, ++ uint16_t *algo) + { + const unsigned char *vsignature = NULL; + +@@ -594,6 +595,8 @@ const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len) + vsignature = fi->veritysigs + (fi->veritysiglength * ix); + if (len) + *len = fi->veritysiglength; ++ if (algo) ++ *algo = fi->verityalgo; + } + return vsignature; + } +@@ -1963,9 +1966,9 @@ const unsigned char * rpmfiFSignature(rpmfi fi, size_t *len) + return rpmfilesFSignature(fi->files, fi ? fi->i : -1, len); + } + +-const unsigned char * rpmfiVSignature(rpmfi fi, size_t *len) ++const unsigned char * rpmfiVSignature(rpmfi fi, size_t *len, uint16_t *algo) + { +- return rpmfilesVSignature(fi->files, fi ? fi->i : -1, len); ++ return rpmfilesVSignature(fi->files, fi ? fi->i : -1, len, algo); + } + + uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp) +diff --git a/lib/rpmfi.h b/lib/rpmfi.h +index fcb9d3acd..6fd2747d6 100644 +--- a/lib/rpmfi.h ++++ b/lib/rpmfi.h +@@ -194,9 +194,10 @@ const unsigned char * rpmfiFSignature(rpmfi fi, size_t *siglen); + * Return current verity (binary) signature of file info set iterator. + * @param fi file info set iterator + * @retval siglen signature length (pass NULL to ignore) ++ * @retval algo fsverity algorithm + * @return current verity signature, NULL on invalid + */ +-const unsigned char * rpmfiVSignature(rpmfi fi, size_t *siglen); ++const unsigned char * rpmfiVSignature(rpmfi fi, size_t *siglen, uint16_t *algo); + + /** \ingroup rpmfi + * Return current file linkto (i.e. symlink(2) target) from file info set iterator. +diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h +index 81b3d01a1..64b33281a 100644 +--- a/lib/rpmfiles.h ++++ b/lib/rpmfiles.h +@@ -450,7 +450,8 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len); + * @retval len signature length (pass NULL to ignore) + * @return verity signature, NULL on invalid + */ +-const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len); ++const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len, ++ uint16_t *algo); + + /** \ingroup rpmfiles + * Return file rdev from file info set. +diff --git a/plugins/fsverity.c b/plugins/fsverity.c +index 15ddcf33e..1e7f38b38 100644 +--- a/plugins/fsverity.c ++++ b/plugins/fsverity.c +@@ -39,6 +39,7 @@ static rpmRC fsverity_fsm_file_prepare(rpmPlugin plugin, rpmfi fi, + struct fsverity_enable_arg arg; + const unsigned char * signature = NULL; + size_t len; ++ uint16_t algo = 0; + int rc = RPMRC_OK; + int fd; + rpmFileAction action = XFO_ACTION(op); +@@ -75,7 +76,7 @@ static rpmRC fsverity_fsm_file_prepare(rpmPlugin plugin, rpmfi fi, + goto exit; + } + +- signature = rpmfiVSignature(fi, &len); ++ signature = rpmfiVSignature(fi, &len, &algo); + if (!signature || !len) { + rpmlog(RPMLOG_DEBUG, "fsverity no signature for: path %s dest %s\n", + path, dest); +@@ -84,7 +85,10 @@ static rpmRC fsverity_fsm_file_prepare(rpmPlugin plugin, rpmfi fi, + + memset(&arg, 0, sizeof(arg)); + arg.version = 1; +- arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; ++ if (algo) ++ arg.hash_algorithm = algo; ++ else ++ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.block_size = RPM_FSVERITY_BLKSZ; + arg.sig_ptr = (uintptr_t)signature; + arg.sig_size = len; +-- +2.13.5 + diff --git a/SOURCES/0030-Add-fsverity-tags-to-rpmgeneral.at.patch b/SOURCES/0030-Add-fsverity-tags-to-rpmgeneral.at.patch new file mode 100644 index 0000000..9883a16 --- /dev/null +++ b/SOURCES/0030-Add-fsverity-tags-to-rpmgeneral.at.patch @@ -0,0 +1,28 @@ +From 89fb0e8c0ecfe0d258d8c8bc766455b67610455c Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 18:52:08 -0400 +Subject: [PATCH 30/33] Add fsverity tags to rpmgeneral.at + +Make sure we pass the checks with the new tags in place. + +Signed-off-by: Jes Sorensen +--- + tests/rpmgeneral.at | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at +index 7f100774c..1b9d1c37f 100644 +--- a/tests/rpmgeneral.at ++++ b/tests/rpmgeneral.at +@@ -292,6 +292,8 @@ VERBOSE + VERIFYSCRIPT + VERIFYSCRIPTFLAGS + VERIFYSCRIPTPROG ++VERITYSIGNATUREALGO ++VERITYSIGNATURES + VERSION + XPM + ]) +-- +2.13.5 + diff --git a/SOURCES/0031-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch b/SOURCES/0031-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch new file mode 100644 index 0000000..ca7d50b --- /dev/null +++ b/SOURCES/0031-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch @@ -0,0 +1,117 @@ +From 9c1677d5366d8e2c299285bb09750b3f436cf385 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 13 Apr 2020 18:24:31 -0400 +Subject: [PATCH 31/33] Add --delfilesign flag to delete IMA and fsverity file + signatures + +This allows a user to remove both types of file signatures from the +package. Previously there was no way to delete IMA signatures, only +replace them by first removing the package signature and then +resigning the package and the files. + +Signed-off-by: Jes Sorensen +--- + rpmsign.c | 12 ++++++++++++ + sign/rpmgensig.c | 17 ++++++++++++++++- + sign/rpmsign.h | 9 +++++++++ + 3 files changed, 37 insertions(+), 1 deletion(-) + +diff --git a/rpmsign.c b/rpmsign.c +index 074dd8b13..e43811e9f 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -14,6 +14,7 @@ enum modes { + MODE_ADDSIGN = (1 << 0), + MODE_RESIGN = (1 << 1), + MODE_DELSIGN = (1 << 2), ++ MODE_DELFILESIGN = (1 << 3), + }; + + static int mode = MODE_NONE; +@@ -35,6 +36,10 @@ static struct poptOption signOptsTable[] = { + N_("sign package(s) (identical to --addsign)"), NULL }, + { "delsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELSIGN, + N_("delete package signatures"), NULL }, ++#if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) ++ { "delfilesign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, ++ MODE_DELFILESIGN, N_("delete IMA and fsverity file signatures"), NULL }, ++#endif + { "rpmv3", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_RPMV3, + N_("create rpm v3 header+payload signatures") }, +@@ -207,6 +212,13 @@ int main(int argc, char *argv[]) + ec++; + } + break; ++ case MODE_DELFILESIGN: ++ ec = 0; ++ while ((arg = poptGetArg(optCon)) != NULL) { ++ if (rpmPkgDelFileSign(arg, &sargs) < 0) ++ ec++; ++ } ++ break; + case MODE_NONE: + printUsage(optCon, stderr, 0); + break; +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 78da1347b..f2fddb898 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -336,6 +336,14 @@ static void deleteSigs(Header sigh) + headerDel(sigh, RPMSIGTAG_PGP5); + } + ++static void deleteFileSigs(Header sigh) ++{ ++ headerDel(sigh, RPMSIGTAG_FILESIGNATURELENGTH); ++ headerDel(sigh, RPMSIGTAG_FILESIGNATURES); ++ headerDel(sigh, RPMSIGTAG_VERITYSIGNATURES); ++ headerDel(sigh, RPMSIGTAG_VERITYSIGNATUREALGO); ++} ++ + static int haveSignature(rpmtd sigtd, Header h) + { + pgpDigParams sig1 = NULL; +@@ -574,7 +582,9 @@ static int rpmSign(const char *rpm, int deleting, int flags) + goto exit; + } + +- if (deleting) { /* Nuke all the signature tags. */ ++ if (deleting == 2) { /* Nuke IMA + fsverity file signature tags. */ ++ deleteFileSigs(sigh); ++ } else if (deleting) { /* Nuke all the signature tags. */ + deleteSigs(sigh); + } else { + /* Signature target containing header + payload */ +@@ -739,3 +749,8 @@ int rpmPkgDelSign(const char *path, const struct rpmSignArgs * args) + { + return rpmSign(path, 1, 0); + } ++ ++int rpmPkgDelFileSign(const char *path, const struct rpmSignArgs * args) ++{ ++ return rpmSign(path, 2, 0); ++} +diff --git a/sign/rpmsign.h b/sign/rpmsign.h +index 2b8a10a1a..5169741dd 100644 +--- a/sign/rpmsign.h ++++ b/sign/rpmsign.h +@@ -44,6 +44,15 @@ int rpmPkgSign(const char *path, const struct rpmSignArgs * args); + */ + int rpmPkgDelSign(const char *path, const struct rpmSignArgs * args); + ++ ++/** \ingroup rpmsign ++ * Delete file signature(s) from a package ++ * @param path path to package ++ * @param args signing parameters (or NULL for defaults) ++ * @return 0 on success ++ */ ++int rpmPkgDelFileSign(const char *path, const struct rpmSignArgs * args); ++ + #ifdef __cplusplus + } + #endif +-- +2.13.5 + diff --git a/SOURCES/0032-Update-man-page-for-rpmsign.patch b/SOURCES/0032-Update-man-page-for-rpmsign.patch new file mode 100644 index 0000000..0b5c86a --- /dev/null +++ b/SOURCES/0032-Update-man-page-for-rpmsign.patch @@ -0,0 +1,72 @@ +From c84db77afcfaab53cd967b2cc592d7ee50719bf7 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Tue, 12 May 2020 13:42:34 -0400 +Subject: [PATCH 32/33] Update man page for rpmsign + +This documents the new arguments --signverity and --certpath required +to sign a package with fsverity signatures. + +Signed-off-by: Jes Sorensen +--- + doc/rpmsign.8 | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/doc/rpmsign.8 b/doc/rpmsign.8 +index f7ceae89b..a212746fe 100644 +--- a/doc/rpmsign.8 ++++ b/doc/rpmsign.8 +@@ -9,6 +9,8 @@ rpmsign \- RPM Package Signing + + \fBrpm\fR \fB--delsign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR + ++\fBrpm\fR \fB--delfilesign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR ++ + .SS "rpmsign-options" + .PP + [\fb--rpmv3\fR] +@@ -30,6 +32,12 @@ packages with a MD5/SHA1 checksums cannot be signed in FIPS mode. + .PP + Delete all signatures from each package \fIPACKAGE_FILE\fR given. + ++\fBrpm\fR \fB--delfilesign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR ++ ++.PP ++Delete all IMA and fsverity file signatures from each package ++\fIPACKAGE_FILE\fR given. ++ + .SS "SIGN OPTIONS" + .PP + .TP +@@ -44,12 +52,23 @@ signature verifiable with rpm < 4.14 or other interoperability reasons. + \fB--fskpath \fIKEY\fB\fR + Used with \fB--signfiles\fR, use file signing key \fIKey\fR. + .TP ++\fB--certpath \fICERT\fB\fR ++Used with \fB--signverity\fR, use file signing certificate \fICert\fR. ++.TP + \fB--signfiles\fR + Sign package files. The macro \fB%_binary_filedigest_algorithm\fR must + be set to a supported algorithm before building the package. The + supported algorithms are SHA1, SHA256, SHA384, and SHA512, which are + represented as 2, 8, 9, and 10 respectively. The file signing key (RSA + private key) must be set before signing the package, it can be configured on the command line with \fB--fskpath\fR or the macro %_file_signing_key. ++.TP ++\fB--signverity\fR ++Sign package files with fsverity signatures. The file signing key (RSA ++private key) and the signing certificate must be set before signing ++the package. The key can be configured on the command line with ++\fB--fskpath\fR or the macro %_file_signing_key, and the cert can be ++configured on the command line with \fB--certpath\fR or the macro ++%_file_signing_cert. + + .SS "USING GPG TO SIGN PACKAGES" + .PP +@@ -110,4 +129,5 @@ Jeff Johnson + Erik Troan + Panu Matilainen + Fionnuala Gunter ++Jes Sorensen + .fi +-- +2.13.5 + diff --git a/SOURCES/0033-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch b/SOURCES/0033-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch new file mode 100644 index 0000000..859a649 --- /dev/null +++ b/SOURCES/0033-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch @@ -0,0 +1,167 @@ +From c5afc6f0ceb8a126c76a13656241897ea93bda85 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Wed, 10 Jun 2020 12:30:54 -0400 +Subject: [PATCH 33/33] rpmsign: Add argument to specify algorithm for fsverity + signatures + +The argument --verity-algo can be used to specify the algorithm for +the fsverity signatures. If nothing is specified, this will default to +sha256. The available algorithms depend on libfsverity, currently +sha256 and sha512 are supported. + +Signed-off-by: Jes Sorensen +--- + doc/rpmsign.8 | 3 +++ + rpmsign.c | 7 +++++++ + sign/rpmgensig.c | 21 +++++++++++++++++++-- + sign/rpmsignverity.c | 6 +++--- + sign/rpmsignverity.h | 2 +- + 5 files changed, 33 insertions(+), 6 deletions(-) + +diff --git a/doc/rpmsign.8 b/doc/rpmsign.8 +index a212746fe..5165e39f9 100644 +--- a/doc/rpmsign.8 ++++ b/doc/rpmsign.8 +@@ -55,6 +55,9 @@ Used with \fB--signfiles\fR, use file signing key \fIKey\fR. + \fB--certpath \fICERT\fB\fR + Used with \fB--signverity\fR, use file signing certificate \fICert\fR. + .TP ++\fB--verityalgo \fIALG\fB\fR ++Used with \fB--signverity\fR, to specify the signing algorithm. sha256 and sha512 are supported, with sha256 being the default if this argument is not specified. This can also be specified with the macro %_verity_algorithm ++.TP + \fB--signfiles\fR + Sign package files. The macro \fB%_binary_filedigest_algorithm\fR must + be set to a supported algorithm before building the package. The +diff --git a/rpmsign.c b/rpmsign.c +index e43811e9f..12299379c 100644 +--- a/rpmsign.c ++++ b/rpmsign.c +@@ -25,6 +25,7 @@ static char * fileSigningKey = NULL; + #endif + #ifdef WITH_FSVERITY + static char * fileSigningCert = NULL; ++static char * verityAlgorithm = NULL; + #endif + + static struct rpmSignArgs sargs = {NULL, 0, 0}; +@@ -52,6 +53,9 @@ static struct poptOption signOptsTable[] = { + { "signverity", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_FSVERITY, + N_("generate fsverity signatures for package(s) files"), NULL}, ++ { "verityalgo", '\0', POPT_ARG_STRING, &verityAlgorithm, 0, ++ N_("algorithm to use for verity signatures, default sha256"), ++ N_("") }, + { "certpath", '\0', POPT_ARG_STRING, &fileSigningCert, 0, + N_("use file signing cert "), + N_("") }, +@@ -138,6 +142,9 @@ static int doSign(poptContext optCon, struct rpmSignArgs *sargs) + if (fileSigningCert) { + rpmPushMacro(NULL, "_file_signing_cert", NULL, fileSigningCert, RMIL_GLOBAL); + } ++ if (verityAlgorithm) { ++ rpmPushMacro(NULL, "_verity_algorithm", NULL, verityAlgorithm, RMIL_GLOBAL); ++ } + #endif + + if (flags_sign_files(sargs->signflags)) { +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index f2fddb898..7b1895555 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -8,6 +8,9 @@ + #include + #include + #include ++#ifdef WITH_FSVERITY ++#include ++#endif + + #include /* RPMSIGTAG & related */ + #include +@@ -451,23 +454,37 @@ static rpmRC includeFileSignatures(Header *sigp, Header *hdrp) + static rpmRC includeVeritySignatures(FD_t fd, Header *sigp, Header *hdrp) + { + #ifdef WITH_FSVERITY +- rpmRC rc; ++ rpmRC rc = RPMRC_OK; + char *key = rpmExpand("%{?_file_signing_key}", NULL); + char *keypass = rpmExpand("%{?_file_signing_key_password}", NULL); + char *cert = rpmExpand("%{?_file_signing_cert}", NULL); ++ char *algorithm = rpmExpand("%{?_verity_algorithm}", NULL); ++ uint16_t algo = 0; + + if (rstreq(keypass, "")) { + free(keypass); + keypass = NULL; + } + ++ if (algorithm && strlen(algorithm) > 0) { ++ algo = libfsverity_find_hash_alg_by_name(algorithm); ++ rpmlog(RPMLOG_DEBUG, _("Searching for algorithm %s got %i\n"), ++ algorithm, algo); ++ if (!algo) { ++ rpmlog(RPMLOG_ERR, _("Unsupported fsverity algorithm %s\n"), ++ algorithm); ++ rc = RPMRC_FAIL; ++ goto out; ++ } ++ } + if (key && cert) { +- rc = rpmSignVerity(fd, *sigp, *hdrp, key, keypass, cert); ++ rc = rpmSignVerity(fd, *sigp, *hdrp, key, keypass, cert, algo); + } else { + rpmlog(RPMLOG_ERR, _("fsverity signatures requires a key and a cert\n")); + rc = RPMRC_FAIL; + } + ++ out: + free(keypass); + free(key); + free(cert); +diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c +index 55096e732..e6c830cdc 100644 +--- a/sign/rpmsignverity.c ++++ b/sign/rpmsignverity.c +@@ -95,7 +95,7 @@ static char *rpmVeritySignFile(rpmfi fi, size_t *sig_size, char *key, + } + + rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, +- char *keypass, char *cert) ++ char *keypass, char *cert, uint16_t algo) + { + int rc; + FD_t gzdi; +@@ -111,7 +111,6 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + char **signatures = NULL; + size_t sig_size; + int nr_files, idx; +- uint16_t algo; + uint32_t algo32; + + Fseek(fd, 0, SEEK_SET); +@@ -156,7 +155,8 @@ rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + nr_files = rpmfiFC(hfi); + signatures = xcalloc(nr_files, sizeof(char *)); + +- algo = FS_VERITY_HASH_ALG_SHA256; ++ if (!algo) ++ algo = FS_VERITY_HASH_ALG_SHA256; + + rpmlog(RPMLOG_DEBUG, _("file count - header: %i, payload %i\n"), + nr_files, rpmfiFC(fi)); +diff --git a/sign/rpmsignverity.h b/sign/rpmsignverity.h +index 69bbaf7f7..d869e8d8e 100644 +--- a/sign/rpmsignverity.h ++++ b/sign/rpmsignverity.h +@@ -27,7 +27,7 @@ extern "C" { + */ + RPM_GNUC_INTERNAL + rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, +- char *keypass, char *cert); ++ char *keypass, char *cert, uint16_t algo); + + #ifdef _cplusplus + } +-- +2.13.5 +