From 45afdab3b03b4766cccaf488956d063f446309e8 Mon Sep 17 00:00:00 2001 From: Matthew Almond Date: Aug 03 2021 23:57:41 +0000 Subject: Add fsverity backport to SIG These patches have been in production at Facebook for a while. This was blocked on the absence of `fsverity-utils-devel`. This is now in EPEL, and EPEL itself is enabled in `centos-stream-8-x86_64` mock config. --- diff --git a/SOURCES/0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch b/SOURCES/0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch new file mode 100644 index 0000000..4ecdab7 --- /dev/null +++ b/SOURCES/0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch @@ -0,0 +1,32 @@ +From 5b21f2f2a12adfd9e1adffc2bf1ad7171ee5d03c 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.27.0 + diff --git a/SOURCES/0002-Add-RPMTAG_IDENTITY-reservation.patch b/SOURCES/0002-Add-RPMTAG_IDENTITY-reservation.patch new file mode 100644 index 0000000..9dca222 --- /dev/null +++ b/SOURCES/0002-Add-RPMTAG_IDENTITY-reservation.patch @@ -0,0 +1,34 @@ +From a74ea72cb47dcdc1006ffbdd23643964bd40f580 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.27.0 + diff --git a/SOURCES/0003-Use-lower-level-headerPut-for-file-signing.patch b/SOURCES/0003-Use-lower-level-headerPut-for-file-signing.patch new file mode 100644 index 0000000..129f420 --- /dev/null +++ b/SOURCES/0003-Use-lower-level-headerPut-for-file-signing.patch @@ -0,0 +1,58 @@ +From 492823ca53f5666b82e94fcfdd422bdcd67005cb Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 11:07:38 +0300 +Subject: [PATCH 03/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.27.0 + diff --git a/SOURCES/0004-Place-file-signatures-into-the-signature-header-wher.patch b/SOURCES/0004-Place-file-signatures-into-the-signature-header-wher.patch new file mode 100644 index 0000000..12f8d8b --- /dev/null +++ b/SOURCES/0004-Place-file-signatures-into-the-signature-header-wher.patch @@ -0,0 +1,329 @@ +From f354c5a4265ddad758ad41abfb2f5fe174a54c69 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 11:44:10 +0300 +Subject: [PATCH 04/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/rpmtag.h | 4 ++ + sign/rpmgensig.c | 161 +++----------------------------------------- + sign/rpmsignfiles.c | 14 ++-- + sign/rpmsignfiles.h | 5 +- + 4 files changed, 22 insertions(+), 162 deletions(-) + +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 002492d20..46719ec75 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 */ +@@ -425,6 +427,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 8f77ad9c1..5fddb56ea 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -417,74 +417,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); + +@@ -494,94 +432,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")); +@@ -674,13 +528,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.27.0 + diff --git a/SOURCES/0005-Unbreak-file-signing-from-previous-commit.patch b/SOURCES/0005-Unbreak-file-signing-from-previous-commit.patch new file mode 100644 index 0000000..ce68ba5 --- /dev/null +++ b/SOURCES/0005-Unbreak-file-signing-from-previous-commit.patch @@ -0,0 +1,30 @@ +From: Panu Matilainen +Date: Tue, 10 Oct 2017 14:40:05 +0300 +Subject: [PATCH 05/33] Unbreak file signing from previous commit +From cabecd35ff6842c029103732ca5e5ea7dcdce256 Mon Sep 17 00:00:00 2001 + +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 | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/package.c b/lib/package.c +index 211fd3902..b7d996a12 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -43,6 +43,8 @@ struct taglate_s { + { RPMSIGTAG_GPG, RPMTAG_SIGGPG, 0, 0 }, + /* { RPMSIGTAG_PGP5, RPMTAG_SIGPGP5, 0, 0 }, */ /* long obsolete, dont use */ + { RPMSIGTAG_PAYLOADSIZE, RPMTAG_ARCHIVESIZE, 1, 1 }, ++ { RPMSIGTAG_FILESIGNATURES, RPMTAG_FILESIGNATURES, 0, 1 }, ++ { RPMSIGTAG_FILESIGNATURELENGTH, RPMTAG_FILESIGNATURELENGTH, 1, 1 }, + { RPMSIGTAG_SHA1, RPMTAG_SHA1HEADER, 1, 0 }, + { RPMSIGTAG_SHA256, RPMTAG_SHA256HEADER, 1, 0 }, + { RPMSIGTAG_DSA, RPMTAG_DSAHEADER, 0, 0 }, +-- +2.27.0 + diff --git a/SOURCES/0006-Assume-failure-in-rpmSignFiles.patch b/SOURCES/0006-Assume-failure-in-rpmSignFiles.patch new file mode 100644 index 0000000..1b987f5 --- /dev/null +++ b/SOURCES/0006-Assume-failure-in-rpmSignFiles.patch @@ -0,0 +1,71 @@ +From bcdd8505e792fd67c1480d43d987b92a61710e53 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 14:43:58 +0300 +Subject: [PATCH 06/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.27.0 + diff --git a/SOURCES/0007-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch b/SOURCES/0007-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch new file mode 100644 index 0000000..b70d845 --- /dev/null +++ b/SOURCES/0007-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch @@ -0,0 +1,108 @@ +From 0a7318ab4467d3156723c7a265dbd3456b8d1e20 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 14:44:18 +0300 +Subject: [PATCH 07/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.27.0 + diff --git a/SOURCES/0008-Eliminate-redundant-signature-length-calculation-fun.patch b/SOURCES/0008-Eliminate-redundant-signature-length-calculation-fun.patch new file mode 100644 index 0000000..1d7daa4 --- /dev/null +++ b/SOURCES/0008-Eliminate-redundant-signature-length-calculation-fun.patch @@ -0,0 +1,105 @@ +From ff2fb80469e9aa478ea4de3eae5d9c13ca411382 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 15:04:38 +0300 +Subject: [PATCH 08/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.27.0 + diff --git a/SOURCES/0009-Drop-redundant-check-on-hash-algo-name.patch b/SOURCES/0009-Drop-redundant-check-on-hash-algo-name.patch new file mode 100644 index 0000000..627511c --- /dev/null +++ b/SOURCES/0009-Drop-redundant-check-on-hash-algo-name.patch @@ -0,0 +1,29 @@ +From efd41edd0c9ba848814a5434d986338c1691418e Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 10 Oct 2017 15:20:16 +0300 +Subject: [PATCH 09/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.27.0 + 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..e619007 --- /dev/null +++ b/SOURCES/0010-Drop-redundant-check-on-hash-algo-name.patch @@ -0,0 +1,40 @@ +From 3461bd52ef2d403de1c420962aac52834f6e4b34 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. +--- + lib/rpmtag.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/rpmtag.h b/lib/rpmtag.h +index 46719ec75..40ff5fa5d 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 */ +@@ -427,8 +427,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.27.0 + diff --git a/SOURCES/0011-Generalize-file-signing-to-use-a-generic-flags-field.patch b/SOURCES/0011-Generalize-file-signing-to-use-a-generic-flags-field.patch new file mode 100644 index 0000000..d86b96b --- /dev/null +++ b/SOURCES/0011-Generalize-file-signing-to-use-a-generic-flags-field.patch @@ -0,0 +1,129 @@ +From 9e1a49197a6ddd0e984c12c9dc15fe7af435b611 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 2 Mar 2020 13:56:33 +0200 +Subject: [PATCH 11/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 5fddb56ea..1981981f4 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -472,10 +472,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; +@@ -531,7 +531,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; + } +@@ -682,7 +682,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.27.0 + diff --git a/SOURCES/0012-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch b/SOURCES/0012-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch new file mode 100644 index 0000000..f287e92 --- /dev/null +++ b/SOURCES/0012-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch @@ -0,0 +1,201 @@ +From 031b8481a0dfe875e9cf0f5d440b9379a62651a6 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 2 Mar 2020 14:47:26 +0200 +Subject: [PATCH 12/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 1981981f4..4903a4de1 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: +@@ -528,6 +531,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); + +@@ -540,6 +549,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; +@@ -549,7 +559,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 880e5edd0..12e2221b3 100644 +--- a/tests/rpmsigdig.at ++++ b/tests/rpmsigdig.at +@@ -391,7 +391,7 @@ AT_CLEANUP + + # ------------------------------ + # Test --addsign +-AT_SETUP([rpmsign --addsign ]) ++AT_SETUP([rpmsign --addsign --rpmv3 ]) + AT_KEYWORDS([rpmsign signature]) + AT_CHECK([ + RPMDB_CLEAR +@@ -399,7 +399,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 +@@ -424,6 +424,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.27.0 + diff --git a/SOURCES/0013-RPMTAG_PAYLOADDIGESTALT-is-not-backported-here-don-t.patch b/SOURCES/0013-RPMTAG_PAYLOADDIGESTALT-is-not-backported-here-don-t.patch new file mode 100644 index 0000000..70975fa --- /dev/null +++ b/SOURCES/0013-RPMTAG_PAYLOADDIGESTALT-is-not-backported-here-don-t.patch @@ -0,0 +1,27 @@ +From 5e11a52627882efe6a15622ec256835a821e3eff Mon Sep 17 00:00:00 2001 +From: Igor Kanyuka +Date: Thu, 29 Apr 2021 18:24:48 -0700 +Subject: [PATCH 13/33] RPMTAG_PAYLOADDIGESTALT is not backported here, don't + check if it's in the header + +--- + sign/rpmgensig.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 4903a4de1..a6e37e71b 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -532,8 +532,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + } + + /* Always add V3 signatures if no payload digest present */ +- if (!(headerIsEntry(h, RPMTAG_PAYLOADDIGEST) || +- headerIsEntry(h, RPMTAG_PAYLOADDIGESTALT))) { ++ if (!headerIsEntry(h, RPMTAG_PAYLOADDIGEST)) { + flags |= RPMSIGN_FLAG_RPMV3; + } + +-- +2.27.0 + diff --git a/SOURCES/0014-Drop-support-for-dmalloc.patch b/SOURCES/0014-Drop-support-for-dmalloc.patch new file mode 100644 index 0000000..55dd7d4 --- /dev/null +++ b/SOURCES/0014-Drop-support-for-dmalloc.patch @@ -0,0 +1,48 @@ +From 6398807623ca24eafac0607b3d09b244cc5dfd5d 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 57a4f4001..3c102d5eb 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1030,13 +1030,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.27.0 + diff --git a/SOURCES/0015-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch b/SOURCES/0015-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch new file mode 100644 index 0000000..e6dae81 --- /dev/null +++ b/SOURCES/0015-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch @@ -0,0 +1,28 @@ +From 7784da14fe57df919df9dfdad30e436ffe6d3e28 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 11:22:15 -0400 +Subject: [PATCH 15/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.27.0 + diff --git a/SOURCES/0016-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch b/SOURCES/0016-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch new file mode 100644 index 0000000..89845cb --- /dev/null +++ b/SOURCES/0016-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch @@ -0,0 +1,136 @@ +From f525681b4f66026578bc728b864bfea3d814c29e Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 27 Mar 2020 18:31:36 -0400 +Subject: [PATCH 16/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 +--- + configure.ac | 19 +++++++++++++++++++ + rpmsign.c | 20 ++++++++++++++------ + sign/Makefile.am | 5 +++++ + sign/rpmsign.h | 1 + + 4 files changed, 39 insertions(+), 6 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 3c102d5eb..cc7144440 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.27.0 + diff --git a/SOURCES/0017-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch b/SOURCES/0017-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch new file mode 100644 index 0000000..ae64146 --- /dev/null +++ b/SOURCES/0017-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch @@ -0,0 +1,51 @@ +From dbb4f464d177e2c3bfa13b1b2bb511fa6fde40d9 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Wed, 27 May 2020 16:49:03 -0400 +Subject: [PATCH 17/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.27.0 + diff --git a/SOURCES/0018-rpmsign-Handle-certpath-for-signing-certificate.patch b/SOURCES/0018-rpmsign-Handle-certpath-for-signing-certificate.patch new file mode 100644 index 0000000..77a8b31 --- /dev/null +++ b/SOURCES/0018-rpmsign-Handle-certpath-for-signing-certificate.patch @@ -0,0 +1,52 @@ +From 5e74846046e06f5a3401c4d2eb4ccfadacd4ab53 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 3 Apr 2020 16:26:06 -0400 +Subject: [PATCH 18/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.27.0 + diff --git a/SOURCES/0019-Implement-rpmSignVerity.patch b/SOURCES/0019-Implement-rpmSignVerity.patch new file mode 100644 index 0000000..70e873a --- /dev/null +++ b/SOURCES/0019-Implement-rpmSignVerity.patch @@ -0,0 +1,243 @@ +From d447376aa2bf66a5d5b6a928fb0c6e65189910ba Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 3 Apr 2020 16:38:08 -0400 +Subject: [PATCH 19/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 a6e37e71b..8d5c5858f 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" + +@@ -446,6 +447,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; +@@ -544,6 +575,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.27.0 + diff --git a/SOURCES/0020-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch b/SOURCES/0020-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch new file mode 100644 index 0000000..b099eaf --- /dev/null +++ b/SOURCES/0020-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch @@ -0,0 +1,95 @@ +From a7e81a1b18c9e9d124a4ea917c8015af62584abb Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Thu, 28 May 2020 17:48:23 -0400 +Subject: [PATCH 20/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.27.0 + diff --git a/SOURCES/0021-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch b/SOURCES/0021-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch new file mode 100644 index 0000000..6caee37 --- /dev/null +++ b/SOURCES/0021-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch @@ -0,0 +1,210 @@ +From ef819fecfed22cab2ccbd128e5ede33db8f2d3e9 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Thu, 9 Apr 2020 12:58:17 -0400 +Subject: [PATCH 21/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 40ff5fa5d..478457ecb 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 */ +@@ -427,8 +428,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.27.0 + diff --git a/SOURCES/0022-rpmSignVerity-Generate-signatures-for-files-not-pres.patch b/SOURCES/0022-rpmSignVerity-Generate-signatures-for-files-not-pres.patch new file mode 100644 index 0000000..dba0ca6 --- /dev/null +++ b/SOURCES/0022-rpmSignVerity-Generate-signatures-for-files-not-pres.patch @@ -0,0 +1,162 @@ +From 22420d9ee652a25357727b00585dc3cfe78b2a80 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 13 Apr 2020 18:14:15 -0400 +Subject: [PATCH 22/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 b7d996a12..c6108f686 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -45,6 +45,7 @@ struct taglate_s { + { RPMSIGTAG_PAYLOADSIZE, RPMTAG_ARCHIVESIZE, 1, 1 }, + { RPMSIGTAG_FILESIGNATURES, RPMTAG_FILESIGNATURES, 0, 1 }, + { RPMSIGTAG_FILESIGNATURELENGTH, RPMTAG_FILESIGNATURELENGTH, 1, 1 }, ++ { RPMSIGTAG_VERITYSIGNATURES, RPMTAG_VERITYSIGNATURES, 0, 0 }, + { RPMSIGTAG_SHA1, RPMTAG_SHA1HEADER, 1, 0 }, + { RPMSIGTAG_SHA256, RPMTAG_SHA256HEADER, 1, 0 }, + { RPMSIGTAG_DSA, RPMTAG_DSAHEADER, 0, 0 }, +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.27.0 + diff --git a/SOURCES/0023-Process-verity-tag-on-package-read.patch b/SOURCES/0023-Process-verity-tag-on-package-read.patch new file mode 100644 index 0000000..4f682c7 --- /dev/null +++ b/SOURCES/0023-Process-verity-tag-on-package-read.patch @@ -0,0 +1,189 @@ +From 34e751ccee43f799dd32f6b9c64020106dba9fac Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 13 Apr 2020 18:21:36 -0400 +Subject: [PATCH 23/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 +@@ -190,6 +190,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 +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; +@@ -442,6 +443,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 +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.27.0 + diff --git a/SOURCES/0024-Generate-a-zero-length-signature-for-symlinks.patch b/SOURCES/0024-Generate-a-zero-length-signature-for-symlinks.patch new file mode 100644 index 0000000..2fc3f5a --- /dev/null +++ b/SOURCES/0024-Generate-a-zero-length-signature-for-symlinks.patch @@ -0,0 +1,33 @@ +From 202359dc598f2162175e3a8552c9b338d27b8989 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Tue, 14 Apr 2020 10:33:32 -0400 +Subject: [PATCH 24/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.27.0 + diff --git a/SOURCES/0025-rpmsignverity.c-Clean-up-debug-logging.patch b/SOURCES/0025-rpmsignverity.c-Clean-up-debug-logging.patch new file mode 100644 index 0000000..bb6e4f3 --- /dev/null +++ b/SOURCES/0025-rpmsignverity.c-Clean-up-debug-logging.patch @@ -0,0 +1,40 @@ +From 7e50b3f4b2ebb963d1080a0a1469517ef81f780c Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Tue, 14 Apr 2020 12:08:09 -0400 +Subject: [PATCH 25/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.27.0 + diff --git a/SOURCES/0026-fsverity-add-tag-for-fsverity-algorithm.patch b/SOURCES/0026-fsverity-add-tag-for-fsverity-algorithm.patch new file mode 100644 index 0000000..d1c12c9 --- /dev/null +++ b/SOURCES/0026-fsverity-add-tag-for-fsverity-algorithm.patch @@ -0,0 +1,161 @@ +From e339fdbbd0b81dc1fcdc2032e861b8a5fa6e062d Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 13:40:26 -0400 +Subject: [PATCH 26/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 c6108f686..3c761d365 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -46,6 +46,7 @@ struct taglate_s { + { RPMSIGTAG_FILESIGNATURES, RPMTAG_FILESIGNATURES, 0, 1 }, + { RPMSIGTAG_FILESIGNATURELENGTH, RPMTAG_FILESIGNATURELENGTH, 1, 1 }, + { RPMSIGTAG_VERITYSIGNATURES, RPMTAG_VERITYSIGNATURES, 0, 0 }, ++ { RPMSIGTAG_VERITYSIGNATUREALGO, RPMTAG_VERITYSIGNATUREALGO, 1, 0 }, + { RPMSIGTAG_SHA1, RPMTAG_SHA1HEADER, 1, 0 }, + { RPMSIGTAG_SHA256, RPMTAG_SHA256HEADER, 1, 0 }, + { RPMSIGTAG_DSA, RPMTAG_DSAHEADER, 0, 0 }, +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 478457ecb..8d1efcc79 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 */ +@@ -431,6 +432,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.27.0 + diff --git a/SOURCES/0027-plugins-fsverity-Install-fsverity-signatures.patch b/SOURCES/0027-plugins-fsverity-Install-fsverity-signatures.patch new file mode 100644 index 0000000..aa78311 --- /dev/null +++ b/SOURCES/0027-plugins-fsverity-Install-fsverity-signatures.patch @@ -0,0 +1,281 @@ +From f1a92e02faa2715777286acd07b8d0f465c5df37 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 11:11:25 -0400 +Subject: [PATCH 27/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 | 29 ++++++++ + macros.in | 4 + + plugins/Makefile.am | 7 ++ + plugins/fsverity.c | 177 ++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 217 insertions(+) + create mode 100644 plugins/fsverity.c + +diff --git a/configure.ac b/configure.ac +index cc7144440..7d3c31831 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1049,6 +1049,35 @@ 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]) ++ ++#================= ++# Check for audit library. ++AC_ARG_WITH(audit, ++AS_HELP_STRING([--with-audit],[Linux audit plugin]), ++with_audit=$withval, ++with_audit=auto) ++ ++WITH_AUDIT_LIB= ++AS_IF([test "$enable_plugins" != no],[ ++ AS_IF([test "x$with_audit" != xno],[ ++ AC_SEARCH_LIBS([audit_open],[audit],[ ++ WITH_AUDIT_LIB="$ac_res" ++ AC_DEFINE(WITH_AUDIT, 1, [libaudit support]) ++ with_audit=yes ++ ],[ ++ if test "x$with_audit" != xauto; then ++ AC_MSG_ERROR([missing audit library]) ++ fi ++ ]) ++ ]) ++]) ++AC_SUBST(WITH_AUDIT_LIB) ++AM_CONDITIONAL(AUDIT,[test "$with_audit" = yes]) ++ + 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 fe8862903..3c722146b 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 + # +@@ -1185,6 +1188,7 @@ package or when debugging this package.\ + %__transaction_syslog %{__plugindir}/syslog.so + %__transaction_ima %{__plugindir}/ima.so + %__transaction_fapolicyd %{__plugindir}/fapolicyd.so ++%__transaction_fsverity %{__plugindir}/fsverity.so + %__transaction_prioreset %{__plugindir}/prioreset.so + + #------------------------------------------------------------------------------ +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index cbfb81e19..e51b71f62 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -48,3 +48,10 @@ fapolicyd_la_sources = fapolicyd.c + fapolicyd_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la + plugins_LTLIBRARIES += fapolicyd.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.27.0 + diff --git a/SOURCES/0028-fsverity-plugin-Use-tag-for-algorithm.patch b/SOURCES/0028-fsverity-plugin-Use-tag-for-algorithm.patch new file mode 100644 index 0000000..0b9bb41 --- /dev/null +++ b/SOURCES/0028-fsverity-plugin-Use-tag-for-algorithm.patch @@ -0,0 +1,116 @@ +From 5a5286ac37cd58779cc0e5b69088d9acc8f40c4e Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 14:13:51 -0400 +Subject: [PATCH 28/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.27.0 + diff --git a/SOURCES/0029-Add-fsverity-tags-to-rpmgeneral.at.patch b/SOURCES/0029-Add-fsverity-tags-to-rpmgeneral.at.patch new file mode 100644 index 0000000..6575b99 --- /dev/null +++ b/SOURCES/0029-Add-fsverity-tags-to-rpmgeneral.at.patch @@ -0,0 +1,28 @@ +From 3132053a066b3dc8aa38b358ecd316ee60fd0f7c Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 20 Apr 2020 18:52:08 -0400 +Subject: [PATCH 29/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 45d38698b..8a7dc827f 100644 +--- a/tests/rpmgeneral.at ++++ b/tests/rpmgeneral.at +@@ -291,6 +291,8 @@ VERBOSE + VERIFYSCRIPT + VERIFYSCRIPTFLAGS + VERIFYSCRIPTPROG ++VERITYSIGNATUREALGO ++VERITYSIGNATURES + VERSION + XPM + ]) +-- +2.27.0 + diff --git a/SOURCES/0030-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch b/SOURCES/0030-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch new file mode 100644 index 0000000..d872d5e --- /dev/null +++ b/SOURCES/0030-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch @@ -0,0 +1,117 @@ +From 46db4f6827840e828f42424454410b930895d9a7 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 13 Apr 2020 18:24:31 -0400 +Subject: [PATCH 30/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 8d5c5858f..02cf0bc62 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; +@@ -580,7 +588,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 */ +@@ -745,3 +755,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.27.0 + diff --git a/SOURCES/0031-Update-man-page-for-rpmsign.patch b/SOURCES/0031-Update-man-page-for-rpmsign.patch new file mode 100644 index 0000000..938e0f1 --- /dev/null +++ b/SOURCES/0031-Update-man-page-for-rpmsign.patch @@ -0,0 +1,72 @@ +From 4d243b7e692e3803a764343dfed23feb1c656f0b Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Tue, 12 May 2020 13:42:34 -0400 +Subject: [PATCH 31/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.27.0 + diff --git a/SOURCES/0032-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch b/SOURCES/0032-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch new file mode 100644 index 0000000..c3f56e8 --- /dev/null +++ b/SOURCES/0032-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch @@ -0,0 +1,168 @@ +From 3669fecaba2858aeca44d1bfc265760611ea8834 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Wed, 10 Jun 2020 12:30:54 -0400 +Subject: [PATCH 32/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 | 22 ++++++++++++++++++++-- + sign/rpmsignverity.c | 6 +++--- + sign/rpmsignverity.h | 2 +- + 5 files changed, 34 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 02cf0bc62..b3b02828a 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -8,6 +8,10 @@ + #include + #include + #include ++#include ++#ifdef WITH_FSVERITY ++#include ++#endif + + #include /* RPMSIGTAG & related */ + #include +@@ -458,23 +462,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.27.0 + diff --git a/SOURCES/0033-Enable-fsverity-in-CI.patch b/SOURCES/0033-Enable-fsverity-in-CI.patch new file mode 100644 index 0000000..55a209f --- /dev/null +++ b/SOURCES/0033-Enable-fsverity-in-CI.patch @@ -0,0 +1,27 @@ +From 84ee9dc61b14056fec489bb099f1f212b3b169a9 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Fri, 28 Aug 2020 11:10:41 -0400 +Subject: [PATCH 33/33] Enable fsverity in CI + +Add fsverity-utils and fsverity-utils-devel as dependencies. + +Signed-off-by: Jes Sorensen +--- + Makefile.am | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Makefile.am b/Makefile.am +index 8e92f0cde..3c1451049 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -17,6 +17,7 @@ DISTCHECK_CONFIGURE_FLAGS = \ + --with-imaevm \ + --with-crypto=openssl \ + --with-fapolicyd \ ++ --with-fsverity \ + --disable-dependency-tracking + + include $(top_srcdir)/rpm.am +-- +2.27.0 + diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec index 353127d..96e3236 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -12,6 +12,8 @@ %bcond_without libarchive # build with libimaevm.so %bcond_with libimaevm +# build with libfsverity.so +%bcond_without libfsverity # build with new db format %bcond_without ndb # build with zstd support? @@ -30,7 +32,7 @@ %global rpmver 4.14.3 #global snapver rc2 -%global rel 15.1 +%global rel 15.2 %global srcver %{version}%{?snapver:-%{snapver}} %global srcdir %{?snapver:testing}%{!?snapver:%{name}-%(echo %{version} | cut -d'.' -f1-2).x} @@ -140,6 +142,43 @@ Patch1001: compile-with-Platform-Python-binary-where-relevant.patch # make unversioned %%__python an error unless explicitly overridden Patch1002: rpm-4.14.2-unversioned-python.patch +# fsverity support +%if %{with libfsverity} +Patch1950: 0001-Add-RPMTAG_AUTOINSTALLED-reservation.patch +Patch1951: 0002-Add-RPMTAG_IDENTITY-reservation.patch +Patch1952: 0003-Use-lower-level-headerPut-for-file-signing.patch +Patch1953: 0004-Place-file-signatures-into-the-signature-header-wher.patch +Patch1954: 0005-Unbreak-file-signing-from-previous-commit.patch +Patch1955: 0006-Assume-failure-in-rpmSignFiles.patch +Patch1956: 0007-Use-rpm-file-info-sets-instead-of-header-for-retriev.patch +Patch1957: 0008-Eliminate-redundant-signature-length-calculation-fun.patch +Patch1958: 0009-Drop-redundant-check-on-hash-algo-name.patch +Patch1959: 0010-Drop-redundant-check-on-hash-algo-name.patch +Patch1960: 0011-Generalize-file-signing-to-use-a-generic-flags-field.patch +Patch1961: 0012-Stop-adding-rpm-v3-header-payload-signatures-by-defa.patch +Patch1962: 0013-RPMTAG_PAYLOADDIGESTALT-is-not-backported-here-don-t.patch +Patch1963: 0014-Drop-support-for-dmalloc.patch +Patch1964: 0015-rpmsign-RPMSIGN_FLAG_IMA-is-already-set.patch +Patch1965: 0016-Add-basic-autoconf-and-framework-for-fsverity-suppor.patch +Patch1966: 0017-rpmsign-Add-helper-to-indicate-file-signing-enabled.patch +Patch1967: 0018-rpmsign-Handle-certpath-for-signing-certificate.patch +Patch1968: 0019-Implement-rpmSignVerity.patch +Patch1969: 0020-Introduce-base2bin-a-helper-to-convert-tag-array-of-.patch +Patch1970: 0021-rpmsignverity-Add-verity-signature-headers-to-the-pa.patch +Patch1971: 0022-rpmSignVerity-Generate-signatures-for-files-not-pres.patch +Patch1972: 0023-Process-verity-tag-on-package-read.patch +Patch1973: 0024-Generate-a-zero-length-signature-for-symlinks.patch +Patch1974: 0025-rpmsignverity.c-Clean-up-debug-logging.patch +Patch1975: 0026-fsverity-add-tag-for-fsverity-algorithm.patch +Patch1976: 0027-plugins-fsverity-Install-fsverity-signatures.patch +Patch1977: 0028-fsverity-plugin-Use-tag-for-algorithm.patch +Patch1978: 0029-Add-fsverity-tags-to-rpmgeneral.at.patch +Patch1979: 0030-Add-delfilesign-flag-to-delete-IMA-and-fsverity-file.patch +Patch1980: 0031-Update-man-page-for-rpmsign.patch +Patch1981: 0032-rpmsign-Add-argument-to-specify-algorithm-for-fsveri.patch +Patch1982: 0033-Enable-fsverity-in-CI.patch +%endif + Patch9998: https://github.com/rpm-software-management/rpm/pull/1381.patch Provides: rpm(pr1381) Patch9999: https://github.com/rpm-software-management/rpm/pull/1470.patch @@ -223,6 +262,10 @@ BuildRequires: libubsan BuildRequires: %{imadevname} >= 1.0 %endif +%if %{with libfsverity} +BuildRequires: fsverity-utils-devel >= 1.1 +%endif + %description The RPM Package Manager (RPM) is a powerful command line driven package management system capable of installing, uninstalling, @@ -398,6 +441,17 @@ Requires: rpm-libs%{_isa} = %{version}-%{release} %description plugin-ima %{summary} +%if %{with libfsverity} +%package plugin-fsverity +Summary: Rpm plugin fsverity file signatures +Group: System Environment/Base +Requires: rpm-libs%{_isa} = %{version}-%{release} + +%description plugin-fsverity +%{summary} + +%endif + %package plugin-prioreset Summary: Rpm plugin for resetting scriptlet priorities for SysV init Group: System Environment/Base @@ -471,6 +525,7 @@ done; %{?with_ndb: --enable-ndb} \ %{!?with_libarchive: --without-archive} \ %{?with_libimaevm: --with-imaevm} \ + %{?with_libfsverity: --with-fsverity} \ %{?with_zstd: --enable-zstd} \ %{?with_lmdb: --enable-lmdb} \ --with-fapolicyd \ @@ -616,6 +671,11 @@ make check || cat tests/rpmtests.log %if %{with plugins} %dir %{_libdir}/rpm-plugins +%if %{with libfsverity} +%files plugin-fsverity +%{_libdir}/rpm-plugins/fsverity.so +%endif + %files plugin-syslog %{_libdir}/rpm-plugins/syslog.so @@ -700,6 +760,9 @@ make check || cat tests/rpmtests.log %doc doc/librpm/html/* %changelog +* Tue Aug 03 2021 Matthew Almond - 4.14.3-15.2 +- Added fsverity backport + * Sat Jul 24 2021 Neal Gompa - 4.14.3-15.1 - Rebuild for Hyperscale