File Fix-access-checks-when-mounting-subdirectories-in-NFSv3.patch of Package nfs-utils

From: Trond Myklebust <trond.myklebust@hammerspace.com>
Date: Thu, 5 Mar 2026 10:41:02 -0500
Subject: Fix access checks when mounting subdirectories in NFSv3
Git-repo: git://git.linux-nfs.org/projects/steved/nfs-utils.git
Git-commit: f36bd900a899088ca1925de079bd58d6205a1f3c
References: CVE-2025-12801 bsc#1259204

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.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
Acked-by: Anthony Iliopoulos <ailiop@suse.com>
---
 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(-)

Index: nfs-utils-2.6.4/nfs.conf
===================================================================
--- nfs-utils-2.6.4.orig/nfs.conf
+++ nfs-utils-2.6.4/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
Index: nfs-utils-2.6.4/support/include/nfsd_path.h
===================================================================
--- nfs-utils-2.6.4.orig/support/include/nfsd_path.h
+++ nfs-utils-2.6.4/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
 
 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* b
 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
Index: nfs-utils-2.6.4/support/misc/nfsd_path.c
===================================================================
--- nfs-utils-2.6.4.orig/support/misc/nfsd_path.c
+++ nfs-utils-2.6.4/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 *re
 }
 
 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;
Index: nfs-utils-2.6.4/utils/mountd/mountd.c
===================================================================
--- nfs-utils-2.6.4.orig/utils/mountd/mountd.c
+++ nfs-utils-2.6.4/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(str
 
 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, dirpat
 	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 '?':
Index: nfs-utils-2.6.4/utils/mountd/mountd.man
===================================================================
--- nfs-utils-2.6.4.orig/utils/mountd/mountd.man
+++ nfs-utils-2.6.4/utils/mountd/mountd.man
@@ -243,6 +243,32 @@ 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
 group id numbers for use in access control.  An NFS request will
openSUSE Build Service is sponsored by