File openssh-7.9p1-gsissh--from-fedora-31-gsi-openssh-package-modified-SLE_15_SP1.patch of Package gsi-openssh

diff -Nur openssh-7.9p1.orig/auth2.c openssh-7.9p1/auth2.c
--- openssh-7.9p1.orig/auth2.c	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/auth2.c	2020-04-30 16:09:26.570162249 +0200
@@ -267,17 +267,58 @@
 	user = packet_get_cstring(NULL);
 	service = packet_get_cstring(NULL);
 	method = packet_get_cstring(NULL);
-	debug("userauth-request for user %s service %s method %s", user, service, method);
+
+#ifdef GSSAPI
+	if (user[0] == '\0') {
+		debug("received empty username for %s", method);
+		if (strcmp(method, "gssapi-keyex") == 0) {
+			char *lname = NULL;
+			PRIVSEP(ssh_gssapi_localname(&lname));
+			if (lname && lname[0] != '\0') {
+				free(user);
+				user = lname;
+				debug("set username to %s from gssapi context", user);
+			} else {
+				debug("failed to set username from gssapi context");
+				packet_send_debug("failed to set username from gssapi context");
+			}
+		}
+	}
+#endif
+
+	debug("userauth-request for user %s service %s method %s",
+	    user[0] ? user : "<implicit>", service, method);
 	debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
 
 	if ((style = strchr(user, ':')) != NULL)
 		*style++ = 0;
 
-	if (authctxt->attempt++ == 0) {
-		/* setup auth context */
-		authctxt->pw = PRIVSEP(getpwnamallow(user));
+	/* If first time or username changed or empty username,
+	   setup/reset authentication context. */
+	if ((authctxt->attempt++ == 0) ||
+	    (strcmp(user, authctxt->user) != 0) ||
+	    (strcmp(user, "") == 0)) {
+		if (authctxt->user) {
+			free(authctxt->user);
+			authctxt->user = NULL;
+		}
+		authctxt->valid = 0;
 		authctxt->user = xstrdup(user);
-		if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
+		if (strcmp(service, "ssh-connection") != 0) {
+			packet_disconnect("Unsupported service %s", service);
+		}
+#ifdef GSSAPI
+		/* If we're going to set the username based on the
+		   GSSAPI context later, then wait until then to
+		   verify it. Just put in placeholders for now. */
+		if ((strcmp(user, "") == 0) &&
+		    ((strcmp(method, "gssapi") == 0) ||
+		     (strcmp(method, "gssapi-with-mic") == 0))) {
+			authctxt->pw = fakepw();
+		} else {
+#endif
+		authctxt->pw = PRIVSEP(getpwnamallow(user));
+		if (authctxt->pw) {
 			authctxt->valid = 1;
 			debug2("%s: setting up authctxt for %s",
 			    __func__, user);
@@ -285,6 +326,9 @@
 			/* Invalid user, fake password information */
 			authctxt->pw = fakepw();
 		}
+#ifdef GSSAPI
+		} /* endif for setting username based on GSSAPI context */
+#endif
 #ifdef USE_PAM
 		if (options.use_pam)
 			PRIVSEP(start_pam(authctxt));
@@ -293,6 +337,7 @@
 		    authctxt->valid ? "authenticating " : "invalid ", user);
 		setproctitle("%s%s", authctxt->valid ? user : "unknown",
 		    use_privsep ? " [net]" : "");
+		if (authctxt->attempt == 1) {
 		authctxt->service = xstrdup(service);
 		authctxt->style = style ? xstrdup(style) : NULL;
 		if (use_privsep)
@@ -300,9 +345,10 @@
 		userauth_banner();
 		if (auth2_setup_methods_lists(authctxt) != 0)
 			packet_disconnect("no authentication methods enabled");
-	} else if (strcmp(user, authctxt->user) != 0 ||
-	    strcmp(service, authctxt->service) != 0) {
-		packet_disconnect("Change of username or service not allowed: "
+		}
+	}
+	if (strcmp(service, authctxt->service) != 0) {
+		packet_disconnect("Change of service not allowed: "
 		    "(%s,%s) -> (%s,%s)",
 		    authctxt->user, authctxt->service, user, service);
 	}
diff -Nur openssh-7.9p1.orig/auth2-gss.c openssh-7.9p1/auth2-gss.c
--- openssh-7.9p1.orig/auth2-gss.c	2020-04-30 16:10:35.159844801 +0200
+++ openssh-7.9p1/auth2-gss.c	2020-04-30 16:09:26.574162102 +0200
@@ -50,6 +50,7 @@
 
 extern ServerOptions options;
 
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
 static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
@@ -63,8 +64,8 @@
 {
 	Authctxt *authctxt = ssh->authctxt;
 	int authenticated = 0;
-	struct sshbuf *b = NULL;
-	gss_buffer_desc mic, gssbuf;
+	struct sshbuf *b = NULL, *b2 = NULL;
+	gss_buffer_desc mic, gssbuf, gssbuf2;
 	u_int len;
 
 	mic.value = packet_get_string(&len);
@@ -81,13 +82,27 @@
 	gssbuf.value = sshbuf_mutable_ptr(b);
 	gssbuf.length = sshbuf_len(b);
 
+	/* client may have used empty username to determine target
+	   name from GSSAPI context */
+	ssh_gssapi_buildmic(b2, "", authctxt->service, "gssapi-keyex");
+
+	gssbuf2.value = sshbuf_mutable_ptr(b2);
+	gssbuf2.length = sshbuf_len(b2);
+
 	/* gss_kex_context is NULL with privsep, so we can't check it here */
 	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
-	    &gssbuf, &mic))))
-		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
-		    authctxt->pw));
+	    &gssbuf, &mic))) ||
+	    !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
+	    &gssbuf2, &mic)))) {
+		if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+			authenticated =
+			    PRIVSEP(ssh_gssapi_userok(authctxt->user,
+			    authctxt->pw, 1));
+		}
+	}
 
 	sshbuf_free(b);
+	sshbuf_free(b2);
 	free(mic.value);
 
 	return (authenticated);
@@ -142,7 +157,9 @@
 		return (0);
 	}
 
-	if (!authctxt->valid || authctxt->user == NULL) {
+	/* authctxt->valid may be 0 if we haven't yet determined
+	   username from gssapi context. */
+	if (authctxt->user == NULL) {
 		debug2("%s: disabled because of invalid user", __func__);
 		free(doid);
 		return (0);
@@ -180,7 +197,7 @@
 	Gssctxt *gssctxt;
 	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
 	gss_buffer_desc recv_tok;
-	OM_uint32 maj_status, min_status, flags;
+	OM_uint32 maj_status, min_status, flags = 0;
 	u_char *p;
 	size_t len;
 	int r;
@@ -201,6 +218,7 @@
 	free(p);
 
 	if (GSS_ERROR(maj_status)) {
+		ssh_gssapi_userauth_error(gssctxt);
 		if (send_tok.length != 0) {
 			if ((r = sshpkt_start(ssh,
 			    SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 ||
@@ -275,6 +293,32 @@
 	return 0;
 }
 
+static void
+gssapi_set_username(Authctxt *authctxt)
+{
+	char *lname = NULL;
+
+	if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) {
+		PRIVSEP(ssh_gssapi_localname(&lname));
+		if (lname && lname[0] != '\0') {
+			if (authctxt->user) free(authctxt->user);
+			authctxt->user = lname;
+			debug("set username to %s from gssapi context", lname);
+			authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
+			if (authctxt->pw) {
+				authctxt->valid = 1;
+#ifdef USE_PAM
+				if (options.use_pam)
+					PRIVSEP(start_pam(authctxt));
+#endif
+			}
+		} else {
+			debug("failed to set username from gssapi context");
+			packet_send_debug("failed to set username from gssapi context");
+		}
+	}
+}
+
 /*
  * This is called when the client thinks we've completed authentication.
  * It should only be enabled in the dispatch handler by the function above,
@@ -285,12 +329,14 @@
 input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
 {
 	Authctxt *authctxt = ssh->authctxt;
-	int r, authenticated;
+	int r, authenticated = 0;
 	const char *displayname;
 
 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
 		fatal("No authentication or GSSAPI context");
 
+	gssapi_set_username(authctxt);
+
 	/*
 	 * We don't need to check the status, because we're only enabled in
 	 * the dispatcher once the exchange is complete
@@ -299,8 +345,11 @@
 	if ((r = sshpkt_get_end(ssh)) != 0)
 		fatal("%s: %s", __func__, ssh_err(r));
 
-	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
-	    authctxt->pw));
+	/* user should be set if valid but we double-check here */
+	if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+		    authctxt->pw, 1));
+	}
 
 	if ((!use_privsep || mm_is_monitor()) &&
 	    (displayname = ssh_gssapi_displayname()) != NULL)
@@ -345,11 +394,17 @@
 		fatal("%s: sshbuf_mutable_ptr failed", __func__);
 	gssbuf.length = sshbuf_len(b);
 
-	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
-		authenticated =
-		    PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
-	else
+	gssapi_set_username(authctxt);
+
+	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) {
+		if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+			authenticated =
+			    PRIVSEP(ssh_gssapi_userok(authctxt->user,
+			    authctxt->pw, 0));
+		}
+	} else {
 		logit("GSSAPI MIC check failed");
+	}
 
 	sshbuf_free(b);
 	free(mic.value);
@@ -367,6 +422,23 @@
 	return 0;
 }
 
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
+	char *errstr;
+	OM_uint32 maj,min;
+
+	errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
+	if (errstr) {
+		packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
+		packet_put_int(maj);
+		packet_put_int(min);
+		packet_put_cstring(errstr);
+		packet_put_cstring("");
+		packet_send();
+		packet_write_wait();
+		free(errstr);
+	}
+}
+
 Authmethod method_gsskeyex = {
 	"gssapi-keyex",
 	userauth_gsskeyex,
diff -Nur openssh-7.9p1.orig/auth.c openssh-7.9p1/auth.c
--- openssh-7.9p1.orig/auth.c	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/auth.c	2020-04-30 16:09:26.574162102 +0200
@@ -343,7 +343,8 @@
 	    method,
 	    submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
 	    authctxt->valid ? "" : "invalid user ",
-	    authctxt->user,
+	    (authctxt->user && authctxt->user[0]) ?
+		authctxt->user : "unknown",
 	    ssh_remote_ipaddr(ssh),
 	    ssh_remote_port(ssh),
 	    extra != NULL ? ": " : "",
@@ -581,6 +582,10 @@
 #endif
 
 	pw = getpwnam(user);
+#ifdef USE_PAM
+	if (options.use_pam && options.permit_pam_user_change && pw == NULL)
+		pw = sshpam_getpw(user);
+#endif
 
 #if defined(_AIX) && defined(HAVE_SETAUTHDB)
 	aix_restoreauthdb();
@@ -600,7 +605,8 @@
 #endif
 	if (pw == NULL) {
 		logit("Invalid user %.100s from %.100s port %d",
-		    user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
+		    (user && user[0]) ? user : "unknown",
+		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
 #ifdef CUSTOM_FAILED_LOGIN
 		record_failed_login(user,
 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh");
diff -Nur openssh-7.9p1.orig/auth.h openssh-7.9p1/auth.h
--- openssh-7.9p1.orig/auth.h	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/auth.h	2020-04-30 16:09:26.574162102 +0200
@@ -83,6 +83,9 @@
 	char		*krb5_ticket_file;
 	char		*krb5_ccname;
 #endif
+#ifdef GSSAPI
+ 	int		 krb5_set_env;
+#endif
 	struct sshbuf	*loginmsg;
 
 	/* Authentication keys already used; these will be refused henceforth */
diff -Nur openssh-7.9p1.orig/auth-pam.c openssh-7.9p1/auth-pam.c
--- openssh-7.9p1.orig/auth-pam.c	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/auth-pam.c	2020-04-30 16:09:26.574162102 +0200
@@ -288,6 +288,56 @@
 # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
 #endif
 
+struct passwd *
+sshpam_getpw(const char *user)
+{
+	struct passwd *pw;
+
+	if ((pw = getpwnam(user)) != NULL)
+		return(pw);
+
+	debug("PAM: faking passwd struct for user '%.100s'", user);
+	if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL)
+		return NULL;
+	pw->pw_name = xstrdup(user);	/* XXX leak */
+	pw->pw_shell = "/bin/true";
+	pw->pw_gecos = "sshd fake PAM user";
+	return (pw);
+}
+
+void
+sshpam_check_userchanged(void)
+{
+	int sshpam_err;
+	struct passwd *pw;
+	const char *user;
+
+	debug("sshpam_check_userchanged");
+	sshpam_err = pam_get_item(sshpam_handle, PAM_USER,
+				  (sshpam_const void **)&user);
+	if (sshpam_err != PAM_SUCCESS)
+		fatal("PAM: could not get PAM_USER: %s",
+		    pam_strerror(sshpam_handle, sshpam_err));
+	debug("sshpam_check_userchanged: user was '%.100s'",
+	    sshpam_authctxt->pw->pw_name);
+	if (strcmp(user, sshpam_authctxt->pw->pw_name) != 0) {
+		debug("PAM: user mapped from '%.100s' to '%.100s'",
+		    sshpam_authctxt->pw->pw_name, user);
+		if ((pw = getpwnam(user)) == NULL)
+			fatal("PAM: could not get passwd entry for user "
+			    "'%.100s' provided by PAM_USER", user);
+		pwfree(sshpam_authctxt->pw);
+		sshpam_authctxt->pw = pwcopy(pw);
+		sshpam_authctxt->valid = allowed_user(pw);
+		free(sshpam_authctxt->user);
+		sshpam_authctxt->user = xstrdup(user);
+		debug("PAM: user '%.100s' now %svalid", user,
+		    sshpam_authctxt->valid ? "" : "in");
+	}
+	debug("sshpam_check_userchanged: user is '%.100s'",
+	    sshpam_authctxt->pw->pw_name);
+}
+
 void
 sshpam_password_change_required(int reqd)
 {
@@ -319,7 +369,7 @@
 static void
 import_environments(struct sshbuf *b)
 {
-	char *env;
+	char *env, *user;
 	u_int n, i, num_env;
 	int r;
 
@@ -335,6 +385,19 @@
 	if ((r = sshbuf_get_u32(b, &n)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	sshpam_password_change_required(n != 0);
+	if (options.permit_pam_user_change) {
+		if ((r = sshbuf_get_cstring(b, &user, NULL)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+		debug("PAM: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+		debug("PAM: got username '%.100s' from thread", user);
+		if ((sshpam_err = pam_set_item(sshpam_handle, PAM_USER, user))
+		    != PAM_SUCCESS)
+			fatal("PAM: failed to set PAM_USER: %s",
+			    pam_strerror(sshpam_handle, sshpam_err));
+		pwfree(sshpam_authctxt->pw);
+		sshpam_authctxt->pw = pwcopy(sshpam_getpw(user));
+	}
 
 	/* Import environment from subprocess */
 	if ((r = sshbuf_get_u32(b, &num_env)) != 0)
@@ -507,6 +570,13 @@
 	if (sshpam_err != PAM_SUCCESS)
 		goto auth_fail;
 
+	if (options.permit_pam_user_change) {
+		debug("sshpam_thread: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+		sshpam_check_userchanged();
+		debug("sshpam_thread: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+	}
 	if (!do_pam_account()) {
 		sshpam_err = PAM_ACCT_EXPIRED;
 		goto auth_fail;
@@ -527,6 +597,13 @@
 	if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 ||
 	    (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	if (options.permit_pam_user_change) {
+		debug("sshpam_thread: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+		if ((r = sshbuf_put_cstring(buffer,
+		    sshpam_authctxt->pw->pw_name)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	}
 
 	/* Export any environment strings set in child */
 	for (i = 0; environ[i] != NULL; i++) {
@@ -1024,6 +1101,18 @@
 	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
 	    pam_strerror(sshpam_handle, sshpam_err));
 
+	if (options.permit_pam_user_change) {
+		debug("do_pam_account: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+		sshpam_check_userchanged();
+		debug("do_pam_account: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+		if (getpwnam(sshpam_authctxt->pw->pw_name) == NULL)
+			fatal("PAM: completed authentication but PAM account invalid");
+		debug("do_pam_account: user is '%.100s'",
+		    sshpam_authctxt->pw->pw_name);
+	}
+
 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
 		sshpam_account_status = 0;
 		return (sshpam_account_status);
@@ -1314,6 +1403,9 @@
 		    pam_strerror(sshpam_handle, sshpam_err));
 
 	sshpam_err = pam_authenticate(sshpam_handle, flags);
+	if (options.permit_pam_user_change) {
+		sshpam_check_userchanged();
+	}
 	sshpam_password = NULL;
 	free(fake);
 	if (sshpam_err == PAM_MAXTRIES)
diff -Nur openssh-7.9p1.orig/auth-pam.h openssh-7.9p1/auth-pam.h
--- openssh-7.9p1.orig/auth-pam.h	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/auth-pam.h	2020-04-30 16:09:26.574162102 +0200
@@ -43,5 +43,6 @@
 int sshpam_get_maxtries_reached(void);
 void sshpam_set_maxtries_reached(int);
 int is_pam_session_open(void);
+struct passwd *sshpam_getpw(const char *);
 
 #endif /* USE_PAM */
diff -Nur openssh-7.9p1.orig/canohost.c openssh-7.9p1/canohost.c
--- openssh-7.9p1.orig/canohost.c	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/canohost.c	2020-04-30 16:09:26.574162102 +0200
@@ -17,6 +17,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/param.h>          /* for MAXHOSTNAMELEN */
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -202,3 +203,33 @@
 {
 	return get_sock_port(sock, 1);
 }
+
+void
+resolve_localhost(char **host)
+{
+	struct hostent *hostinfo;
+
+	hostinfo = gethostbyname(*host);
+	if (hostinfo == NULL || hostinfo->h_name == NULL) {
+		debug("gethostbyname(%s) failed", *host);
+		return;
+	}
+	if (hostinfo->h_addrtype == AF_INET) {
+		struct in_addr addr;
+		addr = *(struct in_addr *)(hostinfo->h_addr);
+		if (ntohl(addr.s_addr) == INADDR_LOOPBACK) {
+			char buf[MAXHOSTNAMELEN];
+			if (gethostname(buf, sizeof(buf)) < 0) {
+				debug("gethostname() failed");
+				return;
+			}
+			hostinfo = gethostbyname(buf);
+			free(*host);
+			if (hostinfo == NULL || hostinfo->h_name == NULL) {
+				*host = xstrdup(buf);
+			} else {
+				*host = xstrdup(hostinfo->h_name);
+			}
+		}
+	}
+}
diff -Nur openssh-7.9p1.orig/canohost.h openssh-7.9p1/canohost.h
--- openssh-7.9p1.orig/canohost.h	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/canohost.h	2020-04-30 16:09:26.578161954 +0200
@@ -23,4 +23,6 @@
 
 #endif /* _CANOHOST_H */
 
+void		 resolve_localhost(char **host);
+
 void		 ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *);
diff -Nur openssh-7.9p1.orig/configure.ac openssh-7.9p1/configure.ac
--- openssh-7.9p1.orig/configure.ac	2020-04-30 16:10:35.199843574 +0200
+++ openssh-7.9p1/configure.ac	2020-04-30 16:09:26.578161954 +0200
@@ -4591,6 +4591,14 @@
 			AC_CHECK_HEADER([gssapi_krb5.h], ,
 					[ CPPFLAGS="$oldCPP" ])
 
+			# If we're using some other GSSAPI
+			if test -n "$GSSAPI" ; then
+				AC_MSG_ERROR([Previously configured GSSAPI library conflicts with Kerberos GSI.])
+			fi
+
+			if test -z "$GSSAPI"; then
+				GSSAPI="KRB5";
+			fi
 		fi
 		if test ! -z "$need_dash_r" ; then
 			LDFLAGS="$LDFLAGS -R${KRB5ROOT}/lib"
@@ -4630,6 +4638,40 @@
 AC_SUBST([GSSLIBS])
 AC_SUBST([K5LIBS])
 
+# Check whether the user wants GSI (Globus) support
+gsi="no"
+AC_ARG_WITH(gsi,
+	[  --with-gsi              Enable Globus GSI authentication support],
+	[
+		gsi="$withval"
+	]
+)
+
+if test "x$gsi" != "xno" ; then
+	# Globus GSSAPI configuration
+	AC_MSG_CHECKING(for Globus GSI)
+	AC_DEFINE(GSI, 1, [Define if you want GSI/Globus authentication support.])
+
+	if test -n "$GSSAPI" ; then
+		AC_MSG_ERROR([Previously configured GSSAPI library conflicts with Globus GSI.])
+	fi
+
+	if test -z "$GSSAPI" ; then
+		GSSAPI="GSI"
+	fi
+
+	AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no])
+	if test "x$PKGCONFIG" != "xno"; then
+		LIBS="$LIBS `$PKGCONFIG --libs globus-gss-assist globus-gssapi-gsi globus-common`"
+		CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags globus-gss-assist globus-gssapi-gsi globus-common`"
+	fi
+
+	AC_DEFINE(GSSAPI)
+	AC_DEFINE(HAVE_GSSAPI_H)
+
+	AC_CHECK_FUNCS(globus_gss_assist_map_and_authorize)
+fi
+
 # Check whether user wants systemd support
 SYSTEMD_MSG="no"
 AC_ARG_WITH(systemd,
diff -Nur openssh-7.9p1.orig/gss-genr.c openssh-7.9p1/gss-genr.c
--- openssh-7.9p1.orig/gss-genr.c	2020-04-30 16:10:35.215843084 +0200
+++ openssh-7.9p1/gss-genr.c	2020-04-30 16:09:26.578161954 +0200
@@ -40,6 +40,7 @@
 #include "ssherr.h"
 #include "sshbuf.h"
 #include "log.h"
+#include "canohost.h"
 #include "ssh2.h"
 #include "cipher.h"
 #include "sshkey.h"
@@ -403,9 +404,18 @@
 ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
 {
 	gss_buffer_desc gssbuf;
+	char *xhost;
 	char *val;
 
-	xasprintf(&val, "host@%s", host);
+	/* Make a copy of the host name, in case it was returned by a
+	 * previous call to gethostbyname(). */
+	xhost = xstrdup(host);
+
+	/* Make sure we have the FQDN. Some GSSAPI implementations don't do
+	 * this for us themselves */
+	resolve_localhost(&xhost);
+
+	xasprintf(&val, "host@%s", xhost);
 	gssbuf.value = val;
 	gssbuf.length = strlen(gssbuf.value);
 
@@ -413,6 +423,7 @@
 	    &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name)))
 		ssh_gssapi_error(ctx);
 
+	free(xhost);
 	free(gssbuf.value);
 	return (ctx->major);
 }
diff -Nur openssh-7.9p1.orig/gss-serv.c openssh-7.9p1/gss-serv.c
--- openssh-7.9p1.orig/gss-serv.c	2020-04-30 16:10:35.163844679 +0200
+++ openssh-7.9p1/gss-serv.c	2020-04-30 16:09:26.578161954 +0200
@@ -50,10 +50,12 @@
 #include "monitor_wrap.h"
 
 extern ServerOptions options;
+extern Authctxt *the_authctxt;
 
 static ssh_gssapi_client gssapi_client =
-    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
-    GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME,  NULL, {NULL, NULL, NULL, NULL}, 0, 0};
+    { {0, NULL}, GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
+      GSS_C_NO_NAME, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL},
+      GSS_C_NO_CONTEXT, 0, 0};
 
 ssh_gssapi_mech gssapi_null_mech =
     { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
@@ -61,14 +63,26 @@
 #ifdef KRB5
 extern ssh_gssapi_mech gssapi_kerberos_mech;
 #endif
+#ifdef GSI
+extern ssh_gssapi_mech gssapi_gsi_mech;
+extern ssh_gssapi_mech gssapi_gsi_mech_micv2;
+#endif
 
 ssh_gssapi_mech* supported_mechs[]= {
 #ifdef KRB5
 	&gssapi_kerberos_mech,
 #endif
+#ifdef GSI
+	&gssapi_gsi_mech_micv2,
+	&gssapi_gsi_mech,
+#endif
 	&gssapi_null_mech,
 };
 
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
+static int limited = 0;
+#endif
+
 /*
  * ssh_gssapi_supported_oids() can cause sandbox violations, so prepare the
  * list of supported mechanisms before privsep is set up.
@@ -228,6 +242,10 @@
 	    (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
 		if (ssh_gssapi_getclient(ctx, &gssapi_client))
 			fatal("Couldn't convert client name");
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
+		if (flags && (*flags & GSS_C_GLOBUS_LIMITED_PROXY_FLAG))
+			limited=1;
+#endif
 	}
 
 	return (status);
@@ -247,6 +265,20 @@
 
 	tok = ename->value;
 
+#ifdef GSI /* GSI gss_export_name() is broken. */
+	if (((ctx->oid->length == gssapi_gsi_mech.oid.length) &&
+	     (memcmp(ctx->oid->elements, gssapi_gsi_mech.oid.elements,
+		     gssapi_gsi_mech.oid.length) == 0)) ||
+	    ((ctx->oid->length == gssapi_gsi_mech_micv2.oid.length) &&
+	     (memcmp(ctx->oid->elements, gssapi_gsi_mech_micv2.oid.elements,
+		     gssapi_gsi_mech_micv2.oid.length) == 0))) {
+		name->length = ename->length;
+		name->value = xmalloc(ename->length+1);
+		memcpy(name->value, ename->value, ename->length);
+		return GSS_S_COMPLETE;
+	}
+#endif
+
 	/*
 	 * Check that ename is long enough for all of the fixed length
 	 * header, and that the initial ID bytes are correct
@@ -307,21 +339,24 @@
 	gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
 
 	if (options.gss_store_rekey && client->used && ctx->client_creds) {
-		if (client->mech->oid.length != ctx->oid->length ||
-		    (memcmp(client->mech->oid.elements,
+		if (client->oid.length != ctx->oid->length ||
+		    (memcmp(client->oid.elements,
 		     ctx->oid->elements, ctx->oid->length) !=0)) {
 			debug("Rekeyed credentials have different mechanism");
 			return GSS_S_COMPLETE;
 		}
 
-		if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
-		    ctx->client_creds, ctx->oid, &new_name,
+		/* Call gss_inquire_cred rather than gss_inquire_cred_by_mech
+		   because GSI doesn't support the latter. -jbasney */
+
+		if ((ctx->major = gss_inquire_cred(&ctx->minor,
+		    ctx->client_creds, &new_name,
 		    NULL, NULL, NULL))) {
 			ssh_gssapi_error(ctx);
 			return (ctx->major);
 		}
 
-		ctx->major = gss_compare_name(&ctx->minor, client->name,
+		ctx->major = gss_compare_name(&ctx->minor, client->cred_name,
 		    new_name, &equal);
 
 		if (GSS_ERROR(ctx->major)) {
@@ -336,9 +371,9 @@
 
 		debug("Marking rekeyed credentials for export");
 
-		gss_release_name(&ctx->minor, &client->name);
+		gss_release_name(&ctx->minor, &client->cred_name);
 		gss_release_cred(&ctx->minor, &client->creds);
-		client->name = new_name;
+		client->cred_name = new_name;
 		client->creds = ctx->client_creds;
 		ctx->client_creds = GSS_C_NO_CREDENTIAL;
 		client->updated = 1;
@@ -355,12 +390,17 @@
 		i++;
 	}
 
+	if (client->oid.elements == NULL)
+		client->oid = *ctx->oid;
 	if (client->mech == NULL)
 		return GSS_S_FAILURE;
 
+	/* Call gss_inquire_cred rather than gss_inquire_cred_by_mech
+	   because GSI doesn't support the latter. -jbasney */
+
 	if (ctx->client_creds &&
-	    (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
-	     ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
+	    (ctx->major = gss_inquire_cred(&ctx->minor,
+	     ctx->client_creds, &client->cred_name, NULL, NULL, NULL))) {
 		ssh_gssapi_error(ctx);
 		return (ctx->major);
 	}
@@ -377,16 +417,25 @@
 		return (ctx->major);
 	}
 
-	if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
+	if ((client->mech->oid.elements != NULL) &&
+	    (ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
 	    &client->exportedname))) {
 		return (ctx->major);
 	}
 
+	if ((ctx->major = gss_duplicate_name(&ctx->minor, ctx->client,
+	    &client->ctx_name)))
+		return ctx->major;
+
 	gss_release_buffer(&ctx->minor, &ename);
 
 	/* We can't copy this structure, so we just move the pointer to it */
 	client->creds = ctx->client_creds;
 	ctx->client_creds = GSS_C_NO_CREDENTIAL;
+
+	/* needed for globus_gss_assist_map_and_authorize() */
+	client->context = ctx->context;
+
 	return (ctx->major);
 }
 
@@ -394,6 +443,7 @@
 void
 ssh_gssapi_cleanup_creds(void)
 {
+#ifdef KRB5
 	krb5_ccache ccache = NULL;
 	krb5_error_code problem;
 
@@ -409,16 +459,31 @@
 			gssapi_client.store.data = NULL;
 		}
 	}
+#else
+	if (gssapi_client.store.filename != NULL) {
+		/* Unlink probably isn't sufficient */
+		debug("removing gssapi cred file\"%s\"",
+		    gssapi_client.store.filename);
+		unlink(gssapi_client.store.filename);
+	}
+#endif
 }
 
 /* As user */
-void
+int
 ssh_gssapi_storecreds(void)
 {
 	if (gssapi_client.mech && gssapi_client.mech->storecreds) {
-		(*gssapi_client.mech->storecreds)(&gssapi_client);
+		if (options.gss_creds_path) {
+			gssapi_client.store.filename =
+			    expand_authorized_keys(options.gss_creds_path,
+			    the_authctxt->pw);
+		}
+		return (*gssapi_client.mech->storecreds)(&gssapi_client);
 	} else
 		debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
+
+	return 0;
 }
 
 /* This allows GSSAPI methods to do things to the childs environment based
@@ -444,11 +509,13 @@
 {
 	OM_uint32 lmin;
 
-	if (gssapi_client.exportedname.length == 0 ||
-	    gssapi_client.exportedname.value == NULL) {
-		debug("No suitable client data");
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
+	if (limited && options.gsi_allow_limited_proxy != 1) {
+		debug("limited proxy not acceptable for remote login");
 		return 0;
 	}
+#endif
+
 	if (gssapi_client.mech && gssapi_client.mech->userok)
 		if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
 			gssapi_client.used = 1;
@@ -459,6 +526,7 @@
 			gss_release_buffer(&lmin, &gssapi_client.displayname);
 			gss_release_buffer(&lmin, &gssapi_client.exportedname);
 			gss_release_cred(&lmin, &gssapi_client.creds);
+			gss_release_name(&lmin, &gssapi_client.ctx_name);
 			explicit_bzero(&gssapi_client,
 			    sizeof(ssh_gssapi_client));
 			return 0;
@@ -468,6 +536,24 @@
 	return (0);
 }
 
+/* Priviledged */
+int
+ssh_gssapi_localname(char **user)
+{
+	*user = NULL;
+	if (gssapi_client.displayname.length == 0 ||
+	    gssapi_client.displayname.value == NULL) {
+		debug("No suitable client data");
+		return (0);
+	}
+	if (gssapi_client.mech && gssapi_client.mech->localname) {
+		return((*gssapi_client.mech->localname)(&gssapi_client,user));
+	} else {
+		debug("Unknown client authentication type");
+	}
+	return (0);
+}
+
 /* These bits are only used for rekeying. The unpriviledged child is running
  * as the user, the monitor is root.
  *
@@ -494,6 +580,7 @@
 	pam_handle_t *pamh = NULL;
 	struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
 	char *envstr;
+	char **p; char **pw;
 #endif
 
 	if (gssapi_client.store.filename == NULL &&
@@ -523,6 +610,18 @@
 	if (ret)
 		return;
 
+	/* Put ssh pam stack env variables in this new pam stack env
+	 * Using pam-pkinit, KRB5CCNAME is set during do_pam_session
+	 * this addition enables pam-pkinit to access KRB5CCNAME if used
+	 * in sshd-rekey stack too
+	 */
+	pw = p = fetch_pam_environment();
+	while ( *pw != NULL ) {
+		pam_putenv(pamh, *pw);
+		pw++;
+	}
+	free_pam_environment(p);
+
 	xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
 	    gssapi_client.store.envval);
 
diff -Nur openssh-7.9p1.orig/gss-serv-gsi.c openssh-7.9p1/gss-serv-gsi.c
--- openssh-7.9p1.orig/gss-serv-gsi.c	1970-01-01 01:00:00.000000000 +0100
+++ openssh-7.9p1/gss-serv-gsi.c	2020-04-30 16:09:26.578161954 +0200
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#ifdef GSI
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "xmalloc.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "log.h"
+#include "misc.h"
+#include "servconf.h"
+#include "ssh-gss.h"
+
+extern ServerOptions options;
+
+#include <globus_gss_assist.h>
+
+static int ssh_gssapi_gsi_userok(ssh_gssapi_client *client, char *name);
+static int ssh_gssapi_gsi_localname(ssh_gssapi_client *client, char **user);
+static int ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client);
+static int ssh_gssapi_gsi_storecreds_micv2(ssh_gssapi_client *client);
+static int ssh_gssapi_gsi_updatecreds(ssh_gssapi_ccache *store,
+    ssh_gssapi_client *client);
+static int ssh_gssapi_gsi_updatecreds_micv2(ssh_gssapi_ccache *store,
+    ssh_gssapi_client *client);
+
+ssh_gssapi_mech gssapi_gsi_mech = {
+	"dZuIebMjgUqaxvbF7hDbAw==",
+	"GSI",
+	{9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"},
+	NULL,
+	&ssh_gssapi_gsi_userok,
+	&ssh_gssapi_gsi_localname,
+	&ssh_gssapi_gsi_storecreds,
+	&ssh_gssapi_gsi_updatecreds
+};
+
+ssh_gssapi_mech gssapi_gsi_mech_micv2 = {
+	"vz8J1E9PzLr8b1K+0remTg==",
+	"GSI",
+	{10, "\x2b\x06\x01\x04\x01\x9b\x50\x01\x01\x01"},
+	NULL,
+	&ssh_gssapi_gsi_userok,
+	&ssh_gssapi_gsi_localname,
+	&ssh_gssapi_gsi_storecreds_micv2,
+	&ssh_gssapi_gsi_updatecreds_micv2
+};
+
+/*
+ * Check if this user is OK to login under GSI. User has been authenticated
+ * as identity in global 'client_name.value' and is trying to log in as passed
+ * username in 'name'.
+ *
+ * Returns non-zero if user is authorized, 0 otherwise.
+ */
+static int
+ssh_gssapi_gsi_userok(ssh_gssapi_client *client, char *name)
+{
+    int authorized = 0;
+    globus_result_t res;
+#ifdef HAVE_GLOBUS_GSS_ASSIST_MAP_AND_AUTHORIZE
+    char lname[256] = "";
+#endif
+
+#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
+    if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
+        return 0;
+    }
+#endif
+
+/* use new globus_gss_assist_map_and_authorize() interface if available */
+#ifdef HAVE_GLOBUS_GSS_ASSIST_MAP_AND_AUTHORIZE
+    debug("calling globus_gss_assist_map_and_authorize()");
+    if (GLOBUS_SUCCESS !=
+        (res = globus_gss_assist_map_and_authorize(client->context, "ssh",
+                                                   name, lname, 256))) {
+        debug("%s", globus_error_print_chain(globus_error_get(res)));
+    } else if (lname[0] && strcmp(name, lname) != 0) {
+        debug("GSI user maps to %s, not %s", lname, name);
+    } else {
+        authorized = 1;
+    }
+#else
+    debug("calling globus_gss_assist_userok()");
+    if (GLOBUS_SUCCESS !=
+        (res = (globus_gss_assist_userok(client->displayname.value,
+                                         name)))) {
+        debug("%s", globus_error_print_chain(globus_error_get(res)));
+    } else {
+        authorized = 1;
+    }
+#endif
+
+    logit("GSI user %s is%s authorized as target user %s",
+        (char *) client->displayname.value, (authorized ? "" : " not"), name);
+
+    return authorized;
+}
+
+/*
+ * Return the local username associated with the GSI credentials.
+ */
+int
+ssh_gssapi_gsi_localname(ssh_gssapi_client *client, char **user)
+{
+    globus_result_t res;
+#ifdef HAVE_GLOBUS_GSS_ASSIST_MAP_AND_AUTHORIZE
+    char lname[256] = "";
+#endif
+
+#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
+    if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
+        return 0;
+    }
+#endif
+
+/* use new globus_gss_assist_map_and_authorize() interface if available */
+#ifdef HAVE_GLOBUS_GSS_ASSIST_MAP_AND_AUTHORIZE
+    debug("calling globus_gss_assist_map_and_authorize()");
+    if (GLOBUS_SUCCESS !=
+        (res = globus_gss_assist_map_and_authorize(client->context, "ssh",
+                                                   NULL, lname, 256))) {
+        debug("%s", globus_error_print_chain(globus_error_get(res)));
+        logit("failed to map GSI user %s", (char *)client->displayname.value);
+        return 0;
+    }
+    *user = strdup(lname);
+#else
+    debug("calling globus_gss_assist_gridmap()");
+    if (GLOBUS_SUCCESS !=
+        (res = globus_gss_assist_gridmap(client->displayname.value, user))) {
+        debug("%s", globus_error_print_chain(globus_error_get(res)));
+        logit("failed to map GSI user %s", (char *)client->displayname.value);
+        return 0;
+    }
+#endif
+
+    logit("GSI user %s mapped to target user %s",
+        (char *) client->displayname.value, *user);
+
+    return 1;
+}
+
+/*
+ * Export GSI credentials to disk.
+ */
+static int
+ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client)
+{
+	OM_uint32	major_status;
+	OM_uint32	minor_status;
+	gss_buffer_desc	export_cred = GSS_C_EMPTY_BUFFER;
+	char *		p;
+
+	if (!client || !client->creds) {
+		return 0;
+	}
+
+	major_status = gss_export_cred(&minor_status,
+				       client->creds,
+				       GSS_C_NO_OID,
+				       1,
+				       &export_cred);
+	if (GSS_ERROR(major_status) && major_status != GSS_S_UNAVAILABLE) {
+		Gssctxt *ctx;
+		ssh_gssapi_build_ctx(&ctx);
+		ctx->major = major_status;
+		ctx->minor = minor_status;
+		ssh_gssapi_set_oid(ctx, &gssapi_gsi_mech.oid);
+		ssh_gssapi_error(ctx);
+		ssh_gssapi_delete_ctx(&ctx);
+		return 0;
+	}
+
+	p = strchr((char *) export_cred.value, '=');
+	if (p == NULL) {
+		logit("Failed to parse exported credentials string '%.100s'",
+		    (char *)export_cred.value);
+		gss_release_buffer(&minor_status, &export_cred);
+		return 0;
+	}
+	*p++ = '\0';
+	if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0) {
+		client->store.envvar = strdup("X509_USER_PROXY");
+	} else {
+		client->store.envvar = strdup((char *)export_cred.value);
+	}
+	if (access(p, R_OK) == 0) {
+		if (client->store.filename) {
+			if (rename(p, client->store.filename) < 0) {
+				logit("Failed to rename %s to %s: %s", p,
+				    client->store.filename, strerror(errno));
+				free(client->store.filename);
+				client->store.filename = strdup(p);
+			} else {
+				p = client->store.filename;
+			}
+		} else {
+			client->store.filename = strdup(p);
+		}
+	}
+	client->store.envval = strdup(p);
+#ifdef USE_PAM
+	if (options.use_pam)
+		do_pam_putenv(client->store.envvar, client->store.envval);
+#endif
+	gss_release_buffer(&minor_status, &export_cred);
+
+	return 1;
+}
+
+/*
+ * Export GSI credentials to disk.
+ */
+static int
+ssh_gssapi_gsi_storecreds_micv2(ssh_gssapi_client *client)
+{
+	OM_uint32	major_status;
+	OM_uint32	minor_status;
+	gss_buffer_desc	export_cred = GSS_C_EMPTY_BUFFER;
+	char *		p;
+
+	if (!client || !client->creds) {
+		return 0;
+	}
+
+	major_status = gss_export_cred(&minor_status,
+				       client->creds,
+				       GSS_C_NO_OID,
+				       1,
+				       &export_cred);
+	if (GSS_ERROR(major_status) && major_status != GSS_S_UNAVAILABLE) {
+		Gssctxt *ctx;
+		ssh_gssapi_build_ctx(&ctx);
+		ctx->major = major_status;
+		ctx->minor = minor_status;
+		ssh_gssapi_set_oid(ctx, &gssapi_gsi_mech_micv2.oid);
+		ssh_gssapi_error(ctx);
+		ssh_gssapi_delete_ctx(&ctx);
+		return 0;
+	}
+
+	p = strchr((char *) export_cred.value, '=');
+	if (p == NULL) {
+		logit("Failed to parse exported credentials string '%.100s'",
+		    (char *)export_cred.value);
+		gss_release_buffer(&minor_status, &export_cred);
+		return 0;
+	}
+	*p++ = '\0';
+	if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0) {
+		client->store.envvar = strdup("X509_USER_PROXY");
+	} else {
+		client->store.envvar = strdup((char *)export_cred.value);
+	}
+	if (access(p, R_OK) == 0) {
+		if (client->store.filename) {
+			if (rename(p, client->store.filename) < 0) {
+				logit("Failed to rename %s to %s: %s", p,
+				    client->store.filename, strerror(errno));
+				free(client->store.filename);
+				client->store.filename = strdup(p);
+			} else {
+				p = client->store.filename;
+			}
+		} else {
+			client->store.filename = strdup(p);
+		}
+	}
+	client->store.envval = strdup(p);
+#ifdef USE_PAM
+	if (options.use_pam)
+		do_pam_putenv(client->store.envvar, client->store.envval);
+#endif
+	gss_release_buffer(&minor_status, &export_cred);
+
+	return 1;
+}
+
+/*
+ * Export updated GSI credentials to disk.
+ */
+static int
+ssh_gssapi_gsi_updatecreds(ssh_gssapi_ccache *store,ssh_gssapi_client *client)
+{
+	return ssh_gssapi_gsi_storecreds(client);
+}
+
+/*
+ * Export updated GSI credentials to disk.
+ */
+static int
+ssh_gssapi_gsi_updatecreds_micv2(ssh_gssapi_ccache *store,ssh_gssapi_client *client)
+{
+	return ssh_gssapi_gsi_storecreds_micv2(client);
+}
+
+#endif /* GSI */
+#endif /* GSSAPI */
diff -Nur openssh-7.9p1.orig/gss-serv-krb5.c openssh-7.9p1/gss-serv-krb5.c
--- openssh-7.9p1.orig/gss-serv-krb5.c	2020-04-30 16:10:35.163844679 +0200
+++ openssh-7.9p1/gss-serv-krb5.c	2020-04-30 16:09:26.578161954 +0200
@@ -109,6 +109,34 @@
 	return retval;
 }
 
+/* Retrieve the local username associated with a set of Kerberos
+ * credentials. Hopefully we can use this for the 'empty' username
+ * logins discussed in the draft  */
+static int
+ssh_gssapi_krb5_localname(ssh_gssapi_client *client, char **user) {
+	krb5_principal princ;
+	int retval;
+
+	if (ssh_gssapi_krb5_init() == 0)
+		return 0;
+
+	if ((retval=krb5_parse_name(krb_context, client->displayname.value,
+	    &princ))) {
+		logit("krb5_parse_name(): %.100s",
+		    krb5_get_err_text(krb_context,retval));
+		return 0;
+	}
+
+	/* We've got to return a malloc'd string */
+	*user = (char *)xmalloc(256);
+	if (krb5_aname_to_localname(krb_context, princ, 256, *user)) {
+		free(*user);
+		*user = NULL;
+		return(0);
+	}
+
+	return(1);
+}
 
 /* This writes out any forwarded credentials from the structure populated
  * during userauth. Called after we have setuid to the user */
@@ -210,7 +238,7 @@
 	return;
 }
 
-int
+static int
 ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
     ssh_gssapi_client *client)
 {
@@ -281,7 +309,7 @@
 	{9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"},
 	NULL,
 	&ssh_gssapi_krb5_userok,
-	NULL,
+	&ssh_gssapi_krb5_localname,
 	&ssh_gssapi_krb5_storecreds,
 	&ssh_gssapi_krb5_updatecreds
 };
diff -Nur openssh-7.9p1.orig/kexgsss.c openssh-7.9p1/kexgsss.c
--- openssh-7.9p1.orig/kexgsss.c	2020-04-30 16:10:35.163844679 +0200
+++ openssh-7.9p1/kexgsss.c	2020-04-30 16:09:26.578161954 +0200
@@ -48,6 +48,7 @@
 
 #include "fips.h"
 
+static void kex_gss_send_error(Gssctxt *ctxt);
 extern ServerOptions options;
 
 int
@@ -97,8 +98,10 @@
 
 	debug2("%s: Acquiring credentials", __func__);
 
-	if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
+	if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) {
+		kex_gss_send_error(ctxt);
 		fatal("Unable to acquire credentials for the server");
+	}
 
 	switch (ssh->kex->kex_type) {
 	case KEX_GSS_GRP1_SHA1:
@@ -187,12 +190,13 @@
 	} while (maj_status & GSS_S_CONTINUE_NEEDED);
 
 	if (GSS_ERROR(maj_status)) {
+		kex_gss_send_error(ctxt);
 		if (send_tok.length > 0) {
 			packet_start(SSH2_MSG_KEXGSS_CONTINUE);
 			packet_put_string((char *)send_tok.value, send_tok.length);
 			packet_send();
 		}
-		fatal("accept_ctx died");
+		packet_disconnect("GSSAPI Key Exchange handshake failed");
 	}
 
 	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
@@ -299,4 +303,23 @@
 		ssh_gssapi_rekey_creds();
 	return 0;
 }
+
+static void
+kex_gss_send_error(Gssctxt *ctxt) {
+	char *errstr;
+	OM_uint32 maj, min;
+
+	errstr = PRIVSEP(ssh_gssapi_last_error(ctxt, &maj, &min));
+	if (errstr) {
+		packet_start(SSH2_MSG_KEXGSS_ERROR);
+		packet_put_int(maj);
+		packet_put_int(min);
+		packet_put_cstring(errstr);
+		packet_put_cstring("");
+		packet_send();
+		packet_write_wait();
+		/* XXX - We should probably log the error locally here */
+		free(errstr);
+	}
+}
 #endif /* GSSAPI */
diff -Nur openssh-7.9p1.orig/Makefile.in openssh-7.9p1/Makefile.in
--- openssh-7.9p1.orig/Makefile.in	2020-04-30 16:10:35.199843574 +0200
+++ openssh-7.9p1/Makefile.in	2020-04-30 16:09:26.578161954 +0200
@@ -130,6 +130,7 @@
 	auth2-none.o auth2-passwd.o auth2-pubkey.o \
 	monitor.o monitor_wrap.o auth-krb5.o \
 	auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
+	gss-serv-gsi.o \
 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
 	sftp-server.o sftp-common.o \
 	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff -Nur openssh-7.9p1.orig/misc.c openssh-7.9p1/misc.c
--- openssh-7.9p1.orig/misc.c	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/misc.c	2020-04-30 16:09:26.582161805 +0200
@@ -238,11 +238,14 @@
 #define WHITESPACE " \t\r\n"
 #define QUOTE	"\""
 
+/* Characters considered as quotations. */
+#define QUOTES "'\""
+
 /* return next token in configuration line */
 static char *
 strdelim_internal(char **s, int split_equals)
 {
-	char *old;
+	char *old, *p, *q;
 	int wspace = 0;
 
 	if (*s == NULL)
@@ -250,6 +253,21 @@
 
 	old = *s;
 
+	if ((q=strchr(QUOTES, (int) *old)) && *q)
+	{
+		/* find next quote character, point old to start of quoted
+		 * string */
+		for (p = ++old; *p && *p != *q; p++)
+			;
+
+		/* find start of next token */
+		*s = (*p) ? p + strspn(p + 1, WHITESPACE) + 1 : NULL;
+
+		/* terminate 'old' token */
+		*p = '\0';
+		return (old);
+	}
+
 	*s = strpbrk(*s,
 	    split_equals ? WHITESPACE QUOTE "=" : WHITESPACE QUOTE);
 	if (*s == NULL)
@@ -325,6 +343,20 @@
 	return copy;
 }
 
+void
+pwfree(struct passwd *pw)
+{
+	free(pw->pw_name);
+	free(pw->pw_passwd);
+	free(pw->pw_gecos);
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+	free(pw->pw_class);
+#endif
+	free(pw->pw_dir);
+	free(pw->pw_shell);
+	free(pw);
+}
+
 /*
  * Convert ASCII string to TCP/IP port number.
  * Port must be >=0 and <=65535.
diff -Nur openssh-7.9p1.orig/misc.h openssh-7.9p1/misc.h
--- openssh-7.9p1.orig/misc.h	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/misc.h	2020-04-30 16:09:26.582161805 +0200
@@ -82,6 +82,7 @@
 void	 sock_set_v6only(int);
 
 struct passwd *pwcopy(struct passwd *);
+void pwfree(struct passwd *);
 const char *ssh_gai_strerror(int);
 
 typedef struct arglist arglist;
diff -Nur openssh-7.9p1.orig/monitor.c openssh-7.9p1/monitor.c
--- openssh-7.9p1.orig/monitor.c	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/monitor.c	2020-04-30 16:09:26.582161805 +0200
@@ -149,6 +149,9 @@
 int mm_answer_gss_userok(int, struct sshbuf *);
 int mm_answer_gss_checkmic(int, struct sshbuf *);
 int mm_answer_gss_sign(int, struct sshbuf *);
+int mm_answer_gss_error(int, struct sshbuf *);
+int mm_answer_gss_indicate_mechs(int, struct sshbuf *);
+int mm_answer_gss_localname(int, struct sshbuf *);
 int mm_answer_gss_updatecreds(int, struct sshbuf *);
 #endif
 
@@ -199,12 +202,12 @@
     {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli},
 #endif
     {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
-    {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
+    {MONITOR_REQ_PWNAM, MON_AUTH, mm_answer_pwnamallow},
     {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
     {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
     {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
 #ifdef USE_PAM
-    {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
+    {MONITOR_REQ_PAM_START, MON_ISAUTH, mm_answer_pam_start},
     {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
     {MONITOR_REQ_PAM_INIT_CTX, MON_ONCE, mm_answer_pam_init_ctx},
     {MONITOR_REQ_PAM_QUERY, 0, mm_answer_pam_query},
@@ -229,8 +232,11 @@
     {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx},
     {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
     {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
-    {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
+    {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
     {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error},
+    {MONITOR_REQ_GSSMECHS, MON_ISAUTH, mm_answer_gss_indicate_mechs},
+    {MONITOR_REQ_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname},
 #endif
     {0, 0, NULL}
 };
@@ -256,6 +262,8 @@
     {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
     {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
     {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error},
+    {MONITOR_REQ_GSSMECHS, 0, mm_answer_gss_indicate_mechs},
     {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
 #endif
     {0, 0, NULL}
@@ -319,6 +327,8 @@
 #ifdef GSSAPI
 	/* and for the GSSAPI key exchange */
 	monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1);
 #endif
 	/* The first few requests do not require asynchronous access */
 	while (!authenticated) {
@@ -434,6 +444,8 @@
 #ifdef GSSAPI
 	/* and for the GSSAPI key exchange */
 	monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1);
 #endif
 
 	if (auth_opts->permit_pty_flag) {
@@ -754,15 +766,18 @@
 
 	debug3("%s", __func__);
 
-	if (authctxt->attempt++ != 0)
-		fatal("%s: multiple attempts for getpwnam", __func__);
-
 	if ((r = sshbuf_get_cstring(m, &username, NULL)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 
 	pwent = getpwnamallow(username);
 
+	if (authctxt->user) free(authctxt->user);
 	authctxt->user = xstrdup(username);
+#ifdef USE_PAM
+	if (options.permit_pam_user_change)
+		setproctitle("%s [priv]", pwent ? "[pam]" : "unknown");
+	else
+#endif
 	setproctitle("%s [priv]", pwent ? username : "unknown");
 	free(username);
 
@@ -1981,6 +1996,79 @@
 }
 
 int
+mm_answer_gss_error(int socket, struct sshbuf *m)
+{
+	OM_uint32 major, minor;
+	char *msg;
+	int r;
+
+	msg=ssh_gssapi_last_error(gsscontext, &major, &minor);
+	sshbuf_reset(m);
+	if ((r = sshbuf_put_u32(m, major)) != 0 ||
+	    (r = sshbuf_put_u32(m, minor)) != 0 ||
+	    (r = sshbuf_put_cstring(m, msg)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	mm_request_send(socket, MONITOR_ANS_GSSERR, m);
+
+	free(msg);
+
+	return(0);
+}
+
+int
+mm_answer_gss_indicate_mechs(int socket, struct sshbuf *m)
+{
+	OM_uint32 major, minor;
+	gss_OID_set mech_set;
+	size_t i;
+	int r;
+
+	major=gss_indicate_mechs(&minor, &mech_set);
+
+	sshbuf_reset(m);
+	if ((r = sshbuf_put_u32(m, major)) != 0 ||
+	    (r = sshbuf_put_u32(m, mech_set->count)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	for (i = 0; i < mech_set->count; i++) {
+		if ((r = sshbuf_put_string(m, mech_set->elements[i].elements,
+		    mech_set->elements[i].length)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	}
+
+	gss_release_oid_set(&minor, &mech_set);
+
+	mm_request_send(socket, MONITOR_ANS_GSSMECHS, m);
+
+	return(0);
+}
+
+int
+mm_answer_gss_localname(int socket, struct sshbuf *m)
+{
+	char *name;
+	int r;
+
+	ssh_gssapi_localname(&name);
+
+	sshbuf_reset(m);
+	if (name) {
+		if ((r = sshbuf_put_cstring(m, name)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+		debug3("%s: sending result %s", __func__, name);
+		free(name);
+	} else {
+		if ((r = sshbuf_put_cstring(m, "")) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+		debug3("%s: sending result \"\"", __func__);
+	}
+
+	mm_request_send(socket, MONITOR_ANS_GSSLOCALNAME, m);
+
+	return(0);
+}
+
+int
 mm_answer_gss_sign(int socket, struct sshbuf *m)
 {
        gss_buffer_desc data;
@@ -2030,12 +2118,14 @@
        ssh_gssapi_ccache store;
        int ok, r;
 
-       if ((r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 ||
+	if ((r = sshbuf_get_cstring(m, &store.filename, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 ||
 	    (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0)
 	       fatal("%s: buffer error: %s", __func__, ssh_err(r));
 
        ok = ssh_gssapi_update_creds(&store);
 
+	free(store.filename);
        free(store.envvar);
        free(store.envval);
 
diff -Nur openssh-7.9p1.orig/monitor.h openssh-7.9p1/monitor.h
--- openssh-7.9p1.orig/monitor.h	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/monitor.h	2020-04-30 16:09:26.582161805 +0200
@@ -68,9 +68,11 @@
 	MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121,
 	MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123,
 	MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124,
-
-	MONITOR_REQ_GSSSIGN = 201, MONITOR_ANS_GSSSIGN = 202,
-	MONITOR_REQ_GSSUPCREDS = 203, MONITOR_ANS_GSSUPCREDS = 204,
+	MONITOR_REQ_GSSMECHS = 200, MONITOR_ANS_GSSMECHS = 201,
+	MONITOR_REQ_GSSLOCALNAME = 202, MONITOR_ANS_GSSLOCALNAME = 203,
+	MONITOR_REQ_GSSERR = 204, MONITOR_ANS_GSSERR = 205,
+	MONITOR_REQ_GSSSIGN = 206, MONITOR_ANS_GSSSIGN = 207,
+	MONITOR_REQ_GSSUPCREDS = 208, MONITOR_ANS_GSSUPCREDS = 208,
 
 };
 
diff -Nur openssh-7.9p1.orig/monitor_wrap.c openssh-7.9p1/monitor_wrap.c
--- openssh-7.9p1.orig/monitor_wrap.c	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/monitor_wrap.c	2020-04-30 16:09:26.582161805 +0200
@@ -1045,6 +1045,94 @@
 	return (authenticated);
 }
 
+char *
+mm_ssh_gssapi_last_error(Gssctxt *ctx, OM_uint32 *major, OM_uint32 *minor)
+{
+	struct sshbuf *m = NULL;
+	OM_uint32 maj,min;
+	char *errstr;
+	int r;
+
+	if ((m = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSERR, m);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSERR, m);
+
+	if ((r = sshbuf_get_u32(m, &maj)) != 0 ||
+	    (r = sshbuf_get_u32(m, &min)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	if (major) *major=maj;
+	if (minor) *minor=min;
+
+	if ((r = sshbuf_get_cstring(m, &errstr, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	sshbuf_free(m);
+
+	return(errstr);
+}
+
+OM_uint32
+mm_gss_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set)
+{
+	struct sshbuf *m = NULL;
+	OM_uint32 major,minor;
+	int count;
+	gss_OID_desc oid;
+	size_t length;
+	int r;
+
+	if ((m = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSMECHS, m);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSMECHS, m);
+	if ((r = sshbuf_get_u32(m, &major)) != 0 ||
+	    (r = sshbuf_get_u32(m, &count)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	gss_create_empty_oid_set(&minor, mech_set);
+	while(count-->0) {
+	    if ((r = sshbuf_get_string(m, (u_char **)&oid.elements, &length)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	    oid.length = length;
+	    gss_add_oid_set_member(&minor, &oid, mech_set);
+	}
+
+	sshbuf_free(m);
+
+	return(major);
+}
+
+int
+mm_ssh_gssapi_localname(char **lname)
+{
+	struct sshbuf *m = NULL;
+	int r;
+
+	if ((m = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);	
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSLOCALNAME, m);
+
+	debug3("%s: waiting for MONITOR_ANS_GSSLOCALNAME", __func__);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSLOCALNAME,
+	    m);
+
+	if ((r = sshbuf_get_cstring(m, lname, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	sshbuf_free(m);
+	if ((*lname == NULL) || (*lname[0] == '\0')) {
+		debug3("%s: gssapi identity mapping failed", __func__);
+	} else {
+		debug3("%s: gssapi identity mapped to %s", __func__, *lname);
+	}
+
+	return(0);
+}
+
 OM_uint32
 mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
 {
@@ -1078,7 +1166,8 @@
        if ((m = sshbuf_new()) == NULL)
 	    fatal("%s: sshbuf_new failed", __func__);
 
-       if ((r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 ||
+	if ((r = sshbuf_put_cstring(m, store->filename ? store->filename : "")) != 0 ||
+	    (r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 ||
 	    (r = sshbuf_put_cstring(m, store->envval ? store->envval : "")) != 0)
 	       fatal("%s: buffer error: %s", __func__, ssh_err(r));
 
diff -Nur openssh-7.9p1.orig/monitor_wrap.h openssh-7.9p1/monitor_wrap.h
--- openssh-7.9p1.orig/monitor_wrap.h	2020-04-30 16:10:35.175844311 +0200
+++ openssh-7.9p1/monitor_wrap.h	2020-04-30 16:09:26.582161805 +0200
@@ -65,6 +65,10 @@
 int mm_ssh_gssapi_userok(char *user, struct passwd *);
 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
 OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
+int mm_ssh_gssapi_localname(char **user);
+OM_uint32 mm_gss_indicate_mechs(OM_uint32 *minor_status,
+    gss_OID_set *mech_set);
+char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
 int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
 #endif
 
diff -Nur openssh-7.9p1.orig/readconf.c openssh-7.9p1/readconf.c
--- openssh-7.9p1.orig/readconf.c	2020-04-30 16:10:35.239842348 +0200
+++ openssh-7.9p1/readconf.c	2020-04-30 16:09:26.582161805 +0200
@@ -2043,13 +2043,13 @@
 	if (options->challenge_response_authentication == -1)
 		options->challenge_response_authentication = 1;
 	if (options->gss_authentication == -1)
-		options->gss_authentication = 0;
+		options->gss_authentication = 1;
 	if (options->gss_keyex == -1)
-		options->gss_keyex = 0;
+		options->gss_keyex = 1;
 	if (options->gss_deleg_creds == -1)
-		options->gss_deleg_creds = 0;
+		options->gss_deleg_creds = 1;
 	if (options->gss_trust_dns == -1)
-		options->gss_trust_dns = 0;
+		options->gss_trust_dns = 1;
 	if (options->gss_renewal_rekey == -1)
 		options->gss_renewal_rekey = 0;
 	if (options->password_authentication == -1)
diff -Nur openssh-7.9p1.orig/readconf.h openssh-7.9p1/readconf.h
--- openssh-7.9p1.orig/readconf.h	2020-04-30 16:10:35.163844679 +0200
+++ openssh-7.9p1/readconf.h	2020-04-30 16:09:26.586161658 +0200
@@ -77,6 +77,8 @@
 	char   *host_key_alias;	/* hostname alias for .ssh/known_hosts */
 	char   *proxy_command;	/* Proxy command for connecting the host. */
 	char   *user;		/* User to log in as. */
+	int     implicit;	/* Login user was not specified.
+				   Server may choose based on authctxt. */
 	int     escape_char;	/* Escape character; -2 = none */
 
 	u_int	num_system_hostfiles;	/* Paths for /etc/ssh/ssh_known_hosts */
diff -Nur openssh-7.9p1.orig/servconf.c openssh-7.9p1/servconf.c
--- openssh-7.9p1.orig/servconf.c	2020-04-30 16:10:35.239842348 +0200
+++ openssh-7.9p1/servconf.c	2020-04-30 16:09:26.586161658 +0200
@@ -84,6 +84,7 @@
 
 	/* Portable-specific options */
 	options->use_pam = -1;
+	options->permit_pam_user_change = -1;
 	options->use_pam_check_locks = -1;
 
 	/* Standard Options */
@@ -126,9 +127,11 @@
 	options->kerberos_ticket_cleanup = -1;
 	options->kerberos_get_afs_token = -1;
 	options->gss_authentication=-1;
+	options->gss_deleg_creds = -1;
 	options->gss_keyex = -1;
 	options->gss_cleanup_creds = -1;
 	options->gss_strict_acceptor = -1;
+	options->gsi_allow_limited_proxy = -1;
 	options->gss_store_rekey = -1;
 	options->password_authentication = -1;
 	options->kbd_interactive_authentication = -1;
@@ -286,6 +289,8 @@
 	/* Portable-specific options */
 	if (options->use_pam == -1)
 		options->use_pam = 0;
+	if (options->permit_pam_user_change == -1)
+		options->permit_pam_user_change = 0;
 	if (options->use_pam_check_locks == -1)
 		options->use_pam_check_locks = 0;
 
@@ -361,13 +366,17 @@
 	if (options->kerberos_get_afs_token == -1)
 		options->kerberos_get_afs_token = 0;
 	if (options->gss_authentication == -1)
-		options->gss_authentication = 0;
+		options->gss_authentication = 1;
+	if (options->gss_deleg_creds == -1)
+		options->gss_deleg_creds = 1;
 	if (options->gss_keyex == -1)
-		options->gss_keyex = 0;
+		options->gss_keyex = 1;
 	if (options->gss_cleanup_creds == -1)
 		options->gss_cleanup_creds = 1;
 	if (options->gss_strict_acceptor == -1)
 		options->gss_strict_acceptor = 1;
+	if (options->gsi_allow_limited_proxy == -1)
+		options->gsi_allow_limited_proxy = 0;
 	if (options->gss_store_rekey == -1)
 		options->gss_store_rekey = 0;
 	if (options->password_authentication == -1)
@@ -494,7 +503,7 @@
 typedef enum {
 	sBadOption,		/* == unknown option */
 	/* Portable-specific options */
-	sUsePAM, sUsePAMChecklocks,
+	sUsePAM, sPermitPAMUserChange, sUsePAMChecklocks,
 	/* Standard Options */
 	sPort, sHostKeyFile, sLoginGraceTime,
 	sPermitRootLogin, sLogFacility, sLogLevel,
@@ -515,6 +524,9 @@
 	sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes,
 	sHostKeyAlgorithms,
 	sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
+	sGssDelegateCreds,
+	sGssCredsPath,
+	sGsiAllowLimitedProxy,
 	sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
 	sGssKeyEx, sGssStoreRekey,
 	sAcceptEnv, sSetEnv, sPermitTunnel,
@@ -545,6 +557,7 @@
 	/* Portable-specific options */
 #ifdef USE_PAM
 	{ "usepam", sUsePAM, SSHCFG_GLOBAL },
+	{ "permitpamuserchange", sPermitPAMUserChange, SSHCFG_GLOBAL },
 	{ "usepamchecklocks", sUsePAMChecklocks, SSHCFG_GLOBAL },
 #else
 	{ "usepam", sUnsupported, SSHCFG_GLOBAL },
@@ -592,13 +605,23 @@
 	{ "afstokenpassing", sUnsupported, SSHCFG_GLOBAL },
 #ifdef GSSAPI
 	{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
+	{ "gssapidelegatecredentials", sGssDelegateCreds, SSHCFG_ALL },
 	{ "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
+	{ "gssapicredentialspath", sGssCredsPath, SSHCFG_GLOBAL },
+#ifdef GSI
+	{ "gsiallowlimitedproxy", sGsiAllowLimitedProxy, SSHCFG_GLOBAL },
+#else
+	{ "gsiallowlimitedproxy", sUnsupported, SSHCFG_GLOBAL },
+#endif
 	{ "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
 	{ "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
 	{ "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
 #else
 	{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
+	{ "gssapidelegatecredentials", sUnsupported, SSHCFG_ALL },
 	{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
+	{ "gssapicredentialspath", sUnsupported, SSHCFG_GLOBAL },
+	{ "gsiallowlimitedproxy", sUnsupported, SSHCFG_GLOBAL },
 	{ "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
 	{ "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
 	{ "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
@@ -665,6 +688,8 @@
 	{ "permitlisten", sPermitListen, SSHCFG_ALL },
 	{ "forcecommand", sForceCommand, SSHCFG_ALL },
 	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
+	{ "disableusagestats", sUnsupported, SSHCFG_GLOBAL},
+	{ "usagestatstargets", sUnsupported, SSHCFG_GLOBAL},
 	{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
 	{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
 	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
@@ -1299,6 +1324,10 @@
 		intptr = &options->use_pam_check_locks;
 		goto parse_flag;
 
+	case sPermitPAMUserChange:
+		intptr = &options->permit_pam_user_change;
+		goto parse_flag;
+
 	/* Standard Options */
 	case sBadOption:
 		return -1;
@@ -1511,6 +1540,10 @@
 		intptr = &options->gss_authentication;
 		goto parse_flag;
 
+	case sGssDelegateCreds:
+		intptr = &options->gss_deleg_creds;
+		goto parse_flag;
+
 	case sGssKeyEx:
 		intptr = &options->gss_keyex;
 		goto parse_flag;
@@ -1519,6 +1552,10 @@
 		intptr = &options->gss_cleanup_creds;
 		goto parse_flag;
 
+	case sGssCredsPath:
+		charptr = &options->gss_creds_path;
+		goto parse_filename;
+
 	case sGssStrictAcceptor:
 		intptr = &options->gss_strict_acceptor;
 		goto parse_flag;
@@ -1527,6 +1564,12 @@
 		intptr = &options->gss_store_rekey;
 		goto parse_flag;
 
+#ifdef GSI
+	case sGsiAllowLimitedProxy:
+		intptr = &options->gsi_allow_limited_proxy;
+		goto parse_flag;
+#endif
+
 	case sPasswordAuthentication:
 		intptr = &options->password_authentication;
 		goto parse_flag;
@@ -2316,6 +2359,7 @@
 
 	M_CP_INTOPT(password_authentication);
 	M_CP_INTOPT(gss_authentication);
+	M_CP_INTOPT(gss_deleg_creds);
 	M_CP_INTOPT(gss_keyex);
 	M_CP_INTOPT(gss_cleanup_creds);
 	M_CP_INTOPT(gss_strict_acceptor);
diff -Nur openssh-7.9p1.orig/servconf.h openssh-7.9p1/servconf.h
--- openssh-7.9p1.orig/servconf.h	2020-04-30 16:10:35.167844556 +0200
+++ openssh-7.9p1/servconf.h	2020-04-30 16:09:26.586161658 +0200
@@ -126,9 +126,12 @@
 						 * file on logout. */
 	int     kerberos_get_afs_token;		/* If true, try to get AFS token if
 						 * authenticated with Kerberos. */
+	int     gsi_allow_limited_proxy;	/* If true, accept limited proxies */
 	int     gss_authentication;	/* If true, permit GSSAPI authentication */
+	int     gss_deleg_creds;	/* If true, store delegated GSSAPI credentials*/
 	int     gss_keyex;		/* If true, permit GSSAPI key exchange */
 	int     gss_cleanup_creds;	/* If true, destroy cred cache on logout */
+	char   *gss_creds_path;		/* Use non-default credentials path */
 	int     gss_strict_acceptor;	/* If true, restrict the GSSAPI acceptor name */
 	int 	gss_store_rekey;
 	int     password_authentication;	/* If true, permit password
@@ -186,6 +189,7 @@
 	char   *adm_forced_command;
 
 	int	use_pam;		/* Enable auth via PAM */
+	int	permit_pam_user_change;	/* Allow PAM to change user name */
 	int	use_pam_check_locks;	/* internally check for locked accounts even when using PAM */
 
 	int	permit_tun;
diff -Nur openssh-7.9p1.orig/ssh.1 openssh-7.9p1/ssh.1
--- openssh-7.9p1.orig/ssh.1	2020-04-30 16:10:35.151845046 +0200
+++ openssh-7.9p1/ssh.1	2020-04-30 16:09:26.586161658 +0200
@@ -1428,6 +1428,18 @@
 on to new connections).
 .It Ev USER
 Set to the name of the user logging in.
+.It Ev X509_CERT_DIR
+Used for GSI authentication. Specifies a non-standard location for the
+CA certificates directory.
+.It Ev X509_USER_CERT
+Used for GSI authentication. Specifies a non-standard location for the
+certificate to be used for authentication to the server.
+.It Ev X509_USER_KEY
+Used for GSI authentication. Specifies a non-standard location for the
+private key to be used for authentication to the server.
+.It Ev X509_USER_PROXY
+Used for GSI authentication. Specifies a non-standard location for the
+proxy credential to be used for authentication to the server.
 .El
 .Pp
 Additionally,
diff -Nur openssh-7.9p1.orig/ssh.c openssh-7.9p1/ssh.c
--- openssh-7.9p1.orig/ssh.c	2020-04-30 16:10:35.147845169 +0200
+++ openssh-7.9p1/ssh.c	2020-04-30 16:09:26.586161658 +0200
@@ -541,6 +541,38 @@
 			fatal("Can't open user config file %.100s: "
 			    "%.100s", config, strerror(errno));
 	} else {
+	    /*
+	     * Since the config file parsing code aborts if it sees
+	     * options it doesn't recognize, allow users to put
+	     * options specific to compile-time add-ons in alternate
+	     * config files so their primary config file will
+	     * interoperate SSH versions that don't support those
+	     * options.
+	     */
+#ifdef GSSAPI
+		r = snprintf(buf, sizeof buf, "%s/%s.gssapi", pw->pw_dir,
+		    _PATH_SSH_USER_CONFFILE);
+		if (r > 0 && (size_t)r < sizeof(buf))
+			(void)read_config_file(buf, pw, host, host_arg,
+			    &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
+			    (final_pass ? SSHCONF_FINAL : 0), want_final_pass);
+#ifdef GSI
+		r = snprintf(buf, sizeof buf, "%s/%s.gsi", pw->pw_dir,
+		    _PATH_SSH_USER_CONFFILE);
+		if (r > 0 && (size_t)r < sizeof(buf))
+			(void)read_config_file(buf, pw, host, host_arg,
+			    &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
+			    (final_pass ? SSHCONF_FINAL : 0), want_final_pass);
+#endif
+#if defined(KRB5)
+		r = snprintf(buf, sizeof buf, "%s/%s.krb", pw->pw_dir,
+		    _PATH_SSH_USER_CONFFILE);
+		if (r > 0 && (size_t)r < sizeof(buf))
+			(void)read_config_file(buf, pw, host, host_arg,
+			    &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
+			    (final_pass ? SSHCONF_FINAL : 0), want_final_pass);
+#endif
+#endif
 		r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
 		    _PATH_SSH_USER_CONFFILE);
 		if (r > 0 && (size_t)r < sizeof(buf))
@@ -1272,8 +1304,12 @@
 
 	seed_rng();
 
-	if (options.user == NULL)
+	if (options.user == NULL) {
 		options.user = xstrdup(pw->pw_name);
+		options.implicit = 1;
+	} else {
+		options.implicit = 0;
+	}
 
 	/* Set up strings used to percent_expand() arguments */
 	if (gethostname(thishost, sizeof(thishost)) == -1)
diff -Nur openssh-7.9p1.orig/ssh_config openssh-7.9p1/ssh_config
--- openssh-7.9p1.orig/ssh_config	2020-04-30 16:10:35.167844556 +0200
+++ openssh-7.9p1/ssh_config	2020-04-30 16:09:26.586161658 +0200
@@ -38,10 +38,10 @@
 
 #   PasswordAuthentication yes
 #   HostbasedAuthentication no
-#   GSSAPIAuthentication no
-#   GSSAPIDelegateCredentials no
-#   GSSAPIKeyExchange no
-#   GSSAPITrustDNS no
+#   GSSAPIAuthentication yes
+#   GSSAPIDelegateCredentials yes
+#   GSSAPIKeyExchange yes
+#   GSSAPITrustDNS yes
 #   BatchMode no
 #   CheckHostIP yes
 #   AddressFamily any
diff -Nur openssh-7.9p1.orig/ssh_config.5 openssh-7.9p1/ssh_config.5
--- openssh-7.9p1.orig/ssh_config.5	2020-04-30 16:10:35.239842348 +0200
+++ openssh-7.9p1/ssh_config.5	2020-04-30 16:09:26.590161510 +0200
@@ -52,6 +52,12 @@
 user's configuration file
 .Pq Pa ~/.ssh/config
 .It
+GSSAPI configuration file
+.Pq Pa $HOME/.ssh/config.gssapi
+.It
+Kerberos configuration file
+.Pq Pa $HOME/.ssh/config.krb
+.It
 system-wide configuration file
 .Pq Pa /etc/ssh/ssh_config
 .El
@@ -737,7 +743,7 @@
 .It Cm GSSAPIAuthentication
 Specifies whether user authentication based on GSSAPI is allowed.
 The default is
-.Cm no .
+.Cm yes .
 .It Cm GSSAPIClientIdentity
 If set, specifies the GSSAPI client identity that ssh should use when
 connecting to the server. The default is unset, which means that the default
@@ -745,12 +751,12 @@
 .It Cm GSSAPIDelegateCredentials
 Forward (delegate) credentials to the server.
 The default is
-.Cm no .
+.Cm yes .
 .It Cm GSSAPIKeyExchange
 Specifies whether key exchange based on GSSAPI may be used. When using
 GSSAPI key exchange the server need not have a host key.
 The default is
-.Dq no .
+.Dq yes .
 .It Cm GSSAPIRenewalForcesRekey
 If set to
 .Dq yes
@@ -771,7 +777,7 @@
 .Dq no, the hostname entered on the
 command line will be passed untouched to the GSSAPI library.
 The default is
-.Dq no .
+.Dq yes .
 .It Cm HashKnownHosts
 Indicates that
 .Xr ssh 1
@@ -1209,7 +1215,7 @@
 .Cm password ) .
 The default is:
 .Bd -literal -offset indent
-gssapi-with-mic,hostbased,publickey,
+gssapi-keyex,gssapi-with-mic,hostbased,publickey,
 keyboard-interactive,password
 .Ed
 .It Cm ProxyCommand
diff -Nur openssh-7.9p1.orig/sshconnect2.c openssh-7.9p1/sshconnect2.c
--- openssh-7.9p1.orig/sshconnect2.c	2020-04-30 16:10:35.167844556 +0200
+++ openssh-7.9p1/sshconnect2.c	2020-04-30 16:09:26.590161510 +0200
@@ -871,6 +871,11 @@
 	int r, ok = 0;
 	const char *gss_host;
 
+	if (!options.gss_authentication) {
+		verbose("GSSAPI authentication disabled.");
+		return 0;
+	}
+
 if (options.gss_server_identity)
 	       gss_host = options.gss_server_identity;
        else if (options.gss_trust_dns)
@@ -961,7 +966,8 @@
 
 	if (status == GSS_S_COMPLETE) {
 		/* send either complete or MIC, depending on mechanism */
-		if (!(flags & GSS_C_INTEG_FLAG)) {
+		if (strcmp(authctxt->method->name, "gssapi") == 0 ||
+		    !(flags & GSS_C_INTEG_FLAG)) {
 			if ((r = sshpkt_start(ssh,
 			    SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)) != 0 ||
 			    (r = sshpkt_send(ssh)) != 0)
@@ -1131,6 +1137,20 @@
 	return r;
 }
 
+#ifdef GSI
+extern
+const gss_OID_desc * const gss_mech_globus_gssapi_openssl;
+extern
+const gss_OID_desc * const gss_mech_globus_gssapi_openssl_micv2;
+#define is_gsi_oid(oid) \
+  ((oid->length == gss_mech_globus_gssapi_openssl->length && \
+    (memcmp(oid->elements, gss_mech_globus_gssapi_openssl->elements, \
+	    oid->length) == 0)) || \
+   (oid->length == gss_mech_globus_gssapi_openssl_micv2->length && \
+    (memcmp(oid->elements, gss_mech_globus_gssapi_openssl_micv2->elements, \
+	    oid->length) == 0)))
+#endif
+
 int
 userauth_gsskeyex(Authctxt *authctxt)
 {
@@ -1151,6 +1171,11 @@
        if ((b = sshbuf_new()) == NULL)
                fatal("%s: sshbuf_new failed", __func__);
 
+#ifdef GSI
+	if (options.implicit && is_gsi_oid(gss_kex_context->oid))
+		ssh_gssapi_buildmic(b, "", authctxt->service, "gssapi-keyex");
+	else
+#endif
        ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
 	   "gssapi-keyex");
 
@@ -1163,7 +1188,13 @@
        }
 
 	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+#ifdef GSI
+	if (options.implicit && is_gsi_oid(gss_kex_context->oid))
+		packet_put_cstring("");
+	else
+#endif
        packet_put_cstring(authctxt->server_user);
+
        packet_put_cstring(authctxt->service);
        packet_put_cstring(authctxt->method->name);
        packet_put_string(mic.value, mic.length);
diff -Nur openssh-7.9p1.orig/sshd.8 openssh-7.9p1/sshd.8
--- openssh-7.9p1.orig/sshd.8	2020-04-30 16:10:35.151845046 +0200
+++ openssh-7.9p1/sshd.8	2020-04-30 16:09:26.590161510 +0200
@@ -787,6 +787,29 @@
 # A CA key, accepted for any host in *.mydomain.com or *.mydomain.org
 @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W...
 .Ed
+.Sh ENVIRONMENT
+.Nm
+will normally set the following environment variables:
+.Bl -tag -width "SSH_ORIGINAL_COMMAND"
+.It Ev GRIDMAP
+Applies to GSI authentication/authorization. Specifies the location of the
+gridmapfile. If not specified, the gridmap file is assumed to be available at
+/etc/grid-security/grid-mapfile for services running as root and at
+HOME/.gridmap for services running as non-root where HOME is the home directory
+of the effective user from the password file entry.
+.It Ev X509_CERT_DIR
+Used for GSI authentication. Specifies a non-standard location for the
+CA certificates directory.
+.It Ev X509_USER_CERT
+Used for GSI authentication. Specifies a non-standard location for the
+certificate to be used for authentication to the client.
+.It Ev X509_USER_KEY
+Used for GSI authentication. Specifies a non-standard location for the
+private key to be used for authentication to the client.
+.It Ev X509_USER_PROXY
+Used for GSI authentication. Specifies a non-standard location for the
+proxy credential to be used for authentication to the client.
+.El
 .Sh FILES
 .Bl -tag -width Ds -compact
 .It Pa ~/.hushlogin
diff -Nur openssh-7.9p1.orig/sshd.c openssh-7.9p1/sshd.c
--- openssh-7.9p1.orig/sshd.c	2020-04-30 16:10:35.235842470 +0200
+++ openssh-7.9p1/sshd.c	2020-04-30 16:09:26.590161510 +0200
@@ -2447,7 +2447,7 @@
 #endif
 
 #ifdef GSSAPI
-	if (options.gss_authentication) {
+	if (options.gss_authentication && options.gss_deleg_creds) {
 		temporarily_use_uid(authctxt->pw);
 		ssh_gssapi_storecreds();
 		restore_uid();
diff -Nur openssh-7.9p1.orig/sshd_config openssh-7.9p1/sshd_config
--- openssh-7.9p1.orig/sshd_config	2020-04-30 16:10:35.167844556 +0200
+++ openssh-7.9p1/sshd_config	2020-04-30 16:09:26.590161510 +0200
@@ -67,10 +67,11 @@
 #KerberosGetAFSToken no
 
 # GSSAPI options
-#GSSAPIAuthentication no
+#GSSAPIAuthentication yes
+#GSSAPIDelegateCredentials yes
 #GSSAPICleanupCredentials yes
 #GSSAPIStrictAcceptorCheck yes
-#GSSAPIKeyExchange no
+#GSSAPIKeyExchange yes
 
 # Set this to 'yes' to enable PAM authentication, account processing,
 # and session processing. If this is enabled, PAM authentication will
@@ -83,6 +84,10 @@
 # and ChallengeResponseAuthentication to 'no'.
 UsePAM yes
 
+# Set to 'yes' to allow the PAM stack to change the user name during
+# calls to authentication
+#PermitPAMUserChange no
+
 #AllowAgentForwarding yes
 #AllowTcpForwarding yes
 #GatewayPorts no
diff -Nur openssh-7.9p1.orig/sshd_config.5 openssh-7.9p1/sshd_config.5
--- openssh-7.9p1.orig/sshd_config.5	2020-04-30 16:10:35.239842348 +0200
+++ openssh-7.9p1/sshd_config.5	2020-04-30 16:09:26.590161510 +0200
@@ -646,20 +646,39 @@
 to allow the client to select the address to which the forwarding is bound.
 The default is
 .Cm no .
+.It Cm GSIAllowLimitedProxy
+Specifies whether to accept limited proxy credentials for authentication.
+The default is
+.Cm no .
 .It Cm GSSAPIAuthentication
 Specifies whether user authentication based on GSSAPI is allowed.
 The default is
-.Cm no .
+.Cm yes .
 .It Cm GSSAPICleanupCredentials
 Specifies whether to automatically destroy the user's credentials cache
 on logout.
 The default is
 .Cm yes .
+.It Cm GSSAPICredentialsPath
+If specified, the delegated GSSAPI credential is stored in the
+given path, overwriting any existing credentials.
+Paths can be specified with syntax similar to the AuthorizedKeysFile
+option (i.e., accepting %h and %u tokens).
+When using this option,
+setting 'GssapiCleanupCredentials no' is recommended,
+so logging out of one session
+doesn't remove the credentials in use by another session of
+the same user.
+Currently only implemented for the GSI mechanism.
+.It Cm GSSAPIDelegateCredentials
+Specifies whether delegated credentials are stored in the user's environment.
+The default is
+.Cm yes .
 .It Cm GSSAPIKeyExchange
 Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
 doesn't rely on ssh keys to verify host identity.
 The default is
-.Dq no .
+.Dq yes .
 .It Cm GSSAPIStrictAcceptorCheck
 Determines whether to be strict about the identity of the GSSAPI acceptor
 a client authenticates against.
@@ -1634,6 +1653,12 @@
 as a non-root user.
 The default is
 .Cm no .
+.It Cm PermitPAMUserChange
+If set to
+.Cm yes
+this will enable PAM authentication to change the name of the user being
+authenticated.  The default is
+.Cm no .
 .It Cm UsePAMCheckLocks
 When set to
 .Dq yes
diff -Nur openssh-7.9p1.orig/ssh-gss.h openssh-7.9p1/ssh-gss.h
--- openssh-7.9p1.orig/ssh-gss.h	2020-04-30 16:10:35.167844556 +0200
+++ openssh-7.9p1/ssh-gss.h	2020-04-30 16:09:26.594161361 +0200
@@ -81,12 +81,14 @@
 } ssh_gssapi_ccache;
 
 typedef struct {
+	gss_OID_desc oid;
 	gss_buffer_desc displayname;
 	gss_buffer_desc exportedname;
 	gss_cred_id_t creds;
-	gss_name_t name;
+	gss_name_t cred_name, ctx_name;
 	struct ssh_gssapi_mech_struct *mech;
 	ssh_gssapi_ccache store;
+	gss_ctx_id_t context; /* needed for globus_gss_assist_map_and_authorize() */
 	int used;
 	int updated;
 } ssh_gssapi_client;
@@ -98,7 +100,7 @@
 	int (*dochild) (ssh_gssapi_client *);
 	int (*userok) (ssh_gssapi_client *, char *);
 	int (*localname) (ssh_gssapi_client *, char **);
-	void (*storecreds) (ssh_gssapi_client *);
+	int (*storecreds) (ssh_gssapi_client *);
 	int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
 } ssh_gssapi_mech;
 
@@ -107,7 +109,7 @@
 	OM_uint32	minor; /* both */
 	gss_ctx_id_t	context; /* both */
 	gss_name_t	name; /* both */
-	gss_OID		oid; /* client */
+	gss_OID		oid; /* both */
 	gss_cred_id_t	creds; /* server */
 	gss_name_t	client; /* server */
 	gss_cred_id_t	client_creds; /* both */
@@ -144,6 +146,9 @@
 OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
 int ssh_gssapi_credentials_updated(Gssctxt *);
 
+int ssh_gssapi_localname(char **name);
+void ssh_gssapi_rekey_creds();
+
 /* In the server */
 typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, const char *);
 char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
@@ -156,7 +161,7 @@
 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
 void ssh_gssapi_do_child(char ***, u_int *);
 void ssh_gssapi_cleanup_creds(void);
-void ssh_gssapi_storecreds(void);
+int ssh_gssapi_storecreds(void);
 const char *ssh_gssapi_displayname(void);
 
 char *ssh_gssapi_server_mechanisms(void);
diff -Nur openssh-7.9p1.orig/version.h openssh-7.9p1/version.h
--- openssh-7.9p1.orig/version.h	2018-10-17 02:01:20.000000000 +0200
+++ openssh-7.9p1/version.h	2020-04-30 16:09:26.594161361 +0200
@@ -2,5 +2,19 @@
 
 #define SSH_VERSION	"OpenSSH_7.9"
 
+#ifdef GSI
+#define GSI_VERSION	" GSI"
+#else
+#define GSI_VERSION	""
+#endif
+
+#ifdef KRB5
+#define KRB5_VERSION	" KRB5"
+#else
+#define KRB5_VERSION	""
+#endif
+
 #define SSH_PORTABLE	"p1"
-#define SSH_RELEASE	SSH_VERSION SSH_PORTABLE
+#define GSI_PORTABLE	"b-GSI"
+#define SSH_RELEASE	SSH_VERSION SSH_PORTABLE GSI_PORTABLE \
+			GSI_VERSION KRB5_VERSION
openSUSE Build Service is sponsored by