import CS nfs-utils-2.5.4-42.el9

This commit is contained in:
AlmaLinux RelEng Bot 2026-03-30 10:01:59 -04:00
parent 2492c62ffb
commit 2711ce8942
10 changed files with 1849 additions and 1 deletions

View File

@ -0,0 +1,251 @@
From ff89a25378179d2a4dc25f6fcfcc1041b29c5407 Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Tue, 6 Jan 2026 13:42:46 -0500
Subject: [PATCH 6/6] Fix access checks when mounting subdirectories in NFSv3
JIRA: https://issues.redhat.com/browse/RHEL-127105
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: Steve Dickson <steved@redhat.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.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(-)
diff --git a/nfs.conf b/nfs.conf
index d32f067e..42518150 100644
--- a/nfs.conf
+++ b/nfs.conf
@@ -46,6 +46,7 @@ use-gss-proxy=1
# 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 311e284d..b989a8b9 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -32,6 +32,7 @@
#include "nfsd_path.h"
#include "nfslib.h"
#include "export.h"
+#include "nfs_ucred.h"
extern void my_svc_run(void);
@@ -41,6 +42,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
@@ -75,9 +77,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))
@@ -527,11 +530,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));
@@ -755,6 +774,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
@@ -868,6 +889,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 c85c5d61..24e750a6 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.52.0

View File

@ -0,0 +1,457 @@
From 14a946ce3dbc7dba964ec3d1e24b21899900325e Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Tue, 6 Jan 2026 13:25:59 -0500
Subject: [PATCH 2/6] NFS export symlink vulnerability fix
JIRA: https://issues.redhat.com/browse/RHEL-127105
Conflicts - support/nfs/exports.c - context diff because RHEL9 does not
have commit d214e3aa ("Implement reexport= export option")
commit cd90f29257904f36509ea5a04a86f42398fbe94a
Author: Christopher Bii <christopherbii@hyub.org>
Date: Wed Jan 15 12:10:48 2025 -0500
NFS export symlink vulnerability fix
Replaced dangerous use of realpath within support/nfs/export.c with
nfsd_realpath variant that is executed within the chrooted thread
rather than main thread.
Implemented nfsd_path.h methods to work securely within chrooted
thread using nfsd_run_task() help
Signed-off-by: Christopher Bii <christopherbii@hyub.org>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
support/export/cache.c | 2 +-
support/include/nfsd_path.h | 5 +-
support/misc/nfsd_path.c | 257 +++++++++++-------------------------
support/nfs/exports.c | 3 +-
4 files changed, 83 insertions(+), 184 deletions(-)
diff --git a/support/export/cache.c b/support/export/cache.c
index 9354f71d..e3efe4ad 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -64,7 +64,7 @@ static ssize_t cache_read(int fd, char *buf, size_t len)
return nfsd_path_read(fd, buf, len);
}
-static ssize_t cache_write(int fd, const char *buf, size_t len)
+static ssize_t cache_write(int fd, void *buf, size_t len)
{
return nfsd_path_write(fd, buf, len);
}
diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
index aa1e1dd0..f600fb5a 100644
--- a/support/include/nfsd_path.h
+++ b/support/include/nfsd_path.h
@@ -8,6 +8,7 @@
struct file_handle;
struct statfs;
+struct nfsd_task_t;
void nfsd_path_init(void);
@@ -23,8 +24,8 @@ int nfsd_path_statfs(const char *pathname,
char * nfsd_realpath(const char *path, char *resolved_path);
-ssize_t nfsd_path_read(int fd, char *buf, size_t len);
-ssize_t nfsd_path_write(int fd, const char *buf, size_t len);
+ssize_t nfsd_path_read(int fd, void* buf, size_t len);
+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,
diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
index c3dea4f0..caec33ca 100644
--- a/support/misc/nfsd_path.c
+++ b/support/misc/nfsd_path.c
@@ -19,7 +19,20 @@
#include "nfsd_path.h"
#include "workqueue.h"
-static struct xthread_workqueue *nfsd_wq;
+static struct xthread_workqueue *nfsd_wq = NULL;
+
+struct nfsd_task_t {
+ int ret;
+ void* data;
+};
+/* Function used to offload tasks that must be ran within the correct
+ * chroot environment.
+ */
+static void
+nfsd_run_task(void (*func)(void*), void* data){
+ nfsd_wq ? xthread_work_run_sync(nfsd_wq, func, data) : func(data);
+};
+
static int
nfsd_path_isslash(const char *path)
@@ -124,224 +137,119 @@ nfsd_path_init(void)
}
struct nfsd_stat_data {
- const char *pathname;
- struct stat *statbuf;
- int ret;
- int err;
+ const char *pathname;
+ struct stat *statbuf;
+ int (*stat_handler)(const char*, struct stat*);
};
static void
-nfsd_statfunc(void *data)
-{
- struct nfsd_stat_data *d = data;
-
- d->ret = xstat(d->pathname, d->statbuf);
- if (d->ret < 0)
- d->err = errno;
-}
-
-static void
-nfsd_lstatfunc(void *data)
+nfsd_handle_stat(void *data)
{
- struct nfsd_stat_data *d = data;
-
- d->ret = xlstat(d->pathname, d->statbuf);
- if (d->ret < 0)
- d->err = errno;
+ struct nfsd_task_t* t = data;
+ struct nfsd_stat_data* d = t->data;
+ t->ret = d->stat_handler(d->pathname, d->statbuf);
}
static int
-nfsd_run_stat(struct xthread_workqueue *wq,
- void (*func)(void *),
- const char *pathname,
- struct stat *statbuf)
+nfsd_run_stat(const char *pathname,
+ struct stat *statbuf,
+ int (*handler)(const char*, struct stat*))
{
- struct nfsd_stat_data data = {
- pathname,
- statbuf,
- 0,
- 0
- };
- xthread_work_run_sync(wq, func, &data);
- if (data.ret < 0)
- errno = data.err;
- return data.ret;
+ struct nfsd_task_t t;
+ struct nfsd_stat_data d = { pathname, statbuf, handler };
+ t.data = &d;
+ nfsd_run_task(nfsd_handle_stat, &t);
+ return t.ret;
}
int
nfsd_path_stat(const char *pathname, struct stat *statbuf)
{
- if (!nfsd_wq)
- return xstat(pathname, statbuf);
- return nfsd_run_stat(nfsd_wq, nfsd_statfunc, pathname, statbuf);
+ return nfsd_run_stat(pathname, statbuf, stat);
}
int
-nfsd_path_lstat(const char *pathname, struct stat *statbuf)
-{
- if (!nfsd_wq)
- return xlstat(pathname, statbuf);
- return nfsd_run_stat(nfsd_wq, nfsd_lstatfunc, pathname, statbuf);
-}
-
-struct nfsd_statfs_data {
- const char *pathname;
- struct statfs *statbuf;
- int ret;
- int err;
+nfsd_path_lstat(const char* pathname, struct stat* statbuf){
+ return nfsd_run_stat(pathname, statbuf, lstat);
};
-static void
-nfsd_statfsfunc(void *data)
-{
- struct nfsd_statfs_data *d = data;
-
- d->ret = statfs(d->pathname, d->statbuf);
- if (d->ret < 0)
- d->err = errno;
-}
-
-static int
-nfsd_run_statfs(struct xthread_workqueue *wq,
- const char *pathname,
- struct statfs *statbuf)
-{
- struct nfsd_statfs_data data = {
- pathname,
- statbuf,
- 0,
- 0
- };
- xthread_work_run_sync(wq, nfsd_statfsfunc, &data);
- if (data.ret < 0)
- errno = data.err;
- return data.ret;
-}
-
int
-nfsd_path_statfs(const char *pathname, struct statfs *statbuf)
+nfsd_path_statfs(const char* pathname, struct statfs* statbuf)
{
- if (!nfsd_wq)
- return statfs(pathname, statbuf);
- return nfsd_run_statfs(nfsd_wq, pathname, statbuf);
-}
+ return nfsd_run_stat(pathname, (struct stat*)statbuf, (int (*)(const char*, struct stat*))statfs);
+};
-struct nfsd_realpath_data {
- const char *pathname;
- char *resolved;
- int err;
+struct nfsd_realpath_t {
+ const char* path;
+ char* resolved_buf;
+ char* res_ptr;
};
static void
nfsd_realpathfunc(void *data)
{
- struct nfsd_realpath_data *d = data;
-
- d->resolved = realpath(d->pathname, d->resolved);
- if (!d->resolved)
- d->err = errno;
+ struct nfsd_realpath_t *d = data;
+ d->res_ptr = realpath(d->path, d->resolved_buf);
}
-char *
-nfsd_realpath(const char *path, char *resolved_path)
+char*
+nfsd_realpath(const char *path, char *resolved_buf)
{
- struct nfsd_realpath_data data = {
- path,
- resolved_path,
- 0
- };
-
- if (!nfsd_wq)
- return realpath(path, resolved_path);
-
- xthread_work_run_sync(nfsd_wq, nfsd_realpathfunc, &data);
- if (!data.resolved)
- errno = data.err;
- return data.resolved;
+ struct nfsd_realpath_t realpath_buf = {
+ .path = path,
+ .resolved_buf = resolved_buf
+ };
+ nfsd_run_task(nfsd_realpathfunc, &realpath_buf);
+ return realpath_buf.res_ptr;
}
-struct nfsd_read_data {
- int fd;
- char *buf;
- size_t len;
- ssize_t ret;
- int err;
+struct nfsd_rw_data {
+ int fd;
+ void* buf;
+ size_t len;
+ ssize_t bytes_read;
};
static void
nfsd_readfunc(void *data)
{
- struct nfsd_read_data *d = data;
-
- d->ret = read(d->fd, d->buf, d->len);
- if (d->ret < 0)
- d->err = errno;
+ struct nfsd_rw_data* t = (struct nfsd_rw_data*)data;
+ t->bytes_read = read(t->fd, t->buf, t->len);
}
static ssize_t
-nfsd_run_read(struct xthread_workqueue *wq, int fd, char *buf, size_t len)
+nfsd_run_read(int fd, void* buf, size_t len)
{
- struct nfsd_read_data data = {
- fd,
- buf,
- len,
- 0,
- 0
- };
- xthread_work_run_sync(wq, nfsd_readfunc, &data);
- if (data.ret < 0)
- errno = data.err;
- return data.ret;
+ struct nfsd_rw_data d = { .fd = fd, .buf = buf, .len = len };
+ nfsd_run_task(nfsd_readfunc, &d);
+ return d.bytes_read;
}
ssize_t
-nfsd_path_read(int fd, char *buf, size_t len)
+nfsd_path_read(int fd, void* buf, size_t len)
{
- if (!nfsd_wq)
- return read(fd, buf, len);
- return nfsd_run_read(nfsd_wq, fd, buf, len);
+ return nfsd_run_read(fd, buf, len);
}
-struct nfsd_write_data {
- int fd;
- const char *buf;
- size_t len;
- ssize_t ret;
- int err;
-};
-
static void
nfsd_writefunc(void *data)
{
- struct nfsd_write_data *d = data;
-
- d->ret = write(d->fd, d->buf, d->len);
- if (d->ret < 0)
- d->err = errno;
+ struct nfsd_rw_data* d = data;
+ d->bytes_read = write(d->fd, d->buf, d->len);
}
static ssize_t
-nfsd_run_write(struct xthread_workqueue *wq, int fd, const char *buf, size_t len)
+nfsd_run_write(int fd, void* buf, size_t len)
{
- struct nfsd_write_data data = {
- fd,
- buf,
- len,
- 0,
- 0
- };
- xthread_work_run_sync(wq, nfsd_writefunc, &data);
- if (data.ret < 0)
- errno = data.err;
- return data.ret;
+ struct nfsd_rw_data d = { .fd = fd, .buf = buf, .len = len };
+ nfsd_run_task(nfsd_writefunc, &d);
+ return d.bytes_read;
}
ssize_t
-nfsd_path_write(int fd, const char *buf, size_t len)
+nfsd_path_write(int fd, void* buf, size_t len)
{
- if (!nfsd_wq)
- return write(fd, buf, len);
- return nfsd_run_write(nfsd_wq, fd, buf, len);
+ return nfsd_run_write(fd, buf, len);
}
#if defined(HAVE_NAME_TO_HANDLE_AT)
@@ -352,23 +260,18 @@ struct nfsd_handle_data {
int *mount_id;
int flags;
int ret;
- int err;
};
static void
nfsd_name_to_handle_func(void *data)
{
struct nfsd_handle_data *d = data;
-
- d->ret = name_to_handle_at(d->fd, d->path,
- d->fh, d->mount_id, d->flags);
- if (d->ret < 0)
- d->err = errno;
+ d->ret = name_to_handle_at(d->fd, d->path, d->fh, d->mount_id, d->flags);
}
static int
-nfsd_run_name_to_handle_at(struct xthread_workqueue *wq,
- int fd, const char *path, struct file_handle *fh,
+nfsd_run_name_to_handle_at(int fd, const char *path,
+ struct file_handle *fh,
int *mount_id, int flags)
{
struct nfsd_handle_data data = {
@@ -377,25 +280,19 @@ nfsd_run_name_to_handle_at(struct xthread_workqueue *wq,
fh,
mount_id,
flags,
- 0,
0
};
- xthread_work_run_sync(wq, nfsd_name_to_handle_func, &data);
- if (data.ret < 0)
- errno = data.err;
+ nfsd_run_task(nfsd_name_to_handle_func, &data);
return data.ret;
}
int
-nfsd_name_to_handle_at(int fd, const char *path, struct file_handle *fh,
+nfsd_name_to_handle_at(int fd, const char *path,
+ struct file_handle *fh,
int *mount_id, int flags)
{
- if (!nfsd_wq)
- return name_to_handle_at(fd, path, fh, mount_id, flags);
-
- return nfsd_run_name_to_handle_at(nfsd_wq, fd, path, fh,
- mount_id, flags);
+ return nfsd_run_name_to_handle_at(fd, path, fh, mount_id, flags);
}
#else
int
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index d36f7664..178534ac 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -31,6 +31,7 @@
#include "xlog.h"
#include "xio.h"
#include "pseudoflavors.h"
+#include "nfsd_path.h"
#define EXPORT_DEFAULT_FLAGS \
(NFSEXP_READONLY|NFSEXP_ROOTSQUASH|NFSEXP_GATHERED_WRITES|NFSEXP_NOSUBTREECHECK)
@@ -198,7 +199,7 @@ getexportent(int fromkernel, int fromexports)
return NULL;
}
/* resolve symlinks */
- if (realpath(ee.e_path, rpath) != NULL) {
+ if (nfsd_realpath(ee.e_path, rpath) != NULL) {
rpath[sizeof (rpath) - 1] = '\0';
strncpy(ee.e_path, rpath, sizeof (ee.e_path) - 1);
ee.e_path[sizeof (ee.e_path) - 1] = '\0';
--
2.52.0

View File

@ -0,0 +1,184 @@
From f0f6a29eab7598983e5a2f1c576750bdab9088c4 Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Tue, 6 Jan 2026 13:25:32 -0500
Subject: [PATCH 1/6] Replace statfs64 with statfs
JIRA: https://issues.redhat.com/browse/RHEL-127105
commit 2c0b524983925ebd6b8ab8b7952fba2aea41c94d
Author: Khem Raj <raj.khem@gmail.com>
Date: Tue Jan 10 09:42:00 2023 -0500
Replace statfs64 with statfs
autoconf AC_SYS_LARGEFILE is used by configure to add needed defines
when needed for enabling 64bit off_t, therefore replacing statfs64 with
statfs should be functionally same. Additionally this helps compiling
with latest musl where 64bit LFS functions like statfs64 and friends are
now moved under _LARGEFILE64_SOURCE feature test macro, this works on
glibc systems because _GNU_SOURCE macros also enables
_LARGEFILE64_SOURCE indirectly. This is not case with musl and this
latest issue is exposed.
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
support/export/cache.c | 14 +++++++-------
support/include/nfsd_path.h | 6 +++---
support/misc/nfsd_path.c | 24 ++++++++++++------------
utils/exportfs/exportfs.c | 4 ++--
4 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/support/export/cache.c b/support/export/cache.c
index 396b3b73..9354f71d 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -346,27 +346,27 @@ static int uuid_by_path(char *path, int type, size_t uuidlen, char *uuid)
/* Possible sources of uuid are
* - blkid uuid
- * - statfs64 uuid
+ * - statfs uuid
*
- * On some filesystems (e.g. vfat) the statfs64 uuid is simply an
+ * On some filesystems (e.g. vfat) the statfs uuid is simply an
* encoding of the device that the filesystem is mounted from, so
* it we be very bad to use that (as device numbers change). blkid
* must be preferred.
- * On other filesystems (e.g. btrfs) the statfs64 uuid contains
+ * On other filesystems (e.g. btrfs) the statfs uuid contains
* important info that the blkid uuid cannot contain: This happens
* when multiple subvolumes are exported (they have the same
- * blkid uuid but different statfs64 uuids).
+ * blkid uuid but different statfs uuids).
* We rely on get_uuid_blkdev *knowing* which is which and not returning
- * a uuid for filesystems where the statfs64 uuid is better.
+ * a uuid for filesystems where the statfs uuid is better.
*
*/
- struct statfs64 st;
+ struct statfs st;
char fsid_val[17];
const char *blkid_val = NULL;
const char *val;
int rc;
- rc = nfsd_path_statfs64(path, &st);
+ rc = nfsd_path_statfs(path, &st);
if (type == 0 && rc == 0) {
const unsigned long *bad;
diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
index 3b73aadd..aa1e1dd0 100644
--- a/support/include/nfsd_path.h
+++ b/support/include/nfsd_path.h
@@ -7,7 +7,7 @@
#include <sys/stat.h>
struct file_handle;
-struct statfs64;
+struct statfs;
void nfsd_path_init(void);
@@ -18,8 +18,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_path_statfs64(const char *pathname,
- struct statfs64 *statbuf);
+int nfsd_path_statfs(const char *pathname,
+ struct statfs *statbuf);
char * nfsd_realpath(const char *path, char *resolved_path);
diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
index 65e53c13..c3dea4f0 100644
--- a/support/misc/nfsd_path.c
+++ b/support/misc/nfsd_path.c
@@ -184,46 +184,46 @@ nfsd_path_lstat(const char *pathname, struct stat *statbuf)
return nfsd_run_stat(nfsd_wq, nfsd_lstatfunc, pathname, statbuf);
}
-struct nfsd_statfs64_data {
+struct nfsd_statfs_data {
const char *pathname;
- struct statfs64 *statbuf;
+ struct statfs *statbuf;
int ret;
int err;
};
static void
-nfsd_statfs64func(void *data)
+nfsd_statfsfunc(void *data)
{
- struct nfsd_statfs64_data *d = data;
+ struct nfsd_statfs_data *d = data;
- d->ret = statfs64(d->pathname, d->statbuf);
+ d->ret = statfs(d->pathname, d->statbuf);
if (d->ret < 0)
d->err = errno;
}
static int
-nfsd_run_statfs64(struct xthread_workqueue *wq,
+nfsd_run_statfs(struct xthread_workqueue *wq,
const char *pathname,
- struct statfs64 *statbuf)
+ struct statfs *statbuf)
{
- struct nfsd_statfs64_data data = {
+ struct nfsd_statfs_data data = {
pathname,
statbuf,
0,
0
};
- xthread_work_run_sync(wq, nfsd_statfs64func, &data);
+ xthread_work_run_sync(wq, nfsd_statfsfunc, &data);
if (data.ret < 0)
errno = data.err;
return data.ret;
}
int
-nfsd_path_statfs64(const char *pathname, struct statfs64 *statbuf)
+nfsd_path_statfs(const char *pathname, struct statfs *statbuf)
{
if (!nfsd_wq)
- return statfs64(pathname, statbuf);
- return nfsd_run_statfs64(nfsd_wq, pathname, statbuf);
+ return statfs(pathname, statbuf);
+ return nfsd_run_statfs(nfsd_wq, pathname, statbuf);
}
struct nfsd_realpath_data {
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index a87a7806..215037a9 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -513,7 +513,7 @@ validate_export(nfs_export *exp)
*/
struct stat stb;
char *path = exportent_realpath(&exp->m_export);
- struct statfs64 stf;
+ struct statfs stf;
int fs_has_fsid = 0;
if (stat(path, &stb) < 0) {
@@ -528,7 +528,7 @@ validate_export(nfs_export *exp)
if (!can_test())
return;
- if (!statfs64(path, &stf) &&
+ if (!statfs(path, &stf) &&
(stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
fs_has_fsid = 1;
--
2.52.0

View File

@ -0,0 +1,52 @@
From 8600bbb7727df779ba1104c9f0c574b06be116a3 Mon Sep 17 00:00:00 2001
From: Olga Kornievskaia <okorniev@redhat.com>
Date: Tue, 18 Nov 2025 10:23:27 -0500
Subject: [nfs-utils PATCH] gssd: protect kerberos ticket cache access
gssd_get_single_krb5_cred() is a function that's will (for when needed)
send a TGT request to the KDC and then store it in a credential cache.
If multiple threads (eg., parallel mounts) are making an upcall at the
same time then getting creds and storing creds need to be serialized due
to do kerberos API not being concurrency safe.
Fixes: https://issues.redhat.com/browse/RHEL-103627
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
utils/gssd/krb5_util.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 09625fb9..137cffda 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -456,12 +456,14 @@ gssd_get_single_krb5_cred(krb5_context context,
krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
#endif
+ pthread_mutex_lock(&ple_lock);
if ((code = krb5_get_init_creds_opt_set_out_ccache(context, opts,
ccache))) {
k5err = gssd_k5_err_msg(context, code);
printerr(1, "WARNING: %s while initializing ccache for "
"principal '%s' using keytab '%s'\n", k5err,
pname ? pname : "<unparsable>", kt_name);
+ pthread_mutex_unlock(&ple_lock);
goto out;
}
if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
@@ -470,10 +472,10 @@ gssd_get_single_krb5_cred(krb5_context context,
printerr(1, "WARNING: %s while getting initial ticket for "
"principal '%s' using keytab '%s'\n", k5err,
pname ? pname : "<unparsable>", kt_name);
+ pthread_mutex_unlock(&ple_lock);
goto out;
}
- pthread_mutex_lock(&ple_lock);
ple->endtime = my_creds.times.endtime;
pthread_mutex_unlock(&ple_lock);
--
2.52.0

View File

@ -0,0 +1,79 @@
From ff21df893d23a0d18bbf4d5b397f09a1bc434888 Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Tue, 6 Jan 2026 13:33:25 -0500
Subject: [PATCH 3/6] mountd: Minor refactor of get_rootfh()
JIRA: https://issues.redhat.com/browse/RHEL-127105
Perform the mountpoint checks before checking the user path.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
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 bcf749fa..c0017b01 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -486,6 +486,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));
@@ -500,12 +517,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",
@@ -513,17 +524,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.52.0

View File

@ -0,0 +1,179 @@
From 320149c8f8bf0b574a09de29eb7d750318b2684b Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Tue, 6 Jan 2026 13:37:37 -0500
Subject: [PATCH 4/6] mountd: Separate lookup of the exported directory and the
mount path
JIRA: https://issues.redhat.com/browse/RHEL-127105
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.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
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 c0017b01..311e284d 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -466,7 +466,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 = "/";
@@ -486,12 +489,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:
@@ -500,18 +512,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.52.0

View File

@ -0,0 +1,60 @@
commit 162a0093a86411c4ce46d44660c57b98e4879701
Author: Thiago Becker <tbecker@redhat.com>
Date: Wed Sep 10 08:21:47 2025 -0500
nfsrahead: modify get_device_info logic
There are a few reports of failures by nfsrahead to set the read ahead
when the nfs mount information is not available when the udev event
fires. This was alleviated by retrying to read mountinfo multiple times,
but some cases where still failing to find the device information. To
further alleviate this issue, this patch adds a 50ms delay between
attempts. To not incur into unecessary delays, the logic in
get_device_info is reworked.
While we are in this, remove a second loop of reptitions of
get_device_info.
Signed-off-by: Thiago Becker <tbecker@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c
index 8a11cf1a..b7b889ff 100644
--- a/tools/nfsrahead/main.c
+++ b/tools/nfsrahead/main.c
@@ -117,9 +117,11 @@ out_free_device_info:
static int get_device_info(const char *device_number, struct device_info *device_info)
{
- int ret = ENOENT;
- for (int retry_count = 0; retry_count < 10 && ret != 0; retry_count++)
+ int ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH);
+ for (int retry_count = 0; retry_count < 5 && ret != 0; retry_count++) {
+ usleep(50000);
ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH);
+ }
return ret;
}
@@ -135,7 +137,7 @@ static int conf_get_readahead(const char *kind) {
int main(int argc, char **argv)
{
- int ret = 0, retry, opt;
+ int ret = 0, opt;
struct device_info device;
unsigned int readahead = 128, log_level, log_stderr = 0;
@@ -163,11 +165,7 @@ int main(int argc, char **argv)
if ((argc - optind) != 1)
xlog_err("expected the device number of a BDI; is udev ok?");
- for (retry = 0; retry <= 10; retry++ )
- if ((ret = get_device_info(argv[optind], &device)) == 0)
- break;
-
- if (ret != 0 || device.fstype == NULL) {
+ if ((ret = get_device_info(argv[optind], &device)) != 0 || device.fstype == NULL) {
xlog(D_GENERAL, "unable to find device %s\n", argv[optind]);
goto out;
}

View File

@ -0,0 +1,88 @@
From df920ac19d2c54be63b34b39fe8f1ae141c60cc0 Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Wed, 10 Sep 2025 08:00:38 -0500
Subject: [PATCH] rpc-statd.service: define dependency on both rpcbind.service
and rpcbind.socket
In 91da135f ("systemd unit files: fix up dependencies on rpcbind"),
Neil laid out the rationale for how the nfs services should define their
dependencies on rpcbind. In a nutshell:
1. Dependencies should only be defined using rpcbind.socket
2. Ordering for dependencies should only be defined usint "After="
3. nfs-server.service should use "Wants=rpcbind.socket", to allow
rpcbind.socket to be masked in NFSv4-only setups.
4. rpc-statd.service should use "Requires=rpcbind.socket", as rpc.statd
is useless if it can't register with rpcbind.
Then in https://bugzilla.redhat.com/show_bug.cgi?id=2100395, Ben noted
that due to the way the dependencies are ordered, when 'systemctl stop
rpcbind.socket' is run, systemd first sends SIGTERM to rpcbind, then
SIGTERM to rpc.statd. On SIGTERM, rpcbind tears down /var/run/rpcbind.sock.
However, rpc-statd on SIGTERM attempts to unregister from rpcbind. This
results in a long delay:
[root@rawhide ~]# time systemctl restart rpcbind.socket
real 1m0.147s
user 0m0.004s
sys 0m0.003s
8a835ceb ("rpc-statd.service: Stop rpcbind and rpc.stat in an exit race")
fixed this by changing the dependency in rpc-statd.service to use
"After=rpcbind.service", bending rule #1 from above.
Yongcheng recently noted that when runnnig the following test:
[root@rawhide ~]# for i in `seq 10`; do systemctl reset-failed; \
systemctl stop rpcbind rpcbind.socket ; systemctl restart nfs-server ; \
systemctl status rpc-statd; done
rpc-statd.service would often fail to start:
rpc.statd[29938]: Version 2.8.2 starting
rpc.statd[29938]: Flags: TI-RPC
rpc.statd[29938]: Failed to register (statd, 1, udp): svc_reg() err: RPC: Remote system error - Connection refused
rpc.statd[29938]: Failed to register (statd, 1, tcp): svc_reg() err: RPC: Success
rpc.statd[29938]: Failed to register (statd, 1, udp6): svc_reg() err: RPC: Success
rpc.statd[29938]: Failed to register (statd, 1, tcp6): svc_reg() err: RPC: Success
rpc.statd[29938]: failed to create RPC listeners, exiting
rpc-statd.service: Control process exited, code=exited, status=1/FAILURE
rpc-statd.service: Failed with result 'exit-code'.
systemd[1]: Failed to start rpc-statd.service - NFS status monitor for NFSv2/3 locking..
Define the dependency on both rpcbind.service and rpcbind.socket. As
Neil explains:
"After" declarations only have effect if the units are in the same
transaction. If the Unit is not being started or stopped, the After
declaration has no effect.
So on startup, this will ensure rpcbind.socket is started before
rpc-statd.service. On shutdown in a transaction that stops both
rpc-statd.service and rpcbind.service, rpcbind.service won't be
stopped until after rpc-statd.service is stopped.
Fixes: https://issues.redhat.com/browse/RHEL-96937
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
systemd/rpc-statd.service | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/systemd/rpc-statd.service b/systemd/rpc-statd.service
index 660ed861..96fd500d 100644
--- a/systemd/rpc-statd.service
+++ b/systemd/rpc-statd.service
@@ -6,7 +6,7 @@ Conflicts=umount.target
Requires=nss-lookup.target rpcbind.socket
Wants=network-online.target
Wants=rpc-statd-notify.service
-After=network-online.target nss-lookup.target rpcbind.service
+After=network-online.target nss-lookup.target rpcbind.service rpcbind.socket
PartOf=nfs-utils.service
IgnoreOnIsolate=yes
--
2.51.0

View File

@ -0,0 +1,465 @@
From b7ea177d6e556fa8e3cf3e0fe27122ec2b80e8ad Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Tue, 6 Jan 2026 13:36:37 -0500
Subject: [PATCH 5/6] support: Add a mini-library to extract and apply RPC
credentials
JIRA: https://issues.redhat.com/browse/RHEL-127105
Conflicts: support/nfs/Makefile.am - context diff because RHEL9 does not
have d214e3aa ("Implement reexport= export option")
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.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
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 4379b14d..2a3d3b5c 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 67e3a8e1..d7ba72a7 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
libnfsconf_la_SOURCES = conffile.c xlog.c
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.52.0

View File

@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
Name: nfs-utils
URL: http://linux-nfs.org/
Version: 2.5.4
Release: 38%{?dist}
Release: 42%{?dist}
Epoch: 1
# group all 32bit related archs
@ -83,6 +83,19 @@ Patch035: nfs-utils-2.5.4-nfsiostat-fixes.patch
Patch036: nfs-utils-2.5.4-gssd-man-gssproxy.patch
Patch037: nfs-utils-2.5.4-gssd-dup-cachecreds.patch
#
# RHEL9.8
#
Patch038: nfs-utils-2.5.4-rpc-statd-service-dependency.patch
Patch039: nfs-utils-2.5.4-nfsrahead-Modify-get_device_info-logic.patch
Patch040: nfs-utils-2.5.4-gssd-protect-kerberos-ticket-cache-access.patch
Patch041: nfs-utils-2.5.4-Replace-statfs64-with-statfs.patch
Patch042: nfs-utils-2.5.4-NFS-export-symlink-vulnerability-fix.patch
Patch043: nfs-utils-2.5.4-mountd-Minor-refactor-of-get_rootfh.patch
Patch044: nfs-utils-2.5.4-mountd-Separate-lookup-of-the-exported-directory-and.patch
Patch045: nfs-utils-2.5.4-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch
Patch046: nfs-utils-2.5.4-Fix-access-checks-when-mounting-subdirectories-in-NF.patch
Patch100: nfs-utils-1.2.1-statdpath-man.patch
Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
Patch102: nfs-utils-1.2.5-idmap-errmsg.patch
@ -129,6 +142,7 @@ Requires: gssproxy => 0.7.0-3
Requires: rpcbind, sed, gawk, grep
Requires: kmod, keyutils, quota, python3-pyyaml
%{?systemd_requires}
Requires: (selinux-policy >= 38.1.75-1.el9 if selinux-policy)
%package -n nfs-utils-coreos
Summary: Minimal NFS utilities for supporting clients
@ -528,6 +542,25 @@ fi
%{_mandir}/*/nfsiostat.8.gz
%changelog
* Fri Mar 6 2026 Scott Mayhew <smayhew@redhat.com> 2.5.4-42
- Replace statfs64 with statfs (RHEL-127105)
- NFS export symlink vulnerability fix (RHEL-127105)
- mountd: Minor refactor of get_rootfh() (RHEL-127105)
- mountd: Separate lookup of the exported directory and the mount path (RHEL-127105)
- support: Add a mini-library to extract and apply RPC credentials (RHEL-127105)
- Fix access checks when mounting subdirectories in NFSv3 (RHEL-127105)
- Add requires for selinux-policy (RHEL-127105)
Resolves: CVE-2025-12801
* Fri Jan 9 2026 Scott Mayhew <smayhew@redhat.com> 2.5.4-41
- gssd: protect kerberos ticket cache access (RHEL-138462)
* Mon Dec 8 2025 Steve Dickson <steved@redhat.com> 2.5.4-40
- nfsrahead: modify get_device_info logic (RHEL-134397)
* Thu Sep 18 2025 Scott Mayhew <smayhew@redhat.com> 2.5.4-39
- Fix dependency definition in rpc-statd.service (RHEL-116079)
* Thu Jun 12 2025 Scott Mayhew <smayhew@redhat.com> 2.5.4-38
- ensure services are stopped when nfs-utils is uninstalled (RHEL-88422)