d2edb4
diff -up nfs-utils-1.3.0/utils/gssd/gssd_proc.c.old nfs-utils-1.3.0/utils/gssd/gssd_proc.c
d2edb4
--- nfs-utils-1.3.0/utils/gssd/gssd_proc.c.old	2015-09-24 09:48:40.833593000 -0400
d2edb4
+++ nfs-utils-1.3.0/utils/gssd/gssd_proc.c	2015-09-24 09:50:58.747069000 -0400
d2edb4
@@ -1023,6 +1023,113 @@ change_identity(uid_t uid)
d2edb4
 	return 0;
d2edb4
 }
d2edb4
 
d2edb4
+AUTH *
d2edb4
+krb5_not_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
d2edb4
+			int *downcall_err, int *chg_err, CLIENT **rpc_clnt)
d2edb4
+{
d2edb4
+	AUTH		*auth = NULL;
d2edb4
+	gss_cred_id_t	gss_cred;
d2edb4
+	char		**dname;
d2edb4
+	int		err, resp = -1;
d2edb4
+
d2edb4
+	printerr(1, "krb5_not_machine_creds: uid %d tgtname %s\n", 
d2edb4
+		uid, tgtname);
d2edb4
+
d2edb4
+	*chg_err = change_identity(uid);
d2edb4
+	if (*chg_err) {
d2edb4
+		printerr(0, "WARNING: failed to change identity: %s",
d2edb4
+			strerror(*chg_err));
d2edb4
+		goto out;
d2edb4
+	}
d2edb4
+
d2edb4
+	/** Tell krb5 gss which credentials cache to use.
d2edb4
+	 * Try first to acquire credentials directly via GSSAPI
d2edb4
+	 */
d2edb4
+	err = gssd_acquire_user_cred(&gss_cred);
d2edb4
+	if (err == 0)
d2edb4
+		resp = create_auth_rpc_client(clp, tgtname, rpc_clnt,
d2edb4
+						&auth, uid,
d2edb4
+						AUTHTYPE_KRB5, gss_cred);
d2edb4
+
d2edb4
+	/** if create_auth_rplc_client fails try the traditional
d2edb4
+	 * method of trolling for credentials
d2edb4
+	 */
d2edb4
+	for (dname = ccachesearch; resp != 0 && *dname != NULL; dname++) {
d2edb4
+		err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername,
d2edb4
+						*dname);
d2edb4
+		if (err == -EKEYEXPIRED)
d2edb4
+			*downcall_err = -EKEYEXPIRED;
d2edb4
+		else if (err == 0)
d2edb4
+			resp = create_auth_rpc_client(clp, tgtname, rpc_clnt,
d2edb4
+						&auth, uid,AUTHTYPE_KRB5,
d2edb4
+						GSS_C_NO_CREDENTIAL);
d2edb4
+	}
d2edb4
+
d2edb4
+out:
d2edb4
+	return auth;
d2edb4
+}
d2edb4
+
d2edb4
+AUTH *
d2edb4
+krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
d2edb4
+		    char *service, CLIENT **rpc_clnt)
d2edb4
+{
d2edb4
+	AUTH	*auth = NULL;
d2edb4
+	char	**credlist = NULL;
d2edb4
+	char	**ccname;
d2edb4
+	int	nocache = 0;
d2edb4
+	int	success = 0;
d2edb4
+
d2edb4
+	printerr(1, "krb5_use_machine_creds: uid %d tgtname %s\n", 
d2edb4
+		uid, tgtname);
d2edb4
+
d2edb4
+	do {
d2edb4
+		gssd_refresh_krb5_machine_credential(clp->servername, NULL,
d2edb4
+						service);
d2edb4
+	/*
d2edb4
+	 * Get a list of credential cache names and try each
d2edb4
+	 * of them until one works or we've tried them all
d2edb4
+	 */
d2edb4
+		if (gssd_get_krb5_machine_cred_list(&credlist)) {
d2edb4
+			printerr(0, "ERROR: No credentials found "
d2edb4
+				"for connection to server %s\n",
d2edb4
+				clp->servername);
d2edb4
+			goto out;
d2edb4
+		}
d2edb4
+		for (ccname = credlist; ccname && *ccname; ccname++) {
d2edb4
+			gssd_setup_krb5_machine_gss_ccache(*ccname);
d2edb4
+			if ((create_auth_rpc_client(clp, tgtname, rpc_clnt,
d2edb4
+						&auth, uid,
d2edb4
+						AUTHTYPE_KRB5,
d2edb4
+						GSS_C_NO_CREDENTIAL)) == 0) {
d2edb4
+				/* Success! */
d2edb4
+				success++;
d2edb4
+				break;
d2edb4
+			}
d2edb4
+			printerr(2, "WARNING: Failed to create machine krb5"
d2edb4
+				"context with cred cache %s for server %s\n",
d2edb4
+				*ccname, clp->servername);
d2edb4
+		}
d2edb4
+		gssd_free_krb5_machine_cred_list(credlist);
d2edb4
+		if (!success) {
d2edb4
+			if(nocache == 0) {
d2edb4
+				nocache++;
d2edb4
+				printerr(2, "WARNING: Machine cache prematurely"					 "expired or corrupted trying to"
d2edb4
+					 "recreate cache for server %s\n",
d2edb4
+					clp->servername);
d2edb4
+			} else {
d2edb4
+				printerr(1, "WARNING: Failed to create machine"
d2edb4
+					 "krb5 context with any credentials"
d2edb4
+					 "cache for server %s\n",
d2edb4
+					clp->servername);
d2edb4
+				goto out;
d2edb4
+			}
d2edb4
+		}
d2edb4
+	} while(!success);
d2edb4
+
d2edb4
+out:
d2edb4
+	return auth;
d2edb4
+}
d2edb4
+
d2edb4
 /*
d2edb4
  * this code uses the userland rpcsec gss library to create a krb5
d2edb4
  * context on behalf of the kernel
d2edb4
@@ -1035,40 +1142,13 @@ process_krb5_upcall(struct clnt_info *cl
d2edb4
 	AUTH			*auth = NULL;
d2edb4
 	struct authgss_private_data pd;
d2edb4
 	gss_buffer_desc		token;
d2edb4
-	char			**credlist = NULL;
d2edb4
-	char			**ccname;
d2edb4
-	char			**dirname;
d2edb4
-	int			create_resp = -1;
d2edb4
 	int			err, downcall_err = -EACCES;
d2edb4
-	gss_cred_id_t		gss_cred;
d2edb4
 	OM_uint32		maj_stat, min_stat, lifetime_rec;
d2edb4
-	pid_t			pid;
d2edb4
+	pid_t			pid, childpid = -1;
d2edb4
 	gss_name_t		gacceptor = GSS_C_NO_NAME;
d2edb4
 	gss_OID			mech;
d2edb4
 	gss_buffer_desc		acceptor  = {0};
d2edb4
 
d2edb4
-	pid = fork();
d2edb4
-	switch(pid) {
d2edb4
-	case 0:
d2edb4
-		/* Child: fall through to rest of function */
d2edb4
-		break;
d2edb4
-	case -1:
d2edb4
-		/* fork() failed! */
d2edb4
-		printerr(0, "WARNING: unable to fork() to handle upcall: %s\n",
d2edb4
-				strerror(errno));
d2edb4
-		return;
d2edb4
-	default:
d2edb4
-		/* Parent: just wait on child to exit and return */
d2edb4
-		do {
d2edb4
-			pid = wait(&err;;
d2edb4
-		} while(pid == -1 && errno != -ECHILD);
d2edb4
-
d2edb4
-		if (WIFSIGNALED(err))
d2edb4
-			printerr(0, "WARNING: forked child was killed with signal %d\n",
d2edb4
-					WTERMSIG(err));
d2edb4
-		return;
d2edb4
-	}
d2edb4
-
d2edb4
 	printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
d2edb4
 
d2edb4
 	token.length = 0;
d2edb4
@@ -1101,76 +1181,48 @@ process_krb5_upcall(struct clnt_info *cl
d2edb4
 	if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
d2edb4
 				service == NULL)) {
d2edb4
 
d2edb4
-		err = change_identity(uid);
d2edb4
-		if (err) {
d2edb4
-			printerr(0, "WARNING: failed to change identity: %s",
d2edb4
-				 strerror(err));
d2edb4
-			goto out_return_error;
d2edb4
-		}
d2edb4
+		/* already running as uid 0 */
d2edb4
+		if (uid == 0)
d2edb4
+			goto no_fork;
d2edb4
+
d2edb4
+		pid = fork();
d2edb4
+		switch(pid) {
d2edb4
+		case 0:
d2edb4
+			/* Child: fall through to rest of function */
d2edb4
+			childpid = getpid();
d2edb4
+			unsetenv("KRB5CCNAME");
d2edb4
+			printerr(1, "CHILD forked pid %d \n", childpid);
d2edb4
+			break;
d2edb4
+		case -1:
d2edb4
+			/* fork() failed! */
d2edb4
+			printerr(0, "WARNING: unable to fork() to handle"
d2edb4
+				"upcall: %s\n", strerror(errno));
d2edb4
+			return;
d2edb4
+		default:
d2edb4
+			/* Parent: just wait on child to exit and return */
d2edb4
+			do {
d2edb4
+				pid = wait(&err;;
d2edb4
+			} while(pid == -1 && errno != -ECHILD);
d2edb4
 
d2edb4
-		/* Tell krb5 gss which credentials cache to use */
d2edb4
-		/* Try first to acquire credentials directly via GSSAPI */
d2edb4
-		err = gssd_acquire_user_cred(&gss_cred);
d2edb4
-		if (!err)
d2edb4
-			create_resp = create_auth_rpc_client(clp, tgtname, &rpc_clnt, &auth, uid,
d2edb4
-							     AUTHTYPE_KRB5, gss_cred);
d2edb4
-		/* if create_auth_rplc_client fails try the traditional method of
d2edb4
-		 * trolling for credentials */
d2edb4
-		for (dirname = ccachesearch; create_resp != 0 && *dirname != NULL; dirname++) {
d2edb4
-			err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname);
d2edb4
-			if (err == -EKEYEXPIRED)
d2edb4
-				downcall_err = -EKEYEXPIRED;
d2edb4
-			else if (!err)
d2edb4
-				create_resp = create_auth_rpc_client(clp, tgtname, &rpc_clnt, &auth, uid,
d2edb4
-							     AUTHTYPE_KRB5, GSS_C_NO_CREDENTIAL);
d2edb4
+			if (WIFSIGNALED(err))
d2edb4
+				printerr(0, "WARNING: forked child was killed"
d2edb4
+					 "with signal %d\n", WTERMSIG(err));
d2edb4
+			return;
d2edb4
 		}
d2edb4
+no_fork:
d2edb4
+
d2edb4
+		auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err,
d2edb4
+						&err, &rpc_clnt);
d2edb4
+		if (err)
d2edb4
+			goto out_return_error;
d2edb4
 	}
d2edb4
-	if (create_resp != 0) {
d2edb4
+	if (auth == NULL) {
d2edb4
 		if (uid == 0 && (root_uses_machine_creds == 1 ||
d2edb4
 				service != NULL)) {
d2edb4
-			int nocache = 0;
d2edb4
-			int success = 0;
d2edb4
-			do {
d2edb4
-				gssd_refresh_krb5_machine_credential(clp->servername,
d2edb4
-								     NULL, service);
d2edb4
-				/*
d2edb4
-				 * Get a list of credential cache names and try each
d2edb4
-				 * of them until one works or we've tried them all
d2edb4
-				 */
d2edb4
-				if (gssd_get_krb5_machine_cred_list(&credlist)) {
d2edb4
-					printerr(0, "ERROR: No credentials found "
d2edb4
-						 "for connection to server %s\n",
d2edb4
-						 clp->servername);
d2edb4
-					goto out_return_error;
d2edb4
-				}
d2edb4
-				for (ccname = credlist; ccname && *ccname; ccname++) {
d2edb4
-					gssd_setup_krb5_machine_gss_ccache(*ccname);
d2edb4
-					if ((create_auth_rpc_client(clp, tgtname, &rpc_clnt,
d2edb4
-								    &auth, uid,
d2edb4
-								    AUTHTYPE_KRB5,
d2edb4
-								    GSS_C_NO_CREDENTIAL)) == 0) {
d2edb4
-						/* Success! */
d2edb4
-						success++;
d2edb4
-						break;
d2edb4
-					}
d2edb4
-					printerr(2, "WARNING: Failed to create machine krb5 context "
d2edb4
-						 "with credentials cache %s for server %s\n",
d2edb4
-						 *ccname, clp->servername);
d2edb4
-				}
d2edb4
-				gssd_free_krb5_machine_cred_list(credlist);
d2edb4
-				if (!success) {
d2edb4
-					if(nocache == 0) {
d2edb4
-						nocache++;
d2edb4
-						printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
d2edb4
-						            "trying to recreate cache for server %s\n", clp->servername);
d2edb4
-					} else {
d2edb4
-						printerr(1, "WARNING: Failed to create machine krb5 context "
d2edb4
-						 "with any credentials cache for server %s\n",
d2edb4
-						 clp->servername);
d2edb4
-						goto out_return_error;
d2edb4
-					}
d2edb4
-				}
d2edb4
-			} while(!success);
d2edb4
+			auth =	krb5_use_machine_creds(clp, uid, tgtname,
d2edb4
+							service, &rpc_clnt);
d2edb4
+			if (auth == NULL)
d2edb4
+				goto out_return_error;
d2edb4
 		} else {
d2edb4
 			printerr(1, "WARNING: Failed to create krb5 context "
d2edb4
 				 "for user with uid %d for server %s\n",
d2edb4
@@ -1225,7 +1277,12 @@ out:
d2edb4
 		AUTH_DESTROY(auth);
d2edb4
 	if (rpc_clnt)
d2edb4
 		clnt_destroy(rpc_clnt);
d2edb4
-	exit(0);
d2edb4
+
d2edb4
+	pid = getpid();
d2edb4
+	if (pid == childpid)
d2edb4
+		exit(0);
d2edb4
+	else
d2edb4
+		return;
d2edb4
 
d2edb4
 out_return_error:
d2edb4
 	do_error_downcall(fd, uid, downcall_err);