!150 fix CVE-2021-35937 CVE-2021-35938 CVE-2021-35939

From: @renxichen 
Reviewed-by: @xujing99 
Signed-off-by: @xujing99
This commit is contained in:
openeuler-ci-bot 2022-09-07 09:43:08 +00:00 committed by Gitee
commit b4bb964fff
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
9 changed files with 945 additions and 1 deletions

View File

@ -0,0 +1,105 @@
From fb13f7fd9eff012cb7b9dbf94ac5381c69404055 Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -0,0 +1,129 @@
From bbc270d78fb361bd78eac9a9117070caeb537d4a Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -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

View File

@ -0,0 +1,40 @@
From 25a435e90844ea98fe5eb7bef22c1aecf3a9c033 Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -0,0 +1,56 @@
From cc22fc694d30a64862f0b16d137deaab5416382d Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -0,0 +1,189 @@
From b599e28112ce5cee98b9ffa7bd96886ec5155e9c Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -0,0 +1,67 @@
From dce44771b2a3325b3dc1ebfe41027df9910a39fd Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -0,0 +1,65 @@
From 693d828c035848585b500dfde6f4e58cfb8d4de4 Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
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

View File

@ -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<renhongxun@h-partners.com> - 4.17.0-11
- fix CVE-2021-35937 CVE-2021-35938 CVE-2021-35939
* Tue Aug 16 2022 Kou Wenqi<kouwenqi@kylinos.cn> - 4.17.0-10
- Type:enhancement
- ID:NA