Compare commits
No commits in common. "c8" and "c8-beta" have entirely different histories.
@ -1,218 +0,0 @@
|
||||
From a0dc9ad489b016ba2d84d21d4f1a1b40df5b548e Mon Sep 17 00:00:00 2001
|
||||
From: Scott Mayhew <smayhew@redhat.com>
|
||||
Date: Tue, 13 Jan 2026 14:30:22 -0500
|
||||
Subject: [PATCH 4/4] Fix access checks when mounting subdirectories in NFSv3
|
||||
|
||||
If a NFSv3 client asks to mount a subdirectory of one of the exported
|
||||
directories, then apply the RPC credential together with any root
|
||||
or all squash rules that would apply to the client in question.
|
||||
---
|
||||
nfs.conf | 1 +
|
||||
utils/mountd/mountd.c | 93 ++++++++++++++++++++++++++++++++++++++++-
|
||||
utils/mountd/mountd.man | 26 ++++++++++++
|
||||
3 files changed, 118 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/nfs.conf b/nfs.conf
|
||||
index 30f9e109..f5a3bb7c 100644
|
||||
--- a/nfs.conf
|
||||
+++ b/nfs.conf
|
||||
@@ -35,6 +35,7 @@ use-gss-proxy=1
|
||||
#
|
||||
[mountd]
|
||||
# debug=0
|
||||
+# apply-root-cred=n
|
||||
# manage-gids=n
|
||||
# descriptors=0
|
||||
# port=0
|
||||
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
|
||||
index 779f51b4..8e26bcb7 100644
|
||||
--- a/utils/mountd/mountd.c
|
||||
+++ b/utils/mountd/mountd.c
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "pseudoflavors.h"
|
||||
#include "nfslib.h"
|
||||
#include "export.h"
|
||||
+#include "nfs_ucred.h"
|
||||
|
||||
extern void my_svc_run(void);
|
||||
|
||||
@@ -40,6 +41,7 @@ static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **,
|
||||
|
||||
int reverse_resolve = 0;
|
||||
int manage_gids;
|
||||
+int apply_root_cred;
|
||||
int use_ipaddr = -1;
|
||||
|
||||
struct state_paths etab;
|
||||
@@ -77,9 +79,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))
|
||||
@@ -458,6 +461,71 @@ mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * NB: In the upstream code, all the nfsd_cred_openat() stuff is located
|
||||
+ * nfsd_path.c, which doesn't existing in RHEL8, so I'm including it here.
|
||||
+ * Also, these functions differ slightly from the upstream versions because
|
||||
+ * RHEL8 lacks the workqueue mechanism, which was added to support chrooted
|
||||
+ * threads (i.e. the rootdir option).
|
||||
+ */
|
||||
+struct nfsd_openat_t {
|
||||
+ const struct nfs_ucred *cred;
|
||||
+ 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;
|
||||
+}
|
||||
+
|
||||
+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);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static 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,
|
||||
+ };
|
||||
+
|
||||
+ if (cred)
|
||||
+ nfsd_cred_openatfunc(&open_buf);
|
||||
+ else
|
||||
+ nfsd_openatfunc(&open_buf);
|
||||
+ if (open_buf.res_fd == -1)
|
||||
+ errno = open_buf.res_error;
|
||||
+ return open_buf.res_fd;
|
||||
+}
|
||||
+
|
||||
static struct nfs_fh_len *
|
||||
get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
|
||||
mountstat3 *error, int v3)
|
||||
@@ -527,11 +595,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 = 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));
|
||||
@@ -764,6 +848,8 @@ main(int argc, 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);
|
||||
|
||||
/* Parse the command line options and arguments. */
|
||||
opterr = 0;
|
||||
@@ -772,6 +858,9 @@ main(int argc, char **argv)
|
||||
case 'g':
|
||||
manage_gids = 1;
|
||||
break;
|
||||
+ case 'c':
|
||||
+ apply_root_cred = 1;
|
||||
+ break;
|
||||
case 'o':
|
||||
descriptors = atoi(optarg);
|
||||
if (descriptors <= 0) {
|
||||
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
|
||||
index 2a91e193..ac6d6d30 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
|
||||
|
||||
@ -1,208 +0,0 @@
|
||||
From ad4eafccd244e87af315c432d076b4f988dde52a Mon Sep 17 00:00:00 2001
|
||||
From: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Date: Mon, 24 Mar 2025 08:43:43 -0400
|
||||
Subject: [PATCH 2/2] gssd: do not use krb5_cc_initialize
|
||||
|
||||
Note: This patch differs from the upstream version in several places
|
||||
because RHEL 8 does not have c8659457 ("gssd: We never use the nocache
|
||||
param of gssd_check_if_cc_exists()") or f066f87b ("gssd: enable forcing
|
||||
cred renewal using the keytab").
|
||||
|
||||
Original commit message:
|
||||
|
||||
When gssd refreshes machine credentials, it uses the
|
||||
krb5_get_init_creds_keytab() and then to save the received credentials
|
||||
in a ticket cache, it proceeds to initialize the credential cache via
|
||||
a krb5_cc_initialize() before storing the received credentials into it.
|
||||
|
||||
krb5_cc_initialize() is not concurrency safe. two gssd upcalls by
|
||||
uid=0, one for krb5i auth flavor and another for krb5p, would enter
|
||||
into krb5_cc_initialize() and one of them would fail, leading to
|
||||
an upcall failure and NFS operation error.
|
||||
|
||||
Instead it was proposed that gssd changes its design to do what
|
||||
kinit does and forgo the use of krb5_cc_initialize and instead setup
|
||||
the output cache via krb5_get_init_creds_opt_set_out_cache() prior
|
||||
to calling krb5_get_init_creds_keytab() which would then store
|
||||
credentials automatically.
|
||||
|
||||
https://mailman.mit.edu/pipermail/krbdev/2025-February/013708.html
|
||||
|
||||
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Signed-off-by: Steve Dickson <steved@redhat.com>
|
||||
(cherry picked from commit 1cd9e3c0d290646e80750249914396566dd6b800)
|
||||
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
|
||||
---
|
||||
utils/gssd/krb5_util.c | 103 ++++++++++++++++++++---------------------
|
||||
1 file changed, 50 insertions(+), 53 deletions(-)
|
||||
|
||||
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
|
||||
index 871add74..43bc8744 100644
|
||||
--- a/utils/gssd/krb5_util.c
|
||||
+++ b/utils/gssd/krb5_util.c
|
||||
@@ -161,7 +161,8 @@ static int select_krb5_ccache(const struct dirent *d);
|
||||
static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
|
||||
const char **cctype, struct dirent **d);
|
||||
static int gssd_get_single_krb5_cred(krb5_context context,
|
||||
- krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache);
|
||||
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache,
|
||||
+ krb5_ccache ccache);
|
||||
static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
|
||||
char **ret_realm);
|
||||
|
||||
@@ -368,16 +369,14 @@ static int
|
||||
gssd_get_single_krb5_cred(krb5_context context,
|
||||
krb5_keytab kt,
|
||||
struct gssd_k5_kt_princ *ple,
|
||||
- int nocache)
|
||||
+ int nocache,
|
||||
+ krb5_ccache ccache)
|
||||
{
|
||||
krb5_get_init_creds_opt *opts = NULL;
|
||||
krb5_creds my_creds;
|
||||
- krb5_ccache ccache = NULL;
|
||||
char kt_name[BUFSIZ];
|
||||
- char cc_name[BUFSIZ];
|
||||
int code;
|
||||
time_t now = time(0);
|
||||
- char *cache_type;
|
||||
char *pname = NULL;
|
||||
char *k5err = NULL;
|
||||
pthread_t tid = pthread_self();
|
||||
@@ -427,6 +426,14 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
|
||||
#endif
|
||||
|
||||
+ if ((code = krb5_get_init_creds_opt_set_out_ccache(context, opts,
|
||||
+ ccache))) {
|
||||
+ k5err = gssd_k5_err_msg(context, code);
|
||||
+ printerr(1, "WARNING: %s while initializing ccache for "
|
||||
+ "principal '%s' using keytab '%s'\n", k5err,
|
||||
+ pname ? pname : "<unparsable>", kt_name);
|
||||
+ goto out;
|
||||
+ }
|
||||
if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
|
||||
kt, 0, NULL, opts))) {
|
||||
k5err = gssd_k5_err_msg(context, code);
|
||||
@@ -436,61 +443,18 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- /*
|
||||
- * Initialize cache file which we're going to be using
|
||||
- */
|
||||
-
|
||||
pthread_mutex_lock(&ple_lock);
|
||||
- if (use_memcache)
|
||||
- cache_type = "MEMORY";
|
||||
- else
|
||||
- cache_type = "FILE";
|
||||
- snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
|
||||
- cache_type,
|
||||
- ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
|
||||
- GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
|
||||
ple->endtime = my_creds.times.endtime;
|
||||
- if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) {
|
||||
- free(ple->ccname);
|
||||
- ple->ccname = strdup(cc_name);
|
||||
- if (ple->ccname == NULL) {
|
||||
- printerr(0, "ERROR: no storage to duplicate credentials "
|
||||
- "cache name '%s'\n", cc_name);
|
||||
- code = ENOMEM;
|
||||
- pthread_mutex_unlock(&ple_lock);
|
||||
- goto out;
|
||||
- }
|
||||
- }
|
||||
pthread_mutex_unlock(&ple_lock);
|
||||
- if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
|
||||
- k5err = gssd_k5_err_msg(context, code);
|
||||
- printerr(0, "ERROR: %s while opening credential cache '%s'\n",
|
||||
- k5err, cc_name);
|
||||
- goto out;
|
||||
- }
|
||||
- if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
|
||||
- k5err = gssd_k5_err_msg(context, code);
|
||||
- printerr(0, "ERROR: %s while initializing credential "
|
||||
- "cache '%s'\n", k5err, cc_name);
|
||||
- goto out;
|
||||
- }
|
||||
- if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
|
||||
- k5err = gssd_k5_err_msg(context, code);
|
||||
- printerr(0, "ERROR: %s while storing credentials in '%s'\n",
|
||||
- k5err, cc_name);
|
||||
- goto out;
|
||||
- }
|
||||
|
||||
code = 0;
|
||||
- printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n",
|
||||
- __func__, tid, pname, cc_name);
|
||||
+ printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n",
|
||||
+ __func__, tid, pname, ple->ccname);
|
||||
out:
|
||||
if (opts)
|
||||
krb5_get_init_creds_opt_free(context, opts);
|
||||
if (pname)
|
||||
k5_free_unparsed_name(context, pname);
|
||||
- if (ccache)
|
||||
- krb5_cc_close(context, ccache);
|
||||
krb5_free_cred_contents(context, &my_creds);
|
||||
krb5_free_string(context, k5err);
|
||||
return (code);
|
||||
@@ -1108,10 +1072,12 @@ gssd_refresh_krb5_machine_credential_internal(char *hostname,
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_context context;
|
||||
- krb5_keytab kt = NULL;;
|
||||
+ krb5_keytab kt = NULL;
|
||||
+ krb5_ccache ccache = NULL;
|
||||
int retval = 0;
|
||||
- char *k5err = NULL;
|
||||
+ char *k5err = NULL, *cache_type;
|
||||
const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
|
||||
+ char cc_name[BUFSIZ];
|
||||
|
||||
/*
|
||||
* If a specific service name was specified, use it.
|
||||
@@ -1170,7 +1136,38 @@ gssd_refresh_krb5_machine_credential_internal(char *hostname,
|
||||
goto out_free_kt;
|
||||
}
|
||||
}
|
||||
- retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
|
||||
+
|
||||
+ if (use_memcache)
|
||||
+ cache_type = "MEMORY";
|
||||
+ else
|
||||
+ cache_type = "FILE";
|
||||
+ snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
|
||||
+ cache_type,
|
||||
+ ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
|
||||
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
|
||||
+
|
||||
+ pthread_mutex_lock(&ple_lock);
|
||||
+ if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) {
|
||||
+ free(ple->ccname);
|
||||
+ ple->ccname = strdup(cc_name);
|
||||
+ if (ple->ccname == NULL) {
|
||||
+ printerr(0, "ERROR: no storage to duplicate credentials "
|
||||
+ "cache name '%s'\n", cc_name);
|
||||
+ code = ENOMEM;
|
||||
+ pthread_mutex_unlock(&ple_lock);
|
||||
+ goto out_free_kt;
|
||||
+ }
|
||||
+ }
|
||||
+ pthread_mutex_unlock(&ple_lock);
|
||||
+ if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
|
||||
+ k5err = gssd_k5_err_msg(context, code);
|
||||
+ printerr(0, "ERROR: %s while opening credential cache '%s'\n",
|
||||
+ k5err, cc_name);
|
||||
+ goto out_free_kt;
|
||||
+ }
|
||||
+
|
||||
+ retval = gssd_get_single_krb5_cred(context, kt, ple, 0, ccache);
|
||||
+ krb5_cc_close(context, ccache);
|
||||
out_free_kt:
|
||||
krb5_kt_close(context, kt);
|
||||
out_free_context:
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
From 7511a77fc7eb7bd3ae38fcf54d49a47c25c3ed50 Mon Sep 17 00:00:00 2001
|
||||
From: Scott Mayhew <smayhew@redhat.com>
|
||||
Date: Mon, 24 Mar 2025 08:59:24 -0400
|
||||
Subject: [nfs-utils PATCH] gssd.man: add documentation for use-gss-proxy
|
||||
nfs.conf option
|
||||
|
||||
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
|
||||
Signed-off-by: Steve Dickson <steved@redhat.com>
|
||||
---
|
||||
utils/gssd/gssd.man | 14 +++++++++++++-
|
||||
1 file changed, 13 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
|
||||
index c735eff6..4a75b056 100644
|
||||
--- a/utils/gssd/gssd.man
|
||||
+++ b/utils/gssd/gssd.man
|
||||
@@ -392,6 +392,17 @@ Setting to
|
||||
is equivalent to providing the
|
||||
.B -H
|
||||
flag.
|
||||
+.TP
|
||||
+.B use-gss-proxy
|
||||
+Setting this to 1 allows
|
||||
+.BR gssproxy (8)
|
||||
+to intercept GSSAPI calls and service them on behalf of
|
||||
+.BR rpc.gssd ,
|
||||
+enabling certain features such as keytab-based client initiation.
|
||||
+Note that this is unrelated to the functionality that
|
||||
+.BR gssproxy (8)
|
||||
+provides on behalf of the NFS server. For more information, see
|
||||
+.BR https://github.com/gssapi/gssproxy/blob/main/docs/NFS.md#nfs-client .
|
||||
.P
|
||||
In addtion, the following value is recognized from the
|
||||
.B [general]
|
||||
@@ -405,7 +416,8 @@ Equivalent to
|
||||
.BR rpc.svcgssd (8),
|
||||
.BR kerberos (1),
|
||||
.BR kinit (1),
|
||||
-.BR krb5.conf (5)
|
||||
+.BR krb5.conf (5),
|
||||
+.BR gssproxy (8)
|
||||
.SH AUTHORS
|
||||
.br
|
||||
Dug Song <dugsong@umich.edu>
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
From 8600bbb7727df779ba1104c9f0c574b06be116a3 Mon Sep 17 00:00:00 2001
|
||||
From: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Date: Tue, 18 Nov 2025 10:23:27 -0500
|
||||
Subject: [nfs-utils PATCH] gssd: protect kerberos ticket cache access
|
||||
|
||||
gssd_get_single_krb5_cred() is a function that's will (for when needed)
|
||||
send a TGT request to the KDC and then store it in a credential cache.
|
||||
If multiple threads (eg., parallel mounts) are making an upcall at the
|
||||
same time then getting creds and storing creds need to be serialized due
|
||||
to do kerberos API not being concurrency safe.
|
||||
|
||||
Fixes: https://issues.redhat.com/browse/RHEL-103627
|
||||
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Signed-off-by: Steve Dickson <steved@redhat.com>
|
||||
---
|
||||
utils/gssd/krb5_util.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
|
||||
index 09625fb9..137cffda 100644
|
||||
--- a/utils/gssd/krb5_util.c
|
||||
+++ b/utils/gssd/krb5_util.c
|
||||
@@ -456,12 +456,14 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
|
||||
#endif
|
||||
|
||||
+ pthread_mutex_lock(&ple_lock);
|
||||
if ((code = krb5_get_init_creds_opt_set_out_ccache(context, opts,
|
||||
ccache))) {
|
||||
k5err = gssd_k5_err_msg(context, code);
|
||||
printerr(1, "WARNING: %s while initializing ccache for "
|
||||
"principal '%s' using keytab '%s'\n", k5err,
|
||||
pname ? pname : "<unparsable>", kt_name);
|
||||
+ pthread_mutex_unlock(&ple_lock);
|
||||
goto out;
|
||||
}
|
||||
if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
|
||||
@@ -470,10 +472,10 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
printerr(1, "WARNING: %s while getting initial ticket for "
|
||||
"principal '%s' using keytab '%s'\n", k5err,
|
||||
pname ? pname : "<unparsable>", kt_name);
|
||||
+ pthread_mutex_unlock(&ple_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
- pthread_mutex_lock(&ple_lock);
|
||||
ple->endtime = my_creds.times.endtime;
|
||||
pthread_mutex_unlock(&ple_lock);
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
From 55d9bf151b100db9bf52e8f968e33f3ae1d234f5 Mon Sep 17 00:00:00 2001
|
||||
From: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Date: Mon, 24 Mar 2025 08:40:32 -0400
|
||||
Subject: [PATCH 1/2] gssd: unconditionally use krb5_get_init_creds_opt_alloc
|
||||
|
||||
Note: This patch has a context difference from the upstream version
|
||||
because RHEL 8 does not have c8659457 ("gssd: We never use the nocache
|
||||
param of gssd_check_if_cc_exists()") or f066f87b ("gssd: enable forcing
|
||||
cred renewal using the keytab").
|
||||
|
||||
Original commit message:
|
||||
|
||||
Modern kerberos API uses krb5_get_init_creds_opt_alloc() for managing
|
||||
its options for credential data structure.
|
||||
|
||||
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Signed-off-by: Steve Dickson <steved@redhat.com>
|
||||
(cherry picked from commit 9b3f949331c6541a358fc28bac323533f94d7e0b)
|
||||
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
|
||||
---
|
||||
utils/gssd/krb5_util.c | 37 ++++++++++---------------------------
|
||||
1 file changed, 10 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
|
||||
index a1a77a2f..871add74 100644
|
||||
--- a/utils/gssd/krb5_util.c
|
||||
+++ b/utils/gssd/krb5_util.c
|
||||
@@ -370,12 +370,7 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
struct gssd_k5_kt_princ *ple,
|
||||
int nocache)
|
||||
{
|
||||
-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
|
||||
- krb5_get_init_creds_opt *init_opts = NULL;
|
||||
-#else
|
||||
- krb5_get_init_creds_opt options;
|
||||
-#endif
|
||||
- krb5_get_init_creds_opt *opts;
|
||||
+ krb5_get_init_creds_opt *opts = NULL;
|
||||
krb5_creds my_creds;
|
||||
krb5_ccache ccache = NULL;
|
||||
char kt_name[BUFSIZ];
|
||||
@@ -413,33 +408,23 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
if ((krb5_unparse_name(context, ple->princ, &pname)))
|
||||
pname = NULL;
|
||||
|
||||
-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
|
||||
- code = krb5_get_init_creds_opt_alloc(context, &init_opts);
|
||||
+ code = krb5_get_init_creds_opt_alloc(context, &opts);
|
||||
if (code) {
|
||||
k5err = gssd_k5_err_msg(context, code);
|
||||
printerr(0, "ERROR: %s allocating gic options\n", k5err);
|
||||
goto out;
|
||||
}
|
||||
- if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1))
|
||||
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
|
||||
+ if (krb5_get_init_creds_opt_set_addressless(context, opts, 1))
|
||||
printerr(1, "WARNING: Unable to set option for addressless "
|
||||
"tickets. May have problems behind a NAT.\n");
|
||||
-#ifdef TEST_SHORT_LIFETIME
|
||||
- /* set a short lifetime (for debugging only!) */
|
||||
- printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n");
|
||||
- krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60);
|
||||
+#else
|
||||
+ krb5_get_init_creds_opt_set_address_list(opts, NULL);
|
||||
#endif
|
||||
- opts = init_opts;
|
||||
-
|
||||
-#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */
|
||||
-
|
||||
- krb5_get_init_creds_opt_init(&options);
|
||||
- krb5_get_init_creds_opt_set_address_list(&options, NULL);
|
||||
#ifdef TEST_SHORT_LIFETIME
|
||||
/* set a short lifetime (for debugging only!) */
|
||||
- printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
|
||||
- krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
|
||||
-#endif
|
||||
- opts = &options;
|
||||
+ printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n");
|
||||
+ krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
|
||||
#endif
|
||||
|
||||
if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
|
||||
@@ -500,10 +485,8 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||||
printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n",
|
||||
__func__, tid, pname, cc_name);
|
||||
out:
|
||||
-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
|
||||
- if (init_opts)
|
||||
- krb5_get_init_creds_opt_free(context, init_opts);
|
||||
-#endif
|
||||
+ if (opts)
|
||||
+ krb5_get_init_creds_opt_free(context, opts);
|
||||
if (pname)
|
||||
k5_free_unparsed_name(context, pname);
|
||||
if (ccache)
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
From 71baef7f9d3e00ed8a1ec38c791aea5854c93eaf Mon Sep 17 00:00:00 2001
|
||||
From: Scott Mayhew <smayhew@redhat.com>
|
||||
Date: Tue, 13 Jan 2026 13:22:29 -0500
|
||||
Subject: [PATCH 1/4] mountd: Minor refactor of get_rootfh()
|
||||
|
||||
Perform the mountpoint checks before checking the user path.
|
||||
---
|
||||
utils/mountd/mountd.c | 32 ++++++++++++++++----------------
|
||||
1 file changed, 16 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
|
||||
index 2b342377..60f5e488 100644
|
||||
--- a/utils/mountd/mountd.c
|
||||
+++ b/utils/mountd/mountd.c
|
||||
@@ -488,6 +488,22 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
|
||||
*error = MNT3ERR_ACCES;
|
||||
return NULL;
|
||||
}
|
||||
+ if (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 &&
|
||||
+ !is_mountpoint(exp->m_export.e_mountpoint[0]?
|
||||
+ exp->m_export.e_mountpoint:
|
||||
+ exp->m_export.e_path)) {
|
||||
+ xlog(L_WARNING, "request to export an unmounted filesystem: %s",
|
||||
+ p);
|
||||
+ *error = MNT3ERR_NOENT;
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
if (stat(p, &stb) < 0) {
|
||||
xlog(L_WARNING, "can't stat exported dir %s: %s",
|
||||
p, strerror(errno));
|
||||
@@ -502,12 +518,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
|
||||
*error = MNT3ERR_NOTDIR;
|
||||
return NULL;
|
||||
}
|
||||
- if (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",
|
||||
@@ -515,16 +525,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
|
||||
*error = MNT3ERR_ACCES;
|
||||
return NULL;
|
||||
}
|
||||
- if (exp->m_export.e_mountpoint &&
|
||||
- !is_mountpoint(exp->m_export.e_mountpoint[0]?
|
||||
- exp->m_export.e_mountpoint:
|
||||
- exp->m_export.e_path)) {
|
||||
- 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
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
From c7d8779b71314afa5e6c529aae85da12ae250f70 Mon Sep 17 00:00:00 2001
|
||||
From: Scott Mayhew <smayhew@redhat.com>
|
||||
Date: Tue, 13 Jan 2026 14:00:19 -0500
|
||||
Subject: [PATCH 2/4] mountd: Separate lookup of the exported directory and the
|
||||
mount path
|
||||
|
||||
When the caller asks to mount a path that does not terminate with an
|
||||
exported directory, we want to split up the lookups so that we can
|
||||
look up the exported directory using the mountd privileged credential,
|
||||
and the remaining subdirectory lookups using the RPC caller's
|
||||
credential.
|
||||
---
|
||||
utils/mountd/mountd.c | 61 ++++++++++++++++++++++++++++++++++++-------
|
||||
1 file changed, 52 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
|
||||
index 60f5e488..779f51b4 100644
|
||||
--- a/utils/mountd/mountd.c
|
||||
+++ b/utils/mountd/mountd.c
|
||||
@@ -468,7 +468,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 = "/";
|
||||
@@ -488,12 +491,20 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
|
||||
*error = MNT3ERR_ACCES;
|
||||
return NULL;
|
||||
}
|
||||
- if (stat(exp->m_export.e_path, &estb) < 0) {
|
||||
- xlog(L_WARNING, "can't stat export point %s: %s",
|
||||
+ dirfd = 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 &&
|
||||
!is_mountpoint(exp->m_export.e_mountpoint[0]?
|
||||
exp->m_export.e_mountpoint:
|
||||
@@ -501,18 +512,50 @@ 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 (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 = 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
|
||||
|
||||
@ -1,141 +0,0 @@
|
||||
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
|
||||
index 8e129c83..d488f9e1 100755
|
||||
--- a/tools/mountstats/mountstats.py
|
||||
+++ b/tools/mountstats/mountstats.py
|
||||
@@ -333,6 +333,11 @@ class DeviceData:
|
||||
found = True
|
||||
self.__parse_rpc_line(words)
|
||||
|
||||
+ def fstype(self):
|
||||
+ """Return the fstype for the mountpoint
|
||||
+ """
|
||||
+ return self.__nfs_data['fstype']
|
||||
+
|
||||
def is_nfs_mountpoint(self):
|
||||
"""Return True if this is an NFS or NFSv4 mountpoint,
|
||||
otherwise return False
|
||||
@@ -953,73 +958,78 @@ def nfsstat_command(args):
|
||||
return 0
|
||||
|
||||
def print_iostat_summary(old, new, devices, time):
|
||||
+ if len(devices) == 0:
|
||||
+ print('No NFS mount points were found')
|
||||
+ return
|
||||
+
|
||||
for device in devices:
|
||||
stats = DeviceData()
|
||||
stats.parse_stats(new[device])
|
||||
- if not old or device not in old:
|
||||
+ if old and device in old:
|
||||
+ old_stats = DeviceData()
|
||||
+ old_stats.parse_stats(old[device])
|
||||
+ if stats.fstype() == old_stats.fstype():
|
||||
+ stats.compare_iostats(old_stats).display_iostats(time)
|
||||
+ else: # device is in old, but fstypes are different
|
||||
+ stats.display_iostats(time)
|
||||
+ else: # device is only in new
|
||||
stats.display_iostats(time)
|
||||
- else:
|
||||
- if ("fstype autofs" not in str(old[device])) and ("fstype autofs" not in str(new[device])):
|
||||
- old_stats = DeviceData()
|
||||
- old_stats.parse_stats(old[device])
|
||||
- diff_stats = stats.compare_iostats(old_stats)
|
||||
- diff_stats.display_iostats(time)
|
||||
+
|
||||
+def list_nfs_mounts(givenlist, mountstats):
|
||||
+ """return a list of NFS mounts given a list to validate or
|
||||
+ return a full list if the given list is empty -
|
||||
+ may return an empty list if none found
|
||||
+ """
|
||||
+ devicelist = []
|
||||
+ if len(givenlist) > 0:
|
||||
+ for device in givenlist:
|
||||
+ if device in mountstats:
|
||||
+ stats = DeviceData()
|
||||
+ stats.parse_stats(mountstats[device])
|
||||
+ if stats.is_nfs_mountpoint():
|
||||
+ devicelist += [device]
|
||||
+ else:
|
||||
+ for device, descr in mountstats.items():
|
||||
+ stats = DeviceData()
|
||||
+ stats.parse_stats(descr)
|
||||
+ if stats.is_nfs_mountpoint():
|
||||
+ devicelist += [device]
|
||||
+ return devicelist
|
||||
|
||||
def iostat_command(args):
|
||||
"""iostat-like command for NFS mount points
|
||||
"""
|
||||
mountstats = parse_stats_file(args.infile)
|
||||
- devices = [os.path.normpath(mp) for mp in args.mountpoints]
|
||||
+ origdevices = [os.path.normpath(mp) for mp in args.mountpoints]
|
||||
|
||||
if args.since:
|
||||
old_mountstats = parse_stats_file(args.since)
|
||||
else:
|
||||
old_mountstats = None
|
||||
|
||||
- # make certain devices contains only NFS mount points
|
||||
- if len(devices) > 0:
|
||||
- check = []
|
||||
- for device in devices:
|
||||
- stats = DeviceData()
|
||||
- try:
|
||||
- stats.parse_stats(mountstats[device])
|
||||
- if stats.is_nfs_mountpoint():
|
||||
- check += [device]
|
||||
- except KeyError:
|
||||
- continue
|
||||
- devices = check
|
||||
- else:
|
||||
- for device, descr in mountstats.items():
|
||||
- stats = DeviceData()
|
||||
- stats.parse_stats(descr)
|
||||
- if stats.is_nfs_mountpoint():
|
||||
- devices += [device]
|
||||
- if len(devices) == 0:
|
||||
- print('No NFS mount points were found')
|
||||
- return 1
|
||||
-
|
||||
sample_time = 0
|
||||
|
||||
+ # make certain devices contains only NFS mount points
|
||||
+ devices = list_nfs_mounts(origdevices, mountstats)
|
||||
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
|
||||
+
|
||||
if args.interval is None:
|
||||
- print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
|
||||
return
|
||||
|
||||
- if args.count is not None:
|
||||
- count = args.count
|
||||
- while count != 0:
|
||||
- print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
|
||||
- old_mountstats = mountstats
|
||||
- time.sleep(args.interval)
|
||||
- sample_time = args.interval
|
||||
- mountstats = parse_stats_file(args.infile)
|
||||
+ count = args.count
|
||||
+ while True:
|
||||
+ if count is not None:
|
||||
count -= 1
|
||||
- else:
|
||||
- while True:
|
||||
- print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
|
||||
- old_mountstats = mountstats
|
||||
- time.sleep(args.interval)
|
||||
- sample_time = args.interval
|
||||
- mountstats = parse_stats_file(args.infile)
|
||||
+ if count == 0:
|
||||
+ break
|
||||
+ time.sleep(args.interval)
|
||||
+ old_mountstats = mountstats
|
||||
+ sample_time = args.interval
|
||||
+ mountstats = parse_stats_file(args.infile)
|
||||
+ # nfs mountpoints may appear or disappear, so we need to
|
||||
+ # recheck the devices list each time we parse mountstats
|
||||
+ devices = list_nfs_mounts(origdevices, mountstats)
|
||||
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
|
||||
|
||||
args.infile.close()
|
||||
if args.since:
|
||||
@ -1,47 +0,0 @@
|
||||
From 3cca9dbeab3b159c27c1dc48e791139744baf6d0 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Coddington <bcodding@redhat.com>
|
||||
Date: Mon, 24 Mar 2025 15:47:43 -0400
|
||||
Subject: [nfs-utils PATCH] nfs(5): Add new rdirplus functionality, clarify
|
||||
|
||||
The proposed kernel [patch][1] will modify the rdirplus mount option to
|
||||
accept optional string values of "none" and "force". Update the man page
|
||||
to reflect these changes and clarify the current client's behavior for the
|
||||
default.
|
||||
|
||||
[1]: https://lore.kernel.org/linux-nfs/8c33cd92be52255b0dd0a7489c9e5cc35434ec95.1741876784.git.bcodding@redhat.com/T/#u
|
||||
|
||||
Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
|
||||
Signed-off-by: Steve Dickson <steved@redhat.com>
|
||||
---
|
||||
utils/mount/nfs.man | 15 ++++++++++-----
|
||||
1 file changed, 10 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
|
||||
index b5c5913b..94dc29d4 100644
|
||||
--- a/utils/mount/nfs.man
|
||||
+++ b/utils/mount/nfs.man
|
||||
@@ -434,11 +434,16 @@ option may also be used by some pNFS drivers to decide how many
|
||||
connections to set up to the data servers.
|
||||
.TP 1.5i
|
||||
.BR rdirplus " / " nordirplus
|
||||
-Selects whether to use NFS v3 or v4 READDIRPLUS requests.
|
||||
-If this option is not specified, the NFS client uses READDIRPLUS requests
|
||||
-on NFS v3 or v4 mounts to read small directories.
|
||||
-Some applications perform better if the client uses only READDIR requests
|
||||
-for all directories.
|
||||
+Selects whether to use NFS v3 or v4 READDIRPLUS requests. If this option is
|
||||
+not specified, the NFS client uses a heuristic to optimize performance by
|
||||
+choosing READDIR vs READDIRPLUS based on how often the calling process uses
|
||||
+the additional attributes returned from READDIRPLUS. Some applications
|
||||
+perform better if the client uses only READDIR requests for all directories.
|
||||
+.TP 1.5i
|
||||
+.BR rdirplus={none|force}
|
||||
+If set to "force", the NFS client always attempts to use READDIRPLUS
|
||||
+requests. If set to "none", the behavior is the same as
|
||||
+.B nordirplus.
|
||||
.TP 1.5i
|
||||
.BI retry= n
|
||||
The number of minutes that the
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,242 +0,0 @@
|
||||
26f8410d mountstats/nfsiostat: merge and rework the infinite and counted loops
|
||||
21238e5c mountstats/nfsiostat: Move the checks for empty mountpoint list into the print function
|
||||
1bd30a9d nfsiostat: make comment explain mount/unmount more broadly
|
||||
2b9251ed nfsiostat: fix crash when filtering mountstats after unmount
|
||||
9836adbc nfsiostat: mirror how mountstats iostat prints the stats
|
||||
936a19cc mountstats/nfsiostat: add a function to return the fstype
|
||||
155c5343 nfsiostat: skip argv[0] when parsing command-line
|
||||
39f57998 nfsiostat/mountstats: handle KeyError in compare_iostats()
|
||||
c4c14011 nfsiostat: replace 'list' reserved word
|
||||
|
||||
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
|
||||
index 7cbe543c..dec0e861 100644
|
||||
--- a/tools/nfs-iostat/nfs-iostat.py
|
||||
+++ b/tools/nfs-iostat/nfs-iostat.py
|
||||
@@ -493,20 +493,20 @@ def list_nfs_mounts(givenlist, mountstats):
|
||||
return a full list if the given list is empty -
|
||||
may return an empty list if none found
|
||||
"""
|
||||
- list = []
|
||||
+ devicelist = []
|
||||
if len(givenlist) > 0:
|
||||
for device in givenlist:
|
||||
stats = DeviceData()
|
||||
stats.parse_stats(mountstats[device])
|
||||
if stats.is_nfs_mountpoint():
|
||||
- list += [device]
|
||||
+ devicelist += [device]
|
||||
else:
|
||||
for device, descr in mountstats.items():
|
||||
stats = DeviceData()
|
||||
stats.parse_stats(descr)
|
||||
if stats.is_nfs_mountpoint():
|
||||
- list += [device]
|
||||
- return list
|
||||
+ devicelist += [device]
|
||||
+ return devicelist
|
||||
|
||||
def iostat_command(name):
|
||||
"""iostat-like command for NFS mount points
|
||||
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
|
||||
index 014f38a3..1054f698 100755
|
||||
--- a/tools/mountstats/mountstats.py
|
||||
+++ b/tools/mountstats/mountstats.py
|
||||
@@ -560,7 +560,10 @@ class DeviceData:
|
||||
# the reference to them. so we build new lists here
|
||||
# for the result object.
|
||||
for op in result.__rpc_data['ops']:
|
||||
- result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
|
||||
+ try:
|
||||
+ result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
|
||||
+ except KeyError:
|
||||
+ continue
|
||||
|
||||
# update the remaining keys
|
||||
if protocol == 'udp':
|
||||
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
|
||||
index b7e98a2a..5556f692 100755
|
||||
--- a/tools/nfs-iostat/nfs-iostat.py
|
||||
+++ b/tools/nfs-iostat/nfs-iostat.py
|
||||
@@ -213,8 +213,11 @@ class DeviceData:
|
||||
# the reference to them. so we build new lists here
|
||||
# for the result object.
|
||||
for op in result.__rpc_data['ops']:
|
||||
- result.__rpc_data[op] = list(map(
|
||||
- difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
|
||||
+ try:
|
||||
+ result.__rpc_data[op] = list(map(
|
||||
+ difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
|
||||
+ except KeyError:
|
||||
+ continue
|
||||
|
||||
# update the remaining keys we care about
|
||||
result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends']
|
||||
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
|
||||
index 85294fb9..e46b1a83 100755
|
||||
--- a/tools/nfs-iostat/nfs-iostat.py
|
||||
+++ b/tools/nfs-iostat/nfs-iostat.py
|
||||
@@ -187,6 +187,11 @@ class DeviceData:
|
||||
found = True
|
||||
self.__parse_rpc_line(words)
|
||||
|
||||
+ def fstype(self):
|
||||
+ """Return the fstype for the mountpoint
|
||||
+ """
|
||||
+ return self.__nfs_data['fstype']
|
||||
+
|
||||
def is_nfs_mountpoint(self):
|
||||
"""Return True if this is an NFS or NFSv4 mountpoint,
|
||||
otherwise return False
|
||||
@@ -471,41 +476,31 @@ def parse_stats_file(filename):
|
||||
return ms_dict
|
||||
|
||||
def print_iostat_summary(old, new, devices, time, options):
|
||||
- stats = {}
|
||||
- diff_stats = {}
|
||||
- devicelist = []
|
||||
- if old:
|
||||
- # Trim device list to only include intersection of old and new data,
|
||||
- # this addresses umounts due to autofs mountpoints
|
||||
- for device in devices:
|
||||
- if "fstype autofs" not in str(old[device]):
|
||||
- devicelist.append(device)
|
||||
- else:
|
||||
- devicelist = devices
|
||||
+ display_stats = {}
|
||||
+
|
||||
+ if len(devices) == 0:
|
||||
+ print('No NFS mount points were found')
|
||||
+ return
|
||||
|
||||
- for device in devicelist:
|
||||
- stats[device] = DeviceData()
|
||||
- stats[device].parse_stats(new[device])
|
||||
- if old:
|
||||
+ for device in devices:
|
||||
+ stats = DeviceData()
|
||||
+ stats.parse_stats(new[device])
|
||||
+ if old and device in old:
|
||||
old_stats = DeviceData()
|
||||
old_stats.parse_stats(old[device])
|
||||
- diff_stats[device] = stats[device].compare_iostats(old_stats)
|
||||
+ if stats.fstype() == old_stats.fstype():
|
||||
+ display_stats[device] = stats.compare_iostats(old_stats)
|
||||
+ else: # device is in old, but fstypes are different
|
||||
+ display_stats[device] = stats
|
||||
+ else: # device is only in new
|
||||
+ display_stats[device] = stats
|
||||
|
||||
if options.sort:
|
||||
- if old:
|
||||
- # We now have compared data and can print a comparison
|
||||
- # ordered by mountpoint ops per second
|
||||
- devicelist.sort(key=lambda x: diff_stats[x].ops(time), reverse=True)
|
||||
- else:
|
||||
- # First iteration, just sort by newly parsed ops/s
|
||||
- devicelist.sort(key=lambda x: stats[x].ops(time), reverse=True)
|
||||
+ devices.sort(key=lambda x: display_stats[x].ops(time), reverse=True)
|
||||
|
||||
count = 1
|
||||
- for device in devicelist:
|
||||
- if old:
|
||||
- diff_stats[device].display_iostats(time, options.which)
|
||||
- else:
|
||||
- stats[device].display_iostats(time, options.which)
|
||||
+ for device in devices:
|
||||
+ display_stats[device].display_iostats(time, options.which)
|
||||
|
||||
count += 1
|
||||
if (count > options.list):
|
||||
@@ -520,10 +515,11 @@ def list_nfs_mounts(givenlist, mountstats):
|
||||
devicelist = []
|
||||
if len(givenlist) > 0:
|
||||
for device in givenlist:
|
||||
- stats = DeviceData()
|
||||
- stats.parse_stats(mountstats[device])
|
||||
- if stats.is_nfs_mountpoint():
|
||||
- devicelist += [device]
|
||||
+ if device in mountstats:
|
||||
+ stats = DeviceData()
|
||||
+ stats.parse_stats(mountstats[device])
|
||||
+ if stats.is_nfs_mountpoint():
|
||||
+ devicelist += [device]
|
||||
else:
|
||||
for device, descr in mountstats.items():
|
||||
stats = DeviceData()
|
||||
@@ -592,11 +588,7 @@ client are listed.
|
||||
parser.add_option_group(displaygroup)
|
||||
|
||||
(options, args) = parser.parse_args(sys.argv)
|
||||
- for arg in args:
|
||||
-
|
||||
- if arg == sys.argv[0]:
|
||||
- continue
|
||||
-
|
||||
+ for arg in args[1:]:
|
||||
if arg in mountstats:
|
||||
origdevices += [arg]
|
||||
elif not interval_seen:
|
||||
@@ -622,47 +614,29 @@ client are listed.
|
||||
print('Illegal <count> value %s' % arg)
|
||||
return
|
||||
|
||||
- # make certain devices contains only NFS mount points
|
||||
- devices = list_nfs_mounts(origdevices, mountstats)
|
||||
- if len(devices) == 0:
|
||||
- print('No NFS mount points were found')
|
||||
- return
|
||||
-
|
||||
-
|
||||
old_mountstats = None
|
||||
sample_time = 0.0
|
||||
|
||||
+ # make certain devices contains only NFS mount points
|
||||
+ devices = list_nfs_mounts(origdevices, mountstats)
|
||||
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options)
|
||||
+
|
||||
if not interval_seen:
|
||||
- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options)
|
||||
return
|
||||
|
||||
- if count_seen:
|
||||
- while count != 0:
|
||||
- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options)
|
||||
- old_mountstats = mountstats
|
||||
- time.sleep(interval)
|
||||
- sample_time = interval
|
||||
- mountstats = parse_stats_file('/proc/self/mountstats')
|
||||
- # automount mountpoints add and drop, if automount is involved
|
||||
- # we need to recheck the devices list when reparsing
|
||||
- devices = list_nfs_mounts(origdevices,mountstats)
|
||||
- if len(devices) == 0:
|
||||
- print('No NFS mount points were found')
|
||||
- return
|
||||
+ while True:
|
||||
+ if count_seen:
|
||||
count -= 1
|
||||
- else:
|
||||
- while True:
|
||||
- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options)
|
||||
- old_mountstats = mountstats
|
||||
- time.sleep(interval)
|
||||
- sample_time = interval
|
||||
- mountstats = parse_stats_file('/proc/self/mountstats')
|
||||
- # automount mountpoints add and drop, if automount is involved
|
||||
- # we need to recheck the devices list when reparsing
|
||||
- devices = list_nfs_mounts(origdevices,mountstats)
|
||||
- if len(devices) == 0:
|
||||
- print('No NFS mount points were found')
|
||||
- return
|
||||
+ if count == 0:
|
||||
+ break
|
||||
+ time.sleep(interval)
|
||||
+ old_mountstats = mountstats
|
||||
+ sample_time = interval
|
||||
+ mountstats = parse_stats_file('/proc/self/mountstats')
|
||||
+ # nfs mountpoints may appear or disappear, so we need to
|
||||
+ # recheck the devices list each time we parse mountstats
|
||||
+ devices = list_nfs_mounts(origdevices, mountstats)
|
||||
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options)
|
||||
|
||||
#
|
||||
# Main
|
||||
@ -1,60 +0,0 @@
|
||||
commit 162a0093a86411c4ce46d44660c57b98e4879701
|
||||
Author: Thiago Becker <tbecker@redhat.com>
|
||||
Date: Wed Sep 10 08:21:47 2025 -0500
|
||||
|
||||
nfsrahead: modify get_device_info logic
|
||||
|
||||
There are a few reports of failures by nfsrahead to set the read ahead
|
||||
when the nfs mount information is not available when the udev event
|
||||
fires. This was alleviated by retrying to read mountinfo multiple times,
|
||||
but some cases where still failing to find the device information. To
|
||||
further alleviate this issue, this patch adds a 50ms delay between
|
||||
attempts. To not incur into unecessary delays, the logic in
|
||||
get_device_info is reworked.
|
||||
|
||||
While we are in this, remove a second loop of reptitions of
|
||||
get_device_info.
|
||||
|
||||
Signed-off-by: Thiago Becker <tbecker@redhat.com>
|
||||
Signed-off-by: Steve Dickson <steved@redhat.com>
|
||||
|
||||
diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c
|
||||
index 8a11cf1a..b7b889ff 100644
|
||||
--- a/tools/nfsrahead/main.c
|
||||
+++ b/tools/nfsrahead/main.c
|
||||
@@ -117,9 +117,11 @@ out_free_device_info:
|
||||
|
||||
static int get_device_info(const char *device_number, struct device_info *device_info)
|
||||
{
|
||||
- int ret = ENOENT;
|
||||
- for (int retry_count = 0; retry_count < 10 && ret != 0; retry_count++)
|
||||
+ int ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH);
|
||||
+ for (int retry_count = 0; retry_count < 5 && ret != 0; retry_count++) {
|
||||
+ usleep(50000);
|
||||
ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH);
|
||||
+ }
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -135,7 +137,7 @@ static int conf_get_readahead(const char *kind) {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
- int ret = 0, retry, opt;
|
||||
+ int ret = 0, opt;
|
||||
struct device_info device;
|
||||
unsigned int readahead = 128, log_level, log_stderr = 0;
|
||||
|
||||
@@ -163,11 +165,7 @@ int main(int argc, char **argv)
|
||||
if ((argc - optind) != 1)
|
||||
xlog_err("expected the device number of a BDI; is udev ok?");
|
||||
|
||||
- for (retry = 0; retry <= 10; retry++ )
|
||||
- if ((ret = get_device_info(argv[optind], &device)) == 0)
|
||||
- break;
|
||||
-
|
||||
- if (ret != 0 || device.fstype == NULL) {
|
||||
+ if ((ret = get_device_info(argv[optind], &device)) != 0 || device.fstype == NULL) {
|
||||
xlog(D_GENERAL, "unable to find device %s\n", argv[optind]);
|
||||
goto out;
|
||||
}
|
||||
@ -1,456 +0,0 @@
|
||||
From db822b7432a3fba65454276779d6c165247f68f0 Mon Sep 17 00:00:00 2001
|
||||
From: Scott Mayhew <smayhew@redhat.com>
|
||||
Date: Tue, 13 Jan 2026 14:05:28 -0500
|
||||
Subject: [PATCH 3/4] support: Add a mini-library to extract and apply RPC
|
||||
credentials
|
||||
|
||||
Add server functionality to extract the credentials from the client RPC
|
||||
call, and apply them. This is needed in order to perform access checking
|
||||
on the requested path in the mountd daemon.
|
||||
---
|
||||
aclocal/libtirpc.m4 | 11 +++
|
||||
support/include/Makefile.am | 1 +
|
||||
support/include/nfs_ucred.h | 44 ++++++++++
|
||||
support/misc/Makefile.am | 3 +-
|
||||
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 27368ff2..b4128c95 100644
|
||||
--- a/aclocal/libtirpc.m4
|
||||
+++ b/aclocal/libtirpc.m4
|
||||
@@ -26,6 +26,17 @@ AC_DEFUN([AC_LIBTIRPC], [
|
||||
[Define to 1 if your tirpc library provides libtirpc_set_debug])],,
|
||||
[${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 599f500e..4d0e1e87 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 \
|
||||
nfslib.h \
|
||||
nfsrpc.h \
|
||||
nls.h \
|
||||
diff --git a/support/include/nfs_ucred.h b/support/include/nfs_ucred.h
|
||||
new file mode 100644
|
||||
index 00000000..d58b61e4
|
||||
--- /dev/null
|
||||
+++ b/support/include/nfs_ucred.h
|
||||
@@ -0,0 +1,44 @@
|
||||
+#ifndef _NFS_UCRED_H
|
||||
+#define _NFS_UCRED_H
|
||||
+
|
||||
+#include <sys/types.h>
|
||||
+
|
||||
+struct nfs_ucred {
|
||||
+ uid_t uid;
|
||||
+ gid_t gid;
|
||||
+ int ngroups;
|
||||
+ gid_t *groups;
|
||||
+};
|
||||
+
|
||||
+struct svc_req;
|
||||
+struct exportent;
|
||||
+
|
||||
+int nfs_ucred_get(struct nfs_ucred **credp, struct svc_req *rqst,
|
||||
+ const struct exportent *ep);
|
||||
+
|
||||
+void nfs_ucred_squash_groups(struct nfs_ucred *cred,
|
||||
+ const struct exportent *ep);
|
||||
+int nfs_ucred_reload_groups(struct nfs_ucred *cred, const struct exportent *ep);
|
||||
+int nfs_ucred_swap_effective(const struct nfs_ucred *cred,
|
||||
+ struct nfs_ucred **savedp);
|
||||
+
|
||||
+static inline void nfs_ucred_free(struct nfs_ucred *cred)
|
||||
+{
|
||||
+ free(cred->groups);
|
||||
+ free(cred);
|
||||
+}
|
||||
+
|
||||
+static inline void nfs_ucred_init_groups(struct nfs_ucred *cred, gid_t *groups,
|
||||
+ int ngroups)
|
||||
+{
|
||||
+ cred->groups = groups;
|
||||
+ cred->ngroups = ngroups;
|
||||
+}
|
||||
+
|
||||
+static inline void nfs_ucred_free_groups(struct nfs_ucred *cred)
|
||||
+{
|
||||
+ free(cred->groups);
|
||||
+ nfs_ucred_init_groups(cred, NULL, 0);
|
||||
+}
|
||||
+
|
||||
+#endif /* _NFS_UCRED_H */
|
||||
diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am
|
||||
index 8936b0d6..55099cba 100644
|
||||
--- a/support/misc/Makefile.am
|
||||
+++ b/support/misc/Makefile.am
|
||||
@@ -1,6 +1,7 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libmisc.a
|
||||
-libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c
|
||||
+libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c \
|
||||
+ ucred.c
|
||||
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
diff --git a/support/misc/ucred.c b/support/misc/ucred.c
|
||||
new file mode 100644
|
||||
index 00000000..92d97912
|
||||
--- /dev/null
|
||||
+++ b/support/misc/ucred.c
|
||||
@@ -0,0 +1,162 @@
|
||||
+#ifdef HAVE_CONFIG_H
|
||||
+#include <config.h>
|
||||
+#endif
|
||||
+
|
||||
+#include <alloca.h>
|
||||
+#include <errno.h>
|
||||
+#include <pwd.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+#include <grp.h>
|
||||
+
|
||||
+#include "exportfs.h"
|
||||
+#include "nfs_ucred.h"
|
||||
+
|
||||
+#include "xlog.h"
|
||||
+
|
||||
+void nfs_ucred_squash_groups(struct nfs_ucred *cred, const struct exportent *ep)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (!(ep->e_flags & NFSEXP_ROOTSQUASH))
|
||||
+ return;
|
||||
+ if (cred->gid == 0)
|
||||
+ cred->gid = ep->e_anongid;
|
||||
+ for (i = 0; i < cred->ngroups; i++) {
|
||||
+ if (cred->groups[i] == 0)
|
||||
+ cred->groups[i] = ep->e_anongid;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int nfs_ucred_init_effective(struct nfs_ucred *cred)
|
||||
+{
|
||||
+ int ngroups = getgroups(0, NULL);
|
||||
+
|
||||
+ if (ngroups > 0) {
|
||||
+ size_t sz = ngroups * sizeof(gid_t);
|
||||
+ gid_t *groups = malloc(sz);
|
||||
+ if (groups == NULL)
|
||||
+ return ENOMEM;
|
||||
+ if (getgroups(ngroups, groups) == -1) {
|
||||
+ free(groups);
|
||||
+ return errno;
|
||||
+ }
|
||||
+ nfs_ucred_init_groups(cred, groups, ngroups);
|
||||
+ } else
|
||||
+ nfs_ucred_init_groups(cred, NULL, 0);
|
||||
+ cred->uid = geteuid();
|
||||
+ cred->gid = getegid();
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static size_t nfs_ucred_getpw_r_size_max(void)
|
||||
+{
|
||||
+ long buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
+
|
||||
+ if (buflen == -1)
|
||||
+ return 16384;
|
||||
+ return buflen;
|
||||
+}
|
||||
+
|
||||
+int nfs_ucred_reload_groups(struct nfs_ucred *cred, const struct exportent *ep)
|
||||
+{
|
||||
+ struct passwd pwd, *pw;
|
||||
+ uid_t uid = cred->uid;
|
||||
+ gid_t gid = cred->gid;
|
||||
+ size_t buflen;
|
||||
+ char *buf;
|
||||
+ int ngroups = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (ep->e_flags & (NFSEXP_ALLSQUASH | NFSEXP_ROOTSQUASH) &&
|
||||
+ (int)uid == ep->e_anonuid)
|
||||
+ return 0;
|
||||
+ buflen = nfs_ucred_getpw_r_size_max();
|
||||
+ buf = alloca(buflen);
|
||||
+ ret = getpwuid_r(uid, &pwd, buf, buflen, &pw);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+ if (!pw)
|
||||
+ return ENOENT;
|
||||
+ if (getgrouplist(pw->pw_name, gid, NULL, &ngroups) == -1 &&
|
||||
+ ngroups > 0) {
|
||||
+ gid_t *groups = malloc(ngroups * sizeof(groups[0]));
|
||||
+ if (groups == NULL)
|
||||
+ return ENOMEM;
|
||||
+ if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) {
|
||||
+ free(groups);
|
||||
+ return ENOMEM;
|
||||
+ }
|
||||
+ free(cred->groups);
|
||||
+ nfs_ucred_init_groups(cred, groups, ngroups);
|
||||
+ nfs_ucred_squash_groups(cred, ep);
|
||||
+ } else
|
||||
+ nfs_ucred_free_groups(cred);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nfs_ucred_set_effective(const struct nfs_ucred *cred,
|
||||
+ const struct nfs_ucred *saved)
|
||||
+{
|
||||
+ uid_t suid = saved ? saved->uid : geteuid();
|
||||
+ gid_t sgid = saved ? saved->gid : getegid();
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Start with a privileged effective user */
|
||||
+ if (setresuid(-1, 0, -1) < 0) {
|
||||
+ xlog(L_WARNING, "can't change privileged user %u-%u. %s",
|
||||
+ geteuid(), getegid(), strerror(errno));
|
||||
+ return errno;
|
||||
+ }
|
||||
+
|
||||
+ if (setgroups(cred->ngroups, cred->groups) == -1) {
|
||||
+ xlog(L_WARNING, "can't change groups for user %u-%u. %s",
|
||||
+ geteuid(), getegid(), strerror(errno));
|
||||
+ return errno;
|
||||
+ }
|
||||
+ if (setresgid(-1, cred->gid, sgid) == -1) {
|
||||
+ xlog(L_WARNING, "can't change gid for user %u-%u. %s",
|
||||
+ geteuid(), getegid(), strerror(errno));
|
||||
+ ret = errno;
|
||||
+ goto restore_groups;
|
||||
+ }
|
||||
+ if (setresuid(-1, cred->uid, suid) == -1) {
|
||||
+ xlog(L_WARNING, "can't change uid for user %u-%u. %s",
|
||||
+ geteuid(), getegid(), strerror(errno));
|
||||
+ ret = errno;
|
||||
+ goto restore_gid;
|
||||
+ }
|
||||
+ return 0;
|
||||
+restore_gid:
|
||||
+ if (setresgid(-1, sgid, -1) < 0) {
|
||||
+ xlog(L_WARNING, "can't restore privileged user %u-%u. %s",
|
||||
+ geteuid(), getegid(), strerror(errno));
|
||||
+ }
|
||||
+restore_groups:
|
||||
+ if (saved)
|
||||
+ setgroups(saved->ngroups, saved->groups);
|
||||
+ else
|
||||
+ setgroups(0, NULL);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int nfs_ucred_swap_effective(const struct nfs_ucred *cred,
|
||||
+ struct nfs_ucred **savedp)
|
||||
+{
|
||||
+ struct nfs_ucred *saved = malloc(sizeof(*saved));
|
||||
+ int ret;
|
||||
+
|
||||
+ if (saved == NULL)
|
||||
+ return ENOMEM;
|
||||
+ ret = nfs_ucred_init_effective(saved);
|
||||
+ if (ret != 0) {
|
||||
+ free(saved);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = nfs_ucred_set_effective(cred, saved);
|
||||
+ if (savedp == NULL || ret != 0)
|
||||
+ nfs_ucred_free(saved);
|
||||
+ else
|
||||
+ *savedp = saved;
|
||||
+ return ret;
|
||||
+}
|
||||
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
|
||||
index 67e3a8e1..d7ba72a7 100644
|
||||
--- a/support/nfs/Makefile.am
|
||||
+++ b/support/nfs/Makefile.am
|
||||
@@ -7,7 +7,7 @@ libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
|
||||
xcommon.c wildmat.c mydaemon.c \
|
||||
rpc_socket.c getport.c \
|
||||
svc_socket.c cacheio.c closeall.c nfs_mntent.c \
|
||||
- svc_create.c atomicio.c strlcat.c strlcpy.c
|
||||
+ svc_create.c atomicio.c strlcat.c strlcpy.c ucred.c
|
||||
libnfs_la_LIBADD = libnfsconf.la
|
||||
|
||||
libnfsconf_la_SOURCES = conffile.c xlog.c
|
||||
diff --git a/support/nfs/ucred.c b/support/nfs/ucred.c
|
||||
new file mode 100644
|
||||
index 00000000..6ea8efdf
|
||||
--- /dev/null
|
||||
+++ b/support/nfs/ucred.c
|
||||
@@ -0,0 +1,147 @@
|
||||
+#ifdef HAVE_CONFIG_H
|
||||
+#include <config.h>
|
||||
+#endif
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+#include <rpc/rpc.h>
|
||||
+
|
||||
+#include "exportfs.h"
|
||||
+#include "nfs_ucred.h"
|
||||
+
|
||||
+#ifdef HAVE_TIRPC_GSS_GETCRED
|
||||
+#include <rpc/rpcsec_gss.h>
|
||||
+#endif /* HAVE_TIRPC_GSS_GETCRED */
|
||||
+#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
|
||||
+#include <rpc/auth_des.h>
|
||||
+#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
|
||||
+
|
||||
+static int nfs_ucred_copy_cred(struct nfs_ucred *cred, uid_t uid, gid_t gid,
|
||||
+ const gid_t *groups, int ngroups)
|
||||
+{
|
||||
+ if (ngroups > 0) {
|
||||
+ size_t sz = ngroups * sizeof(groups[0]);
|
||||
+ cred->groups = malloc(sz);
|
||||
+ if (cred->groups == NULL)
|
||||
+ return ENOMEM;
|
||||
+ cred->ngroups = ngroups;
|
||||
+ memcpy(cred->groups, groups, sz);
|
||||
+ } else
|
||||
+ nfs_ucred_init_groups(cred, NULL, 0);
|
||||
+ cred->uid = uid;
|
||||
+ cred->gid = gid;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nfs_ucred_init_cred_squashed(struct nfs_ucred *cred,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ cred->uid = ep->e_anonuid;
|
||||
+ cred->gid = ep->e_anongid;
|
||||
+ nfs_ucred_init_groups(cred, NULL, 0);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nfs_ucred_init_cred(struct nfs_ucred *cred, uid_t uid, gid_t gid,
|
||||
+ const gid_t *groups, int ngroups,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ if (ep->e_flags & NFSEXP_ALLSQUASH) {
|
||||
+ nfs_ucred_init_cred_squashed(cred, ep);
|
||||
+ } else if (ep->e_flags & NFSEXP_ROOTSQUASH && uid == 0) {
|
||||
+ nfs_ucred_init_cred_squashed(cred, ep);
|
||||
+ if (gid != 0)
|
||||
+ cred->gid = gid;
|
||||
+ } else {
|
||||
+ int ret = nfs_ucred_copy_cred(cred, uid, gid, groups, ngroups);
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+ nfs_ucred_squash_groups(cred, ep);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nfs_ucred_init_null(struct nfs_ucred *cred,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ return nfs_ucred_init_cred_squashed(cred, ep);
|
||||
+}
|
||||
+
|
||||
+static int nfs_ucred_init_unix(struct nfs_ucred *cred, struct svc_req *rqst,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ struct authunix_parms *aup;
|
||||
+
|
||||
+ aup = (struct authunix_parms *)rqst->rq_clntcred;
|
||||
+ return nfs_ucred_init_cred(cred, aup->aup_uid, aup->aup_gid,
|
||||
+ aup->aup_gids, aup->aup_len, ep);
|
||||
+}
|
||||
+
|
||||
+#ifdef HAVE_TIRPC_GSS_GETCRED
|
||||
+static int nfs_ucred_init_gss(struct nfs_ucred *cred, struct svc_req *rqst,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ rpc_gss_ucred_t *gss_ucred = NULL;
|
||||
+
|
||||
+ if (!rpc_gss_getcred(rqst, NULL, &gss_ucred, NULL) || gss_ucred == NULL)
|
||||
+ return EINVAL;
|
||||
+ return nfs_ucred_init_cred(cred, gss_ucred->uid, gss_ucred->gid,
|
||||
+ gss_ucred->gidlist, gss_ucred->gidlen, ep);
|
||||
+}
|
||||
+#endif /* HAVE_TIRPC_GSS_GETCRED */
|
||||
+
|
||||
+#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
|
||||
+int authdes_getucred(struct authdes_cred *adc, uid_t *uid, gid_t *gid,
|
||||
+ int *grouplen, gid_t *groups);
|
||||
+
|
||||
+static int nfs_ucred_init_des(struct nfs_ucred *cred, struct svc_req *rqst,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ struct authdes_cred *des_cred;
|
||||
+ uid_t uid;
|
||||
+ gid_t gid;
|
||||
+ int grouplen;
|
||||
+ gid_t groups[NGROUPS];
|
||||
+
|
||||
+ des_cred = (struct authdes_cred *)rqst->rq_clntcred;
|
||||
+ if (!authdes_getucred(des_cred, &uid, &gid, &grouplen, &groups[0]))
|
||||
+ return EINVAL;
|
||||
+ return nfs_ucred_init_cred(cred, uid, gid, groups, grouplen, ep);
|
||||
+}
|
||||
+#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
|
||||
+
|
||||
+int nfs_ucred_get(struct nfs_ucred **credp, struct svc_req *rqst,
|
||||
+ const struct exportent *ep)
|
||||
+{
|
||||
+ struct nfs_ucred *cred = malloc(sizeof(*cred));
|
||||
+ int ret;
|
||||
+
|
||||
+ *credp = NULL;
|
||||
+ if (cred == NULL)
|
||||
+ return ENOMEM;
|
||||
+ switch (rqst->rq_cred.oa_flavor) {
|
||||
+ case AUTH_UNIX:
|
||||
+ ret = nfs_ucred_init_unix(cred, rqst, ep);
|
||||
+ break;
|
||||
+#ifdef HAVE_TIRPC_GSS_GETCRED
|
||||
+ case RPCSEC_GSS:
|
||||
+ ret = nfs_ucred_init_gss(cred, rqst, ep);
|
||||
+ break;
|
||||
+#endif /* HAVE_TIRPC_GSS_GETCRED */
|
||||
+#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
|
||||
+ case AUTH_DES:
|
||||
+ ret = nfs_ucred_init_des(cred, rqst, ep);
|
||||
+ break;
|
||||
+#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
|
||||
+ default:
|
||||
+ ret = nfs_ucred_init_null(cred, ep);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (ret == 0) {
|
||||
+ *credp = cred;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ free(cred);
|
||||
+ return ret;
|
||||
+}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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.3.3
|
||||
Release: 68%{?dist}
|
||||
Release: 59%{?dist}
|
||||
Epoch: 1
|
||||
|
||||
# group all 32bit related archs
|
||||
@ -109,22 +109,6 @@ Patch055: nfs-utils-2.3.3-systemd-rpcstatd.patch
|
||||
Patch056: nfs-utils-2.3.3-mountd-v4clnts.patch
|
||||
Patch057: nfs-utils-2.3.3-covscan-return-value.patch
|
||||
|
||||
#
|
||||
# RHEL 8.10.z
|
||||
#
|
||||
Patch058: nfs-utils-2.3.3-gssd-man-document-use-gss-proxy.patch
|
||||
Patch059: nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch
|
||||
Patch060: nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch
|
||||
Patch061: nfs-utils-2.3.3-nfsiostat-fixes.patch
|
||||
Patch062: nfs-utils-2.3.3-mountstats-fixes.patch
|
||||
Patch063: nfs-utils-2.3.3-nfs-man-rdirplus.patch
|
||||
Patch064: nfs-utils-2.3.3-nfsrahead-Modify-get_device_info-logic.patch
|
||||
Patch065: nfs-utils-2.3.3-gssd-protect-kerberos-ticket-cache-access.patch
|
||||
Patch066: nfs-utils-2.3.3-mountd-Minor-refactor-of-get_rootfh.patch
|
||||
Patch067: nfs-utils-2.3.3-mountd-Separate-lookup-of-the-exported-directory-and.patch
|
||||
Patch068: nfs-utils-2.3.3-support-Add-a-mini-library-to-extract-and-apply-RPC-.patch
|
||||
Patch069: nfs-utils-2.3.3-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-2.3.3-idmap-errmsg.patch
|
||||
@ -167,7 +151,6 @@ Requires: libnfsidmap libevent
|
||||
Requires: libtirpc >= 0.2.3-1 libblkid libcap libmount
|
||||
%{?systemd_requires}
|
||||
Requires: gssproxy => 0.7.0-3
|
||||
Requires: (selinux-policy >= 3.14.3-139.el8_10.2 if selinux-policy)
|
||||
|
||||
%package -n libnfsidmap
|
||||
Summary: NFSv4 User and Group ID Mapping Library
|
||||
@ -403,38 +386,6 @@ fi
|
||||
%{_libdir}/libnfsidmap.so
|
||||
|
||||
%changelog
|
||||
* Fri Feb 27 2026 Scott Mayhew <smayhew@redhat.com> 2.3.3-68
|
||||
- Add requires for selinux-policy (RHEL-127095)
|
||||
|
||||
* Fri Jan 30 2026 Scott Mayhew <smayhew@redhat.com> 2.3.3-67
|
||||
- mountd: Minor refactor of get_rootfh() (RHEL-127095)
|
||||
- mountd: Separate lookup of the exported directory and the mount path (RHEL-127095)
|
||||
- support: Add a mini-library to extract and apply RPC credentials (RHEL-127095)
|
||||
- Fix access checks when mounting subdirectories in NFSv3 (RHEL-127095)
|
||||
Resolves: CVE-2025-12801
|
||||
|
||||
* Fri Jan 9 2026 Scott Mayhew <smayhew@redhat.com> 2.3.3-66
|
||||
- gssd: protect kerberos ticket cache access (RHEL-103627)
|
||||
|
||||
* Mon Dec 8 2025 Steve Dickson <steved@redhat.com> 2.3.3-65
|
||||
- nfsrahead: modify get_device_info logic (RHEL-108924)
|
||||
|
||||
* Tue May 20 2025 Scott Mayhew <smayhew@redhat.com> 2.3.3-64
|
||||
- update rdirplus documentation on nfs(5) man page (RHEL-91253)
|
||||
|
||||
* Fri May 9 2025 Scott Mayhew <smayhew@redhat.com> 2.3.3-63
|
||||
- mountstats fixes (RHEL-90242)
|
||||
|
||||
* Thu May 8 2025 Scott Mayhew <smayhew@redhat.com> 2.3.3-62
|
||||
- nfsiostat fixes (RHEL-90242)
|
||||
|
||||
* Mon Apr 28 2025 Scott Mayhew <smayhew@redhat.com> 2.3.3-61
|
||||
- gssd: unconditionally use krb5_get_init_creds_opt_alloc (RHEL-62422)
|
||||
- gssd: do not use krb5_cc_initialize (RHEL-62422)
|
||||
|
||||
* Tue Apr 15 2025 Scott Mayhew <smayhew@redhat.com> 2.3.3-60
|
||||
- gssd.man: add documentation for use-gss-proxy (RHEL-13085)
|
||||
|
||||
* Thu Jan 12 2023 Steve Dickson <steved@redhat.com> 2.3.3-59
|
||||
- Covscan Scan: Wrong Check of Return Value (bz 2151966)
|
||||
- Covscan Scan: Clang (experimental) (bz 2151971)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user