From 4cb8ad6d5bcaf4c7d3a3bc0413cb1ea72cd898d7 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 06 2021 11:59:00 +0000 Subject: import nfs-utils-2.3.3-46.el8 --- diff --git a/SOURCES/nfs-utils-2.3.3-exportfs-root.patch b/SOURCES/nfs-utils-2.3.3-exportfs-root.patch new file mode 100644 index 0000000..4aa4bc1 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-exportfs-root.patch @@ -0,0 +1,43 @@ +commit ac266e2edc4f40eef810d52c72657b645e4010db +Author: Ondrej Mosnacek +Date: Tue Apr 6 15:57:37 2021 -0400 + + exportfs: fix unexporting of '/' + + The code that has been added to strip trailing slashes from path in + unexportfs_parsed() forgot to account for the case of the root + directory, which is simply '/'. In that case it accesses path[-1] and + reduces the path to an empty string, which then fails to match any + export. + + Fix it by stopping the stripping when the path is just a single + character - it doesn't matter if it's a '/' or not, we want to keep it + either way in that case. + + Reproducer: + + exportfs localhost:/ + exportfs -u localhost:/ + + Without this patch, the unexport step fails with "exportfs: Could not + find 'localhost:/' to unexport." + + Fixes: a9a7728d8743 ("exportfs: Deal with path's trailing "/" in unexportfs_parsed()") + Link: https://bugzilla.redhat.com/show_bug.cgi?id=1941171 + + Signed-off-by: Ondrej Mosnacek + Signed-off-by: Steve Dickson + +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 262dd19a..25d757d8 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -383,7 +383,7 @@ unexportfs_parsed(char *hname, char *path, int verbose) + * so need to deal with it. + */ + size_t nlen = strlen(path); +- while (path[nlen - 1] == '/') ++ while ((nlen > 1) && (path[nlen - 1] == '/')) + nlen--; + + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { diff --git a/SOURCES/nfs-utils-2.3.3-gssd-debug-cleanup.patch b/SOURCES/nfs-utils-2.3.3-gssd-debug-cleanup.patch new file mode 100644 index 0000000..cac0c28 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-debug-cleanup.patch @@ -0,0 +1,290 @@ +diff -up nfs-utils-2.3.3/utils/gssd/err_util.c.orig nfs-utils-2.3.3/utils/gssd/err_util.c +--- nfs-utils-2.3.3/utils/gssd/err_util.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/gssd/err_util.c 2021-07-19 12:29:21.366829573 -0400 +@@ -70,3 +70,17 @@ int get_verbosity(void) + { + return verbosity; + } ++ ++char * ++sec2time(int value) ++{ ++ static char buf[BUFSIZ]; ++ int hr, min, sec; ++ ++ hr = (value / 3600); ++ min = (value - (3600*hr))/60; ++ sec = (value - (3600*hr) - (min*60)); ++ sprintf(buf, "%dh:%dm:%ds", hr, min, sec); ++ return(buf); ++} ++ +diff -up nfs-utils-2.3.3/utils/gssd/err_util.h.orig nfs-utils-2.3.3/utils/gssd/err_util.h +--- nfs-utils-2.3.3/utils/gssd/err_util.h.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/gssd/err_util.h 2021-07-19 12:29:21.367829599 -0400 +@@ -34,5 +34,6 @@ + void initerr(char *progname, int verbosity, int fg); + void printerr(int priority, char *format, ...); + int get_verbosity(void); ++char * sec2time(int); + + #endif /* _ERR_UTIL_H_ */ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 12:24:13.963644016 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 12:29:21.368829626 -0400 +@@ -396,7 +396,7 @@ gssd_free_client(struct clnt_info *clp) + if (refcnt > 0) + return; + +- printerr(3, "freeing client %s\n", clp->relpath); ++ printerr(4, "freeing client %s\n", clp->relpath); + + if (clp->krb5_fd >= 0) + close(clp->krb5_fd); +@@ -417,7 +417,7 @@ gssd_free_client(struct clnt_info *clp) + static void + gssd_destroy_client(struct clnt_info *clp) + { +- printerr(3, "destroying client %s\n", clp->relpath); ++ printerr(4, "destroying client %s\n", clp->relpath); + + if (clp->krb5_ev) { + event_del(clp->krb5_ev); +@@ -494,7 +494,7 @@ scan_active_thread_list(void) + * upcall_thread_info from the list and free it. + */ + if (tret == PTHREAD_CANCELED) +- printerr(3, "watchdog: thread id 0x%lx cancelled successfully\n", ++ printerr(2, "watchdog: thread id 0x%lx cancelled successfully\n", + info->tid); + saveprev = info->list.tqe_prev; + TAILQ_REMOVE(&active_thread_list, info, list); +@@ -783,7 +783,7 @@ gssd_scan(void) + { + struct dirent *d; + +- printerr(3, "doing a full rescan\n"); ++ printerr(4, "doing a full rescan\n"); + rewinddir(pipefs_dir); + + while ((d = readdir(pipefs_dir))) { +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 2021-07-19 12:24:13.964644043 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 12:29:21.368829626 -0400 +@@ -166,8 +166,9 @@ do_downcall(int k5_fd, uid_t uid, struct + unsigned int buf_size = 0; + pthread_t tid = pthread_self(); + +- printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n", +- tid, lifetime_rec, acceptor->length, acceptor->value); ++ if (get_verbosity() > 1) ++ printerr(2, "do_downcall(0x%lx): lifetime_rec=%s acceptor=%.*s\n", ++ tid, sec2time(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 + +@@ -193,7 +194,7 @@ do_downcall(int k5_fd, uid_t uid, struct + return; + out_err: + free(buf); +- printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid); ++ printerr(1, "do_downcall(0x%lx): Failed to write downcall!\n", tid); + return; + } + +@@ -204,8 +205,9 @@ do_error_downcall(int k5_fd, uid_t uid, + char *p = buf, *end = buf + 1024; + unsigned int timeout = 0; + int zero = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "doing error downcall\n"); ++ printerr(2, "do_error_downcall(0x%lx): uid %d err %d\n", tid, uid, err); + + if (WRITE_BYTES(&p, end, uid)) goto out_err; + if (WRITE_BYTES(&p, end, timeout)) goto out_err; +@@ -328,6 +330,7 @@ create_auth_rpc_client(struct clnt_info + struct timeval timeout; + struct sockaddr *addr = (struct sockaddr *) &clp->addr; + socklen_t salen; ++ pthread_t tid = pthread_self(); + + sec.qop = GSS_C_QOP_DEFAULT; + sec.svc = RPCSEC_GSS_SVC_NONE; +@@ -361,8 +364,8 @@ create_auth_rpc_client(struct clnt_info + + /* create an rpc connection to the nfs server */ + +- printerr(2, "creating %s client for server %s\n", clp->protocol, +- clp->servername); ++ printerr(3, "create_auth_rpc_client(0x%lx): creating %s client for server %s\n", ++ tid, clp->protocol, clp->servername); + + protocol = IPPROTO_TCP; + if ((strcmp(clp->protocol, "udp")) == 0) +@@ -405,7 +408,8 @@ create_auth_rpc_client(struct clnt_info + if (!tgtname) + tgtname = clp->servicename; + +- printerr(2, "creating context with server %s\n", tgtname); ++ printerr(3, "create_auth_rpc_client(0x%lx): creating context with server %s\n", ++ tid, tgtname); + auth = authgss_create_default(rpc_clnt, tgtname, &sec); + if (!auth) { + /* Our caller should print appropriate message */ +@@ -507,9 +511,10 @@ krb5_not_machine_creds(struct clnt_info + gss_cred_id_t gss_cred; + char **dname; + int err, resp = -1; ++ pthread_t tid = pthread_self(); + +- printerr(2, "krb5_not_machine_creds: uid %d tgtname %s\n", +- uid, tgtname); ++ printerr(2, "krb5_not_machine_creds(0x%lx): uid %d tgtname %s\n", ++ tid, uid, tgtname); + + *chg_err = change_identity(uid); + if (*chg_err) { +@@ -555,9 +560,10 @@ krb5_use_machine_creds(struct clnt_info + char **ccname; + int nocache = 0; + int success = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "krb5_use_machine_creds: uid %d tgtname %s\n", +- uid, tgtname); ++ printerr(2, "krb5_use_machine_creds(0x%lx): uid %d tgtname %s\n", ++ tid, uid, tgtname); + + do { + gssd_refresh_krb5_machine_credential(clp->servername, +@@ -874,6 +880,7 @@ start_upcall_thread(void (*func)(struct + pthread_t th; + struct upcall_thread_info *tinfo; + int ret; ++ pthread_t tid = pthread_self(); + + tinfo = alloc_upcall_thread_info(); + if (!tinfo) +@@ -896,6 +903,9 @@ start_upcall_thread(void (*func)(struct + free(tinfo); + return ret; + } ++ printerr(2, "start_upcall_thread(0x%lx): created thread id 0x%lx\n", ++ tid, th); ++ + tinfo->tid = th; + pthread_mutex_lock(&active_thread_list_lock); + clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout); +@@ -958,7 +968,7 @@ handle_gssd_upcall(struct clnt_info *clp + } + lbuf[lbuflen-1] = 0; + +- printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ printerr(2, "\n%s(0x%lx): '%s' (%s)\n", __func__, tid, + lbuf, clp->relpath); + + for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) { +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-07-19 12:24:13.951643697 -0400 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-07-19 12:36:27.746223992 -0400 +@@ -375,6 +375,7 @@ gssd_get_single_krb5_cred(krb5_context c + char *cache_type; + char *pname = NULL; + char *k5err = NULL; ++ pthread_t tid = pthread_self(); + + memset(&my_creds, 0, sizeof(my_creds)); + +@@ -385,8 +386,8 @@ gssd_get_single_krb5_cred(krb5_context c + 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); ++ printerr(3, "%s(0x%lx): Credentials in CC '%s' are good until %s", ++ __func__, tid, ple->ccname, ctime((time_t *)&ple->endtime)); + code = 0; + pthread_mutex_unlock(&ple_lock); + goto out; +@@ -486,7 +487,8 @@ gssd_get_single_krb5_cred(krb5_context c + } + + code = 0; +- printerr(2, "%s: principal '%s' ccache:'%s'\n", __func__, pname, cc_name); ++ 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) +@@ -615,6 +617,7 @@ get_full_hostname(const char *inhost, ch + struct addrinfo hints; + int retval; + char *c; ++ pthread_t tid = pthread_self(); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; +@@ -624,8 +627,8 @@ get_full_hostname(const char *inhost, ch + /* Get full target hostname */ + retval = getaddrinfo(inhost, NULL, &hints, &addrs); + if (retval) { +- printerr(1, "%s while getting full hostname for '%s'\n", +- gai_strerror(retval), inhost); ++ printerr(1, "%s(0x%lx): getaddrinfo(%s) failed: %s\n", ++ __func__, tid, inhost, gai_strerror(retval)); + goto out; + } + strncpy(outhost, addrs->ai_canonname, outhostlen); +@@ -633,7 +636,10 @@ get_full_hostname(const char *inhost, ch + for (c = outhost; *c != '\0'; c++) + *c = tolower(*c); + +- printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost); ++ if (get_verbosity() && strcmp(inhost, outhost)) ++ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost'%s'\n", ++ inhost, outhost); ++ + retval = 0; + out: + return retval; +@@ -819,6 +825,7 @@ find_keytab_entry(krb5_context context, + krb5_principal princ; + const char *notsetstr = "not set"; + char *adhostoverride = NULL; ++ pthread_t tid = pthread_self(); + + + /* Get full target hostname */ +@@ -972,7 +979,7 @@ find_keytab_entry(krb5_context context, + tried_upper = 1; + } + } else { +- printerr(2, "Success getting keytab entry for '%s'\n",spn); ++ printerr(2, "find_keytab_entry(0x%lx): Success getting keytab entry for '%s'\n",tid, spn); + retval = 0; + goto out; + } +@@ -1113,9 +1120,6 @@ gssd_refresh_krb5_machine_credential_int + 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. +@@ -1124,9 +1128,10 @@ gssd_refresh_krb5_machine_credential_int + svcnames[0] = service; + svcnames[1] = NULL; + } +- if (hostname == NULL && ple == NULL) ++ if (hostname == NULL && ple == NULL) { ++ printerr(0, "ERROR: %s: Invalid args\n", __func__); + return EINVAL; +- ++ } + code = krb5_init_context(&context); + if (code) { + k5err = gssd_k5_err_msg(NULL, code); diff --git a/SOURCES/nfs-utils-2.3.3-gssd-failed-thread.patch b/SOURCES/nfs-utils-2.3.3-gssd-failed-thread.patch new file mode 100644 index 0000000..53c68e5 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-failed-thread.patch @@ -0,0 +1,402 @@ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 09:39:04.273895536 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 09:40:13.942751214 -0400 +@@ -364,7 +364,7 @@ out: + /* Actually frees clp and fields that might be used from other + * threads if was last reference. + */ +-static void ++void + gssd_free_client(struct clnt_info *clp) + { + int refcnt; +@@ -416,55 +416,6 @@ gssd_destroy_client(struct clnt_info *cl + + static void gssd_scan(void); + +-static int +-start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info) +-{ +- pthread_attr_t attr; +- pthread_t th; +- int ret; +- +- ret = pthread_attr_init(&attr); +- if (ret != 0) { +- printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", +- ret, strerror(errno)); +- return ret; +- } +- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); +- if (ret != 0) { +- printerr(0, "ERROR: failed to create pthread attr: ret %d: " +- "%s\n", ret, strerror(errno)); +- return ret; +- } +- +- ret = pthread_create(&th, &attr, (void *)func, (void *)info); +- if (ret != 0) +- printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", +- ret, strerror(errno)); +- return ret; +-} +- +-static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp) +-{ +- struct clnt_upcall_info *info; +- +- 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. +@@ -473,44 +424,16 @@ static void + gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) + { + struct clnt_info *clp = data; +- struct clnt_upcall_info *info; +- +- info = alloc_upcall_info(clp); +- if (info == NULL) +- return; +- +- 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_upcall_info(info); +- return; +- } +- info->lbuf[info->lbuflen-1] = 0; + +- if (start_upcall_thread(handle_gssd_upcall, info)) +- free_upcall_info(info); ++ handle_gssd_upcall(clp); + } + + static void + gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) + { + struct clnt_info *clp = data; +- struct clnt_upcall_info *info; +- +- info = alloc_upcall_info(clp); +- if (info == NULL) +- return; +- +- if (read(clp->krb5_fd, &info->uid, +- 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_upcall_info(info); +- return; +- } + +- if (start_upcall_thread(handle_krb5_upcall, info)) +- free_upcall_info(info); ++ handle_krb5_upcall(clp); + } + + static struct clnt_info * +diff -up nfs-utils-2.3.3/utils/gssd/gssd.h.orig nfs-utils-2.3.3/utils/gssd/gssd.h +--- nfs-utils-2.3.3/utils/gssd/gssd.h.orig 2021-07-19 09:39:04.269895430 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.h 2021-07-19 09:40:13.943751240 -0400 +@@ -84,14 +84,17 @@ struct clnt_info { + + struct clnt_upcall_info { + struct clnt_info *clp; +- char lbuf[RPC_CHAN_BUF_SIZE]; +- int lbuflen; + uid_t uid; ++ int fd; ++ char *srchost; ++ char *target; ++ char *service; + }; + +-void handle_krb5_upcall(struct clnt_upcall_info *clp); +-void handle_gssd_upcall(struct clnt_upcall_info *clp); ++void handle_krb5_upcall(struct clnt_info *clp); ++void handle_gssd_upcall(struct clnt_info *clp); + void free_upcall_info(struct clnt_upcall_info *info); ++void gssd_free_client(struct clnt_info *clp); + + + #endif /* _RPC_GSSD_H_ */ +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 2021-07-19 09:39:04.269895430 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 09:40:13.944751267 -0400 +@@ -80,6 +80,8 @@ + #include "nfslib.h" + #include "gss_names.h" + ++extern pthread_mutex_t clp_lock; ++ + /* Encryption types supported by the kernel rpcsec_gss code */ + int num_krb5_enctypes = 0; + krb5_enctype *krb5_enctypes = NULL; +@@ -719,22 +721,133 @@ out_return_error: + goto out; + } + +-void +-handle_krb5_upcall(struct clnt_upcall_info *info) +-{ +- struct clnt_info *clp = info->clp; ++static struct clnt_upcall_info * ++alloc_upcall_info(struct clnt_info *clp, uid_t uid, int fd, char *srchost, ++ char *target, char *service) ++{ ++ struct clnt_upcall_info *info; ++ ++ info = malloc(sizeof(struct clnt_upcall_info)); ++ if (info == NULL) ++ return NULL; ++ ++ memset(info, 0, sizeof(*info)); ++ pthread_mutex_lock(&clp_lock); ++ clp->refcount++; ++ pthread_mutex_unlock(&clp_lock); ++ info->clp = clp; ++ info->uid = uid; ++ info->fd = fd; ++ if (srchost) { ++ info->srchost = strdup(srchost); ++ if (info->srchost == NULL) ++ goto out_info; ++ } ++ if (target) { ++ info->target = strdup(target); ++ if (info->target == NULL) ++ goto out_srchost; ++ } ++ if (service) { ++ info->service = strdup(service); ++ if (info->service == NULL) ++ goto out_target; ++ } ++ ++out: ++ return info; + +- printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); ++out_target: ++ if (info->target) ++ free(info->target); ++out_srchost: ++ if (info->srchost) ++ free(info->srchost); ++out_info: ++ free(info); ++ info = NULL; ++ goto out; ++} ++ ++void free_upcall_info(struct clnt_upcall_info *info) ++{ ++ gssd_free_client(info->clp); ++ if (info->service) ++ free(info->service); ++ if (info->target) ++ free(info->target); ++ if (info->srchost) ++ free(info->srchost); ++ free(info); ++} + +- process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL); ++static void ++gssd_work_thread_fn(struct clnt_upcall_info *info) ++{ ++ process_krb5_upcall(info->clp, info->uid, info->fd, info->srchost, info->target, info->service); + free_upcall_info(info); + } + ++static int ++start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info) ++{ ++ pthread_attr_t attr; ++ pthread_t th; ++ int ret; ++ ++ ret = pthread_attr_init(&attr); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++ } ++ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to create pthread attr: ret %d: " ++ "%s\n", ret, strerror(errno)); ++ return ret; ++ } ++ ++ ret = pthread_create(&th, &attr, (void *)func, (void *)info); ++ if (ret != 0) ++ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++} ++ + void +-handle_gssd_upcall(struct clnt_upcall_info *info) ++handle_krb5_upcall(struct clnt_info *clp) + { +- struct clnt_info *clp = info->clp; + uid_t uid; ++ struct clnt_upcall_info *info; ++ int err; ++ ++ if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { ++ printerr(0, "WARNING: failed reading uid from krb5 " ++ "upcall pipe: %s\n", strerror(errno)); ++ return; ++ } ++ printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath); ++ ++ info = alloc_upcall_info(clp, uid, clp->krb5_fd, NULL, NULL, NULL); ++ if (info == NULL) { ++ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__); ++ do_error_downcall(clp->krb5_fd, uid, -EACCES); ++ return; ++ } ++ err = start_upcall_thread(gssd_work_thread_fn, info); ++ if (err != 0) { ++ do_error_downcall(clp->krb5_fd, uid, -EACCES); ++ free_upcall_info(info); ++ } ++} ++ ++void ++handle_gssd_upcall(struct clnt_info *clp) ++{ ++ uid_t uid; ++ char lbuf[RPC_CHAN_BUF_SIZE]; ++ int lbuflen = 0; + char *p; + char *mech = NULL; + char *uidstr = NULL; +@@ -742,20 +855,22 @@ handle_gssd_upcall(struct clnt_upcall_in + char *service = NULL; + char *srchost = NULL; + char *enctypes = NULL; +- char *upcall_str; +- char *pbuf = info->lbuf; + pthread_t tid = pthread_self(); ++ struct clnt_upcall_info *info; ++ int err; + +- printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, +- info->lbuf, clp->relpath); +- +- upcall_str = strdup(info->lbuf); +- if (upcall_str == NULL) { +- printerr(0, "ERROR: malloc failure\n"); +- goto out_nomem; ++ lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf)); ++ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed reading request\n"); ++ return; + } ++ lbuf[lbuflen-1] = 0; ++ ++ printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ lbuf, clp->relpath); + +- while ((p = strsep(&pbuf, " "))) { ++ for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) { + if (!strncmp(p, "mech=", strlen("mech="))) + mech = p + strlen("mech="); + else if (!strncmp(p, "uid=", strlen("uid="))) +@@ -773,8 +888,8 @@ handle_gssd_upcall(struct clnt_upcall_in + if (!mech || strlen(mech) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find gss mechanism name " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + if (uidstr) { +@@ -786,21 +901,21 @@ handle_gssd_upcall(struct clnt_upcall_in + if (!uidstr) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find uid " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + if (enctypes && parse_enctypes(enctypes) != 0) { + printerr(0, "WARNING: handle_gssd_upcall: " + "parsing encryption types failed: errno %d\n", errno); +- goto out; ++ return; + } + + if (target && strlen(target) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse target name " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + /* +@@ -814,21 +929,26 @@ handle_gssd_upcall(struct clnt_upcall_in + if (service && strlen(service) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse service type " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + +- if (strcmp(mech, "krb5") == 0 && clp->servername) +- process_krb5_upcall(clp, uid, clp->gssd_fd, srchost, target, service); +- else { ++ if (strcmp(mech, "krb5") == 0 && clp->servername) { ++ info = alloc_upcall_info(clp, uid, clp->gssd_fd, srchost, target, service); ++ if (info == NULL) { ++ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__); ++ do_error_downcall(clp->gssd_fd, uid, -EACCES); ++ return; ++ } ++ err = start_upcall_thread(gssd_work_thread_fn, info); ++ if (err != 0) { ++ do_error_downcall(clp->gssd_fd, uid, -EACCES); ++ free_upcall_info(info); ++ } ++ } else { + if (clp->servername) + printerr(0, "WARNING: handle_gssd_upcall: " + "received unknown gss mech '%s'\n", mech); + do_error_downcall(clp->gssd_fd, uid, -EACCES); + } +-out: +- free(upcall_str); +-out_nomem: +- free_upcall_info(info); +- return; + } diff --git a/SOURCES/nfs-utils-2.3.3-gssd-k5identity.patch b/SOURCES/nfs-utils-2.3.3-gssd-k5identity.patch new file mode 100644 index 0000000..dd25da4 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-k5identity.patch @@ -0,0 +1,141 @@ +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2021-04-17 10:49:23.660184527 -0400 ++++ nfs-utils-2.3.3/nfs.conf 2021-04-17 11:14:41.482108562 -0400 +@@ -21,6 +21,7 @@ use-gss-proxy=1 + # keytab-file=/etc/krb5.keytab + # cred-cache-directory= + # preferred-realm= ++# set-home=1 + # + [lockd] + # port=0 +diff -up nfs-utils-2.3.3/systemd/nfs.conf.man.orig nfs-utils-2.3.3/systemd/nfs.conf.man +--- nfs-utils-2.3.3/systemd/nfs.conf.man.orig 2021-04-17 10:49:23.696185472 -0400 ++++ nfs-utils-2.3.3/systemd/nfs.conf.man 2021-04-17 11:14:41.483108588 -0400 +@@ -222,7 +222,8 @@ Recognized values: + .BR rpc-timeout , + .BR keytab-file , + .BR cred-cache-directory , +-.BR preferred-realm . ++.BR preferred-realm , ++.BR set-home . + + See + .BR rpc.gssd (8) +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-04-17 10:49:23.684185157 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-04-17 11:14:41.483108588 -0400 +@@ -87,6 +87,8 @@ unsigned int context_timeout = 0; + unsigned int rpc_timeout = 5; + char *preferred_realm = NULL; + char *ccachedir = NULL; ++/* set $HOME to "/" by default */ ++static bool set_home = true; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; + static bool use_gssproxy = false; +@@ -885,7 +887,7 @@ sig_die(int signal) + static void + usage(char *progname) + { +- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D]\n", ++ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H]\n", + progname); + exit(1); + } +@@ -926,6 +928,7 @@ read_gss_conf(void) + preferred_realm = s; + + use_gssproxy = conf_get_bool("gssd", "use-gss-proxy", use_gssproxy); ++ set_home = conf_get_bool("gssd", "set-home", set_home); + } + + int +@@ -946,7 +949,7 @@ main(int argc, char *argv[]) + verbosity = conf_get_num("gssd", "verbosity", verbosity); + rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity); + +- while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { ++ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:")) != -1) { + switch (opt) { + case 'f': + fg = 1; +@@ -994,6 +997,9 @@ main(int argc, char *argv[]) + case 'D': + avoid_dns = false; + break; ++ case 'H': ++ set_home = false; ++ break; + default: + usage(argv[0]); + break; +@@ -1003,13 +1009,19 @@ main(int argc, char *argv[]) + /* + * Some krb5 routines try to scrape info out of files in the user's + * home directory. This can easily deadlock when that homedir is on a +- * kerberized NFS mount. By setting $HOME unconditionally to "/", we +- * prevent this behavior in routines that use $HOME in preference to +- * the results of getpw*. ++ * kerberized NFS mount. By setting $HOME to "/" by default, we prevent ++ * this behavior in routines that use $HOME in preference to the results ++ * of getpw*. ++ * ++ * Some users do not use Kerberized home dirs and need $HOME to remain ++ * unchanged. Those users can leave $HOME unchanged by setting set_home ++ * to false. + */ +- if (setenv("HOME", "/", 1)) { +- printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno)); +- exit(1); ++ if (set_home) { ++ if (setenv("HOME", "/", 1)) { ++ printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno)); ++ exit(1); ++ } + } + + if (use_gssproxy) { +diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man +--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-04-17 10:49:23.650184264 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-04-17 11:14:41.484108615 -0400 +@@ -8,7 +8,7 @@ + rpc.gssd \- RPCSEC_GSS daemon + .SH SYNOPSIS + .B rpc.gssd +-.RB [ \-DfMnlvr ] ++.RB [ \-DfMnlvrH ] + .RB [ \-k + .IR keytab ] + .RB [ \-p +@@ -297,6 +297,16 @@ The default timeout is set to 5 seconds. + If you get messages like "WARNING: can't create tcp rpc_clnt to server + %servername% for user with uid %uid%: RPC: Remote system error - + Connection timed out", you should consider an increase of this timeout. ++.TP ++.B -H ++Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity ++files versus trying to read /.k5identity for each user. ++ ++If ++.B \-H ++is not set, rpc.gssd will use the first match found in ++/var/kerberos/krb5/user/$EUID/client.keytab and will not use a principal based on ++host and/or service parameters listed in $HOME/.k5identity. + .SH CONFIGURATION FILE + Many of the options that can be set on the command line can also be + controlled through values set in the +@@ -354,6 +364,13 @@ Equivalent to + .B preferred-realm + Equivalent to + .BR -R . ++.TP ++.B set-home ++Setting to ++.B false ++is equivalent to providing the ++.B -H ++flag. + .P + In addtion, the following value is recognized from the + .B [general] diff --git a/SOURCES/nfs-utils-2.3.3-gssd-man-tflag.patch b/SOURCES/nfs-utils-2.3.3-gssd-man-tflag.patch new file mode 100644 index 0000000..d710faa --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-man-tflag.patch @@ -0,0 +1,17 @@ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man +--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-04-17 11:21:18.326543446 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-04-17 12:35:59.867574517 -0400 +@@ -347,11 +347,11 @@ Equivalent to + .TP + .B context-timeout + Equivalent to +-.BR -T . ++.BR -t . + .TP + .B rpc-timeout + Equivalent to +-.BR -t . ++.BR -T . + .TP + .B keytab-file + Equivalent to diff --git a/SOURCES/nfs-utils-2.3.3-gssd-mutex-refcnt.patch b/SOURCES/nfs-utils-2.3.3-gssd-mutex-refcnt.patch new file mode 100644 index 0000000..7764ff4 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-mutex-refcnt.patch @@ -0,0 +1,43 @@ +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-07-22 15:27:27.728680553 -0400 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-07-22 15:30:08.916979585 -0400 +@@ -165,18 +165,28 @@ static int gssd_get_single_krb5_cred(krb + 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) ++static void release_ple_locked(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); ++ 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); + } + ++static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple) ++{ ++ pthread_mutex_lock(&ple_lock); ++ release_ple_locked(context, ple); ++ pthread_mutex_unlock(&ple_lock); ++} ++ ++ + /* + * Called from the scandir function to weed out potential krb5 + * credentials cache files +@@ -1396,7 +1406,7 @@ gssd_destroy_krb5_principals(int destroy + } + } + +- release_ple(context, ple); ++ release_ple_locked(context, ple); + } + pthread_mutex_unlock(&ple_lock); + krb5_free_context(context); diff --git a/SOURCES/nfs-utils-2.3.3-gssd-timeout-thread.patch b/SOURCES/nfs-utils-2.3.3-gssd-timeout-thread.patch new file mode 100644 index 0000000..6b57377 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-gssd-timeout-thread.patch @@ -0,0 +1,625 @@ +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2021-07-19 09:45:40.441448059 -0400 ++++ nfs-utils-2.3.3/nfs.conf 2021-07-19 12:08:55.314182838 -0400 +@@ -22,6 +22,8 @@ use-gss-proxy=1 + # cred-cache-directory= + # preferred-realm= + # set-home=1 ++# upcall-timeout=30 ++# cancel-timed-out-upcalls=0 + # + [lockd] + # port=0 +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 09:45:40.448448246 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 12:08:55.315182865 -0400 +@@ -96,8 +96,29 @@ pthread_mutex_t clp_lock = PTHREAD_MUTEX + static bool signal_received = false; + static struct event_base *evbase = NULL; + ++int upcall_timeout = DEF_UPCALL_TIMEOUT; ++static bool cancel_timed_out_upcalls = false; ++ + TAILQ_HEAD(topdir_list_head, topdir) topdir_list; + ++/* ++ * active_thread_list: ++ * ++ * used to track upcalls for timeout purposes. ++ * ++ * protected by the active_thread_list_lock mutex. ++ * ++ * upcall_thread_info structures are added to the tail of the list ++ * by start_upcall_thread(), so entries closer to the head of the list ++ * will be closer to hitting the upcall timeout. ++ * ++ * upcall_thread_info structures are removed from the list upon a ++ * sucessful join of the upcall thread by the watchdog thread (via ++ * scan_active_thread_list(). ++ */ ++TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list; ++pthread_mutex_t active_thread_list_lock = PTHREAD_MUTEX_INITIALIZER; ++ + struct topdir { + TAILQ_ENTRY(topdir) list; + TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list; +@@ -436,6 +457,138 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short + handle_krb5_upcall(clp); + } + ++/* ++ * scan_active_thread_list: ++ * ++ * Walks the active_thread_list, trying to join as many upcall threads as ++ * possible. For threads that have terminated, the corresponding ++ * upcall_thread_info will be removed from the list and freed. Threads that ++ * are still busy and have exceeded the upcall_timeout will cause an error to ++ * be logged and may be canceled (depending on the value of ++ * cancel_timed_out_upcalls). ++ * ++ * Returns the number of seconds that the watchdog thread should wait before ++ * calling scan_active_thread_list() again. ++ */ ++static int ++scan_active_thread_list(void) ++{ ++ struct upcall_thread_info *info; ++ struct timespec now; ++ unsigned int sleeptime; ++ bool sleeptime_set = false; ++ int err; ++ void *tret, *saveprev; ++ ++ sleeptime = upcall_timeout; ++ pthread_mutex_lock(&active_thread_list_lock); ++ clock_gettime(CLOCK_MONOTONIC, &now); ++ TAILQ_FOREACH(info, &active_thread_list, list) { ++ err = pthread_tryjoin_np(info->tid, &tret); ++ switch (err) { ++ case 0: ++ /* ++ * The upcall thread has either completed successfully, or ++ * has been canceled _and_ has acted on the cancellation request ++ * (i.e. has hit a cancellation point). We can now remove the ++ * upcall_thread_info from the list and free it. ++ */ ++ if (tret == PTHREAD_CANCELED) ++ printerr(3, "watchdog: thread id 0x%lx cancelled successfully\n", ++ info->tid); ++ saveprev = info->list.tqe_prev; ++ TAILQ_REMOVE(&active_thread_list, info, list); ++ free(info); ++ info = saveprev; ++ break; ++ case EBUSY: ++ /* ++ * The upcall thread is still running. If the timeout has expired ++ * then we either cancel the thread, log an error, and do an error ++ * downcall to the kernel (cancel_timed_out_upcalls=true) or simply ++ * log an error (cancel_timed_out_upcalls=false). In either case, ++ * the error is logged only once. ++ */ ++ if (now.tv_sec >= info->timeout.tv_sec) { ++ if (cancel_timed_out_upcalls && !(info->flags & UPCALL_THREAD_CANCELED)) { ++ printerr(0, "watchdog: thread id 0x%lx timed out\n", ++ info->tid); ++ pthread_cancel(info->tid); ++ info->flags |= (UPCALL_THREAD_CANCELED|UPCALL_THREAD_WARNED); ++ do_error_downcall(info->fd, info->uid, -ETIMEDOUT); ++ } else { ++ if (!(info->flags & UPCALL_THREAD_WARNED)) { ++ printerr(0, "watchdog: thread id 0x%lx running for %ld seconds\n", ++ info->tid, ++ now.tv_sec - info->timeout.tv_sec + upcall_timeout); ++ info->flags |= UPCALL_THREAD_WARNED; ++ } ++ } ++ } else if (!sleeptime_set) { ++ /* ++ * The upcall thread is still running, but the timeout has not yet ++ * expired. Calculate the time remaining until the timeout will ++ * expire. This is the amount of time the watchdog thread will ++ * wait before running again. We only need to do this for the busy ++ * thread closest to the head of the list - entries appearing later ++ * in the list will time out later. ++ */ ++ sleeptime = info->timeout.tv_sec - now.tv_sec; ++ sleeptime_set = true; ++ } ++ break; ++ default: ++ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */ ++ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n", ++ info->tid, err, strerror(err)); ++ break; ++ } ++ } ++ pthread_mutex_unlock(&active_thread_list_lock); ++ ++ return sleeptime; ++} ++ ++static void * ++watchdog_thread_fn(void *UNUSED(arg)) ++{ ++ unsigned int sleeptime; ++ ++ for (;;) { ++ sleeptime = scan_active_thread_list(); ++ printerr(4, "watchdog: sleeping %u secs\n", sleeptime); ++ sleep(sleeptime); ++ } ++ return (void *)0; ++} ++ ++static int ++start_watchdog_thread(void) ++{ ++ pthread_attr_t attr; ++ pthread_t th; ++ int ret; ++ ++ ret = pthread_attr_init(&attr); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++ } ++ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to create pthread attr: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++ } ++ ret = pthread_create(&th, &attr, watchdog_thread_fn, NULL); ++ if (ret != 0) { ++ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", ++ ret, strerror(errno)); ++ } ++ return ret; ++} ++ + static struct clnt_info * + gssd_get_clnt(struct topdir *tdi, const char *name) + { +@@ -810,7 +963,7 @@ sig_die(int signal) + static void + usage(char *progname) + { +- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H]\n", ++ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H] [-U upcall timeout] [-C]\n", + progname); + exit(1); + } +@@ -831,6 +984,9 @@ read_gss_conf(void) + #endif + context_timeout = conf_get_num("gssd", "context-timeout", context_timeout); + rpc_timeout = conf_get_num("gssd", "rpc-timeout", rpc_timeout); ++ upcall_timeout = conf_get_num("gssd", "upcall-timeout", upcall_timeout); ++ cancel_timed_out_upcalls = conf_get_bool("gssd", "cancel-timed-out-upcalls", ++ cancel_timed_out_upcalls); + s = conf_get_str("gssd", "pipefs-directory"); + if (!s) + s = conf_get_str("general", "pipefs-directory"); +@@ -872,7 +1028,7 @@ main(int argc, char *argv[]) + verbosity = conf_get_num("gssd", "verbosity", verbosity); + rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity); + +- while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:")) != -1) { ++ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:U:C")) != -1) { + switch (opt) { + case 'f': + fg = 1; +@@ -923,6 +1079,12 @@ main(int argc, char *argv[]) + case 'H': + set_home = false; + break; ++ case 'U': ++ upcall_timeout = atoi(optarg); ++ break; ++ case 'C': ++ cancel_timed_out_upcalls = true; ++ break; + default: + usage(argv[0]); + break; +@@ -995,6 +1157,11 @@ main(int argc, char *argv[]) + else + progname = argv[0]; + ++ if (upcall_timeout > MAX_UPCALL_TIMEOUT) ++ upcall_timeout = MAX_UPCALL_TIMEOUT; ++ else if (upcall_timeout < MIN_UPCALL_TIMEOUT) ++ upcall_timeout = MIN_UPCALL_TIMEOUT; ++ + initerr(progname, verbosity, fg); + #ifdef HAVE_LIBTIRPC_SET_DEBUG + /* +@@ -1045,6 +1212,14 @@ main(int argc, char *argv[]) + gssd_inotify_cb, NULL); + event_add(inotify_ev, NULL); + ++ TAILQ_INIT(&active_thread_list); ++ ++ rc = start_watchdog_thread(); ++ if (rc != 0) { ++ printerr(0, "ERROR: failed to start watchdog thread: %d\n", rc); ++ exit(EXIT_FAILURE); ++ } ++ + TAILQ_INIT(&topdir_list); + gssd_scan(); + daemon_ready(); +diff -up nfs-utils-2.3.3/utils/gssd/gssd.h.orig nfs-utils-2.3.3/utils/gssd/gssd.h +--- nfs-utils-2.3.3/utils/gssd/gssd.h.orig 2021-07-19 09:45:40.449448272 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.h 2021-07-19 12:08:55.315182865 -0400 +@@ -50,6 +50,12 @@ + #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" + #define GSSD_SERVICE_NAME "nfs" + #define RPC_CHAN_BUF_SIZE 32768 ++ ++/* timeouts are in seconds */ ++#define MIN_UPCALL_TIMEOUT 5 ++#define DEF_UPCALL_TIMEOUT 30 ++#define MAX_UPCALL_TIMEOUT 600 ++ + /* + * The gss mechanisms that we can handle + */ +@@ -91,10 +97,22 @@ struct clnt_upcall_info { + char *service; + }; + ++struct upcall_thread_info { ++ TAILQ_ENTRY(upcall_thread_info) list; ++ pthread_t tid; ++ struct timespec timeout; ++ uid_t uid; ++ int fd; ++ unsigned short flags; ++#define UPCALL_THREAD_CANCELED 0x0001 ++#define UPCALL_THREAD_WARNED 0x0002 ++}; ++ + void handle_krb5_upcall(struct clnt_info *clp); + void handle_gssd_upcall(struct clnt_info *clp); + void free_upcall_info(struct clnt_upcall_info *info); + void gssd_free_client(struct clnt_info *clp); ++int do_error_downcall(int k5_fd, uid_t uid, int err); + + + #endif /* _RPC_GSSD_H_ */ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man +--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-07-19 09:45:40.443448112 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-07-19 12:08:55.315182865 -0400 +@@ -8,7 +8,7 @@ + rpc.gssd \- RPCSEC_GSS daemon + .SH SYNOPSIS + .B rpc.gssd +-.RB [ \-DfMnlvrH ] ++.RB [ \-DfMnlvrHC ] + .RB [ \-k + .IR keytab ] + .RB [ \-p +@@ -17,6 +17,10 @@ rpc.gssd \- RPCSEC_GSS daemon + .IR ccachedir ] + .RB [ \-t + .IR timeout ] ++.RB [ \-T ++.IR timeout ] ++.RB [ \-U ++.IR timeout ] + .RB [ \-R + .IR realm ] + .SH INTRODUCTION +@@ -290,7 +294,7 @@ seconds, which allows changing Kerberos + The default is no explicit timeout, which means the kernel context will live + the lifetime of the Kerberos service ticket used in its creation. + .TP +-.B -T timeout ++.BI "-T " timeout + Timeout, in seconds, to create an RPC connection with a server while + establishing an authenticated gss context for a user. + The default timeout is set to 5 seconds. +@@ -298,6 +302,18 @@ If you get messages like "WARNING: can't + %servername% for user with uid %uid%: RPC: Remote system error - + Connection timed out", you should consider an increase of this timeout. + .TP ++.BI "-U " timeout ++Timeout, in seconds, for upcall threads. Threads executing longer than ++.I timeout ++seconds will cause an error message to be logged. The default ++.I timeout ++is 30 seconds. The minimum is 5 seconds. The maximum is 600 seconds. ++.TP ++.B -C ++In addition to logging an error message for threads that have timed out, ++the thread will be canceled and an error of -ETIMEDOUT will be reported ++to the kernel. ++.TP + .B -H + Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity + files versus trying to read /.k5identity for each user. +@@ -365,6 +381,17 @@ Equivalent to + Equivalent to + .BR -R . + .TP ++.B upcall-timeout ++Equivalent to ++.BR -U . ++.TP ++.B cancel-timed-out-upcalls ++Setting to ++.B true ++is equivalent to providing the ++.B -C ++flag. ++.TP + .B set-home + Setting to + .B false +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 2021-07-19 09:45:40.449448272 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 12:08:55.316182891 -0400 +@@ -81,11 +81,24 @@ + #include "gss_names.h" + + extern pthread_mutex_t clp_lock; ++extern pthread_mutex_t active_thread_list_lock; ++extern int upcall_timeout; ++extern TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list; + + /* Encryption types supported by the kernel rpcsec_gss code */ + int num_krb5_enctypes = 0; + krb5_enctype *krb5_enctypes = NULL; + ++/* Args for the cleanup_handler() */ ++struct cleanup_args { ++ OM_uint32 *min_stat; ++ gss_buffer_t acceptor; ++ gss_buffer_t token; ++ struct authgss_private_data *pd; ++ AUTH **auth; ++ CLIENT **rpc_clnt; ++}; ++ + /* + * Parse the supported encryption type information + */ +@@ -184,7 +197,7 @@ out_err: + return; + } + +-static int ++int + do_error_downcall(int k5_fd, uid_t uid, int err) + { + char buf[1024]; +@@ -604,27 +617,66 @@ out: + } + + /* ++ * cleanup_handler: ++ * ++ * Free any resources allocated by process_krb5_upcall(). ++ * ++ * Runs upon normal termination of process_krb5_upcall as well as if the ++ * thread is canceled. ++ */ ++static void ++cleanup_handler(void *arg) ++{ ++ struct cleanup_args *args = (struct cleanup_args *)arg; ++ ++ gss_release_buffer(args->min_stat, args->acceptor); ++ if (args->token->value) ++ free(args->token->value); ++#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA ++ if (args->pd->pd_ctx_hndl.length != 0 || args->pd->pd_ctx != 0) ++ authgss_free_private_data(args->pd); ++#endif ++ if (*args->auth) ++ AUTH_DESTROY(*args->auth); ++ if (*args->rpc_clnt) ++ clnt_destroy(*args->rpc_clnt); ++} ++ ++/* ++ * process_krb5_upcall: ++ * + * this code uses the userland rpcsec gss library to create a krb5 + * context on behalf of the kernel ++ * ++ * This is the meat of the upcall thread. Note that cancelability is disabled ++ * and enabled at various points to ensure that any resources reserved by the ++ * lower level libraries are released safely. + */ + static void +-process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *srchost, +- char *tgtname, char *service) ++process_krb5_upcall(struct clnt_upcall_info *info) + { ++ struct clnt_info *clp = info->clp; ++ uid_t uid = info->uid; ++ int fd = info->fd; ++ char *srchost = info->srchost; ++ char *tgtname = info->target; ++ char *service = info->service; + CLIENT *rpc_clnt = NULL; + AUTH *auth = NULL; + struct authgss_private_data pd; + gss_buffer_desc token; +- int err, downcall_err = -EACCES; ++ int err, downcall_err; + OM_uint32 maj_stat, min_stat, lifetime_rec; + gss_name_t gacceptor = GSS_C_NO_NAME; + gss_OID mech; + gss_buffer_desc acceptor = {0}; ++ struct cleanup_args cleanup_args = {&min_stat, &acceptor, &token, &pd, &auth, &rpc_clnt}; + + token.length = 0; + token.value = NULL; + memset(&pd, 0, sizeof(struct authgss_private_data)); + ++ pthread_cleanup_push(cleanup_handler, &cleanup_args); + /* + * If "service" is specified, then the kernel is indicating that + * we must use machine credentials for this request. (Regardless +@@ -646,6 +698,8 @@ process_krb5_upcall(struct clnt_info *cl + * used for this case is not important. + * + */ ++ downcall_err = -EACCES; ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && + service == NULL)) { + +@@ -666,15 +720,21 @@ process_krb5_upcall(struct clnt_info *cl + goto out_return_error; + } + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (!authgss_get_private_data(auth, &pd)) { + printerr(1, "WARNING: Failed to obtain authentication " + "data for user with uid %d for server %s\n", + uid, clp->servername); + goto out_return_error; + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + + /* Grab the context lifetime and acceptor name out of the ctx. */ ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor, + &lifetime_rec, &mech, NULL, NULL, NULL); + +@@ -686,37 +746,35 @@ process_krb5_upcall(struct clnt_info *cl + get_hostbased_client_buffer(gacceptor, mech, &acceptor); + gss_release_name(&min_stat, &gacceptor); + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + + /* + * The serialization can mean turning pd.pd_ctx into a lucid context. If + * that happens then the pd.pd_ctx will be unusable, so we must never + * try to use it after this point. + */ ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) { + printerr(1, "WARNING: Failed to serialize krb5 context for " + "user with uid %d for server %s\n", + uid, clp->servername); + goto out_return_error; + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + + do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor); + + out: +- gss_release_buffer(&min_stat, &acceptor); +- if (token.value) +- free(token.value); +-#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA +- if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0) +- authgss_free_private_data(&pd); +-#endif +- if (auth) +- AUTH_DESTROY(auth); +- if (rpc_clnt) +- clnt_destroy(rpc_clnt); ++ pthread_cleanup_pop(1); + + return; + + out_return_error: ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); ++ + do_error_downcall(fd, uid, downcall_err); + goto out; + } +@@ -782,36 +840,69 @@ void free_upcall_info(struct clnt_upcall + } + + static void +-gssd_work_thread_fn(struct clnt_upcall_info *info) ++cleanup_clnt_upcall_info(void *arg) + { +- process_krb5_upcall(info->clp, info->uid, info->fd, info->srchost, info->target, info->service); ++ struct clnt_upcall_info *info = (struct clnt_upcall_info *)arg; ++ + free_upcall_info(info); + } + ++static void ++gssd_work_thread_fn(struct clnt_upcall_info *info) ++{ ++ pthread_cleanup_push(cleanup_clnt_upcall_info, info); ++ process_krb5_upcall(info); ++ pthread_cleanup_pop(1); ++} ++ ++static struct upcall_thread_info * ++alloc_upcall_thread_info(void) ++{ ++ struct upcall_thread_info *info; ++ ++ info = malloc(sizeof(struct upcall_thread_info)); ++ if (info == NULL) ++ return NULL; ++ memset(info, 0, sizeof(*info)); ++ return info; ++} ++ + static int +-start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info) ++start_upcall_thread(void (*func)(struct clnt_upcall_info *), struct clnt_upcall_info *info) + { + pthread_attr_t attr; + pthread_t th; ++ struct upcall_thread_info *tinfo; + int ret; + ++ tinfo = alloc_upcall_thread_info(); ++ if (!tinfo) ++ return -ENOMEM; ++ tinfo->fd = info->fd; ++ tinfo->uid = info->uid; ++ + ret = pthread_attr_init(&attr); + if (ret != 0) { + printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", + ret, strerror(errno)); +- return ret; +- } +- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); +- if (ret != 0) { +- printerr(0, "ERROR: failed to create pthread attr: ret %d: " +- "%s\n", ret, strerror(errno)); ++ free(tinfo); + return ret; + } + + ret = pthread_create(&th, &attr, (void *)func, (void *)info); +- if (ret != 0) ++ if (ret != 0) { + printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", + ret, strerror(errno)); ++ free(tinfo); ++ return ret; ++ } ++ tinfo->tid = th; ++ pthread_mutex_lock(&active_thread_list_lock); ++ clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout); ++ tinfo->timeout.tv_sec += upcall_timeout; ++ TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list); ++ pthread_mutex_unlock(&active_thread_list_lock); ++ + return ret; + } + diff --git a/SOURCES/nfs-utils-2.3.3-mount-sloppy.patch b/SOURCES/nfs-utils-2.3.3-mount-sloppy.patch new file mode 100644 index 0000000..e734c74 --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-mount-sloppy.patch @@ -0,0 +1,116 @@ +diff -up nfs-utils-2.3.3/utils/mount/nfs.man.save nfs-utils-2.3.3/utils/mount/nfs.man +--- nfs-utils-2.3.3/utils/mount/nfs.man.save 2021-07-28 14:42:20.977740892 -0400 ++++ nfs-utils-2.3.3/utils/mount/nfs.man 2021-07-28 14:42:01.133212815 -0400 +@@ -525,6 +525,13 @@ using the FS-Cache facility. See cachefi + and /Documentation/filesystems/caching + for detail on how to configure the FS-Cache facility. + Default value is nofsc. ++.TP 1.5i ++.B sloppy ++The ++.B sloppy ++option is an alternative to specifying ++.BR mount.nfs " -s " option. ++ + .SS "Options for NFS versions 2 and 3 only" + Use these options, along with the options in the above subsection, + for NFS versions 2 and 3 only. +diff -up nfs-utils-2.3.3/utils/mount/parse_opt.c.save nfs-utils-2.3.3/utils/mount/parse_opt.c +--- nfs-utils-2.3.3/utils/mount/parse_opt.c.save 2021-07-28 14:40:15.467400995 -0400 ++++ nfs-utils-2.3.3/utils/mount/parse_opt.c 2021-07-28 14:39:57.666927309 -0400 +@@ -178,6 +178,22 @@ static void options_tail_insert(struct m + options->count++; + } + ++static void options_head_insert(struct mount_options *options, ++ struct mount_option *option) ++{ ++ struct mount_option *ohead = options->head; ++ ++ option->prev = NULL; ++ option->next = ohead; ++ if (ohead) ++ ohead->prev = option; ++ else ++ options->tail = option; ++ options->head = option; ++ ++ options->count++; ++} ++ + static void options_delete(struct mount_options *options, + struct mount_option *option) + { +@@ -374,6 +390,23 @@ po_return_t po_join(struct mount_options + } + + /** ++ * po_insert - insert an option into a group of options ++ * @options: pointer to mount options ++ * @option: pointer to a C string containing the option to add ++ * ++ */ ++po_return_t po_insert(struct mount_options *options, char *str) ++{ ++ struct mount_option *option = option_create(str); ++ ++ if (option) { ++ options_head_insert(options, option); ++ return PO_SUCCEEDED; ++ } ++ return PO_FAILED; ++} ++ ++/** + * po_append - concatenate an option onto a group of options + * @options: pointer to mount options + * @option: pointer to a C string containing the option to add +diff -up nfs-utils-2.3.3/utils/mount/parse_opt.h.save nfs-utils-2.3.3/utils/mount/parse_opt.h +--- nfs-utils-2.3.3/utils/mount/parse_opt.h.save 2021-07-28 14:40:54.292434148 -0400 ++++ nfs-utils-2.3.3/utils/mount/parse_opt.h 2021-07-28 14:39:57.666927309 -0400 +@@ -43,6 +43,7 @@ void po_replace(struct mount_options * + struct mount_options *); + po_return_t po_join(struct mount_options *, char **); + ++po_return_t po_insert(struct mount_options *, char *); + po_return_t po_append(struct mount_options *, char *); + po_found_t po_contains(struct mount_options *, char *); + po_found_t po_contains_prefix(struct mount_options *options, +diff -up nfs-utils-2.3.3/utils/mount/stropts.c.save nfs-utils-2.3.3/utils/mount/stropts.c +--- nfs-utils-2.3.3/utils/mount/stropts.c.save 2021-07-28 14:41:14.842981010 -0400 ++++ nfs-utils-2.3.3/utils/mount/stropts.c 2021-07-28 14:42:01.134212842 -0400 +@@ -336,13 +336,21 @@ static int nfs_verify_lock_option(struct + return 1; + } + +-static int nfs_append_sloppy_option(struct mount_options *options) ++static int nfs_insert_sloppy_option(struct mount_options *options) + { +- if (!sloppy || linux_version_code() < MAKE_VERSION(2, 6, 27)) ++ if (linux_version_code() < MAKE_VERSION(2, 6, 27)) + return 1; + +- if (po_append(options, "sloppy") == PO_FAILED) +- return 0; ++ if (po_contains(options, "sloppy")) { ++ po_remove_all(options, "sloppy"); ++ sloppy++; ++ } ++ ++ if (sloppy) { ++ if (po_insert(options, "sloppy") == PO_FAILED) ++ return 0; ++ } ++ + return 1; + } + +@@ -424,7 +432,7 @@ static int nfs_validate_options(struct n + if (!nfs_set_version(mi)) + return 0; + +- if (!nfs_append_sloppy_option(mi->options)) ++ if (!nfs_insert_sloppy_option(mi->options)) + return 0; + + return 1; diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index 2d9af3b..345fa9f 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: 41%{?dist} +Release: 46%{?dist} Epoch: 1 # group all 32bit related archs @@ -73,6 +73,19 @@ Patch035: nfs-utils-2.3.3-exports-manpage-outdated.patch Patch036: nfs-utils-2.3.3-gssd-multithread-updates.patch Patch037: nfs-utils-2.3.3-mountd-pseudofs.patch +# +# RHEL 8.5 +# +Patch038: nfs-utils-2.3.3-gssd-k5identity.patch +Patch039: nfs-utils-2.3.3-gssd-man-tflag.patch +Patch040: nfs-utils-2.3.3-exportfs-root.patch +Patch041: nfs-utils-2.3.3-mount-sloppy.patch +Patch042: nfs-utils-2.3.3-gssd-failed-thread.patch +Patch043: nfs-utils-2.3.3-gssd-timeout-thread.patch +Patch044: nfs-utils-2.3.3-gssd-debug-cleanup.patch +Patch045: nfs-utils-2.3.3-gssd-mutex-refcnt.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 @@ -140,16 +153,8 @@ developing programs which use the libnfsidmap library. %description -The nfs-utils package provides a daemon for the kernel NFS server and -related tools, which provides a much higher level of performance than the -traditional Linux NFS server used by most users. - -This package also contains the showmount program. Showmount queries the -mount daemon on a remote host for information about the NFS (Network File -System) server on the remote host. For example, showmount can display the -clients which are mounted on that host. - -This package also contains the mount.nfs and umount.nfs program. +The nfs-utils package provides various utilities for use with NFS +clients and servers. %prep %autosetup -p1 @@ -355,6 +360,26 @@ fi %{_libdir}/libnfsidmap.so %changelog +* Wed Jul 28 2021 Steve Dickson 2.3.3-46 +- mount.nfs: Fix the sloppy option processing (bz 1967883) + +* Thu Jul 22 2021 Steve Dickson 2.3.3-45 +- gssd: use mutex to protect decrement of refcount (bz 1511706) + +* Mon Jul 19 2021 Steve Dickson 2.3.3-44 +- gssd: Deal with failed thread creation (bz 1981400) +- gssd: Add timeout for upcall threads (bz 1981403) +- gssd: Cleaned up debug messages (bz 1961056) +- spec: Updated description of the nfs-utils rpm (bz 1981419) + +* Sat Jul 10 2021 Steve Dickson 2.3.3-43 +- mount.nfs: insert 'sloppy' at beginning of the options (bz 1967883) + +* Mon May 10 2021 Steve Dickson 2.3.3-42 +- gssd: Add options to allow for the use of ~/.k5identity file (bz 1868087) +- man: Correct gssd(8) description of rpc-timeout and context-timeout (bz 1908232) +- exportfs: fix unexporting of '/' (bz 1944119) + * Wed Jan 20 2021 Steve Dickson 2.3.3-41 - mountd: never root squash on the pseudofs (bz 1804912)