Zoltan Fridrich 5c3160
diff --color -ruNp a/auth2.c b/auth2.c
Zoltan Fridrich 5c3160
--- a/auth2.c	2024-08-28 12:35:01.189659493 +0200
Zoltan Fridrich 5c3160
+++ b/auth2.c	2024-08-28 12:35:41.246432045 +0200
Zoltan Fridrich 5c3160
@@ -71,6 +71,7 @@ extern Authmethod method_passwd;
Zoltan Fridrich 5c3160
 extern Authmethod method_kbdint;
Zoltan Fridrich 5c3160
 extern Authmethod method_hostbased;
Zoltan Fridrich 5c3160
 #ifdef GSSAPI
Zoltan Fridrich 5c3160
+extern Authmethod method_gsskeyex;
Zoltan Fridrich 5c3160
 extern Authmethod method_gssapi;
Zoltan Fridrich 5c3160
 #endif
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -78,6 +79,7 @@ Authmethod *authmethods[] = {
Zoltan Fridrich 5c3160
 	&method_none,
Zoltan Fridrich 5c3160
 	&method_pubkey,
Zoltan Fridrich 5c3160
 #ifdef GSSAPI
Zoltan Fridrich 5c3160
+	&method_gsskeyex,
Zoltan Fridrich 5c3160
 	&method_gssapi,
Zoltan Fridrich 5c3160
 #endif
Zoltan Fridrich 5c3160
 	&method_passwd,
Zoltan Fridrich 5c3160
diff --color -ruNp a/auth2-gss.c b/auth2-gss.c
Zoltan Fridrich 5c3160
--- a/auth2-gss.c	2024-08-28 12:35:01.189659493 +0200
Zoltan Fridrich 5c3160
+++ b/auth2-gss.c	2024-08-28 12:35:41.265432411 +0200
Zoltan Fridrich 5c3160
@@ -51,6 +51,7 @@
Zoltan Fridrich 5c3160
 #define SSH_GSSAPI_MAX_MECHS	2048
Dmitry Belyavskiy f561c6
 
Zoltan Fridrich 5c3160
 extern ServerOptions options;
Zoltan Fridrich 5c3160
+extern struct authmethod_cfg methodcfg_gsskeyex;
Zoltan Fridrich 5c3160
 extern struct authmethod_cfg methodcfg_gssapi;
Dmitry Belyavskiy f561c6
 
Zoltan Fridrich 5c3160
 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
Zoltan Fridrich 5c3160
@@ -59,6 +60,48 @@ static int input_gssapi_exchange_complet
Jakub Jelen def1de
 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 /*
Jakub Jelen def1de
+ * The 'gssapi_keyex' userauth mechanism.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+static int
c04e46
+userauth_gsskeyex(struct ssh *ssh, const char *method)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	Authctxt *authctxt = ssh->authctxt;
Jakub Jelen def1de
+	int r, authenticated = 0;
Jakub Jelen def1de
+	struct sshbuf *b = NULL;
Jakub Jelen def1de
+	gss_buffer_desc mic, gssbuf;
Jakub Jelen def1de
+	u_char *p;
Jakub Jelen def1de
+	size_t len;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "parsing");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((b = sshbuf_new()) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_new failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	mic.value = p;
Jakub Jelen def1de
+	mic.length = len;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
Jakub Jelen 25c16c
+	    "gssapi-keyex", ssh->kex->session_id);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_mutable_ptr failed");
Jakub Jelen def1de
+	gssbuf.length = sshbuf_len(b);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* gss_kex_context is NULL with privsep, so we can't check it here */
Dmitry Belyavskiy 089d79
+	if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gss_kex_context,
Dmitry Belyavskiy 089d79
+	    &gssbuf, &mic)))
Dmitry Belyavskiy 089d79
+		authenticated = mm_ssh_gssapi_userok(authctxt->user,
Dmitry Belyavskiy 089d79
+		    authctxt->pw, 1);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_free(b);
Jakub Jelen def1de
+	free(mic.value);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (authenticated);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Zoltan Fridrich 5c3160
+/*
Jakub Jelen def1de
  * We only support those mechanisms that we know about (ie ones that we know
Jakub Jelen def1de
  * how to check local user kuserok and the like)
Zoltan Fridrich 5c3160
  */
Zoltan Fridrich 5c3160
@@ -267,7 +310,7 @@ input_gssapi_exchange_complete(int type,
Jakub Jelen def1de
 	if ((r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen 25c16c
 		fatal_fr(r, "parse packet");
Jakub Jelen def1de
 
Dmitry Belyavskiy 089d79
-	authenticated = mm_ssh_gssapi_userok(authctxt->user);
Dmitry Belyavskiy 089d79
+ 	authenticated = mm_ssh_gssapi_userok(authctxt->user, authctxt->pw, 1);
Jakub Jelen def1de
 
Dmitry Belyavskiy 089d79
 	authctxt->postponed = 0;
Dmitry Belyavskiy 089d79
 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
Zoltan Fridrich 5c3160
@@ -315,7 +358,7 @@ input_gssapi_mic(int type, u_int32_t ple
Jakub Jelen def1de
 	gssbuf.length = sshbuf_len(b);
Jakub Jelen def1de
 
Dmitry Belyavskiy 089d79
 	if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))
Dmitry Belyavskiy 089d79
-		authenticated = mm_ssh_gssapi_userok(authctxt->user);
Dmitry Belyavskiy 089d79
+		authenticated = mm_ssh_gssapi_userok(authctxt->user, authctxt->pw, 0);
Jakub Jelen def1de
 	else
Jakub Jelen def1de
 		logit("GSSAPI MIC check failed");
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -333,6 +376,11 @@ input_gssapi_mic(int type, u_int32_t ple
Jakub Jelen def1de
 	return 0;
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Dmitry Belyavskiy 089d79
+Authmethod method_gsskeyex = {
Dmitry Belyavskiy 089d79
+	&methodcfg_gsskeyex,
Dmitry Belyavskiy 089d79
+	userauth_gsskeyex,
Dmitry Belyavskiy 089d79
+};
Dmitry Belyavskiy 089d79
+
Jakub Jelen def1de
 Authmethod method_gssapi = {
Dmitry Belyavskiy 089d79
 	&methodcfg_gssapi,
Dmitry Belyavskiy 089d79
 	userauth_gssapi,
Zoltan Fridrich 5c3160
diff --color -ruNp a/auth2-methods.c b/auth2-methods.c
Zoltan Fridrich 5c3160
--- a/auth2-methods.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/auth2-methods.c	2024-08-28 12:35:41.265432411 +0200
Zoltan Fridrich 5c3160
@@ -50,6 +50,11 @@ struct authmethod_cfg methodcfg_pubkey =
Zoltan Fridrich 5c3160
 	&options.pubkey_authentication
Zoltan Fridrich 5c3160
 };
Jakub Jelen def1de
 #ifdef GSSAPI
Zoltan Fridrich 5c3160
+struct authmethod_cfg methodcfg_gsskeyex = {
Zoltan Fridrich 5c3160
+	"gssapi-keyex",
Zoltan Fridrich 5c3160
+	NULL,
Zoltan Fridrich 5c3160
+	&options.gss_authentication
Zoltan Fridrich 5c3160
+};
Zoltan Fridrich 5c3160
 struct authmethod_cfg methodcfg_gssapi = {
Zoltan Fridrich 5c3160
 	"gssapi-with-mic",
Zoltan Fridrich 5c3160
 	NULL,
Zoltan Fridrich 5c3160
@@ -76,6 +81,7 @@ static struct authmethod_cfg *authmethod
Zoltan Fridrich 5c3160
 	&methodcfg_none,
Zoltan Fridrich 5c3160
 	&methodcfg_pubkey,
Jakub Jelen def1de
 #ifdef GSSAPI
Zoltan Fridrich 5c3160
+	&methodcfg_gsskeyex,
Zoltan Fridrich 5c3160
 	&methodcfg_gssapi,
Jakub Jelen def1de
 #endif
Zoltan Fridrich 5c3160
 	&methodcfg_passwd,
Zoltan Fridrich 5c3160
diff --color -ruNp a/auth.c b/auth.c
Zoltan Fridrich 5c3160
--- a/auth.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/auth.c	2024-08-28 12:35:41.245432026 +0200
Zoltan Fridrich 5c3160
@@ -356,7 +356,8 @@ auth_root_allowed(struct ssh *ssh, const
Zoltan Fridrich 5c3160
 	case PERMIT_NO_PASSWD:
Zoltan Fridrich 5c3160
 		if (strcmp(method, "publickey") == 0 ||
Zoltan Fridrich 5c3160
 		    strcmp(method, "hostbased") == 0 ||
Zoltan Fridrich 5c3160
-		    strcmp(method, "gssapi-with-mic") == 0)
Zoltan Fridrich 5c3160
+		    strcmp(method, "gssapi-with-mic") == 0 ||
Zoltan Fridrich 5c3160
+		    strcmp(method, "gssapi-keyex") == 0)
Zoltan Fridrich 5c3160
 			return 1;
Zoltan Fridrich 5c3160
 		break;
Zoltan Fridrich 5c3160
 	case PERMIT_FORCED_ONLY:
Zoltan Fridrich 5c3160
diff --color -ruNp a/canohost.c b/canohost.c
Zoltan Fridrich 5c3160
--- a/canohost.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/canohost.c	2024-08-28 12:35:41.246432045 +0200
Jakub Jelen def1de
@@ -35,6 +35,99 @@
Jakub Jelen def1de
 #include "canohost.h"
Jakub Jelen def1de
 #include "misc.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Returns the remote DNS hostname as a string. The returned string must not
Jakub Jelen def1de
+ * be freed. NB. this will usually trigger a DNS query the first time it is
Jakub Jelen def1de
+ * called.
Jakub Jelen def1de
+ * This function does additional checks on the hostname to mitigate some
Jakub Jelen def1de
+ * attacks on legacy rhosts-style authentication.
Jakub Jelen def1de
+ * XXX is RhostsRSAAuthentication vulnerable to these?
Jakub Jelen def1de
+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+char *
Jakub Jelen def1de
+remote_hostname(struct ssh *ssh)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct sockaddr_storage from;
Jakub Jelen def1de
+	socklen_t fromlen;
Jakub Jelen def1de
+	struct addrinfo hints, *ai, *aitop;
Jakub Jelen def1de
+	char name[NI_MAXHOST], ntop2[NI_MAXHOST];
Jakub Jelen def1de
+	const char *ntop = ssh_remote_ipaddr(ssh);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Get IP address of client. */
Jakub Jelen def1de
+	fromlen = sizeof(from);
Jakub Jelen def1de
+	memset(&from, 0, sizeof(from));
Jakub Jelen def1de
+	if (getpeername(ssh_packet_get_connection_in(ssh),
Jakub Jelen 36fef5
+	    (struct sockaddr *)&from, &fromlen) == -1) {
Jakub Jelen def1de
+		debug("getpeername failed: %.100s", strerror(errno));
Jakub Jelen 51f5c1
+		return xstrdup(ntop);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ipv64_normalise_mapped(&from, &fromlen);
Jakub Jelen def1de
+	if (from.ss_family == AF_INET6)
Jakub Jelen def1de
+		fromlen = sizeof(struct sockaddr_in6);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	debug3("Trying to reverse map address %.100s.", ntop);
Jakub Jelen def1de
+	/* Map the IP address to a host name. */
Jakub Jelen def1de
+	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
Jakub Jelen def1de
+	    NULL, 0, NI_NAMEREQD) != 0) {
Jakub Jelen def1de
+		/* Host name not found.  Use ip address. */
Jakub Jelen 51f5c1
+		return xstrdup(ntop);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * if reverse lookup result looks like a numeric hostname,
Jakub Jelen def1de
+	 * someone is trying to trick us by PTR record like following:
Jakub Jelen def1de
+	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+	memset(&hints, 0, sizeof(hints));
Jakub Jelen def1de
+	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
Jakub Jelen def1de
+	hints.ai_flags = AI_NUMERICHOST;
Jakub Jelen def1de
+	if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
Jakub Jelen def1de
+		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
Jakub Jelen def1de
+		    name, ntop);
Jakub Jelen def1de
+		freeaddrinfo(ai);
Jakub Jelen 51f5c1
+		return xstrdup(ntop);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Names are stored in lowercase. */
Jakub Jelen def1de
+	lowercase(name);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * Map it back to an IP address and check that the given
Jakub Jelen def1de
+	 * address actually is an address of this host.  This is
Jakub Jelen def1de
+	 * necessary because anyone with access to a name server can
Jakub Jelen def1de
+	 * define arbitrary names for an IP address. Mapping from
Jakub Jelen def1de
+	 * name to IP address can be trusted better (but can still be
Jakub Jelen def1de
+	 * fooled if the intruder has access to the name server of
Jakub Jelen def1de
+	 * the domain).
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+	memset(&hints, 0, sizeof(hints));
Jakub Jelen def1de
+	hints.ai_family = from.ss_family;
Jakub Jelen def1de
+	hints.ai_socktype = SOCK_STREAM;
Jakub Jelen def1de
+	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
Jakub Jelen def1de
+		logit("reverse mapping checking getaddrinfo for %.700s "
Jakub Jelen def1de
+		    "[%s] failed.", name, ntop);
Jakub Jelen 51f5c1
+		return xstrdup(ntop);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	/* Look for the address from the list of addresses. */
Jakub Jelen def1de
+	for (ai = aitop; ai; ai = ai->ai_next) {
Jakub Jelen def1de
+		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
Jakub Jelen def1de
+		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
Jakub Jelen def1de
+		    (strcmp(ntop, ntop2) == 0))
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	freeaddrinfo(aitop);
Jakub Jelen def1de
+	/* If we reached the end of the list, the address was not there. */
Jakub Jelen def1de
+	if (ai == NULL) {
Jakub Jelen def1de
+		/* Address not found for the host name. */
Jakub Jelen def1de
+		logit("Address %.100s maps to %.600s, but this does not "
Jakub Jelen def1de
+		    "map back to the address.", ntop, name);
Jakub Jelen 51f5c1
+		return xstrdup(ntop);
Jakub Jelen def1de
+	}
Jakub Jelen 51f5c1
+	return xstrdup(name);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 void
Jakub Jelen def1de
 ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
Jakub Jelen def1de
 {
Zoltan Fridrich 5c3160
diff --color -ruNp a/canohost.h b/canohost.h
Zoltan Fridrich 5c3160
--- a/canohost.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/canohost.h	2024-08-28 12:35:41.246432045 +0200
Jakub Jelen def1de
@@ -15,6 +15,9 @@
Jakub Jelen def1de
 #ifndef _CANOHOST_H
Jakub Jelen def1de
 #define _CANOHOST_H
Jakub Jelen def1de
 
Jakub Jelen def1de
+struct ssh;
Jakub Jelen def1de
+
Jakub Jelen def1de
+char		*remote_hostname(struct ssh *);
Jakub Jelen def1de
 char		*get_peer_ipaddr(int);
Jakub Jelen def1de
 int		 get_peer_port(int);
Jakub Jelen def1de
 char		*get_local_ipaddr(int);
Zoltan Fridrich 5c3160
diff --color -ruNp a/clientloop.c b/clientloop.c
Zoltan Fridrich 5c3160
--- a/clientloop.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/clientloop.c	2024-08-28 12:35:41.246432045 +0200
Zoltan Fridrich 5c3160
@@ -115,6 +115,10 @@
Jakub Jelen def1de
 #include "ssherr.h"
Jakub Jelen def1de
 #include "hostfile.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+#include "ssh-gss.h"
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Dmitry Belyavskiy 03150f
 /* Permitted RSA signature algorithms for UpdateHostkeys proofs */
Dmitry Belyavskiy 03150f
 #define HOSTKEY_PROOF_RSA_ALGS	"rsa-sha2-512,rsa-sha2-256"
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -1590,6 +1594,14 @@ client_loop(struct ssh *ssh, int have_pt
Dmitry Belyavskiy f561c6
 		/* Do channel operations. */
Dmitry Belyavskiy f561c6
 		channel_after_poll(ssh, pfd, npfd_active);
Jakub Jelen def1de
 
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+			if (options.gss_renewal_rekey &&
Jakub Jelen def1de
+			    ssh_gssapi_credentials_updated(NULL)) {
Jakub Jelen def1de
+				debug("credentials updated - forcing rekey");
Jakub Jelen def1de
+				need_rekeying = 1;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Jakub Jelen def1de
 		/* Buffer input from the connection.  */
c04e46
 		if (conn_in_ready)
c04e46
 			client_process_net_input(ssh);
Zoltan Fridrich 5c3160
diff --color -ruNp a/configure.ac b/configure.ac
Zoltan Fridrich 5c3160
--- a/configure.ac	2024-08-28 12:35:01.202659743 +0200
Zoltan Fridrich 5c3160
+++ b/configure.ac	2024-08-28 12:35:41.247432064 +0200
Zoltan Fridrich 5c3160
@@ -774,6 +774,30 @@ int main(void) { if (NSVersionOfRunTimeL
Jakub Jelen def1de
 	    [Use tunnel device compatibility to OpenBSD])
Jakub Jelen def1de
 	AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
Jakub Jelen def1de
 	    [Prepend the address family to IP tunnel traffic])
Jakub Jelen def1de
+	AC_MSG_CHECKING([if we have the Security Authorization Session API])
Jakub Jelen def1de
+	AC_TRY_COMPILE([#include <Security/AuthSession.h>],
Jakub Jelen def1de
+		[SessionCreate(0, 0);],
Jakub Jelen def1de
+		[ac_cv_use_security_session_api="yes"
Jakub Jelen def1de
+		 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
Jakub Jelen def1de
+			[platform has the Security Authorization Session API])
Jakub Jelen def1de
+		 LIBS="$LIBS -framework Security"
Jakub Jelen def1de
+		 AC_MSG_RESULT([yes])],
Jakub Jelen def1de
+		[ac_cv_use_security_session_api="no"
Jakub Jelen def1de
+		 AC_MSG_RESULT([no])])
Jakub Jelen def1de
+	AC_MSG_CHECKING([if we have an in-memory credentials cache])
Jakub Jelen def1de
+	AC_TRY_COMPILE(
Jakub Jelen def1de
+		[#include <Kerberos/Kerberos.h>],
Jakub Jelen def1de
+		[cc_context_t c;
Jakub Jelen def1de
+		 (void) cc_initialize (&c, 0, NULL, NULL);],
Jakub Jelen def1de
+		[AC_DEFINE([USE_CCAPI], [1],
Jakub Jelen def1de
+			[platform uses an in-memory credentials cache])
Jakub Jelen def1de
+		 LIBS="$LIBS -framework Security"
Jakub Jelen def1de
+		 AC_MSG_RESULT([yes])
Jakub Jelen def1de
+		 if test "x$ac_cv_use_security_session_api" = "xno"; then
Jakub Jelen def1de
+			AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
Jakub Jelen def1de
+		fi],
Jakub Jelen def1de
+		[AC_MSG_RESULT([no])]
Jakub Jelen def1de
+	)
Jakub Jelen def1de
 	m4_pattern_allow([AU_IPv])
Jakub Jelen def1de
 	AC_CHECK_DECL([AU_IPv4], [],
Jakub Jelen def1de
 	    AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
Zoltan Fridrich 5c3160
diff --color -ruNp a/gss-genr.c b/gss-genr.c
Zoltan Fridrich 5c3160
--- a/gss-genr.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/gss-genr.c	2024-08-28 12:35:41.248432084 +0200
Zoltan Fridrich 5c3160
@@ -42,9 +42,33 @@
Jakub Jelen def1de
 #include "sshbuf.h"
Jakub Jelen def1de
 #include "log.h"
Jakub Jelen def1de
 #include "ssh2.h"
Jakub Jelen def1de
+#include "cipher.h"
Jakub Jelen def1de
+#include "sshkey.h"
Jakub Jelen def1de
+#include "kex.h"
Jakub Jelen def1de
+#include "digest.h"
Jakub Jelen def1de
+#include "packet.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
 #include "ssh-gss.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
+typedef struct {
Jakub Jelen def1de
+	char *encoded;
Jakub Jelen def1de
+	gss_OID oid;
Jakub Jelen def1de
+} ssh_gss_kex_mapping;
Jakub Jelen def1de
+
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * XXX - It would be nice to find a more elegant way of handling the
Jakub Jelen def1de
+ * XXX   passing of the key exchange context to the userauth routines
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+Gssctxt *gss_kex_context = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+ssh_gssapi_oid_table_ok(void) {
Jakub Jelen def1de
+	return (gss_enc2oid != NULL);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 /* sshbuf_get for gss_buffer_desc */
Jakub Jelen def1de
 int
Jakub Jelen def1de
 ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
Zoltan Fridrich 5c3160
@@ -60,6 +84,159 @@ ssh_gssapi_get_buffer_desc(struct sshbuf
Jakub Jelen def1de
 	return 0;
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+/* sshpkt_get of gss_buffer_desc */
Jakub Jelen def1de
+int
Jakub Jelen def1de
+ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+	u_char *p;
Jakub Jelen def1de
+	size_t len;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
Jakub Jelen def1de
+		return r;
Jakub Jelen def1de
+	g->value = p;
Jakub Jelen def1de
+	g->length = len;
Jakub Jelen def1de
+	return 0;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * We test mechanisms to ensure that we can use them, to avoid starting
Jakub Jelen def1de
+ * a key exchange with a bad mechanism
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+char *
Jakub Jelen def1de
+ssh_gssapi_client_mechanisms(const char *host, const char *client,
Jakub Jelen def1de
+    const char *kex) {
Jakub Jelen def1de
+	gss_OID_set gss_supported = NULL;
Jakub Jelen def1de
+	OM_uint32 min_status;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
Jakub Jelen def1de
+		return NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
Jakub Jelen def1de
+	    host, client, kex);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+char *
Jakub Jelen def1de
+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
Jakub Jelen def1de
+    const char *host, const char *client, const char *kex) {
Jakub Jelen def1de
+	struct sshbuf *buf = NULL;
Jakub Jelen def1de
+	size_t i;
Jakub Jelen 51f5c1
+	int r = SSH_ERR_ALLOC_FAIL;
Jakub Jelen 51f5c1
+	int oidpos, enclen;
Jakub Jelen def1de
+	char *mechs, *encoded;
Jakub Jelen def1de
+	u_char digest[SSH_DIGEST_MAX_LENGTH];
Jakub Jelen def1de
+	char deroid[2];
Jakub Jelen def1de
+	struct ssh_digest_ctx *md = NULL;
Jakub Jelen def1de
+	char *s, *cp, *p;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_enc2oid != NULL) {
Jakub Jelen def1de
+		for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
Jakub Jelen def1de
+			free(gss_enc2oid[i].encoded);
Jakub Jelen def1de
+		free(gss_enc2oid);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
Jakub Jelen def1de
+	    (gss_supported->count + 1));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((buf = sshbuf_new()) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_new failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	oidpos = 0;
Jakub Jelen def1de
+	s = cp = xstrdup(kex);
Jakub Jelen def1de
+	for (i = 0; i < gss_supported->count; i++) {
Jakub Jelen def1de
+		if (gss_supported->elements[i].length < 128 &&
Jakub Jelen def1de
+		    (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
Jakub Jelen def1de
+
Jakub Jelen def1de
+			deroid[0] = SSH_GSS_OIDTYPE;
Jakub Jelen def1de
+			deroid[1] = gss_supported->elements[i].length;
Jakub Jelen def1de
+
Jakub Jelen def1de
+			if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
Jakub Jelen def1de
+			    (r = ssh_digest_update(md, deroid, 2)) != 0 ||
Jakub Jelen def1de
+			    (r = ssh_digest_update(md,
Jakub Jelen def1de
+			        gss_supported->elements[i].elements,
Jakub Jelen def1de
+			        gss_supported->elements[i].length)) != 0 ||
Jakub Jelen def1de
+			    (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
Jakub Jelen 25c16c
+				fatal_fr(r, "digest failed");
Jakub Jelen def1de
+			ssh_digest_free(md);
Jakub Jelen def1de
+			md = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+			encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
Jakub Jelen def1de
+			    * 2);
Jakub Jelen def1de
+			enclen = __b64_ntop(digest,
Jakub Jelen def1de
+			    ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
Jakub Jelen def1de
+			    ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
Jakub Jelen def1de
+
Jakub Jelen def1de
+			cp = strncpy(s, kex, strlen(kex));
Jakub Jelen def1de
+			for ((p = strsep(&cp, ",")); p && *p != '\0';
Jakub Jelen def1de
+				(p = strsep(&cp, ","))) {
Jakub Jelen def1de
+				if (sshbuf_len(buf) != 0 &&
Jakub Jelen def1de
+				    (r = sshbuf_put_u8(buf, ',')) != 0)
Jakub Jelen 25c16c
+					fatal_fr(r, "sshbuf_put_u8 error");
Jakub Jelen def1de
+				if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
Jakub Jelen def1de
+				    (r = sshbuf_put(buf, encoded, enclen)) != 0)
Jakub Jelen 25c16c
+					fatal_fr(r, "sshbuf_put error");
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+
Jakub Jelen def1de
+			gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
Jakub Jelen def1de
+			gss_enc2oid[oidpos].encoded = encoded;
Jakub Jelen def1de
+			oidpos++;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	free(s);
Jakub Jelen def1de
+	gss_enc2oid[oidpos].oid = NULL;
Jakub Jelen def1de
+	gss_enc2oid[oidpos].encoded = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((mechs = sshbuf_dup_string(buf)) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_dup_string failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_free(buf);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (strlen(mechs) == 0) {
Jakub Jelen def1de
+		free(mechs);
Jakub Jelen def1de
+		mechs = NULL;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (mechs);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+gss_OID
Jakub Jelen def1de
+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
Jakub Jelen def1de
+	int i = 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+#define SKIP_KEX_NAME(type) \
Jakub Jelen def1de
+	case type: \
Jakub Jelen def1de
+		if (strlen(name) < sizeof(type##_ID)) \
Jakub Jelen def1de
+			return GSS_C_NO_OID; \
Jakub Jelen def1de
+		name += sizeof(type##_ID) - 1; \
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	switch (kex_type) {
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1)
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1)
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256)
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512)
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_GEX_SHA1)
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256)
Jakub Jelen def1de
+	SKIP_KEX_NAME(KEX_GSS_C25519_SHA256)
Jakub Jelen def1de
+	default:
Jakub Jelen def1de
+		return GSS_C_NO_OID;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+#undef SKIP_KEX_NAME
Jakub Jelen def1de
+
Jakub Jelen def1de
+	while (gss_enc2oid[i].encoded != NULL &&
Jakub Jelen def1de
+	    strcmp(name, gss_enc2oid[i].encoded) != 0)
Jakub Jelen def1de
+		i++;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_enc2oid[i].oid != NULL && ctx != NULL)
Jakub Jelen def1de
+		ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return gss_enc2oid[i].oid;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 /* Check that the OID in a data stream matches that in the context */
Jakub Jelen def1de
 int
Jakub Jelen def1de
 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
Zoltan Fridrich 5c3160
@@ -216,7 +393,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	ctx->major = gss_init_sec_context(&ctx->minor,
Jakub Jelen def1de
-	    GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
Jakub Jelen def1de
+	    ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
Jakub Jelen def1de
 	    GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
Jakub Jelen def1de
 	    0, NULL, recv_tok, NULL, send_tok, flags, NULL);
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -246,8 +423,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, con
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 OM_uint32
Jakub Jelen def1de
+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	gss_buffer_desc gssbuf;
Jakub Jelen def1de
+	gss_name_t gssname;
Jakub Jelen def1de
+	OM_uint32 status;
Jakub Jelen def1de
+	gss_OID_set oidset;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gssbuf.value = (void *) name;
Jakub Jelen def1de
+	gssbuf.length = strlen(gssbuf.value);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_create_empty_oid_set(&status, &oidset);
Jakub Jelen def1de
+	gss_add_oid_set_member(&status, ctx->oid, &oidset);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ctx->major = gss_import_name(&ctx->minor, &gssbuf,
Jakub Jelen def1de
+	    GSS_C_NT_USER_NAME, &gssname);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!ctx->major)
Jakub Jelen def1de
+		ctx->major = gss_acquire_cred(&ctx->minor,
Jakub Jelen def1de
+		    gssname, 0, oidset, GSS_C_INITIATE,
Jakub Jelen def1de
+		    &ctx->client_creds, NULL, NULL);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_release_name(&status, &gssname);
Jakub Jelen def1de
+	gss_release_oid_set(&status, &oidset);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (ctx->major)
Jakub Jelen def1de
+		ssh_gssapi_error(ctx);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return(ctx->major);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Zoltan Fridrich 5c3160
+OM_uint32
Jakub Jelen def1de
 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
Jakub Jelen def1de
 {
Jakub Jelen def1de
+	if (ctx == NULL)
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
Jakub Jelen def1de
 	    GSS_C_QOP_DEFAULT, buffer, hash)))
Jakub Jelen def1de
 		ssh_gssapi_error(ctx);
Zoltan Fridrich 5c3160
@@ -255,6 +466,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer
Jakub Jelen def1de
 	return (ctx->major);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+/* Priviledged when used by server */
Jakub Jelen def1de
+OM_uint32
Jakub Jelen def1de
+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	if (ctx == NULL)
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
Jakub Jelen def1de
+	    gssbuf, gssmic, NULL);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (ctx->major);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 void
Jakub Jelen def1de
 ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
Jakub Jelen 25c16c
     const char *context, const struct sshbuf *session_id)
Zoltan Fridrich 5c3160
@@ -271,11 +495,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, co
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
 int
Jakub Jelen def1de
-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
Jakub Jelen def1de
+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
Jakub Jelen def1de
+    const char *client)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
Jakub Jelen def1de
 	OM_uint32 major, minor;
Jakub Jelen def1de
 	gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
Jakub Jelen def1de
+	Gssctxt *intctx = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (ctx == NULL)
Jakub Jelen def1de
+		ctx = &intct;;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* RFC 4462 says we MUST NOT do SPNEGO */
Dmitry Belyavskiy 089d79
 	if (oid->length == spnego_oid.length &&
Zoltan Fridrich 5c3160
@@ -285,6 +514,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx
Jakub Jelen def1de
 	ssh_gssapi_build_ctx(ctx);
Jakub Jelen def1de
 	ssh_gssapi_set_oid(*ctx, oid);
Jakub Jelen def1de
 	major = ssh_gssapi_import_name(*ctx, host);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!GSS_ERROR(major) && client)
Jakub Jelen def1de
+		major = ssh_gssapi_client_identity(*ctx, client);
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if (!GSS_ERROR(major)) {
Dmitry Belyavskiy 089d79
 		major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
Jakub Jelen def1de
 		    NULL);
Zoltan Fridrich 5c3160
@@ -294,10 +527,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx
Jakub Jelen def1de
 			    GSS_C_NO_BUFFER);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(major))
Jakub Jelen def1de
+	if (GSS_ERROR(major) || intctx != NULL)
Jakub Jelen def1de
 		ssh_gssapi_delete_ctx(ctx);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	return (!GSS_ERROR(major));
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+int
Jakub Jelen def1de
+ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
Jakub Jelen def1de
+	static gss_name_t saved_name = GSS_C_NO_NAME;
Jakub Jelen def1de
+	static OM_uint32 saved_lifetime = 0;
Jakub Jelen def1de
+	static gss_OID saved_mech = GSS_C_NO_OID;
Jakub Jelen def1de
+	static gss_name_t name;
Jakub Jelen def1de
+	static OM_uint32 last_call = 0;
Jakub Jelen def1de
+	OM_uint32 lifetime, now, major, minor;
Jakub Jelen def1de
+	int equal;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	now = time(NULL);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (ctxt) {
Jakub Jelen def1de
+		debug("Rekey has happened - updating saved versions");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (saved_name != GSS_C_NO_NAME)
Jakub Jelen def1de
+			gss_release_name(&minor, &saved_name);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
Jakub Jelen def1de
+		    &saved_name, &saved_lifetime, NULL, NULL);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (!GSS_ERROR(major)) {
Jakub Jelen def1de
+			saved_mech = ctxt->oid;
Jakub Jelen def1de
+		        saved_lifetime+= now;
Jakub Jelen def1de
+		} else {
Jakub Jelen def1de
+			/* Handle the error */
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (now - last_call < 10)
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	last_call = now;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (saved_mech == GSS_C_NO_OID)
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
Jakub Jelen def1de
+	    &name, &lifetime, NULL, NULL);
Jakub Jelen def1de
+	if (major == GSS_S_CREDENTIALS_EXPIRED)
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+	else if (GSS_ERROR(major))
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	major = gss_compare_name(&minor, saved_name, name, &equal);
Jakub Jelen def1de
+	gss_release_name(&minor, &name);
Jakub Jelen def1de
+	if (GSS_ERROR(major))
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (equal && (saved_lifetime < lifetime + now - 10))
Jakub Jelen def1de
+		return 1;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return 0;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 #endif /* GSSAPI */
Zoltan Fridrich 5c3160
diff --color -ruNp a/gss-serv.c b/gss-serv.c
Zoltan Fridrich 5c3160
--- a/gss-serv.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/gss-serv.c	2024-08-28 12:35:41.248432084 +0200
Jakub Jelen def1de
@@ -1,7 +1,7 @@
Jakub Jelen 5cd955
 /* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */
Jakub Jelen def1de
 
Jakub Jelen def1de
 /*
Jakub Jelen def1de
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Jakub Jelen def1de
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Jakub Jelen def1de
  *
Jakub Jelen def1de
  * Redistribution and use in source and binary forms, with or without
Jakub Jelen def1de
  * modification, are permitted provided that the following conditions
Jakub Jelen def1de
@@ -44,17 +44,19 @@
Jakub Jelen def1de
 #include "session.h"
Jakub Jelen def1de
 #include "misc.h"
Jakub Jelen def1de
 #include "servconf.h"
Jakub Jelen def1de
+#include "uidswap.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
 #include "ssh-gss.h"
Jakub Jelen def1de
+#include "monitor_wrap.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
 extern ServerOptions options;
Jakub Jelen def1de
 
Jakub Jelen def1de
 static ssh_gssapi_client gssapi_client =
Jakub Jelen def1de
-    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
Jakub Jelen def1de
-    GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
Jakub Jelen def1de
+    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
Jakub Jelen def1de
+    GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0};
Jakub Jelen def1de
 
Jakub Jelen def1de
 ssh_gssapi_mech gssapi_null_mech =
Jakub Jelen def1de
-    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
Jakub Jelen def1de
+    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
Jakub Jelen def1de
 
Jakub Jelen def1de
 #ifdef KRB5
Jakub Jelen def1de
 extern ssh_gssapi_mech gssapi_kerberos_mech;
Zoltan Fridrich 5c3160
@@ -141,6 +143,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 /* Unprivileged */
Jakub Jelen def1de
+char *
Jakub Jelen def1de
+ssh_gssapi_server_mechanisms(void) {
Jakub Jelen def1de
+	if (supported_oids == NULL)
Jakub Jelen def1de
+		ssh_gssapi_prepare_supported_oids();
Jakub Jelen def1de
+	return (ssh_gssapi_kex_mechs(supported_oids,
Jakub Jelen def1de
+	    &ssh_gssapi_server_check_mech, NULL, NULL,
Jakub Jelen def1de
+	    options.gss_kex_algorithms));
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/* Unprivileged */
Jakub Jelen def1de
+int
Jakub Jelen def1de
+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
Jakub Jelen def1de
+    const char *dummy) {
Jakub Jelen def1de
+	Gssctxt *ctx = NULL;
Jakub Jelen def1de
+	int res;
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+	res = !GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctx, oid));
Jakub Jelen def1de
+	ssh_gssapi_delete_ctx(&ctx;;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (res);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Zoltan Fridrich 5c3160
+/* Unprivileged */
Jakub Jelen def1de
 void
Jakub Jelen def1de
 ssh_gssapi_supported_oids(gss_OID_set *oidset)
Zoltan Fridrich 5c3160
 {
Zoltan Fridrich 5c3160
@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o
Jakub Jelen def1de
 	gss_OID_set supported;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	gss_create_empty_oid_set(&min_status, oidset);
Jakub Jelen def1de
-	gss_indicate_mechs(&min_status, &supported);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
Jakub Jelen def1de
+		return;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	while (supported_mechs[i]->name != NULL) {
Jakub Jelen def1de
 		if (GSS_ERROR(gss_test_oid_set_member(&min_status,
Jakub Jelen def1de
@@ -276,8 +303,48 @@ OM_uint32
Jakub Jelen def1de
 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	int i = 0;
Jakub Jelen def1de
+	int equal = 0;
Jakub Jelen def1de
+	gss_name_t new_name = GSS_C_NO_NAME;
Jakub Jelen def1de
+	gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (options.gss_store_rekey && client->used && ctx->client_creds) {
Jakub Jelen def1de
+		if (client->mech->oid.length != ctx->oid->length ||
Jakub Jelen def1de
+		    (memcmp(client->mech->oid.elements,
Jakub Jelen def1de
+		     ctx->oid->elements, ctx->oid->length) !=0)) {
Jakub Jelen def1de
+			debug("Rekeyed credentials have different mechanism");
Jakub Jelen def1de
+			return GSS_S_COMPLETE;
Jakub Jelen def1de
+		}
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
-	gss_buffer_desc ename;
Jakub Jelen def1de
+		if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
Jakub Jelen def1de
+		    ctx->client_creds, ctx->oid, &new_name,
Jakub Jelen def1de
+		    NULL, NULL, NULL))) {
Jakub Jelen def1de
+			ssh_gssapi_error(ctx);
Jakub Jelen def1de
+			return (ctx->major);
Jakub Jelen def1de
+		}
Zoltan Fridrich 5c3160
+
Jakub Jelen def1de
+		ctx->major = gss_compare_name(&ctx->minor, client->name,
Jakub Jelen def1de
+		    new_name, &equal);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (GSS_ERROR(ctx->major)) {
Jakub Jelen def1de
+			ssh_gssapi_error(ctx);
Jakub Jelen def1de
+			return (ctx->major);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (!equal) {
Jakub Jelen def1de
+			debug("Rekeyed credentials have different name");
Jakub Jelen def1de
+			return GSS_S_COMPLETE;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		debug("Marking rekeyed credentials for export");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		gss_release_name(&ctx->minor, &client->name);
Jakub Jelen def1de
+		gss_release_cred(&ctx->minor, &client->creds);
Jakub Jelen def1de
+		client->name = new_name;
Jakub Jelen def1de
+		client->creds = ctx->client_creds;
Jakub Jelen def1de
+		ctx->client_creds = GSS_C_NO_CREDENTIAL;
Jakub Jelen def1de
+		client->updated = 1;
Jakub Jelen def1de
+		return GSS_S_COMPLETE;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	client->mech = NULL;
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g
Jakub Jelen def1de
 	if (client->mech == NULL)
Jakub Jelen def1de
 		return GSS_S_FAILURE;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	if (ctx->client_creds &&
Jakub Jelen def1de
+	    (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
Jakub Jelen def1de
+	     ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
Jakub Jelen def1de
+		ssh_gssapi_error(ctx);
Jakub Jelen def1de
+		return (ctx->major);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
Jakub Jelen def1de
 	    &client->displayname, NULL))) {
Jakub Jelen def1de
 		ssh_gssapi_error(ctx);
Zoltan Fridrich 5c3160
@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g
Jakub Jelen def1de
 		return (ctx->major);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
+	gss_release_buffer(&ctx->minor, &ename);
Jakub Jelen def1de
+
Jakub Jelen def1de
 	/* We can't copy this structure, so we just move the pointer to it */
Jakub Jelen def1de
 	client->creds = ctx->client_creds;
Jakub Jelen def1de
 	ctx->client_creds = GSS_C_NO_CREDENTIAL;
Zoltan Fridrich 5c3160
@@ -319,11 +395,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g
Jakub Jelen def1de
 void
Jakub Jelen def1de
 ssh_gssapi_cleanup_creds(void)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	if (gssapi_client.store.filename != NULL) {
Jakub Jelen def1de
-		/* Unlink probably isn't sufficient */
Jakub Jelen def1de
-		debug("removing gssapi cred file\"%s\"",
Jakub Jelen def1de
-		    gssapi_client.store.filename);
Jakub Jelen def1de
-		unlink(gssapi_client.store.filename);
Jakub Jelen def1de
+	krb5_ccache ccache = NULL;
Jakub Jelen def1de
+	krb5_error_code problem;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gssapi_client.store.data != NULL) {
Jakub Jelen def1de
+		if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) {
Jakub Jelen 25c16c
+			debug_f("krb5_cc_resolve(): %.100s",
Jakub Jelen def1de
+				krb5_get_err_text(gssapi_client.store.data, problem));
Jakub Jelen def1de
+		} else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) {
Jakub Jelen 25c16c
+			debug_f("krb5_cc_destroy(): %.100s",
Jakub Jelen def1de
+				krb5_get_err_text(gssapi_client.store.data, problem));
Jakub Jelen def1de
+		} else {
Jakub Jelen def1de
+			krb5_free_context(gssapi_client.store.data);
Jakub Jelen def1de
+			gssapi_client.store.data = NULL;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -356,19 +441,23 @@ ssh_gssapi_do_child(char ***envp, u_int
Jakub Jelen def1de
 
Jakub Jelen def1de
 /* Privileged */
Jakub Jelen def1de
 int
Jakub Jelen def1de
-ssh_gssapi_userok(char *user)
Jakub Jelen def1de
+ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	OM_uint32 lmin;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	(void) kex; /* used in privilege separation */
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if (gssapi_client.exportedname.length == 0 ||
Jakub Jelen def1de
 	    gssapi_client.exportedname.value == NULL) {
Jakub Jelen def1de
 		debug("No suitable client data");
Jakub Jelen def1de
 		return 0;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 	if (gssapi_client.mech && gssapi_client.mech->userok)
Jakub Jelen def1de
-		if ((*gssapi_client.mech->userok)(&gssapi_client, user))
Jakub Jelen def1de
+		if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
Jakub Jelen def1de
+			gssapi_client.used = 1;
Jakub Jelen def1de
+			gssapi_client.store.owner = pw;
Jakub Jelen def1de
 			return 1;
Jakub Jelen def1de
-		else {
Jakub Jelen def1de
+		} else {
Jakub Jelen def1de
 			/* Destroy delegated credentials if userok fails */
Jakub Jelen def1de
 			gss_release_buffer(&lmin, &gssapi_client.displayname);
Jakub Jelen def1de
 			gss_release_buffer(&lmin, &gssapi_client.exportedname);
Dmitry Belyavskiy 089d79
@@ -382,14 +471,85 @@ ssh_gssapi_userok(char *user)
Jakub Jelen def1de
 	return (0);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
-/* Privileged */
Jakub Jelen def1de
-OM_uint32
Jakub Jelen def1de
-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
Jakub Jelen def1de
+/* These bits are only used for rekeying. The unpriviledged child is running
Jakub Jelen def1de
+ * as the user, the monitor is root.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * In the child, we want to :
Jakub Jelen def1de
+ *    *) Ask the monitor to store our credentials into the store we specify
Jakub Jelen def1de
+ *    *) If it succeeds, maybe do a PAM update
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+/* Stuff for PAM */
Jakub Jelen def1de
+
Jakub Jelen def1de
+#ifdef USE_PAM
Jakub Jelen def1de
+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
Jakub Jelen def1de
+    struct pam_response **resp, void *data)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
Jakub Jelen def1de
-	    gssbuf, gssmic, NULL);
Jakub Jelen def1de
+	return (PAM_CONV_ERR);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 
Jakub Jelen def1de
-	return (ctx->major);
Jakub Jelen def1de
+void
Jakub Jelen def1de
+ssh_gssapi_rekey_creds(void) {
Jakub Jelen def1de
+	int ok;
Jakub Jelen def1de
+#ifdef USE_PAM
Jakub Jelen def1de
+	int ret;
Jakub Jelen def1de
+	pam_handle_t *pamh = NULL;
Jakub Jelen def1de
+	struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
Jakub Jelen def1de
+	char *envstr;
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gssapi_client.store.filename == NULL &&
Jakub Jelen def1de
+	    gssapi_client.store.envval == NULL &&
Jakub Jelen def1de
+	    gssapi_client.store.envvar == NULL)
Jakub Jelen def1de
+		return;
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+	ok = mm_ssh_gssapi_update_creds(&gssapi_client.store);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!ok)
Jakub Jelen def1de
+		return;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	debug("Rekeyed credentials stored successfully");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Actually managing to play with the ssh pam stack from here will
Jakub Jelen def1de
+	 * be next to impossible. In any case, we may want different options
Jakub Jelen def1de
+	 * for rekeying. So, use our own :)
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+#ifdef USE_PAM	
Jakub Jelen def1de
+	ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
Jakub Jelen def1de
+ 	    &pamconv, &pamh);
Jakub Jelen def1de
+	if (ret)
Jakub Jelen def1de
+		return;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
Jakub Jelen def1de
+	    gssapi_client.store.envval);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ret = pam_putenv(pamh, envstr);
Jakub Jelen def1de
+	if (!ret)
Jakub Jelen def1de
+		pam_setcred(pamh, PAM_REINITIALIZE_CRED);
Jakub Jelen def1de
+	pam_end(pamh, PAM_SUCCESS);
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
Jakub Jelen def1de
+	int ok = 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Check we've got credentials to store */
Jakub Jelen def1de
+	if (!gssapi_client.updated)
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gssapi_client.updated = 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	temporarily_use_uid(gssapi_client.store.owner);
Jakub Jelen def1de
+	if (gssapi_client.mech && gssapi_client.mech->updatecreds)
Jakub Jelen def1de
+		ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		debug("No update function for this mechanism");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	restore_uid();
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return ok;
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
 /* Privileged */
Zoltan Fridrich 5c3160
diff --color -ruNp a/gss-serv-krb5.c b/gss-serv-krb5.c
Zoltan Fridrich 5c3160
--- a/gss-serv-krb5.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/gss-serv-krb5.c	2024-08-28 12:35:41.248432084 +0200
Zoltan Fridrich 5c3160
@@ -1,7 +1,7 @@
Zoltan Fridrich 5c3160
 /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 /*
Zoltan Fridrich 5c3160
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Zoltan Fridrich 5c3160
+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
Zoltan Fridrich 5c3160
  *
Zoltan Fridrich 5c3160
  * Redistribution and use in source and binary forms, with or without
Zoltan Fridrich 5c3160
  * modification, are permitted provided that the following conditions
Zoltan Fridrich 5c3160
@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
Zoltan Fridrich 5c3160
 	krb5_error_code problem;
Zoltan Fridrich 5c3160
 	krb5_principal princ;
Zoltan Fridrich 5c3160
 	OM_uint32 maj_status, min_status;
Zoltan Fridrich 5c3160
-	int len;
Zoltan Fridrich 5c3160
+	const char *new_ccname, *new_cctype;
Zoltan Fridrich 5c3160
 	const char *errmsg;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 	if (client->creds == NULL) {
Zoltan Fridrich 5c3160
@@ -180,11 +180,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
Zoltan Fridrich 5c3160
 		return;
Zoltan Fridrich 5c3160
 	}
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
-	client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
Zoltan Fridrich 5c3160
+	new_cctype = krb5_cc_get_type(krb_context, ccache);
Zoltan Fridrich 5c3160
+	new_ccname = krb5_cc_get_name(krb_context, ccache);
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 	client->store.envvar = "KRB5CCNAME";
Zoltan Fridrich 5c3160
-	len = strlen(client->store.filename) + 6;
Zoltan Fridrich 5c3160
-	client->store.envval = xmalloc(len);
Zoltan Fridrich 5c3160
-	snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
Zoltan Fridrich 5c3160
+#ifdef USE_CCAPI
Zoltan Fridrich 5c3160
+	xasprintf(&client->store.envval, "API:%s", new_ccname);
Zoltan Fridrich 5c3160
+	client->store.filename = NULL;
Zoltan Fridrich 5c3160
+#else
Zoltan Fridrich 5c3160
+	if (new_ccname[0] == ':')
Zoltan Fridrich 5c3160
+		new_ccname++;
Zoltan Fridrich 5c3160
+	xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname);
Zoltan Fridrich 5c3160
+	if (strcmp(new_cctype, "DIR") == 0) {
Zoltan Fridrich 5c3160
+		char *p;
Zoltan Fridrich 5c3160
+		p = strrchr(client->store.envval, '/');
Zoltan Fridrich 5c3160
+		if (p)
Zoltan Fridrich 5c3160
+			*p = '\0';
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+	if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0))
Zoltan Fridrich 5c3160
+		client->store.filename = xstrdup(new_ccname);
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 #ifdef USE_PAM
Zoltan Fridrich 5c3160
 	if (options.use_pam)
Zoltan Fridrich 5c3160
@@ -193,9 +208,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 	krb5_cc_close(krb_context, ccache);
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
+	client->store.data = krb_context;
Jakub Jelen def1de
+
Zoltan Fridrich 5c3160
 	return;
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+int
Zoltan Fridrich 5c3160
+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
Zoltan Fridrich 5c3160
+    ssh_gssapi_client *client)
Jakub Jelen def1de
+{
Zoltan Fridrich 5c3160
+	krb5_ccache ccache = NULL;
Zoltan Fridrich 5c3160
+	krb5_principal principal = NULL;
Zoltan Fridrich 5c3160
+	char *name = NULL;
Zoltan Fridrich 5c3160
+	krb5_error_code problem;
Zoltan Fridrich 5c3160
+	OM_uint32 maj_status, min_status;
Jakub Jelen def1de
+
Zoltan Fridrich 5c3160
+	if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
Zoltan Fridrich 5c3160
+                logit("krb5_cc_resolve(): %.100s",
Zoltan Fridrich 5c3160
+                    krb5_get_err_text(krb_context, problem));
Zoltan Fridrich 5c3160
+                return 0;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	/* Find out who the principal in this cache is */
Zoltan Fridrich 5c3160
+	if ((problem = krb5_cc_get_principal(krb_context, ccache,
Zoltan Fridrich 5c3160
+	    &principal))) {
Zoltan Fridrich 5c3160
+		logit("krb5_cc_get_principal(): %.100s",
Zoltan Fridrich 5c3160
+		    krb5_get_err_text(krb_context, problem));
Zoltan Fridrich 5c3160
+		krb5_cc_close(krb_context, ccache);
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+	}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
Zoltan Fridrich 5c3160
+		logit("krb5_unparse_name(): %.100s",
Zoltan Fridrich 5c3160
+		    krb5_get_err_text(krb_context, problem));
Zoltan Fridrich 5c3160
+		krb5_free_principal(krb_context, principal);
Zoltan Fridrich 5c3160
+		krb5_cc_close(krb_context, ccache);
Zoltan Fridrich 5c3160
+		return 0;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	if (strcmp(name,client->exportedname.value)!=0) {
Zoltan Fridrich 5c3160
+		debug("Name in local credentials cache differs. Not storing");
Zoltan Fridrich 5c3160
+		krb5_free_principal(krb_context, principal);
Zoltan Fridrich 5c3160
+		krb5_cc_close(krb_context, ccache);
Zoltan Fridrich 5c3160
+		krb5_free_unparsed_name(krb_context, name);
Zoltan Fridrich 5c3160
+		return 0;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+	krb5_free_unparsed_name(krb_context, name);
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	/* Name matches, so lets get on with it! */
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
Zoltan Fridrich 5c3160
+		logit("krb5_cc_initialize(): %.100s",
Zoltan Fridrich 5c3160
+		    krb5_get_err_text(krb_context, problem));
Zoltan Fridrich 5c3160
+		krb5_free_principal(krb_context, principal);
Zoltan Fridrich 5c3160
+		krb5_cc_close(krb_context, ccache);
Zoltan Fridrich 5c3160
+		return 0;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	krb5_free_principal(krb_context, principal);
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
Zoltan Fridrich 5c3160
+	    ccache))) {
Zoltan Fridrich 5c3160
+		logit("gss_krb5_copy_ccache() failed. Sorry!");
Zoltan Fridrich 5c3160
+		krb5_cc_close(krb_context, ccache);
Zoltan Fridrich 5c3160
+		return 0;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+
Jakub Jelen def1de
+	return 1;
Jakub Jelen def1de
+}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 ssh_gssapi_mech gssapi_kerberos_mech = {
Zoltan Fridrich 5c3160
 	"toWM5Slw5Ew8Mqkay+al2g==",
Zoltan Fridrich 5c3160
 	"Kerberos",
Zoltan Fridrich 5c3160
@@ -203,7 +285,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
Zoltan Fridrich 5c3160
 	NULL,
Zoltan Fridrich 5c3160
 	&ssh_gssapi_krb5_userok,
Zoltan Fridrich 5c3160
 	NULL,
Zoltan Fridrich 5c3160
-	&ssh_gssapi_krb5_storecreds
Zoltan Fridrich 5c3160
+	&ssh_gssapi_krb5_storecreds,
Zoltan Fridrich 5c3160
+	&ssh_gssapi_krb5_updatecreds
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 #endif /* KRB5 */
Zoltan Fridrich 5c3160
diff --color -ruNp a/kex.c b/kex.c
Zoltan Fridrich 5c3160
--- a/kex.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/kex.c	2024-08-28 12:35:41.249432103 +0200
Dmitry Belyavskiy 262bb3
@@ -303,17 +303,37 @@ static int
Dmitry Belyavskiy 262bb3
 kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m)
Dmitry Belyavskiy 262bb3
 {
Dmitry Belyavskiy 262bb3
 	int r;
Dmitry Belyavskiy 262bb3
+	int have_key = 0;
Dmitry Belyavskiy 262bb3
+	int ext_count = 2;
Dmitry Belyavskiy 262bb3
+
Dmitry Belyavskiy 262bb3
+#ifdef GSSAPI
Dmitry Belyavskiy 262bb3
+	/*
Dmitry Belyavskiy 262bb3
+	 * Currently GSS KEX don't provide host keys as optional message, so
Dmitry Belyavskiy 262bb3
+	 * no reasons to announce the publickey-hostbound extension
Dmitry Belyavskiy 262bb3
+	 */
Dmitry Belyavskiy 262bb3
+	if (ssh->kex->gss == NULL)
Dmitry Belyavskiy 262bb3
+	    have_key = 1;
Dmitry Belyavskiy 262bb3
+#endif
Dmitry Belyavskiy 262bb3
+	ext_count += have_key;
Dmitry Belyavskiy 262bb3
+
Dmitry Belyavskiy 262bb3
 
Dmitry Belyavskiy 262bb3
 	if (ssh->kex->server_sig_algs == NULL &&
Dmitry Belyavskiy 262bb3
 	    (ssh->kex->server_sig_algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
Dmitry Belyavskiy 262bb3
 		return SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 262bb3
-	if ((r = sshbuf_put_u32(m, 3)) != 0 ||
Dmitry Belyavskiy 262bb3
+	if ((r = sshbuf_put_u32(m, ext_count)) != 0 ||
Dmitry Belyavskiy 262bb3
 	    (r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 ||
Dmitry Belyavskiy 262bb3
-	    (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 ||
Dmitry Belyavskiy 262bb3
-	    (r = sshbuf_put_cstring(m,
Dmitry Belyavskiy 262bb3
-	    "publickey-hostbound@openssh.com")) != 0 ||
Dmitry Belyavskiy 262bb3
-	    (r = sshbuf_put_cstring(m, "0")) != 0 ||
Dmitry Belyavskiy 262bb3
-	    (r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 ||
Dmitry Belyavskiy 262bb3
+	    (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0) {
Dmitry Belyavskiy 262bb3
+		error_fr(r, "compose");
Dmitry Belyavskiy 262bb3
+		return r;
Dmitry Belyavskiy 262bb3
+	}
Dmitry Belyavskiy 262bb3
+	if (have_key) {
Dmitry Belyavskiy 262bb3
+	    if ((r = sshbuf_put_cstring(m, "publickey-hostbound@openssh.com")) != 0 ||
Dmitry Belyavskiy 262bb3
+	        (r = sshbuf_put_cstring(m, "0")) != 0) {
Dmitry Belyavskiy 262bb3
+		    error_fr(r, "compose");
Dmitry Belyavskiy 262bb3
+		    return r;
Dmitry Belyavskiy 262bb3
+		}
Dmitry Belyavskiy 262bb3
+	}
Dmitry Belyavskiy 262bb3
+	if ((r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 ||
Dmitry Belyavskiy 262bb3
 	    (r = sshbuf_put_cstring(m, "0")) != 0) {
Dmitry Belyavskiy 262bb3
 		error_fr(r, "compose");
Dmitry Belyavskiy 262bb3
 		return r;
Zoltan Fridrich 5c3160
@@ -737,6 +737,9 @@ kex_free(struct kex *kex)
Zoltan Fridrich 5c3160
 	sshbuf_free(kex->server_version);
Zoltan Fridrich 5c3160
 	sshbuf_free(kex->client_pub);
Zoltan Fridrich 5c3160
 	sshbuf_free(kex->session_id);
Jakub Jelen def1de
+#ifdef GSSAPI
Zoltan Fridrich 5c3160
+	free(kex->gss_host);
Zoltan Fridrich 5c3160
+#endif /* GSSAPI */
Zoltan Fridrich 5c3160
 	sshbuf_free(kex->initial_sig);
Zoltan Fridrich 5c3160
 	sshkey_free(kex->initial_hostkey);
Zoltan Fridrich 5c3160
 	free(kex->failed_choice);
Zoltan Fridrich 5c3160
diff --color -ruNp a/kexdh.c b/kexdh.c
Zoltan Fridrich 5c3160
--- a/kexdh.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/kexdh.c	2024-08-28 12:35:41.249432103 +0200
Zoltan Fridrich 5c3160
@@ -49,13 +49,23 @@ kex_dh_keygen(struct kex *kex)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	switch (kex->kex_type) {
Jakub Jelen def1de
 	case KEX_DH_GRP1_SHA1:
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	case KEX_GSS_GRP1_SHA1:
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 		kex->dh = dh_new_group1();
Jakub Jelen def1de
 		break;
Jakub Jelen def1de
 	case KEX_DH_GRP14_SHA1:
Jakub Jelen def1de
 	case KEX_DH_GRP14_SHA256:
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	case KEX_GSS_GRP14_SHA1:
Jakub Jelen def1de
+	case KEX_GSS_GRP14_SHA256:
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 		kex->dh = dh_new_group14();
Jakub Jelen def1de
 		break;
Jakub Jelen def1de
 	case KEX_DH_GRP16_SHA512:
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	case KEX_GSS_GRP16_SHA512:
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 		kex->dh = dh_new_group16();
Jakub Jelen def1de
 		break;
Jakub Jelen def1de
 	case KEX_DH_GRP18_SHA512:
Zoltan Fridrich 5c3160
diff --color -ruNp a/kexgen.c b/kexgen.c
Zoltan Fridrich 5c3160
--- a/kexgen.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/kexgen.c	2024-08-28 12:35:41.249432103 +0200
Jakub Jelen 51f5c1
@@ -44,7 +44,7 @@
Jakub Jelen def1de
 static int input_kex_gen_init(int, u_int32_t, struct ssh *);
Jakub Jelen def1de
 static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
Jakub Jelen def1de
 
Jakub Jelen def1de
-static int
Jakub Jelen def1de
+int
Jakub Jelen def1de
 kex_gen_hash(
Jakub Jelen def1de
     int hash_alg,
Jakub Jelen def1de
     const struct sshbuf *client_version,
Zoltan Fridrich 5c3160
diff --color -ruNp a/kexgssc.c b/kexgssc.c
Zoltan Fridrich 5c3160
--- a/kexgssc.c	1970-01-01 01:00:00.000000000 +0100
Zoltan Fridrich 5c3160
+++ b/kexgssc.c	2024-08-28 12:35:41.250432122 +0200
5c1da7
@@ -0,0 +1,612 @@
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Redistribution and use in source and binary forms, with or without
Jakub Jelen def1de
+ * modification, are permitted provided that the following conditions
Jakub Jelen def1de
+ * are met:
Jakub Jelen def1de
+ * 1. Redistributions of source code must retain the above copyright
Jakub Jelen def1de
+ *    notice, this list of conditions and the following disclaimer.
Jakub Jelen def1de
+ * 2. Redistributions in binary form must reproduce the above copyright
Jakub Jelen def1de
+ *    notice, this list of conditions and the following disclaimer in the
Jakub Jelen def1de
+ *    documentation and/or other materials provided with the distribution.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
Jakub Jelen def1de
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Jakub Jelen def1de
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Jakub Jelen def1de
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
Jakub Jelen def1de
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Jakub Jelen def1de
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Jakub Jelen def1de
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Jakub Jelen def1de
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Jakub Jelen def1de
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Jakub Jelen def1de
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "includes.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "includes.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include <openssl/crypto.h>
Jakub Jelen def1de
+#include <openssl/bn.h>
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include <string.h>
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "xmalloc.h"
Jakub Jelen def1de
+#include "sshbuf.h"
Jakub Jelen def1de
+#include "ssh2.h"
Jakub Jelen def1de
+#include "sshkey.h"
Jakub Jelen def1de
+#include "cipher.h"
Jakub Jelen def1de
+#include "kex.h"
Jakub Jelen def1de
+#include "log.h"
Jakub Jelen def1de
+#include "packet.h"
Jakub Jelen def1de
+#include "dh.h"
Jakub Jelen def1de
+#include "digest.h"
Jakub Jelen def1de
+#include "ssherr.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "ssh-gss.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+kexgss_client(struct ssh *ssh)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct kex *kex = ssh->kex;
Jakub Jelen def1de
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
Jakub Jelen def1de
+	    recv_tok = GSS_C_EMPTY_BUFFER,
Jakub Jelen def1de
+	    gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
Jakub Jelen def1de
+	Gssctxt *ctxt;
Jakub Jelen def1de
+	OM_uint32 maj_status, min_status, ret_flags;
Jakub Jelen def1de
+	struct sshbuf *server_blob = NULL;
Jakub Jelen def1de
+	struct sshbuf *shared_secret = NULL;
Jakub Jelen def1de
+	struct sshbuf *server_host_key_blob = NULL;
Jakub Jelen 51f5c1
+	struct sshbuf *empty = NULL;
Jakub Jelen def1de
+	u_char *msg;
Jakub Jelen def1de
+	int type = 0;
Jakub Jelen def1de
+	int first = 1;
Jakub Jelen def1de
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Jakub Jelen def1de
+	size_t hashlen;
Jakub Jelen def1de
+	u_char c;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Initialise our GSSAPI world */
Jakub Jelen def1de
+	ssh_gssapi_build_ctx(&ctxt);
Jakub Jelen def1de
+	if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
Jakub Jelen def1de
+	    == GSS_C_NO_OID)
Jakub Jelen def1de
+		fatal("Couldn't identify host exchange");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (ssh_gssapi_import_name(ctxt, kex->gss_host))
Jakub Jelen def1de
+		fatal("Couldn't import hostname");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (kex->gss_client &&
Jakub Jelen def1de
+	    ssh_gssapi_client_identity(ctxt, kex->gss_client))
Jakub Jelen def1de
+		fatal("Couldn't acquire client credentials");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Step 1 */
Jakub Jelen def1de
+	switch (kex->kex_type) {
Jakub Jelen def1de
+	case KEX_GSS_GRP1_SHA1:
Jakub Jelen def1de
+	case KEX_GSS_GRP14_SHA1:
Jakub Jelen def1de
+	case KEX_GSS_GRP14_SHA256:
Jakub Jelen def1de
+	case KEX_GSS_GRP16_SHA512:
Jakub Jelen def1de
+		r = kex_dh_keypair(kex);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	case KEX_GSS_NISTP256_SHA256:
Jakub Jelen def1de
+		r = kex_ecdh_keypair(kex);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	case KEX_GSS_C25519_SHA256:
Jakub Jelen def1de
+		r = kex_c25519_keypair(kex);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	default:
Jakub Jelen 25c16c
+		fatal_f("Unexpected KEX type %d", kex->kex_type);
Jakub Jelen def1de
+	}
5c1da7
+	if (r != 0) {
5c1da7
+		ssh_gssapi_delete_ctx(&ctxt);
Jakub Jelen def1de
+		return r;
5c1da7
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	token_ptr = GSS_C_NO_BUFFER;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	do {
Jakub Jelen def1de
+		debug("Calling gss_init_sec_context");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		maj_status = ssh_gssapi_init_ctx(ctxt,
Jakub Jelen def1de
+		    kex->gss_deleg_creds, token_ptr, &send_tok,
Jakub Jelen def1de
+		    &ret_flags);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (GSS_ERROR(maj_status)) {
Jakub Jelen def1de
+			/* XXX Useles code: Missing send? */
Jakub Jelen def1de
+			if (send_tok.length != 0) {
Jakub Jelen def1de
+				if ((r = sshpkt_start(ssh,
Jakub Jelen def1de
+				        SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Jakub Jelen def1de
+				        send_tok.length)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			fatal("gss_init_context failed");
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/* If we've got an old receive buffer get rid of it */
Jakub Jelen def1de
+		if (token_ptr != GSS_C_NO_BUFFER)
Jakub Jelen def1de
+			gss_release_buffer(&min_status, &recv_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (maj_status == GSS_S_COMPLETE) {
Jakub Jelen def1de
+			/* If mutual state flag is not true, kex fails */
Jakub Jelen def1de
+			if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Jakub Jelen def1de
+				fatal("Mutual authentication failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* If integ avail flag is not true kex fails */
Jakub Jelen def1de
+			if (!(ret_flags & GSS_C_INTEG_FLAG))
Jakub Jelen def1de
+				fatal("Integrity check failed");
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/*
Jakub Jelen def1de
+		 * If we have data to send, then the last message that we
Jakub Jelen def1de
+		 * received cannot have been a 'complete'.
Jakub Jelen def1de
+		 */
Jakub Jelen def1de
+		if (send_tok.length != 0) {
Jakub Jelen def1de
+			if (first) {
Jakub Jelen def1de
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Jakub Jelen def1de
+				        send_tok.length)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
Jakub Jelen def1de
+					fatal("failed to construct packet: %s", ssh_err(r));
Jakub Jelen def1de
+				first = 0;
Jakub Jelen def1de
+			} else {
Jakub Jelen def1de
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Jakub Jelen def1de
+				        send_tok.length)) != 0)
Jakub Jelen def1de
+					fatal("failed to construct packet: %s", ssh_err(r));
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			if ((r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+				fatal("failed to send packet: %s", ssh_err(r));
Jakub Jelen def1de
+			gss_release_buffer(&min_status, &send_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* If we've sent them data, they should reply */
Jakub Jelen def1de
+			do {
Jakub Jelen def1de
+				type = ssh_packet_read(ssh);
Jakub Jelen def1de
+				if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
87ae5d
+					u_char *tmp = NULL;
5c1da7
+					size_t tmp_len = 0;
5c1da7
+
Jakub Jelen def1de
+					debug("Received KEXGSS_HOSTKEY");
Jakub Jelen def1de
+					if (server_host_key_blob)
Jakub Jelen def1de
+						fatal("Server host key received more than once");
5c1da7
+					if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Jakub Jelen def1de
+						fatal("Failed to read server host key: %s", ssh_err(r));
5c1da7
+					if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
5c1da7
+						fatal("sshbuf_from failed");
Jakub Jelen def1de
+				}
Jakub Jelen def1de
+			} while (type == SSH2_MSG_KEXGSS_HOSTKEY);
Jakub Jelen def1de
+
Jakub Jelen def1de
+			switch (type) {
Jakub Jelen def1de
+			case SSH2_MSG_KEXGSS_CONTINUE:
Jakub Jelen def1de
+				debug("Received GSSAPI_CONTINUE");
Jakub Jelen def1de
+				if (maj_status == GSS_S_COMPLETE)
Jakub Jelen def1de
+					fatal("GSSAPI Continue received from server when complete");
Jakub Jelen def1de
+				if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+				        &recv_tok)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+					fatal("Failed to read token: %s", ssh_err(r));
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			case SSH2_MSG_KEXGSS_COMPLETE:
Jakub Jelen def1de
+				debug("Received GSSAPI_COMPLETE");
Jakub Jelen def1de
+				if (msg_tok.value != NULL)
Jakub Jelen def1de
+				        fatal("Received GSSAPI_COMPLETE twice?");
Jakub Jelen def1de
+				if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
Jakub Jelen def1de
+				    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+				        &msg_tok)) != 0)
Jakub Jelen def1de
+					fatal("Failed to read message: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+				/* Is there a token included? */
Jakub Jelen def1de
+				if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+				if (c) {
Jakub Jelen def1de
+					if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
Jakub Jelen def1de
+					    ssh, &recv_tok)) != 0)
Jakub Jelen def1de
+						fatal("Failed to read token: %s", ssh_err(r));
Jakub Jelen def1de
+					/* If we're already complete - protocol error */
Jakub Jelen def1de
+					if (maj_status == GSS_S_COMPLETE)
Jakub Jelen def1de
+						sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Jakub Jelen def1de
+				} else {
Jakub Jelen def1de
+					/* No token included */
Jakub Jelen def1de
+					if (maj_status != GSS_S_COMPLETE)
Jakub Jelen def1de
+						sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Jakub Jelen def1de
+				}
Jakub Jelen def1de
+				if ((r = sshpkt_get_end(ssh)) != 0) {
Jakub Jelen def1de
+					fatal("Expecting end of packet.");
Jakub Jelen def1de
+				}
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			case SSH2_MSG_KEXGSS_ERROR:
Jakub Jelen def1de
+				debug("Received Error");
Jakub Jelen def1de
+				if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Jakub Jelen def1de
+				    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt_get failed: %s", ssh_err(r));
Jakub Jelen def1de
+				fatal("GSSAPI Error: \n%.400s", msg);
Jakub Jelen def1de
+			default:
Jakub Jelen def1de
+				sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
Jakub Jelen def1de
+				    type);
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			token_ptr = &recv_tok;
Jakub Jelen def1de
+		} else {
Jakub Jelen def1de
+			/* No data, and not complete */
Jakub Jelen def1de
+			if (maj_status != GSS_S_COMPLETE)
Jakub Jelen def1de
+				fatal("Not complete, and no token output");
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * We _must_ have received a COMPLETE message in reply from the
Jakub Jelen def1de
+	 * server, which will have set server_blob and msg_tok
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (type != SSH2_MSG_KEXGSS_COMPLETE)
Jakub Jelen def1de
+		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* compute shared secret */
Jakub Jelen def1de
+	switch (kex->kex_type) {
Jakub Jelen def1de
+	case KEX_GSS_GRP1_SHA1:
Jakub Jelen def1de
+	case KEX_GSS_GRP14_SHA1:
Jakub Jelen def1de
+	case KEX_GSS_GRP14_SHA256:
Jakub Jelen def1de
+	case KEX_GSS_GRP16_SHA512:
Jakub Jelen def1de
+		r = kex_dh_dec(kex, server_blob, &shared_secret);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	case KEX_GSS_C25519_SHA256:
Jakub Jelen def1de
+		if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
Jakub Jelen def1de
+			fatal("The received key has MSB of last octet set!");
Jakub Jelen def1de
+		r = kex_c25519_dec(kex, server_blob, &shared_secret);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	case KEX_GSS_NISTP256_SHA256:
Jakub Jelen def1de
+		if (sshbuf_len(server_blob) != 65)
Jakub Jelen def1de
+			fatal("The received NIST-P256 key did not match"
Jakub Jelen def1de
+			    "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
Jakub Jelen def1de
+			fatal("The received NIST-P256 key does not have first octet 0x04");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		r = kex_ecdh_dec(kex, server_blob, &shared_secret);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	default:
Jakub Jelen def1de
+		r = SSH_ERR_INVALID_ARGUMENT;
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (r != 0)
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+
Jakub Jelen 51f5c1
+	if ((empty = sshbuf_new()) == NULL) {
Jakub Jelen 51f5c1
+		r = SSH_ERR_ALLOC_FAIL;
Jakub Jelen 51f5c1
+		goto out;
Jakub Jelen 51f5c1
+	}
Jakub Jelen 51f5c1
+
Jakub Jelen def1de
+	hashlen = sizeof(hash);
Jakub Jelen def1de
+	if ((r = kex_gen_hash(
Jakub Jelen def1de
+	    kex->hash_alg,
Jakub Jelen def1de
+	    kex->client_version,
Jakub Jelen def1de
+	    kex->server_version,
Jakub Jelen def1de
+	    kex->my,
Jakub Jelen def1de
+	    kex->peer,
Jakub Jelen def1de
+	    (server_host_key_blob ? server_host_key_blob : empty),
Jakub Jelen def1de
+	    kex->client_pub,
Jakub Jelen def1de
+	    server_blob,
Jakub Jelen def1de
+	    shared_secret,
Jakub Jelen def1de
+	    hash, &hashlen)) != 0)
Jakub Jelen 25c16c
+		fatal_f("Unexpected KEX type %d", kex->kex_type);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gssbuf.value = hash;
Jakub Jelen def1de
+	gssbuf.length = hashlen;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Verify that the hash matches the MIC we just got. */
Jakub Jelen def1de
+	if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
Jakub Jelen def1de
+		sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_release_buffer(&min_status, &msg_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (kex->gss_deleg_creds)
Jakub Jelen def1de
+		ssh_gssapi_credentials_updated(ctxt);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_kex_context == NULL)
Jakub Jelen def1de
+		gss_kex_context = ctxt;
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		ssh_gssapi_delete_ctx(&ctxt);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Jakub Jelen def1de
+		r = kex_send_newkeys(ssh);
Jakub Jelen def1de
+
Jakub Jelen def1de
+out:
Jakub Jelen def1de
+	explicit_bzero(hash, sizeof(hash));
Jakub Jelen def1de
+	explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
Jakub Jelen def1de
+	sshbuf_free(empty);
Jakub Jelen def1de
+	sshbuf_free(server_host_key_blob);
Jakub Jelen def1de
+	sshbuf_free(server_blob);
Jakub Jelen def1de
+	sshbuf_free(shared_secret);
Jakub Jelen def1de
+	sshbuf_free(kex->client_pub);
Jakub Jelen def1de
+	kex->client_pub = NULL;
Jakub Jelen def1de
+	return r;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+kexgssgex_client(struct ssh *ssh)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct kex *kex = ssh->kex;
Jakub Jelen def1de
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
Jakub Jelen def1de
+	    recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
Jakub Jelen def1de
+            msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
Jakub Jelen def1de
+	Gssctxt *ctxt;
Jakub Jelen def1de
+	OM_uint32 maj_status, min_status, ret_flags;
Jakub Jelen def1de
+	struct sshbuf *shared_secret = NULL;
Jakub Jelen def1de
+	BIGNUM *p = NULL;
Jakub Jelen def1de
+	BIGNUM *g = NULL;
Jakub Jelen def1de
+	struct sshbuf *buf = NULL;
Jakub Jelen def1de
+	struct sshbuf *server_host_key_blob = NULL;
Jakub Jelen def1de
+	struct sshbuf *server_blob = NULL;
Jakub Jelen def1de
+	BIGNUM *dh_server_pub = NULL;
Jakub Jelen def1de
+	u_char *msg;
Jakub Jelen def1de
+	int type = 0;
Jakub Jelen def1de
+	int first = 1;
Jakub Jelen def1de
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Jakub Jelen def1de
+	size_t hashlen;
Jakub Jelen def1de
+	const BIGNUM *pub_key, *dh_p, *dh_g;
Jakub Jelen def1de
+	int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
Jakub Jelen 51f5c1
+	struct sshbuf *empty = NULL;
Jakub Jelen def1de
+	u_char c;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Initialise our GSSAPI world */
Jakub Jelen def1de
+	ssh_gssapi_build_ctx(&ctxt);
Jakub Jelen def1de
+	if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
Jakub Jelen def1de
+	    == GSS_C_NO_OID)
Jakub Jelen def1de
+		fatal("Couldn't identify host exchange");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (ssh_gssapi_import_name(ctxt, kex->gss_host))
Jakub Jelen def1de
+		fatal("Couldn't import hostname");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (kex->gss_client &&
Jakub Jelen def1de
+	    ssh_gssapi_client_identity(ctxt, kex->gss_client))
Jakub Jelen def1de
+		fatal("Couldn't acquire client credentials");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	debug("Doing group exchange");
Jakub Jelen def1de
+	nbits = dh_estimate(kex->dh_need * 8);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	kex->min = DH_GRP_MIN;
Jakub Jelen def1de
+	kex->max = DH_GRP_MAX;
Jakub Jelen def1de
+	kex->nbits = nbits;
Jakub Jelen def1de
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_u32(ssh, min)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_u32(ssh, max)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+		fatal("Failed to construct a packet: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
Jakub Jelen def1de
+		fatal("Error: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+		fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (BN_num_bits(p) < min || BN_num_bits(p) > max)
Jakub Jelen def1de
+		fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
Jakub Jelen def1de
+		    min, BN_num_bits(p), max);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((kex->dh = dh_new_group(g, p)) == NULL)
Jakub Jelen def1de
+		fatal("dn_new_group() failed");
Jakub Jelen def1de
+	p = g = NULL; /* belong to kex->dh now */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+	DH_get0_key(kex->dh, &pub_key, NULL);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	token_ptr = GSS_C_NO_BUFFER;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	do {
Jakub Jelen def1de
+		/* Step 2 - call GSS_Init_sec_context() */
Jakub Jelen def1de
+		debug("Calling gss_init_sec_context");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		maj_status = ssh_gssapi_init_ctx(ctxt,
Jakub Jelen def1de
+		    kex->gss_deleg_creds, token_ptr, &send_tok,
Jakub Jelen def1de
+		    &ret_flags);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (GSS_ERROR(maj_status)) {
Jakub Jelen def1de
+			/* XXX Useles code: Missing send? */
Jakub Jelen def1de
+			if (send_tok.length != 0) {
Jakub Jelen def1de
+				if ((r = sshpkt_start(ssh,
Jakub Jelen def1de
+				        SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Jakub Jelen def1de
+				        send_tok.length)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			fatal("gss_init_context failed");
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/* If we've got an old receive buffer get rid of it */
Jakub Jelen def1de
+		if (token_ptr != GSS_C_NO_BUFFER)
Jakub Jelen def1de
+			gss_release_buffer(&min_status, &recv_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (maj_status == GSS_S_COMPLETE) {
Jakub Jelen def1de
+			/* If mutual state flag is not true, kex fails */
Jakub Jelen def1de
+			if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Jakub Jelen def1de
+				fatal("Mutual authentication failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* If integ avail flag is not true kex fails */
Jakub Jelen def1de
+			if (!(ret_flags & GSS_C_INTEG_FLAG))
Jakub Jelen def1de
+				fatal("Integrity check failed");
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/*
Jakub Jelen def1de
+		 * If we have data to send, then the last message that we
Jakub Jelen def1de
+		 * received cannot have been a 'complete'.
Jakub Jelen def1de
+		 */
Jakub Jelen def1de
+		if (send_tok.length != 0) {
Jakub Jelen def1de
+			if (first) {
Jakub Jelen def1de
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Jakub Jelen def1de
+				        send_tok.length)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+				first = 0;
Jakub Jelen def1de
+			} else {
Jakub Jelen def1de
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_put_string(ssh,send_tok.value,
Jakub Jelen def1de
+				        send_tok.length)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			if ((r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt_send failed: %s", ssh_err(r));
Jakub Jelen def1de
+			gss_release_buffer(&min_status, &send_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* If we've sent them data, they should reply */
Jakub Jelen def1de
+			do {
Jakub Jelen def1de
+				type = ssh_packet_read(ssh);
Jakub Jelen def1de
+				if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
87ae5d
+					u_char *tmp = NULL;
5c1da7
+					size_t tmp_len = 0;
5c1da7
+
Jakub Jelen def1de
+					debug("Received KEXGSS_HOSTKEY");
Jakub Jelen def1de
+					if (server_host_key_blob)
Jakub Jelen def1de
+						fatal("Server host key received more than once");
5c1da7
+					if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Jakub Jelen def1de
+						fatal("sshpkt failed: %s", ssh_err(r));
5c1da7
+					if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
5c1da7
+						fatal("sshbuf_from failed");
Jakub Jelen def1de
+				}
Jakub Jelen def1de
+			} while (type == SSH2_MSG_KEXGSS_HOSTKEY);
Jakub Jelen def1de
+
Jakub Jelen def1de
+			switch (type) {
Jakub Jelen def1de
+			case SSH2_MSG_KEXGSS_CONTINUE:
Jakub Jelen def1de
+				debug("Received GSSAPI_CONTINUE");
Jakub Jelen def1de
+				if (maj_status == GSS_S_COMPLETE)
Jakub Jelen def1de
+					fatal("GSSAPI Continue received from server when complete");
Jakub Jelen def1de
+				if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+				        &recv_tok)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			case SSH2_MSG_KEXGSS_COMPLETE:
Jakub Jelen def1de
+				debug("Received GSSAPI_COMPLETE");
Jakub Jelen def1de
+				if (msg_tok.value != NULL)
Jakub Jelen def1de
+				        fatal("Received GSSAPI_COMPLETE twice?");
Jakub Jelen def1de
+				if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
Jakub Jelen def1de
+				    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+				        &msg_tok)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+				/* Is there a token included? */
Jakub Jelen def1de
+				if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+				if (c) {
Jakub Jelen def1de
+					if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
Jakub Jelen def1de
+					        ssh, &recv_tok)) != 0 ||
Jakub Jelen def1de
+					    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+						fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+					/* If we're already complete - protocol error */
Jakub Jelen def1de
+					if (maj_status == GSS_S_COMPLETE)
Jakub Jelen def1de
+						sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Jakub Jelen def1de
+				} else {
Jakub Jelen def1de
+					/* No token included */
Jakub Jelen def1de
+					if (maj_status != GSS_S_COMPLETE)
Jakub Jelen def1de
+						sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Jakub Jelen def1de
+				}
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			case SSH2_MSG_KEXGSS_ERROR:
Jakub Jelen def1de
+				debug("Received Error");
Jakub Jelen def1de
+				if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Jakub Jelen def1de
+				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Jakub Jelen def1de
+				    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+					fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+				fatal("GSSAPI Error: \n%.400s", msg);
Jakub Jelen def1de
+			default:
Jakub Jelen def1de
+				sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
Jakub Jelen def1de
+				    type);
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			token_ptr = &recv_tok;
Jakub Jelen def1de
+		} else {
Jakub Jelen def1de
+			/* No data, and not complete */
Jakub Jelen def1de
+			if (maj_status != GSS_S_COMPLETE)
Jakub Jelen def1de
+				fatal("Not complete, and no token output");
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * We _must_ have received a COMPLETE message in reply from the
Jakub Jelen def1de
+	 * server, which will have set dh_server_pub and msg_tok
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (type != SSH2_MSG_KEXGSS_COMPLETE)
Jakub Jelen def1de
+		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* 7. C verifies that the key Q_S is valid */
Jakub Jelen def1de
+	/* 8. C computes shared secret */
Jakub Jelen def1de
+	if ((buf = sshbuf_new()) == NULL ||
Jakub Jelen def1de
+	    (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
Jakub Jelen def1de
+	    (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+	sshbuf_free(buf);
Jakub Jelen 51f5c1
+	buf = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((shared_secret = sshbuf_new()) == NULL) {
Jakub Jelen def1de
+		r = SSH_ERR_ALLOC_FAIL;
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
Jakub Jelen def1de
+		goto out;
Jakub Jelen 51f5c1
+	if ((empty = sshbuf_new()) == NULL) {
Jakub Jelen 51f5c1
+		r = SSH_ERR_ALLOC_FAIL;
Jakub Jelen 51f5c1
+		goto out;
Jakub Jelen 51f5c1
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Jakub Jelen def1de
+	hashlen = sizeof(hash);
Jakub Jelen def1de
+	if ((r = kexgex_hash(
Jakub Jelen def1de
+	    kex->hash_alg,
Jakub Jelen def1de
+	    kex->client_version,
Jakub Jelen def1de
+	    kex->server_version,
Jakub Jelen def1de
+	    kex->my,
Jakub Jelen def1de
+	    kex->peer,
Jakub Jelen def1de
+	    (server_host_key_blob ? server_host_key_blob : empty),
Jakub Jelen def1de
+ 	    kex->min, kex->nbits, kex->max,
Jakub Jelen def1de
+	    dh_p, dh_g,
Jakub Jelen def1de
+	    pub_key,
Jakub Jelen def1de
+	    dh_server_pub,
Jakub Jelen def1de
+	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Jakub Jelen def1de
+	    hash, &hashlen)) != 0)
Jakub Jelen def1de
+		fatal("Failed to calculate hash: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gssbuf.value = hash;
Jakub Jelen def1de
+	gssbuf.length = hashlen;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Verify that the hash matches the MIC we just got. */
Jakub Jelen def1de
+	if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
Jakub Jelen def1de
+		sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_release_buffer(&min_status, &msg_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (kex->gss_deleg_creds)
Jakub Jelen def1de
+		ssh_gssapi_credentials_updated(ctxt);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_kex_context == NULL)
Jakub Jelen def1de
+		gss_kex_context = ctxt;
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		ssh_gssapi_delete_ctx(&ctxt);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Finally derive the keys and send them */
Jakub Jelen def1de
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Jakub Jelen def1de
+		r = kex_send_newkeys(ssh);
Jakub Jelen def1de
+out:
Jakub Jelen 51f5c1
+	sshbuf_free(buf);
Jakub Jelen def1de
+	sshbuf_free(server_blob);
Jakub Jelen def1de
+	sshbuf_free(empty);
Jakub Jelen def1de
+	explicit_bzero(hash, sizeof(hash));
Jakub Jelen def1de
+	DH_free(kex->dh);
Jakub Jelen def1de
+	kex->dh = NULL;
Jakub Jelen def1de
+	BN_clear_free(dh_server_pub);
Jakub Jelen def1de
+	sshbuf_free(shared_secret);
Jakub Jelen def1de
+	sshbuf_free(server_host_key_blob);
Jakub Jelen def1de
+	return r;
Jakub Jelen def1de
+}
Dmitry Belyavskiy f561c6
+
Jakub Jelen def1de
+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
Zoltan Fridrich 5c3160
diff --color -ruNp a/kexgsss.c b/kexgsss.c
Zoltan Fridrich 5c3160
--- a/kexgsss.c	1970-01-01 01:00:00.000000000 +0100
Zoltan Fridrich 5c3160
+++ b/kexgsss.c	2024-08-28 12:35:41.250432122 +0200
5c1da7
@@ -0,0 +1,482 @@
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Redistribution and use in source and binary forms, with or without
Jakub Jelen def1de
+ * modification, are permitted provided that the following conditions
Jakub Jelen def1de
+ * are met:
Jakub Jelen def1de
+ * 1. Redistributions of source code must retain the above copyright
Jakub Jelen def1de
+ *    notice, this list of conditions and the following disclaimer.
Jakub Jelen def1de
+ * 2. Redistributions in binary form must reproduce the above copyright
Jakub Jelen def1de
+ *    notice, this list of conditions and the following disclaimer in the
Jakub Jelen def1de
+ *    documentation and/or other materials provided with the distribution.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
Jakub Jelen def1de
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Jakub Jelen def1de
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Jakub Jelen def1de
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
Jakub Jelen def1de
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Jakub Jelen def1de
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Jakub Jelen def1de
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Jakub Jelen def1de
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Jakub Jelen def1de
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Jakub Jelen def1de
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "includes.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include <string.h>
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include <openssl/crypto.h>
Jakub Jelen def1de
+#include <openssl/bn.h>
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "xmalloc.h"
Jakub Jelen def1de
+#include "sshbuf.h"
Jakub Jelen def1de
+#include "ssh2.h"
Jakub Jelen def1de
+#include "sshkey.h"
Jakub Jelen def1de
+#include "cipher.h"
Jakub Jelen def1de
+#include "kex.h"
Jakub Jelen def1de
+#include "log.h"
Jakub Jelen def1de
+#include "packet.h"
Jakub Jelen def1de
+#include "dh.h"
Jakub Jelen def1de
+#include "ssh-gss.h"
Jakub Jelen def1de
+#include "monitor_wrap.h"
Jakub Jelen def1de
+#include "misc.h"      /* servconf.h needs misc.h for struct ForwardOptions */
Jakub Jelen def1de
+#include "servconf.h"
Jakub Jelen def1de
+#include "ssh-gss.h"
Jakub Jelen def1de
+#include "digest.h"
Jakub Jelen def1de
+#include "ssherr.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+extern ServerOptions options;
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+kexgss_server(struct ssh *ssh)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct kex *kex = ssh->kex;
Jakub Jelen def1de
+	OM_uint32 maj_status, min_status;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * Some GSSAPI implementations use the input value of ret_flags (an
Jakub Jelen def1de
+	 * output variable) as a means of triggering mechanism specific
Jakub Jelen def1de
+	 * features. Initializing it to zero avoids inadvertently
Jakub Jelen def1de
+	 * activating this non-standard behaviour.
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	OM_uint32 ret_flags = 0;
5c1da7
+	gss_buffer_desc gssbuf = {0, NULL}, recv_tok, msg_tok;
Jakub Jelen def1de
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Jakub Jelen def1de
+	Gssctxt *ctxt = NULL;
Jakub Jelen def1de
+	struct sshbuf *shared_secret = NULL;
Jakub Jelen def1de
+	struct sshbuf *client_pubkey = NULL;
Jakub Jelen def1de
+	struct sshbuf *server_pubkey = NULL;
Jakub Jelen def1de
+	struct sshbuf *empty = sshbuf_new();
Jakub Jelen def1de
+	int type = 0;
Jakub Jelen def1de
+	gss_OID oid;
Jakub Jelen def1de
+	char *mechs;
Jakub Jelen def1de
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Jakub Jelen def1de
+	size_t hashlen;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Initialise GSSAPI */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* If we're rekeying, privsep means that some of the private structures
Jakub Jelen def1de
+	 * in the GSSAPI code are no longer available. This kludges them back
Jakub Jelen def1de
+	 * into life
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+	if (!ssh_gssapi_oid_table_ok()) {
Jakub Jelen def1de
+		mechs = ssh_gssapi_server_mechanisms();
Jakub Jelen def1de
+		free(mechs);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen 25c16c
+	debug2_f("Identifying %s", kex->name);
Jakub Jelen def1de
+	oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
Jakub Jelen def1de
+	if (oid == GSS_C_NO_OID)
Jakub Jelen def1de
+	   fatal("Unknown gssapi mechanism");
Jakub Jelen def1de
+
Jakub Jelen 25c16c
+	debug2_f("Acquiring credentials");
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid)))
Jakub Jelen def1de
+		fatal("Unable to acquire credentials for the server");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	do {
Jakub Jelen def1de
+		debug("Wait SSH2_MSG_KEXGSS_INIT");
Jakub Jelen def1de
+		type = ssh_packet_read(ssh);
Jakub Jelen def1de
+		switch(type) {
Jakub Jelen def1de
+		case SSH2_MSG_KEXGSS_INIT:
5c1da7
+			if (gssbuf.value != NULL)
Jakub Jelen def1de
+				fatal("Received KEXGSS_INIT after initialising");
Jakub Jelen def1de
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+			        &recv_tok)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+			switch (kex->kex_type) {
Jakub Jelen def1de
+			case KEX_GSS_GRP1_SHA1:
Jakub Jelen def1de
+			case KEX_GSS_GRP14_SHA1:
Jakub Jelen def1de
+			case KEX_GSS_GRP14_SHA256:
Jakub Jelen def1de
+			case KEX_GSS_GRP16_SHA512:
Jakub Jelen def1de
+				r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
Jakub Jelen def1de
+				    &shared_secret);
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			case KEX_GSS_NISTP256_SHA256:
Jakub Jelen def1de
+				r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
Jakub Jelen def1de
+				    &shared_secret);
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			case KEX_GSS_C25519_SHA256:
Jakub Jelen def1de
+				r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
Jakub Jelen def1de
+				    &shared_secret);
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			default:
Jakub Jelen 25c16c
+				fatal_f("Unexpected KEX type %d", kex->kex_type);
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			if (r != 0)
Jakub Jelen def1de
+				goto out;
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
5c1da7
+
5c1da7
+			/* Calculate the hash early so we can free the
5c1da7
+			* client_pubkey, which has reference to the parent
5c1da7
+			* buffer state->incoming_packet
5c1da7
+			*/
5c1da7
+			hashlen = sizeof(hash);
5c1da7
+			if ((r = kex_gen_hash(
5c1da7
+			    kex->hash_alg,
5c1da7
+			    kex->client_version,
5c1da7
+			    kex->server_version,
5c1da7
+			    kex->peer,
5c1da7
+			    kex->my,
5c1da7
+			    empty,
5c1da7
+			    client_pubkey,
5c1da7
+			    server_pubkey,
5c1da7
+			    shared_secret,
5c1da7
+			    hash, &hashlen)) != 0)
5c1da7
+				goto out;
5c1da7
+
5c1da7
+			gssbuf.value = hash;
5c1da7
+			gssbuf.length = hashlen;
5c1da7
+
5c1da7
+			sshbuf_free(client_pubkey);
5c1da7
+			client_pubkey = NULL;
5c1da7
+
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		case SSH2_MSG_KEXGSS_CONTINUE:
Jakub Jelen def1de
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+			        &recv_tok)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		default:
Jakub Jelen def1de
+			sshpkt_disconnect(ssh,
Jakub Jelen def1de
+			    "Protocol error: didn't expect packet type %d",
Jakub Jelen def1de
+			    type);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+		maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok,
Dmitry Belyavskiy 089d79
+		    &send_tok, &ret_flags);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		gss_release_buffer(&min_status, &recv_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
Jakub Jelen def1de
+			fatal("Zero length token output when incomplete");
Jakub Jelen def1de
+
5c1da7
+		if (gssbuf.value == NULL)
Jakub Jelen def1de
+			fatal("No client public key");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (maj_status & GSS_S_CONTINUE_NEEDED) {
Jakub Jelen def1de
+			debug("Sending GSSAPI_CONTINUE");
Jakub Jelen def1de
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			gss_release_buffer(&min_status, &send_tok);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (GSS_ERROR(maj_status)) {
Jakub Jelen def1de
+		if (send_tok.length > 0) {
Jakub Jelen def1de
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		fatal("accept_ctx died");
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Jakub Jelen def1de
+		fatal("Mutual Authentication flag wasn't set");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!(ret_flags & GSS_C_INTEG_FLAG))
Jakub Jelen def1de
+		fatal("Integrity flag wasn't set");
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))
Jakub Jelen def1de
+		fatal("Couldn't get MIC");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
Jakub Jelen def1de
+		fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (send_tok.length != 0) {
Jakub Jelen def1de
+		if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
Jakub Jelen def1de
+		    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Jakub Jelen def1de
+			fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
Jakub Jelen def1de
+			fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if ((r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+		fatal("sshpkt_send failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_release_buffer(&min_status, &send_tok);
Jakub Jelen def1de
+	gss_release_buffer(&min_status, &msg_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_kex_context == NULL)
Jakub Jelen def1de
+		gss_kex_context = ctxt;
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		ssh_gssapi_delete_ctx(&ctxt);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Jakub Jelen def1de
+		r = kex_send_newkeys(ssh);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* If this was a rekey, then save out any delegated credentials we
Jakub Jelen def1de
+	 * just exchanged.  */
Jakub Jelen def1de
+	if (options.gss_store_rekey)
Jakub Jelen def1de
+		ssh_gssapi_rekey_creds();
Jakub Jelen def1de
+out:
Jakub Jelen def1de
+	sshbuf_free(empty);
Jakub Jelen def1de
+	explicit_bzero(hash, sizeof(hash));
Jakub Jelen def1de
+	sshbuf_free(shared_secret);
Jakub Jelen def1de
+	sshbuf_free(client_pubkey);
Jakub Jelen def1de
+	sshbuf_free(server_pubkey);
Jakub Jelen def1de
+	return r;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+kexgssgex_server(struct ssh *ssh)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct kex *kex = ssh->kex;
Jakub Jelen def1de
+	OM_uint32 maj_status, min_status;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * Some GSSAPI implementations use the input value of ret_flags (an
Jakub Jelen def1de
+	 * output variable) as a means of triggering mechanism specific
Jakub Jelen def1de
+	 * features. Initializing it to zero avoids inadvertently
Jakub Jelen def1de
+	 * activating this non-standard behaviour.
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	OM_uint32 ret_flags = 0;
Jakub Jelen def1de
+	gss_buffer_desc gssbuf, recv_tok, msg_tok;
Jakub Jelen def1de
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Jakub Jelen def1de
+	Gssctxt *ctxt = NULL;
Jakub Jelen def1de
+	struct sshbuf *shared_secret = NULL;
Jakub Jelen def1de
+	int type = 0;
Jakub Jelen def1de
+	gss_OID oid;
Jakub Jelen def1de
+	char *mechs;
Jakub Jelen def1de
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Jakub Jelen def1de
+	size_t hashlen;
Jakub Jelen def1de
+	BIGNUM *dh_client_pub = NULL;
Jakub Jelen def1de
+	const BIGNUM *pub_key, *dh_p, *dh_g;
Jakub Jelen def1de
+	int min = -1, max = -1, nbits = -1;
Jakub Jelen def1de
+	int cmin = -1, cmax = -1; /* client proposal */
Jakub Jelen def1de
+	struct sshbuf *empty = sshbuf_new();
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Initialise GSSAPI */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* If we're rekeying, privsep means that some of the private structures
Jakub Jelen def1de
+	 * in the GSSAPI code are no longer available. This kludges them back
Jakub Jelen def1de
+	 * into life
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+	if (!ssh_gssapi_oid_table_ok())
Jakub Jelen def1de
+		if ((mechs = ssh_gssapi_server_mechanisms()))
Jakub Jelen def1de
+			free(mechs);
Jakub Jelen def1de
+
Jakub Jelen 25c16c
+	debug2_f("Identifying %s", kex->name);
Jakub Jelen def1de
+	oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
Jakub Jelen def1de
+	if (oid == GSS_C_NO_OID)
Jakub Jelen def1de
+	   fatal("Unknown gssapi mechanism");
Jakub Jelen def1de
+
Jakub Jelen 25c16c
+	debug2_f("Acquiring credentials");
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid)))
Jakub Jelen def1de
+		fatal("Unable to acquire credentials for the server");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* 5. S generates an ephemeral key pair (do the allocations early) */
Jakub Jelen def1de
+	debug("Doing group exchange");
Jakub Jelen def1de
+	ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
Jakub Jelen def1de
+	/* store client proposal to provide valid signature */
Jakub Jelen def1de
+	if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+		fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+	kex->nbits = nbits;
Jakub Jelen def1de
+	kex->min = cmin;
Jakub Jelen def1de
+	kex->max = cmax;
Jakub Jelen def1de
+	min = MAX(DH_GRP_MIN, cmin);
Jakub Jelen def1de
+	max = MIN(DH_GRP_MAX, cmax);
Jakub Jelen def1de
+	nbits = MAXIMUM(DH_GRP_MIN, nbits);
Jakub Jelen def1de
+	nbits = MINIMUM(DH_GRP_MAX, nbits);
Jakub Jelen def1de
+	if (max < min || nbits < min || max < nbits)
Jakub Jelen def1de
+		fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
Jakub Jelen def1de
+		    min, nbits, max);
Dmitry Belyavskiy 089d79
+	kex->dh = mm_choose_dh(min, nbits, max);
Jakub Jelen def1de
+	if (kex->dh == NULL) {
Jakub Jelen def1de
+		sshpkt_disconnect(ssh, "Protocol error: no matching group found");
Jakub Jelen def1de
+		fatal("Protocol error: no matching group found");
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Jakub Jelen def1de
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+		fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = ssh_packet_write_wait(ssh)) != 0)
Jakub Jelen def1de
+		fatal("ssh_packet_write_wait: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Compute our exchange value in parallel with the client */
Jakub Jelen def1de
+	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	do {
Jakub Jelen def1de
+		debug("Wait SSH2_MSG_GSSAPI_INIT");
Jakub Jelen def1de
+		type = ssh_packet_read(ssh);
Jakub Jelen def1de
+		switch(type) {
Jakub Jelen def1de
+		case SSH2_MSG_KEXGSS_INIT:
Jakub Jelen def1de
+			if (dh_client_pub != NULL)
Jakub Jelen def1de
+				fatal("Received KEXGSS_INIT after initialising");
Jakub Jelen def1de
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+			        &recv_tok)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		case SSH2_MSG_KEXGSS_CONTINUE:
Jakub Jelen def1de
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Jakub Jelen def1de
+			        &recv_tok)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_get_end(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		default:
Jakub Jelen def1de
+			sshpkt_disconnect(ssh,
Jakub Jelen def1de
+			    "Protocol error: didn't expect packet type %d",
Jakub Jelen def1de
+			    type);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+		maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok,
Dmitry Belyavskiy 089d79
+		    &send_tok, &ret_flags);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		gss_release_buffer(&min_status, &recv_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
Jakub Jelen def1de
+			fatal("Zero length token output when incomplete");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (dh_client_pub == NULL)
Jakub Jelen def1de
+			fatal("No client public key");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (maj_status & GSS_S_CONTINUE_NEEDED) {
Jakub Jelen def1de
+			debug("Sending GSSAPI_CONTINUE");
Jakub Jelen def1de
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+			gss_release_buffer(&min_status, &send_tok);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (GSS_ERROR(maj_status)) {
Jakub Jelen def1de
+		if (send_tok.length > 0) {
Jakub Jelen def1de
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Jakub Jelen def1de
+			    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+				fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		fatal("accept_ctx died");
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Jakub Jelen def1de
+		fatal("Mutual Authentication flag wasn't set");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!(ret_flags & GSS_C_INTEG_FLAG))
Jakub Jelen def1de
+		fatal("Integrity flag wasn't set");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* calculate shared secret */
Jakub Jelen def1de
+	if ((shared_secret = sshbuf_new()) == NULL) {
Jakub Jelen def1de
+		r = SSH_ERR_ALLOC_FAIL;
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	DH_get0_key(kex->dh, &pub_key, NULL);
Jakub Jelen def1de
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Jakub Jelen def1de
+	hashlen = sizeof(hash);
Jakub Jelen def1de
+	if ((r = kexgex_hash(
Jakub Jelen def1de
+	    kex->hash_alg,
Jakub Jelen def1de
+	    kex->client_version,
Jakub Jelen def1de
+	    kex->server_version,
Jakub Jelen def1de
+	    kex->peer,
Jakub Jelen def1de
+	    kex->my,
Jakub Jelen def1de
+	    empty,
Jakub Jelen def1de
+	    cmin, nbits, cmax,
Jakub Jelen def1de
+	    dh_p, dh_g,
Jakub Jelen def1de
+	    dh_client_pub,
Jakub Jelen def1de
+	    pub_key,
Jakub Jelen def1de
+	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Jakub Jelen def1de
+	    hash, &hashlen)) != 0)
Jakub Jelen def1de
+		fatal("kexgex_hash failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gssbuf.value = hash;
Jakub Jelen def1de
+	gssbuf.length = hashlen;
Jakub Jelen def1de
+
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))
Jakub Jelen def1de
+		fatal("Couldn't get MIC");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
Jakub Jelen def1de
+		fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (send_tok.length != 0) {
Jakub Jelen def1de
+		if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
Jakub Jelen def1de
+		    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Jakub Jelen def1de
+			fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
Jakub Jelen def1de
+			fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if ((r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
+		fatal("sshpkt failed: %s", ssh_err(r));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_release_buffer(&min_status, &send_tok);
Jakub Jelen def1de
+	gss_release_buffer(&min_status, &msg_tok);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_kex_context == NULL)
Jakub Jelen def1de
+		gss_kex_context = ctxt;
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		ssh_gssapi_delete_ctx(&ctxt);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Finally derive the keys and send them */
Jakub Jelen def1de
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Jakub Jelen def1de
+		r = kex_send_newkeys(ssh);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* If this was a rekey, then save out any delegated credentials we
Jakub Jelen def1de
+	 * just exchanged.  */
Jakub Jelen def1de
+	if (options.gss_store_rekey)
Jakub Jelen def1de
+		ssh_gssapi_rekey_creds();
Jakub Jelen def1de
+out:
Jakub Jelen def1de
+	sshbuf_free(empty);
Jakub Jelen def1de
+	explicit_bzero(hash, sizeof(hash));
Jakub Jelen def1de
+	DH_free(kex->dh);
Jakub Jelen def1de
+	kex->dh = NULL;
Jakub Jelen def1de
+	BN_clear_free(dh_client_pub);
Jakub Jelen def1de
+	sshbuf_free(shared_secret);
Jakub Jelen def1de
+	return r;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
Zoltan Fridrich 5c3160
diff --color -ruNp a/kex.h b/kex.h
Zoltan Fridrich 5c3160
--- a/kex.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/kex.h	2024-08-28 12:35:41.249432103 +0200
Zoltan Fridrich 5c3160
@@ -102,6 +102,15 @@ enum kex_exchange {
Zoltan Fridrich 5c3160
 	KEX_ECDH_SHA2,
Zoltan Fridrich 5c3160
 	KEX_C25519_SHA256,
Zoltan Fridrich 5c3160
 	KEX_KEM_SNTRUP761X25519_SHA512,
Zoltan Fridrich 5c3160
+#ifdef GSSAPI
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP1_SHA1,
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP14_SHA1,
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP14_SHA256,
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP16_SHA512,
Zoltan Fridrich 5c3160
+	KEX_GSS_GEX_SHA1,
Zoltan Fridrich 5c3160
+	KEX_GSS_NISTP256_SHA256,
Zoltan Fridrich 5c3160
+	KEX_GSS_C25519_SHA256,
Zoltan Fridrich 5c3160
+#endif
Zoltan Fridrich 5c3160
 	KEX_MAX
Zoltan Fridrich 5c3160
 };
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
@@ -164,6 +173,12 @@ struct kex {
Zoltan Fridrich 5c3160
 	u_int	flags;
Zoltan Fridrich 5c3160
 	int	hash_alg;
Zoltan Fridrich 5c3160
 	int	ec_nid;
Zoltan Fridrich 5c3160
+#ifdef GSSAPI
Zoltan Fridrich 5c3160
+	int	gss_deleg_creds;
Zoltan Fridrich 5c3160
+	int	gss_trust_dns;
Zoltan Fridrich 5c3160
+	char    *gss_host;
Zoltan Fridrich 5c3160
+	char	*gss_client;
Zoltan Fridrich 5c3160
+#endif
Zoltan Fridrich 5c3160
 	char	*failed_choice;
Zoltan Fridrich 5c3160
 	int	(*verify_host_key)(struct sshkey *, struct ssh *);
Zoltan Fridrich 5c3160
 	struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
Zoltan Fridrich 5c3160
@@ -189,8 +204,10 @@ int	 kex_hash_from_name(const char *);
Zoltan Fridrich 5c3160
 int	 kex_nid_from_name(const char *);
Zoltan Fridrich 5c3160
 int	 kex_names_valid(const char *);
Zoltan Fridrich 5c3160
 char	*kex_alg_list(char);
Zoltan Fridrich 5c3160
+char	*kex_gss_alg_list(char);
Zoltan Fridrich 5c3160
 char	*kex_names_cat(const char *, const char *);
Zoltan Fridrich 5c3160
 int	 kex_has_any_alg(const char *, const char *);
Zoltan Fridrich 5c3160
+int	 kex_gss_names_valid(const char *);
Zoltan Fridrich 5c3160
 int	 kex_assemble_names(char **, const char *, const char *);
Zoltan Fridrich 5c3160
 void	 kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX],
Zoltan Fridrich 5c3160
     const char *, const char *, const char *, const char *, const char *);
Zoltan Fridrich 5c3160
@@ -224,6 +241,12 @@ int	 kexgex_client(struct ssh *);
Zoltan Fridrich 5c3160
 int	 kexgex_server(struct ssh *);
Zoltan Fridrich 5c3160
 int	 kex_gen_client(struct ssh *);
Zoltan Fridrich 5c3160
 int	 kex_gen_server(struct ssh *);
Zoltan Fridrich 5c3160
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Zoltan Fridrich 5c3160
+int	 kexgssgex_client(struct ssh *);
Zoltan Fridrich 5c3160
+int	 kexgssgex_server(struct ssh *);
Zoltan Fridrich 5c3160
+int	 kexgss_client(struct ssh *);
Zoltan Fridrich 5c3160
+int	 kexgss_server(struct ssh *);
Zoltan Fridrich 5c3160
+#endif
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 int	 kex_dh_keypair(struct kex *);
Zoltan Fridrich 5c3160
 int	 kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
Zoltan Fridrich 5c3160
@@ -256,6 +279,12 @@ int	 kexgex_hash(int, const struct sshbu
Zoltan Fridrich 5c3160
     const BIGNUM *, const u_char *, size_t,
Zoltan Fridrich 5c3160
     u_char *, size_t *);
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
+int	 kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
Zoltan Fridrich 5c3160
+    const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
Zoltan Fridrich 5c3160
+    const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
Zoltan Fridrich 5c3160
+    const struct sshbuf *client_pub, const struct sshbuf *server_pub,
Zoltan Fridrich 5c3160
+    const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 void	kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
Zoltan Fridrich 5c3160
 	__attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
Zoltan Fridrich 5c3160
 	__attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
Zoltan Fridrich 5c3160
diff --color -ruNp a/kex-names.c b/kex-names.c
Zoltan Fridrich 5c3160
--- a/kex-names.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/kex-names.c	2024-08-28 12:35:41.249432103 +0200
Zoltan Fridrich 5c3160
@@ -45,6 +45,10 @@
Zoltan Fridrich 5c3160
 #include "ssherr.h"
Zoltan Fridrich 5c3160
 #include "xmalloc.h"
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
+#ifdef GSSAPI
Zoltan Fridrich 5c3160
+#include "ssh-gss.h"
Zoltan Fridrich 5c3160
+#endif
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 struct kexalg {
Zoltan Fridrich 5c3160
 	char *name;
Zoltan Fridrich 5c3160
 	u_int type;
Zoltan Fridrich 5c3160
@@ -83,15 +87,28 @@ static const struct kexalg kexalgs[] = {
Zoltan Fridrich 5c3160
 #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Zoltan Fridrich 5c3160
 	{ NULL, 0, -1, -1},
Zoltan Fridrich 5c3160
 };
Zoltan Fridrich 5c3160
+static const struct kexalg gss_kexalgs[] = {
Zoltan Fridrich 5c3160
+#ifdef GSSAPI
Zoltan Fridrich 5c3160
+	{ KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Zoltan Fridrich 5c3160
+	{ KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
Zoltan Fridrich 5c3160
+	{ KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
Zoltan Fridrich 5c3160
+	{ KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
Zoltan Fridrich 5c3160
+	{ KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
Zoltan Fridrich 5c3160
+	{ KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
Zoltan Fridrich 5c3160
+	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
Zoltan Fridrich 5c3160
+	{ KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Zoltan Fridrich 5c3160
+#endif
Zoltan Fridrich 5c3160
+	{ NULL, 0, -1, -1},
Zoltan Fridrich 5c3160
+};
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
-char *
Zoltan Fridrich 5c3160
-kex_alg_list(char sep)
Zoltan Fridrich 5c3160
+static char *
Zoltan Fridrich 5c3160
+kex_alg_list_internal(char sep, const struct kexalg *algs)
Zoltan Fridrich 5c3160
 {
Zoltan Fridrich 5c3160
 	char *ret = NULL, *tmp;
Zoltan Fridrich 5c3160
 	size_t nlen, rlen = 0;
Zoltan Fridrich 5c3160
 	const struct kexalg *k;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
-	for (k = kexalgs; k->name != NULL; k++) {
Zoltan Fridrich 5c3160
+	for (k = algs; k->name != NULL; k++) {
Zoltan Fridrich 5c3160
 		if (ret != NULL)
Zoltan Fridrich 5c3160
 			ret[rlen++] = sep;
Zoltan Fridrich 5c3160
 		nlen = strlen(k->name);
Zoltan Fridrich 5c3160
@@ -106,6 +123,18 @@ kex_alg_list(char sep)
Zoltan Fridrich 5c3160
 	return ret;
Zoltan Fridrich 5c3160
 }
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
+char *
Zoltan Fridrich 5c3160
+kex_alg_list(char sep)
Zoltan Fridrich 5c3160
+{
Zoltan Fridrich 5c3160
+	return kex_alg_list_internal(sep, kexalgs);
Zoltan Fridrich 5c3160
+}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+char *
Zoltan Fridrich 5c3160
+kex_gss_alg_list(char sep)
Zoltan Fridrich 5c3160
+{
Zoltan Fridrich 5c3160
+	return kex_alg_list_internal(sep, gss_kexalgs);
Zoltan Fridrich 5c3160
+}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 static const struct kexalg *
Zoltan Fridrich 5c3160
 kex_alg_by_name(const char *name)
Zoltan Fridrich 5c3160
 {
Zoltan Fridrich 5c3160
@@ -115,6 +144,10 @@ kex_alg_by_name(const char *name)
Zoltan Fridrich 5c3160
 		if (strcmp(k->name, name) == 0)
Zoltan Fridrich 5c3160
 			return k;
Zoltan Fridrich 5c3160
 	}
Zoltan Fridrich 5c3160
+	for (k = gss_kexalgs; k->name != NULL; k++) {
Zoltan Fridrich 5c3160
+		if (strncmp(k->name, name, strlen(k->name)) == 0)
Zoltan Fridrich 5c3160
+			return k;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
 	return NULL;
Zoltan Fridrich 5c3160
 }
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
@@ -328,3 +361,26 @@ kex_assemble_names(char **listp, const c
Zoltan Fridrich 5c3160
 	free(ret);
Zoltan Fridrich 5c3160
 	return r;
Zoltan Fridrich 5c3160
 }
Zoltan Fridrich 5c3160
+ 
Zoltan Fridrich 5c3160
+/* Validate GSS KEX method name list */
Zoltan Fridrich 5c3160
+int
Zoltan Fridrich 5c3160
+kex_gss_names_valid(const char *names)
Zoltan Fridrich 5c3160
+{
Zoltan Fridrich 5c3160
+	char *s, *cp, *p;
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	if (names == NULL || *names == '\0')
Zoltan Fridrich 5c3160
+		return 0;
Zoltan Fridrich 5c3160
+	s = cp = xstrdup(names);
Zoltan Fridrich 5c3160
+	for ((p = strsep(&cp, ",")); p && *p != '\0';
Zoltan Fridrich 5c3160
+	    (p = strsep(&cp, ","))) {
Zoltan Fridrich 5c3160
+		if (strncmp(p, "gss-", 4) != 0
Zoltan Fridrich 5c3160
+		  || kex_alg_by_name(p) == NULL) {
Zoltan Fridrich 5c3160
+			error("Unsupported KEX algorithm \"%.100s\"", p);
Zoltan Fridrich 5c3160
+			free(s);
Zoltan Fridrich 5c3160
+			return 0;
Zoltan Fridrich 5c3160
+		}
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+	debug3("gss kex names ok: [%s]", names);
Zoltan Fridrich 5c3160
+	free(s);
Zoltan Fridrich 5c3160
+	return 1;
Zoltan Fridrich 5c3160
+}
Zoltan Fridrich 5c3160
diff --color -ruNp a/Makefile.in b/Makefile.in
Zoltan Fridrich 5c3160
--- a/Makefile.in	2024-08-28 12:35:01.200659705 +0200
Zoltan Fridrich 5c3160
+++ b/Makefile.in	2024-08-28 12:35:41.244432006 +0200
Zoltan Fridrich 5c3160
@@ -114,6 +114,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
Zoltan Fridrich 5c3160
 	kex.o kex-names.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
Zoltan Fridrich 5c3160
 	kexgexc.o kexgexs.o \
Zoltan Fridrich 5c3160
 	kexsntrup761x25519.o sntrup761.o kexgen.o \
Zoltan Fridrich 5c3160
+	kexgssc.o \
Zoltan Fridrich 5c3160
 	sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
Zoltan Fridrich 5c3160
 	sshbuf-io.o
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
@@ -135,7 +136,7 @@ SSHD_SESSION_OBJS=sshd-session.o auth-rh
Zoltan Fridrich 5c3160
 	auth2-chall.o groupaccess.o \
Zoltan Fridrich 5c3160
 	auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
Zoltan Fridrich 5c3160
 	auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-pubkeyfile.o \
Zoltan Fridrich 5c3160
-	monitor.o monitor_wrap.o auth-krb5.o \
Zoltan Fridrich 5c3160
+	monitor.o monitor_wrap.o auth-krb5.o kexgsss.o \
Zoltan Fridrich 5c3160
 	auth2-gss.o gss-serv.o gss-serv-krb5.o \
Zoltan Fridrich 5c3160
 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o \
Zoltan Fridrich 5c3160
 	sftp-server.o sftp-common.o \
Zoltan Fridrich 5c3160
@@ -529,7 +530,7 @@ regress-prep:
Zoltan Fridrich 5c3160
 	    ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 REGRESSLIBS=libssh.a $(LIBCOMPAT)
Zoltan Fridrich 5c3160
-TESTLIBS=$(LIBS) $(CHANNELLIBS)
Zoltan Fridrich 5c3160
+TESTLIBS=$(LIBS) $(CHANNELLIBS) $(GSSLIBS)
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 regress/modpipe$(EXEEXT): $(srcdir)/regress/modpipe.c $(REGRESSLIBS)
Zoltan Fridrich 5c3160
 	$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/modpipe.c \
Zoltan Fridrich 5c3160
diff --color -ruNp a/monitor.c b/monitor.c
Zoltan Fridrich 5c3160
--- a/monitor.c	2024-08-28 12:35:01.192659551 +0200
Zoltan Fridrich 5c3160
+++ b/monitor.c	2024-08-28 12:35:41.251432142 +0200
Zoltan Fridrich 5c3160
@@ -143,6 +143,8 @@ int mm_answer_gss_setup_ctx(struct ssh *
Zoltan Fridrich 5c3160
 int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
Zoltan Fridrich 5c3160
 int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
Zoltan Fridrich 5c3160
 int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
Zoltan Fridrich 5c3160
+int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
Zoltan Fridrich 5c3160
+int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
Zoltan Fridrich 5c3160
 #endif
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 #ifdef SSH_AUDIT_EVENTS
Zoltan Fridrich 5c3160
@@ -219,11 +221,18 @@ struct mon_table mon_dispatch_proto20[]
Zoltan Fridrich 5c3160
     {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
Jakub Jelen def1de
     {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
Jakub Jelen def1de
     {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
Jakub Jelen def1de
+    {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
     {0, 0, NULL}
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Jakub Jelen def1de
 struct mon_table mon_dispatch_postauth20[] = {
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+    {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
Jakub Jelen def1de
+    {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
Jakub Jelen def1de
+    {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
Jakub Jelen def1de
+    {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 #ifdef WITH_OPENSSL
Jakub Jelen def1de
     {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
Jakub Jelen def1de
 #endif
Zoltan Fridrich 5c3160
@@ -292,6 +301,10 @@ monitor_child_preauth(struct ssh *ssh, s
Jakub Jelen def1de
 	/* Permit requests for moduli and signatures */
Jakub Jelen def1de
 	monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
Jakub Jelen def1de
 	monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	/* and for the GSSAPI key exchange */
Jakub Jelen def1de
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* The first few requests do not require asynchronous access */
Jakub Jelen def1de
 	while (!authenticated) {
Zoltan Fridrich 5c3160
@@ -344,8 +357,15 @@ monitor_child_preauth(struct ssh *ssh, s
Norbert Pocs c5082a
 		if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
Norbert Pocs c5082a
 			auth_log(ssh, authenticated, partial,
Norbert Pocs c5082a
 			    auth_method, auth_submethod);
Norbert Pocs c5082a
-			if (!partial && !authenticated)
Norbert Pocs c5082a
+			if (!partial && !authenticated) {
Norbert Pocs c5082a
+#ifdef GSSAPI
Norbert Pocs c5082a
+				/* If gssapi-with-mic failed, MONITOR_REQ_GSSCHECKMIC is disabled.
Norbert Pocs c5082a
+				 * We have to reenable it to try again for gssapi-keyex */
Norbert Pocs c5082a
+				if (strcmp(auth_method, "gssapi-with-mic") == 0 && options.gss_keyex)
Norbert Pocs c5082a
+					monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
Norbert Pocs c5082a
+#endif
Norbert Pocs c5082a
 				authctxt->failures++;
Norbert Pocs c5082a
+			}
Norbert Pocs c5082a
 			if (authenticated || partial) {
Norbert Pocs c5082a
 				auth2_update_session_info(authctxt,
Norbert Pocs c5082a
 				    auth_method, auth_submethod);
Zoltan Fridrich 5c3160
@@ -413,6 +433,10 @@ monitor_child_postauth(struct ssh *ssh,
Jakub Jelen def1de
 	monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
Jakub Jelen def1de
 	monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
Jakub Jelen def1de
 	monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	/* and for the GSSAPI key exchange */
Jakub Jelen def1de
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if (auth_opts->permit_pty_flag) {
Jakub Jelen def1de
 		monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
Zoltan Fridrich 5c3160
@@ -1793,6 +1817,17 @@ monitor_apply_keystate(struct ssh *ssh,
Jakub Jelen def1de
 # ifdef OPENSSL_HAS_ECC
Jakub Jelen 25c16c
 	kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
Jakub Jelen def1de
 # endif
Jakub Jelen def1de
+# ifdef GSSAPI
Jakub Jelen 25c16c
+	if (options.gss_keyex) {
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
Jakub Jelen 25c16c
+		kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
Jakub Jelen 25c16c
+	}
Jakub Jelen def1de
+# endif
Jakub Jelen def1de
 #endif /* WITH_OPENSSL */
Jakub Jelen 25c16c
 	kex->kex[KEX_C25519_SHA256] = kex_gen_server;
Jakub Jelen 25c16c
 	kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
Zoltan Fridrich 5c3160
@@ -1885,8 +1920,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh,
Jakub Jelen def1de
 	u_char *p;
Jakub Jelen def1de
 	int r;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (!options.gss_authentication)
Jakub Jelen 25c16c
-		fatal_f("GSSAPI authentication not enabled");
Jakub Jelen def1de
+	if (!options.gss_authentication && !options.gss_keyex)
Jakub Jelen 25c16c
+		fatal_f("GSSAPI not enabled");
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((r = sshbuf_get_string(m, &p, &len)) != 0)
Jakub Jelen 25c16c
 		fatal_fr(r, "parse");
Zoltan Fridrich 5c3160
@@ -1918,8 +1953,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh
Jakub Jelen def1de
 	OM_uint32 flags = 0; /* GSI needs this */
Jakub Jelen def1de
 	int r;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (!options.gss_authentication)
Jakub Jelen 25c16c
-		fatal_f("GSSAPI authentication not enabled");
Jakub Jelen def1de
+	if (!options.gss_authentication && !options.gss_keyex)
Jakub Jelen 25c16c
+		fatal_f("GSSAPI not enabled");
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
Jakub Jelen 25c16c
 		fatal_fr(r, "ssh_gssapi_get_buffer_desc");
Zoltan Fridrich 5c3160
@@ -1939,6 +1974,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh
Jakub Jelen def1de
 		monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
Jakub Jelen def1de
 		monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
Jakub Jelen def1de
 		monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
Jakub Jelen def1de
+		monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 	return (0);
Jakub Jelen def1de
 }
Zoltan Fridrich 5c3160
@@ -1950,8 +1986,8 @@ mm_answer_gss_checkmic(struct ssh *ssh,
Jakub Jelen def1de
 	OM_uint32 ret;
Jakub Jelen def1de
 	int r;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (!options.gss_authentication)
Jakub Jelen 25c16c
-		fatal_f("GSSAPI authentication not enabled");
Jakub Jelen def1de
+	if (!options.gss_authentication && !options.gss_keyex)
Jakub Jelen 25c16c
+		fatal_f("GSSAPI not enabled");
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
Jakub Jelen def1de
 	    (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
Zoltan Fridrich 5c3160
@@ -1977,13 +2013,17 @@ mm_answer_gss_checkmic(struct ssh *ssh,
Jakub Jelen def1de
 int
Jakub Jelen def1de
 mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	int r, authenticated;
Jakub Jelen def1de
+	int r, authenticated, kex;
Jakub Jelen def1de
 	const char *displayname;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (!options.gss_authentication)
Jakub Jelen 25c16c
-		fatal_f("GSSAPI authentication not enabled");
Jakub Jelen def1de
+	if (!options.gss_authentication && !options.gss_keyex)
Jakub Jelen 25c16c
+		fatal_f("GSSAPI not enabled");
Jakub Jelen def1de
 
Jakub Jelen def1de
-	authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
Jakub Jelen def1de
+	if ((r = sshbuf_get_u32(m, &kex)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	authenticated = authctxt->valid &&
Jakub Jelen def1de
+	    ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	sshbuf_reset(m);
Jakub Jelen def1de
 	if ((r = sshbuf_put_u32(m, authenticated)) != 0)
Zoltan Fridrich 5c3160
@@ -1992,7 +2032,11 @@ mm_answer_gss_userok(struct ssh *ssh, in
Jakub Jelen 25c16c
 	debug3_f("sending result %d", authenticated);
Jakub Jelen def1de
 	mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
Jakub Jelen def1de
 
Jakub Jelen def1de
-	auth_method = "gssapi-with-mic";
Jakub Jelen def1de
+	if (kex) {
Jakub Jelen def1de
+		auth_method = "gssapi-keyex";
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		auth_method = "gssapi-with-mic";
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((displayname = ssh_gssapi_displayname()) != NULL)
Jakub Jelen def1de
 		auth2_record_info(authctxt, "%s", displayname);
Zoltan Fridrich 5c3160
@@ -2000,5 +2044,84 @@ mm_answer_gss_userok(struct ssh *ssh, in
Jakub Jelen def1de
 	/* Monitor loop will terminate if authenticated */
Jakub Jelen def1de
 	return (authenticated);
Jakub Jelen def1de
 }
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	gss_buffer_desc data;
Jakub Jelen def1de
+	gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
Jakub Jelen def1de
+	OM_uint32 major, minor;
Jakub Jelen def1de
+	size_t len;
Jakub Jelen def1de
+	u_char *p = NULL;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!options.gss_authentication && !options.gss_keyex)
Jakub Jelen 25c16c
+		fatal_f("GSSAPI not enabled");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshbuf_get_string(m, &p, &len)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+	data.value = p;
Jakub Jelen def1de
+	data.length = len;
Jakub Jelen def1de
+	/* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
Jakub Jelen def1de
+	if (data.length != 20 && data.length != 32 && data.length != 64)
Jakub Jelen 25c16c
+		fatal_f("data length incorrect: %d", (int) data.length);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Save the session ID on the first time around */
Jakub Jelen def1de
+	if (session_id2_len == 0) {
Jakub Jelen def1de
+		session_id2_len = data.length;
Jakub Jelen def1de
+		session_id2 = xmalloc(session_id2_len);
Jakub Jelen def1de
+		memcpy(session_id2, data.value, session_id2_len);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	major = ssh_gssapi_sign(gsscontext, &data, &hash);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	free(data.value);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_reset(m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshbuf_put_u32(m, major)) != 0 ||
Jakub Jelen def1de
+	    (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	gss_release_buffer(&minor, &hash);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Turn on getpwnam permissions */
Jakub Jelen def1de
+	monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* And credential updating, for when rekeying */
Jakub Jelen def1de
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (0);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
Jakub Jelen def1de
+	ssh_gssapi_ccache store;
Jakub Jelen def1de
+	int r, ok;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (!options.gss_authentication && !options.gss_keyex)
Jakub Jelen 25c16c
+		fatal_f("GSSAPI not enabled");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
Jakub Jelen def1de
+	    (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
Jakub Jelen def1de
+	    (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ok = ssh_gssapi_update_creds(&store);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	free(store.filename);
Jakub Jelen def1de
+	free(store.envvar);
Jakub Jelen def1de
+	free(store.envval);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_reset(m);
Jakub Jelen def1de
+	if ((r = sshbuf_put_u32(m, ok)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return(0);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 #endif /* GSSAPI */
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
diff --color -ruNp a/monitor.h b/monitor.h
Zoltan Fridrich 5c3160
--- a/monitor.h	2024-08-28 12:35:01.192659551 +0200
Zoltan Fridrich 5c3160
+++ b/monitor.h	2024-08-28 12:35:41.251432142 +0200
Zoltan Fridrich 5c3160
@@ -67,6 +67,8 @@ enum monitor_reqtype {
Jakub Jelen def1de
 	MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
Jakub Jelen def1de
 	MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
Jakub Jelen def1de
 
Jakub Jelen def1de
+	MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
Jakub Jelen def1de
+	MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Jakub Jelen def1de
 struct ssh;
Zoltan Fridrich 5c3160
diff --color -ruNp a/monitor_wrap.c b/monitor_wrap.c
Zoltan Fridrich 5c3160
--- a/monitor_wrap.c	2024-08-28 12:35:01.192659551 +0200
Zoltan Fridrich 5c3160
+++ b/monitor_wrap.c	2024-08-28 12:35:41.251432142 +0200
Zoltan Fridrich 5c3160
@@ -1075,13 +1075,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
 int
Jakub Jelen def1de
-mm_ssh_gssapi_userok(char *user)
Jakub Jelen def1de
+mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	struct sshbuf *m;
Jakub Jelen def1de
 	int r, authenticated = 0;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((m = sshbuf_new()) == NULL)
Jakub Jelen 25c16c
 		fatal_f("sshbuf_new failed");
Jakub Jelen def1de
+	if ((r = sshbuf_put_u32(m, kex)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
 
Jakub Jelen def1de
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
Jakub Jelen def1de
 	mm_request_receive_expect(pmonitor->m_recvfd,
Zoltan Fridrich 5c3160
@@ -1094,6 +1096,59 @@ mm_ssh_gssapi_userok(char *user)
Jakub Jelen 25c16c
 	debug3_f("user %sauthenticated", authenticated ? "" : "not ");
Jakub Jelen def1de
 	return (authenticated);
Jakub Jelen def1de
 }
Jakub Jelen def1de
+
Jakub Jelen def1de
+OM_uint32
Jakub Jelen def1de
+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct sshbuf *m;
Jakub Jelen def1de
+	OM_uint32 major;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((m = sshbuf_new()) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_new failed");
Jakub Jelen def1de
+	if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
Jakub Jelen def1de
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshbuf_get_u32(m, &major)) != 0 ||
Jakub Jelen def1de
+	    (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_free(m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (major);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct sshbuf *m;
Jakub Jelen def1de
+	int r, ok;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((m = sshbuf_new()) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_new failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshbuf_put_cstring(m,
Jakub Jelen def1de
+	    store->filename ? store->filename : "")) != 0 ||
Jakub Jelen def1de
+	    (r = sshbuf_put_cstring(m,
Jakub Jelen def1de
+	    store->envvar ? store->envvar : "")) != 0 ||
Jakub Jelen def1de
+	    (r = sshbuf_put_cstring(m,
Jakub Jelen def1de
+	    store->envval ? store->envval : "")) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
Jakub Jelen def1de
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshbuf_get_u32(m, &ok)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "buffer error");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_free(m);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (ok);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 #endif /* GSSAPI */
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 /*
Zoltan Fridrich 5c3160
diff --color -ruNp a/monitor_wrap.h b/monitor_wrap.h
Zoltan Fridrich 5c3160
--- a/monitor_wrap.h	2024-08-28 12:35:01.193659570 +0200
Zoltan Fridrich 5c3160
+++ b/monitor_wrap.h	2024-08-28 12:35:41.251432142 +0200
Zoltan Fridrich 5c3160
@@ -67,8 +67,10 @@ void mm_decode_activate_server_options(s
Jakub Jelen def1de
 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
Jakub Jelen def1de
 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
Jakub Jelen def1de
    gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
Jakub Jelen def1de
-int mm_ssh_gssapi_userok(char *user);
Jakub Jelen def1de
+int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
Jakub Jelen def1de
 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
Jakub Jelen def1de
+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
Jakub Jelen def1de
+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 
Jakub Jelen def1de
 #ifdef USE_PAM
Zoltan Fridrich 5c3160
diff --color -ruNp a/packet.c b/packet.c
Zoltan Fridrich 5c3160
--- a/packet.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/packet.c	2024-08-28 12:35:41.260432315 +0200
Zoltan Fridrich 5c3160
@@ -1517,6 +1517,29 @@ ssh_packet_read(struct ssh *ssh)
Zoltan Fridrich 5c3160
 	return type;
Zoltan Fridrich 5c3160
 }
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
+/*
Zoltan Fridrich 5c3160
+ * Waits until a packet has been received, verifies that its type matches
Zoltan Fridrich 5c3160
+ * that given, and gives a fatal error and exits if there is a mismatch.
Zoltan Fridrich 5c3160
+ */
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+int
Zoltan Fridrich 5c3160
+ssh_packet_read_expect(struct ssh *ssh, u_int expected_type)
Zoltan Fridrich 5c3160
+{
Zoltan Fridrich 5c3160
+	int r;
Zoltan Fridrich 5c3160
+	u_char type;
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+	if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0)
Zoltan Fridrich 5c3160
+		return r;
Zoltan Fridrich 5c3160
+	if (type != expected_type) {
Zoltan Fridrich 5c3160
+		if ((r = sshpkt_disconnect(ssh,
Zoltan Fridrich 5c3160
+		    "Protocol error: expected packet type %d, got %d",
Zoltan Fridrich 5c3160
+		    expected_type, type)) != 0)
Zoltan Fridrich 5c3160
+			return r;
Zoltan Fridrich 5c3160
+		return SSH_ERR_PROTOCOL_ERROR;
Zoltan Fridrich 5c3160
+	}
Zoltan Fridrich 5c3160
+	return 0;
Zoltan Fridrich 5c3160
+}
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 static int
Zoltan Fridrich 5c3160
 ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
Zoltan Fridrich 5c3160
 {
Zoltan Fridrich 5c3160
diff --color -ruNp a/packet.h b/packet.h
Zoltan Fridrich 5c3160
--- a/packet.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/packet.h	2024-08-28 12:35:41.260432315 +0200
Zoltan Fridrich 5c3160
@@ -124,6 +124,7 @@ int	 ssh_packet_send2_wrapped(struct ssh
Zoltan Fridrich 5c3160
 int	 ssh_packet_send2(struct ssh *);
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 int      ssh_packet_read(struct ssh *);
Zoltan Fridrich 5c3160
+int	 ssh_packet_read_expect(struct ssh *, u_int type);
Zoltan Fridrich 5c3160
 int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p);
Zoltan Fridrich 5c3160
 int	 ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len);
Zoltan Fridrich 5c3160
 int	 ssh_packet_process_read(struct ssh *, int);
Zoltan Fridrich 5c3160
diff --color -ruNp a/readconf.c b/readconf.c
Zoltan Fridrich 5c3160
--- a/readconf.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/readconf.c	2024-08-28 12:35:41.253432180 +0200
Zoltan Fridrich 5c3160
@@ -70,6 +70,7 @@
Jakub Jelen def1de
 #include "uidswap.h"
Jakub Jelen def1de
 #include "myproposal.h"
Jakub Jelen def1de
 #include "digest.h"
Jakub Jelen def1de
+#include "ssh-gss.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
 /* Format of the configuration file:
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -164,6 +165,8 @@ typedef enum {
Jakub Jelen def1de
 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
Jakub Jelen def1de
 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
Jakub Jelen def1de
 	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
Jakub Jelen def1de
+	oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
Jakub Jelen def1de
+	oGssServerIdentity, oGssKexAlgorithms,
Jakub Jelen def1de
 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
Jakub Jelen def1de
 	oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
Jakub Jelen def1de
 	oHashKnownHosts,
Zoltan Fridrich 5c3160
@@ -210,10 +213,22 @@ static struct {
Jakub Jelen def1de
 	/* Sometimes-unsupported options */
Jakub Jelen def1de
 #if defined(GSSAPI)
Jakub Jelen def1de
 	{ "gssapiauthentication", oGssAuthentication },
Jakub Jelen def1de
+	{ "gssapikeyexchange", oGssKeyEx },
Jakub Jelen def1de
 	{ "gssapidelegatecredentials", oGssDelegateCreds },
Jakub Jelen def1de
+	{ "gssapitrustdns", oGssTrustDns },
Jakub Jelen def1de
+	{ "gssapiclientidentity", oGssClientIdentity },
Jakub Jelen def1de
+	{ "gssapiserveridentity", oGssServerIdentity },
Jakub Jelen def1de
+	{ "gssapirenewalforcesrekey", oGssRenewalRekey },
Jakub Jelen def1de
+	{ "gssapikexalgorithms", oGssKexAlgorithms },
Jakub Jelen def1de
 # else
Jakub Jelen def1de
 	{ "gssapiauthentication", oUnsupported },
Jakub Jelen def1de
+	{ "gssapikeyexchange", oUnsupported },
Jakub Jelen def1de
 	{ "gssapidelegatecredentials", oUnsupported },
Jakub Jelen def1de
+	{ "gssapitrustdns", oUnsupported },
Jakub Jelen def1de
+	{ "gssapiclientidentity", oUnsupported },
Jakub Jelen def1de
+	{ "gssapiserveridentity", oUnsupported },
Jakub Jelen def1de
+	{ "gssapirenewalforcesrekey", oUnsupported },
Jakub Jelen def1de
+	{ "gssapikexalgorithms", oUnsupported },
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 #ifdef ENABLE_PKCS11
Jakub Jelen def1de
 	{ "pkcs11provider", oPKCS11Provider },
Zoltan Fridrich 5c3160
@@ -1227,10 +1242,42 @@ parse_time:
Jakub Jelen def1de
 		intptr = &options->gss_authentication;
Jakub Jelen def1de
 		goto parse_flag;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	case oGssKeyEx:
Jakub Jelen def1de
+		intptr = &options->gss_keyex;
Jakub Jelen def1de
+		goto parse_flag;
Jakub Jelen def1de
+
Jakub Jelen def1de
 	case oGssDelegateCreds:
Jakub Jelen def1de
 		intptr = &options->gss_deleg_creds;
Jakub Jelen def1de
 		goto parse_flag;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	case oGssTrustDns:
Jakub Jelen def1de
+		intptr = &options->gss_trust_dns;
Jakub Jelen def1de
+		goto parse_flag;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	case oGssClientIdentity:
Jakub Jelen def1de
+		charptr = &options->gss_client_identity;
Jakub Jelen def1de
+		goto parse_string;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	case oGssServerIdentity:
Jakub Jelen def1de
+		charptr = &options->gss_server_identity;
Jakub Jelen def1de
+		goto parse_string;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	case oGssRenewalRekey:
Jakub Jelen def1de
+		intptr = &options->gss_renewal_rekey;
Jakub Jelen def1de
+		goto parse_flag;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	case oGssKexAlgorithms:
Dmitry Belyavskiy 8f4d19
+		arg = argv_next(&ac, &av;;
Jakub Jelen def1de
+		if (!arg || *arg == '\0')
Jakub Jelen def1de
+			fatal("%.200s line %d: Missing argument.",
Jakub Jelen def1de
+			    filename, linenum);
Jakub Jelen def1de
+		if (!kex_gss_names_valid(arg))
Jakub Jelen def1de
+			fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
Jakub Jelen def1de
+			    filename, linenum, arg ? arg : "<NONE>");
Jakub Jelen def1de
+		if (*activep && options->gss_kex_algorithms == NULL)
Jakub Jelen def1de
+			options->gss_kex_algorithms = xstrdup(arg);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+
Jakub Jelen def1de
 	case oBatchMode:
Jakub Jelen def1de
 		intptr = &options->batch_mode;
Jakub Jelen def1de
 		goto parse_flag;
Zoltan Fridrich 5c3160
@@ -2542,7 +2589,13 @@ initialize_options(Options * options)
Dmitry Belyavskiy 8f4d19
 	options->fwd_opts.streamlocal_bind_unlink = -1;
Jakub Jelen def1de
 	options->pubkey_authentication = -1;
Jakub Jelen def1de
 	options->gss_authentication = -1;
Jakub Jelen def1de
+	options->gss_keyex = -1;
Jakub Jelen def1de
 	options->gss_deleg_creds = -1;
Jakub Jelen def1de
+	options->gss_trust_dns = -1;
Jakub Jelen def1de
+	options->gss_renewal_rekey = -1;
Jakub Jelen def1de
+	options->gss_client_identity = NULL;
Jakub Jelen def1de
+	options->gss_server_identity = NULL;
Jakub Jelen def1de
+	options->gss_kex_algorithms = NULL;
Jakub Jelen def1de
 	options->password_authentication = -1;
Jakub Jelen def1de
 	options->kbd_interactive_authentication = -1;
Jakub Jelen def1de
 	options->kbd_interactive_devices = NULL;
Zoltan Fridrich 5c3160
@@ -2705,8 +2758,18 @@ fill_default_options(Options * options)
Dmitry Belyavskiy 03150f
 		options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
Jakub Jelen def1de
 	if (options->gss_authentication == -1)
Jakub Jelen def1de
 		options->gss_authentication = 0;
Jakub Jelen def1de
+	if (options->gss_keyex == -1)
Jakub Jelen def1de
+		options->gss_keyex = 0;
Jakub Jelen def1de
 	if (options->gss_deleg_creds == -1)
Jakub Jelen def1de
 		options->gss_deleg_creds = 0;
Jakub Jelen def1de
+	if (options->gss_trust_dns == -1)
Jakub Jelen def1de
+		options->gss_trust_dns = 0;
Jakub Jelen def1de
+	if (options->gss_renewal_rekey == -1)
Jakub Jelen def1de
+		options->gss_renewal_rekey = 0;
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	if (options->gss_kex_algorithms == NULL)
Jakub Jelen def1de
+		options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 	if (options->password_authentication == -1)
Jakub Jelen def1de
 		options->password_authentication = 1;
Jakub Jelen def1de
 	if (options->kbd_interactive_authentication == -1)
Zoltan Fridrich 5c3160
@@ -3533,7 +3596,14 @@ dump_client_config(Options *o, const cha
Jakub Jelen def1de
 	dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
Jakub Jelen def1de
 #ifdef GSSAPI
Jakub Jelen def1de
 	dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
Jakub Jelen def1de
+	dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
Jakub Jelen def1de
 	dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
Jakub Jelen def1de
+	dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
Jakub Jelen def1de
+	dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
Jakub Jelen def1de
+	dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
Jakub Jelen def1de
+	dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
Jakub Jelen def1de
+	dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
Jakub Jelen def1de
+	    o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
Jakub Jelen def1de
 #endif /* GSSAPI */
Jakub Jelen def1de
 	dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
Jakub Jelen def1de
 	dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
Zoltan Fridrich 5c3160
diff --color -ruNp a/readconf.h b/readconf.h
Zoltan Fridrich 5c3160
--- a/readconf.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/readconf.h	2024-08-28 12:35:41.254432199 +0200
Zoltan Fridrich 5c3160
@@ -40,7 +40,13 @@ typedef struct {
Dmitry Belyavskiy 8f4d19
 	int     pubkey_authentication;	/* Try ssh2 pubkey authentication. */
Dmitry Belyavskiy 8f4d19
 	int     hostbased_authentication;	/* ssh2's rhosts_rsa */
Jakub Jelen def1de
 	int     gss_authentication;	/* Try GSS authentication */
Jakub Jelen def1de
+	int     gss_keyex;		/* Try GSS key exchange */
Jakub Jelen def1de
 	int     gss_deleg_creds;	/* Delegate GSS credentials */
Jakub Jelen def1de
+	int	gss_trust_dns;		/* Trust DNS for GSS canonicalization */
Jakub Jelen def1de
+	int	gss_renewal_rekey;	/* Credential renewal forces rekey */
Jakub Jelen def1de
+	char    *gss_client_identity;   /* Principal to initiate GSSAPI with */
Jakub Jelen def1de
+	char    *gss_server_identity;   /* GSSAPI target principal */
Jakub Jelen def1de
+	char    *gss_kex_algorithms;	/* GSSAPI kex methods to be offered by client. */
Jakub Jelen def1de
 	int     password_authentication;	/* Try password
Jakub Jelen def1de
 						 * authentication. */
Jakub Jelen def1de
 	int     kbd_interactive_authentication; /* Try keyboard-interactive auth. */
Zoltan Fridrich 5c3160
diff --color -ruNp a/servconf.c b/servconf.c
Zoltan Fridrich 5c3160
--- a/servconf.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/servconf.c	2024-08-28 12:35:41.255432218 +0200
Zoltan Fridrich 5c3160
@@ -68,6 +68,7 @@
Jakub Jelen def1de
 #include "auth.h"
Jakub Jelen def1de
 #include "myproposal.h"
Jakub Jelen def1de
 #include "digest.h"
Jakub Jelen def1de
+#include "ssh-gss.h"
Jakub Jelen def1de
 
Dmitry Belyavskiy 089d79
 #if !defined(SSHD_PAM_SERVICE)
Dmitry Belyavskiy 089d79
 # define SSHD_PAM_SERVICE		"sshd"
Zoltan Fridrich 5c3160
@@ -137,8 +138,11 @@ initialize_server_options(ServerOptions
Jakub Jelen def1de
 	options->kerberos_ticket_cleanup = -1;
Jakub Jelen def1de
 	options->kerberos_get_afs_token = -1;
Jakub Jelen def1de
 	options->gss_authentication=-1;
Jakub Jelen def1de
+	options->gss_keyex = -1;
Jakub Jelen def1de
 	options->gss_cleanup_creds = -1;
Jakub Jelen def1de
 	options->gss_strict_acceptor = -1;
Jakub Jelen def1de
+	options->gss_store_rekey = -1;
Jakub Jelen def1de
+	options->gss_kex_algorithms = NULL;
Jakub Jelen def1de
 	options->password_authentication = -1;
Jakub Jelen def1de
 	options->kbd_interactive_authentication = -1;
Dmitry Belyavskiy 8f4d19
 	options->permit_empty_passwd = -1;
Zoltan Fridrich 5c3160
@@ -376,10 +380,18 @@ fill_default_server_options(ServerOption
Jakub Jelen def1de
 		options->kerberos_get_afs_token = 0;
Jakub Jelen def1de
 	if (options->gss_authentication == -1)
Jakub Jelen def1de
 		options->gss_authentication = 0;
Jakub Jelen def1de
+	if (options->gss_keyex == -1)
Jakub Jelen def1de
+		options->gss_keyex = 0;
Jakub Jelen def1de
 	if (options->gss_cleanup_creds == -1)
Jakub Jelen def1de
 		options->gss_cleanup_creds = 1;
Jakub Jelen def1de
 	if (options->gss_strict_acceptor == -1)
Jakub Jelen def1de
 		options->gss_strict_acceptor = 1;
Jakub Jelen def1de
+	if (options->gss_store_rekey == -1)
Jakub Jelen def1de
+		options->gss_store_rekey = 0;
Jakub Jelen def1de
+#ifdef GSSAPI
Jakub Jelen def1de
+	if (options->gss_kex_algorithms == NULL)
Jakub Jelen def1de
+		options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 	if (options->password_authentication == -1)
Jakub Jelen def1de
 		options->password_authentication = 1;
Jakub Jelen def1de
 	if (options->kbd_interactive_authentication == -1)
Zoltan Fridrich 5c3160
@@ -558,6 +570,7 @@ typedef enum {
Dmitry Belyavskiy 089d79
 	sPerSourcePenalties, sPerSourcePenaltyExemptList,
Jakub Jelen def1de
 	sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
Jakub Jelen def1de
 	sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
Jakub Jelen def1de
+	sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
Jakub Jelen def1de
 	sAcceptEnv, sSetEnv, sPermitTunnel,
Jakub Jelen def1de
 	sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
Jakub Jelen def1de
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
Zoltan Fridrich 5c3160
@@ -643,12 +656,22 @@ static struct {
Jakub Jelen def1de
 #ifdef GSSAPI
Jakub Jelen def1de
 	{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
Jakub Jelen def1de
 	{ "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
Jakub Jelen def1de
 	{ "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
Jakub Jelen def1de
 #else
Jakub Jelen def1de
 	{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
Jakub Jelen def1de
 	{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
 	{ "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
+	{ "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
+	{ "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
Jakub Jelen def1de
 	{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
Jakub Jelen def1de
 	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
Dmitry Belyavskiy 8f4d19
 	{ "challengeresponseauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */
Zoltan Fridrich 5c3160
@@ -1585,6 +1608,10 @@ process_server_config_line_depth(ServerO
Jakub Jelen def1de
 		intptr = &options->gss_authentication;
Jakub Jelen def1de
 		goto parse_flag;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	case sGssKeyEx:
Jakub Jelen def1de
+		intptr = &options->gss_keyex;
Jakub Jelen def1de
+		goto parse_flag;
Jakub Jelen def1de
+
Jakub Jelen def1de
 	case sGssCleanupCreds:
Jakub Jelen def1de
 		intptr = &options->gss_cleanup_creds;
Jakub Jelen def1de
 		goto parse_flag;
Zoltan Fridrich 5c3160
@@ -1593,6 +1620,22 @@ process_server_config_line_depth(ServerO
Jakub Jelen def1de
 		intptr = &options->gss_strict_acceptor;
Jakub Jelen def1de
 		goto parse_flag;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	case sGssStoreRekey:
Jakub Jelen def1de
+		intptr = &options->gss_store_rekey;
Jakub Jelen def1de
+		goto parse_flag;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	case sGssKexAlgorithms:
Dmitry Belyavskiy 8f4d19
+		arg = argv_next(&ac, &av;;
Jakub Jelen def1de
+		if (!arg || *arg == '\0')
Jakub Jelen def1de
+			fatal("%.200s line %d: Missing argument.",
Jakub Jelen def1de
+			    filename, linenum);
Jakub Jelen def1de
+		if (!kex_gss_names_valid(arg))
Jakub Jelen def1de
+			fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
Jakub Jelen def1de
+			    filename, linenum, arg ? arg : "<NONE>");
Jakub Jelen def1de
+		if (*activep && options->gss_kex_algorithms == NULL)
Jakub Jelen def1de
+			options->gss_kex_algorithms = xstrdup(arg);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+
Jakub Jelen def1de
 	case sPasswordAuthentication:
Jakub Jelen def1de
 		intptr = &options->password_authentication;
Jakub Jelen def1de
 		goto parse_flag;
Zoltan Fridrich 5c3160
@@ -3178,6 +3221,10 @@ dump_config(ServerOptions *o)
Jakub Jelen def1de
 #ifdef GSSAPI
Jakub Jelen def1de
 	dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
Jakub Jelen def1de
 	dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
Jakub Jelen def1de
+	dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
Jakub Jelen def1de
+	dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
Jakub Jelen def1de
+	dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
Jakub Jelen def1de
+	dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 	dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
Jakub Jelen def1de
 	dump_cfg_fmtint(sKbdInteractiveAuthentication,
Zoltan Fridrich 5c3160
diff --color -ruNp a/servconf.h b/servconf.h
Zoltan Fridrich 5c3160
--- a/servconf.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/servconf.h	2024-08-28 12:35:41.255432218 +0200
Zoltan Fridrich 5c3160
@@ -149,8 +149,11 @@ typedef struct {
Jakub Jelen def1de
 	int     kerberos_get_afs_token;		/* If true, try to get AFS token if
Jakub Jelen def1de
 						 * authenticated with Kerberos. */
Jakub Jelen def1de
 	int     gss_authentication;	/* If true, permit GSSAPI authentication */
Jakub Jelen def1de
+	int     gss_keyex;		/* If true, permit GSSAPI key exchange */
Jakub Jelen def1de
 	int     gss_cleanup_creds;	/* If true, destroy cred cache on logout */
Jakub Jelen def1de
 	int     gss_strict_acceptor;	/* If true, restrict the GSSAPI acceptor name */
Jakub Jelen def1de
+	int 	gss_store_rekey;
Jakub Jelen def1de
+	char   *gss_kex_algorithms;	/* GSSAPI kex methods to be offered by client. */
Jakub Jelen def1de
 	int     password_authentication;	/* If true, permit password
Jakub Jelen def1de
 						 * authentication. */
Jakub Jelen def1de
 	int     kbd_interactive_authentication;	/* If true, permit */
Zoltan Fridrich 5c3160
diff --color -ruNp a/session.c b/session.c
Zoltan Fridrich 5c3160
--- a/session.c	2024-08-28 12:35:01.197659647 +0200
Zoltan Fridrich 5c3160
+++ b/session.c	2024-08-28 12:35:41.255432218 +0200
Zoltan Fridrich 5c3160
@@ -2674,13 +2674,19 @@ do_cleanup(struct ssh *ssh, Authctxt *au
Jakub Jelen def1de
 
Jakub Jelen def1de
 #ifdef KRB5
Jakub Jelen def1de
 	if (options.kerberos_ticket_cleanup &&
Jakub Jelen def1de
-	    authctxt->krb5_ctx)
Jakub Jelen def1de
+	    authctxt->krb5_ctx) {
Jakub Jelen def1de
+		temporarily_use_uid(authctxt->pw);
Jakub Jelen def1de
 		krb5_cleanup_proc(authctxt);
Jakub Jelen def1de
+		restore_uid();
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 
Jakub Jelen def1de
 #ifdef GSSAPI
Jakub Jelen def1de
-	if (options.gss_cleanup_creds)
Jakub Jelen def1de
+	if (options.gss_cleanup_creds) {
Jakub Jelen def1de
+		temporarily_use_uid(authctxt->pw);
Jakub Jelen def1de
 		ssh_gssapi_cleanup_creds();
Jakub Jelen def1de
+		restore_uid();
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* remove agent socket */
Zoltan Fridrich 5c3160
diff --color -ruNp a/ssh.1 b/ssh.1
Zoltan Fridrich 5c3160
--- a/ssh.1	2024-08-28 12:35:01.207659840 +0200
Zoltan Fridrich 5c3160
+++ b/ssh.1	2024-08-28 12:35:41.256432238 +0200
Zoltan Fridrich 5c3160
@@ -536,7 +536,13 @@ For full details of the options listed b
Jakub Jelen def1de
 .It GatewayPorts
Jakub Jelen def1de
 .It GlobalKnownHostsFile
Jakub Jelen def1de
 .It GSSAPIAuthentication
Jakub Jelen def1de
+.It GSSAPIKeyExchange
Jakub Jelen def1de
+.It GSSAPIClientIdentity
Jakub Jelen def1de
 .It GSSAPIDelegateCredentials
Jakub Jelen def1de
+.It GSSAPIKexAlgorithms
Jakub Jelen def1de
+.It GSSAPIRenewalForcesRekey
Jakub Jelen def1de
+.It GSSAPIServerIdentity
Jakub Jelen def1de
+.It GSSAPITrustDns
Jakub Jelen def1de
 .It HashKnownHosts
Jakub Jelen def1de
 .It Host
Jakub Jelen 25c16c
 .It HostbasedAcceptedAlgorithms
Zoltan Fridrich 5c3160
@@ -624,6 +630,8 @@ flag),
Jakub Jelen def1de
 (supported message integrity codes),
Jakub Jelen def1de
 .Ar kex
Jakub Jelen def1de
 (key exchange algorithms),
Jakub Jelen def1de
+.Ar kex-gss
Jakub Jelen def1de
+(GSSAPI key exchange algorithms),
Jakub Jelen def1de
 .Ar key
Jakub Jelen def1de
 (key types),
Dmitry Belyavskiy f23830
 .Ar key-ca-sign
Zoltan Fridrich 5c3160
diff --color -ruNp a/ssh.c b/ssh.c
Zoltan Fridrich 5c3160
--- a/ssh.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/ssh.c	2024-08-28 12:35:41.256432238 +0200
Zoltan Fridrich 5c3160
@@ -827,6 +827,8 @@ main(int ac, char **av)
Jakub Jelen 51f5c1
 			else if (strcmp(optarg, "kex") == 0 ||
Jakub Jelen 51f5c1
 			    strcasecmp(optarg, "KexAlgorithms") == 0)
Jakub Jelen def1de
 				cp = kex_alg_list('\n');
Jakub Jelen def1de
+			else if (strcmp(optarg, "kex-gss") == 0)
Jakub Jelen def1de
+				cp = kex_gss_alg_list('\n');
Jakub Jelen def1de
 			else if (strcmp(optarg, "key") == 0)
Jakub Jelen def1de
 				cp = sshkey_alg_list(0, 0, 0, '\n');
Jakub Jelen def1de
 			else if (strcmp(optarg, "key-cert") == 0)
Zoltan Fridrich 5c3160
@@ -857,8 +859,8 @@ main(int ac, char **av)
Jakub Jelen 51f5c1
 			} else if (strcmp(optarg, "help") == 0) {
Jakub Jelen def1de
 				cp = xstrdup(
Jakub Jelen 51f5c1
 				    "cipher\ncipher-auth\ncompression\nkex\n"
Jakub Jelen 51f5c1
-				    "key\nkey-cert\nkey-plain\nkey-sig\nmac\n"
Jakub Jelen 51f5c1
-				    "protocol-version\nsig");
Jakub Jelen 51f5c1
+				    "kex-gss\nkey\nkey-cert\nkey-plain\n"
Jakub Jelen 51f5c1
+				    "key-sig\nmac\nprotocol-version\nsig");
Jakub Jelen def1de
 			}
Jakub Jelen 51f5c1
 			if (cp == NULL)
Jakub Jelen 51f5c1
 				fatal("Unsupported query \"%s\"", optarg);
Zoltan Fridrich 5c3160
diff --color -ruNp a/ssh_config b/ssh_config
Zoltan Fridrich 5c3160
--- a/ssh_config	2024-08-28 12:35:01.216660014 +0200
Zoltan Fridrich 5c3160
+++ b/ssh_config	2024-08-28 12:35:41.256432238 +0200
Jakub Jelen def1de
@@ -24,6 +24,8 @@
Jakub Jelen def1de
 #   HostbasedAuthentication no
Jakub Jelen def1de
 #   GSSAPIAuthentication no
Jakub Jelen def1de
 #   GSSAPIDelegateCredentials no
Jakub Jelen def1de
+#   GSSAPIKeyExchange no
Jakub Jelen def1de
+#   GSSAPITrustDNS no
Jakub Jelen def1de
 #   BatchMode no
Dmitry Belyavskiy f23830
 #   CheckHostIP no
Jakub Jelen def1de
 #   AddressFamily any
Zoltan Fridrich 5c3160
diff --color -ruNp a/ssh_config.5 b/ssh_config.5
Zoltan Fridrich 5c3160
--- a/ssh_config.5	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/ssh_config.5	2024-08-28 12:35:41.257432257 +0200
Zoltan Fridrich 5c3160
@@ -938,10 +938,68 @@ The default is
Jakub Jelen def1de
 Specifies whether user authentication based on GSSAPI is allowed.
Jakub Jelen def1de
 The default is
Jakub Jelen def1de
 .Cm no .
Jakub Jelen def1de
+.It Cm GSSAPIClientIdentity
Jakub Jelen def1de
+If set, specifies the GSSAPI client identity that ssh should use when
Jakub Jelen def1de
+connecting to the server. The default is unset, which means that the default
Jakub Jelen def1de
+identity will be used.
Jakub Jelen def1de
 .It Cm GSSAPIDelegateCredentials
Jakub Jelen def1de
 Forward (delegate) credentials to the server.
Jakub Jelen def1de
 The default is
Jakub Jelen def1de
 .Cm no .
Jakub Jelen def1de
+.It Cm GSSAPIKeyExchange
Jakub Jelen def1de
+Specifies whether key exchange based on GSSAPI may be used. When using
Jakub Jelen def1de
+GSSAPI key exchange the server need not have a host key.
Jakub Jelen def1de
+The default is
Jakub Jelen def1de
+.Dq no .
Jakub Jelen def1de
+.It Cm GSSAPIRenewalForcesRekey
Jakub Jelen def1de
+If set to
Jakub Jelen def1de
+.Dq yes
Jakub Jelen def1de
+then renewal of the client's GSSAPI credentials will force the rekeying of the
Jakub Jelen def1de
+ssh connection. With a compatible server, this will delegate the renewed
Jakub Jelen def1de
+credentials to a session on the server.
Jakub Jelen def1de
+.Pp
Jakub Jelen def1de
+Checks are made to ensure that credentials are only propagated when the new
Jakub Jelen def1de
+credentials match the old ones on the originating client and where the
Jakub Jelen def1de
+receiving server still has the old set in its cache.
Jakub Jelen def1de
+.Pp
Jakub Jelen def1de
+The default is
Jakub Jelen def1de
+.Dq no .
Jakub Jelen def1de
+.Pp
Jakub Jelen def1de
+For this to work
Jakub Jelen def1de
+.Cm GSSAPIKeyExchange
Jakub Jelen def1de
+needs to be enabled in the server and also used by the client.
Jakub Jelen def1de
+.It Cm GSSAPIServerIdentity
Jakub Jelen def1de
+If set, specifies the GSSAPI server identity that ssh should expect when
Jakub Jelen def1de
+connecting to the server. The default is unset, which means that the
Jakub Jelen def1de
+expected GSSAPI server identity will be determined from the target
Jakub Jelen def1de
+hostname.
Jakub Jelen def1de
+.It Cm GSSAPITrustDns
Jakub Jelen def1de
+Set to
Jakub Jelen def1de
+.Dq yes
Jakub Jelen def1de
+to indicate that the DNS is trusted to securely canonicalize
Jakub Jelen def1de
+the name of the host being connected to. If
Jakub Jelen def1de
+.Dq no ,
Jakub Jelen def1de
+the hostname entered on the
Jakub Jelen def1de
+command line will be passed untouched to the GSSAPI library.
Jakub Jelen def1de
+The default is
Jakub Jelen def1de
+.Dq no .
Jakub Jelen def1de
+.It Cm GSSAPIKexAlgorithms
Jakub Jelen def1de
+The list of key exchange algorithms that are offered for GSSAPI
Jakub Jelen def1de
+key exchange. Possible values are
Jakub Jelen def1de
+.Bd -literal -offset 3n
Jakub Jelen def1de
+gss-gex-sha1-,
Jakub Jelen def1de
+gss-group1-sha1-,
Jakub Jelen def1de
+gss-group14-sha1-,
Jakub Jelen def1de
+gss-group14-sha256-,
Jakub Jelen def1de
+gss-group16-sha512-,
Jakub Jelen def1de
+gss-nistp256-sha256-,
Jakub Jelen def1de
+gss-curve25519-sha256-
Jakub Jelen def1de
+.Ed
Jakub Jelen def1de
+.Pp
Jakub Jelen def1de
+The default is
Jakub Jelen 1cc7c8
+.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
Jakub Jelen 1cc7c8
+gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
Jakub Jelen 1cc7c8
+This option only applies to connections using GSSAPI.
Jakub Jelen def1de
 .It Cm HashKnownHosts
Jakub Jelen def1de
 Indicates that
Jakub Jelen def1de
 .Xr ssh 1
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshconnect2.c b/sshconnect2.c
Zoltan Fridrich 5c3160
--- a/sshconnect2.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/sshconnect2.c	2024-08-28 12:35:41.258432276 +0200
Zoltan Fridrich 5c3160
@@ -222,6 +222,11 @@ ssh_kex2(struct ssh *ssh, char *host, st
Dmitry Belyavskiy 089d79
 	char *all_key, *hkalgs = NULL;
Jakub Jelen 51f5c1
 	int r, use_known_hosts_order = 0;
Jakub Jelen def1de
 
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+	char *orig = NULL, *gss = NULL;
Jakub Jelen def1de
+	char *gss_host = NULL;
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Jakub Jelen def1de
 	xxx_host = host;
Jakub Jelen def1de
 	xxx_hostaddr = hostaddr;
Jakub Jelen 25c16c
 	xxx_conn_info = cinfo;
Zoltan Fridrich 5c3160
@@ -255,6 +260,42 @@ ssh_kex2(struct ssh *ssh, char *host, st
Dmitry Belyavskiy 089d79
 	    compression_alg_list(options.compression),
Dmitry Belyavskiy f561c6
 	    hkalgs ? hkalgs : options.hostkeyalgorithms);
Zoltan Fridrich 5c3160
 
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+	if (options.gss_keyex) {
Jakub Jelen def1de
+		/* Add the GSSAPI mechanisms currently supported on this
Jakub Jelen def1de
+		 * client to the key exchange algorithm proposal */
Jakub Jelen def1de
+		orig = myproposal[PROPOSAL_KEX_ALGS];
Jakub Jelen def1de
+
Jakub Jelen 57ba1b
+		if (options.gss_server_identity) {
Jakub Jelen def1de
+			gss_host = xstrdup(options.gss_server_identity);
Jakub Jelen 57ba1b
+		} else if (options.gss_trust_dns) {
Jakub Jelen def1de
+			gss_host = remote_hostname(ssh);
Jakub Jelen 57ba1b
+			/* Fall back to specified host if we are using proxy command
Jakub Jelen 57ba1b
+			 * and can not use DNS on that socket */
Jakub Jelen 57ba1b
+			if (strcmp(gss_host, "UNKNOWN") == 0) {
Jakub Jelen 57ba1b
+				free(gss_host);
Jakub Jelen 57ba1b
+				gss_host = xstrdup(host);
Jakub Jelen 57ba1b
+			}
Jakub Jelen 57ba1b
+		} else {
Jakub Jelen def1de
+			gss_host = xstrdup(host);
Jakub Jelen 57ba1b
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		gss = ssh_gssapi_client_mechanisms(gss_host,
Jakub Jelen def1de
+		    options.gss_client_identity, options.gss_kex_algorithms);
Jakub Jelen def1de
+		if (gss) {
Jakub Jelen def1de
+			debug("Offering GSSAPI proposal: %s", gss);
Jakub Jelen def1de
+			xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
Jakub Jelen def1de
+			    "%s,%s", gss, orig);
Jakub Jelen def1de
+
Jakub Jelen def1de
+			/* If we've got GSSAPI algorithms, then we also support the
Jakub Jelen def1de
+			 * 'null' hostkey, as a last resort */
Jakub Jelen def1de
+			orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
Jakub Jelen def1de
+			xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
Jakub Jelen def1de
+			    "%s,null", orig);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+#endif
Zoltan Fridrich 5c3160
+
Dmitry Belyavskiy f561c6
 	free(hkalgs);
Dmitry Belyavskiy f561c6
 
Zoltan Fridrich 5c3160
 	/* start key exchange */
Zoltan Fridrich 5c3160
@@ -271,14 +312,44 @@ ssh_kex2(struct ssh *ssh, char *host, st
Jakub Jelen def1de
 # ifdef OPENSSL_HAS_ECC
Jakub Jelen def1de
 	ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
Jakub Jelen def1de
 # endif
Jakub Jelen def1de
-#endif
Jakub Jelen def1de
+# ifdef GSSAPI
Jakub Jelen def1de
+	if (options.gss_keyex) {
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
Jakub Jelen def1de
+		ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+# endif
Jakub Jelen def1de
+#endif /* WITH_OPENSSL */
Jakub Jelen def1de
 	ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
c04e46
 	ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
Jakub Jelen def1de
 	ssh->kex->verify_host_key=&verify_host_key_callback;
Jakub Jelen def1de
 
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+	if (options.gss_keyex) {
Jakub Jelen def1de
+		ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
Jakub Jelen def1de
+		ssh->kex->gss_trust_dns = options.gss_trust_dns;
Jakub Jelen def1de
+		ssh->kex->gss_client = options.gss_client_identity;
Jakub Jelen def1de
+		ssh->kex->gss_host = gss_host;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Jakub Jelen def1de
 	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
Dmitry Belyavskiy 089d79
 	kex_proposal_free_entries(myproposal);
Jakub Jelen def1de
 
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+	/* repair myproposal after it was crumpled by the */
Jakub Jelen def1de
+	/* ext-info removal above */
Jakub Jelen def1de
+	if (gss) {
Jakub Jelen def1de
+		orig = myproposal[PROPOSAL_KEX_ALGS];
Jakub Jelen def1de
+		xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
Jakub Jelen def1de
+		    "%s,%s", gss, orig);
Jakub Jelen def1de
+		free(gss);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+#endif
Dmitry Belyavskiy 089d79
 #ifdef DEBUG_KEXDH
Dmitry Belyavskiy 089d79
 	/* send 1st encrypted/maced/compressed message */
Dmitry Belyavskiy 089d79
 	if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
Zoltan Fridrich 5c3160
@@ -368,6 +439,7 @@ static int input_gssapi_response(int typ
Jakub Jelen def1de
 static int input_gssapi_token(int type, u_int32_t, struct ssh *);
Jakub Jelen def1de
 static int input_gssapi_error(int, u_int32_t, struct ssh *);
Jakub Jelen def1de
 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
Jakub Jelen def1de
+static int userauth_gsskeyex(struct ssh *);
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 
Jakub Jelen def1de
 void	userauth(struct ssh *, char *);
Zoltan Fridrich 5c3160
@@ -384,6 +456,11 @@ static char *authmethods_get(void);
Jakub Jelen def1de
 
Jakub Jelen def1de
 Authmethod authmethods[] = {
Jakub Jelen def1de
 #ifdef GSSAPI
Jakub Jelen def1de
+	{"gssapi-keyex",
Jakub Jelen def1de
+		userauth_gsskeyex,
Jakub Jelen def1de
+		NULL,
Jakub Jelen def1de
+		&options.gss_keyex,
Jakub Jelen def1de
+		NULL},
Jakub Jelen def1de
 	{"gssapi-with-mic",
Jakub Jelen def1de
 		userauth_gssapi,
Jakub Jelen def1de
 		userauth_gssapi_cleanup,
Zoltan Fridrich 5c3160
@@ -755,12 +832,32 @@ userauth_gssapi(struct ssh *ssh)
Jakub Jelen def1de
 	OM_uint32 min;
Jakub Jelen def1de
 	int r, ok = 0;
Jakub Jelen def1de
 	gss_OID mech = NULL;
Jakub Jelen 57ba1b
+	char *gss_host = NULL;
Jakub Jelen def1de
+
Jakub Jelen 57ba1b
+	if (options.gss_server_identity) {
Jakub Jelen def1de
+		gss_host = xstrdup(options.gss_server_identity);
Jakub Jelen 57ba1b
+	} else if (options.gss_trust_dns) {
Jakub Jelen def1de
+		gss_host = remote_hostname(ssh);
Jakub Jelen 57ba1b
+		/* Fall back to specified host if we are using proxy command
Jakub Jelen 57ba1b
+		 * and can not use DNS on that socket */
Jakub Jelen 57ba1b
+		if (strcmp(gss_host, "UNKNOWN") == 0) {
Jakub Jelen 57ba1b
+			free(gss_host);
Jakub Jelen 57ba1b
+			gss_host = xstrdup(authctxt->host);
Jakub Jelen 57ba1b
+		}
Jakub Jelen 57ba1b
+	} else {
Jakub Jelen def1de
+		gss_host = xstrdup(authctxt->host);
Jakub Jelen 57ba1b
+	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* Try one GSSAPI method at a time, rather than sending them all at
Jakub Jelen def1de
 	 * once. */
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if (authctxt->gss_supported_mechs == NULL)
Jakub Jelen def1de
-		gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
Jakub Jelen 51f5c1
+		if (GSS_ERROR(gss_indicate_mechs(&min,
Jakub Jelen 51f5c1
+		    &authctxt->gss_supported_mechs))) {
Jakub Jelen 51f5c1
+			authctxt->gss_supported_mechs = NULL;
Jakub Jelen def1de
+			free(gss_host);
Jakub Jelen def1de
+			return 0;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* Check to see whether the mechanism is usable before we offer it */
Jakub Jelen def1de
 	while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
Zoltan Fridrich 5c3160
@@ -769,13 +866,15 @@ userauth_gssapi(struct ssh *ssh)
Jakub Jelen def1de
 		    elements[authctxt->mech_tried];
Jakub Jelen def1de
 		/* My DER encoding requires length<128 */
Jakub Jelen def1de
 		if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
Jakub Jelen def1de
-		    mech, authctxt->host)) {
Jakub Jelen def1de
+		    mech, gss_host, options.gss_client_identity)) {
Jakub Jelen def1de
 			ok = 1; /* Mechanism works */
Jakub Jelen def1de
 		} else {
Jakub Jelen def1de
 			authctxt->mech_tried++;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
+	free(gss_host);
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if (!ok || mech == NULL)
Jakub Jelen def1de
 		return 0;
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -1009,6 +1108,55 @@ input_gssapi_error(int type, u_int32_t p
Jakub Jelen def1de
 	free(lang);
Jakub Jelen def1de
 	return r;
Jakub Jelen def1de
 }
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+userauth_gsskeyex(struct ssh *ssh)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct sshbuf *b = NULL;
Jakub Jelen def1de
+	Authctxt *authctxt = ssh->authctxt;
Jakub Jelen def1de
+	gss_buffer_desc gssbuf;
Jakub Jelen def1de
+	gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
Jakub Jelen def1de
+	OM_uint32 ms;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	static int attempt = 0;
Jakub Jelen def1de
+	if (attempt++ >= 1)
Jakub Jelen def1de
+		return (0);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss_kex_context == NULL) {
Jakub Jelen def1de
+		debug("No valid Key exchange context");
Jakub Jelen def1de
+		return (0);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((b = sshbuf_new()) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_new failed");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
Jakub Jelen 25c16c
+	    "gssapi-keyex", ssh->kex->session_id);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
Jakub Jelen 25c16c
+		fatal_f("sshbuf_mutable_ptr failed");
Jakub Jelen def1de
+	gssbuf.length = sshbuf_len(b);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
Jakub Jelen def1de
+		sshbuf_free(b);
Jakub Jelen def1de
+		return (0);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
Jakub Jelen def1de
+	    (r = sshpkt_send(ssh)) != 0)
Jakub Jelen 25c16c
+		fatal_fr(r, "parsing");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sshbuf_free(b);
Jakub Jelen def1de
+	gss_release_buffer(&ms, &mic);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return (1);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 #endif /* GSSAPI */
Jakub Jelen def1de
 
Jakub Jelen def1de
 static int
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshd.c b/sshd.c
Zoltan Fridrich 5c3160
--- a/sshd.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/sshd.c	2024-08-28 12:35:41.258432276 +0200
Zoltan Fridrich 5c3160
@@ -1551,7 +1551,8 @@ main(int ac, char **av)
Dmitry Belyavskiy 089d79
 		free(fp);
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 	accumulate_host_timing_secret(cfg, NULL);
Dmitry Belyavskiy 089d79
-	if (!sensitive_data.have_ssh2_key) {
Dmitry Belyavskiy 089d79
+	/* The GSSAPI key exchange can run without a host key */
Dmitry Belyavskiy 089d79
+	if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
Dmitry Belyavskiy 089d79
 		logit("sshd: no hostkeys available -- exiting.");
Dmitry Belyavskiy 089d79
 		exit(1);
Dmitry Belyavskiy 089d79
 	}
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshd_config b/sshd_config
Zoltan Fridrich 5c3160
--- a/sshd_config	2024-08-28 12:35:01.221660110 +0200
Zoltan Fridrich 5c3160
+++ b/sshd_config	2024-08-28 12:35:41.259432296 +0200
Zoltan Fridrich 5c3160
@@ -77,6 +77,8 @@ AuthorizedKeysFile	.ssh/authorized_keys
Zoltan Fridrich 5c3160
 # GSSAPI options
Zoltan Fridrich 5c3160
 #GSSAPIAuthentication no
Zoltan Fridrich 5c3160
 #GSSAPICleanupCredentials yes
Zoltan Fridrich 5c3160
+#GSSAPIStrictAcceptorCheck yes
Zoltan Fridrich 5c3160
+#GSSAPIKeyExchange no
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 # Set this to 'yes' to enable PAM authentication, account processing,
Zoltan Fridrich 5c3160
 # and session processing. If this is enabled, PAM authentication will
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshd_config.5 b/sshd_config.5
Zoltan Fridrich 5c3160
--- a/sshd_config.5	2024-08-28 12:35:01.218660052 +0200
Zoltan Fridrich 5c3160
+++ b/sshd_config.5	2024-08-28 12:35:41.259432296 +0200
Zoltan Fridrich 5c3160
@@ -739,6 +739,11 @@ Specifies whether to automatically destr
Zoltan Fridrich 5c3160
 on logout.
Zoltan Fridrich 5c3160
 The default is
Zoltan Fridrich 5c3160
 .Cm yes .
Zoltan Fridrich 5c3160
+.It Cm GSSAPIKeyExchange
Zoltan Fridrich 5c3160
+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
Zoltan Fridrich 5c3160
+doesn't rely on ssh keys to verify host identity.
Zoltan Fridrich 5c3160
+The default is
Zoltan Fridrich 5c3160
+.Cm no .
Zoltan Fridrich 5c3160
 .It Cm GSSAPIStrictAcceptorCheck
Zoltan Fridrich 5c3160
 Determines whether to be strict about the identity of the GSSAPI acceptor
Zoltan Fridrich 5c3160
 a client authenticates against.
Zoltan Fridrich 5c3160
@@ -753,6 +758,32 @@ machine's default store.
Zoltan Fridrich 5c3160
 This facility is provided to assist with operation on multi homed machines.
Zoltan Fridrich 5c3160
 The default is
Zoltan Fridrich 5c3160
 .Cm yes .
Zoltan Fridrich 5c3160
+.It Cm GSSAPIStoreCredentialsOnRekey
Zoltan Fridrich 5c3160
+Controls whether the user's GSSAPI credentials should be updated following a
Zoltan Fridrich 5c3160
+successful connection rekeying. This option can be used to accepted renewed
Zoltan Fridrich 5c3160
+or updated credentials from a compatible client. The default is
Zoltan Fridrich 5c3160
+.Dq no .
Zoltan Fridrich 5c3160
+.Pp
Zoltan Fridrich 5c3160
+For this to work
Zoltan Fridrich 5c3160
+.Cm GSSAPIKeyExchange
Zoltan Fridrich 5c3160
+needs to be enabled in the server and also used by the client.
Zoltan Fridrich 5c3160
+.It Cm GSSAPIKexAlgorithms
Zoltan Fridrich 5c3160
+The list of key exchange algorithms that are accepted by GSSAPI
Zoltan Fridrich 5c3160
+key exchange. Possible values are
Zoltan Fridrich 5c3160
+.Bd -literal -offset 3n
Zoltan Fridrich 5c3160
+gss-gex-sha1-,
Zoltan Fridrich 5c3160
+gss-group1-sha1-,
Zoltan Fridrich 5c3160
+gss-group14-sha1-,
Zoltan Fridrich 5c3160
+gss-group14-sha256-,
Zoltan Fridrich 5c3160
+gss-group16-sha512-,
Zoltan Fridrich 5c3160
+gss-nistp256-sha256-,
Zoltan Fridrich 5c3160
+gss-curve25519-sha256-
Zoltan Fridrich 5c3160
+.Ed
Zoltan Fridrich 5c3160
+.Pp
Zoltan Fridrich 5c3160
+The default is
Zoltan Fridrich 5c3160
+.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
Zoltan Fridrich 5c3160
+gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
Zoltan Fridrich 5c3160
+This option only applies to connections using GSSAPI.
Zoltan Fridrich 5c3160
 .It Cm HostbasedAcceptedAlgorithms
Zoltan Fridrich 5c3160
 Specifies the signature algorithms that will be accepted for hostbased
Zoltan Fridrich 5c3160
 authentication as a list of comma-separated patterns.
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshd-session.c b/sshd-session.c
Zoltan Fridrich 5c3160
--- a/sshd-session.c	2024-08-28 12:35:01.221660110 +0200
Zoltan Fridrich 5c3160
+++ b/sshd-session.c	2024-08-28 12:35:41.263432373 +0200
Zoltan Fridrich 5c3160
@@ -660,8 +660,8 @@ notify_hostkeys(struct ssh *ssh)
Jakub Jelen def1de
 	}
Jakub Jelen 25c16c
 	debug3_f("sent %u hostkeys", nkeys);
Jakub Jelen def1de
 	if (nkeys == 0)
Jakub Jelen 25c16c
-		fatal_f("no hostkeys");
Jakub Jelen def1de
-	if ((r = sshpkt_send(ssh)) != 0)
Jakub Jelen 25c16c
+		debug3_f("no hostkeys");
Jakub Jelen def1de
+	else if ((r = sshpkt_send(ssh)) != 0)
Jakub Jelen def1de
 		sshpkt_fatal(ssh, r, "%s: send", __func__);
Jakub Jelen def1de
 	sshbuf_free(buf);
Jakub Jelen def1de
 }
Zoltan Fridrich 5c3160
@@ -1180,8 +1180,9 @@ main(int ac, char **av)
Zoltan Fridrich 5c3160
 			break;
Zoltan Fridrich 5c3160
 		}
Zoltan Fridrich 5c3160
 	}
Zoltan Fridrich 5c3160
-	if (!have_key)
Zoltan Fridrich 5c3160
-		fatal("internal error: monitor received no hostkeys");
Zoltan Fridrich 5c3160
+	/* The GSSAPI key exchange can run without a host key */
Zoltan Fridrich 5c3160
+	if (!have_key && !options.gss_keyex)
Zoltan Fridrich 5c3160
+		fatal("internal error: monitor received no hostkeys and GSS KEX is not configured");
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 	/* Ensure that umask disallows at least group and world write */
Zoltan Fridrich 5c3160
 	new_umask = umask(0077) | 0022;
Zoltan Fridrich 5c3160
@@ -1462,6 +1463,48 @@ do_ssh2_kex(struct ssh *ssh)
Dmitry Belyavskiy f561c6
 
Dmitry Belyavskiy f561c6
 	free(hkalgs);
Jakub Jelen def1de
 
Jakub Jelen def1de
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Jakub Jelen def1de
+	{
Jakub Jelen def1de
+	char *orig;
Jakub Jelen def1de
+	char *gss = NULL;
Jakub Jelen def1de
+	char *newstr = NULL;
Jakub Jelen def1de
+	orig = myproposal[PROPOSAL_KEX_ALGS];
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * If we don't have a host key, then there's no point advertising
Jakub Jelen def1de
+	 * the other key exchange algorithms
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
Jakub Jelen def1de
+		orig = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (options.gss_keyex)
Jakub Jelen def1de
+		gss = ssh_gssapi_server_mechanisms();
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		gss = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (gss && orig)
Jakub Jelen def1de
+		xasprintf(&newstr, "%s,%s", gss, orig);
Jakub Jelen def1de
+	else if (gss)
Zoltan Fridrich 5c3160
+		xasprintf(&newstr, "%s,%s", gss, "kex-strict-s-v00@openssh.com");
Jakub Jelen def1de
+	else if (orig)
Jakub Jelen def1de
+		newstr = orig;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/*
Jakub Jelen def1de
+	 * If we've got GSSAPI mechanisms, then we've got the 'null' host
Jakub Jelen def1de
+	 * key alg, but we can't tell people about it unless its the only
Jakub Jelen def1de
+	 * host key algorithm we support
Jakub Jelen def1de
+	 */
Jakub Jelen def1de
+	if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
Zoltan Fridrich 5c3160
+		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xstrdup("null");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (newstr)
Jakub Jelen def1de
+		myproposal[PROPOSAL_KEX_ALGS] = newstr;
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		fatal("No supported key exchange algorithms");
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Jakub Jelen def1de
 	/* start key exchange */
Jakub Jelen def1de
 	if ((r = kex_setup(ssh, myproposal)) != 0)
Jakub Jelen 25c16c
 		fatal_r(r, "kex_setup");
Zoltan Fridrich 5c3160
@@ -1479,7 +1522,18 @@ do_ssh2_kex(struct ssh *ssh)
Dmitry Belyavskiy 089d79
  #ifdef OPENSSL_HAS_ECC
Jakub Jelen def1de
 	kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
Dmitry Belyavskiy 089d79
  #endif
Jakub Jelen def1de
-#endif
Jakub Jelen def1de
+# ifdef GSSAPI
Jakub Jelen def1de
+	if (options.gss_keyex) {
Jakub Jelen def1de
+		kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
Jakub Jelen def1de
+		kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
Jakub Jelen def1de
+		kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
Jakub Jelen def1de
+		kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
Jakub Jelen def1de
+		kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
Jakub Jelen def1de
+		kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
Jakub Jelen def1de
+		kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+# endif
Jakub Jelen def1de
+#endif /* WITH_OPENSSL */
Jakub Jelen def1de
 	kex->kex[KEX_C25519_SHA256] = kex_gen_server;
Jakub Jelen 25c16c
 	kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
Jakub Jelen def1de
 	kex->load_host_public_key=&get_hostkey_public_by_type;
Zoltan Fridrich 5c3160
diff --color -ruNp a/ssh-gss.h b/ssh-gss.h
Zoltan Fridrich 5c3160
--- a/ssh-gss.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/ssh-gss.h	2024-08-28 12:35:41.256432238 +0200
Zoltan Fridrich 5c3160
@@ -61,10 +61,34 @@
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
 #define SSH_GSS_OIDTYPE 0x06
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_INIT                            30
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_CONTINUE                        31
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_COMPLETE                        32
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_HOSTKEY                         33
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_ERROR                           34
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_GROUPREQ			40
Zoltan Fridrich 5c3160
+#define SSH2_MSG_KEXGSS_GROUP				41
Zoltan Fridrich 5c3160
+#define KEX_GSS_GRP1_SHA1_ID				"gss-group1-sha1-"
Zoltan Fridrich 5c3160
+#define KEX_GSS_GRP14_SHA1_ID				"gss-group14-sha1-"
Zoltan Fridrich 5c3160
+#define KEX_GSS_GRP14_SHA256_ID			"gss-group14-sha256-"
Zoltan Fridrich 5c3160
+#define KEX_GSS_GRP16_SHA512_ID			"gss-group16-sha512-"
Zoltan Fridrich 5c3160
+#define KEX_GSS_GEX_SHA1_ID				"gss-gex-sha1-"
Zoltan Fridrich 5c3160
+#define KEX_GSS_NISTP256_SHA256_ID			"gss-nistp256-sha256-"
Zoltan Fridrich 5c3160
+#define KEX_GSS_C25519_SHA256_ID			"gss-curve25519-sha256-"
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+#define        GSS_KEX_DEFAULT_KEX \
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP14_SHA256_ID "," \
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP16_SHA512_ID	"," \
Zoltan Fridrich 5c3160
+	KEX_GSS_NISTP256_SHA256_ID "," \
Zoltan Fridrich 5c3160
+	KEX_GSS_C25519_SHA256_ID "," \
Zoltan Fridrich 5c3160
+	KEX_GSS_GRP14_SHA1_ID "," \
Zoltan Fridrich 5c3160
+	KEX_GSS_GEX_SHA1_ID
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 typedef struct {
Zoltan Fridrich 5c3160
 	char *filename;
Zoltan Fridrich 5c3160
 	char *envvar;
Zoltan Fridrich 5c3160
 	char *envval;
Zoltan Fridrich 5c3160
+	struct passwd *owner;
Zoltan Fridrich 5c3160
 	void *data;
Zoltan Fridrich 5c3160
 } ssh_gssapi_ccache;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
@@ -72,8 +96,11 @@ typedef struct {
Zoltan Fridrich 5c3160
 	gss_buffer_desc displayname;
Zoltan Fridrich 5c3160
 	gss_buffer_desc exportedname;
Zoltan Fridrich 5c3160
 	gss_cred_id_t creds;
Zoltan Fridrich 5c3160
+	gss_name_t name;
Zoltan Fridrich 5c3160
 	struct ssh_gssapi_mech_struct *mech;
Zoltan Fridrich 5c3160
 	ssh_gssapi_ccache store;
Zoltan Fridrich 5c3160
+	int used;
Zoltan Fridrich 5c3160
+	int updated;
Zoltan Fridrich 5c3160
 } ssh_gssapi_client;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 typedef struct ssh_gssapi_mech_struct {
Zoltan Fridrich 5c3160
@@ -84,6 +111,7 @@ typedef struct ssh_gssapi_mech_struct {
Zoltan Fridrich 5c3160
 	int (*userok) (ssh_gssapi_client *, char *);
Zoltan Fridrich 5c3160
 	int (*localname) (ssh_gssapi_client *, char **);
Zoltan Fridrich 5c3160
 	void (*storecreds) (ssh_gssapi_client *);
Zoltan Fridrich 5c3160
+	int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
Zoltan Fridrich 5c3160
 } ssh_gssapi_mech;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 typedef struct {
Zoltan Fridrich 5c3160
@@ -94,10 +122,11 @@ typedef struct {
Zoltan Fridrich 5c3160
 	gss_OID		oid; /* client */
Zoltan Fridrich 5c3160
 	gss_cred_id_t	creds; /* server */
Zoltan Fridrich 5c3160
 	gss_name_t	client; /* server */
Zoltan Fridrich 5c3160
-	gss_cred_id_t	client_creds; /* server */
Zoltan Fridrich 5c3160
+	gss_cred_id_t	client_creds; /* both */
Zoltan Fridrich 5c3160
 } Gssctxt;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 extern ssh_gssapi_mech *supported_mechs[];
Zoltan Fridrich 5c3160
+extern Gssctxt *gss_kex_context;
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 int  ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
Zoltan Fridrich 5c3160
 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
Zoltan Fridrich 5c3160
@@ -108,6 +137,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 struct sshbuf;
Zoltan Fridrich 5c3160
 int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
Zoltan Fridrich 5c3160
+int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
Zoltan Fridrich 5c3160
 OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
Zoltan Fridrich 5c3160
@@ -122,17 +152,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
Zoltan Fridrich 5c3160
 OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
Zoltan Fridrich 5c3160
 void ssh_gssapi_buildmic(struct sshbuf *, const char *,
Zoltan Fridrich 5c3160
     const char *, const char *, const struct sshbuf *);
Zoltan Fridrich 5c3160
-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
Zoltan Fridrich 5c3160
+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
Zoltan Fridrich 5c3160
+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
Zoltan Fridrich 5c3160
+int ssh_gssapi_credentials_updated(Gssctxt *);
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 /* In the server */
Zoltan Fridrich 5c3160
+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
Zoltan Fridrich 5c3160
+    const char *);
Zoltan Fridrich 5c3160
+char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
Zoltan Fridrich 5c3160
+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
Zoltan Fridrich 5c3160
+    const char *, const char *);
Zoltan Fridrich 5c3160
+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
Zoltan Fridrich 5c3160
+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
Zoltan Fridrich 5c3160
+    const char *);
Zoltan Fridrich 5c3160
 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
Zoltan Fridrich 5c3160
-int ssh_gssapi_userok(char *name);
Zoltan Fridrich 5c3160
+int ssh_gssapi_userok(char *name, struct passwd *, int kex);
Zoltan Fridrich 5c3160
 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
Zoltan Fridrich 5c3160
 void ssh_gssapi_do_child(char ***, u_int *);
Zoltan Fridrich 5c3160
 void ssh_gssapi_cleanup_creds(void);
Zoltan Fridrich 5c3160
 void ssh_gssapi_storecreds(void);
Zoltan Fridrich 5c3160
 const char *ssh_gssapi_displayname(void);
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
+char *ssh_gssapi_server_mechanisms(void);
Zoltan Fridrich 5c3160
+int ssh_gssapi_oid_table_ok(void);
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
Zoltan Fridrich 5c3160
+void ssh_gssapi_rekey_creds(void);
Zoltan Fridrich 5c3160
+
Zoltan Fridrich 5c3160
 #endif /* GSSAPI */
Zoltan Fridrich 5c3160
 
Zoltan Fridrich 5c3160
 #endif /* _SSH_GSS_H */
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshkey.c b/sshkey.c
Zoltan Fridrich 5c3160
--- a/sshkey.c	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/sshkey.c	2024-08-28 12:35:41.260432315 +0200
Zoltan Fridrich 5c3160
@@ -131,6 +131,75 @@ extern const struct sshkey_impl sshkey_x
Dmitry Belyavskiy f561c6
 extern const struct sshkey_impl sshkey_xmss_cert_impl;
Dmitry Belyavskiy f561c6
 #endif
Zoltan Fridrich 5c3160
 
Dmitry Belyavskiy f561c6
+static int ssh_gss_equal(const struct sshkey *, const struct sshkey *)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static int ssh_gss_serialize_public(const struct sshkey *, struct sshbuf *,
Dmitry Belyavskiy f561c6
+	enum sshkey_serialize_rep)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static int ssh_gss_deserialize_public(const char *, struct sshbuf *,
Dmitry Belyavskiy f561c6
+	     struct sshkey *)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static int ssh_gss_serialize_private(const struct sshkey *, struct sshbuf *,
Dmitry Belyavskiy f561c6
+	     enum sshkey_serialize_rep)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static int ssh_gss_deserialize_private(const char *, struct sshbuf *,
Dmitry Belyavskiy f561c6
+	     struct sshkey *)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static int ssh_gss_copy_public(const struct sshkey *, struct sshkey *)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static int ssh_gss_verify(const struct sshkey *, const u_char *, size_t,
Dmitry Belyavskiy f561c6
+	    const u_char *, size_t, const char *, u_int,
Dmitry Belyavskiy f561c6
+	    struct sshkey_sig_details **)
Dmitry Belyavskiy f561c6
+{
Dmitry Belyavskiy f561c6
+	return SSH_ERR_FEATURE_UNSUPPORTED;
Dmitry Belyavskiy f561c6
+}
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+static const struct sshkey_impl_funcs sshkey_gss_funcs = {
Dmitry Belyavskiy f561c6
+	/* .size = */		NULL,
Dmitry Belyavskiy f561c6
+	/* .alloc = */		NULL,
Dmitry Belyavskiy f561c6
+	/* .cleanup = */	NULL,
Dmitry Belyavskiy f561c6
+	/* .equal = */		ssh_gss_equal,
Dmitry Belyavskiy f561c6
+	/* .ssh_serialize_public = */ ssh_gss_serialize_public,
Dmitry Belyavskiy f561c6
+	/* .ssh_deserialize_public = */ ssh_gss_deserialize_public,
Dmitry Belyavskiy f561c6
+	/* .ssh_serialize_private = */ ssh_gss_serialize_private,
Dmitry Belyavskiy f561c6
+	/* .ssh_deserialize_private = */ ssh_gss_deserialize_private,
Dmitry Belyavskiy f561c6
+	/* .generate = */	NULL,
Dmitry Belyavskiy f561c6
+	/* .copy_public = */	ssh_gss_copy_public,
Dmitry Belyavskiy f561c6
+	/* .sign = */		NULL,
Dmitry Belyavskiy f561c6
+	/* .verify = */		ssh_gss_verify,
Dmitry Belyavskiy f561c6
+};
Dmitry Belyavskiy f561c6
+
Dmitry Belyavskiy f561c6
+/* The struct is intentionally dummy and has no gss calls */
Dmitry Belyavskiy f561c6
+static const struct sshkey_impl sshkey_gss_kex_impl = {
Dmitry Belyavskiy f561c6
+	/* .name = */		"null",
Dmitry Belyavskiy f561c6
+	/* .shortname = */	"null",
Dmitry Belyavskiy f561c6
+	/* .sigalg = */		NULL,
Dmitry Belyavskiy f561c6
+	/* .type = */		KEY_NULL,
Dmitry Belyavskiy f561c6
+	/* .nid = */		0,
Dmitry Belyavskiy f561c6
+	/* .cert = */		0,
Dmitry Belyavskiy f561c6
+	/* .sigonly = */	0,
Dmitry Belyavskiy f561c6
+	/* .keybits = */	0, /* FIXME */
Dmitry Belyavskiy f561c6
+	/* .funcs = */		&sshkey_gss_funcs,
Dmitry Belyavskiy f561c6
+};
Zoltan Fridrich 5c3160
+
Dmitry Belyavskiy f561c6
 const struct sshkey_impl * const keyimpls[] = {
Dmitry Belyavskiy f561c6
 	&sshkey_ed25519_impl,
Zoltan Fridrich 5c3160
 	&sshkey_ed25519_cert_impl,
Zoltan Fridrich 5c3160
@@ -169,6 +238,7 @@ const struct sshkey_impl * const keyimpl
Dmitry Belyavskiy f561c6
 	&sshkey_xmss_impl,
Dmitry Belyavskiy f561c6
 	&sshkey_xmss_cert_impl,
Dmitry Belyavskiy f561c6
 #endif
Dmitry Belyavskiy f561c6
+	&sshkey_gss_kex_impl,
Dmitry Belyavskiy f561c6
 	NULL
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Zoltan Fridrich 5c3160
@@ -324,7 +394,7 @@ sshkey_alg_list(int certs_only, int plai
Jakub Jelen def1de
 
Dmitry Belyavskiy f561c6
 	for (i = 0; keyimpls[i] != NULL; i++) {
Dmitry Belyavskiy f561c6
 		impl = keyimpls[i];
Dmitry Belyavskiy f561c6
-		if (impl->name == NULL)
Dmitry Belyavskiy f561c6
+		if (impl->name == NULL || impl->type == KEY_NULL)
Jakub Jelen def1de
 			continue;
Dmitry Belyavskiy f561c6
 		if (!include_sigonly && impl->sigonly)
Jakub Jelen def1de
 			continue;
Zoltan Fridrich 5c3160
diff --color -ruNp a/sshkey.h b/sshkey.h
Zoltan Fridrich 5c3160
--- a/sshkey.h	2024-07-01 06:36:28.000000000 +0200
Zoltan Fridrich 5c3160
+++ b/sshkey.h	2024-08-28 12:35:41.260432315 +0200
Zoltan Fridrich 5c3160
@@ -71,6 +71,7 @@ enum sshkey_types {
Jakub Jelen 51f5c1
 	KEY_ECDSA_SK_CERT,
Jakub Jelen 51f5c1
 	KEY_ED25519_SK,
Jakub Jelen 51f5c1
 	KEY_ED25519_SK_CERT,
Jakub Jelen def1de
+	KEY_NULL,
Jakub Jelen def1de
 	KEY_UNSPEC
Jakub Jelen def1de
 };
Jakub Jelen def1de