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]] -=-=-=-=-=-=-=-=-=-=-=-
