diff mbox series

[kirkstone] rpm: Backport fix CVE-2021-35939

Message ID 20240423162844.1563233-1-vkumbhar@mvista.com
State Changes Requested
Delegated to: Steve Sakoman
Headers show
Series [kirkstone] rpm: Backport fix CVE-2021-35939 | expand

Commit Message

Vivek Kumbhar April 23, 2024, 4:28 p.m. UTC
Upstream-Status: Backport https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556
                          https://github.com/rpm-software-management/rpm/commit/fb13f7fd9eff012cb7b9dbf94ac5381c69404055

Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
---
 .../rpm/files/CVE-2021-35939.patch            | 378 ++++++++++++++++++
 meta/recipes-devtools/rpm/rpm_4.17.1.bb       |   1 +
 2 files changed, 379 insertions(+)
 create mode 100644 meta/recipes-devtools/rpm/files/CVE-2021-35939.patch

Comments

Steve Sakoman April 25, 2024, 7:59 p.m. UTC | #1
This patch caused multiple build failures both locally and on the autobuilder.

Here is a link to the autobuilder run:

https://autobuilder.yoctoproject.org/typhoon/#/builders/83/builds/6845

Sample error log:

https://errors.yoctoproject.org/Errors/Details/763370/

Steve

On Tue, Apr 23, 2024 at 9:30 AM Vivek Kumbhar via
lists.openembedded.org <vkumbhar=mvista.com@lists.openembedded.org>
wrote:
>
> Upstream-Status: Backport https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556
>                           https://github.com/rpm-software-management/rpm/commit/fb13f7fd9eff012cb7b9dbf94ac5381c69404055
>
> Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
> ---
>  .../rpm/files/CVE-2021-35939.patch            | 378 ++++++++++++++++++
>  meta/recipes-devtools/rpm/rpm_4.17.1.bb       |   1 +
>  2 files changed, 379 insertions(+)
>  create mode 100644 meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
>
> diff --git a/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> new file mode 100644
> index 0000000000..b60cc0e5ce
> --- /dev/null
> +++ b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> @@ -0,0 +1,378 @@
> +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.
> +
> +Upstream-Status: Backport [https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556]
> +CVE: CVE-2021-35939
> +Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
> +---
> + INSTALL              |   2 +
> + configure.ac         |   3 +-
> + lib/fsm.c            | 144 +++++++++++++++++++++++++++++++++++++++++--
> + lib/rpmfi.c          |  27 +++++++-
> + lib/rpmfi_internal.h |  17 +++++
> + 5 files changed, 183 insertions(+), 10 deletions(-)
> +
> +diff --git a/INSTALL b/INSTALL
> +index 358e5ae0d..9a9c7b0d0 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 78c555f90..4ddacdfe2 100644
> +--- a/configure.ac
> ++++ b/configure.ac
> +@@ -570,7 +570,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 935a0a5c6..0b29284e8 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));
> +@@ -929,6 +1048,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)
> +@@ -943,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) {
> +               if (fp->action == FA_TOUCH) {
> +@@ -977,7 +1107,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) {
> +@@ -1019,6 +1149,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;
> +diff --git a/lib/rpmfi.c b/lib/rpmfi.c
> +index 33b657aa2..740e257fe 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 */
> +@@ -309,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);
> +@@ -319,9 +333,17 @@ int rpmfiSetFX(rpmfi fi, int fx)
> +     int i = -1;
> +
> +     if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) {
> +-      i = fi->i;
> ++      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;
> + }
> +@@ -1816,10 +1838,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 dccc6ccbe..37f1d45f5 100644
> +--- a/lib/rpmfi_internal.h
> ++++ b/lib/rpmfi_internal.h
> +@@ -13,6 +13,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
> +--
> +2.39.3
> +
> diff --git a/meta/recipes-devtools/rpm/rpm_4.17.1.bb b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> index 9b6446f265..aad8f2468f 100644
> --- a/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> +++ b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> @@ -40,6 +40,7 @@ SRC_URI = "git://github.com/rpm-software-management/rpm;branch=rpm-4.17.x;protoc
>             file://0001-docs-do-not-build-manpages-requires-pandoc.patch \
>             file://0001-build-pack.c-do-not-insert-payloadflags-into-.rpm-me.patch \
>             file://0001-configure.ac-add-linux-gnux32-variant-to-triplet-han.patch \
> +           file://CVE-2021-35939.patch \
>             "
>
>  PE = "1"
> --
> 2.39.3
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#198630): https://lists.openembedded.org/g/openembedded-core/message/198630
> Mute This Topic: https://lists.openembedded.org/mt/105692772/3620601
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
Vivek Kumbhar April 26, 2024, 4:21 a.m. UTC | #2
Okay, Will correct and send V2.

Thanks,
Vivek

On Fri, Apr 26, 2024 at 1:29 AM Steve Sakoman <steve@sakoman.com> wrote:

> This patch caused multiple build failures both locally and on the
> autobuilder.
>
> Here is a link to the autobuilder run:
>
> https://autobuilder.yoctoproject.org/typhoon/#/builders/83/builds/6845
>
> Sample error log:
>
> https://errors.yoctoproject.org/Errors/Details/763370/
>
> Steve
>
> On Tue, Apr 23, 2024 at 9:30 AM Vivek Kumbhar via
> lists.openembedded.org <vkumbhar=mvista.com@lists.openembedded.org>
> wrote:
> >
> > Upstream-Status: Backport
> https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556
> >
> https://github.com/rpm-software-management/rpm/commit/fb13f7fd9eff012cb7b9dbf94ac5381c69404055
> >
> > Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
> > ---
> >  .../rpm/files/CVE-2021-35939.patch            | 378 ++++++++++++++++++
> >  meta/recipes-devtools/rpm/rpm_4.17.1.bb       |   1 +
> >  2 files changed, 379 insertions(+)
> >  create mode 100644 meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> >
> > diff --git a/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> > new file mode 100644
> > index 0000000000..b60cc0e5ce
> > --- /dev/null
> > +++ b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> > @@ -0,0 +1,378 @@
> > +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.
> > +
> > +Upstream-Status: Backport [
> https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556
> ]
> > +CVE: CVE-2021-35939
> > +Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
> > +---
> > + INSTALL              |   2 +
> > + configure.ac         |   3 +-
> > + lib/fsm.c            | 144 +++++++++++++++++++++++++++++++++++++++++--
> > + lib/rpmfi.c          |  27 +++++++-
> > + lib/rpmfi_internal.h |  17 +++++
> > + 5 files changed, 183 insertions(+), 10 deletions(-)
> > +
> > +diff --git a/INSTALL b/INSTALL
> > +index 358e5ae0d..9a9c7b0d0 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 78c555f90..4ddacdfe2 100644
> > +--- a/configure.ac
> > ++++ b/configure.ac
> > +@@ -570,7 +570,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 935a0a5c6..0b29284e8 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));
> > +@@ -929,6 +1048,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)
> > +@@ -943,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) {
> > +               if (fp->action == FA_TOUCH) {
> > +@@ -977,7 +1107,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) {
> > +@@ -1019,6 +1149,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;
> > +diff --git a/lib/rpmfi.c b/lib/rpmfi.c
> > +index 33b657aa2..740e257fe 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 */
> > +@@ -309,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);
> > +@@ -319,9 +333,17 @@ int rpmfiSetFX(rpmfi fi, int fx)
> > +     int i = -1;
> > +
> > +     if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) {
> > +-      i = fi->i;
> > ++      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;
> > + }
> > +@@ -1816,10 +1838,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 dccc6ccbe..37f1d45f5 100644
> > +--- a/lib/rpmfi_internal.h
> > ++++ b/lib/rpmfi_internal.h
> > +@@ -13,6 +13,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
> > +--
> > +2.39.3
> > +
> > diff --git a/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> > index 9b6446f265..aad8f2468f 100644
> > --- a/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> > +++ b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> > @@ -40,6 +40,7 @@ SRC_URI = "git://
> github.com/rpm-software-management/rpm;branch=rpm-4.17.x;protoc
> >             file://0001-docs-do-not-build-manpages-requires-pandoc.patch
> \
> >
>  file://0001-build-pack.c-do-not-insert-payloadflags-into-.rpm-me.patch \
> >
>  file://0001-configure.ac-add-linux-gnux32-variant-to-triplet-han.patch \
> > +           file://CVE-2021-35939.patch \
> >             "
> >
> >  PE = "1"
> > --
> > 2.39.3
> >
> >
> > -=-=-=-=-=-=-=-=-=-=-=-
> > Links: You receive all messages sent to this group.
> > View/Reply Online (#198630):
> https://lists.openembedded.org/g/openembedded-core/message/198630
> > Mute This Topic: https://lists.openembedded.org/mt/105692772/3620601
> > Group Owner: openembedded-core+owner@lists.openembedded.org
> > Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [
> steve@sakoman.com]
> > -=-=-=-=-=-=-=-=-=-=-=-
> >
>
Mittal, Anuj April 26, 2024, 4:51 a.m. UTC | #3
On Tue, 2024-04-23 at 21:58 +0530, Vivek Kumbhar via
lists.openembedded.org wrote:
> Upstream-Status: Backport
> https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556
>                          
> https://github.com/rpm-software-management/rpm/commit/fb13f7fd9eff012cb7b9dbf94ac5381c69404055
> 
> Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
> ---
>  .../rpm/files/CVE-2021-35939.patch            | 378
> ++++++++++++++++++
>  meta/recipes-devtools/rpm/rpm_4.17.1.bb       |   1 +
>  2 files changed, 379 insertions(+)
>  create mode 100644 meta/recipes-devtools/rpm/files/CVE-2021-
> 35939.patch
> 
> diff --git a/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> new file mode 100644
> index 0000000000..b60cc0e5ce
> --- /dev/null
> +++ b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
> @@ -0,0 +1,378 @@
> +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.
> +
> +Upstream-Status: Backport
> [https://github.com/rpm-software-management/rpm/commit/96ec957e281220
> f8e137a2d5eb23b83a6377d556]
> +CVE: CVE-2021-35939
> +Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
> +---
> + INSTALL              |   2 +
> + configure.ac         |   3 +-
> + lib/fsm.c            | 144
> +++++++++++++++++++++++++++++++++++++++++--
> + lib/rpmfi.c          |  27 +++++++-
> + lib/rpmfi_internal.h |  17 +++++

rpmfi.c and rpmfi_internal.h changes are not part of the commit
mentioned in Upstream-Status above. It seems they are here:

https://github.com/rpm-software-management/rpm/commit/fb13f7fd9eff012cb7b9dbf94ac5381c69404055

Please backport this as a separate patch or describe the changes done.

Thanks,

Anuj

> + 5 files changed, 183 insertions(+), 10 deletions(-)
> +
> +diff --git a/INSTALL b/INSTALL
> +index 358e5ae0d..9a9c7b0d0 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 78c555f90..4ddacdfe2 100644
> +--- a/configure.ac
> ++++ b/configure.ac
> +@@ -570,7 +570,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 935a0a5c6..0b29284e8 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));
> +@@ -929,6 +1048,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)
> +@@ -943,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) {
> + 		if (fp->action == FA_TOUCH) {
> +@@ -977,7 +1107,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) {
> +@@ -1019,6 +1149,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;
> +diff --git a/lib/rpmfi.c b/lib/rpmfi.c
> +index 33b657aa2..740e257fe 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 */
> +@@ -309,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);
> +@@ -319,9 +333,17 @@ int rpmfiSetFX(rpmfi fi, int fx)
> +     int i = -1;
> + 
> +     if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) {
> +-	i = fi->i;
> ++	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;
> + }
> +@@ -1816,10 +1838,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 dccc6ccbe..37f1d45f5 100644
> +--- a/lib/rpmfi_internal.h
> ++++ b/lib/rpmfi_internal.h
> +@@ -13,6 +13,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
> +-- 
> +2.39.3
> +
> diff --git a/meta/recipes-devtools/rpm/rpm_4.17.1.bb b/meta/recipes-
> devtools/rpm/rpm_4.17.1.bb
> index 9b6446f265..aad8f2468f 100644
> --- a/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> +++ b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
> @@ -40,6 +40,7 @@ SRC_URI = "git://github.com/rpm-software-
> management/rpm;branch=rpm-4.17.x;protoc
>            
> file://0001-docs-do-not-build-manpages-requires-pandoc.patch \
>            
> file://0001-build-pack.c-do-not-insert-payloadflags-into-.rpm-me.patch
>  \
>            
> file://0001-configure.ac-add-linux-gnux32-variant-to-triplet-han.patch
>  \
> +           file://CVE-2021-35939.patch \
>             "
>  
>  PE = "1"
> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#198630):
> https://lists.openembedded.org/g/openembedded-core/message/198630
> Mute This Topic: https://lists.openembedded.org/mt/105692772/3616702
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe:
> https://lists.openembedded.org/g/openembedded-core/unsub [
> anuj.mittal@intel.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
diff mbox series

Patch

diff --git a/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
new file mode 100644
index 0000000000..b60cc0e5ce
--- /dev/null
+++ b/meta/recipes-devtools/rpm/files/CVE-2021-35939.patch
@@ -0,0 +1,378 @@ 
+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.
+
+Upstream-Status: Backport [https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556]
+CVE: CVE-2021-35939
+Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
+---
+ INSTALL              |   2 +
+ configure.ac         |   3 +-
+ lib/fsm.c            | 144 +++++++++++++++++++++++++++++++++++++++++--
+ lib/rpmfi.c          |  27 +++++++-
+ lib/rpmfi_internal.h |  17 +++++
+ 5 files changed, 183 insertions(+), 10 deletions(-)
+
+diff --git a/INSTALL b/INSTALL
+index 358e5ae0d..9a9c7b0d0 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 78c555f90..4ddacdfe2 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -570,7 +570,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 935a0a5c6..0b29284e8 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));
+@@ -929,6 +1048,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)
+@@ -943,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) {
+ 		if (fp->action == FA_TOUCH) {
+@@ -977,7 +1107,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) {
+@@ -1019,6 +1149,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;
+diff --git a/lib/rpmfi.c b/lib/rpmfi.c
+index 33b657aa2..740e257fe 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 */
+@@ -309,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);
+@@ -319,9 +333,17 @@ int rpmfiSetFX(rpmfi fi, int fx)
+     int i = -1;
+ 
+     if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) {
+-	i = fi->i;
++	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;
+ }
+@@ -1816,10 +1838,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 dccc6ccbe..37f1d45f5 100644
+--- a/lib/rpmfi_internal.h
++++ b/lib/rpmfi_internal.h
+@@ -13,6 +13,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
+-- 
+2.39.3
+
diff --git a/meta/recipes-devtools/rpm/rpm_4.17.1.bb b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
index 9b6446f265..aad8f2468f 100644
--- a/meta/recipes-devtools/rpm/rpm_4.17.1.bb
+++ b/meta/recipes-devtools/rpm/rpm_4.17.1.bb
@@ -40,6 +40,7 @@  SRC_URI = "git://github.com/rpm-software-management/rpm;branch=rpm-4.17.x;protoc
            file://0001-docs-do-not-build-manpages-requires-pandoc.patch \
            file://0001-build-pack.c-do-not-insert-payloadflags-into-.rpm-me.patch \
            file://0001-configure.ac-add-linux-gnux32-variant-to-triplet-han.patch \
+           file://CVE-2021-35939.patch \
            "
 
 PE = "1"