From 6f8b834345c00f2977f731c81d9d2430622c7046 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Wed, 16 Dec 2020 16:37:37 +0000 Subject: [PATCH] import nfs-utils-2.3.3-40.el8 --- ...utils-2.3.3-gssd-multithread-updates.patch | 1009 +++++++++++++++++ .../nfs-utils-2.3.3-nfsiostat-div-zero.patch | 11 - SPECS/nfs-utils.spec | 8 +- 3 files changed, 1015 insertions(+), 13 deletions(-) create mode 100644 SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch diff --git a/SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch b/SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch new file mode 100644 index 0000000..2813d0c --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch @@ -0,0 +1,1009 @@ +diff --git a/aclocal/libevent.m4 b/aclocal/libevent.m4 +index b5ac00ff..0ebcb524 100644 +--- a/aclocal/libevent.m4 ++++ b/aclocal/libevent.m4 +@@ -4,9 +4,13 @@ AC_DEFUN([AC_LIBEVENT], [ + dnl Check for libevent, but do not add -levent to LIBS + AC_CHECK_LIB([event], [event_dispatch], [LIBEVENT=-levent], + [AC_MSG_ERROR([libevent not found.])]) ++ AC_CHECK_LIB([event_core], [event_base_dispatch], [LIBEVENT=-levent_core], ++ [AC_MSG_ERROR([libevent2 not found.])]) + AC_SUBST(LIBEVENT) + + AC_CHECK_HEADERS([event.h], , + [AC_MSG_ERROR([libevent headers not found.])]) ++ AC_CHECK_HEADERS([event2/event.h], , ++ [AC_MSG_ERROR([libevent headers not found.])]) + + ])dnl +diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c +index 2a7f3a13..982b96f4 100644 +--- a/utils/gssd/gss_names.c ++++ b/utils/gssd/gss_names.c +@@ -110,10 +110,12 @@ get_hostbased_client_name(gss_name_t client_name, gss_OID mech, + /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to + * an NT_HOSTBASED_SERVICE name */ + if (g_OID_equal(&krb5oid, mech)) { +- if (get_krb5_hostbased_name(&name, &cname) == 0) +- *hostbased_name = cname; ++ if (get_krb5_hostbased_name(&name, &cname) != 0) ++ goto out_rel_buf; ++ *hostbased_name = cname; + } else { + printerr(1, "WARNING: unknown/unsupport mech OID\n"); ++ goto out_rel_buf; + } + + res = 0; +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index af66ed62..6f461fdf 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -64,7 +64,7 @@ + #include + #include + #include +-#include ++#include + + #include "gssd.h" + #include "err_util.h" +@@ -77,7 +77,7 @@ static char *pipefs_path = GSSD_PIPEFS_DIR; + static DIR *pipefs_dir; + static int pipefs_fd; + static int inotify_fd; +-struct event inotify_ev; ++struct event *inotify_ev; + + char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE; + char **ccachesearch; +@@ -90,9 +90,9 @@ char *ccachedir = NULL; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; + static bool use_gssproxy = false; +-int thread_started = false; +-pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER; +-pthread_cond_t pcond = PTHREAD_COND_INITIALIZER; ++pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER; ++static bool signal_received = false; ++static struct event_base *evbase = NULL; + + TAILQ_HEAD(topdir_list_head, topdir) topdir_list; + +@@ -359,20 +359,28 @@ out: + free(port); + } + ++/* Actually frees clp and fields that might be used from other ++ * threads if was last reference. ++ */ + static void +-gssd_destroy_client(struct clnt_info *clp) ++gssd_free_client(struct clnt_info *clp) + { +- if (clp->krb5_fd >= 0) { ++ int refcnt; ++ ++ pthread_mutex_lock(&clp_lock); ++ refcnt = --clp->refcount; ++ pthread_mutex_unlock(&clp_lock); ++ if (refcnt > 0) ++ return; ++ ++ printerr(3, "freeing client %s\n", clp->relpath); ++ ++ if (clp->krb5_fd >= 0) + close(clp->krb5_fd); +- event_del(&clp->krb5_ev); +- } + +- if (clp->gssd_fd >= 0) { ++ if (clp->gssd_fd >= 0) + close(clp->gssd_fd); +- event_del(&clp->gssd_ev); +- } + +- inotify_rm_watch(inotify_fd, clp->wd); + free(clp->relpath); + free(clp->servicename); + free(clp->servername); +@@ -380,6 +388,30 @@ gssd_destroy_client(struct clnt_info *clp) + free(clp); + } + ++/* Called when removing from clnt_list to tear down event handling. ++ * Will then free clp if was last reference. ++ */ ++static void ++gssd_destroy_client(struct clnt_info *clp) ++{ ++ printerr(3, "destroying client %s\n", clp->relpath); ++ ++ if (clp->krb5_ev) { ++ event_del(clp->krb5_ev); ++ event_free(clp->krb5_ev); ++ clp->krb5_ev = NULL; ++ } ++ ++ if (clp->gssd_ev) { ++ event_del(clp->gssd_ev); ++ event_free(clp->gssd_ev); ++ clp->gssd_ev = NULL; ++ } ++ ++ inotify_rm_watch(inotify_fd, clp->wd); ++ gssd_free_client(clp); ++} ++ + static void gssd_scan(void); + + static int +@@ -416,11 +448,21 @@ static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp) + info = malloc(sizeof(struct clnt_upcall_info)); + if (info == NULL) + return NULL; ++ ++ pthread_mutex_lock(&clp_lock); ++ clp->refcount++; ++ pthread_mutex_unlock(&clp_lock); + info->clp = clp; + + return info; + } + ++void free_upcall_info(struct clnt_upcall_info *info) ++{ ++ gssd_free_client(info->clp); ++ free(info); ++} ++ + /* For each upcall read the upcall info into the buffer, then create a + * thread in a detached state so that resources are released back into + * the system without the need for a join. +@@ -438,13 +480,13 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) + info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf)); + if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') { + printerr(0, "WARNING: %s: failed reading request\n", __func__); +- free(info); ++ free_upcall_info(info); + return; + } + info->lbuf[info->lbuflen-1] = 0; + + if (start_upcall_thread(handle_gssd_upcall, info)) +- free(info); ++ free_upcall_info(info); + } + + static void +@@ -461,12 +503,12 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) + sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) { + printerr(0, "WARNING: %s: failed reading uid from krb5 " + "upcall pipe: %s\n", __func__, strerror(errno)); +- free(info); ++ free_upcall_info(info); + return; + } + + if (start_upcall_thread(handle_krb5_upcall, info)) +- free(info); ++ free_upcall_info(info); + } + + static struct clnt_info * +@@ -501,6 +543,7 @@ gssd_get_clnt(struct topdir *tdi, const char *name) + clp->name = clp->relpath + strlen(tdi->name) + 1; + clp->krb5_fd = -1; + clp->gssd_fd = -1; ++ clp->refcount = 1; + + TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list); + return clp; +@@ -535,15 +578,15 @@ gssd_scan_clnt(struct clnt_info *clp) + clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK); + + if (gssd_was_closed && clp->gssd_fd >= 0) { +- event_set(&clp->gssd_ev, clp->gssd_fd, EV_READ | EV_PERSIST, +- gssd_clnt_gssd_cb, clp); +- event_add(&clp->gssd_ev, NULL); ++ clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST, ++ gssd_clnt_gssd_cb, clp); ++ event_add(clp->gssd_ev, NULL); + } + + if (krb5_was_closed && clp->krb5_fd >= 0) { +- event_set(&clp->krb5_ev, clp->krb5_fd, EV_READ | EV_PERSIST, +- gssd_clnt_krb5_cb, clp); +- event_add(&clp->krb5_ev, NULL); ++ clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST, ++ gssd_clnt_krb5_cb, clp); ++ event_add(clp->krb5_ev, NULL); + } + + if (clp->krb5_fd == -1 && clp->gssd_fd == -1) +@@ -649,7 +692,7 @@ gssd_scan_topdir(const char *name) + if (clp->scanned) + continue; + +- printerr(3, "destroying client %s\n", clp->relpath); ++ printerr(3, "orphaned client %s\n", clp->relpath); + saveprev = clp->list.tqe_prev; + TAILQ_REMOVE(&tdi->clnt_list, clp, list); + gssd_destroy_client(clp); +@@ -746,12 +789,16 @@ gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotif + } else if (ev->mask & IN_DELETE) { + if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) { + close(clp->gssd_fd); +- event_del(&clp->gssd_ev); ++ event_del(clp->gssd_ev); ++ event_free(clp->gssd_ev); ++ clp->gssd_ev = NULL; + clp->gssd_fd = -1; + + } else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) { + close(clp->krb5_fd); +- event_del(&clp->krb5_ev); ++ event_del(clp->krb5_ev); ++ event_free(clp->krb5_ev); ++ clp->krb5_ev = NULL; + clp->krb5_fd = -1; + } + +@@ -824,10 +871,15 @@ found: + static void + sig_die(int signal) + { +- if (root_uses_machine_creds) +- gssd_destroy_krb5_machine_creds(); ++ if (signal_received) { ++ gssd_destroy_krb5_principals(root_uses_machine_creds); ++ printerr(1, "forced exiting on signal %d\n", signal); ++ exit(0); ++ } ++ ++ signal_received = true; + printerr(1, "exiting on signal %d\n", signal); +- exit(0); ++ event_base_loopexit(evbase, NULL); + } + + static void +@@ -884,9 +936,10 @@ main(int argc, char *argv[]) + int rpc_verbosity = 0; + int opt; + int i; ++ int rc; + extern char *optarg; + char *progname; +- struct event sighup_ev; ++ struct event *sighup_ev; + + read_gss_conf(); + +@@ -1025,7 +1078,11 @@ main(int argc, char *argv[]) + if (gssd_check_mechs() != 0) + errx(1, "Problem with gssapi library"); + +- event_init(); ++ evbase = event_base_new(); ++ if (!evbase) { ++ printerr(0, "ERROR: failed to create event base\n"); ++ exit(EXIT_FAILURE); ++ } + + pipefs_dir = opendir(pipefs_path); + if (!pipefs_dir) { +@@ -1047,18 +1104,43 @@ main(int argc, char *argv[]) + + signal(SIGINT, sig_die); + signal(SIGTERM, sig_die); +- signal_set(&sighup_ev, SIGHUP, gssd_scan_cb, NULL); +- signal_add(&sighup_ev, NULL); +- event_set(&inotify_ev, inotify_fd, EV_READ | EV_PERSIST, gssd_inotify_cb, NULL); +- event_add(&inotify_ev, NULL); ++ sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL); ++ evsignal_add(sighup_ev, NULL); ++ inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST, ++ gssd_inotify_cb, NULL); ++ event_add(inotify_ev, NULL); + + TAILQ_INIT(&topdir_list); + gssd_scan(); + daemon_ready(); + +- event_dispatch(); ++ rc = event_base_dispatch(evbase); + +- printerr(0, "ERROR: event_dispatch() returned!\n"); +- return EXIT_FAILURE; +-} ++ printerr(0, "event_dispatch() returned %i!\n", rc); ++ ++ gssd_destroy_krb5_principals(root_uses_machine_creds); ++ ++ while (!TAILQ_EMPTY(&topdir_list)) { ++ struct topdir *tdi = TAILQ_FIRST(&topdir_list); ++ TAILQ_REMOVE(&topdir_list, tdi, list); ++ while (!TAILQ_EMPTY(&tdi->clnt_list)) { ++ struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list); ++ TAILQ_REMOVE(&tdi->clnt_list, clp, list); ++ gssd_destroy_client(clp); ++ } ++ free(tdi); ++ } ++ ++ event_free(inotify_ev); ++ event_free(sighup_ev); ++ event_base_free(evbase); ++ ++ close(inotify_fd); ++ close(pipefs_fd); ++ closedir(pipefs_dir); + ++ free(preferred_realm); ++ free(ccachesearch); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} +diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h +index f4f59754..1e8c58d4 100644 +--- a/utils/gssd/gssd.h ++++ b/utils/gssd/gssd.h +@@ -62,13 +62,10 @@ extern int root_uses_machine_creds; + extern unsigned int context_timeout; + extern unsigned int rpc_timeout; + extern char *preferred_realm; +-extern pthread_mutex_t ple_lock; +-extern pthread_cond_t pcond; +-extern pthread_mutex_t pmutex; +-extern int thread_started; + + struct clnt_info { + TAILQ_ENTRY(clnt_info) list; ++ int refcount; + int wd; + bool scanned; + char *name; +@@ -79,9 +76,9 @@ struct clnt_info { + int vers; + char *protocol; + int krb5_fd; +- struct event krb5_ev; ++ struct event *krb5_ev; + int gssd_fd; +- struct event gssd_ev; ++ struct event *gssd_ev; + struct sockaddr_storage addr; + }; + +@@ -94,6 +91,7 @@ struct clnt_upcall_info { + + void handle_krb5_upcall(struct clnt_upcall_info *clp); + void handle_gssd_upcall(struct clnt_upcall_info *clp); ++void free_upcall_info(struct clnt_upcall_info *info); + + + #endif /* _RPC_GSSD_H_ */ +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index bfcf3f09..ae3ebe81 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -149,9 +149,10 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + char *buf = NULL, *p = NULL, *end = NULL; + unsigned int timeout = context_timeout; + unsigned int buf_size = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n", +- lifetime_rec, acceptor->length, acceptor->value); ++ printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n", ++ tid, lifetime_rec, acceptor->length, acceptor->value); + buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) + + sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length + + sizeof(context_token->length) + context_token->length + +@@ -177,7 +178,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + return; + out_err: + free(buf); +- printerr(1, "Failed to write downcall!\n"); ++ printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid); + return; + } + +@@ -231,7 +232,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen, + switch (sa->sa_family) { + case AF_INET: + if (s4->sin_port != 0) { +- printerr(2, "DEBUG: port already set to %d\n", ++ printerr(4, "DEBUG: port already set to %d\n", + ntohs(s4->sin_port)); + return 1; + } +@@ -239,7 +240,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen, + #ifdef IPV6_SUPPORTED + case AF_INET6: + if (s6->sin6_port != 0) { +- printerr(2, "DEBUG: port already set to %d\n", ++ printerr(4, "DEBUG: port already set to %d\n", + ntohs(s6->sin6_port)); + return 1; + } +@@ -544,7 +545,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, + uid, tgtname); + + do { +- gssd_refresh_krb5_machine_credential(clp->servername, NULL, ++ gssd_refresh_krb5_machine_credential(clp->servername, + service, srchost); + /* + * Get a list of credential cache names and try each +@@ -726,7 +727,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info) + printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); + + process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL); +- free(info); ++ free_upcall_info(info); + } + + void +@@ -743,8 +744,10 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + char *enctypes = NULL; + char *upcall_str; + char *pbuf = info->lbuf; ++ pthread_t tid = pthread_self(); + +- printerr(2, "%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath); ++ printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ info->lbuf, clp->relpath); + + upcall_str = strdup(info->lbuf); + if (upcall_str == NULL) { +@@ -826,6 +829,6 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + out: + free(upcall_str); + out_nomem: +- free(info); ++ free_upcall_info(info); + return; + } +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 26e51edf..d675c3a4 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -126,9 +126,28 @@ + #include "gss_util.h" + #include "krb5_util.h" + ++/* ++ * List of principals from our keytab that we ++ * will try to use to obtain credentials ++ * (known as a principal list entry (ple)) ++ */ ++struct gssd_k5_kt_princ { ++ struct gssd_k5_kt_princ *next; ++ // Only protect against deletion, not modification ++ int refcount; ++ // Only set during creation in new_ple() ++ krb5_principal princ; ++ char *realm; ++ // Modified during usage by gssd_get_single_krb5_cred() ++ char *ccname; ++ krb5_timestamp endtime; ++}; ++ ++ + /* Global list of principals/cache file names for machine credentials */ +-struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; +-pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER; ++static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; ++/* This mutex protects list modification & ple->ccname */ ++static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER; + + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES + int limit_to_legacy_enctypes = 0; +@@ -146,6 +165,18 @@ static int gssd_get_single_krb5_cred(krb5_context context, + static int query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm); + ++static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple) ++{ ++ if (--ple->refcount) ++ return; ++ ++ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm); ++ krb5_free_principal(context, ple->princ); ++ free(ple->ccname); ++ free(ple->realm); ++ free(ple); ++} ++ + /* + * Called from the scandir function to weed out potential krb5 + * credentials cache files +@@ -352,12 +383,15 @@ gssd_get_single_krb5_cred(krb5_context context, + * 300 because clock skew must be within 300sec for kerberos + */ + now += 300; ++ pthread_mutex_lock(&ple_lock); + if (ple->ccname && ple->endtime > now && !nocache) { + printerr(3, "INFO: Credentials in CC '%s' are good until %d\n", + ple->ccname, ple->endtime); + code = 0; ++ pthread_mutex_unlock(&ple_lock); + goto out; + } ++ pthread_mutex_unlock(&ple_lock); + + if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) { + printerr(0, "ERROR: Unable to get keytab name in " +@@ -410,6 +444,7 @@ gssd_get_single_krb5_cred(krb5_context context, + * Initialize cache file which we're going to be using + */ + ++ pthread_mutex_lock(&ple_lock); + if (use_memcache) + cache_type = "MEMORY"; + else +@@ -419,15 +454,18 @@ gssd_get_single_krb5_cred(krb5_context context, + ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, + GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); + ple->endtime = my_creds.times.endtime; +- if (ple->ccname != NULL) ++ 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; +- goto out; ++ 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", +@@ -465,6 +503,7 @@ gssd_get_single_krb5_cred(krb5_context context, + + /* + * Given a principal, find a matching ple structure ++ * Called with mutex held + */ + static struct gssd_k5_kt_princ * + find_ple_by_princ(krb5_context context, krb5_principal princ) +@@ -481,6 +520,7 @@ find_ple_by_princ(krb5_context context, krb5_principal princ) + + /* + * Create, initialize, and add a new ple structure to the global list ++ * Called with mutex held + */ + static struct gssd_k5_kt_princ * + new_ple(krb5_context context, krb5_principal princ) +@@ -532,6 +572,7 @@ new_ple(krb5_context context, krb5_principal princ) + p->next = ple; + } + ++ ple->refcount = 1; + return ple; + outerr: + if (ple) { +@@ -550,13 +591,14 @@ get_ple_by_princ(krb5_context context, krb5_principal princ) + { + struct gssd_k5_kt_princ *ple; + +- /* Need to serialize list if we ever become multi-threaded! */ +- + pthread_mutex_lock(&ple_lock); + ple = find_ple_by_princ(context, princ); + if (ple == NULL) { + ple = new_ple(context, princ); + } ++ if (ple != NULL) { ++ ple->refcount++; ++ } + pthread_mutex_unlock(&ple_lock); + + return ple; +@@ -721,6 +763,8 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + retval = ENOMEM; + k5_free_kt_entry(context, kte); + } else { ++ release_ple(context, ple); ++ ple = NULL; + retval = 0; + *found = 1; + } +@@ -796,12 +840,12 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + /* Compute the active directory machine name HOST$ */ + krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name", + notsetstr, &adhostoverride); +- if (strcmp(adhostoverride, notsetstr) != 0) { +- printerr (1, +- "AD host string overridden with \"%s\" from appdefaults\n", +- adhostoverride); +- /* No overflow: Windows cannot handle strings longer than 19 chars */ +- strcpy(myhostad, adhostoverride); ++ if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) { ++ printerr(1, ++ "AD host string overridden with \"%s\" from appdefaults\n", ++ adhostoverride); ++ /* No overflow: Windows cannot handle strings longer than 19 chars */ ++ strcpy(myhostad, adhostoverride); + } else { + strcpy(myhostad, myhostname); + for (i = 0; myhostad[i] != 0; ++i) { +@@ -928,7 +972,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + tried_upper = 1; + } + } else { +- printerr(3, "Success getting keytab entry for '%s'\n",spn); ++ printerr(2, "Success getting keytab entry for '%s'\n",spn); + retval = 0; + goto out; + } +@@ -1053,6 +1097,93 @@ err_cache: + return (*ret_princname && *ret_realm); + } + ++/* ++ * Obtain (or refresh if necessary) Kerberos machine credentials ++ * If a ple is passed in, it's reference will be released ++ */ ++static int ++gssd_refresh_krb5_machine_credential_internal(char *hostname, ++ struct gssd_k5_kt_princ *ple, ++ char *service, char *srchost) ++{ ++ krb5_error_code code = 0; ++ krb5_context context; ++ krb5_keytab kt = NULL;; ++ int retval = 0; ++ char *k5err = NULL; ++ const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; ++ ++ printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", ++ __func__, hostname, ple, service, srchost); ++ ++ /* ++ * If a specific service name was specified, use it. ++ * Otherwise, use the default list. ++ */ ++ if (service != NULL && strcmp(service, "*") != 0) { ++ svcnames[0] = service; ++ svcnames[1] = NULL; ++ } ++ if (hostname == NULL && ple == NULL) ++ return EINVAL; ++ ++ code = krb5_init_context(&context); ++ if (code) { ++ k5err = gssd_k5_err_msg(NULL, code); ++ printerr(0, "ERROR: %s: %s while initializing krb5 context\n", ++ __func__, k5err); ++ retval = code; ++ goto out; ++ } ++ ++ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", ++ __func__, k5err, keytabfile); ++ goto out_free_context; ++ } ++ ++ if (ple == NULL) { ++ krb5_keytab_entry kte; ++ ++ code = find_keytab_entry(context, kt, srchost, hostname, ++ &kte, svcnames); ++ if (code) { ++ printerr(0, "ERROR: %s: no usable keytab entry found " ++ "in keytab %s for connection with host %s\n", ++ __FUNCTION__, keytabfile, hostname); ++ retval = code; ++ goto out_free_kt; ++ } ++ ++ ple = get_ple_by_princ(context, kte.principal); ++ k5_free_kt_entry(context, &kte); ++ if (ple == NULL) { ++ char *pname; ++ if ((krb5_unparse_name(context, kte.principal, &pname))) { ++ pname = NULL; ++ } ++ printerr(0, "ERROR: %s: Could not locate or create " ++ "ple struct for principal %s for connection " ++ "with host %s\n", ++ __FUNCTION__, pname ? pname : "", ++ hostname); ++ if (pname) k5_free_unparsed_name(context, pname); ++ goto out_free_kt; ++ } ++ } ++ retval = gssd_get_single_krb5_cred(context, kt, ple, 0); ++out_free_kt: ++ krb5_kt_close(context, kt); ++out_free_context: ++ if (ple) ++ release_ple(context, ple); ++ krb5_free_context(context); ++out: ++ free(k5err); ++ return retval; ++} ++ + /*==========================*/ + /*=== External routines ===*/ + /*==========================*/ +@@ -1146,37 +1277,56 @@ gssd_get_krb5_machine_cred_list(char ***list) + goto out; + } + +- /* Need to serialize list if we ever become multi-threaded! */ +- ++ pthread_mutex_lock(&ple_lock); + for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { +- if (ple->ccname) { +- /* Make sure cred is up-to-date before returning it */ +- retval = gssd_refresh_krb5_machine_credential(NULL, ple, +- NULL, NULL); +- if (retval) +- continue; +- if (i + 1 > listsize) { +- listsize += listinc; +- l = (char **) +- realloc(l, listsize * sizeof(char *)); +- if (l == NULL) { +- retval = ENOMEM; +- goto out; +- } +- } +- if ((l[i++] = strdup(ple->ccname)) == NULL) { ++ if (!ple->ccname) ++ continue; ++ ++ /* Take advantage of the fact we only remove the ple ++ * from the list during shutdown. If it's modified ++ * concurrently at worst we'll just miss a new entry ++ * before the current ple ++ * ++ * gssd_refresh_krb5_machine_credential_internal() will ++ * release the ple refcount ++ */ ++ ple->refcount++; ++ pthread_mutex_unlock(&ple_lock); ++ /* Make sure cred is up-to-date before returning it */ ++ retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple, ++ NULL, NULL); ++ pthread_mutex_lock(&ple_lock); ++ if (gssd_k5_kt_princ_list == NULL) { ++ /* Looks like we did shutdown... abort */ ++ l[i] = NULL; ++ gssd_free_krb5_machine_cred_list(l); ++ retval = ENOMEM; ++ goto out_lock; ++ } ++ if (retval) ++ continue; ++ if (i + 1 > listsize) { ++ listsize += listinc; ++ l = (char **) ++ realloc(l, listsize * sizeof(char *)); ++ if (l == NULL) { + retval = ENOMEM; +- goto out; ++ goto out_lock; + } + } ++ if ((l[i++] = strdup(ple->ccname)) == NULL) { ++ retval = ENOMEM; ++ goto out_lock; ++ } + } + if (i > 0) { + l[i] = NULL; + *list = l; + retval = 0; +- goto out; + } else + free((void *)l); ++out_lock: ++ pthread_mutex_unlock(&ple_lock); + out: + return retval; + } +@@ -1201,7 +1351,7 @@ gssd_free_krb5_machine_cred_list(char **list) + * Called upon exit. Destroys machine credentials. + */ + void +-gssd_destroy_krb5_machine_creds(void) ++gssd_destroy_krb5_principals(int destroy_machine_creds) + { + krb5_context context; + krb5_error_code code = 0; +@@ -1213,33 +1363,38 @@ gssd_destroy_krb5_machine_creds(void) + if (code) { + k5err = gssd_k5_err_msg(NULL, code); + printerr(0, "ERROR: %s while initializing krb5\n", k5err); +- goto out; ++ free(k5err); ++ return; + } + +- for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { +- if (!ple->ccname) +- continue; +- if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "WARNING: %s while resolving credential " +- "cache '%s' for destruction\n", k5err, +- ple->ccname); +- krb5_free_string(context, k5err); +- k5err = NULL; +- continue; +- } ++ pthread_mutex_lock(&ple_lock); ++ while (gssd_k5_kt_princ_list) { ++ ple = gssd_k5_kt_princ_list; ++ gssd_k5_kt_princ_list = ple->next; + +- if ((code = krb5_cc_destroy(context, ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "WARNING: %s while destroying credential " +- "cache '%s'\n", k5err, ple->ccname); +- krb5_free_string(context, k5err); +- k5err = NULL; ++ if (destroy_machine_creds && ple->ccname) { ++ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "WARNING: %s while resolving credential " ++ "cache '%s' for destruction\n", k5err, ++ ple->ccname); ++ free(k5err); ++ k5err = NULL; ++ } ++ ++ if (!code && (code = krb5_cc_destroy(context, ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "WARNING: %s while destroying credential " ++ "cache '%s'\n", k5err, ple->ccname); ++ free(k5err); ++ k5err = NULL; ++ } + } ++ ++ release_ple(context, ple); + } ++ pthread_mutex_unlock(&ple_lock); + krb5_free_context(context); +- out: +- krb5_free_string(context, k5err); + } + + /* +@@ -1247,83 +1402,10 @@ gssd_destroy_krb5_machine_creds(void) + */ + int + gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, + char *service, char *srchost) + { +- krb5_error_code code = 0; +- krb5_context context; +- krb5_keytab kt = NULL;; +- int retval = 0; +- char *k5err = NULL; +- const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; +- +- printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", +- __func__, hostname, ple, service, srchost); +- +- /* +- * If a specific service name was specified, use it. +- * Otherwise, use the default list. +- */ +- if (service != NULL && strcmp(service, "*") != 0) { +- svcnames[0] = service; +- svcnames[1] = NULL; +- } +- if (hostname == NULL && ple == NULL) +- return EINVAL; +- +- code = krb5_init_context(&context); +- if (code) { +- k5err = gssd_k5_err_msg(NULL, code); +- printerr(0, "ERROR: %s: %s while initializing krb5 context\n", +- __func__, k5err); +- retval = code; +- goto out; +- } +- +- if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", +- __func__, k5err, keytabfile); +- goto out_free_context; +- } +- +- if (ple == NULL) { +- krb5_keytab_entry kte; +- +- code = find_keytab_entry(context, kt, srchost, hostname, +- &kte, svcnames); +- if (code) { +- printerr(0, "ERROR: %s: no usable keytab entry found " +- "in keytab %s for connection with host %s\n", +- __FUNCTION__, keytabfile, hostname); +- retval = code; +- goto out_free_kt; +- } +- +- ple = get_ple_by_princ(context, kte.principal); +- k5_free_kt_entry(context, &kte); +- if (ple == NULL) { +- char *pname; +- if ((krb5_unparse_name(context, kte.principal, &pname))) { +- pname = NULL; +- } +- printerr(0, "ERROR: %s: Could not locate or create " +- "ple struct for principal %s for connection " +- "with host %s\n", +- __FUNCTION__, pname ? pname : "", +- hostname); +- if (pname) k5_free_unparsed_name(context, pname); +- goto out_free_kt; +- } +- } +- retval = gssd_get_single_krb5_cred(context, kt, ple, 0); +-out_free_kt: +- krb5_kt_close(context, kt); +-out_free_context: +- krb5_free_context(context); +-out: +- krb5_free_string(context, k5err); +- return retval; ++ return gssd_refresh_krb5_machine_credential_internal(hostname, NULL, ++ service, srchost); + } + + /* +diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h +index b000b444..2415205a 100644 +--- a/utils/gssd/krb5_util.h ++++ b/utils/gssd/krb5_util.h +@@ -9,27 +9,13 @@ + #include "gss_oids.h" + #endif + +-/* +- * List of principals from our keytab that we +- * will try to use to obtain credentials +- * (known as a principal list entry (ple)) +- */ +-struct gssd_k5_kt_princ { +- struct gssd_k5_kt_princ *next; +- krb5_principal princ; +- char *ccname; +- char *realm; +- krb5_timestamp endtime; +-}; +- + + int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, + char *dirname); + int gssd_get_krb5_machine_cred_list(char ***list); + void gssd_free_krb5_machine_cred_list(char **list); +-void gssd_destroy_krb5_machine_creds(void); ++void gssd_destroy_krb5_principals(int destroy_machine_creds); + int gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, + char *service, char *srchost); + char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); + void gssd_k5_get_default_realm(char **def_realm); diff --git a/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch b/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch index 22ffbff..8e6d1b3 100644 --- a/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch +++ b/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch @@ -10,14 +10,3 @@ diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/too return (sends / sample_time) def display_iostats(self, sample_time, which): -diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c ---- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2018-09-06 14:09:08.000000000 -0400 -+++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2020-12-10 10:45:47.211671499 -0500 -@@ -682,6 +682,7 @@ process_krb5_upcall(struct clnt_info *cl - } else { - get_hostbased_client_buffer(gacceptor, mech, &acceptor); - gss_release_name(&min_stat, &gacceptor); -+ gss_release_oid(&min_stat, &mech); - } - - /* diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index ca91f8c..43d03f6 100644 --- a/SPECS/nfs-utils.spec +++ b/SPECS/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.3.3 -Release: 39%{?dist} +Release: 40%{?dist} Epoch: 1 # group all 32bit related archs @@ -70,6 +70,7 @@ Patch032: nfs-utils-2.3.3-nfsiostat-key-error.patch Patch033: nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch Patch034: nfs-utils-2.3.3-systemd-exportfs-nofail.patch Patch035: nfs-utils-2.3.3-exports-manpage-outdated.patch +Patch036: nfs-utils-2.3.3-gssd-multithread-updates.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -353,7 +354,10 @@ fi %{_libdir}/libnfsidmap.so %changelog -* Fri Dec 11 2020 08:54:06 AM EST +* Mon Dec 14 2020 Steve Dickson 2.3.3-40 +- gssd: upstream multithreaded updates (bz 1906792) + +* Fri Dec 11 2020 Steve Dickson 2.3.3-39 - systemd: Ingnore export failures in nfs-server.serivce unit (bz 1894873) - exports.man: Remove some outdated verbiage (bz 1769688)