import Oracle_OSS nfs-utils-2.5.4-38.0.1.el9_7.3

This commit is contained in:
AlmaLinux RelEng Bot 2026-03-10 09:08:44 -04:00
parent e21839e8ed
commit 524d2ae2c1
7 changed files with 1643 additions and 1 deletions

View File

@ -0,0 +1,251 @@
From 067c3c85ab2fd96a4851a9a0b1fa0ddd1d970e6d 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-127104
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 d32f067..4251815 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 3e5a2f5..06c0f2f 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 dfe88e4..6466666 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 311e284..b989a8b 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 c85c5d6..24e750a 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 5fd572a840b79681e84ddbf6d0dc908c614e01cf 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-127104
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 9354f71..e3efe4a 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 aa1e1dd..f600fb5 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 c3dea4f..caec33c 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 d36f766..178534a 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 ed3a033f0c35a1ed7f34dba91da3ad0feebe002d 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-127104
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 396b3b7..9354f71 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 3b73aad..aa1e1dd 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 65e53c1..c3dea4f 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 a87a780..215037a 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,79 @@
From 36af075e9dad40f6050bba78e771a55be88e66ac 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-127104
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 bcf749f..c0017b0 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 55d3c55188e8378cc6e471eaeb63d917d4005ba0 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-127104
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 f600fb5..3e5a2f5 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 caec33c..dfe88e4 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 c0017b0..311e284 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,465 @@
From 36e04aded6ade43f2b262d7c893362c3028148df 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-127104
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 4379b14..2a3d3b5 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 1373891..631a84f 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 0000000..d58b61e
--- /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 f9993e3..7ea2d79 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 0000000..92d9791
--- /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 67e3a8e..d7ba72a 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 0000000..6ea8efd
--- /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: 38.0.1%{?dist}.3
Epoch: 1
# group all 32bit related archs
@ -83,6 +83,16 @@ 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.7.z
#
Patch038: nfs-utils-2.5.4-Replace-statfs64-with-statfs.patch
Patch039: nfs-utils-2.5.4-NFS-export-symlink-vulnerability-fix.patch
Patch040: nfs-utils-2.5.4-mountd-Minor-refactor-of-get_rootfh.patch
Patch041: nfs-utils-2.5.4-mountd-Separate-lookup-of-the-exported-directory-and.patch
Patch042: nfs-utils-2.5.4-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch
Patch043: 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
@ -128,6 +138,7 @@ Requires: libtirpc >= 0.2.3-1 libblkid libcap libmount
Requires: gssproxy => 0.7.0-3
Requires: rpcbind, sed, gawk, grep
Requires: kmod, keyutils, quota, python3-pyyaml
Requires: (selinux-policy >= 38.1.65-1.el9_7.1 if selinux-policy)
%{?systemd_requires}
%package -n nfs-utils-coreos
@ -367,6 +378,7 @@ if [ $1 -eq 0 ] ; then
fi
%triggerin -- nfs-utils > 1:2.1.1-3
/bin/systemctl --system daemon-reload >/dev/null 2>&1 || :
/bin/systemctl try-restart gssproxy || :
%triggerun -- nfs-utils < 1:2.5.4-3
@ -528,6 +540,21 @@ fi
%{_mandir}/*/nfsiostat.8.gz
%changelog
* Thu Mar 05 2026 EL Errata <el-errata_ww@oracle.com> - 2.5.4-38.0.1.el9_7.3
- spec: remove multiple warnings when upgrading nfs-utils with gssproxy [Orabug: 36044562]
* Wed Feb 18 2026 Scott Mayhew <smayhew@redhat.com> 2.5.4-38.3
- Add requires for selinux-policy (RHEL-127104)
* Thu Jan 15 2026 Scott Mayhew <smayhew@redhat.com> 2.5.4-38.2
- Replace statfs64 with statfs (RHEL-127104)
- NFS export symlink vulnerability fix (RHEL-127104)
- mountd: Minor refactor of get_rootfh() (RHEL-127104)
- mountd: Separate lookup of the exported directory and the mount path (RHEL-127104)
- support: Add a mini-library to extract and apply RPC credentials (RHEL-127104)
- Fix access checks when mounting subdirectories in NFSv3 (RHEL-127104)
Resolves: CVE-2025-12801
* Thu Jun 12 2025 Scott Mayhew <smayhew@redhat.com> 2.5.4-38
- ensure services are stopped when nfs-utils is uninstalled (RHEL-88422)