diff --git a/nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch b/nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch new file mode 100644 index 0000000..2a7d217 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch @@ -0,0 +1,208 @@ +From ad4eafccd244e87af315c432d076b4f988dde52a Mon Sep 17 00:00:00 2001 +From: Olga Kornievskaia +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 +Signed-off-by: Steve Dickson +(cherry picked from commit 1cd9e3c0d290646e80750249914396566dd6b800) +Signed-off-by: Scott Mayhew +--- + 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 : "", 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 + diff --git a/nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch b/nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch new file mode 100644 index 0000000..eabeda9 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch @@ -0,0 +1,98 @@ +From 55d9bf151b100db9bf52e8f968e33f3ae1d234f5 Mon Sep 17 00:00:00 2001 +From: Olga Kornievskaia +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 +Signed-off-by: Steve Dickson +(cherry picked from commit 9b3f949331c6541a358fc28bac323533f94d7e0b) +Signed-off-by: Scott Mayhew +--- + 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 + diff --git a/nfs-utils.spec b/nfs-utils.spec index e6f0096..1c9ed60 100644 --- a/nfs-utils.spec +++ b/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.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 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 2.3.3-60 - gssd.man: add documentation for use-gss-proxy (RHEL-13085)