From: Deepak Rathore <[email protected]>

- This patch applies the upstream fix [1] as referenced in [5].
- To successfully apply the fixed commit, apply the dependent commits
  [2] to [4] which are included in v2.8.6, as referenced in [5].
- Reference:
  [1] https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=f36bd900a899
  [2] https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=7e8b36522f58
  [3] https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=42f01e6a78fe
  [4] https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=51738ae56d92
  [5] https://security-tracker.debian.org/tracker/CVE-2025-12801

Signed-off-by: Deepak Rathore <[email protected]>

diff --git 
a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p1.patch
 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p1.patch
new file mode 100644
index 0000000000..39c0c12e38
--- /dev/null
+++ 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p1.patch
@@ -0,0 +1,80 @@
+From f642bba8c6c59352ab7259dc5321805cd6236638 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <[email protected]>
+Date: Mon, 10 Nov 2025 11:26:03 -0500
+Subject: [PATCH 1/4] mountd: Minor refactor of get_rootfh()
+
+Perform the mountpoint checks before checking the user path.
+
+CVE: CVE-2025-12801
+Upstream-Status: Backport 
[https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=7e8b36522f58657359c6842119fc516c6dd1baa4]
+
+Reviewed-by: Jeff Layton <[email protected]>
+Signed-off-by: Trond Myklebust <[email protected]>
+Signed-off-by: Steve Dickson <[email protected]>
+(cherry picked from commit 7e8b36522f58657359c6842119fc516c6dd1baa4)
+Signed-off-by: Deepak Rathore <[email protected]>
+---
+ utils/mountd/mountd.c | 34 +++++++++++++++++-----------------
+ 1 file changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
+index dbd5546d..39afd4aa 100644
+--- a/utils/mountd/mountd.c
++++ b/utils/mountd/mountd.c
+@@ -412,6 +412,23 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+               *error = MNT3ERR_ACCES;
+               return NULL;
+       }
++      if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
++              xlog(L_WARNING, "can't stat export point %s: %s",
++                   p, strerror(errno));
++              *error = MNT3ERR_NOENT;
++              return NULL;
++      }
++      if (exp->m_export.e_mountpoint &&
++                 !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
++                                exp->m_export.e_mountpoint:
++                                exp->m_export.e_path,
++                                nfsd_path_lstat)) {
++              xlog(L_WARNING, "request to export an unmounted filesystem: %s",
++                   p);
++              *error = MNT3ERR_NOENT;
++              return NULL;
++      }
++
+       if (nfsd_path_stat(p, &stb) < 0) {
+               xlog(L_WARNING, "can't stat exported dir %s: %s",
+                               p, strerror(errno));
+@@ -426,12 +443,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+               *error = MNT3ERR_NOTDIR;
+               return NULL;
+       }
+-      if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
+-              xlog(L_WARNING, "can't stat export point %s: %s",
+-                   p, strerror(errno));
+-              *error = MNT3ERR_NOENT;
+-              return NULL;
+-      }
+       if (estb.st_dev != stb.st_dev
+           && !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) {
+               xlog(L_WARNING, "request to export directory %s below nearest 
filesystem %s",
+@@ -439,17 +450,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+               *error = MNT3ERR_ACCES;
+               return NULL;
+       }
+-      if (exp->m_export.e_mountpoint &&
+-                 !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
+-                                exp->m_export.e_mountpoint:
+-                                exp->m_export.e_path,
+-                                nfsd_path_lstat)) {
+-              xlog(L_WARNING, "request to export an unmounted filesystem: %s",
+-                   p);
+-              *error = MNT3ERR_NOENT;
+-              return NULL;
+-      }
+-
+       /* This will be a static private nfs_export with just one
+        * address.  We feed it to kernel then extract the filehandle,
+        */
+--
+2.35.6
diff --git 
a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p2.patch
 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p2.patch
new file mode 100644
index 0000000000..45ab31642c
--- /dev/null
+++ 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p2.patch
@@ -0,0 +1,180 @@
+From 105b2b59292de54565e27b4cb88e7b7e6ff855f5 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <[email protected]>
+Date: Mon, 10 Nov 2025 11:28:39 -0500
+Subject: [PATCH 2/4] mountd: Separate lookup of the exported directory and the
+ mount path
+
+When the caller asks to mount a path that does not terminate with an
+exported directory, we want to split up the lookups so that we can
+look up the exported directory using the mountd privileged credential,
+and the remaining subdirectory lookups using the RPC caller's
+credential.
+
+CVE: CVE-2025-12801
+Upstream-Status: Backport 
[https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=42f01e6a78fed98f12437ac8b28cfb12b6bad056]
+
+Reviewed-by: Jeff Layton <[email protected]>
+Signed-off-by: Trond Myklebust <[email protected]>
+Signed-off-by: Steve Dickson <[email protected]>
+(cherry picked from commit 42f01e6a78fed98f12437ac8b28cfb12b6bad056)
+Signed-off-by: Deepak Rathore <[email protected]>
+---
+ support/include/nfsd_path.h |  1 +
+ support/misc/nfsd_path.c    | 31 ++++++++++++++++++
+ utils/mountd/mountd.c       | 63 +++++++++++++++++++++++++++++++------
+ 3 files changed, 86 insertions(+), 9 deletions(-)
+
+diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
+index f600fb5a..3e5a2f5d 100644
+--- a/support/include/nfsd_path.h
++++ b/support/include/nfsd_path.h
+@@ -18,6 +18,7 @@ char *               nfsd_path_prepend_dir(const char *dir, 
const char *pathname);
+
+ int           nfsd_path_stat(const char *pathname, struct stat *statbuf);
+ int           nfsd_path_lstat(const char *pathname, struct stat *statbuf);
++int           nfsd_openat(int dirfd, const char *path, int flags);
+
+ int           nfsd_path_statfs(const char *pathname,
+                                  struct statfs *statbuf);
+diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
+index caec33ca..dfe88e4f 100644
+--- a/support/misc/nfsd_path.c
++++ b/support/misc/nfsd_path.c
+@@ -203,6 +203,37 @@ nfsd_realpath(const char *path, char *resolved_buf)
+         return realpath_buf.res_ptr;
+ }
+
++struct nfsd_openat_t {
++      const char *path;
++      int dirfd;
++      int flags;
++      int res_fd;
++      int res_error;
++};
++
++static void nfsd_openatfunc(void *data)
++{
++      struct nfsd_openat_t *d = data;
++
++      d->res_fd = openat(d->dirfd, d->path, d->flags);
++      if (d->res_fd == -1)
++              d->res_error = errno;
++}
++
++int nfsd_openat(int dirfd, const char *path, int flags)
++{
++      struct nfsd_openat_t open_buf = {
++              .path = path,
++              .dirfd = dirfd,
++              .flags = flags,
++      };
++
++      nfsd_run_task(nfsd_openatfunc, &open_buf);
++      if (open_buf.res_fd == -1)
++              errno = open_buf.res_error;
++      return open_buf.res_fd;
++}
++
+ struct nfsd_rw_data {
+       int             fd;
+       void*           buf;
+diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
+index 39afd4aa..f43ebef5 100644
+--- a/utils/mountd/mountd.c
++++ b/utils/mountd/mountd.c
+@@ -392,7 +392,10 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+       struct nfs_fh_len *fh;
+       char            rpath[MAXPATHLEN+1];
+       char            *p = *path;
++      char            *subpath;
+       char            buf[INET6_ADDRSTRLEN];
++      size_t          epathlen;
++      int             dirfd;
+
+       if (*p == '\0')
+               p = "/";
+@@ -412,12 +415,21 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+               *error = MNT3ERR_ACCES;
+               return NULL;
+       }
+-      if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
+-              xlog(L_WARNING, "can't stat export point %s: %s",
++
++      dirfd = nfsd_openat(AT_FDCWD, exp->m_export.e_path, O_PATH);
++      if (dirfd == -1) {
++              xlog(L_WARNING, "can't open export point %s: %s",
+                    p, strerror(errno));
+               *error = MNT3ERR_NOENT;
+               return NULL;
+       }
++      if (fstat(dirfd, &estb) == -1) {
++              xlog(L_WARNING, "can't stat export point %s: %s",
++                   p, strerror(errno));
++              *error = MNT3ERR_ACCES;
++              close(dirfd);
++              return NULL;
++      }
+       if (exp->m_export.e_mountpoint &&
+                  !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
+                                 exp->m_export.e_mountpoint:
+@@ -426,18 +438,51 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+               xlog(L_WARNING, "request to export an unmounted filesystem: %s",
+                    p);
+               *error = MNT3ERR_NOENT;
++              close(dirfd);
+               return NULL;
+       }
+
+-      if (nfsd_path_stat(p, &stb) < 0) {
+-              xlog(L_WARNING, "can't stat exported dir %s: %s",
+-                              p, strerror(errno));
+-              if (errno == ENOENT)
+-                      *error = MNT3ERR_NOENT;
+-              else
+-                      *error = MNT3ERR_ACCES;
++      epathlen = strlen(exp->m_export.e_path);
++      if (epathlen > strlen(p)) {
++              xlog(L_WARNING, "raced with change of exported path: %s", p);
++              *error = MNT3ERR_NOENT;
++              close(dirfd);
+               return NULL;
+       }
++      subpath = &p[epathlen];
++      while (*subpath == '/')
++              subpath++;
++      if (*subpath != '\0') {
++              int fd;
++
++              /* Just perform a lookup of the path */
++              fd = nfsd_openat(dirfd, subpath, O_PATH);
++              close(dirfd);
++              if (fd == -1) {
++                      xlog(L_WARNING, "can't open exported dir %s: %s", p,
++                           strerror(errno));
++                      if (errno == ENOENT)
++                              *error = MNT3ERR_NOENT;
++                      else
++                              *error = MNT3ERR_ACCES;
++                      return NULL;
++              }
++              if (fstat(fd, &stb) == -1) {
++                      xlog(L_WARNING, "can't open exported dir %s: %s", p,
++                           strerror(errno));
++                      if (errno == ENOENT)
++                              *error = MNT3ERR_NOENT;
++                      else
++                              *error = MNT3ERR_ACCES;
++                      close(fd);
++                      return NULL;
++              }
++              close(fd);
++      } else {
++              close(dirfd);
++              stb = estb;
++      }
++
+       if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+               xlog(L_WARNING, "%s is not a directory or regular file", p);
+               *error = MNT3ERR_NOTDIR;
+--
+2.35.6
diff --git 
a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p3.patch
 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p3.patch
new file mode 100644
index 0000000000..456fefdff8
--- /dev/null
+++ 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-depended_p3.patch
@@ -0,0 +1,464 @@
+From 23e06d86004a8d5e3549e026183263584d056bc7 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <[email protected]>
+Date: Mon, 10 Nov 2025 12:18:38 -0500
+Subject: [PATCH 3/4] support: Add a mini-library to extract and apply RPC
+ credentials
+
+Add server functionality to extract the credentials from the client RPC
+call, and apply them. This is needed in order to perform access checking
+on the requested path in the mountd daemon.
+
+CVE: CVE-2025-12801
+Upstream-Status: Backport 
[https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=51738ae56d922d4961e60dad73ad1c2d97d8d99b]
+
+Reviewed-by: Jeff Layton <[email protected]>
+Signed-off-by: Trond Myklebust <[email protected]>
+Signed-off-by: Steve Dickson <[email protected]>
+(cherry picked from commit 51738ae56d922d4961e60dad73ad1c2d97d8d99b)
+Signed-off-by: Deepak Rathore <[email protected]>
+---
+ aclocal/libtirpc.m4         |  12 +++
+ support/include/Makefile.am |   1 +
+ support/include/nfs_ucred.h |  44 ++++++++++
+ support/misc/Makefile.am    |   2 +-
+ support/misc/ucred.c        | 162 ++++++++++++++++++++++++++++++++++++
+ support/nfs/Makefile.am     |   2 +-
+ support/nfs/ucred.c         | 147 ++++++++++++++++++++++++++++++++
+ 7 files changed, 368 insertions(+), 2 deletions(-)
+ create mode 100644 support/include/nfs_ucred.h
+ create mode 100644 support/misc/ucred.c
+ create mode 100644 support/nfs/ucred.c
+
+diff --git a/aclocal/libtirpc.m4 b/aclocal/libtirpc.m4
+index ef48a2ae..06629db9 100644
+--- a/aclocal/libtirpc.m4
++++ b/aclocal/libtirpc.m4
+@@ -31,6 +31,18 @@ AC_DEFUN([AC_LIBTIRPC], [
+                          [AC_DEFINE([HAVE_TIRPC_GSS_SECCREATE], [1],
+                                     [Define to 1 if your tirpc library 
provides rpc_gss_seccreate])],,
+                          [${LIBS}])])
++
++     AS_IF([test -n "${LIBTIRPC}"],
++           [AC_CHECK_LIB([tirpc], [rpc_gss_getcred],
++                         [AC_DEFINE([HAVE_TIRPC_GSS_GETCRED], [1],
++                                    [Define to 1 if your tirpc library 
provides rpc_gss_getcred])],,
++                         [${LIBS}])])
++
++     AS_IF([test -n "${LIBTIRPC}"],
++           [AC_CHECK_LIB([tirpc], [authdes_getucred],
++                         [AC_DEFINE([HAVE_TIRPC_AUTHDES_GETUCRED], [1],
++                                    [Define to 1 if your tirpc library 
provides authdes_getucred])],,
++                         [${LIBS}])])
+   AC_SUBST([AM_CPPFLAGS])
+   AC_SUBST(LIBTIRPC)
+
+diff --git a/support/include/Makefile.am b/support/include/Makefile.am
+index 1373891a..631a84f8 100644
+--- a/support/include/Makefile.am
++++ b/support/include/Makefile.am
+@@ -10,6 +10,7 @@ noinst_HEADERS = \
+       misc.h \
+       nfs_mntent.h \
+       nfs_paths.h \
++      nfs_ucred.h \
+       nfsd_path.h \
+       nfslib.h \
+       nfsrpc.h \
+diff --git a/support/include/nfs_ucred.h b/support/include/nfs_ucred.h
+new file mode 100644
+index 00000000..d58b61e4
+--- /dev/null
++++ b/support/include/nfs_ucred.h
+@@ -0,0 +1,44 @@
++#ifndef _NFS_UCRED_H
++#define _NFS_UCRED_H
++
++#include <sys/types.h>
++
++struct nfs_ucred {
++      uid_t uid;
++      gid_t gid;
++      int ngroups;
++      gid_t *groups;
++};
++
++struct svc_req;
++struct exportent;
++
++int nfs_ucred_get(struct nfs_ucred **credp, struct svc_req *rqst,
++                const struct exportent *ep);
++
++void nfs_ucred_squash_groups(struct nfs_ucred *cred,
++                           const struct exportent *ep);
++int nfs_ucred_reload_groups(struct nfs_ucred *cred, const struct exportent 
*ep);
++int nfs_ucred_swap_effective(const struct nfs_ucred *cred,
++                           struct nfs_ucred **savedp);
++
++static inline void nfs_ucred_free(struct nfs_ucred *cred)
++{
++      free(cred->groups);
++      free(cred);
++}
++
++static inline void nfs_ucred_init_groups(struct nfs_ucred *cred, gid_t 
*groups,
++                                       int ngroups)
++{
++      cred->groups = groups;
++      cred->ngroups = ngroups;
++}
++
++static inline void nfs_ucred_free_groups(struct nfs_ucred *cred)
++{
++      free(cred->groups);
++      nfs_ucred_init_groups(cred, NULL, 0);
++}
++
++#endif /* _NFS_UCRED_H */
+diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am
+index f9993e3a..7ea2d798 100644
+--- a/support/misc/Makefile.am
++++ b/support/misc/Makefile.am
+@@ -2,6 +2,6 @@
+
+ noinst_LIBRARIES = libmisc.a
+ libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c \
+-                  nfsd_path.c workqueue.c xstat.c
++                  nfsd_path.c ucred.c workqueue.c xstat.c
+
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/support/misc/ucred.c b/support/misc/ucred.c
+new file mode 100644
+index 00000000..92d97912
+--- /dev/null
++++ b/support/misc/ucred.c
+@@ -0,0 +1,162 @@
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <alloca.h>
++#include <errno.h>
++#include <pwd.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <grp.h>
++
++#include "exportfs.h"
++#include "nfs_ucred.h"
++
++#include "xlog.h"
++
++void nfs_ucred_squash_groups(struct nfs_ucred *cred, const struct exportent 
*ep)
++{
++      int i;
++
++      if (!(ep->e_flags & NFSEXP_ROOTSQUASH))
++              return;
++      if (cred->gid == 0)
++              cred->gid = ep->e_anongid;
++      for (i = 0; i < cred->ngroups; i++) {
++              if (cred->groups[i] == 0)
++                      cred->groups[i] = ep->e_anongid;
++      }
++}
++
++static int nfs_ucred_init_effective(struct nfs_ucred *cred)
++{
++      int ngroups = getgroups(0, NULL);
++
++      if (ngroups > 0) {
++              size_t sz = ngroups * sizeof(gid_t);
++              gid_t *groups = malloc(sz);
++              if (groups == NULL)
++                      return ENOMEM;
++              if (getgroups(ngroups, groups) == -1) {
++                      free(groups);
++                      return errno;
++              }
++              nfs_ucred_init_groups(cred, groups, ngroups);
++      } else
++              nfs_ucred_init_groups(cred, NULL, 0);
++      cred->uid = geteuid();
++      cred->gid = getegid();
++      return 0;
++}
++
++static size_t nfs_ucred_getpw_r_size_max(void)
++{
++      long buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
++
++      if (buflen == -1)
++              return 16384;
++      return buflen;
++}
++
++int nfs_ucred_reload_groups(struct nfs_ucred *cred, const struct exportent 
*ep)
++{
++      struct passwd pwd, *pw;
++      uid_t uid = cred->uid;
++      gid_t gid = cred->gid;
++      size_t buflen;
++      char *buf;
++      int ngroups = 0;
++      int ret;
++
++      if (ep->e_flags & (NFSEXP_ALLSQUASH | NFSEXP_ROOTSQUASH) &&
++          (int)uid == ep->e_anonuid)
++              return 0;
++      buflen = nfs_ucred_getpw_r_size_max();
++      buf = alloca(buflen);
++      ret = getpwuid_r(uid, &pwd, buf, buflen, &pw);
++      if (ret != 0)
++              return ret;
++      if (!pw)
++              return ENOENT;
++      if (getgrouplist(pw->pw_name, gid, NULL, &ngroups) == -1 &&
++          ngroups > 0) {
++              gid_t *groups = malloc(ngroups * sizeof(groups[0]));
++              if (groups == NULL)
++                      return ENOMEM;
++              if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) {
++                      free(groups);
++                      return ENOMEM;
++              }
++              free(cred->groups);
++              nfs_ucred_init_groups(cred, groups, ngroups);
++              nfs_ucred_squash_groups(cred, ep);
++      } else
++              nfs_ucred_free_groups(cred);
++      return 0;
++}
++
++static int nfs_ucred_set_effective(const struct nfs_ucred *cred,
++                                 const struct nfs_ucred *saved)
++{
++      uid_t suid = saved ? saved->uid : geteuid();
++      gid_t sgid = saved ? saved->gid : getegid();
++      int ret;
++
++      /* Start with a privileged effective user */
++      if (setresuid(-1, 0, -1) < 0) {
++              xlog(L_WARNING, "can't change privileged user %u-%u. %s",
++                   geteuid(), getegid(), strerror(errno));
++              return errno;
++      }
++
++      if (setgroups(cred->ngroups, cred->groups) == -1) {
++              xlog(L_WARNING, "can't change groups for user %u-%u. %s",
++                   geteuid(), getegid(), strerror(errno));
++              return errno;
++      }
++      if (setresgid(-1, cred->gid, sgid) == -1) {
++              xlog(L_WARNING, "can't change gid for user %u-%u. %s",
++                   geteuid(), getegid(), strerror(errno));
++              ret = errno;
++              goto restore_groups;
++      }
++      if (setresuid(-1, cred->uid, suid) == -1) {
++              xlog(L_WARNING, "can't change uid for user %u-%u. %s",
++                   geteuid(), getegid(), strerror(errno));
++              ret = errno;
++              goto restore_gid;
++      }
++      return 0;
++restore_gid:
++      if (setresgid(-1, sgid, -1) < 0) {
++              xlog(L_WARNING, "can't restore privileged user %u-%u. %s",
++                   geteuid(), getegid(), strerror(errno));
++      }
++restore_groups:
++      if (saved)
++              setgroups(saved->ngroups, saved->groups);
++      else
++              setgroups(0, NULL);
++      return ret;
++}
++
++int nfs_ucred_swap_effective(const struct nfs_ucred *cred,
++                           struct nfs_ucred **savedp)
++{
++      struct nfs_ucred *saved = malloc(sizeof(*saved));
++      int ret;
++
++      if (saved == NULL)
++              return ENOMEM;
++      ret = nfs_ucred_init_effective(saved);
++      if (ret != 0) {
++              free(saved);
++              return ret;
++      }
++      ret = nfs_ucred_set_effective(cred, saved);
++      if (savedp == NULL || ret != 0)
++              nfs_ucred_free(saved);
++      else
++              *savedp = saved;
++      return ret;
++}
+diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
+index 2e1577cc..f6921265 100644
+--- a/support/nfs/Makefile.am
++++ b/support/nfs/Makefile.am
+@@ -7,7 +7,7 @@ libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c 
rpcdispatch.c \
+                  xcommon.c wildmat.c mydaemon.c \
+                  rpc_socket.c getport.c \
+                  svc_socket.c cacheio.c closeall.c nfs_mntent.c \
+-                 svc_create.c atomicio.c strlcat.c strlcpy.c
++                 svc_create.c atomicio.c strlcat.c strlcpy.c ucred.c
+ libnfs_la_LIBADD = libnfsconf.la
+ libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) 
-I$(top_srcdir)/support/reexport
+
+diff --git a/support/nfs/ucred.c b/support/nfs/ucred.c
+new file mode 100644
+index 00000000..6ea8efdf
+--- /dev/null
++++ b/support/nfs/ucred.c
+@@ -0,0 +1,147 @@
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <errno.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <rpc/rpc.h>
++
++#include "exportfs.h"
++#include "nfs_ucred.h"
++
++#ifdef HAVE_TIRPC_GSS_GETCRED
++#include <rpc/rpcsec_gss.h>
++#endif /* HAVE_TIRPC_GSS_GETCRED */
++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
++#include <rpc/auth_des.h>
++#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
++
++static int nfs_ucred_copy_cred(struct nfs_ucred *cred, uid_t uid, gid_t gid,
++                             const gid_t *groups, int ngroups)
++{
++      if (ngroups > 0) {
++              size_t sz = ngroups * sizeof(groups[0]);
++              cred->groups = malloc(sz);
++              if (cred->groups == NULL)
++                      return ENOMEM;
++              cred->ngroups = ngroups;
++              memcpy(cred->groups, groups, sz);
++      } else
++              nfs_ucred_init_groups(cred, NULL, 0);
++      cred->uid = uid;
++      cred->gid = gid;
++      return 0;
++}
++
++static int nfs_ucred_init_cred_squashed(struct nfs_ucred *cred,
++                                      const struct exportent *ep)
++{
++      cred->uid = ep->e_anonuid;
++      cred->gid = ep->e_anongid;
++      nfs_ucred_init_groups(cred, NULL, 0);
++      return 0;
++}
++
++static int nfs_ucred_init_cred(struct nfs_ucred *cred, uid_t uid, gid_t gid,
++                             const gid_t *groups, int ngroups,
++                             const struct exportent *ep)
++{
++      if (ep->e_flags & NFSEXP_ALLSQUASH) {
++              nfs_ucred_init_cred_squashed(cred, ep);
++      } else if (ep->e_flags & NFSEXP_ROOTSQUASH && uid == 0) {
++              nfs_ucred_init_cred_squashed(cred, ep);
++              if (gid != 0)
++                      cred->gid = gid;
++      } else {
++              int ret = nfs_ucred_copy_cred(cred, uid, gid, groups, ngroups);
++              if (ret != 0)
++                      return ret;
++              nfs_ucred_squash_groups(cred, ep);
++      }
++      return 0;
++}
++
++static int nfs_ucred_init_null(struct nfs_ucred *cred,
++                             const struct exportent *ep)
++{
++      return nfs_ucred_init_cred_squashed(cred, ep);
++}
++
++static int nfs_ucred_init_unix(struct nfs_ucred *cred, struct svc_req *rqst,
++                             const struct exportent *ep)
++{
++      struct authunix_parms *aup;
++
++      aup = (struct authunix_parms *)rqst->rq_clntcred;
++      return nfs_ucred_init_cred(cred, aup->aup_uid, aup->aup_gid,
++                                 aup->aup_gids, aup->aup_len, ep);
++}
++
++#ifdef HAVE_TIRPC_GSS_GETCRED
++static int nfs_ucred_init_gss(struct nfs_ucred *cred, struct svc_req *rqst,
++                            const struct exportent *ep)
++{
++      rpc_gss_ucred_t *gss_ucred = NULL;
++
++      if (!rpc_gss_getcred(rqst, NULL, &gss_ucred, NULL) || gss_ucred == NULL)
++              return EINVAL;
++      return nfs_ucred_init_cred(cred, gss_ucred->uid, gss_ucred->gid,
++                                 gss_ucred->gidlist, gss_ucred->gidlen, ep);
++}
++#endif /* HAVE_TIRPC_GSS_GETCRED */
++
++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
++int authdes_getucred(struct authdes_cred *adc, uid_t *uid, gid_t *gid,
++                   int *grouplen, gid_t *groups);
++
++static int nfs_ucred_init_des(struct nfs_ucred *cred, struct svc_req *rqst,
++                            const struct exportent *ep)
++{
++      struct authdes_cred *des_cred;
++      uid_t uid;
++      gid_t gid;
++      int grouplen;
++      gid_t groups[NGROUPS];
++
++      des_cred = (struct authdes_cred *)rqst->rq_clntcred;
++      if (!authdes_getucred(des_cred, &uid, &gid, &grouplen, &groups[0]))
++              return EINVAL;
++      return nfs_ucred_init_cred(cred, uid, gid, groups, grouplen, ep);
++}
++#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
++
++int nfs_ucred_get(struct nfs_ucred **credp, struct svc_req *rqst,
++                const struct exportent *ep)
++{
++      struct nfs_ucred *cred = malloc(sizeof(*cred));
++      int ret;
++
++      *credp = NULL;
++      if (cred == NULL)
++              return ENOMEM;
++      switch (rqst->rq_cred.oa_flavor) {
++      case AUTH_UNIX:
++              ret = nfs_ucred_init_unix(cred, rqst, ep);
++              break;
++#ifdef HAVE_TIRPC_GSS_GETCRED
++      case RPCSEC_GSS:
++              ret = nfs_ucred_init_gss(cred, rqst, ep);
++              break;
++#endif /* HAVE_TIRPC_GSS_GETCRED */
++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
++      case AUTH_DES:
++              ret = nfs_ucred_init_des(cred, rqst, ep);
++              break;
++#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
++      default:
++              ret = nfs_ucred_init_null(cred, ep);
++              break;
++      }
++      if (ret == 0) {
++              *credp = cred;
++              return 0;
++      }
++      free(cred);
++      return ret;
++}
+--
+2.35.6
diff --git a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch 
b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch
new file mode 100644
index 0000000000..bd36331f6c
--- /dev/null
+++ b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch
@@ -0,0 +1,253 @@
+From d2e0fff6ad07e71d405ddbbe7eb3a07407c9a204 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <[email protected]>
+Date: Thu, 5 Mar 2026 10:41:02 -0500
+Subject: [PATCH 4/4] Fix access checks when mounting subdirectories in NFSv3
+
+If a NFSv3 client asks to mount a subdirectory of one of the exported
+directories, then apply the RPC credential together with any root
+or all squash rules that would apply to the client in question.
+
+CVE: CVE-2025-12801
+Upstream-Status: Backport 
[https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=f36bd900a899088ca1925de079bd58d6205a1f3c]
+
+Reviewed-by: Jeff Layton <[email protected]>
+Signed-off-by: Trond Myklebust <[email protected]>
+Signed-off-by: Scott Mayhew <[email protected]>
+Signed-off-by: Steve Dickson <[email protected]>
+(cherry picked from commit f36bd900a899088ca1925de079bd58d6205a1f3c)
+Signed-off-by: Deepak Rathore <[email protected]>
+---
+ nfs.conf                    |  1 +
+ support/include/nfsd_path.h |  9 ++++++++-
+ support/misc/nfsd_path.c    | 32 ++++++++++++++++++++++++++++++--
+ utils/mountd/mountd.c       | 28 ++++++++++++++++++++++++++--
+ utils/mountd/mountd.man     | 26 ++++++++++++++++++++++++++
+ 5 files changed, 91 insertions(+), 5 deletions(-)
+
+diff --git a/nfs.conf b/nfs.conf
+index 3cca68c3..ddf0c143 100644
+--- a/nfs.conf
++++ b/nfs.conf
+@@ -46,6 +46,7 @@
+ # ttl=1800
+ [mountd]
+ # debug="all|auth|call|general|parse"
++# apply-root-cred=n
+ # manage-gids=n
+ # descriptors=0
+ # port=0
+diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
+index 3e5a2f5d..06c0f2f4 100644
+--- a/support/include/nfsd_path.h
++++ b/support/include/nfsd_path.h
+@@ -9,6 +9,7 @@
+ struct file_handle;
+ struct statfs;
+ struct nfsd_task_t;
++struct nfs_ucred;
+
+ void          nfsd_path_init(void);
+
+@@ -18,7 +19,8 @@ char *               nfsd_path_prepend_dir(const char *dir, 
const char *pathname);
+
+ int           nfsd_path_stat(const char *pathname, struct stat *statbuf);
+ int           nfsd_path_lstat(const char *pathname, struct stat *statbuf);
+-int           nfsd_openat(int dirfd, const char *path, int flags);
++int           nfsd_cred_openat(const struct nfs_ucred *cred, int dirfd,
++                               const char *path, int flags);
+
+ int           nfsd_path_statfs(const char *pathname,
+                                  struct statfs *statbuf);
+@@ -31,4 +33,9 @@ ssize_t              nfsd_path_write(int fd, void* buf, 
size_t len);
+ int           nfsd_name_to_handle_at(int fd, const char *path,
+                                      struct file_handle *fh,
+                                      int *mount_id, int flags);
++
++static inline int nfsd_openat(int dirfd, const char *path, int flags)
++{
++      return nfsd_cred_openat(NULL, dirfd, path, flags);
++}
+ #endif
+diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
+index dfe88e4f..6466666d 100644
+--- a/support/misc/nfsd_path.c
++++ b/support/misc/nfsd_path.c
+@@ -17,6 +17,7 @@
+ #include "xstat.h"
+ #include "nfslib.h"
+ #include "nfsd_path.h"
++#include "nfs_ucred.h"
+ #include "workqueue.h"
+
+ static struct xthread_workqueue *nfsd_wq = NULL;
+@@ -204,6 +205,7 @@ nfsd_realpath(const char *path, char *resolved_buf)
+ }
+
+ struct nfsd_openat_t {
++      const struct nfs_ucred *cred;
+       const char *path;
+       int dirfd;
+       int flags;
+@@ -220,15 +222,41 @@ static void nfsd_openatfunc(void *data)
+               d->res_error = errno;
+ }
+
+-int nfsd_openat(int dirfd, const char *path, int flags)
++static void nfsd_cred_openatfunc(void *data)
++{
++      struct nfsd_openat_t *d = data;
++      struct nfs_ucred *saved = NULL;
++      int ret;
++
++      ret = nfs_ucred_swap_effective(d->cred, &saved);
++      if (ret != 0) {
++              d->res_fd = -1;
++              d->res_error = ret;
++              return;
++      }
++
++      nfsd_openatfunc(data);
++
++      if (saved != NULL) {
++              nfs_ucred_swap_effective(saved, NULL);
++              nfs_ucred_free(saved);
++      }
++}
++
++int nfsd_cred_openat(const struct nfs_ucred *cred, int dirfd, const char 
*path,
++                   int flags)
+ {
+       struct nfsd_openat_t open_buf = {
++              .cred = cred,
+               .path = path,
+               .dirfd = dirfd,
+               .flags = flags,
+       };
+
+-      nfsd_run_task(nfsd_openatfunc, &open_buf);
++      if (cred)
++              nfsd_run_task(nfsd_cred_openatfunc, &open_buf);
++      else
++              nfsd_run_task(nfsd_openatfunc, &open_buf);
+       if (open_buf.res_fd == -1)
+               errno = open_buf.res_error;
+       return open_buf.res_fd;
+diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
+index f43ebef5..6e6777cd 100644
+--- a/utils/mountd/mountd.c
++++ b/utils/mountd/mountd.c
+@@ -31,6 +31,7 @@
+ #include "nfsd_path.h"
+ #include "nfslib.h"
+ #include "export.h"
++#include "nfs_ucred.h"
+
+ extern void my_svc_run(void);
+
+@@ -40,6 +41,7 @@ static struct nfs_fh_len *get_rootfh(struct svc_req *, 
dirpath *, nfs_export **,
+
+ int reverse_resolve = 0;
+ int manage_gids;
++int apply_root_cred;
+ int use_ipaddr = -1;
+
+ /* PRC: a high-availability callout program can be specified with -H
+@@ -74,9 +76,10 @@ static struct option longopts[] =
+       { "log-auth", 0, 0, 'l'},
+       { "cache-use-ipaddr", 0, 0, 'i'},
+       { "ttl", 1, 0, 'T'},
++      { "apply-root-cred", 0, 0, 'c' },
+       { NULL, 0, 0, 0 }
+ };
+-static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:";
++static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:c";
+
+ #define NFSVERSBIT(vers)      (0x1 << (vers - 1))
+ #define NFSVERSBIT_ALL                (NFSVERSBIT(2) | NFSVERSBIT(3) | 
NFSVERSBIT(4))
+@@ -453,11 +456,27 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
nfs_export **expret,
+       while (*subpath == '/')
+               subpath++;
+       if (*subpath != '\0') {
++              struct nfs_ucred *cred = NULL;
+               int fd;
+
++              /* Load the user cred */
++              if (!apply_root_cred) {
++                      nfs_ucred_get(&cred, rqstp, &exp->m_export);
++                      if (cred == NULL) {
++                              xlog(L_WARNING, "can't retrieve credential");
++                              *error = MNT3ERR_ACCES;
++                              close(dirfd);
++                              return NULL;
++                      }
++                      if (manage_gids)
++                              nfs_ucred_reload_groups(cred, &exp->m_export);
++              }
++
+               /* Just perform a lookup of the path */
+-              fd = nfsd_openat(dirfd, subpath, O_PATH);
++              fd = nfsd_cred_openat(cred, dirfd, subpath, O_PATH);
+               close(dirfd);
++              if (cred)
++                      nfs_ucred_free(cred);
+               if (fd == -1) {
+                       xlog(L_WARNING, "can't open exported dir %s: %s", p,
+                            strerror(errno));
+@@ -681,6 +700,8 @@ read_mountd_conf(char **argv)
+       ttl = conf_get_num("mountd", "ttl", default_ttl);
+       if (ttl > 0)
+               default_ttl = ttl;
++      apply_root_cred = conf_get_bool("mountd", "apply-root-cred",
++                                      apply_root_cred);
+ }
+
+ int
+@@ -794,6 +815,9 @@ main(int argc, char **argv)
+                       }
+                       default_ttl = ttl;
+                       break;
++              case 'c':
++                      apply_root_cred = 1;
++                      break;
+               case 0:
+                       break;
+               case '?':
+diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
+index a206a3e2..f4f1fc23 100644
+--- a/utils/mountd/mountd.man
++++ b/utils/mountd/mountd.man
+@@ -242,6 +242,32 @@ can support both NFS version 2 and the newer version 3.
+ Print the version of
+ .B rpc.mountd
+ and exit.
++.TP
++.B \-c " or " \-\-apply-root-cred
++When mountd is asked to allow a NFSv3 mount to a subdirectory of the
++exported directory, then it will check if the user asking to mount has
++lookup rights to the directories below that exported directory. When
++performing the check, mountd will apply any root squash or all squash
++rules that were specified for that client.
++
++Performing lookup checks as the user requires that the mountd daemon
++be run as root or that it be given CAP_SETUID and CAP_SETGID privileges
++so that it can change its own effective user and effective group settings.
++When troubleshooting, please also note that LSM frameworks such as SELinux
++can sometimes prevent the daemon from changing the effective user/groups
++despite the capability settings.
++
++In earlier versions of mountd, the same checks were performed using the
++mountd daemon's root privileges, meaning that it could authorise access
++to directories that are not normally accessible to the user requesting
++to mount them. This option enables that legacy behaviour.
++
++.BR Note:
++If there is a need to provide access to specific subdirectories that
++are not normally accessible to a client, it is always possible to add
++export entries that explicitly grant such access. That ability does
++not depend on this option being enabled.
++
+ .TP
+ .B \-g " or " \-\-manage-gids
+ Accept requests from the kernel to map user id numbers into  lists of
+--
+2.35.6
diff --git a/meta/recipes-connectivity/nfs-utils/nfs-utils_2.8.4.bb 
b/meta/recipes-connectivity/nfs-utils/nfs-utils_2.8.4.bb
index 08e7dd8900..c7f0656ed8 100644
--- a/meta/recipes-connectivity/nfs-utils/nfs-utils_2.8.4.bb
+++ b/meta/recipes-connectivity/nfs-utils/nfs-utils_2.8.4.bb
@@ -24,6 +24,10 @@ SRC_URI = 
"${KERNELORG_MIRROR}/linux/utils/nfs-utils/${PV}/nfs-utils-${PV}.tar.x
            file://0001-locktest-Makefile.am-Do-not-use-build-flags.patch \
            file://0004-Use-nogroup-for-nobody-group.patch \
            file://0005-find-OE-provided-Kerberos.patch \
+                  file://CVE-2025-12801-depended_p1.patch \
+                  file://CVE-2025-12801-depended_p2.patch \
+                  file://CVE-2025-12801-depended_p3.patch \
+                  file://CVE-2025-12801.patch \
            "
 
 SRC_URI[sha256sum] = 
"11c4cc598a434d7d340bad3e072a373ba1dcc2c49f855d44b202222b78ecdbf5"
-- 
2.35.6

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#233411): 
https://lists.openembedded.org/g/openembedded-core/message/233411
Mute This Topic: https://lists.openembedded.org/mt/118385408/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

  • [OE-core][whinlatter][PATCH] nfs... Deepak Rathore via lists.openembedded.org

Reply via email to