From b8562822b3c9505df5478638000a68bb12c4d13b Mon Sep 17 00:00:00 2001 From: gaoyusong Date: Wed, 8 Feb 2023 11:02:53 +0800 Subject: [PATCH] Add digest list plugin support Signed-off-by: gaoyusong --- 0001-Generate-digest-lists.patch | 323 ++++++++++ 0002-Add-digest-list-plugin.patch | 582 ++++++++++++++++++ ...st-to-release-if-it-is-already-there.patch | 121 ++++ ...lists-before-calling-genCpioListAndH.patch | 256 ++++++++ ...ss_digest_list-after-files-are-added.patch | 115 ++++ 0006-fix-lsetxattr-error-in-container.patch | 64 ++ ...inux-plugin-check-context-file-exist.patch | 26 + 0008-Fix-digest_list_counter.patch | 81 +++ 0009-Check-rpm-parser.patch | 29 + rpm.spec | 14 +- 10 files changed, 1610 insertions(+), 1 deletion(-) create mode 100644 0001-Generate-digest-lists.patch create mode 100644 0002-Add-digest-list-plugin.patch create mode 100644 0003-Don-t-add-dist-to-release-if-it-is-already-there.patch create mode 100644 0004-Generate-digest-lists-before-calling-genCpioListAndH.patch create mode 100644 0005-call-process_digest_list-after-files-are-added.patch create mode 100644 0006-fix-lsetxattr-error-in-container.patch create mode 100644 0007-rpm-selinux-plugin-check-context-file-exist.patch create mode 100644 0008-Fix-digest_list_counter.patch create mode 100644 0009-Check-rpm-parser.patch diff --git a/0001-Generate-digest-lists.patch b/0001-Generate-digest-lists.patch new file mode 100644 index 0000000..2a9e31c --- /dev/null +++ b/0001-Generate-digest-lists.patch @@ -0,0 +1,323 @@ +From 928b54fde56410c4615ac1b2a14727270a2d3692 Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Thu, 12 Mar 2020 17:29:55 +0100 +Subject: [PATCH 01/13] Generate digest lists + +--- + build/files.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 169 insertions(+), 6 deletions(-) + +diff --git a/build/files.c b/build/files.c +index 5ae20c6..dbac22f 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -50,6 +50,7 @@ + #define DEBUG_LIB_PREFIX "/usr/lib/debug/" + #define DEBUG_ID_DIR "/usr/lib/debug/.build-id" + #define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz" ++#define DIGEST_LIST_DIR "/.digest_lists" + + #undef HASHTYPE + #undef HTKEYTYPE +@@ -129,6 +130,8 @@ typedef struct AttrRec_s { + + /* list of files */ + static StringBuf check_fileList = NULL; ++/* list of files per binary package */ ++static StringBuf check_fileList_bin_pkg = NULL; + + typedef struct FileEntry_s { + rpmfileAttrs attrFlags; +@@ -193,6 +196,10 @@ typedef struct FileList_s { + struct FileEntry_s cur; + } * FileList; + ++static char *digest_list_dir; ++ ++static int genDigestList(Header header, FileList fl, StringBuf fileList); ++ + static void nullAttrRec(AttrRec ar) + { + memset(ar, 0, sizeof(*ar)); +@@ -996,11 +1003,14 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) + { + FileListRec flp; + char buf[BUFSIZ]; ++ char file_info[BUFSIZ]; ++ char file_digest[128 * 2 + 1]; + int i, npaths = 0; + int fail_on_dupes = rpmExpandNumeric("%{?_duplicate_files_terminate_build}") > 0; + uint32_t defaultalgo = RPM_HASH_MD5, digestalgo; + rpm_loff_t totalFileSize = 0; + Header h = pkg->header; /* just a shortcut */ ++ int processed = 0; + time_t source_date_epoch = 0; + char *srcdate = getenv("SOURCE_DATE_EPOCH"); + +@@ -1069,8 +1079,9 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) + + pkg->dpaths = xmalloc((fl->files.used + 1) * sizeof(*pkg->dpaths)); + ++process_files: + /* Generate the header. */ +- for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { ++ for (i = processed, flp = fl->files.recs + processed; i < fl->files.used; i++, flp++) { + rpm_ino_t fileid = flp - fl->files.recs; + + /* Merge duplicate entries. */ +@@ -1206,7 +1217,8 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) + (void) rpmDoDigest(digestalgo, flp->diskPath, 1, + (unsigned char *)buf); + headerPutString(h, RPMTAG_FILEDIGESTS, buf); +- ++ snprintf(file_digest, sizeof(file_digest), "%s", buf); ++ + buf[0] = '\0'; + if (S_ISLNK(flp->fl_mode)) { + ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1); +@@ -1246,7 +1258,33 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) + flp->flags &= PARSEATTR_MASK; + + headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1); ++ ++ if (!processed && check_fileList_bin_pkg && S_ISREG(flp->fl_mode) && ++ !(flp->flags & RPMFILE_GHOST)) { ++ appendStringBuf(check_fileList_bin_pkg, "path="); ++ appendStringBuf(check_fileList_bin_pkg, flp->diskPath); ++ snprintf(file_info, sizeof(file_info), ++ "|digestalgopgp=%d|digest=%s|mode=%d" ++ "|uname=%s|gname=%s|caps=%s\n", ++ digestalgo, file_digest, flp->fl_mode, ++ rpmstrPoolStr(fl->pool, flp->uname), ++ rpmstrPoolStr(fl->pool, flp->gname), flp->caps && ++ strlen(flp->caps) ? flp->caps : ""); ++ appendStringBuf(check_fileList_bin_pkg, file_info); ++ } ++ } ++ ++ if (!processed) { ++ if (genDigestList(pkg->header, fl, check_fileList_bin_pkg) > 0) { ++ fl->processingFailed = 1; ++ } else if (i < fl->files.used) { ++ pkg->dpaths = xrealloc(pkg->dpaths, ++ (fl->files.used + 1) * sizeof(*pkg->dpaths)); ++ processed = i; ++ goto process_files; ++ } + } ++ + pkg->dpaths[npaths] = NULL; + + if (totalFileSize < UINT32_MAX) { +@@ -1359,8 +1397,8 @@ static int validFilename(const char *fn) + * @param statp file stat (possibly NULL) + * @return RPMRC_OK on success + */ +-static rpmRC addFile(FileList fl, const char * diskPath, +- struct stat * statp) ++static rpmRC addFile_common(FileList fl, const char * diskPath, ++ struct stat * statp, int digest_list) + { + size_t plen = strlen(diskPath); + char buf[plen + 1]; +@@ -1371,6 +1409,10 @@ static rpmRC addFile(FileList fl, const char * diskPath, + gid_t fileGid; + const char *fileUname; + const char *fileGname; ++ char realPath[PATH_MAX]; ++ int digest_list_prefix = 0; ++ struct stat st; ++ int exclude = 0; + rpmRC rc = RPMRC_FAIL; /* assume failure */ + + /* Strip trailing slash. The special case of '/' path is handled below. */ +@@ -1406,6 +1448,33 @@ static rpmRC addFile(FileList fl, const char * diskPath, + if (*cpioPath == '\0') + cpioPath = "/"; + ++ snprintf(realPath, sizeof(realPath), "%s", diskPath); ++ rpmCleanPath(realPath); ++ ++ digest_list_prefix = (!strncmp(realPath, digest_list_dir, ++ strlen(digest_list_dir))); ++ ++ if ((!digest_list && digest_list_prefix) || ++ (digest_list && !digest_list_prefix)) { ++ rc = RPMRC_OK; ++ goto exit; ++ } ++ ++ if (digest_list) { ++ if (strncmp(cpioPath, DIGEST_LIST_DIR, sizeof(DIGEST_LIST_DIR) - 1)) { ++ rc = RPMRC_OK; ++ goto exit; ++ } ++ ++ cpioPath += sizeof(DIGEST_LIST_DIR) - 1; ++ ++ snprintf(realPath, sizeof(realPath), "%.*s%s", ++ (int)(strlen(digest_list_dir) - sizeof(DIGEST_LIST_DIR) + 1), ++ digest_list_dir, cpioPath); ++ if (!stat(realPath, &st)) ++ exclude = 1; ++ } ++ + /* + * Unless recursing, we dont have stat() info at hand. Handle the + * various cases, preserving historical behavior wrt %dev(): +@@ -1543,6 +1612,8 @@ static rpmRC addFile(FileList fl, const char * diskPath, + } + + flp->flags = fl->cur.attrFlags; ++ if (exclude) ++ flp->flags |= RPMFILE_EXCLUDE; + flp->specdFlags = fl->cur.specdFlags; + flp->verifyFlags = fl->cur.verifyFlags; + +@@ -1563,6 +1634,32 @@ exit: + return rc; + } + ++/** ++ * Add a file to the package manifest. ++ * @param fl package file tree walk data ++ * @param diskPath path to file ++ * @param statp file stat (possibly NULL) ++ * @return RPMRC_OK on success ++ */ ++static rpmRC addFile(FileList fl, const char * diskPath, ++ struct stat * statp) ++{ ++ return addFile_common(fl, diskPath, statp, 0); ++} ++ ++/** ++ * Add a digest list to the package manifest. ++ * @param fl package file tree walk data ++ * @param diskPath path to digest list ++ * @param statp file stat (possibly NULL) ++ * @return RPMRC_OK on success ++ */ ++static rpmRC addDigestList(FileList fl, const char * diskPath, ++ struct stat * statp) ++{ ++ return addFile_common(fl, diskPath, statp, 1); ++} ++ + /** + * Add directory (and all of its files) to the package manifest. + * @param fl package file tree walk data +@@ -2584,6 +2681,58 @@ static void addPackageFileList (struct FileList_s *fl, Package pkg, + argvFree(fileNames); + } + ++/** ++ * Generate digest lists list for current binary package. ++ * @header package header ++ * @fl file list ++ * @param fileList packaged file list ++ * @return -1 if skipped, 0 on OK, 1 on error ++ */ ++static int genDigestList(Header header, FileList fl, StringBuf fileList) ++{ ++ const char *errorString; ++ char *binFormat = rpmGetPath("%{_rpmfilename}", NULL); ++ char *binRpm = headerFormat(header, binFormat, &errorString); ++ static char * av_brp[] = { "%{?__brp_digest_list}", DIGEST_LIST_DIR + 1, NULL, NULL }; ++ StringBuf sb_stdout = NULL; ++ int rc = -1; ++ char * s = rpmExpand(av_brp[0], NULL); ++ ++ if (!(s && *s)) ++ goto exit; ++ ++ av_brp[2] = strchr(binRpm, '/') + 1; ++ rpmlog(RPMLOG_NOTICE, _("Generating digest list: %s\n"), s); ++ ++ rc = rpmfcExec(av_brp, fileList, &sb_stdout, 0, binRpm); ++ if (sb_stdout && getStringBuf(sb_stdout)) { ++ const char * t = getStringBuf(sb_stdout), *ptr; ++ char *digest_list_path; ++ ++ while((ptr = strchr(t, '\n'))) { ++ digest_list_path = strndup(t, ptr - t); ++ if (!digest_list_path) { ++ rc = -1; ++ goto exit; ++ } ++ FileEntryFree(&fl->cur); ++ resetPackageFilesDefaults(fl, fl->pkgFlags); ++ rc = addDigestList(fl, digest_list_path, NULL); ++ free(digest_list_path); ++ if (rc != RPMRC_OK) ++ break; ++ ++ t = ptr + 1; ++ } ++ } ++exit: ++ free(binFormat); ++ free(binRpm); ++ free(s); ++ freeStringBuf(sb_stdout); ++ return rc; ++} ++ + static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + Package pkg, int didInstall, int test) + { +@@ -2597,6 +2746,10 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + if (readFilesManifest(spec, pkg, *fp)) + return RPMRC_FAIL; + } ++ ++ /* Init the buffer containing the list of packaged files */ ++ check_fileList_bin_pkg = newStringBuf(); ++ + /* Init the file list structure */ + memset(&fl, 0, sizeof(fl)); + +@@ -2658,6 +2811,7 @@ exit: + FileListFree(&fl); + specialDirFree(specialDoc); + specialDirFree(specialLic); ++ freeStringBuf(check_fileList_bin_pkg); + return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK; + } + +@@ -3126,6 +3280,7 @@ static void addPackageDeps(Package from, Package to, enum rpmTag_e tag) + rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + int didInstall, int test) + { ++ struct stat st; + Package pkg; + rpmRC rc = RPMRC_OK; + char *buildroot; +@@ -3142,7 +3297,14 @@ rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + check_fileList = newStringBuf(); + genSourceRpmName(spec); + buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); +- ++ ++ digest_list_dir = rpmGenPath(buildroot, DIGEST_LIST_DIR, NULL); ++ if (!digest_list_dir) ++ goto exit; ++ ++ if (!stat(digest_list_dir, &st)) ++ rpmlog(RPMLOG_NOTICE, _("Ignoring files in: %s\n"), digest_list_dir); ++ + if (rpmExpandNumeric("%{?_debuginfo_subpackages}")) { + maindbg = findDebuginfoPackage(spec); + if (maindbg) { +@@ -3248,6 +3410,7 @@ exit: + check_fileList = freeStringBuf(check_fileList); + _free(buildroot); + _free(uniquearch); +- ++ _free(digest_list_dir); ++ + return rc; + } +-- +2.33.0 + diff --git a/0002-Add-digest-list-plugin.patch b/0002-Add-digest-list-plugin.patch new file mode 100644 index 0000000..1505626 --- /dev/null +++ b/0002-Add-digest-list-plugin.patch @@ -0,0 +1,582 @@ +From 6d01e903dd47c852fc2b277b17195da92e5d3ffe Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Wed, 26 Feb 2020 15:54:24 +0100 +Subject: [PATCH 02/13] Add digest list plugin + +--- + macros.in | 1 + + plugins/Makefile.am | 4 + + plugins/digest_list.c | 499 ++++++++++++++++++++++++++++++++++++++++ + rpmio/rpmpgp_internal.c | 5 +- + 4 files changed, 508 insertions(+), 1 deletion(-) + create mode 100644 plugins/digest_list.c + +diff --git a/macros.in b/macros.in +index 949fd7d..c00d270 100644 +--- a/macros.in ++++ b/macros.in +@@ -1135,6 +1135,7 @@ package or when debugging this package.\ + %__transaction_prioreset %{__plugindir}/prioreset.so + %__transaction_audit %{__plugindir}/audit.so + %__transaction_dbus_announce %{__plugindir}/dbus_announce.so ++%__transaction_digest_list %{__plugindir}/digest_list.so + + #------------------------------------------------------------------------------ + # Macros for further automated spec %setup and patch application +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index 822c7d2..161fe4c 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -69,3 +69,7 @@ audit_la_sources = audit.c + audit_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la @WITH_AUDIT_LIB@ + plugins_LTLIBRARIES += audit.la + endif ++ ++digest_list_la_sources = digest_list.c ++digest_list_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la ++plugins_LTLIBRARIES += digest_list.la +diff --git a/plugins/digest_list.c b/plugins/digest_list.c +new file mode 100644 +index 0000000..293593f +--- /dev/null ++++ b/plugins/digest_list.c +@@ -0,0 +1,499 @@ ++#include "system.h" ++#include "errno.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "lib/rpmplugin.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++ ++#define IMA_DIR "/sys/kernel/security/ima" ++#define DIGEST_LIST_DATA_PATH IMA_DIR "/digest_list_data" ++#define DIGEST_LIST_DATA_DEL_PATH IMA_DIR "/digest_list_data_del" ++#define DIGEST_LIST_COUNT IMA_DIR "/digests_count" ++#define DIGEST_LIST_DEFAULT_PATH "/etc/ima/digest_lists" ++#define RPM_PARSER "/usr/libexec/rpm_parser" ++ ++#define DIGEST_LIST_OP_ADD 0 ++#define DIGEST_LIST_OP_DEL 1 ++ ++enum hash_algo { ++ HASH_ALGO_MD4, ++ HASH_ALGO_MD5, ++ HASH_ALGO_SHA1, ++ HASH_ALGO_RIPE_MD_160, ++ HASH_ALGO_SHA256, ++ HASH_ALGO_SHA384, ++ HASH_ALGO_SHA512, ++ HASH_ALGO_SHA224, ++ HASH_ALGO_RIPE_MD_128, ++ HASH_ALGO_RIPE_MD_256, ++ HASH_ALGO_RIPE_MD_320, ++ HASH_ALGO_WP_256, ++ HASH_ALGO_WP_384, ++ HASH_ALGO_WP_512, ++ HASH_ALGO_TGR_128, ++ HASH_ALGO_TGR_160, ++ HASH_ALGO_TGR_192, ++ HASH_ALGO_SM3_256, ++ HASH_ALGO__LAST ++}; ++ ++#define PGPHASHALGO__LAST PGPHASHALGO_SHA224 + 1 ++enum hash_algo pgp_algo_mapping[PGPHASHALGO__LAST] = { ++ [PGPHASHALGO_MD5] = HASH_ALGO_MD5, ++ [PGPHASHALGO_SHA1] = HASH_ALGO_SHA1, ++ [PGPHASHALGO_SHA224] = HASH_ALGO_SHA224, ++ [PGPHASHALGO_SHA256] = HASH_ALGO_SHA256, ++ [PGPHASHALGO_SHA384] = HASH_ALGO_SHA384, ++ [PGPHASHALGO_SHA512] = HASH_ALGO_SHA512, ++}; ++ ++/* from integrity.h */ ++enum evm_ima_xattr_type { ++ IMA_XATTR_DIGEST = 0x01, ++ EVM_XATTR_HMAC, ++ EVM_IMA_XATTR_DIGSIG, ++ IMA_XATTR_DIGEST_NG, ++ EVM_XATTR_PORTABLE_DIGSIG, ++ EVM_IMA_XATTR_DIGEST_LIST, ++ IMA_XATTR_LAST ++}; ++ ++struct evm_ima_xattr_data { ++ uint8_t type; ++ uint8_t digest[SHA512_DIGEST_LENGTH + 1]; ++} __attribute__((packed)); ++ ++struct signature_v2_hdr { ++ uint8_t type; /* xattr type */ ++ uint8_t version; /* signature format version */ ++ uint8_t hash_algo; /* Digest algorithm [enum hash_algo] */ ++ __be32 keyid; /* IMA key identifier - not X509/PGP specific */ ++ __be16 sig_size; /* signature size */ ++ uint8_t sig[0]; /* signature payload */ ++} __attribute__((packed)); ++ ++static int upload_digest_list(char *path, int type, int digest_list_signed) ++{ ++ size_t size; ++ char buf[21]; ++ const char *ima_path = DIGEST_LIST_DATA_PATH; ++ struct stat st; ++ pid_t pid; ++ int ret = 0, fd; ++ ++ if (type == TR_REMOVED) ++ ima_path = DIGEST_LIST_DATA_DEL_PATH; ++ ++ if (stat(ima_path, &st) == -1) ++ return 0; ++ ++ /* First determine if kernel interface can accept new digest lists */ ++ fd = open(DIGEST_LIST_COUNT, O_RDONLY); ++ if (fd < 0) { ++ rpmlog(RPMLOG_ERR, "digest_list: could not open IMA interface " ++ "'%s': %s\n", DIGEST_LIST_COUNT, strerror(errno)); ++ return -EACCES; ++ } ++ ++ ret = read(fd, buf, sizeof(buf)); ++ close(fd); ++ ++ if (ret <= 0) { ++ rpmlog(RPMLOG_ERR, "digest_list: could not read from IMA " ++ "interface '%s': %s\n", DIGEST_LIST_COUNT, ++ strerror(errno)); ++ return -EACCES; ++ } ++ ++ /* Last character is newline */ ++ buf[ret - 1] = '\0'; ++ ++ rpmlog(RPMLOG_DEBUG, "digest_list: digests count %s\n", buf); ++ ++ if (*buf == '0') { ++ rpmlog(RPMLOG_DEBUG, "digest_list: not uploading '%s' to IMA " ++ "interface '%s'\n", path, ima_path); ++ return RPMRC_OK; ++ } ++ ++ /* If the digest list is not signed, execute the RPM parser */ ++ if (!digest_list_signed) { ++ if ((pid = fork()) == 0) { ++ execlp(RPM_PARSER, RPM_PARSER, (type == TR_ADDED) ? ++ "add" : "del", path, NULL); ++ _exit(EXIT_FAILURE); ++ } ++ ++ waitpid(pid, &ret, 0); ++ if (ret != 0) ++ rpmlog(RPMLOG_ERR, "digest_list: %s returned %d\n", ++ RPM_PARSER, ret); ++ return 0; ++ } ++ ++ fd = open(ima_path, O_WRONLY); ++ if (fd < 0) { ++ rpmlog(RPMLOG_ERR, "digest_list: could not open IMA interface " ++ "'%s': %s\n", ima_path, strerror(errno)); ++ return -EACCES; ++ } ++ ++ /* Write the path of the digest list to securityfs */ ++ size = write(fd, path, strlen(path)); ++ if (size != strlen(path)) { ++ rpmlog(RPMLOG_ERR, "digest_list: could not write '%s' to IMA " ++ "interface '%s': %s\n", path, ima_path, strerror(errno)); ++ ret = -EIO; ++ goto out; ++ } ++ ++ rpmlog(RPMLOG_DEBUG, "digest_list: written '%s' to '%s'\n", path, ++ ima_path); ++out: ++ close(fd); ++ return ret; ++} ++ ++static int write_rpm_digest_list(rpmte te, char *path) ++{ ++ FD_t fd; ++ ssize_t written; ++ Header rpm = rpmteHeader(te); ++ rpmtd immutable; ++ int ret = 0; ++ ++ immutable = rpmtdNew(); ++ headerGet(rpm, RPMTAG_HEADERIMMUTABLE, immutable, 0); ++ ++ fd = Fopen(path, "w.ufdio"); ++ if (fd == NULL || Ferror(fd)) { ++ ret = -EACCES; ++ goto out; ++ } ++ ++ written = Fwrite(rpm_header_magic, sizeof(uint8_t), ++ sizeof(rpm_header_magic), fd); ++ ++ if (written != sizeof(rpm_header_magic)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ written = Fwrite(immutable->data, sizeof(uint8_t), ++ immutable->count, fd); ++ if (written != immutable->count || Ferror(fd)) ++ ret = -EIO; ++out: ++ Fclose(fd); ++ rpmtdFree(immutable); ++ return ret; ++} ++ ++static int write_rpm_digest_list_ima_xattr(rpmte te, char *path) ++{ ++ rpmtd signature; ++ ssize_t written; ++ uint8_t sig[2048] = { 0 }; ++ pgpDigParams sigp = NULL; ++ struct signature_v2_hdr *sig_hdr = (struct signature_v2_hdr *)sig; ++ Header rpm = rpmteHeader(te); ++ FD_t fd; ++ int ret = 0, sig_size, sig_size_rounded; ++ ++ signature = rpmtdNew(); ++ headerGet(rpm, RPMTAG_RSAHEADER, signature, 0); ++ ret = pgpPrtParams(signature->data, signature->count, ++ PGPTAG_SIGNATURE, &sigp); ++ ++ if (ret) { ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ fd = Fopen(path, "a.ufdio"); ++ if (fd == NULL || Ferror(fd)) { ++ ret = -EACCES; ++ goto out; ++ } ++ ++ written = Fwrite(sigp->hash, sizeof(uint8_t), ++ sigp->hashlen, fd); ++ if (written != sigp->hashlen || Ferror(fd)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ if (sigp->version == 4) { ++ /* V4 trailer is six octets long (rfc4880) */ ++ uint8_t trailer[6]; ++ uint32_t nb = sigp->hashlen; ++ nb = htonl(nb); ++ trailer[0] = sigp->version; ++ trailer[1] = 0xff; ++ memcpy(trailer+2, &nb, 4); ++ ++ written = Fwrite(trailer, sizeof(uint8_t), sizeof(trailer), fd); ++ if (written != sizeof(trailer) || Ferror(fd)) { ++ ret = -EIO; ++ goto out; ++ } ++ } ++ ++ Fclose(fd); ++ ++ sig_hdr->type = EVM_IMA_XATTR_DIGSIG; ++ sig_hdr->version = 2; ++ sig_hdr->hash_algo = pgp_algo_mapping[sigp->hash_algo]; ++ memcpy((void *)&sig_hdr->keyid, sigp->signid + sizeof(uint32_t), ++ sizeof(uint32_t)); ++ ++ sig_size = (pgpMpiBits(sigp->data) + 7) >> 3; ++ if (sizeof(sig_hdr) + sig_size > sizeof(sig)) { ++ rpmlog(RPMLOG_ERR, ++ "digest_list: signature in %s too big\n", path); ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ sig_size_rounded = ((sig_size + 7) >> 3) * 8; ++ sig_hdr->sig_size = __cpu_to_be16(sig_size_rounded); ++ ++ memcpy(sig_hdr->sig + sig_size_rounded - sig_size, ++ (uint8_t *)sigp->data + 2, sig_size); ++ ++ ret = lsetxattr(path, XATTR_NAME_IMA, ++ sig, sizeof(*sig_hdr) + sig_size_rounded, 0); ++ if (ret < 0) ++ rpmlog(RPMLOG_ERR, "digest_list: could not apply security.ima " ++ "on '%s': %s\n", path, strerror(errno)); ++ else ++ rpmlog(RPMLOG_DEBUG, "digest_list: security.ima successfully " ++ "applied on '%s'\n", path); ++out: ++ pgpDigParamsFree(sigp); ++ rpmtdFree(signature); ++ return ret; ++} ++ ++static int write_digest_list_ima_xattr(rpmte te, char *path, char *path_sig) ++{ ++ rpmtd signature; ++ uint8_t sig[2048] = { 0 }; ++ pgpDigParams sigp = NULL; ++ struct signature_v2_hdr *sig_hdr = (struct signature_v2_hdr *)sig; ++ Header rpm = rpmteHeader(te); ++ FD_t fd; ++ struct stat st; ++ int ret = 0, sig_size; ++ ++ signature = rpmtdNew(); ++ headerGet(rpm, RPMTAG_RSAHEADER, signature, 0); ++ ret = pgpPrtParams(signature->data, signature->count, ++ PGPTAG_SIGNATURE, &sigp); ++ ++ if (ret) { ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ sig_hdr->type = EVM_IMA_XATTR_DIGSIG; ++ sig_hdr->version = 2; ++ sig_hdr->hash_algo = HASH_ALGO_SHA256; ++ memcpy((void *)&sig_hdr->keyid, sigp->signid + sizeof(uint32_t), ++ sizeof(uint32_t)); ++ ++ if (stat(path_sig, &st) == -1) { ++ ret = -EACCES; ++ goto out; ++ } ++ ++ if (sizeof(sig_hdr) + st.st_size > sizeof(sig)) { ++ rpmlog(RPMLOG_ERR, "digest_list: signature in %s too big\n", ++ path); ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ fd = Fopen(path_sig, "r.ufdio"); ++ if (fd < 0) { ++ rpmlog(RPMLOG_ERR, "digest_list: could not open '%s': %s\n", ++ path_sig, strerror(errno)); ++ ret = -EACCES; ++ goto out; ++ } ++ ++ sig_size = Fread(sig_hdr->sig, sizeof(uint8_t), st.st_size, fd); ++ if (sig_size != st.st_size || Ferror(fd)) { ++ rpmlog(RPMLOG_ERR, "digest_list: could not read '%s': %s\n", ++ path_sig, strerror(errno)); ++ Fclose(fd); ++ ret = -EIO; ++ goto out; ++ } ++ ++ sig_hdr->sig_size = __cpu_to_be16(sig_size); ++ ++ rpmlog(RPMLOG_DEBUG, ++ "digest_list: read signature of %d bytes from '%s'\n", ++ sig_size, path_sig); ++ ++ ret = lsetxattr(path, XATTR_NAME_IMA, ++ sig, sizeof(*sig_hdr) + sig_size, 0); ++ if (ret < 0) ++ rpmlog(RPMLOG_ERR, "digest_list: could not apply security.ima " ++ "on '%s': %s\n", path, strerror(errno)); ++ else ++ rpmlog(RPMLOG_DEBUG, "digest_list: security.ima successfully " ++ "applied on '%s'\n", path); ++out: ++ pgpDigParamsFree(sigp); ++ rpmtdFree(signature); ++ return ret; ++} ++ ++static int process_digest_list(rpmte te, int parser) ++{ ++ char *path = NULL, *path_sig = NULL; ++ int digest_list_signed = 0; ++ struct stat st; ++ ssize_t size; ++ rpmRC ret = RPMRC_OK; ++ ++ path = malloc(PATH_MAX); ++ if (!path) { ++ ret = RPMRC_FAIL; ++ goto out; ++ } ++ ++ path_sig = malloc(PATH_MAX); ++ if (!path_sig) { ++ ret = RPMRC_FAIL; ++ goto out; ++ } ++ ++ if (parser) ++ snprintf(path_sig, PATH_MAX, ++ "%s.sig/0-parser_list-compact-libexec.sig", ++ DIGEST_LIST_DEFAULT_PATH); ++ else ++ snprintf(path_sig, PATH_MAX, ++ "%s.sig/0-metadata_list-compact-%s-%s-%s.%s.sig", ++ DIGEST_LIST_DEFAULT_PATH, rpmteN(te), rpmteV(te), ++ rpmteR(te), rpmteA(te)); ++ ++ if (!stat(path_sig, &st)) ++ digest_list_signed = 1; ++ ++ if (parser && !digest_list_signed) ++ goto out; ++ ++ if (parser) ++ snprintf(path, PATH_MAX, "%s/0-parser_list-compact-libexec", ++ DIGEST_LIST_DEFAULT_PATH); ++ else ++ snprintf(path, PATH_MAX, ++ "%s/0-metadata_list-compact-%s-%s-%s.%s", ++ DIGEST_LIST_DEFAULT_PATH, rpmteN(te), rpmteV(te), ++ rpmteR(te), rpmteA(te)); ++ ++ if (stat(path, &st) == -1) ++ goto out; ++ ++ if (!parser && !digest_list_signed) ++ snprintf(path, PATH_MAX, "%s/0-metadata_list-rpm-%s-%s-%s.%s", ++ DIGEST_LIST_DEFAULT_PATH, rpmteN(te), rpmteV(te), ++ rpmteR(te), rpmteA(te)); ++ ++ size = lgetxattr(path, XATTR_NAME_IMA, NULL, 0); ++ ++ /* Don't upload again if digest list was already processed */ ++ if ((rpmteType(te) == TR_ADDED && size > 0) || ++ (rpmteType(te) == TR_REMOVED && size < 0)) { ++ rpmlog(RPMLOG_DEBUG, "digest_list: '%s' already processed, " ++ "nothing to do\n", path); ++ goto out; ++ } ++ ++ if (rpmteType(te) == TR_ADDED) { ++ if (!digest_list_signed) { ++ /* Write RPM header to the disk */ ++ ret = write_rpm_digest_list(te, path); ++ if (ret < 0) { ++ ret = RPMRC_FAIL; ++ goto out; ++ } ++ ++ /* Write RPM header sig to security.ima */ ++ ret = write_rpm_digest_list_ima_xattr(te, path); ++ } else { ++ ret = write_digest_list_ima_xattr(te, path, path_sig); ++ } ++ ++ if (ret < 0) { ++ ret = RPMRC_FAIL; ++ goto out; ++ } ++ } ++ ++ /* Upload digest list to securityfs */ ++ upload_digest_list(path, rpmteType(te), digest_list_signed); ++ ++ if (rpmteType(te) == TR_REMOVED) { ++ if (!digest_list_signed) { ++ unlink(path); ++ goto out; ++ } ++ ++ ret = lremovexattr(path, XATTR_NAME_IMA); ++ if (ret < 0) ++ rpmlog(RPMLOG_ERR, "digest_list: cannot remove " ++ "security.ima from '%s'\n", path); ++ else ++ rpmlog(RPMLOG_DEBUG, "digest_list: security.ima " ++ "successfully removed from '%s'\n", path); ++ } ++out: ++ free(path); ++ free(path_sig); ++ return ret; ++} ++ ++static rpmRC digest_list_psm_pre(rpmPlugin plugin, rpmte te) ++{ ++ process_digest_list(te, 0); ++ if (!strcmp(rpmteN(te), "digest-list-tools")) ++ process_digest_list(te, 1); ++ ++ return RPMRC_OK; ++} ++ ++static rpmRC digest_list_psm_post(rpmPlugin plugin, rpmte te, int res) ++{ ++ if (res != RPMRC_OK) ++ return RPMRC_OK; ++ ++ process_digest_list(te, 0); ++ if (!strcmp(rpmteN(te), "digest-list-tools")) ++ process_digest_list(te, 1); ++ ++ return RPMRC_OK; ++} ++ ++struct rpmPluginHooks_s digest_list_hooks = { ++ .psm_pre = digest_list_psm_pre, ++ .psm_post = digest_list_psm_post, ++}; +diff --git a/rpmio/rpmpgp_internal.c b/rpmio/rpmpgp_internal.c +index 19947be..11b6855 100644 +--- a/rpmio/rpmpgp_internal.c ++++ b/rpmio/rpmpgp_internal.c +@@ -25,6 +25,7 @@ static int _print = 0; + struct pgpDigParams_s { + char * userid; + uint8_t * hash; ++ const uint8_t * data; + uint8_t tag; + + uint8_t key_flags; /*!< key usage flags */ +@@ -484,6 +485,7 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, + } + + p = ((uint8_t *)v) + sizeof(*v); ++ _digp->data = p; + rc = tag ? pgpPrtSigParams(tag, v->pubkey_algo, p, h, hlen, _digp) : 0; + } break; + case 4: +@@ -545,7 +547,7 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, + p += 2; + if (p > hend) + return 1; +- ++ _digp->data = p; + rc = tag ? pgpPrtSigParams(tag, v->pubkey_algo, p, h, hlen, _digp) : 0; + } break; + default: +@@ -636,6 +638,7 @@ static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, + } + + p = ((uint8_t *)v) + sizeof(*v); ++ _digp->data = p; + rc = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _digp); + } + } break; +-- +2.33.0 + diff --git a/0003-Don-t-add-dist-to-release-if-it-is-already-there.patch b/0003-Don-t-add-dist-to-release-if-it-is-already-there.patch new file mode 100644 index 0000000..16d8818 --- /dev/null +++ b/0003-Don-t-add-dist-to-release-if-it-is-already-there.patch @@ -0,0 +1,121 @@ +From 9c7c63275682c36d6adff90b4b4436ed546ee268 Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Wed, 22 Jul 2020 17:24:58 +0200 +Subject: [PATCH 03/13] Don't add dist to release if it is already there + +--- + build/parsePreamble.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/build/parsePreamble.c b/build/parsePreamble.c +index 729fd4f..306a029 100644 +--- a/build/parsePreamble.c ++++ b/build/parsePreamble.c +@@ -801,7 +801,8 @@ static rpmRC handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag, + SINGLE_TOKEN_ONLY; + if (tag == RPMTAG_RELEASE) { + char *dist = rpmExpand("%{?dist}",NULL); +- rasprintf(&field,"%s%s",field,dist); ++ rasprintf(&field,"%s%s",field, ++ (dist && strstr(field, dist)) ? "" : dist); + free(dist); + } + if (rpmCharCheck(spec, field, ALLOWED_CHARS_VERREL, NULL)) +diff --git a/plugins/digest_list.c b/plugins/digest_list.c +index 293593f..f68ec8c 100644 +--- a/plugins/digest_list.c ++++ b/plugins/digest_list.c +@@ -5,8 +5,8 @@ + #include + #include + #include +-#include +-#include ++#include ++#include "rpmio/rpmpgp_internal.h" + #include + #include "lib/rpmplugin.h" + #include +diff --git a/rpmio/rpmpgp_internal.c b/rpmio/rpmpgp_internal.c +index 11b6855..16bf57e 100644 +--- a/rpmio/rpmpgp_internal.c ++++ b/rpmio/rpmpgp_internal.c +@@ -19,35 +19,6 @@ + + static int _print = 0; + +-/** \ingroup rpmio +- * Values parsed from OpenPGP signature/pubkey packet(s). +- */ +-struct pgpDigParams_s { +- char * userid; +- uint8_t * hash; +- const uint8_t * data; +- uint8_t tag; +- +- uint8_t key_flags; /*!< key usage flags */ +- uint8_t version; /*!< version number. */ +- uint32_t time; /*!< key/signature creation time. */ +- uint8_t pubkey_algo; /*!< public key algorithm. */ +- +- uint8_t hash_algo; +- uint8_t sigtype; +- uint32_t hashlen; +- uint8_t signhash16[2]; +- pgpKeyID_t signid; +- uint8_t saved; /*!< Various flags. `PGPDIG_SAVED_*` are never reset. +- * `PGPDIG_SIG_HAS_*` are reset for each signature. */ +-#define PGPDIG_SAVED_TIME (1 << 0) +-#define PGPDIG_SAVED_ID (1 << 1) +-#define PGPDIG_SIG_HAS_CREATION_TIME (1 << 2) +-#define PGPDIG_SIG_HAS_KEY_FLAGS (1 << 3) +- +- pgpDigAlg alg; +-}; +- + /** \ingroup rpmio + * Container for values parsed from an OpenPGP signature and public key. + */ +diff --git a/rpmio/rpmpgp_internal.h b/rpmio/rpmpgp_internal.h +index 64b50de..67fecb0 100644 +--- a/rpmio/rpmpgp_internal.h ++++ b/rpmio/rpmpgp_internal.h +@@ -10,6 +10,35 @@ typedef int (*verifyfunc)(pgpDigAlg pgpkey, pgpDigAlg pgpsig, + uint8_t *hash, size_t hashlen, int hash_algo); + typedef void (*freefunc)(pgpDigAlg digp); + ++/** \ingroup rpmio ++ * Values parsed from OpenPGP signature/pubkey packet(s). ++ */ ++struct pgpDigParams_s { ++ char * userid; ++ uint8_t * hash; ++ const uint8_t * data; ++ uint8_t tag; ++ ++ uint8_t key_flags; /*!< key usage flags */ ++ uint8_t version; /*!< version number. */ ++ uint32_t time; /*!< key/signature creation time. */ ++ uint8_t pubkey_algo; /*!< public key algorithm. */ ++ ++ uint8_t hash_algo; ++ uint8_t sigtype; ++ uint32_t hashlen; ++ uint8_t signhash16[2]; ++ pgpKeyID_t signid; ++ uint8_t saved; /*!< Various flags. `PGPDIG_SAVED_*` are never reset. ++ * `PGPDIG_SIG_HAS_*` are reset for each signature. */ ++#define PGPDIG_SAVED_TIME (1 << 0) ++#define PGPDIG_SAVED_ID (1 << 1) ++#define PGPDIG_SIG_HAS_CREATION_TIME (1 << 2) ++#define PGPDIG_SIG_HAS_KEY_FLAGS (1 << 3) ++ ++ pgpDigAlg alg; ++}; ++ + struct pgpDigAlg_s { + setmpifunc setmpi; + verifyfunc verify; +-- +2.33.0 + diff --git a/0004-Generate-digest-lists-before-calling-genCpioListAndH.patch b/0004-Generate-digest-lists-before-calling-genCpioListAndH.patch new file mode 100644 index 0000000..0dd7447 --- /dev/null +++ b/0004-Generate-digest-lists-before-calling-genCpioListAndH.patch @@ -0,0 +1,256 @@ +From 670d639bfde427a4f3bb9a9f0350d581cacf6fdb Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Wed, 12 Aug 2020 18:23:42 +0200 +Subject: [PATCH 04/13] Generate digest lists before calling + genCpioListAndHeader() + +Signed-off-by: luhuaxin +--- + build/files.c | 182 ++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 147 insertions(+), 35 deletions(-) + +diff --git a/build/files.c b/build/files.c +index dbac22f..84858b6 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -999,20 +999,149 @@ static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid) + * @param pkg (sub) package + * @param isSrc pass 1 for source packages 0 otherwise + */ +-static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) ++static void genDigestListInput(FileList fl, Package pkg, int isSrc) + { + FileListRec flp; + char buf[BUFSIZ]; + char file_info[BUFSIZ]; + char file_digest[128 * 2 + 1]; ++ int i; ++ uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo; ++ Header h = pkg->header; /* just a shortcut */ ++ ++ /* ++ * See if non-md5 file digest algorithm is requested. If not ++ * specified, quietly assume md5. Otherwise check if supported type. ++ */ ++ digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" : ++ "%{_binary_filedigest_algorithm}"); ++ if (digestalgo == 0) { ++ digestalgo = defaultalgo; ++ } ++ ++ if (rpmDigestLength(digestalgo) == 0) { ++ rpmlog(RPMLOG_WARNING, ++ _("Unknown file digest algorithm %u, falling back to MD5\n"), ++ digestalgo); ++ digestalgo = defaultalgo; ++ } ++ ++ /* Sort the big list */ ++ if (fl->files.recs) { ++ qsort(fl->files.recs, fl->files.used, ++ sizeof(*(fl->files.recs)), compareFileListRecs); ++ } ++ ++ /* Generate the header. */ ++ for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { ++ /* Merge duplicate entries. */ ++ while (i < (fl->files.used - 1) && ++ rstreq(flp->cpioPath, flp[1].cpioPath)) { ++ ++ /* Two entries for the same file found, merge the entries. */ ++ /* Note that an %exclude is a duplication of a file reference */ ++ ++ /* file flags */ ++ flp[1].flags |= flp->flags; ++ ++ if (!(flp[1].flags & RPMFILE_EXCLUDE)) ++ rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"), ++ flp->cpioPath); ++ ++ /* file mode */ ++ if (S_ISDIR(flp->fl_mode)) { ++ if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) < ++ (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE))) ++ flp[1].fl_mode = flp->fl_mode; ++ } else { ++ if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) < ++ (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE))) ++ flp[1].fl_mode = flp->fl_mode; ++ } ++ ++ /* uid */ ++ if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) < ++ (flp->specdFlags & (SPECD_UID | SPECD_DEFUID))) ++ { ++ flp[1].fl_uid = flp->fl_uid; ++ flp[1].uname = flp->uname; ++ } ++ ++ /* gid */ ++ if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) < ++ (flp->specdFlags & (SPECD_GID | SPECD_DEFGID))) ++ { ++ flp[1].fl_gid = flp->fl_gid; ++ flp[1].gname = flp->gname; ++ } ++ ++ /* verify flags */ ++ if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) < ++ (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY))) ++ flp[1].verifyFlags = flp->verifyFlags; ++ ++ /* XXX to-do: language */ ++ ++ flp++; i++; ++ } ++ ++ /* Skip files that were marked with %exclude. */ ++ if (flp->flags & RPMFILE_EXCLUDE) ++ { ++ argvAdd(&pkg->fileExcludeList, flp->cpioPath); ++ continue; ++ } ++ ++ buf[0] = '\0'; ++ if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST)) ++ (void) rpmDoDigest(digestalgo, flp->diskPath, 1, ++ (unsigned char *)buf); ++ headerPutString(h, RPMTAG_FILEDIGESTS, buf); ++ snprintf(file_digest, sizeof(file_digest), "%s", buf); ++ ++ if (check_fileList_bin_pkg && S_ISREG(flp->fl_mode) && ++ !(flp->flags & RPMFILE_GHOST)) { ++ appendStringBuf(check_fileList_bin_pkg, "path="); ++ appendStringBuf(check_fileList_bin_pkg, flp->diskPath); ++ snprintf(file_info, sizeof(file_info), ++ "|digestalgopgp=%d|digest=%s|mode=%d" ++ "|uname=%s|gname=%s|caps=%s\n", ++ digestalgo, file_digest, flp->fl_mode, ++ rpmstrPoolStr(fl->pool, flp->uname), ++ rpmstrPoolStr(fl->pool, flp->gname), flp->caps && ++ strlen(flp->caps) ? flp->caps : ""); ++ appendStringBuf(check_fileList_bin_pkg, file_info); ++ } ++ } ++ ++ if (genDigestList(pkg->header, fl, check_fileList_bin_pkg) > 0) ++ fl->processingFailed = 1; ++} ++ ++/** ++ * Add file entries to header. ++ * @todo Should directories have %doc/%config attributes? (#14531) ++ * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead. ++ * @param fl package file tree walk data ++ * @param pkg (sub) package ++ * @param isSrc pass 1 for source packages 0 otherwise ++ */ ++static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) ++{ ++ FileListRec flp; ++ char buf[BUFSIZ]; + int i, npaths = 0; + int fail_on_dupes = rpmExpandNumeric("%{?_duplicate_files_terminate_build}") > 0; + uint32_t defaultalgo = RPM_HASH_MD5, digestalgo; + rpm_loff_t totalFileSize = 0; + Header h = pkg->header; /* just a shortcut */ +- int processed = 0; + time_t source_date_epoch = 0; + char *srcdate = getenv("SOURCE_DATE_EPOCH"); ++ struct rpmtd_s oldfiledigests; ++ ++ headerGet(h, RPMTAG_FILEDIGESTS, &oldfiledigests, HEADERGET_ALLOC); ++ headerDel(h, RPMTAG_FILEDIGESTS); ++ rpmtdInit(&oldfiledigests); + + /* Limit the maximum date to SOURCE_DATE_EPOCH if defined + * similar to the tar --clamp-mtime option +@@ -1079,9 +1208,8 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) + + pkg->dpaths = xmalloc((fl->files.used + 1) * sizeof(*pkg->dpaths)); + +-process_files: + /* Generate the header. */ +- for (i = processed, flp = fl->files.recs + processed; i < fl->files.used; i++, flp++) { ++ for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { + rpm_ino_t fileid = flp - fl->files.recs; + + /* Merge duplicate entries. */ +@@ -1211,13 +1339,17 @@ process_files: + if (fl->haveCaps) { + headerPutString(h, RPMTAG_FILECAPS, flp->caps); + } +- ++ + buf[0] = '\0'; +- if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST)) +- (void) rpmDoDigest(digestalgo, flp->diskPath, 1, +- (unsigned char *)buf); +- headerPutString(h, RPMTAG_FILEDIGESTS, buf); +- snprintf(file_digest, sizeof(file_digest), "%s", buf); ++ if (strstr(flp->diskPath, DIGEST_LIST_DIR) || !oldfiledigests.count) { ++ if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST)) ++ (void) rpmDoDigest(digestalgo, flp->diskPath, 1, ++ (unsigned char *)buf); ++ headerPutString(h, RPMTAG_FILEDIGESTS, buf); ++ } else { ++ headerPutString(h, RPMTAG_FILEDIGESTS, ++ rpmtdNextString(&oldfiledigests)); ++ } + + buf[0] = '\0'; + if (S_ISLNK(flp->fl_mode)) { +@@ -1258,31 +1390,6 @@ process_files: + flp->flags &= PARSEATTR_MASK; + + headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1); +- +- if (!processed && check_fileList_bin_pkg && S_ISREG(flp->fl_mode) && +- !(flp->flags & RPMFILE_GHOST)) { +- appendStringBuf(check_fileList_bin_pkg, "path="); +- appendStringBuf(check_fileList_bin_pkg, flp->diskPath); +- snprintf(file_info, sizeof(file_info), +- "|digestalgopgp=%d|digest=%s|mode=%d" +- "|uname=%s|gname=%s|caps=%s\n", +- digestalgo, file_digest, flp->fl_mode, +- rpmstrPoolStr(fl->pool, flp->uname), +- rpmstrPoolStr(fl->pool, flp->gname), flp->caps && +- strlen(flp->caps) ? flp->caps : ""); +- appendStringBuf(check_fileList_bin_pkg, file_info); +- } +- } +- +- if (!processed) { +- if (genDigestList(pkg->header, fl, check_fileList_bin_pkg) > 0) { +- fl->processingFailed = 1; +- } else if (i < fl->files.used) { +- pkg->dpaths = xrealloc(pkg->dpaths, +- (fl->files.used + 1) * sizeof(*pkg->dpaths)); +- processed = i; +- goto process_files; +- } + } + + pkg->dpaths[npaths] = NULL; +@@ -1323,6 +1430,7 @@ process_files: + /* Binary packages with dirNames cannot be installed by legacy rpm. */ + (void) rpmlibNeedsFeature(pkg, "CompressedFileNames", "3.0.4-1"); + } ++ rpmtdFreeData(&oldfiledigests); + } + + static FileRecords FileRecordsFree(FileRecords files) +@@ -2805,6 +2913,10 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + if (checkHardLinks(&fl.files)) + (void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1"); + ++ genDigestListInput(&fl, pkg, 0); ++ if (fl.processingFailed) ++ goto exit; ++ + genCpioListAndHeader(&fl, pkg, 0); + + exit: +-- +2.33.0 + diff --git a/0005-call-process_digest_list-after-files-are-added.patch b/0005-call-process_digest_list-after-files-are-added.patch new file mode 100644 index 0000000..597616b --- /dev/null +++ b/0005-call-process_digest_list-after-files-are-added.patch @@ -0,0 +1,115 @@ +From f7b95fc722d8ef438872b270039b24e8ddd9fc0d Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Mon, 26 Oct 2020 12:10:31 +0800 +Subject: [PATCH 05/13] call process_digest_list after files are added + +Signed-off-by: Anakin Zhang +--- + plugins/digest_list.c | 78 ++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 69 insertions(+), 9 deletions(-) + +diff --git a/plugins/digest_list.c b/plugins/digest_list.c +index 293593f..1f50394 100644 +--- a/plugins/digest_list.c ++++ b/plugins/digest_list.c +@@ -472,28 +472,88 @@ out: + return ret; + } + ++rpmte cur_te; ++int digest_list_counter; ++ + static rpmRC digest_list_psm_pre(rpmPlugin plugin, rpmte te) + { +- process_digest_list(te, 0); +- if (!strcmp(rpmteN(te), "digest-list-tools")) +- process_digest_list(te, 1); ++ Header rpm = rpmteHeader(te); ++ rpmtd dirnames; ++ int i; ++ ++ digest_list_counter = 0; ++ ++ dirnames = rpmtdNew(); ++ headerGet(rpm, RPMTAG_DIRNAMES, dirnames, 0); ++ ++ while ((i = rpmtdNext(dirnames)) >= 0) { ++ char *dirname = (char *) rpmtdGetString(dirnames); ++ if (!strncmp(dirname, DIGEST_LIST_DEFAULT_PATH, ++ sizeof(DIGEST_LIST_DEFAULT_PATH) - 1)) ++ digest_list_counter++; ++ } + ++ rpmtdFree(dirnames); ++ ++ cur_te = te; + return RPMRC_OK; + } + +-static rpmRC digest_list_psm_post(rpmPlugin plugin, rpmte te, int res) ++static rpmRC digest_list_file_common(rpmPlugin plugin, rpmfi fi, ++ const char* path, mode_t file_mode, ++ rpmFsmOp op, int pre, int res) + { +- if (res != RPMRC_OK) ++ rpmFileAction action = XFO_ACTION(op); ++ ++ if (!digest_list_counter) ++ return RPMRC_OK; ++ ++ if (!cur_te) ++ return RPMRC_OK; ++ ++ if (!pre && res != RPMRC_OK) ++ return res; ++ ++ if ((pre && action != FA_ERASE) || ++ (!pre && action != FA_CREATE)) + return RPMRC_OK; + +- process_digest_list(te, 0); +- if (!strcmp(rpmteN(te), "digest-list-tools")) +- process_digest_list(te, 1); ++ if (digest_list_counter) { ++ if (!pre) { ++ if (!strncmp(path, DIGEST_LIST_DEFAULT_PATH, ++ sizeof(DIGEST_LIST_DEFAULT_PATH) - 1)) ++ digest_list_counter--; ++ } else { ++ digest_list_counter = 0; ++ } ++ ++ if (digest_list_counter) ++ return RPMRC_OK; ++ } ++ ++ process_digest_list(cur_te, 0); ++ if (!strcmp(rpmteN(cur_te), "digest-list-tools")) ++ process_digest_list(cur_te, 1); + + return RPMRC_OK; + } + ++static rpmRC digest_list_file_pre(rpmPlugin plugin, rpmfi fi, ++ const char* path, mode_t file_mode, ++ rpmFsmOp op) ++{ ++ return digest_list_file_common(plugin, fi, path, file_mode, op, 1, 0); ++} ++ ++static rpmRC digest_list_file_post(rpmPlugin plugin, rpmfi fi, ++ const char* path, mode_t file_mode, ++ rpmFsmOp op, int res) ++{ ++ return digest_list_file_common(plugin, fi, path, file_mode, op, 0, res); ++} ++ + struct rpmPluginHooks_s digest_list_hooks = { + .psm_pre = digest_list_psm_pre, +- .psm_post = digest_list_psm_post, ++ .fsm_file_pre = digest_list_file_pre, ++ .fsm_file_post = digest_list_file_post, + }; +-- +2.33.0 + diff --git a/0006-fix-lsetxattr-error-in-container.patch b/0006-fix-lsetxattr-error-in-container.patch new file mode 100644 index 0000000..d3f1152 --- /dev/null +++ b/0006-fix-lsetxattr-error-in-container.patch @@ -0,0 +1,64 @@ +From 28f6a9c80b721344fa9682bb0236ae02e0f4f2e5 Mon Sep 17 00:00:00 2001 +From: Zhang Tianxing +Date: Mon, 13 Sep 2021 17:32:11 +0800 +Subject: [PATCH 06/13] fix lsetxattr error in container + +The digest list plugin in rpm will set security.ima xattr to IMA digest lists +when installing or updating an rpm package. However, in a container without +CAP_SYS_ADMIN, we'll get error messages when calling lsetxattr. + +This patch is to skip lsetxattr when CAP_SYS_ADMIN is missing. + +Signed-off-by: Zhang Tianxing +--- + plugins/digest_list.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/plugins/digest_list.c b/plugins/digest_list.c +index 1f50394..23f911a 100644 +--- a/plugins/digest_list.c ++++ b/plugins/digest_list.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -371,6 +372,10 @@ static int process_digest_list(rpmte te, int parser) + int digest_list_signed = 0; + struct stat st; + ssize_t size; ++ struct __user_cap_header_struct cap_header_data; ++ cap_user_header_t cap_header = &cap_header_data; ++ struct __user_cap_data_struct cap_data_data; ++ cap_user_data_t cap_data = &cap_data_data; + rpmRC ret = RPMRC_OK; + + path = malloc(PATH_MAX); +@@ -436,7 +441,21 @@ static int process_digest_list(rpmte te, int parser) + ret = RPMRC_FAIL; + goto out; + } ++ } + ++ /* don't call lsetxattr without CAP_SYS_ADMIN */ ++ cap_header->pid = getpid(); ++ cap_header->version = _LINUX_CAPABILITY_VERSION_1; ++ if (capget(cap_header, cap_data) < 0) { ++ ret = -ENOENT; ++ goto out; ++ } ++ if (!(cap_data->effective & CAP_TO_MASK(CAP_SYS_ADMIN))) { ++ ret = -EPERM; ++ goto out; ++ } ++ ++ if (!digest_list_signed) { + /* Write RPM header sig to security.ima */ + ret = write_rpm_digest_list_ima_xattr(te, path); + } else { +-- +2.33.0 + diff --git a/0007-rpm-selinux-plugin-check-context-file-exist.patch b/0007-rpm-selinux-plugin-check-context-file-exist.patch new file mode 100644 index 0000000..7cc7c55 --- /dev/null +++ b/0007-rpm-selinux-plugin-check-context-file-exist.patch @@ -0,0 +1,26 @@ +From cc9c53b97b56238320319ed8a0780ba327e4cef2 Mon Sep 17 00:00:00 2001 +From: luhuaxin <1539327763@qq.com> +Date: Tue, 26 Oct 2021 18:39:46 +0800 +Subject: [PATCH 07/13] rpm selinux plugin check context file exist + +--- + plugins/selinux.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/plugins/selinux.c b/plugins/selinux.c +index 316ff88..ac1e354 100644 +--- a/plugins/selinux.c ++++ b/plugins/selinux.c +@@ -64,7 +64,8 @@ static rpmRC selinux_tsm_pre(rpmPlugin plugin, rpmts ts) + rpmRC rc = RPMRC_OK; + + /* If SELinux isn't enabled on the system, dont mess with it */ +- if (!is_selinux_enabled()) { ++ if (!is_selinux_enabled() || selinux_file_context_path() == NULL || ++ access(selinux_file_context_path(), F_OK)) { + rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS)); + } + +-- +2.33.0 + diff --git a/0008-Fix-digest_list_counter.patch b/0008-Fix-digest_list_counter.patch new file mode 100644 index 0000000..feed1ee --- /dev/null +++ b/0008-Fix-digest_list_counter.patch @@ -0,0 +1,81 @@ +From 943c1848f6aeadbe913f88466a9fdddc42ec51c5 Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Wed, 10 Mar 2021 12:22:39 +0100 +Subject: [PATCH 08/13] Fix digest_list_counter + +--- + plugins/digest_list.c | 38 +++++++++++++++++++++++--------------- + 1 file changed, 23 insertions(+), 15 deletions(-) + +diff --git a/plugins/digest_list.c b/plugins/digest_list.c +index 23f911a..4142caf 100644 +--- a/plugins/digest_list.c ++++ b/plugins/digest_list.c +@@ -497,8 +497,8 @@ int digest_list_counter; + static rpmRC digest_list_psm_pre(rpmPlugin plugin, rpmte te) + { + Header rpm = rpmteHeader(te); +- rpmtd dirnames; +- int i; ++ rpmtd dirnames, dirindexes; ++ int i = -1; + + digest_list_counter = 0; + +@@ -507,13 +507,26 @@ static rpmRC digest_list_psm_pre(rpmPlugin plugin, rpmte te) + + while ((i = rpmtdNext(dirnames)) >= 0) { + char *dirname = (char *) rpmtdGetString(dirnames); ++ + if (!strncmp(dirname, DIGEST_LIST_DEFAULT_PATH, +- sizeof(DIGEST_LIST_DEFAULT_PATH) - 1)) +- digest_list_counter++; ++ sizeof(DIGEST_LIST_DEFAULT_PATH) - 1) && ++ dirname[sizeof(DIGEST_LIST_DEFAULT_PATH) - 1] == '/') ++ break; + } + + rpmtdFree(dirnames); + ++ if (i == -1) ++ return RPMRC_OK; ++ ++ dirindexes = rpmtdNew(); ++ headerGet(rpm, RPMTAG_DIRINDEXES, dirindexes, 0); ++ while (rpmtdNext(dirindexes) >= 0) ++ if (rpmtdGetNumber(dirindexes) == i) ++ digest_list_counter++; ++ ++ rpmtdFree(dirindexes); ++ + cur_te = te; + return RPMRC_OK; + } +@@ -537,18 +550,13 @@ static rpmRC digest_list_file_common(rpmPlugin plugin, rpmfi fi, + (!pre && action != FA_CREATE)) + return RPMRC_OK; + +- if (digest_list_counter) { +- if (!pre) { +- if (!strncmp(path, DIGEST_LIST_DEFAULT_PATH, +- sizeof(DIGEST_LIST_DEFAULT_PATH) - 1)) +- digest_list_counter--; +- } else { +- digest_list_counter = 0; +- } ++ if (strncmp(path, DIGEST_LIST_DEFAULT_PATH, ++ sizeof(DIGEST_LIST_DEFAULT_PATH) - 1) || ++ path[sizeof(DIGEST_LIST_DEFAULT_PATH) - 1] != '/') ++ return RPMRC_OK; + +- if (digest_list_counter) +- return RPMRC_OK; +- } ++ if (!pre && --digest_list_counter) ++ return RPMRC_OK; + + process_digest_list(cur_te, 0); + if (!strcmp(rpmteN(cur_te), "digest-list-tools")) +-- +2.33.0 + diff --git a/0009-Check-rpm-parser.patch b/0009-Check-rpm-parser.patch new file mode 100644 index 0000000..127964f --- /dev/null +++ b/0009-Check-rpm-parser.patch @@ -0,0 +1,29 @@ +From 218a3e2bca1c48b4338939cf01941e5231513300 Mon Sep 17 00:00:00 2001 +From: Roberto Sassu +Date: Wed, 10 Mar 2021 12:23:32 +0100 +Subject: [PATCH 09/13] Check rpm parser + +--- + plugins/digest_list.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/plugins/digest_list.c b/plugins/digest_list.c +index 4142caf..ca77282 100644 +--- a/plugins/digest_list.c ++++ b/plugins/digest_list.c +@@ -133,6 +133,12 @@ static int upload_digest_list(char *path, int type, int digest_list_signed) + + /* If the digest list is not signed, execute the RPM parser */ + if (!digest_list_signed) { ++ if (stat(RPM_PARSER, &st) == -1) { ++ rpmlog(RPMLOG_DEBUG, "digest_list: %s not found, " ++ "not uploading digest list\n", RPM_PARSER); ++ return 0; ++ } ++ + if ((pid = fork()) == 0) { + execlp(RPM_PARSER, RPM_PARSER, (type == TR_ADDED) ? + "add" : "del", path, NULL); +-- +2.33.0 + diff --git a/rpm.spec b/rpm.spec index 88a81bd..eb2a1d8 100644 --- a/rpm.spec +++ b/rpm.spec @@ -1,6 +1,6 @@ Name: rpm Version: 4.18.0 -Release: 3 +Release: 4 Summary: RPM Package Manager License: GPLv2+ URL: http://www.rpm.org/ @@ -34,6 +34,15 @@ Patch6012: backport-Fix-fileleak-and-memleak-in-rpmInstall.patch Patch6013: backport-Fix-fileleak-when-urlGetFile-fails-in-rpmInstall.patch Patch9000: rpm-fix-rpm-is-blocked-when-open-fifo-file.patch +Patch9001: 0001-Generate-digest-lists.patch +Patch9002: 0002-Add-digest-list-plugin.patch +Patch9003: 0003-Don-t-add-dist-to-release-if-it-is-already-there.patch +Patch9004: 0004-Generate-digest-lists-before-calling-genCpioListAndH.patch +Patch9005: 0005-call-process_digest_list-after-files-are-added.patch +Patch9006: 0006-fix-lsetxattr-error-in-container.patch +Patch9007: 0007-rpm-selinux-plugin-check-context-file-exist.patch +Patch9008: 0008-Fix-digest_list_counter.patch +Patch9009: 0009-Check-rpm-parser.patch BuildRequires: gcc autoconf automake libtool make gawk popt-devel openssl-devel readline-devel BuildRequires: zlib-devel zstd-devel >= 1.3.8 xz-devel bzip2-devel libarchive-devel ima-evm-utils-devel @@ -320,6 +329,9 @@ make clean %exclude %{_mandir}/man8/rpmspec.8.gz %changelog +* Wed Feb 08 2023 gaoyusong - 4.18.0-4 +- Add digest list plugin support + * Mon Feb 06 2023 xujing - 4.18.0-3 - make clean after check to adapt rpm upstream logic