From e71679d585d352d13c92b3b12d7ada95cfd6fee4 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 12 Mar 2020 17:29:55 +0100 Subject: [PATCH] Generate digest lists This patch helps to generate digest lists during rpm building process. Signed-off-by: Roberto Sassu Signed-off-by: Tianxing Zhang --- build/files.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 294 insertions(+), 10 deletions(-) diff --git a/build/files.c b/build/files.c index c43deb5..613de67 100644 --- a/build/files.c +++ b/build/files.c @@ -50,6 +50,8 @@ #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" +#define DEST_DIGEST_LIST_DIR "/etc/ima/digest_lists" #undef HASHTYPE #undef HTKEYTYPE @@ -129,6 +131,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 +197,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)); @@ -980,6 +988,139 @@ 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 genDigestListInput(FileList fl, Package pkg, int isSrc) +{ + FileListRec flp; + char buf[BUFSIZ]; + char file_info[BUFSIZ]; + char file_digest[128 * 2 + 1]; + int i, gen_digest_lists = 1; + 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 (S_ISREG(flp->fl_mode) && + !strncmp(flp->cpioPath, DEST_DIGEST_LIST_DIR, + sizeof(DEST_DIGEST_LIST_DIR) - 1)) + gen_digest_lists = 0; + } + + if (gen_digest_lists && + 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; @@ -991,6 +1132,11 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) int override_date = 0; time_t source_date_epoch; 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 @@ -1184,13 +1330,18 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) 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); - + 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)) { ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1); @@ -1269,6 +1420,7 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) /* 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) @@ -1343,8 +1495,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]; @@ -1355,6 +1507,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. */ @@ -1390,6 +1546,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(): @@ -1527,6 +1710,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; @@ -1548,6 +1733,32 @@ exit: } /** + * 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 * @param diskPath path to file @@ -2556,6 +2767,61 @@ 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; + + if (!strchr(binRpm, '/')) + 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) { @@ -2569,6 +2832,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)); @@ -2624,12 +2891,17 @@ 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: FileListFree(&fl); specialDirFree(specialDoc); specialDirFree(specialLic); + freeStringBuf(check_fileList_bin_pkg); return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK; } @@ -3093,6 +3365,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; @@ -3109,7 +3382,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) { @@ -3215,6 +3495,7 @@ exit: check_fileList = freeStringBuf(check_fileList); _free(buildroot); _free(uniquearch); - + _free(digest_list_dir); + return rc; } -- 1.8.3.1