Fix rpc.gssd creating machine credcache with dup entries for the nfs service principal

Resolves: RHEL-62422
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
This commit is contained in:
Scott Mayhew 2025-04-28 16:46:17 -04:00
parent a0154bb448
commit 99360e6f4c
3 changed files with 313 additions and 1 deletions

View File

@ -0,0 +1,208 @@
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

View File

@ -0,0 +1,98 @@
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

View File

@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
Name: nfs-utils
URL: http://linux-nfs.org/
Version: 2.3.3
Release: 60%{?dist}
Release: 61%{?dist}
Epoch: 1
# group all 32bit related archs
@ -113,6 +113,8 @@ 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
Patch100: nfs-utils-1.2.1-statdpath-man.patch
Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
@ -391,6 +393,10 @@ fi
%{_libdir}/libnfsidmap.so
%changelog
* 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)