summaryrefslogtreecommitdiff
path: root/backport-CVE-2021-35937-CVE-2021-35939.patch
diff options
context:
space:
mode:
Diffstat (limited to 'backport-CVE-2021-35937-CVE-2021-35939.patch')
-rw-r--r--backport-CVE-2021-35937-CVE-2021-35939.patch281
1 files changed, 281 insertions, 0 deletions
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 <pmatilai@redhat.com>
+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 <inttypes.h>
+ #include <utime.h>
+ #include <errno.h>
++#include <fcntl.h>
+ #if WITH_CAP
+ #include <sys/capability.h>
+ #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
+