From 6efb345d81486d001a2297fb1eae2a14402ec1a7 Mon Sep 17 00:00:00 2001 From: rwx403335 Date: Wed, 31 Aug 2022 14:13:02 +0800 Subject: [PATCH] fix CVE-2021-35937 CVE-2021-35938 CVE-2021-35939 --- ...lback-on-directory-changes-during-rp.patch | 105 +++++++ backport-Bury-rpmio-FD-use-to-fsmUnpack.patch | 129 ++++++++ backport-CVE-2021-35937-CVE-2021-35939.patch | 281 ++++++++++++++++++ backport-CVE-2021-35938.patch | 40 +++ ...ped-hardlink-with-content-case-with-.patch | 56 ++++ ...-creation-steps-the-at-family-of-cal.patch | 189 ++++++++++++ ...-the-hardlink-metadata-setting-logic.patch | 67 +++++ ...iptor-of-created-file-from-fsmMkfile.patch | 65 ++++ rpm.spec | 14 +- 9 files changed, 945 insertions(+), 1 deletion(-) create mode 100644 backport-Add-optional-callback-on-directory-changes-during-rp.patch create mode 100644 backport-Bury-rpmio-FD-use-to-fsmUnpack.patch create mode 100644 backport-CVE-2021-35937-CVE-2021-35939.patch create mode 100644 backport-CVE-2021-35938.patch create mode 100644 backport-Consolidate-skipped-hardlink-with-content-case-with-.patch create mode 100644 backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch create mode 100644 backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch create mode 100644 backport-Return-descriptor-of-created-file-from-fsmMkfile.patch diff --git a/backport-Add-optional-callback-on-directory-changes-during-rp.patch b/backport-Add-optional-callback-on-directory-changes-during-rp.patch new file mode 100644 index 0000000..53765d4 --- /dev/null +++ b/backport-Add-optional-callback-on-directory-changes-during-rp.patch @@ -0,0 +1,105 @@ +From fb13f7fd9eff012cb7b9dbf94ac5381c69404055 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 9 Feb 2022 14:47:14 +0200 +Subject: [PATCH] Add optional callback on directory changes during rpmfi + iteration + +Internal only for now in case we need to fiddle with the API some more, +but no reason this couldn't be made public later. +--- + lib/rpmfi.c | 24 +++++++++++++++++++++++- + lib/rpmfi_internal.h | 17 +++++++++++++++++ + 2 files changed, 40 insertions(+), 1 deletion(-) + +diff --git a/lib/rpmfi.c b/lib/rpmfi.c +index 4673fbb..e8e7d08 100644 +--- a/lib/rpmfi.c ++++ b/lib/rpmfi.c +@@ -55,6 +55,9 @@ struct rpmfi_s { + int intervalStart; /*!< Start of iterating interval. */ + int intervalEnd; /*!< End of iterating interval. */ + ++ rpmfiChdirCb onChdir; /*!< Callback for directory changes */ ++ void *onChdirData; /*!< Caller private callback data */ ++ + rpmfiles files; /*!< File info set */ + rpmcpio_t archive; /*!< Archive with payload */ + unsigned char * found; /*!< Bit field of files found in the archive */ +@@ -312,6 +312,17 @@ int rpmfiDI(rpmfi fi) + } + #endif + ++int rpmfiSetOnChdir(rpmfi fi, rpmfiChdirCb cb, void *data) ++{ ++ int rc = -1; ++ if (fi != NULL) { ++ fi->onChdir = cb; ++ fi->onChdirData = data; ++ rc = 0; ++ } ++ return rc; ++} ++ + int rpmfiFX(rpmfi fi) + { + return (fi != NULL ? fi->i : -1); +@@ -313,9 +327,17 @@ int rpmfiSetFX(rpmfi fi, int fx) + int i = -1; + + if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) { ++ int dx = fi->j; ++ i = fi->i; + fi->i = fx; + fi->j = rpmfilesDI(fi->files, fi->i); + i = fi->i; ++ ++ if (fi->j != dx && fi->onChdir) { ++ int chrc = fi->onChdir(fi, fi->onChdirData); ++ if (chrc < 0) ++ i = chrc; ++ } + } + return i; + } +@@ -1780,9 +1802,9 @@ static rpmfi initIter(rpmfiles files, int itype, int link) + if (files && itype>=0 && itype<=RPMFILEITERMAX) { + fi = xcalloc(1, sizeof(*fi)); + fi->i = -1; ++ fi->j = -1; + fi->files = link ? rpmfilesLink(files) : files; + fi->next = nextfuncs[itype]; +- fi->i = -1; + if (itype == RPMFI_ITER_BACK) { + fi->i = rpmfilesFC(fi->files); + } else if (itype >=RPMFI_ITER_READ_ARCHIVE +diff --git a/lib/rpmfi_internal.h b/lib/rpmfi_internal.h +index dccc6ccb..37f1d45 100644 +--- a/lib/rpmfi_internal.h ++++ b/lib/rpmfi_internal.h +@@ -14,6 +14,23 @@ extern "C" { + #endif + + /** \ingroup rpmfi ++ * Callback on file iterator directory changes ++ * @param fi file info ++ * @param data caller private callback data ++ * @return 0 on success, < 0 on error (to stop iteration) ++ */ ++typedef int (*rpmfiChdirCb)(rpmfi fi, void *data); ++ ++/** \ingroup rpmfi ++ * Set a callback for directory changes during iteration. ++ * @param fi file info ++ * @param cb callback function ++ * @param data caller private callback data ++ * @return string pool handle (weak reference) ++ */ ++int rpmfiSetOnChdir(rpmfi fi, rpmfiChdirCb cb, void *data); ++ ++/** \ingroup rpmfi + * Return file info set string pool handle + * @param fi file info + * @return string pool handle (weak reference) +-- +1.8.3.1 + diff --git a/backport-Bury-rpmio-FD-use-to-fsmUnpack.patch b/backport-Bury-rpmio-FD-use-to-fsmUnpack.patch new file mode 100644 index 0000000..c735909 --- /dev/null +++ b/backport-Bury-rpmio-FD-use-to-fsmUnpack.patch @@ -0,0 +1,129 @@ +From bbc270d78fb361bd78eac9a9117070caeb537d4a Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 14 Feb 2022 12:35:58 +0200 +Subject: [PATCH] Bury rpmio FD use to fsmUnpack() + +fsmUnpack() is the only place in FSM that needs to deal with rpmio FD +types, everywhere else they are nothing but a hindrance that need to +be converted to OS level descriptors for use. Better deal with OS +level descriptors to begin with. +--- + lib/fsm.c | 37 ++++++++++++++++--------------------- + 1 file changed, 16 insertions(+), 21 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 13b1142..b019f57 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -110,14 +110,14 @@ static int fsmSetFCaps(const char *path, const char *captxt) + return rc; + } + +-static int fsmClose(FD_t *wfdp) ++static int fsmClose(int *wfdp) + { + int rc = 0; +- if (wfdp && *wfdp) { ++ if (wfdp && *wfdp >= 0) { + int myerrno = errno; + static int oneshot = 0; + static int flush_io = 0; +- int fdno = Fileno(*wfdp); ++ int fdno = *wfdp; + + if (!oneshot) { + flush_io = (rpmExpandNumeric("%{?_flush_io}") > 0); +@@ -126,61 +126,56 @@ static int fsmClose(FD_t *wfdp) + if (flush_io) { + fsync(fdno); + } +- if (Fclose(*wfdp)) ++ if (close(fdno)) + rc = RPMERR_CLOSE_FAILED; + + if (_fsm_debug) { + rpmlog(RPMLOG_DEBUG, " %8s ([%d]) %s\n", __func__, + fdno, (rc < 0 ? strerror(errno) : "")); + } +- *wfdp = NULL; ++ *wfdp = -1; + errno = myerrno; + } + return rc; + } + +-static int fsmOpen(FD_t *wfdp, int dirfd, const char *dest) ++static int fsmOpen(int *wfdp, int dirfd, const char *dest) + { + int rc = 0; + /* Create the file with 0200 permissions (write by owner). */ + int fd = openat(dirfd, dest, O_WRONLY|O_EXCL|O_CREAT, 0200); + +- if (fd >= 0) { +- *wfdp = fdDup(fd); +- close(fd); +- } +- +- if (fd < 0 || Ferror(*wfdp)) ++ if (fd < 0) + rc = RPMERR_OPEN_FAILED; + + if (_fsm_debug) { + rpmlog(RPMLOG_DEBUG, " %8s (%s [%d]) %s\n", __func__, +- dest, Fileno(*wfdp), (rc < 0 ? strerror(errno) : "")); ++ dest, fd, (rc < 0 ? strerror(errno) : "")); + } +- +- if (rc) +- fsmClose(wfdp); ++ *wfdp = fd; + + return rc; + } + +-static int fsmUnpack(rpmfi fi, FD_t fd, rpmpsm psm, int nodigest) ++static int fsmUnpack(rpmfi fi, int fdno, rpmpsm psm, int nodigest) + { ++ FD_t fd = fdDup(fdno); + int rc = rpmfiArchiveReadToFilePsm(fi, fd, nodigest, psm); + if (_fsm_debug) { + rpmlog(RPMLOG_DEBUG, " %8s (%s %" PRIu64 " bytes [%d]) %s\n", __func__, + rpmfiFN(fi), rpmfiFSize(fi), Fileno(fd), + (rc < 0 ? strerror(errno) : "")); + } ++ Fclose(fd); + return rc; + } + + static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, +- struct filedata_s ** firstlink, FD_t *firstlinkfile) ++ struct filedata_s ** firstlink, int *firstlinkfile) + { + int rc = 0; +- FD_t fd = NULL; ++ int fd = -1; + + if (*firstlink == NULL) { + /* First encounter, open file for writing */ +@@ -206,7 +201,7 @@ static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + if (*firstlink) { + fp->setmeta = 1; + *firstlink = NULL; +- *firstlinkfile = NULL; ++ *firstlinkfile = -1; + } + } + +@@ -811,7 +806,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + int fc = rpmfilesFC(files); + int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0; + int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0; +- FD_t firstlinkfile = NULL; ++ int firstlinkfile = -1; + char *tid = NULL; + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + struct filedata_s *firstlink = NULL; +-- +1.8.3.1 + diff --git a/backport-CVE-2021-35937-CVE-2021-35939.patch b/backport-CVE-2021-35937-CVE-2021-35939.patch new file mode 100644 index 0000000..b5f994a --- /dev/null +++ b/backport-CVE-2021-35937-CVE-2021-35939.patch @@ -0,0 +1,281 @@ +From 96ec957e281220f8e137a2d5eb23b83a6377d556 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 10 Feb 2022 14:32:43 +0200 +Subject: [PATCH] Validate intermediate symlinks during installation, + CVE-2021-35939 + +Whenever directory changes during unpacking, walk the entire tree from +starting from / and validate any symlinks crossed, fail the install +on invalid links. + +This is the first of step of many towards securing our file operations +against local tamperers and besides plugging that one CVE, paves the way +for the next step by adding the necessary directory fd tracking. +This also bumps the rpm OS requirements to a whole new level by requiring +the *at() family of calls from POSIX-1.2008. + +This necessarily does a whole lot of huffing and puffing we previously +did not do. It should be possible to cache secure (ie root-owned) +directory structures to avoid validating everything a million times +but for now, just keeping things simple. +--- + INSTALL | 2 + + configure.ac | 3 +- + lib/fsm.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 142 insertions(+), 7 deletions(-) + +diff --git a/INSTALL b/INSTALL +index 677ef88..961a160 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -103,6 +103,8 @@ option to configure). For GCC, OpenMP 4.5 is fully supported since GCC 6.1, + which is available from + http://www.gnu.org/ + ++Rpm requires a POSIX.1-2008 level operating system. ++ + To compile RPM: + -------------- + +diff --git a/configure.ac b/configure.ac +index 3ee3407..0099e5f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -580,7 +580,8 @@ AC_CHECK_FUNCS([secure_getenv __secure_getenv]) + + AC_CHECK_FUNCS( + [mkstemp getcwd basename dirname realpath setenv unsetenv regcomp lchown \ +- utimes getline localtime_r statvfs getaddrinfo ], ++ utimes getline localtime_r statvfs getaddrinfo \ ++ openat mkdirat fstatat ], + [], [AC_MSG_ERROR([function required by rpm])]) + + AC_LIBOBJ(fnmatch) +diff --git a/lib/fsm.c b/lib/fsm.c +index 9118983..b6b152a 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #if WITH_CAP + #include + #endif +@@ -20,6 +21,7 @@ + #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ + #include "lib/fsm.h" + #include "lib/rpmte_internal.h" /* XXX rpmfs */ ++#include "lib/rpmfi_internal.h" /* rpmfiSetOnChdir */ + #include "lib/rpmplugins.h" /* rpm plugins hooks */ + #include "lib/rpmug.h" + +@@ -406,17 +408,118 @@ static int fsmRmdir(const char *path) + return rc; + } + +-static int fsmMkdir(const char *path, mode_t mode) ++static int fsmMkdir(int dirfd, const char *path, mode_t mode) + { +- int rc = mkdir(path, (mode & 07777)); ++ int rc = mkdirat(dirfd, path, (mode & 07777)); + if (_fsm_debug) +- rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__, +- path, (unsigned)(mode & 07777), ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%04o) %s\n", __func__, ++ dirfd, path, (unsigned)(mode & 07777), + (rc < 0 ? strerror(errno) : "")); + if (rc < 0) rc = RPMERR_MKDIR_FAILED; + return rc; + } + ++static int fsmOpenat(int dirfd, const char *path, int flags) ++{ ++ struct stat lsb, sb; ++ int sflags = flags | O_NOFOLLOW; ++ int fd = openat(dirfd, path, sflags); ++ ++ /* ++ * Only ever follow symlinks by root or target owner. Since we can't ++ * open the symlink itself, the order matters: we stat the link *after* ++ * opening the target, and if the link ownership changed between the calls ++ * it could've only been the link owner or root. ++ */ ++ if (fd < 0 && errno == ELOOP && flags != sflags) { ++ int ffd = openat(dirfd, path, flags); ++ if (ffd >= 0 && fstatat(dirfd, path, &lsb, AT_SYMLINK_NOFOLLOW) == 0) { ++ if (fstat(ffd, &sb) == 0) { ++ if (lsb.st_uid == 0 || lsb.st_uid == sb.st_uid) { ++ fd = ffd; ++ } else { ++ close(ffd); ++ } ++ } ++ } ++ } ++ return fd; ++} ++ ++static int fsmDoMkDir(rpmPlugins plugins, int dirfd, const char *dn, ++ int owned, mode_t mode) ++{ ++ int rc; ++ rpmFsmOp op = (FA_CREATE); ++ if (!owned) ++ op |= FAF_UNOWNED; ++ ++ /* Run fsm file pre hook for all plugins */ ++ rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op); ++ ++ if (!rc) ++ rc = fsmMkdir(dirfd, dn, mode); ++ ++ if (!rc) { ++ rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn, mode, op); ++ } ++ ++ /* Run fsm file post hook for all plugins */ ++ rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc); ++ ++ if (!rc) { ++ rpmlog(RPMLOG_DEBUG, ++ "%s directory created with perms %04o\n", ++ dn, (unsigned)(mode & 07777)); ++ } ++ ++ return rc; ++} ++ ++static int ensureDir(rpmPlugins plugins, const char *p, int owned, int create) ++{ ++ char *path = xstrdup(p); ++ char *dp = path; ++ char *sp = NULL, *bn; ++ int oflags = O_RDONLY; ++ ++ int dirfd = fsmOpenat(-1, "/", oflags); ++ int fd = dirfd; /* special case of "/" */ ++ ++ while ((bn = strtok_r(dp, "/", &sp)) != NULL) { ++ struct stat sb; ++ fd = fsmOpenat(dirfd, bn, oflags); ++ ++ if (fd < 0 && errno == ENOENT && create) { ++ mode_t mode = S_IFDIR | (_dirPerms & 07777); ++ if (fsmDoMkDir(plugins, dirfd, bn, owned, mode) == 0) { ++ fd = fsmOpenat(dirfd, bn, oflags|O_NOFOLLOW); ++ } ++ } ++ ++ if (fd >= 0 && fstat(fd, &sb) == 0 && !S_ISDIR(sb.st_mode)) { ++ close(fd); ++ errno = ENOTDIR; ++ fd = -1; ++ } ++ ++ close(dirfd); ++ if (fd >= 0) { ++ dirfd = fd; ++ } else { ++ dirfd = -1; ++ rpmlog(RPMLOG_ERR, _("failed to open dir %s of %s: %s\n"), ++ bn, p, strerror(errno)); ++ break; ++ } ++ ++ dp = NULL; ++ } ++ ++ free(path); ++ return dirfd; ++} ++ + static int fsmMkfifo(const char *path, mode_t mode) + { + int rc = mkfifo(path, (mode & 07777)); +@@ -507,7 +610,7 @@ static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins) + rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op); + + if (!rc) +- rc = fsmMkdir(dn, mode); ++ rc = fsmMkdir(-1, dn, mode); + + if (!rc) { + rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn, +@@ -874,6 +977,21 @@ static void setFileState(rpmfs fs, int i) + } + } + ++struct diriter_s { ++ int dirfd; ++}; ++ ++static int onChdir(rpmfi fi, void *data) ++{ ++ struct diriter_s *di = data; ++ ++ if (di->dirfd >= 0) { ++ close(di->dirfd); ++ di->dirfd = -1; ++ } ++ return 0; ++} ++ + int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rpmpsm psm, char ** failedFile) + { +@@ -890,6 +1008,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + char *tid = NULL; + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + struct filedata_s *firstlink = NULL; ++ struct diriter_s di = { -1 }; + + /* transaction id used for temporary path suffix while installing */ + rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts)); +@@ -932,6 +1051,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = RPMERR_BAD_MAGIC; + goto exit; + } ++ rpmfiSetOnChdir(fi, onChdir, &di); + + /* Detect and create directories not explicitly in package. */ + if (!rc) +@@ -1063,6 +1063,16 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!fp->suffix) { + rc = fsmBackup(fi, fp->action); + } ++ ++ if (di.dirfd == -1) { ++ di.dirfd = ensureDir(plugins, rpmfiDN(fi), 0, ++ (fp->action == FA_CREATE)); ++ if (di.dirfd == -1) { ++ rc = RPMERR_OPEN_FAILED; ++ break; ++ } ++ } ++ + /* Assume file does't exist when tmp suffix is in use */ + if (!fp->suffix) { + rc = fsmVerify(fp->fpath, fi); +@@ -980,7 +1110,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + mode_t mode = fp->sb.st_mode; + mode &= ~07777; + mode |= 00700; +- rc = fsmMkdir(fp->fpath, mode); ++ rc = fsmMkdir(di.dirfd, fp->fpath, mode); + } + } else if (S_ISLNK(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -1022,6 +1152,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + fp->stage = FILE_UNPACK; + } + fi = rpmfiFree(fi); ++ close(di.dirfd); ++ di.dirfd = -1; + + if (!rc && fx < 0 && fx != RPMERR_ITER_END) + rc = fx; +-- +1.8.3.1 + diff --git a/backport-CVE-2021-35938.patch b/backport-CVE-2021-35938.patch new file mode 100644 index 0000000..4e5b3d4 --- /dev/null +++ b/backport-CVE-2021-35938.patch @@ -0,0 +1,40 @@ +From 25a435e90844ea98fe5eb7bef22c1aecf3a9c033 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 14 Feb 2022 14:29:33 +0200 +Subject: [PATCH] Set file metadata via fd-based ops for everything but + symlinks + +Regular file ops are fd-based already, for the rest we need to open them +manually. Files with temporary suffix must never be followed, for +directories (and pre-existing FA_TOUCHed files) use the rpm symlink +"root or target owner allowed" rule wrt following. + +This mostly fixes CVE-2021-35938, but as we're not yet using dirfd-based +operatiosn for everything there are corner cases left undone. And then +there's the plugin API which needs updating for all this. +--- + lib/fsm.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 913e9de..6f781c6 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -1133,6 +1133,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = RPMERR_UNKNOWN_FILETYPE; + } + ++ if (!rc && fd == -1 && !S_ISLNK(fp->sb.st_mode)) { ++ /* Only follow safe symlinks, and never on temporary files */ ++ fd = fsmOpenat(di.dirfd, fp->fpath, ++ fp->suffix ? AT_SYMLINK_NOFOLLOW : 0); ++ if (fd < 0) ++ rc = RPMERR_OPEN_FAILED; ++ } ++ + if (fd != firstlinkfile) + fsmClose(&fd); + } +-- +1.8.3.1 + diff --git a/backport-Consolidate-skipped-hardlink-with-content-case-with-.patch b/backport-Consolidate-skipped-hardlink-with-content-case-with-.patch new file mode 100644 index 0000000..e992dca --- /dev/null +++ b/backport-Consolidate-skipped-hardlink-with-content-case-with-.patch @@ -0,0 +1,56 @@ +From cc22fc694d30a64862f0b16d137deaab5416382d Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 13:05:45 +0200 +Subject: [PATCH] Consolidate skipped hardlink with content case with the + others + +Handling this in a separate clause makes the logic much clearer and +(in theory at least) lets us handle hardlinks to any content, not +just regular files. +--- + lib/fsm.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index ec6ee2c..82610c7 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -832,9 +832,18 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + while (!rc && (fx = rpmfiNext(fi)) >= 0) { + struct filedata_s *fp = &fdata[fx]; + ++ /* ++ * Tricksy case: this file is a being skipped, but it's part of ++ * a hardlinked set and has the actual content linked with it. ++ * Write the content to the first non-skipped file of the set ++ * instead. ++ */ ++ if (fp->skip && firstlink && rpmfiArchiveHasContent(fi)) ++ fp = firstlink; ++ + if (!fp->skip) { + /* Directories replacing something need early backup */ +- if (!fp->suffix) { ++ if (!fp->suffix && fp != firstlink) { + rc = fsmBackup(fi, fp->action); + } + +@@ -904,15 +913,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!IS_DEV_LOG(fp->fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; + } +- } else if (firstlink && rpmfiArchiveHasContent(fi)) { +- /* +- * Tricksy case: this file is a being skipped, but it's part of +- * a hardlinked set and has the actual content linked with it. +- * Write the content to the first non-skipped file of the set +- * instead. +- */ +- rc = fsmMkfile(fi, firstlink, files, psm, nodigest, +- &firstlink, &firstlinkfile); + } + + /* Notify on success. */ +-- +1.8.3.1 + diff --git a/backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch b/backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch new file mode 100644 index 0000000..1b4de6f --- /dev/null +++ b/backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch @@ -0,0 +1,189 @@ +From b599e28112ce5cee98b9ffa7bd96886ec5155e9c Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 15:35:16 +0200 +Subject: [PATCH] Convert the file creation steps the *at() family of calls + +Supposedly no functional changes here, we just need all these things +converted before we can swap over to relative paths. +--- + configure.ac | 2 +- + lib/fsm.c | 59 ++++++++++++++++++++++++++++++----------------------------- + 2 files changed, 31 insertions(+), 30 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 0099e5f..ac90037 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -581,7 +581,7 @@ AC_CHECK_FUNCS([secure_getenv __secure_getenv]) + AC_CHECK_FUNCS( + [mkstemp getcwd basename dirname realpath setenv unsetenv regcomp lchown \ + utimes getline localtime_r statvfs getaddrinfo \ +- openat mkdirat fstatat ], ++ openat mkdirat fstatat linkat symlinkat mkfifoat mknodat ], + [], [AC_MSG_ERROR([function required by rpm])]) + + AC_LIBOBJ(fnmatch) +diff --git a/lib/fsm.c b/lib/fsm.c +index ae1bd3f..8443954 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -214,13 +214,13 @@ const char * dnlNextIterator(DNLI_t dnli) + return dn; + } + +-static int fsmLink(const char *opath, const char *path) ++static int fsmLink(int odirfd, const char *opath, int dirfd, const char *path) + { +- int rc = link(opath, path); ++ int rc = linkat(odirfd, opath, dirfd, path, 0); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, +- opath, path, (rc < 0 ? strerror(errno) : "")); ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, %d %s) %s\n", __func__, ++ odirfd, opath, dirfd, path, (rc < 0 ? strerror(errno) : "")); + } + + if (rc < 0) +@@ -139,17 +139,18 @@ static int fsmClose(FD_t *wfdp) + return rc; + } + +-static int fsmOpen(FD_t *wfdp, const char *dest) ++static int fsmOpen(FD_t *wfdp, int dirfd, const char *dest) + { + int rc = 0; + /* Create the file with 0200 permissions (write by owner). */ +- { +- mode_t old_umask = umask(0577); +- *wfdp = Fopen(dest, "wx.ufdio"); +- umask(old_umask); ++ int fd = openat(dirfd, dest, O_WRONLY|O_EXCL|O_CREAT, 0200); ++ ++ if (fd >= 0) { ++ *wfdp = fdDup(fd); ++ close(fd); + } + +- if (Ferror(*wfdp)) ++ if (fd < 0 || Ferror(*wfdp)) + rc = RPMERR_OPEN_FAILED; + + if (_fsm_debug) { +@@ -174,7 +175,7 @@ static int fsmUnpack(rpmfi fi, FD_t fd, rpmpsm psm, int nodigest) + return rc; + } + +-static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, ++static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, + struct filedata_s ** firstlink, FD_t *firstlinkfile) + { +@@ -183,7 +184,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + + if (*firstlink == NULL) { + /* First encounter, open file for writing */ +- rc = fsmOpen(&fd, fp->fpath); ++ rc = fsmOpen(&fd, dirfd, fp->fpath); + /* If it's a part of a hardlinked set, the content may come later */ + if (fp->sb.st_nlink > 1) { + *firstlink = fp; +@@ -192,7 +193,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + } else { + /* Create hard links for others and avoid redundant metadata setting */ + if (*firstlink != fp) { +- rc = fsmLink((*firstlink)->fpath, fp->fpath); ++ rc = fsmLink(dirfd, (*firstlink)->fpath, dirfd, fp->fpath); + } + fd = *firstlinkfile; + } +@@ -382,13 +383,13 @@ static int ensureDir(rpmPlugins plugins, const char *p, int owned, int create) + return dirfd; + } + +-static int fsmMkfifo(const char *path, mode_t mode) ++static int fsmMkfifo(int dirfd, const char *path, mode_t mode) + { +- int rc = mkfifo(path, (mode & 07777)); ++ int rc = mkfifoat(dirfd, path, (mode & 07777)); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", +- __func__, path, (unsigned)(mode & 07777), ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%04o) %s\n", ++ __func__, dirfd, path, (unsigned)(mode & 07777), + (rc < 0 ? strerror(errno) : "")); + } + +@@ -398,14 +399,14 @@ static int fsmMkfifo(const char *path, mode_t mode) + return rc; + } + +-static int fsmMknod(const char *path, mode_t mode, dev_t dev) ++static int fsmMknod(int dirfd, const char *path, mode_t mode, dev_t dev) + { + /* FIX: check S_IFIFO or dev != 0 */ +- int rc = mknod(path, (mode & ~07777), dev); ++ int rc = mknodat(dirfd, path, (mode & ~07777), dev); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n", +- __func__, path, (unsigned)(mode & ~07777), ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%o, 0x%x) %s\n", ++ __func__, dirfd, path, (unsigned)(mode & ~07777), + (unsigned)dev, (rc < 0 ? strerror(errno) : "")); + } + +@@ -440,13 +441,13 @@ static void fsmDebug(const char *fpath, rpmFileAction action, + (fpath ? fpath : "")); + } + +-static int fsmSymlink(const char *opath, const char *path) ++static int fsmSymlink(const char *opath, int dirfd, const char *path) + { +- int rc = symlink(opath, path); ++ int rc = symlinkat(opath, dirfd, path); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, +- opath, path, (rc < 0 ? strerror(errno) : "")); ++ rpmlog(RPMLOG_DEBUG, " %8s (%s, %d %s) %s\n", __func__, ++ opath, dirfd, path, (rc < 0 ? strerror(errno) : "")); + } + + if (rc < 0) +@@ -884,7 +885,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- rc = fsmMkfile(fi, fp, files, psm, nodigest, ++ rc = fsmMkfile(di.dirfd, fi, fp, files, psm, nodigest, + &firstlink, &firstlinkfile); + } + } else if (S_ISDIR(fp->sb.st_mode)) { +@@ -896,19 +897,19 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + } + } else if (S_ISLNK(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- rc = fsmSymlink(rpmfiFLink(fi), fp->fpath); ++ rc = fsmSymlink(rpmfiFLink(fi), di.dirfd, fp->fpath); + } + } else if (S_ISFIFO(fp->sb.st_mode)) { + /* This mimics cpio S_ISSOCK() behavior but probably isn't right */ + if (rc == RPMERR_ENOENT) { +- rc = fsmMkfifo(fp->fpath, 0000); ++ rc = fsmMkfifo(di.dirfd, fp->fpath, 0000); + } + } else if (S_ISCHR(fp->sb.st_mode) || + S_ISBLK(fp->sb.st_mode) || + S_ISSOCK(fp->sb.st_mode)) + { + if (rc == RPMERR_ENOENT) { +- rc = fsmMknod(fp->fpath, fp->sb.st_mode, fp->sb.st_rdev); ++ rc = fsmMknod(di.dirfd, fp->fpath, fp->sb.st_mode, fp->sb.st_rdev); + } + } else { + /* XXX Special case /dev/log, which shouldn't be packaged anyways */ +-- +1.8.3.1 + diff --git a/backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch b/backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch new file mode 100644 index 0000000..58bd285 --- /dev/null +++ b/backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch @@ -0,0 +1,67 @@ +From dce44771b2a3325b3dc1ebfe41027df9910a39fd Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 13:18:11 +0200 +Subject: [PATCH] Fix + sanitize the hardlink metadata setting logic + +Fix the initial setmeta value to something meaningful: we will never +set metadata on skipped files, and hardlinks are handled with a special +logic during install. They'd need different kind of special logic on +FA_TOUCH so just play it safe and always apply metadata on those. + +Harlink metadata setting on install should happen on the *last* entry +of hardlinked set that gets installed (wrt various skip scenarios) +as otherwise creating those additional links affects the timestamp. +Note in particular the "last file of..." case in fsmMkfile() where we +the comment said just that, but set the metadata on the *first* file +which would then be NULL'ed away. + +This all gets current masked by the fact that we do the metadata setting on +a separate round, but that is about to change plus this makes the overall +logic clearer anyhow. +--- + lib/fsm.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 82610c7..d9cfe6f 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -193,7 +193,6 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + /* Create hard links for others and avoid redundant metadata setting */ + if (*firstlink != fp) { + rc = fsmLink((*firstlink)->fpath, fp->fpath); +- fp->setmeta = 0; + } + fd = *firstlinkfile; + } +@@ -204,7 +203,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + rc = fsmUnpack(fi, fd, psm, nodigest); + /* Last file of hardlink set, ensure metadata gets set */ + if (*firstlink) { +- (*firstlink)->setmeta = 1; ++ fp->setmeta = 1; + *firstlink = NULL; + *firstlinkfile = NULL; + } +@@ -797,7 +796,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + else + fp->action = rpmfsGetAction(fs, fx); + fp->skip = XFA_SKIPPING(fp->action); +- fp->setmeta = 1; + if (XFA_CREATING(fp->action) && !S_ISDIR(rpmfiFMode(fi))) + fp->suffix = tid; + fp->fpath = fsmFsPath(fi, fp->suffix); +@@ -805,6 +803,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + /* Remap file perms, owner, and group. */ + rc = rpmfiStat(fi, 1, &fp->sb); + ++ /* Hardlinks are tricky and handled elsewhere for install */ ++ fp->setmeta = (fp->skip == 0) && ++ (fp->sb.st_nlink == 1 || fp->action == FA_TOUCH); ++ + setFileState(fs, fx); + fsmDebug(fp->fpath, fp->action, &fp->sb); + +-- +1.8.3.1 + diff --git a/backport-Return-descriptor-of-created-file-from-fsmMkfile.patch b/backport-Return-descriptor-of-created-file-from-fsmMkfile.patch new file mode 100644 index 0000000..bb9bb16 --- /dev/null +++ b/backport-Return-descriptor-of-created-file-from-fsmMkfile.patch @@ -0,0 +1,65 @@ +From 693d828c035848585b500dfde6f4e58cfb8d4de4 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 14 Feb 2022 12:44:42 +0200 +Subject: [PATCH] Return descriptor of created file from fsmMkfile() + +This will be needed for using fd-based metadata operations. +--- + lib/fsm.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index b019f57..7c4796f 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -172,7 +172,8 @@ static int fsmUnpack(rpmfi fi, int fdno, rpmpsm psm, int nodigest) + + static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, +- struct filedata_s ** firstlink, int *firstlinkfile) ++ struct filedata_s ** firstlink, int *firstlinkfile, ++ int *fdp) + { + int rc = 0; + int fd = -1; +@@ -204,9 +205,7 @@ static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + *firstlinkfile = -1; + } + } +- +- if (fd != *firstlinkfile) +- fsmClose(&fd); ++ *fdp = fd; + + return rc; + } +@@ -1065,6 +1065,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + fp = firstlink; + + if (!fp->skip) { ++ int fd = -1; + /* Directories replacing something need early backup */ + if (!fp->suffix && fp != firstlink) { + rc = fsmBackup(fi, fp->action); +@@ -910,7 +910,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + rc = fsmMkfile(di.dirfd, fi, fp, files, psm, nodigest, +- &firstlink, &firstlinkfile); ++ &firstlink, &firstlinkfile, &fd); + } + } else if (S_ISDIR(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -1131,6 +1132,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!IS_DEV_LOG(fp->fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; + } ++ ++ if (fd != firstlinkfile) ++ fsmClose(&fd); + } + + /* Notify on success. */ +-- +1.8.3.1 + diff --git a/rpm.spec b/rpm.spec index bc7a2d4..6496bfc 100644 --- a/rpm.spec +++ b/rpm.spec @@ -1,6 +1,6 @@ Name: rpm Version: 4.17.0 -Release: 10 +Release: 11 Summary: RPM Package Manager License: GPLv2+ URL: http://www.rpm.org/ @@ -69,6 +69,15 @@ Patch6035: backport-rpm2cpio.sh-Don-t-drop-newlines-from-header-sizes.patch Patch6036: backport-Prevent-readelf-internet-access-during-rpaths-checki.patch Patch6037: backport-Fix-short-circuiting-of-version-strings-in-expressio.patch +Patch6038: backport-Add-optional-callback-on-directory-changes-during-rp.patch +Patch6039: backport-CVE-2021-35937-CVE-2021-35939.patch +Patch6040: backport-Consolidate-skipped-hardlink-with-content-case-with-.patch +Patch6041: backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch +Patch6042: backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch +Patch6043: backport-Bury-rpmio-FD-use-to-fsmUnpack.patch +Patch6044: backport-Return-descriptor-of-created-file-from-fsmMkfile.patch +Patch6045: backport-CVE-2021-35938.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 BuildRequires: dbus-devel fakechroot elfutils-devel elfutils-libelf-devel ima-evm-utils @@ -337,6 +346,9 @@ make check || (cat tests/rpmtests.log; exit 0) %{_mandir}/man1/gendiff.1* %changelog +* Wed Aug 31 2022 Hongxun Ren - 4.17.0-11 +- fix CVE-2021-35937 CVE-2021-35938 CVE-2021-35939 + * Tue Aug 16 2022 Kou Wenqi - 4.17.0-10 - Type:enhancement - ID:NA