From 2711ce8942b545e4ea067775e3091688ca63ed0e Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Mon, 30 Mar 2026 10:01:59 -0400 Subject: [PATCH] import CS nfs-utils-2.5.4-42.el9 --- ...s-when-mounting-subdirectories-in-NF.patch | 251 ++++++++++ ...NFS-export-symlink-vulnerability-fix.patch | 457 +++++++++++++++++ ...s-2.5.4-Replace-statfs64-with-statfs.patch | 184 +++++++ ...protect-kerberos-ticket-cache-access.patch | 52 ++ ...-mountd-Minor-refactor-of-get_rootfh.patch | 79 +++ ...lookup-of-the-exported-directory-and.patch | 179 +++++++ ...srahead-Modify-get_device_info-logic.patch | 60 +++ ...s-2.5.4-rpc-statd-service-dependency.patch | 88 ++++ ...ni-library-to-extract-and-apply-RPC-.patch | 465 ++++++++++++++++++ SPECS/nfs-utils.spec | 35 +- 10 files changed, 1849 insertions(+), 1 deletion(-) create mode 100644 SOURCES/nfs-utils-2.5.4-Fix-access-checks-when-mounting-subdirectories-in-NF.patch create mode 100644 SOURCES/nfs-utils-2.5.4-NFS-export-symlink-vulnerability-fix.patch create mode 100644 SOURCES/nfs-utils-2.5.4-Replace-statfs64-with-statfs.patch create mode 100644 SOURCES/nfs-utils-2.5.4-gssd-protect-kerberos-ticket-cache-access.patch create mode 100644 SOURCES/nfs-utils-2.5.4-mountd-Minor-refactor-of-get_rootfh.patch create mode 100644 SOURCES/nfs-utils-2.5.4-mountd-Separate-lookup-of-the-exported-directory-and.patch create mode 100644 SOURCES/nfs-utils-2.5.4-nfsrahead-Modify-get_device_info-logic.patch create mode 100644 SOURCES/nfs-utils-2.5.4-rpc-statd-service-dependency.patch create mode 100644 SOURCES/nfs-utils-2.5.4-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch diff --git a/SOURCES/nfs-utils-2.5.4-Fix-access-checks-when-mounting-subdirectories-in-NF.patch b/SOURCES/nfs-utils-2.5.4-Fix-access-checks-when-mounting-subdirectories-in-NF.patch new file mode 100644 index 0000000..f5e1054 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-Fix-access-checks-when-mounting-subdirectories-in-NF.patch @@ -0,0 +1,251 @@ +From ff89a25378179d2a4dc25f6fcfcc1041b29c5407 Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +Signed-off-by: Trond Myklebust +Signed-off-by: Steve Dickson +Signed-off-by: Scott Mayhew +--- + 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 + diff --git a/SOURCES/nfs-utils-2.5.4-NFS-export-symlink-vulnerability-fix.patch b/SOURCES/nfs-utils-2.5.4-NFS-export-symlink-vulnerability-fix.patch new file mode 100644 index 0000000..f02b337 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-NFS-export-symlink-vulnerability-fix.patch @@ -0,0 +1,457 @@ +From 14a946ce3dbc7dba964ec3d1e24b21899900325e Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +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 + Signed-off-by: Steve Dickson + +Signed-off-by: Scott Mayhew +--- + 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 + diff --git a/SOURCES/nfs-utils-2.5.4-Replace-statfs64-with-statfs.patch b/SOURCES/nfs-utils-2.5.4-Replace-statfs64-with-statfs.patch new file mode 100644 index 0000000..3a4f228 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-Replace-statfs64-with-statfs.patch @@ -0,0 +1,184 @@ +From f0f6a29eab7598983e5a2f1c576750bdab9088c4 Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +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 + Signed-off-by: Steve Dickson + +Signed-off-by: Scott Mayhew +--- + 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 + + 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 + diff --git a/SOURCES/nfs-utils-2.5.4-gssd-protect-kerberos-ticket-cache-access.patch b/SOURCES/nfs-utils-2.5.4-gssd-protect-kerberos-ticket-cache-access.patch new file mode 100644 index 0000000..af805fe --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-gssd-protect-kerberos-ticket-cache-access.patch @@ -0,0 +1,52 @@ +From 8600bbb7727df779ba1104c9f0c574b06be116a3 Mon Sep 17 00:00:00 2001 +From: Olga Kornievskaia +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 +Signed-off-by: Steve Dickson +--- + 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 : "", 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 : "", 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 + diff --git a/SOURCES/nfs-utils-2.5.4-mountd-Minor-refactor-of-get_rootfh.patch b/SOURCES/nfs-utils-2.5.4-mountd-Minor-refactor-of-get_rootfh.patch new file mode 100644 index 0000000..d51c21c --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-mountd-Minor-refactor-of-get_rootfh.patch @@ -0,0 +1,79 @@ +From ff21df893d23a0d18bbf4d5b397f09a1bc434888 Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +Signed-off-by: Trond Myklebust +Signed-off-by: Steve Dickson +Signed-off-by: Scott Mayhew +--- + 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 + diff --git a/SOURCES/nfs-utils-2.5.4-mountd-Separate-lookup-of-the-exported-directory-and.patch b/SOURCES/nfs-utils-2.5.4-mountd-Separate-lookup-of-the-exported-directory-and.patch new file mode 100644 index 0000000..a9aa394 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-mountd-Separate-lookup-of-the-exported-directory-and.patch @@ -0,0 +1,179 @@ +From 320149c8f8bf0b574a09de29eb7d750318b2684b Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +Signed-off-by: Trond Myklebust +Signed-off-by: Steve Dickson +Signed-off-by: Scott Mayhew +--- + 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 + diff --git a/SOURCES/nfs-utils-2.5.4-nfsrahead-Modify-get_device_info-logic.patch b/SOURCES/nfs-utils-2.5.4-nfsrahead-Modify-get_device_info-logic.patch new file mode 100644 index 0000000..4b0e5a2 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-nfsrahead-Modify-get_device_info-logic.patch @@ -0,0 +1,60 @@ +commit 162a0093a86411c4ce46d44660c57b98e4879701 +Author: Thiago Becker +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 + Signed-off-by: Steve Dickson + +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; + } diff --git a/SOURCES/nfs-utils-2.5.4-rpc-statd-service-dependency.patch b/SOURCES/nfs-utils-2.5.4-rpc-statd-service-dependency.patch new file mode 100644 index 0000000..75345c6 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-rpc-statd-service-dependency.patch @@ -0,0 +1,88 @@ +From df920ac19d2c54be63b34b39fe8f1ae141c60cc0 Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +Signed-off-by: Scott Mayhew +Signed-off-by: Steve Dickson +--- + 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 + diff --git a/SOURCES/nfs-utils-2.5.4-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch b/SOURCES/nfs-utils-2.5.4-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch new file mode 100644 index 0000000..0008b2a --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch @@ -0,0 +1,465 @@ +From b7ea177d6e556fa8e3cf3e0fe27122ec2b80e8ad Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +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 +Signed-off-by: Trond Myklebust +Signed-off-by: Steve Dickson +Signed-off-by: Scott Mayhew +--- + 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 ++ ++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 ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include "exportfs.h" ++#include "nfs_ucred.h" ++ ++#ifdef HAVE_TIRPC_GSS_GETCRED ++#include ++#endif /* HAVE_TIRPC_GSS_GETCRED */ ++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED ++#include ++#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 + diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index 8ca02ea..d5225fa 100644 --- a/SPECS/nfs-utils.spec +++ b/SPECS/nfs-utils.spec @@ -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 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 2.5.4-41 +- gssd: protect kerberos ticket cache access (RHEL-138462) + +* Mon Dec 8 2025 Steve Dickson 2.5.4-40 +- nfsrahead: modify get_device_info logic (RHEL-134397) + +* Thu Sep 18 2025 Scott Mayhew 2.5.4-39 +- Fix dependency definition in rpc-statd.service (RHEL-116079) + * Thu Jun 12 2025 Scott Mayhew 2.5.4-38 - ensure services are stopped when nfs-utils is uninstalled (RHEL-88422)