rcolebaugh / rpms / openssh

Forked from rpms/openssh 2 years ago
Clone
Petr Šabata 81d24c
diff --git a/Makefile.in b/Makefile.in
Petr Šabata 81d24c
index e7549470..b68c1710 100644
Petr Šabata 81d24c
--- a/Makefile.in
Petr Šabata 81d24c
+++ b/Makefile.in
Petr Šabata 81d24c
@@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
Petr Šabata 81d24c
 	kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
Petr Šabata 81d24c
 	kexgexc.o kexgexs.o \
DistroBaker d029bb
 	kexsntrup761x25519.o sntrup761.o kexgen.o \
Petr Šabata 81d24c
+	kexgssc.o \
Petr Šabata 81d24c
 	sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
Petr Šabata 81d24c
 	sshbuf-io.o
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
Petr Šabata 81d24c
 	auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
Petr Šabata 81d24c
 	auth2-none.o auth2-passwd.o auth2-pubkey.o \
Petr Šabata 81d24c
 	monitor.o monitor_wrap.o auth-krb5.o \
Petr Šabata 81d24c
-	auth2-gss.o gss-serv.o gss-serv-krb5.o \
Petr Šabata 81d24c
+	auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
Petr Šabata 81d24c
 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
DistroBaker d029bb
 	srclimit.o sftp-server.o sftp-common.o \
Petr Šabata 81d24c
 	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
Dmitry Belyavskiy f9e5de
diff -up a/auth.c.gsskex b/auth.c
Dmitry Belyavskiy f9e5de
--- a/auth.c.gsskex	2021-08-20 06:03:49.000000000 +0200
Dmitry Belyavskiy f9e5de
+++ b/auth.c	2021-08-27 12:41:51.262788953 +0200
Dmitry Belyavskiy f9e5de
@@ -402,7 +402,8 @@ auth_root_allowed(struct ssh *ssh, const
Petr Šabata 81d24c
 	case PERMIT_NO_PASSWD:
Petr Šabata 81d24c
 		if (strcmp(method, "publickey") == 0 ||
Petr Šabata 81d24c
 		    strcmp(method, "hostbased") == 0 ||
Petr Šabata 81d24c
-		    strcmp(method, "gssapi-with-mic") == 0)
Petr Šabata 81d24c
+		    strcmp(method, "gssapi-with-mic") == 0 ||
Petr Šabata 81d24c
+		    strcmp(method, "gssapi-keyex") == 0)
Petr Šabata 81d24c
 			return 1;
Petr Šabata 81d24c
 		break;
Petr Šabata 81d24c
 	case PERMIT_FORCED_ONLY:
Dmitry Belyavskiy f9e5de
@@ -730,97 +731,6 @@ fakepw(void)
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Dmitry Belyavskiy f9e5de
 /*
Petr Šabata 81d24c
- * Returns the remote DNS hostname as a string. The returned string must not
Petr Šabata 81d24c
- * be freed. NB. this will usually trigger a DNS query the first time it is
Petr Šabata 81d24c
- * called.
Petr Šabata 81d24c
- * This function does additional checks on the hostname to mitigate some
Dmitry Belyavskiy f9e5de
- * attacks on based on conflation of hostnames and IP addresses.
Petr Šabata 81d24c
- */
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-static char *
Petr Šabata 81d24c
-remote_hostname(struct ssh *ssh)
Petr Šabata 81d24c
-{
Petr Šabata 81d24c
-	struct sockaddr_storage from;
Petr Šabata 81d24c
-	socklen_t fromlen;
Petr Šabata 81d24c
-	struct addrinfo hints, *ai, *aitop;
Petr Šabata 81d24c
-	char name[NI_MAXHOST], ntop2[NI_MAXHOST];
Petr Šabata 81d24c
-	const char *ntop = ssh_remote_ipaddr(ssh);
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	/* Get IP address of client. */
Petr Šabata 81d24c
-	fromlen = sizeof(from);
Petr Šabata 81d24c
-	memset(&from, 0, sizeof(from));
Petr Šabata 81d24c
-	if (getpeername(ssh_packet_get_connection_in(ssh),
Petr Šabata 81d24c
-	    (struct sockaddr *)&from, &fromlen) == -1) {
Petr Šabata 81d24c
-		debug("getpeername failed: %.100s", strerror(errno));
Petr Šabata 81d24c
-		return xstrdup(ntop);
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	ipv64_normalise_mapped(&from, &fromlen);
Petr Šabata 81d24c
-	if (from.ss_family == AF_INET6)
Petr Šabata 81d24c
-		fromlen = sizeof(struct sockaddr_in6);
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	debug3("Trying to reverse map address %.100s.", ntop);
Petr Šabata 81d24c
-	/* Map the IP address to a host name. */
Petr Šabata 81d24c
-	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
Petr Šabata 81d24c
-	    NULL, 0, NI_NAMEREQD) != 0) {
Petr Šabata 81d24c
-		/* Host name not found.  Use ip address. */
Petr Šabata 81d24c
-		return xstrdup(ntop);
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	/*
Petr Šabata 81d24c
-	 * if reverse lookup result looks like a numeric hostname,
Petr Šabata 81d24c
-	 * someone is trying to trick us by PTR record like following:
Petr Šabata 81d24c
-	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
Petr Šabata 81d24c
-	 */
Petr Šabata 81d24c
-	memset(&hints, 0, sizeof(hints));
Petr Šabata 81d24c
-	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
Petr Šabata 81d24c
-	hints.ai_flags = AI_NUMERICHOST;
Petr Šabata 81d24c
-	if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
Petr Šabata 81d24c
-		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
Petr Šabata 81d24c
-		    name, ntop);
Petr Šabata 81d24c
-		freeaddrinfo(ai);
Petr Šabata 81d24c
-		return xstrdup(ntop);
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	/* Names are stored in lowercase. */
Petr Šabata 81d24c
-	lowercase(name);
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	/*
Petr Šabata 81d24c
-	 * Map it back to an IP address and check that the given
Petr Šabata 81d24c
-	 * address actually is an address of this host.  This is
Petr Šabata 81d24c
-	 * necessary because anyone with access to a name server can
Petr Šabata 81d24c
-	 * define arbitrary names for an IP address. Mapping from
Petr Šabata 81d24c
-	 * name to IP address can be trusted better (but can still be
Petr Šabata 81d24c
-	 * fooled if the intruder has access to the name server of
Petr Šabata 81d24c
-	 * the domain).
Petr Šabata 81d24c
-	 */
Petr Šabata 81d24c
-	memset(&hints, 0, sizeof(hints));
Petr Šabata 81d24c
-	hints.ai_family = from.ss_family;
Petr Šabata 81d24c
-	hints.ai_socktype = SOCK_STREAM;
Petr Šabata 81d24c
-	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
Petr Šabata 81d24c
-		logit("reverse mapping checking getaddrinfo for %.700s "
Petr Šabata 81d24c
-		    "[%s] failed.", name, ntop);
Petr Šabata 81d24c
-		return xstrdup(ntop);
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-	/* Look for the address from the list of addresses. */
Petr Šabata 81d24c
-	for (ai = aitop; ai; ai = ai->ai_next) {
Petr Šabata 81d24c
-		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
Petr Šabata 81d24c
-		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
Petr Šabata 81d24c
-		    (strcmp(ntop, ntop2) == 0))
Petr Šabata 81d24c
-				break;
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-	freeaddrinfo(aitop);
Petr Šabata 81d24c
-	/* If we reached the end of the list, the address was not there. */
Petr Šabata 81d24c
-	if (ai == NULL) {
Petr Šabata 81d24c
-		/* Address not found for the host name. */
Petr Šabata 81d24c
-		logit("Address %.100s maps to %.600s, but this does not "
Petr Šabata 81d24c
-		    "map back to the address.", ntop, name);
Petr Šabata 81d24c
-		return xstrdup(ntop);
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-	return xstrdup(name);
Petr Šabata 81d24c
-}
Petr Šabata 81d24c
-
Dmitry Belyavskiy f9e5de
-/*
Petr Šabata 81d24c
  * Return the canonical name of the host in the other side of the current
Petr Šabata 81d24c
  * connection.  The host name is cached, so it is efficient to call this
Dmitry Belyavskiy f9e5de
  * several times.
Petr Šabata 81d24c
diff --git a/auth2-gss.c b/auth2-gss.c
Petr Šabata 81d24c
index 9351e042..d6446c0c 100644
Petr Šabata 81d24c
--- a/auth2-gss.c
Petr Šabata 81d24c
+++ b/auth2-gss.c
Petr Šabata 81d24c
@@ -1,7 +1,7 @@
DistroBaker d029bb
 /* $OpenBSD: auth2-gss.c,v 1.32 2021/01/27 10:15:08 djm Exp $ */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
  *
Petr Šabata 81d24c
  * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
  * modification, are permitted provided that the following conditions
Petr Šabata 81d24c
@@ -54,6 +54,48 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
Petr Šabata 81d24c
 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
Petr Šabata 81d24c
 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * The 'gssapi_keyex' userauth mechanism.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static int
Petr Šabata 81d24c
+userauth_gsskeyex(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	Authctxt *authctxt = ssh->authctxt;
Petr Šabata 81d24c
+	int r, authenticated = 0;
Petr Šabata 81d24c
+	struct sshbuf *b = NULL;
Petr Šabata 81d24c
+	gss_buffer_desc mic, gssbuf;
Petr Šabata 81d24c
+	u_char *p;
Petr Šabata 81d24c
+	size_t len;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_get_end(ssh)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "parsing");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((b = sshbuf_new()) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_new failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	mic.value = p;
Petr Šabata 81d24c
+	mic.length = len;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
DistroBaker d029bb
+	    "gssapi-keyex", ssh->kex->session_id);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_mutable_ptr failed");
Petr Šabata 81d24c
+	gssbuf.length = sshbuf_len(b);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* gss_kex_context is NULL with privsep, so we can't check it here */
Petr Šabata 81d24c
+	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
Petr Šabata 81d24c
+	    &gssbuf, &mic))))
Petr Šabata 81d24c
+		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
Petr Šabata 81d24c
+		    authctxt->pw, 1));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_free(b);
Petr Šabata 81d24c
+	free(mic.value);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (authenticated);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
  * We only support those mechanisms that we know about (ie ones that we know
Petr Šabata 81d24c
  * how to check local user kuserok and the like)
Petr Šabata 81d24c
@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
Petr Šabata 81d24c
 	if ((r = sshpkt_get_end(ssh)) != 0)
DistroBaker d029bb
 		fatal_fr(r, "parse packet");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
Petr Šabata 81d24c
+	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
Petr Šabata 81d24c
+	    authctxt->pw, 1));
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((!use_privsep || mm_is_monitor()) &&
Petr Šabata 81d24c
 	    (displayname = ssh_gssapi_displayname()) != NULL)
Petr Šabata 81d24c
@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
Petr Šabata 81d24c
 	gssbuf.length = sshbuf_len(b);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
Petr Šabata 81d24c
-		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
Petr Šabata 81d24c
+		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
Petr Šabata 81d24c
+		    authctxt->pw, 0));
Petr Šabata 81d24c
 	else
Petr Šabata 81d24c
 		logit("GSSAPI MIC check failed");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
Petr Šabata 81d24c
 	return 0;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+Authmethod method_gsskeyex = {
Petr Šabata 81d24c
+	"gssapi-keyex",
Petr Šabata 81d24c
+	userauth_gsskeyex,
Petr Šabata 81d24c
+	&options.gss_authentication
Petr Šabata 81d24c
+};
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 Authmethod method_gssapi = {
Petr Šabata 81d24c
 	"gssapi-with-mic",
Petr Šabata 81d24c
 	userauth_gssapi,
Petr Šabata 81d24c
diff --git a/auth2.c b/auth2.c
Petr Šabata 81d24c
index 0e776224..1c217268 100644
Petr Šabata 81d24c
--- a/auth2.c
Petr Šabata 81d24c
+++ b/auth2.c
Petr Šabata 81d24c
@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
Petr Šabata 81d24c
 extern Authmethod method_kbdint;
Petr Šabata 81d24c
 extern Authmethod method_hostbased;
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
+extern Authmethod method_gsskeyex;
Petr Šabata 81d24c
 extern Authmethod method_gssapi;
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
Petr Šabata 81d24c
 	&method_none,
Petr Šabata 81d24c
 	&method_pubkey,
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
+	&method_gsskeyex,
Petr Šabata 81d24c
 	&method_gssapi,
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 	&method_passwd,
Petr Šabata 81d24c
diff --git a/canohost.c b/canohost.c
Petr Šabata 81d24c
index abea9c6e..8e81b519 100644
Petr Šabata 81d24c
--- a/canohost.c
Petr Šabata 81d24c
+++ b/canohost.c
Petr Šabata 81d24c
@@ -35,6 +35,99 @@
Petr Šabata 81d24c
 #include "canohost.h"
Petr Šabata 81d24c
 #include "misc.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Returns the remote DNS hostname as a string. The returned string must not
Petr Šabata 81d24c
+ * be freed. NB. this will usually trigger a DNS query the first time it is
Petr Šabata 81d24c
+ * called.
Petr Šabata 81d24c
+ * This function does additional checks on the hostname to mitigate some
Petr Šabata 81d24c
+ * attacks on legacy rhosts-style authentication.
Petr Šabata 81d24c
+ * XXX is RhostsRSAAuthentication vulnerable to these?
Petr Šabata 81d24c
+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+remote_hostname(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct sockaddr_storage from;
Petr Šabata 81d24c
+	socklen_t fromlen;
Petr Šabata 81d24c
+	struct addrinfo hints, *ai, *aitop;
Petr Šabata 81d24c
+	char name[NI_MAXHOST], ntop2[NI_MAXHOST];
Petr Šabata 81d24c
+	const char *ntop = ssh_remote_ipaddr(ssh);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Get IP address of client. */
Petr Šabata 81d24c
+	fromlen = sizeof(from);
Petr Šabata 81d24c
+	memset(&from, 0, sizeof(from));
Petr Šabata 81d24c
+	if (getpeername(ssh_packet_get_connection_in(ssh),
Petr Šabata 81d24c
+	    (struct sockaddr *)&from, &fromlen) == -1) {
Petr Šabata 81d24c
+		debug("getpeername failed: %.100s", strerror(errno));
Petr Šabata 81d24c
+		return xstrdup(ntop);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ipv64_normalise_mapped(&from, &fromlen);
Petr Šabata 81d24c
+	if (from.ss_family == AF_INET6)
Petr Šabata 81d24c
+		fromlen = sizeof(struct sockaddr_in6);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug3("Trying to reverse map address %.100s.", ntop);
Petr Šabata 81d24c
+	/* Map the IP address to a host name. */
Petr Šabata 81d24c
+	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
Petr Šabata 81d24c
+	    NULL, 0, NI_NAMEREQD) != 0) {
Petr Šabata 81d24c
+		/* Host name not found.  Use ip address. */
Petr Šabata 81d24c
+		return xstrdup(ntop);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * if reverse lookup result looks like a numeric hostname,
Petr Šabata 81d24c
+	 * someone is trying to trick us by PTR record like following:
Petr Šabata 81d24c
+	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+	memset(&hints, 0, sizeof(hints));
Petr Šabata 81d24c
+	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
Petr Šabata 81d24c
+	hints.ai_flags = AI_NUMERICHOST;
Petr Šabata 81d24c
+	if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
Petr Šabata 81d24c
+		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
Petr Šabata 81d24c
+		    name, ntop);
Petr Šabata 81d24c
+		freeaddrinfo(ai);
Petr Šabata 81d24c
+		return xstrdup(ntop);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Names are stored in lowercase. */
Petr Šabata 81d24c
+	lowercase(name);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * Map it back to an IP address and check that the given
Petr Šabata 81d24c
+	 * address actually is an address of this host.  This is
Petr Šabata 81d24c
+	 * necessary because anyone with access to a name server can
Petr Šabata 81d24c
+	 * define arbitrary names for an IP address. Mapping from
Petr Šabata 81d24c
+	 * name to IP address can be trusted better (but can still be
Petr Šabata 81d24c
+	 * fooled if the intruder has access to the name server of
Petr Šabata 81d24c
+	 * the domain).
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+	memset(&hints, 0, sizeof(hints));
Petr Šabata 81d24c
+	hints.ai_family = from.ss_family;
Petr Šabata 81d24c
+	hints.ai_socktype = SOCK_STREAM;
Petr Šabata 81d24c
+	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
Petr Šabata 81d24c
+		logit("reverse mapping checking getaddrinfo for %.700s "
Petr Šabata 81d24c
+		    "[%s] failed.", name, ntop);
Petr Šabata 81d24c
+		return xstrdup(ntop);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	/* Look for the address from the list of addresses. */
Petr Šabata 81d24c
+	for (ai = aitop; ai; ai = ai->ai_next) {
Petr Šabata 81d24c
+		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
Petr Šabata 81d24c
+		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
Petr Šabata 81d24c
+		    (strcmp(ntop, ntop2) == 0))
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	freeaddrinfo(aitop);
Petr Šabata 81d24c
+	/* If we reached the end of the list, the address was not there. */
Petr Šabata 81d24c
+	if (ai == NULL) {
Petr Šabata 81d24c
+		/* Address not found for the host name. */
Petr Šabata 81d24c
+		logit("Address %.100s maps to %.600s, but this does not "
Petr Šabata 81d24c
+		    "map back to the address.", ntop, name);
Petr Šabata 81d24c
+		return xstrdup(ntop);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	return xstrdup(name);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 void
Petr Šabata 81d24c
 ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
diff --git a/canohost.h b/canohost.h
Petr Šabata 81d24c
index 26d62855..0cadc9f1 100644
Petr Šabata 81d24c
--- a/canohost.h
Petr Šabata 81d24c
+++ b/canohost.h
Petr Šabata 81d24c
@@ -15,6 +15,9 @@
Petr Šabata 81d24c
 #ifndef _CANOHOST_H
Petr Šabata 81d24c
 #define _CANOHOST_H
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+struct ssh;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char		*remote_hostname(struct ssh *);
Petr Šabata 81d24c
 char		*get_peer_ipaddr(int);
Petr Šabata 81d24c
 int		 get_peer_port(int);
Petr Šabata 81d24c
 char		*get_local_ipaddr(int);
Petr Šabata 81d24c
diff --git a/clientloop.c b/clientloop.c
Petr Šabata 81d24c
index ebd0dbca..1bdac6a4 100644
Petr Šabata 81d24c
--- a/clientloop.c
Petr Šabata 81d24c
+++ b/clientloop.c
Petr Šabata 81d24c
@@ -112,6 +112,10 @@
Petr Šabata 81d24c
 #include "ssherr.h"
Petr Šabata 81d24c
 #include "hostfile.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* import options */
Petr Šabata 81d24c
 extern Options options;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -1379,9 +1383,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
Petr Šabata 81d24c
 			break;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 		/* Do channel operations unless rekeying in progress. */
Petr Šabata 81d24c
-		if (!ssh_packet_is_rekeying(ssh))
Petr Šabata 81d24c
+		if (!ssh_packet_is_rekeying(ssh)) {
Petr Šabata 81d24c
 			channel_after_select(ssh, readset, writeset);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+			if (options.gss_renewal_rekey &&
Petr Šabata 81d24c
+			    ssh_gssapi_credentials_updated(NULL)) {
Petr Šabata 81d24c
+				debug("credentials updated - forcing rekey");
Petr Šabata 81d24c
+				need_rekeying = 1;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 		/* Buffer input from the connection.  */
Petr Šabata 81d24c
 		client_process_net_input(ssh, readset);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
diff --git a/configure.ac b/configure.ac
Petr Šabata 81d24c
index b689db4b..efafb6bd 100644
Petr Šabata 81d24c
--- a/configure.ac
Petr Šabata 81d24c
+++ b/configure.ac
Petr Šabata 81d24c
@@ -674,6 +674,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
Petr Šabata 81d24c
 	    [Use tunnel device compatibility to OpenBSD])
Petr Šabata 81d24c
 	AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
Petr Šabata 81d24c
 	    [Prepend the address family to IP tunnel traffic])
Petr Šabata 81d24c
+	AC_MSG_CHECKING([if we have the Security Authorization Session API])
Petr Šabata 81d24c
+	AC_TRY_COMPILE([#include <Security/AuthSession.h>],
Petr Šabata 81d24c
+		[SessionCreate(0, 0);],
Petr Šabata 81d24c
+		[ac_cv_use_security_session_api="yes"
Petr Šabata 81d24c
+		 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
Petr Šabata 81d24c
+			[platform has the Security Authorization Session API])
Petr Šabata 81d24c
+		 LIBS="$LIBS -framework Security"
Petr Šabata 81d24c
+		 AC_MSG_RESULT([yes])],
Petr Šabata 81d24c
+		[ac_cv_use_security_session_api="no"
Petr Šabata 81d24c
+		 AC_MSG_RESULT([no])])
Petr Šabata 81d24c
+	AC_MSG_CHECKING([if we have an in-memory credentials cache])
Petr Šabata 81d24c
+	AC_TRY_COMPILE(
Petr Šabata 81d24c
+		[#include <Kerberos/Kerberos.h>],
Petr Šabata 81d24c
+		[cc_context_t c;
Petr Šabata 81d24c
+		 (void) cc_initialize (&c, 0, NULL, NULL);],
Petr Šabata 81d24c
+		[AC_DEFINE([USE_CCAPI], [1],
Petr Šabata 81d24c
+			[platform uses an in-memory credentials cache])
Petr Šabata 81d24c
+		 LIBS="$LIBS -framework Security"
Petr Šabata 81d24c
+		 AC_MSG_RESULT([yes])
Petr Šabata 81d24c
+		 if test "x$ac_cv_use_security_session_api" = "xno"; then
Petr Šabata 81d24c
+			AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
Petr Šabata 81d24c
+		fi],
Petr Šabata 81d24c
+		[AC_MSG_RESULT([no])]
Petr Šabata 81d24c
+	)
Petr Šabata 81d24c
 	m4_pattern_allow([AU_IPv])
Petr Šabata 81d24c
 	AC_CHECK_DECL([AU_IPv4], [],
Petr Šabata 81d24c
 	    AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
Petr Šabata 81d24c
diff --git a/gss-genr.c b/gss-genr.c
Petr Šabata 81d24c
index d56257b4..763a63ff 100644
Petr Šabata 81d24c
--- a/gss-genr.c
Petr Šabata 81d24c
+++ b/gss-genr.c
Petr Šabata 81d24c
@@ -1,7 +1,7 @@
DistroBaker d029bb
 /* $OpenBSD: gss-genr.c,v 1.28 2021/01/27 10:05:28 djm Exp $ */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
  *
Petr Šabata 81d24c
  * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
  * modification, are permitted provided that the following conditions
DistroBaker d029bb
@@ -41,9 +41,33 @@
Petr Šabata 81d24c
 #include "sshbuf.h"
Petr Šabata 81d24c
 #include "log.h"
Petr Šabata 81d24c
 #include "ssh2.h"
Petr Šabata 81d24c
+#include "cipher.h"
Petr Šabata 81d24c
+#include "sshkey.h"
Petr Šabata 81d24c
+#include "kex.h"
Petr Šabata 81d24c
+#include "digest.h"
Petr Šabata 81d24c
+#include "packet.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #include "ssh-gss.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+typedef struct {
Petr Šabata 81d24c
+	char *encoded;
Petr Šabata 81d24c
+	gss_OID oid;
Petr Šabata 81d24c
+} ssh_gss_kex_mapping;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * XXX - It would be nice to find a more elegant way of handling the
Petr Šabata 81d24c
+ * XXX   passing of the key exchange context to the userauth routines
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+Gssctxt *gss_kex_context = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+ssh_gssapi_oid_table_ok(void) {
Petr Šabata 81d24c
+	return (gss_enc2oid != NULL);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* sshbuf_get for gss_buffer_desc */
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
 ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
DistroBaker d029bb
@@ -62,6 +86,159 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
Petr Šabata 81d24c
 	return 0;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/* sshpkt_get of gss_buffer_desc */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+	u_char *p;
Petr Šabata 81d24c
+	size_t len;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
Petr Šabata 81d24c
+		return r;
Petr Šabata 81d24c
+	g->value = p;
Petr Šabata 81d24c
+	g->length = len;
Petr Šabata 81d24c
+	return 0;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * We test mechanisms to ensure that we can use them, to avoid starting
Petr Šabata 81d24c
+ * a key exchange with a bad mechanism
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+ssh_gssapi_client_mechanisms(const char *host, const char *client,
Petr Šabata 81d24c
+    const char *kex) {
Petr Šabata 81d24c
+	gss_OID_set gss_supported = NULL;
Petr Šabata 81d24c
+	OM_uint32 min_status;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
Petr Šabata 81d24c
+	    host, client, kex);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
Petr Šabata 81d24c
+    const char *host, const char *client, const char *kex) {
Petr Šabata 81d24c
+	struct sshbuf *buf = NULL;
Petr Šabata 81d24c
+	size_t i;
Petr Šabata 81d24c
+	int r = SSH_ERR_ALLOC_FAIL;
Petr Šabata 81d24c
+	int oidpos, enclen;
Petr Šabata 81d24c
+	char *mechs, *encoded;
Petr Šabata 81d24c
+	u_char digest[SSH_DIGEST_MAX_LENGTH];
Petr Šabata 81d24c
+	char deroid[2];
Petr Šabata 81d24c
+	struct ssh_digest_ctx *md = NULL;
Petr Šabata 81d24c
+	char *s, *cp, *p;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_enc2oid != NULL) {
Petr Šabata 81d24c
+		for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
Petr Šabata 81d24c
+			free(gss_enc2oid[i].encoded);
Petr Šabata 81d24c
+		free(gss_enc2oid);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
Petr Šabata 81d24c
+	    (gss_supported->count + 1));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((buf = sshbuf_new()) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_new failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	oidpos = 0;
Petr Šabata 81d24c
+	s = cp = xstrdup(kex);
Petr Šabata 81d24c
+	for (i = 0; i < gss_supported->count; i++) {
Petr Šabata 81d24c
+		if (gss_supported->elements[i].length < 128 &&
Petr Šabata 81d24c
+		    (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			deroid[0] = SSH_GSS_OIDTYPE;
Petr Šabata 81d24c
+			deroid[1] = gss_supported->elements[i].length;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
Petr Šabata 81d24c
+			    (r = ssh_digest_update(md, deroid, 2)) != 0 ||
Petr Šabata 81d24c
+			    (r = ssh_digest_update(md,
Petr Šabata 81d24c
+			        gss_supported->elements[i].elements,
Petr Šabata 81d24c
+			        gss_supported->elements[i].length)) != 0 ||
Petr Šabata 81d24c
+			    (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
DistroBaker d029bb
+				fatal_fr(r, "digest failed");
Petr Šabata 81d24c
+			ssh_digest_free(md);
Petr Šabata 81d24c
+			md = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
Petr Šabata 81d24c
+			    * 2);
Petr Šabata 81d24c
+			enclen = __b64_ntop(digest,
Petr Šabata 81d24c
+			    ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
Petr Šabata 81d24c
+			    ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			cp = strncpy(s, kex, strlen(kex));
Petr Šabata 81d24c
+			for ((p = strsep(&cp, ",")); p && *p != '\0';
Petr Šabata 81d24c
+				(p = strsep(&cp, ","))) {
Petr Šabata 81d24c
+				if (sshbuf_len(buf) != 0 &&
Petr Šabata 81d24c
+				    (r = sshbuf_put_u8(buf, ',')) != 0)
DistroBaker d029bb
+					fatal_fr(r, "sshbuf_put_u8 error");
Petr Šabata 81d24c
+				if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
Petr Šabata 81d24c
+				    (r = sshbuf_put(buf, encoded, enclen)) != 0)
DistroBaker d029bb
+					fatal_fr(r, "sshbuf_put error");
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
Petr Šabata 81d24c
+			gss_enc2oid[oidpos].encoded = encoded;
Petr Šabata 81d24c
+			oidpos++;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	free(s);
Petr Šabata 81d24c
+	gss_enc2oid[oidpos].oid = NULL;
Petr Šabata 81d24c
+	gss_enc2oid[oidpos].encoded = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((mechs = sshbuf_dup_string(buf)) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_dup_string failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_free(buf);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(mechs) == 0) {
Petr Šabata 81d24c
+		free(mechs);
Petr Šabata 81d24c
+		mechs = NULL;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (mechs);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+gss_OID
Petr Šabata 81d24c
+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
Petr Šabata 81d24c
+	int i = 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define SKIP_KEX_NAME(type) \
Petr Šabata 81d24c
+	case type: \
Petr Šabata 81d24c
+		if (strlen(name) < sizeof(type##_ID)) \
Petr Šabata 81d24c
+			return GSS_C_NO_OID; \
Petr Šabata 81d24c
+		name += sizeof(type##_ID) - 1; \
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	switch (kex_type) {
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1)
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1)
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256)
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512)
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_GEX_SHA1)
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256)
Petr Šabata 81d24c
+	SKIP_KEX_NAME(KEX_GSS_C25519_SHA256)
Petr Šabata 81d24c
+	default:
Petr Šabata 81d24c
+		return GSS_C_NO_OID;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#undef SKIP_KEX_NAME
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	while (gss_enc2oid[i].encoded != NULL &&
Petr Šabata 81d24c
+	    strcmp(name, gss_enc2oid[i].encoded) != 0)
Petr Šabata 81d24c
+		i++;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_enc2oid[i].oid != NULL && ctx != NULL)
Petr Šabata 81d24c
+		ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return gss_enc2oid[i].oid;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* Check that the OID in a data stream matches that in the context */
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
Petr Šabata 81d24c
@@ -218,7 +398,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	ctx->major = gss_init_sec_context(&ctx->minor,
Petr Šabata 81d24c
-	    GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
Petr Šabata 81d24c
+	    ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
Petr Šabata 81d24c
 	    GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
Petr Šabata 81d24c
 	    0, NULL, recv_tok, NULL, send_tok, flags, NULL);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -247,9 +427,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
Petr Šabata 81d24c
 	return (ctx->major);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+OM_uint32
Petr Šabata 81d24c
+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	gss_buffer_desc gssbuf;
Petr Šabata 81d24c
+	gss_name_t gssname;
Petr Šabata 81d24c
+	OM_uint32 status;
Petr Šabata 81d24c
+	gss_OID_set oidset;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gssbuf.value = (void *) name;
Petr Šabata 81d24c
+	gssbuf.length = strlen(gssbuf.value);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_create_empty_oid_set(&status, &oidset);
Petr Šabata 81d24c
+	gss_add_oid_set_member(&status, ctx->oid, &oidset);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ctx->major = gss_import_name(&ctx->minor, &gssbuf,
Petr Šabata 81d24c
+	    GSS_C_NT_USER_NAME, &gssname);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!ctx->major)
Petr Šabata 81d24c
+		ctx->major = gss_acquire_cred(&ctx->minor,
Petr Šabata 81d24c
+		    gssname, 0, oidset, GSS_C_INITIATE,
Petr Šabata 81d24c
+		    &ctx->client_creds, NULL, NULL);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_release_name(&status, &gssname);
Petr Šabata 81d24c
+	gss_release_oid_set(&status, &oidset);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (ctx->major)
Petr Šabata 81d24c
+		ssh_gssapi_error(ctx);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return(ctx->major);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 OM_uint32
Petr Šabata 81d24c
 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
+	if (ctx == NULL)
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
Petr Šabata 81d24c
 	    GSS_C_QOP_DEFAULT, buffer, hash)))
Petr Šabata 81d24c
 		ssh_gssapi_error(ctx);
Petr Šabata 81d24c
@@ -257,6 +471,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
Petr Šabata 81d24c
 	return (ctx->major);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/* Priviledged when used by server */
Petr Šabata 81d24c
+OM_uint32
Petr Šabata 81d24c
+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	if (ctx == NULL)
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
Petr Šabata 81d24c
+	    gssbuf, gssmic, NULL);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (ctx->major);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 void
Petr Šabata 81d24c
 ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
DistroBaker d029bb
     const char *context, const struct sshbuf *session_id)
Petr Šabata 81d24c
@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
Petr Šabata 81d24c
+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
Petr Šabata 81d24c
+    const char *client)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
Petr Šabata 81d24c
 	OM_uint32 major, minor;
Petr Šabata 81d24c
 	gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
Petr Šabata 81d24c
+	Gssctxt *intctx = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (ctx == NULL)
Petr Šabata 81d24c
+		ctx = &intct;;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* RFC 4462 says we MUST NOT do SPNEGO */
Petr Šabata 81d24c
 	if (oid->length == spnego_oid.length && 
Petr Šabata 81d24c
@@ -287,6 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
Petr Šabata 81d24c
 	ssh_gssapi_build_ctx(ctx);
Petr Šabata 81d24c
 	ssh_gssapi_set_oid(*ctx, oid);
Petr Šabata 81d24c
 	major = ssh_gssapi_import_name(*ctx, host);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!GSS_ERROR(major) && client)
Petr Šabata 81d24c
+		major = ssh_gssapi_client_identity(*ctx, client);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (!GSS_ERROR(major)) {
Petr Šabata 81d24c
 		major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 
Petr Šabata 81d24c
 		    NULL);
Petr Šabata 81d24c
@@ -296,10 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
Petr Šabata 81d24c
 			    GSS_C_NO_BUFFER);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (GSS_ERROR(major)) 
Petr Šabata 81d24c
+	if (GSS_ERROR(major) || intctx != NULL)
Petr Šabata 81d24c
 		ssh_gssapi_delete_ctx(ctx);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	return (!GSS_ERROR(major));
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
Petr Šabata 81d24c
+	static gss_name_t saved_name = GSS_C_NO_NAME;
Petr Šabata 81d24c
+	static OM_uint32 saved_lifetime = 0;
Petr Šabata 81d24c
+	static gss_OID saved_mech = GSS_C_NO_OID;
Petr Šabata 81d24c
+	static gss_name_t name;
Petr Šabata 81d24c
+	static OM_uint32 last_call = 0;
Petr Šabata 81d24c
+	OM_uint32 lifetime, now, major, minor;
Petr Šabata 81d24c
+	int equal;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	now = time(NULL);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (ctxt) {
Petr Šabata 81d24c
+		debug("Rekey has happened - updating saved versions");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (saved_name != GSS_C_NO_NAME)
Petr Šabata 81d24c
+			gss_release_name(&minor, &saved_name);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
Petr Šabata 81d24c
+		    &saved_name, &saved_lifetime, NULL, NULL);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (!GSS_ERROR(major)) {
Petr Šabata 81d24c
+			saved_mech = ctxt->oid;
Petr Šabata 81d24c
+		        saved_lifetime+= now;
Petr Šabata 81d24c
+		} else {
Petr Šabata 81d24c
+			/* Handle the error */
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (now - last_call < 10)
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	last_call = now;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (saved_mech == GSS_C_NO_OID)
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
Petr Šabata 81d24c
+	    &name, &lifetime, NULL, NULL);
Petr Šabata 81d24c
+	if (major == GSS_S_CREDENTIALS_EXPIRED)
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	else if (GSS_ERROR(major))
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	major = gss_compare_name(&minor, saved_name, name, &equal);
Petr Šabata 81d24c
+	gss_release_name(&minor, &name);
Petr Šabata 81d24c
+	if (GSS_ERROR(major))
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (equal && (saved_lifetime < lifetime + now - 10))
Petr Šabata 81d24c
+		return 1;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return 0;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 #endif /* GSSAPI */
Petr Šabata 81d24c
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
Petr Šabata 81d24c
index a151bc1e..8d2b677f 100644
Petr Šabata 81d24c
--- a/gss-serv-krb5.c
Petr Šabata 81d24c
+++ b/gss-serv-krb5.c
Petr Šabata 81d24c
@@ -1,7 +1,7 @@
Petr Šabata 81d24c
 /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
  *
Petr Šabata 81d24c
  * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
  * modification, are permitted provided that the following conditions
Petr Šabata 81d24c
@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
Petr Šabata 81d24c
 	krb5_error_code problem;
Petr Šabata 81d24c
 	krb5_principal princ;
Petr Šabata 81d24c
 	OM_uint32 maj_status, min_status;
Petr Šabata 81d24c
-	int len;
Petr Šabata 81d24c
+	const char *new_ccname, *new_cctype;
Petr Šabata 81d24c
 	const char *errmsg;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if (client->creds == NULL) {
Petr Šabata 81d24c
@@ -180,11 +180,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
Petr Šabata 81d24c
 		return;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
Petr Šabata 81d24c
+	new_cctype = krb5_cc_get_type(krb_context, ccache);
Petr Šabata 81d24c
+	new_ccname = krb5_cc_get_name(krb_context, ccache);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	client->store.envvar = "KRB5CCNAME";
Petr Šabata 81d24c
-	len = strlen(client->store.filename) + 6;
Petr Šabata 81d24c
-	client->store.envval = xmalloc(len);
Petr Šabata 81d24c
-	snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
Petr Šabata 81d24c
+#ifdef USE_CCAPI
Petr Šabata 81d24c
+	xasprintf(&client->store.envval, "API:%s", new_ccname);
Petr Šabata 81d24c
+	client->store.filename = NULL;
Petr Šabata 81d24c
+#else
Petr Šabata 81d24c
+	if (new_ccname[0] == ':')
Petr Šabata 81d24c
+		new_ccname++;
Petr Šabata 81d24c
+	xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname);
Petr Šabata 81d24c
+	if (strcmp(new_cctype, "DIR") == 0) {
Petr Šabata 81d24c
+		char *p;
Petr Šabata 81d24c
+		p = strrchr(client->store.envval, '/');
Petr Šabata 81d24c
+		if (p)
Petr Šabata 81d24c
+			*p = '\0';
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0))
Petr Šabata 81d24c
+		client->store.filename = xstrdup(new_ccname);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef USE_PAM
Petr Šabata 81d24c
 	if (options.use_pam)
Petr Šabata 81d24c
@@ -193,9 +208,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	krb5_cc_close(krb_context, ccache);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	client->store.data = krb_context;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	return;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
Petr Šabata 81d24c
+    ssh_gssapi_client *client)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	krb5_ccache ccache = NULL;
Petr Šabata 81d24c
+	krb5_principal principal = NULL;
Petr Šabata 81d24c
+	char *name = NULL;
Petr Šabata 81d24c
+	krb5_error_code problem;
Petr Šabata 81d24c
+	OM_uint32 maj_status, min_status;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
Petr Šabata 81d24c
+                logit("krb5_cc_resolve(): %.100s",
Petr Šabata 81d24c
+                    krb5_get_err_text(krb_context, problem));
Petr Šabata 81d24c
+                return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Find out who the principal in this cache is */
Petr Šabata 81d24c
+	if ((problem = krb5_cc_get_principal(krb_context, ccache,
Petr Šabata 81d24c
+	    &principal))) {
Petr Šabata 81d24c
+		logit("krb5_cc_get_principal(): %.100s",
Petr Šabata 81d24c
+		    krb5_get_err_text(krb_context, problem));
Petr Šabata 81d24c
+		krb5_cc_close(krb_context, ccache);
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
Petr Šabata 81d24c
+		logit("krb5_unparse_name(): %.100s",
Petr Šabata 81d24c
+		    krb5_get_err_text(krb_context, problem));
Petr Šabata 81d24c
+		krb5_free_principal(krb_context, principal);
Petr Šabata 81d24c
+		krb5_cc_close(krb_context, ccache);
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strcmp(name,client->exportedname.value)!=0) {
Petr Šabata 81d24c
+		debug("Name in local credentials cache differs. Not storing");
Petr Šabata 81d24c
+		krb5_free_principal(krb_context, principal);
Petr Šabata 81d24c
+		krb5_cc_close(krb_context, ccache);
Petr Šabata 81d24c
+		krb5_free_unparsed_name(krb_context, name);
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	krb5_free_unparsed_name(krb_context, name);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Name matches, so lets get on with it! */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
Petr Šabata 81d24c
+		logit("krb5_cc_initialize(): %.100s",
Petr Šabata 81d24c
+		    krb5_get_err_text(krb_context, problem));
Petr Šabata 81d24c
+		krb5_free_principal(krb_context, principal);
Petr Šabata 81d24c
+		krb5_cc_close(krb_context, ccache);
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	krb5_free_principal(krb_context, principal);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
Petr Šabata 81d24c
+	    ccache))) {
Petr Šabata 81d24c
+		logit("gss_krb5_copy_ccache() failed. Sorry!");
Petr Šabata 81d24c
+		krb5_cc_close(krb_context, ccache);
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return 1;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 ssh_gssapi_mech gssapi_kerberos_mech = {
Petr Šabata 81d24c
 	"toWM5Slw5Ew8Mqkay+al2g==",
Petr Šabata 81d24c
 	"Kerberos",
Petr Šabata 81d24c
@@ -203,7 +285,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
Petr Šabata 81d24c
 	NULL,
Petr Šabata 81d24c
 	&ssh_gssapi_krb5_userok,
Petr Šabata 81d24c
 	NULL,
Petr Šabata 81d24c
-	&ssh_gssapi_krb5_storecreds
Petr Šabata 81d24c
+	&ssh_gssapi_krb5_storecreds,
Petr Šabata 81d24c
+	&ssh_gssapi_krb5_updatecreds
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #endif /* KRB5 */
Petr Šabata 81d24c
diff --git a/gss-serv.c b/gss-serv.c
Petr Šabata 81d24c
index ab3a15f0..6ce56e92 100644
Petr Šabata 81d24c
--- a/gss-serv.c
Petr Šabata 81d24c
+++ b/gss-serv.c
Petr Šabata 81d24c
@@ -1,7 +1,7 @@
Petr Šabata 81d24c
 /* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
  *
Petr Šabata 81d24c
  * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
  * modification, are permitted provided that the following conditions
Petr Šabata 81d24c
@@ -44,17 +44,19 @@
Petr Šabata 81d24c
 #include "session.h"
Petr Šabata 81d24c
 #include "misc.h"
Petr Šabata 81d24c
 #include "servconf.h"
Petr Šabata 81d24c
+#include "uidswap.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #include "ssh-gss.h"
Petr Šabata 81d24c
+#include "monitor_wrap.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 extern ServerOptions options;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static ssh_gssapi_client gssapi_client =
Petr Šabata 81d24c
-    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
Petr Šabata 81d24c
-    GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
Petr Šabata 81d24c
+    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
Petr Šabata 81d24c
+    GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0};
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 ssh_gssapi_mech gssapi_null_mech =
Petr Šabata 81d24c
-    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
Petr Šabata 81d24c
+    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef KRB5
Petr Šabata 81d24c
 extern ssh_gssapi_mech gssapi_kerberos_mech;
Petr Šabata 81d24c
@@ -140,6 +142,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
Petr Šabata 81d24c
 	return (ssh_gssapi_acquire_cred(*ctx));
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/* Unprivileged */
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+ssh_gssapi_server_mechanisms(void) {
Petr Šabata 81d24c
+	if (supported_oids == NULL)
Petr Šabata 81d24c
+		ssh_gssapi_prepare_supported_oids();
Petr Šabata 81d24c
+	return (ssh_gssapi_kex_mechs(supported_oids,
Petr Šabata 81d24c
+	    &ssh_gssapi_server_check_mech, NULL, NULL,
Petr Šabata 81d24c
+	    options.gss_kex_algorithms));
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* Unprivileged */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
Petr Šabata 81d24c
+    const char *dummy) {
Petr Šabata 81d24c
+	Gssctxt *ctx = NULL;
Petr Šabata 81d24c
+	int res;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
Petr Šabata 81d24c
+	ssh_gssapi_delete_ctx(&ctx;;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (res);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* Unprivileged */
Petr Šabata 81d24c
 void
Petr Šabata 81d24c
 ssh_gssapi_supported_oids(gss_OID_set *oidset)
Petr Šabata 81d24c
@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
Petr Šabata 81d24c
 	gss_OID_set supported;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	gss_create_empty_oid_set(&min_status, oidset);
Petr Šabata 81d24c
-	gss_indicate_mechs(&min_status, &supported);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	while (supported_mechs[i]->name != NULL) {
Petr Šabata 81d24c
 		if (GSS_ERROR(gss_test_oid_set_member(&min_status,
Petr Šabata 81d24c
@@ -276,8 +303,48 @@ OM_uint32
Petr Šabata 81d24c
 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	int i = 0;
Petr Šabata 81d24c
+	int equal = 0;
Petr Šabata 81d24c
+	gss_name_t new_name = GSS_C_NO_NAME;
Petr Šabata 81d24c
+	gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (options.gss_store_rekey && client->used && ctx->client_creds) {
Petr Šabata 81d24c
+		if (client->mech->oid.length != ctx->oid->length ||
Petr Šabata 81d24c
+		    (memcmp(client->mech->oid.elements,
Petr Šabata 81d24c
+		     ctx->oid->elements, ctx->oid->length) !=0)) {
Petr Šabata 81d24c
+			debug("Rekeyed credentials have different mechanism");
Petr Šabata 81d24c
+			return GSS_S_COMPLETE;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
Petr Šabata 81d24c
+		    ctx->client_creds, ctx->oid, &new_name,
Petr Šabata 81d24c
+		    NULL, NULL, NULL))) {
Petr Šabata 81d24c
+			ssh_gssapi_error(ctx);
Petr Šabata 81d24c
+			return (ctx->major);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	gss_buffer_desc ename;
Petr Šabata 81d24c
+		ctx->major = gss_compare_name(&ctx->minor, client->name,
Petr Šabata 81d24c
+		    new_name, &equal);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (GSS_ERROR(ctx->major)) {
Petr Šabata 81d24c
+			ssh_gssapi_error(ctx);
Petr Šabata 81d24c
+			return (ctx->major);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (!equal) {
Petr Šabata 81d24c
+			debug("Rekeyed credentials have different name");
Petr Šabata 81d24c
+			return GSS_S_COMPLETE;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		debug("Marking rekeyed credentials for export");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		gss_release_name(&ctx->minor, &client->name);
Petr Šabata 81d24c
+		gss_release_cred(&ctx->minor, &client->creds);
Petr Šabata 81d24c
+		client->name = new_name;
Petr Šabata 81d24c
+		client->creds = ctx->client_creds;
Petr Šabata 81d24c
+		ctx->client_creds = GSS_C_NO_CREDENTIAL;
Petr Šabata 81d24c
+		client->updated = 1;
Petr Šabata 81d24c
+		return GSS_S_COMPLETE;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	client->mech = NULL;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
Petr Šabata 81d24c
 	if (client->mech == NULL)
Petr Šabata 81d24c
 		return GSS_S_FAILURE;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	if (ctx->client_creds &&
Petr Šabata 81d24c
+	    (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
Petr Šabata 81d24c
+	     ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
Petr Šabata 81d24c
+		ssh_gssapi_error(ctx);
Petr Šabata 81d24c
+		return (ctx->major);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
Petr Šabata 81d24c
 	    &client->displayname, NULL))) {
Petr Šabata 81d24c
 		ssh_gssapi_error(ctx);
Petr Šabata 81d24c
@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
Petr Šabata 81d24c
 		return (ctx->major);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	gss_release_buffer(&ctx->minor, &ename);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	/* We can't copy this structure, so we just move the pointer to it */
Petr Šabata 81d24c
 	client->creds = ctx->client_creds;
Petr Šabata 81d24c
 	ctx->client_creds = GSS_C_NO_CREDENTIAL;
Petr Šabata 81d24c
@@ -319,11 +395,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
Petr Šabata 81d24c
 void
Petr Šabata 81d24c
 ssh_gssapi_cleanup_creds(void)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	if (gssapi_client.store.filename != NULL) {
Petr Šabata 81d24c
-		/* Unlink probably isn't sufficient */
Petr Šabata 81d24c
-		debug("removing gssapi cred file\"%s\"",
Petr Šabata 81d24c
-		    gssapi_client.store.filename);
Petr Šabata 81d24c
-		unlink(gssapi_client.store.filename);
Petr Šabata 81d24c
+	krb5_ccache ccache = NULL;
Petr Šabata 81d24c
+	krb5_error_code problem;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gssapi_client.store.data != NULL) {
Petr Šabata 81d24c
+		if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) {
DistroBaker d029bb
+			debug_f("krb5_cc_resolve(): %.100s",
Petr Šabata 81d24c
+				krb5_get_err_text(gssapi_client.store.data, problem));
Petr Šabata 81d24c
+		} else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) {
DistroBaker d029bb
+			debug_f("krb5_cc_destroy(): %.100s",
Petr Šabata 81d24c
+				krb5_get_err_text(gssapi_client.store.data, problem));
Petr Šabata 81d24c
+		} else {
Petr Šabata 81d24c
+			krb5_free_context(gssapi_client.store.data);
Petr Šabata 81d24c
+			gssapi_client.store.data = NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -356,19 +441,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* Privileged */
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
-ssh_gssapi_userok(char *user)
Petr Šabata 81d24c
+ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	OM_uint32 lmin;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	(void) kex; /* used in privilege separation */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (gssapi_client.exportedname.length == 0 ||
Petr Šabata 81d24c
 	    gssapi_client.exportedname.value == NULL) {
Petr Šabata 81d24c
 		debug("No suitable client data");
Petr Šabata 81d24c
 		return 0;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if (gssapi_client.mech && gssapi_client.mech->userok)
Petr Šabata 81d24c
-		if ((*gssapi_client.mech->userok)(&gssapi_client, user))
Petr Šabata 81d24c
+		if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
Petr Šabata 81d24c
+			gssapi_client.used = 1;
Petr Šabata 81d24c
+			gssapi_client.store.owner = pw;
Petr Šabata 81d24c
 			return 1;
Petr Šabata 81d24c
-		else {
Petr Šabata 81d24c
+		} else {
Petr Šabata 81d24c
 			/* Destroy delegated credentials if userok fails */
Petr Šabata 81d24c
 			gss_release_buffer(&lmin, &gssapi_client.displayname);
Petr Šabata 81d24c
 			gss_release_buffer(&lmin, &gssapi_client.exportedname);
Petr Šabata 81d24c
@@ -382,14 +471,90 @@ ssh_gssapi_userok(char *user)
Petr Šabata 81d24c
 	return (0);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-/* Privileged */
Petr Šabata 81d24c
-OM_uint32
Petr Šabata 81d24c
-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
Petr Šabata 81d24c
+/* These bits are only used for rekeying. The unpriviledged child is running
Petr Šabata 81d24c
+ * as the user, the monitor is root.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * In the child, we want to :
Petr Šabata 81d24c
+ *    *) Ask the monitor to store our credentials into the store we specify
Petr Šabata 81d24c
+ *    *) If it succeeds, maybe do a PAM update
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* Stuff for PAM */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#ifdef USE_PAM
Petr Šabata 81d24c
+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
Petr Šabata 81d24c
+    struct pam_response **resp, void *data)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
Petr Šabata 81d24c
-	    gssbuf, gssmic, NULL);
Petr Šabata 81d24c
+	return (PAM_CONV_ERR);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	return (ctx->major);
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+ssh_gssapi_rekey_creds(void) {
Petr Šabata 81d24c
+	int ok;
Petr Šabata 81d24c
+#ifdef USE_PAM
Petr Šabata 81d24c
+	int ret;
Petr Šabata 81d24c
+	pam_handle_t *pamh = NULL;
Petr Šabata 81d24c
+	struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
Petr Šabata 81d24c
+	char *envstr;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gssapi_client.store.filename == NULL &&
Petr Šabata 81d24c
+	    gssapi_client.store.envval == NULL &&
Petr Šabata 81d24c
+	    gssapi_client.store.envvar == NULL)
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!ok)
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug("Rekeyed credentials stored successfully");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Actually managing to play with the ssh pam stack from here will
Petr Šabata 81d24c
+	 * be next to impossible. In any case, we may want different options
Petr Šabata 81d24c
+	 * for rekeying. So, use our own :)
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+#ifdef USE_PAM	
Petr Šabata 81d24c
+	if (!use_privsep) {
Petr Šabata 81d24c
+		debug("Not even going to try and do PAM with privsep disabled");
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
Petr Šabata 81d24c
+ 	    &pamconv, &pamh);
Petr Šabata 81d24c
+	if (ret)
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
Petr Šabata 81d24c
+	    gssapi_client.store.envval);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ret = pam_putenv(pamh, envstr);
Petr Šabata 81d24c
+	if (!ret)
Petr Šabata 81d24c
+		pam_setcred(pamh, PAM_REINITIALIZE_CRED);
Petr Šabata 81d24c
+	pam_end(pamh, PAM_SUCCESS);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
Petr Šabata 81d24c
+	int ok = 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Check we've got credentials to store */
Petr Šabata 81d24c
+	if (!gssapi_client.updated)
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gssapi_client.updated = 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	temporarily_use_uid(gssapi_client.store.owner);
Petr Šabata 81d24c
+	if (gssapi_client.mech && gssapi_client.mech->updatecreds)
Petr Šabata 81d24c
+		ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		debug("No update function for this mechanism");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	restore_uid();
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return ok;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* Privileged */
Petr Šabata 81d24c
diff --git a/kex.c b/kex.c
Petr Šabata 81d24c
index ce85f043..574c7609 100644
Petr Šabata 81d24c
--- a/kex.c
Petr Šabata 81d24c
+++ b/kex.c
Petr Šabata 81d24c
@@ -57,11 +57,16 @@
Petr Šabata 81d24c
 #include "misc.h"
Petr Šabata 81d24c
 #include "dispatch.h"
Petr Šabata 81d24c
 #include "monitor.h"
Petr Šabata 81d24c
+#include "xmalloc.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #include "ssherr.h"
Petr Šabata 81d24c
 #include "sshbuf.h"
Petr Šabata 81d24c
 #include "digest.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* prototype */
Petr Šabata 81d24c
 static int kex_choose_conf(struct ssh *);
Petr Šabata 81d24c
 static int kex_input_newkeys(int, u_int32_t, struct ssh *);
Petr Šabata 81d24c
@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = {
Petr Šabata 81d24c
 #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Petr Šabata 81d24c
 	{ NULL, 0, -1, -1},
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
+static const struct kexalg gss_kexalgs[] = {
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	{ KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Petr Šabata 81d24c
+	{ KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
Petr Šabata 81d24c
+	{ KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
Petr Šabata 81d24c
+	{ KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
Petr Šabata 81d24c
+	{ KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
Petr Šabata 81d24c
+	{ KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
Petr Šabata 81d24c
+	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
Petr Šabata 81d24c
+	{ KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+	{ NULL, 0, -1, -1},
Petr Šabata 81d24c
+};
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-char *
Petr Šabata 81d24c
-kex_alg_list(char sep)
Petr Šabata 81d24c
+static char *
Petr Šabata 81d24c
+kex_alg_list_internal(char sep, const struct kexalg *algs)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	char *ret = NULL, *tmp;
Petr Šabata 81d24c
 	size_t nlen, rlen = 0;
Petr Šabata 81d24c
 	const struct kexalg *k;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	for (k = kexalgs; k->name != NULL; k++) {
Petr Šabata 81d24c
+	for (k = algs; k->name != NULL; k++) {
Petr Šabata 81d24c
 		if (ret != NULL)
Petr Šabata 81d24c
 			ret[rlen++] = sep;
Petr Šabata 81d24c
 		nlen = strlen(k->name);
Petr Šabata 81d24c
@@ -138,6 +156,18 @@ kex_alg_list(char sep)
Petr Šabata 81d24c
 	return ret;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+kex_alg_list(char sep)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	return kex_alg_list_internal(sep, kexalgs);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+kex_gss_alg_list(char sep)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	return kex_alg_list_internal(sep, gss_kexalgs);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 static const struct kexalg *
Petr Šabata 81d24c
 kex_alg_by_name(const char *name)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name)
Petr Šabata 81d24c
 		if (strcmp(k->name, name) == 0)
Petr Šabata 81d24c
 			return k;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
+	for (k = gss_kexalgs; k->name != NULL; k++) {
Petr Šabata 81d24c
+		if (strncmp(k->name, name, strlen(k->name)) == 0)
Petr Šabata 81d24c
+			return k;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 	return NULL;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
Petr Šabata 81d24c
 	return r;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/* Validate GSS KEX method name list */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+kex_gss_names_valid(const char *names)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *s, *cp, *p;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (names == NULL || *names == '\0')
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	s = cp = xstrdup(names);
Petr Šabata 81d24c
+	for ((p = strsep(&cp, ",")); p && *p != '\0';
Petr Šabata 81d24c
+	    (p = strsep(&cp, ","))) {
Petr Šabata 81d24c
+		if (strncmp(p, "gss-", 4) != 0
Petr Šabata 81d24c
+		  || kex_alg_by_name(p) == NULL) {
Petr Šabata 81d24c
+			error("Unsupported KEX algorithm \"%.100s\"", p);
Petr Šabata 81d24c
+			free(s);
Petr Šabata 81d24c
+			return 0;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	debug3("gss kex names ok: [%s]", names);
Petr Šabata 81d24c
+	free(s);
Petr Šabata 81d24c
+	return 1;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* put algorithm proposal into buffer */
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
 kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Petr Šabata 81d24c
@@ -698,6 +755,9 @@ kex_free(struct kex *kex)
Petr Šabata 81d24c
 	sshbuf_free(kex->server_version);
Petr Šabata 81d24c
 	sshbuf_free(kex->client_pub);
DistroBaker d029bb
 	sshbuf_free(kex->session_id);
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	free(kex->gss_host);
Petr Šabata 81d24c
+#endif /* GSSAPI */
Petr Šabata 81d24c
 	free(kex->failed_choice);
Petr Šabata 81d24c
 	free(kex->hostkey_alg);
Petr Šabata 81d24c
 	free(kex->name);
Petr Šabata 81d24c
diff --git a/kex.h b/kex.h
Petr Šabata 81d24c
index a5ae6ac0..fe714141 100644
Petr Šabata 81d24c
--- a/kex.h
Petr Šabata 81d24c
+++ b/kex.h
Petr Šabata 81d24c
@@ -102,6 +102,15 @@ enum kex_exchange {
Petr Šabata 81d24c
 	KEX_ECDH_SHA2,
Petr Šabata 81d24c
 	KEX_C25519_SHA256,
DistroBaker d029bb
 	KEX_KEM_SNTRUP761X25519_SHA512,
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	KEX_GSS_GRP1_SHA1,
Petr Šabata 81d24c
+	KEX_GSS_GRP14_SHA1,
Petr Šabata 81d24c
+	KEX_GSS_GRP14_SHA256,
Petr Šabata 81d24c
+	KEX_GSS_GRP16_SHA512,
Petr Šabata 81d24c
+	KEX_GSS_GEX_SHA1,
Petr Šabata 81d24c
+	KEX_GSS_NISTP256_SHA256,
Petr Šabata 81d24c
+	KEX_GSS_C25519_SHA256,
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	KEX_MAX
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -153,6 +162,12 @@ struct kex {
Petr Šabata 81d24c
 	u_int	flags;
Petr Šabata 81d24c
 	int	hash_alg;
Petr Šabata 81d24c
 	int	ec_nid;
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	int	gss_deleg_creds;
Petr Šabata 81d24c
+	int	gss_trust_dns;
Petr Šabata 81d24c
+	char    *gss_host;
Petr Šabata 81d24c
+	char	*gss_client;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	char	*failed_choice;
Petr Šabata 81d24c
 	int	(*verify_host_key)(struct sshkey *, struct ssh *);
Petr Šabata 81d24c
 	struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
Petr Šabata 81d24c
@@ -174,8 +189,10 @@ struct kex {
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int	 kex_names_valid(const char *);
Petr Šabata 81d24c
 char	*kex_alg_list(char);
Petr Šabata 81d24c
+char	*kex_gss_alg_list(char);
Petr Šabata 81d24c
 char	*kex_names_cat(const char *, const char *);
Petr Šabata 81d24c
 int	 kex_assemble_names(char **, const char *, const char *);
Petr Šabata 81d24c
+int	 kex_gss_names_valid(const char *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int	 kex_exchange_identification(struct ssh *, int, const char *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -202,6 +219,12 @@ int	 kexgex_client(struct ssh *);
Petr Šabata 81d24c
 int	 kexgex_server(struct ssh *);
Petr Šabata 81d24c
 int	 kex_gen_client(struct ssh *);
Petr Šabata 81d24c
 int	 kex_gen_server(struct ssh *);
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+int	 kexgssgex_client(struct ssh *);
Petr Šabata 81d24c
+int	 kexgssgex_server(struct ssh *);
Petr Šabata 81d24c
+int	 kexgss_client(struct ssh *);
Petr Šabata 81d24c
+int	 kexgss_server(struct ssh *);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int	 kex_dh_keypair(struct kex *);
Petr Šabata 81d24c
 int	 kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
Petr Šabata 81d24c
@@ -234,6 +257,12 @@ int	 kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
Petr Šabata 81d24c
     const BIGNUM *, const u_char *, size_t,
Petr Šabata 81d24c
     u_char *, size_t *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+int	 kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
Petr Šabata 81d24c
+    const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
Petr Šabata 81d24c
+    const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
Petr Šabata 81d24c
+    const struct sshbuf *client_pub, const struct sshbuf *server_pub,
Petr Šabata 81d24c
+    const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 void	kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
Petr Šabata 81d24c
 	__attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
Petr Šabata 81d24c
 	__attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
Petr Šabata 81d24c
diff --git a/kexdh.c b/kexdh.c
Petr Šabata 81d24c
index 67133e33..edaa4676 100644
Petr Šabata 81d24c
--- a/kexdh.c
Petr Šabata 81d24c
+++ b/kexdh.c
Petr Šabata 81d24c
@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	switch (kex->kex_type) {
Petr Šabata 81d24c
 	case KEX_DH_GRP1_SHA1:
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	case KEX_GSS_GRP1_SHA1:
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 		kex->dh = dh_new_group1();
Petr Šabata 81d24c
 		break;
Petr Šabata 81d24c
 	case KEX_DH_GRP14_SHA1:
Petr Šabata 81d24c
 	case KEX_DH_GRP14_SHA256:
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	case KEX_GSS_GRP14_SHA1:
Petr Šabata 81d24c
+	case KEX_GSS_GRP14_SHA256:
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 		kex->dh = dh_new_group14();
Petr Šabata 81d24c
 		break;
Petr Šabata 81d24c
 	case KEX_DH_GRP16_SHA512:
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	case KEX_GSS_GRP16_SHA512:
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 		kex->dh = dh_new_group16();
Petr Šabata 81d24c
 		break;
Petr Šabata 81d24c
 	case KEX_DH_GRP18_SHA512:
Petr Šabata 81d24c
diff --git a/kexgen.c b/kexgen.c
Petr Šabata 81d24c
index 69348b96..c0e8c2f4 100644
Petr Šabata 81d24c
--- a/kexgen.c
Petr Šabata 81d24c
+++ b/kexgen.c
Petr Šabata 81d24c
@@ -44,7 +44,7 @@
Petr Šabata 81d24c
 static int input_kex_gen_init(int, u_int32_t, struct ssh *);
Petr Šabata 81d24c
 static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-static int
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
 kex_gen_hash(
Petr Šabata 81d24c
     int hash_alg,
Petr Šabata 81d24c
     const struct sshbuf *client_version,
Petr Šabata 81d24c
diff --git a/kexgssc.c b/kexgssc.c
Petr Šabata 81d24c
new file mode 100644
Petr Šabata 81d24c
index 00000000..f6e1405e
Petr Šabata 81d24c
--- /dev/null
Petr Šabata 81d24c
+++ b/kexgssc.c
Dmitry Belyavskiy d075fa
@@ -0,0 +1,611 @@
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
+ * modification, are permitted provided that the following conditions
Petr Šabata 81d24c
+ * are met:
Petr Šabata 81d24c
+ * 1. Redistributions of source code must retain the above copyright
Petr Šabata 81d24c
+ *    notice, this list of conditions and the following disclaimer.
Petr Šabata 81d24c
+ * 2. Redistributions in binary form must reproduce the above copyright
Petr Šabata 81d24c
+ *    notice, this list of conditions and the following disclaimer in the
Petr Šabata 81d24c
+ *    documentation and/or other materials provided with the distribution.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
Petr Šabata 81d24c
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Petr Šabata 81d24c
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Petr Šabata 81d24c
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
Petr Šabata 81d24c
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Petr Šabata 81d24c
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Petr Šabata 81d24c
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Petr Šabata 81d24c
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Petr Šabata 81d24c
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Petr Šabata 81d24c
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "includes.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "includes.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <openssl/crypto.h>
Petr Šabata 81d24c
+#include <openssl/bn.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <string.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "xmalloc.h"
Petr Šabata 81d24c
+#include "sshbuf.h"
Petr Šabata 81d24c
+#include "ssh2.h"
Petr Šabata 81d24c
+#include "sshkey.h"
Petr Šabata 81d24c
+#include "cipher.h"
Petr Šabata 81d24c
+#include "kex.h"
Petr Šabata 81d24c
+#include "log.h"
Petr Šabata 81d24c
+#include "packet.h"
Petr Šabata 81d24c
+#include "dh.h"
Petr Šabata 81d24c
+#include "digest.h"
Petr Šabata 81d24c
+#include "ssherr.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+kexgss_client(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct kex *kex = ssh->kex;
Petr Šabata 81d24c
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
Petr Šabata 81d24c
+	    recv_tok = GSS_C_EMPTY_BUFFER,
Petr Šabata 81d24c
+	    gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
Petr Šabata 81d24c
+	Gssctxt *ctxt;
Petr Šabata 81d24c
+	OM_uint32 maj_status, min_status, ret_flags;
Petr Šabata 81d24c
+	struct sshbuf *server_blob = NULL;
Petr Šabata 81d24c
+	struct sshbuf *shared_secret = NULL;
Petr Šabata 81d24c
+	struct sshbuf *server_host_key_blob = NULL;
Petr Šabata 81d24c
+	struct sshbuf *empty = NULL;
Petr Šabata 81d24c
+	u_char *msg;
Petr Šabata 81d24c
+	int type = 0;
Petr Šabata 81d24c
+	int first = 1;
Petr Šabata 81d24c
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Petr Šabata 81d24c
+	size_t hashlen;
Petr Šabata 81d24c
+	u_char c;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Initialise our GSSAPI world */
Petr Šabata 81d24c
+	ssh_gssapi_build_ctx(&ctxt);
Petr Šabata 81d24c
+	if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
Petr Šabata 81d24c
+	    == GSS_C_NO_OID)
Petr Šabata 81d24c
+		fatal("Couldn't identify host exchange");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (ssh_gssapi_import_name(ctxt, kex->gss_host))
Petr Šabata 81d24c
+		fatal("Couldn't import hostname");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (kex->gss_client &&
Petr Šabata 81d24c
+	    ssh_gssapi_client_identity(ctxt, kex->gss_client))
Petr Šabata 81d24c
+		fatal("Couldn't acquire client credentials");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Step 1 */
Petr Šabata 81d24c
+	switch (kex->kex_type) {
Petr Šabata 81d24c
+	case KEX_GSS_GRP1_SHA1:
Petr Šabata 81d24c
+	case KEX_GSS_GRP14_SHA1:
Petr Šabata 81d24c
+	case KEX_GSS_GRP14_SHA256:
Petr Šabata 81d24c
+	case KEX_GSS_GRP16_SHA512:
Petr Šabata 81d24c
+		r = kex_dh_keypair(kex);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	case KEX_GSS_NISTP256_SHA256:
Petr Šabata 81d24c
+		r = kex_ecdh_keypair(kex);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	case KEX_GSS_C25519_SHA256:
Petr Šabata 81d24c
+		r = kex_c25519_keypair(kex);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	default:
DistroBaker d029bb
+		fatal_f("Unexpected KEX type %d", kex->kex_type);
Petr Šabata 81d24c
+	}
Dmitry Belyavskiy d075fa
+	if (r != 0) {
Dmitry Belyavskiy d075fa
+		ssh_gssapi_delete_ctx(&ctxt);
Petr Šabata 81d24c
+		return r;
Dmitry Belyavskiy d075fa
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	token_ptr = GSS_C_NO_BUFFER;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	do {
Petr Šabata 81d24c
+		debug("Calling gss_init_sec_context");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		maj_status = ssh_gssapi_init_ctx(ctxt,
Petr Šabata 81d24c
+		    kex->gss_deleg_creds, token_ptr, &send_tok,
Petr Šabata 81d24c
+		    &ret_flags);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (GSS_ERROR(maj_status)) {
Petr Šabata 81d24c
+			/* XXX Useles code: Missing send? */
Petr Šabata 81d24c
+			if (send_tok.length != 0) {
Petr Šabata 81d24c
+				if ((r = sshpkt_start(ssh,
Petr Šabata 81d24c
+				        SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Petr Šabata 81d24c
+				        send_tok.length)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			fatal("gss_init_context failed");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/* If we've got an old receive buffer get rid of it */
Petr Šabata 81d24c
+		if (token_ptr != GSS_C_NO_BUFFER)
Petr Šabata 81d24c
+			gss_release_buffer(&min_status, &recv_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (maj_status == GSS_S_COMPLETE) {
Petr Šabata 81d24c
+			/* If mutual state flag is not true, kex fails */
Petr Šabata 81d24c
+			if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Petr Šabata 81d24c
+				fatal("Mutual authentication failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* If integ avail flag is not true kex fails */
Petr Šabata 81d24c
+			if (!(ret_flags & GSS_C_INTEG_FLAG))
Petr Šabata 81d24c
+				fatal("Integrity check failed");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/*
Petr Šabata 81d24c
+		 * If we have data to send, then the last message that we
Petr Šabata 81d24c
+		 * received cannot have been a 'complete'.
Petr Šabata 81d24c
+		 */
Petr Šabata 81d24c
+		if (send_tok.length != 0) {
Petr Šabata 81d24c
+			if (first) {
Petr Šabata 81d24c
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Petr Šabata 81d24c
+				        send_tok.length)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
Petr Šabata 81d24c
+					fatal("failed to construct packet: %s", ssh_err(r));
Petr Šabata 81d24c
+				first = 0;
Petr Šabata 81d24c
+			} else {
Petr Šabata 81d24c
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Petr Šabata 81d24c
+				        send_tok.length)) != 0)
Petr Šabata 81d24c
+					fatal("failed to construct packet: %s", ssh_err(r));
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			if ((r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("failed to send packet: %s", ssh_err(r));
Petr Šabata 81d24c
+			gss_release_buffer(&min_status, &send_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* If we've sent them data, they should reply */
Petr Šabata 81d24c
+			do {
Petr Šabata 81d24c
+				type = ssh_packet_read(ssh);
Petr Šabata 81d24c
+				if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
Dmitry Belyavskiy d075fa
+					char *tmp = NULL;
Dmitry Belyavskiy d075fa
+					size_t tmp_len = 0;
Dmitry Belyavskiy d075fa
+
Petr Šabata 81d24c
+					debug("Received KEXGSS_HOSTKEY");
Petr Šabata 81d24c
+					if (server_host_key_blob)
Petr Šabata 81d24c
+						fatal("Server host key received more than once");
Dmitry Belyavskiy d075fa
+					if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Petr Šabata 81d24c
+						fatal("Failed to read server host key: %s", ssh_err(r));
Dmitry Belyavskiy d075fa
+					if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
Dmitry Belyavskiy d075fa
+						fatal("sshbuf_from failed");
Petr Šabata 81d24c
+				}
Petr Šabata 81d24c
+			} while (type == SSH2_MSG_KEXGSS_HOSTKEY);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			switch (type) {
Petr Šabata 81d24c
+			case SSH2_MSG_KEXGSS_CONTINUE:
Petr Šabata 81d24c
+				debug("Received GSSAPI_CONTINUE");
Petr Šabata 81d24c
+				if (maj_status == GSS_S_COMPLETE)
Petr Šabata 81d24c
+					fatal("GSSAPI Continue received from server when complete");
Petr Šabata 81d24c
+				if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+				        &recv_tok)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+					fatal("Failed to read token: %s", ssh_err(r));
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			case SSH2_MSG_KEXGSS_COMPLETE:
Petr Šabata 81d24c
+				debug("Received GSSAPI_COMPLETE");
Petr Šabata 81d24c
+				if (msg_tok.value != NULL)
Petr Šabata 81d24c
+				        fatal("Received GSSAPI_COMPLETE twice?");
Petr Šabata 81d24c
+				if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
Petr Šabata 81d24c
+				    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+				        &msg_tok)) != 0)
Petr Šabata 81d24c
+					fatal("Failed to read message: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+				/* Is there a token included? */
Petr Šabata 81d24c
+				if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+				if (c) {
Petr Šabata 81d24c
+					if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
Petr Šabata 81d24c
+					    ssh, &recv_tok)) != 0)
Petr Šabata 81d24c
+						fatal("Failed to read token: %s", ssh_err(r));
Petr Šabata 81d24c
+					/* If we're already complete - protocol error */
Petr Šabata 81d24c
+					if (maj_status == GSS_S_COMPLETE)
Petr Šabata 81d24c
+						sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Petr Šabata 81d24c
+				} else {
Petr Šabata 81d24c
+					/* No token included */
Petr Šabata 81d24c
+					if (maj_status != GSS_S_COMPLETE)
Petr Šabata 81d24c
+						sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Petr Šabata 81d24c
+				}
Petr Šabata 81d24c
+				if ((r = sshpkt_get_end(ssh)) != 0) {
Petr Šabata 81d24c
+					fatal("Expecting end of packet.");
Petr Šabata 81d24c
+				}
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			case SSH2_MSG_KEXGSS_ERROR:
Petr Šabata 81d24c
+				debug("Received Error");
Petr Šabata 81d24c
+				if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Petr Šabata 81d24c
+				    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt_get failed: %s", ssh_err(r));
Petr Šabata 81d24c
+				fatal("GSSAPI Error: \n%.400s", msg);
Petr Šabata 81d24c
+			default:
Petr Šabata 81d24c
+				sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
Petr Šabata 81d24c
+				    type);
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			token_ptr = &recv_tok;
Petr Šabata 81d24c
+		} else {
Petr Šabata 81d24c
+			/* No data, and not complete */
Petr Šabata 81d24c
+			if (maj_status != GSS_S_COMPLETE)
Petr Šabata 81d24c
+				fatal("Not complete, and no token output");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * We _must_ have received a COMPLETE message in reply from the
Petr Šabata 81d24c
+	 * server, which will have set server_blob and msg_tok
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (type != SSH2_MSG_KEXGSS_COMPLETE)
Petr Šabata 81d24c
+		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* compute shared secret */
Petr Šabata 81d24c
+	switch (kex->kex_type) {
Petr Šabata 81d24c
+	case KEX_GSS_GRP1_SHA1:
Petr Šabata 81d24c
+	case KEX_GSS_GRP14_SHA1:
Petr Šabata 81d24c
+	case KEX_GSS_GRP14_SHA256:
Petr Šabata 81d24c
+	case KEX_GSS_GRP16_SHA512:
Petr Šabata 81d24c
+		r = kex_dh_dec(kex, server_blob, &shared_secret);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	case KEX_GSS_C25519_SHA256:
Petr Šabata 81d24c
+		if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
Petr Šabata 81d24c
+			fatal("The received key has MSB of last octet set!");
Petr Šabata 81d24c
+		r = kex_c25519_dec(kex, server_blob, &shared_secret);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	case KEX_GSS_NISTP256_SHA256:
Petr Šabata 81d24c
+		if (sshbuf_len(server_blob) != 65)
Petr Šabata 81d24c
+			fatal("The received NIST-P256 key did not match"
Petr Šabata 81d24c
+			    "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
Petr Šabata 81d24c
+			fatal("The received NIST-P256 key does not have first octet 0x04");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		r = kex_ecdh_dec(kex, server_blob, &shared_secret);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	default:
Petr Šabata 81d24c
+		r = SSH_ERR_INVALID_ARGUMENT;
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (r != 0)
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((empty = sshbuf_new()) == NULL) {
Petr Šabata 81d24c
+		r = SSH_ERR_ALLOC_FAIL;
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	hashlen = sizeof(hash);
Petr Šabata 81d24c
+	if ((r = kex_gen_hash(
Petr Šabata 81d24c
+	    kex->hash_alg,
Petr Šabata 81d24c
+	    kex->client_version,
Petr Šabata 81d24c
+	    kex->server_version,
Petr Šabata 81d24c
+	    kex->my,
Petr Šabata 81d24c
+	    kex->peer,
Petr Šabata 81d24c
+	    (server_host_key_blob ? server_host_key_blob : empty),
Petr Šabata 81d24c
+	    kex->client_pub,
Petr Šabata 81d24c
+	    server_blob,
Petr Šabata 81d24c
+	    shared_secret,
Petr Šabata 81d24c
+	    hash, &hashlen)) != 0)
DistroBaker d029bb
+		fatal_f("Unexpected KEX type %d", kex->kex_type);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gssbuf.value = hash;
Petr Šabata 81d24c
+	gssbuf.length = hashlen;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Verify that the hash matches the MIC we just got. */
Petr Šabata 81d24c
+	if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
Petr Šabata 81d24c
+		sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_release_buffer(&min_status, &msg_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (kex->gss_deleg_creds)
Petr Šabata 81d24c
+		ssh_gssapi_credentials_updated(ctxt);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_kex_context == NULL)
Petr Šabata 81d24c
+		gss_kex_context = ctxt;
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		ssh_gssapi_delete_ctx(&ctxt);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Petr Šabata 81d24c
+		r = kex_send_newkeys(ssh);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+out:
Petr Šabata 81d24c
+	explicit_bzero(hash, sizeof(hash));
Petr Šabata 81d24c
+	explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
Petr Šabata 81d24c
+	sshbuf_free(empty);
Petr Šabata 81d24c
+	sshbuf_free(server_host_key_blob);
Petr Šabata 81d24c
+	sshbuf_free(server_blob);
Petr Šabata 81d24c
+	sshbuf_free(shared_secret);
Petr Šabata 81d24c
+	sshbuf_free(kex->client_pub);
Petr Šabata 81d24c
+	kex->client_pub = NULL;
Petr Šabata 81d24c
+	return r;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+kexgssgex_client(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct kex *kex = ssh->kex;
Petr Šabata 81d24c
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
Petr Šabata 81d24c
+	    recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
Petr Šabata 81d24c
+            msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
Petr Šabata 81d24c
+	Gssctxt *ctxt;
Petr Šabata 81d24c
+	OM_uint32 maj_status, min_status, ret_flags;
Petr Šabata 81d24c
+	struct sshbuf *shared_secret = NULL;
Petr Šabata 81d24c
+	BIGNUM *p = NULL;
Petr Šabata 81d24c
+	BIGNUM *g = NULL;
Petr Šabata 81d24c
+	struct sshbuf *buf = NULL;
Petr Šabata 81d24c
+	struct sshbuf *server_host_key_blob = NULL;
Petr Šabata 81d24c
+	struct sshbuf *server_blob = NULL;
Petr Šabata 81d24c
+	BIGNUM *dh_server_pub = NULL;
Petr Šabata 81d24c
+	u_char *msg;
Petr Šabata 81d24c
+	int type = 0;
Petr Šabata 81d24c
+	int first = 1;
Petr Šabata 81d24c
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Petr Šabata 81d24c
+	size_t hashlen;
Petr Šabata 81d24c
+	const BIGNUM *pub_key, *dh_p, *dh_g;
Petr Šabata 81d24c
+	int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
Petr Šabata 81d24c
+	struct sshbuf *empty = NULL;
Petr Šabata 81d24c
+	u_char c;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Initialise our GSSAPI world */
Petr Šabata 81d24c
+	ssh_gssapi_build_ctx(&ctxt);
Petr Šabata 81d24c
+	if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
Petr Šabata 81d24c
+	    == GSS_C_NO_OID)
Petr Šabata 81d24c
+		fatal("Couldn't identify host exchange");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (ssh_gssapi_import_name(ctxt, kex->gss_host))
Petr Šabata 81d24c
+		fatal("Couldn't import hostname");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (kex->gss_client &&
Petr Šabata 81d24c
+	    ssh_gssapi_client_identity(ctxt, kex->gss_client))
Petr Šabata 81d24c
+		fatal("Couldn't acquire client credentials");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug("Doing group exchange");
Petr Šabata 81d24c
+	nbits = dh_estimate(kex->dh_need * 8);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	kex->min = DH_GRP_MIN;
Petr Šabata 81d24c
+	kex->max = DH_GRP_MAX;
Petr Šabata 81d24c
+	kex->nbits = nbits;
Petr Šabata 81d24c
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_u32(ssh, min)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_u32(ssh, max)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("Failed to construct a packet: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
Petr Šabata 81d24c
+		fatal("Error: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (BN_num_bits(p) < min || BN_num_bits(p) > max)
Petr Šabata 81d24c
+		fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
Petr Šabata 81d24c
+		    min, BN_num_bits(p), max);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((kex->dh = dh_new_group(g, p)) == NULL)
Petr Šabata 81d24c
+		fatal("dn_new_group() failed");
Petr Šabata 81d24c
+	p = g = NULL; /* belong to kex->dh now */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	DH_get0_key(kex->dh, &pub_key, NULL);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	token_ptr = GSS_C_NO_BUFFER;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	do {
Petr Šabata 81d24c
+		/* Step 2 - call GSS_Init_sec_context() */
Petr Šabata 81d24c
+		debug("Calling gss_init_sec_context");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		maj_status = ssh_gssapi_init_ctx(ctxt,
Petr Šabata 81d24c
+		    kex->gss_deleg_creds, token_ptr, &send_tok,
Petr Šabata 81d24c
+		    &ret_flags);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (GSS_ERROR(maj_status)) {
Petr Šabata 81d24c
+			/* XXX Useles code: Missing send? */
Petr Šabata 81d24c
+			if (send_tok.length != 0) {
Petr Šabata 81d24c
+				if ((r = sshpkt_start(ssh,
Petr Šabata 81d24c
+				        SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Petr Šabata 81d24c
+				        send_tok.length)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			fatal("gss_init_context failed");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/* If we've got an old receive buffer get rid of it */
Petr Šabata 81d24c
+		if (token_ptr != GSS_C_NO_BUFFER)
Petr Šabata 81d24c
+			gss_release_buffer(&min_status, &recv_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (maj_status == GSS_S_COMPLETE) {
Petr Šabata 81d24c
+			/* If mutual state flag is not true, kex fails */
Petr Šabata 81d24c
+			if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Petr Šabata 81d24c
+				fatal("Mutual authentication failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* If integ avail flag is not true kex fails */
Petr Šabata 81d24c
+			if (!(ret_flags & GSS_C_INTEG_FLAG))
Petr Šabata 81d24c
+				fatal("Integrity check failed");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/*
Petr Šabata 81d24c
+		 * If we have data to send, then the last message that we
Petr Šabata 81d24c
+		 * received cannot have been a 'complete'.
Petr Šabata 81d24c
+		 */
Petr Šabata 81d24c
+		if (send_tok.length != 0) {
Petr Šabata 81d24c
+			if (first) {
Petr Šabata 81d24c
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_string(ssh, send_tok.value,
Petr Šabata 81d24c
+				        send_tok.length)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+				first = 0;
Petr Šabata 81d24c
+			} else {
Petr Šabata 81d24c
+				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_put_string(ssh,send_tok.value,
Petr Šabata 81d24c
+				        send_tok.length)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			if ((r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt_send failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			gss_release_buffer(&min_status, &send_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* If we've sent them data, they should reply */
Petr Šabata 81d24c
+			do {
Petr Šabata 81d24c
+				type = ssh_packet_read(ssh);
Petr Šabata 81d24c
+				if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
Dmitry Belyavskiy d075fa
+					char *tmp = NULL;
Dmitry Belyavskiy d075fa
+					size_t tmp_len = 0;
Dmitry Belyavskiy d075fa
+
Petr Šabata 81d24c
+					debug("Received KEXGSS_HOSTKEY");
Petr Šabata 81d24c
+					if (server_host_key_blob)
Petr Šabata 81d24c
+						fatal("Server host key received more than once");
Dmitry Belyavskiy d075fa
+					if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Petr Šabata 81d24c
+						fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy d075fa
+					if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
Dmitry Belyavskiy d075fa
+						fatal("sshbuf_from failed");
Petr Šabata 81d24c
+				}
Petr Šabata 81d24c
+			} while (type == SSH2_MSG_KEXGSS_HOSTKEY);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			switch (type) {
Petr Šabata 81d24c
+			case SSH2_MSG_KEXGSS_CONTINUE:
Petr Šabata 81d24c
+				debug("Received GSSAPI_CONTINUE");
Petr Šabata 81d24c
+				if (maj_status == GSS_S_COMPLETE)
Petr Šabata 81d24c
+					fatal("GSSAPI Continue received from server when complete");
Petr Šabata 81d24c
+				if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+				        &recv_tok)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			case SSH2_MSG_KEXGSS_COMPLETE:
Petr Šabata 81d24c
+				debug("Received GSSAPI_COMPLETE");
Petr Šabata 81d24c
+				if (msg_tok.value != NULL)
Petr Šabata 81d24c
+				        fatal("Received GSSAPI_COMPLETE twice?");
Petr Šabata 81d24c
+				if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
Petr Šabata 81d24c
+				    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+				        &msg_tok)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+				/* Is there a token included? */
Petr Šabata 81d24c
+				if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+				if (c) {
Petr Šabata 81d24c
+					if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
Petr Šabata 81d24c
+					        ssh, &recv_tok)) != 0 ||
Petr Šabata 81d24c
+					    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+						fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+					/* If we're already complete - protocol error */
Petr Šabata 81d24c
+					if (maj_status == GSS_S_COMPLETE)
Petr Šabata 81d24c
+						sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Petr Šabata 81d24c
+				} else {
Petr Šabata 81d24c
+					/* No token included */
Petr Šabata 81d24c
+					if (maj_status != GSS_S_COMPLETE)
Petr Šabata 81d24c
+						sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Petr Šabata 81d24c
+				}
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			case SSH2_MSG_KEXGSS_ERROR:
Petr Šabata 81d24c
+				debug("Received Error");
Petr Šabata 81d24c
+				if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Petr Šabata 81d24c
+				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Petr Šabata 81d24c
+				    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+					fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+				fatal("GSSAPI Error: \n%.400s", msg);
Petr Šabata 81d24c
+			default:
Petr Šabata 81d24c
+				sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
Petr Šabata 81d24c
+				    type);
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			token_ptr = &recv_tok;
Petr Šabata 81d24c
+		} else {
Petr Šabata 81d24c
+			/* No data, and not complete */
Petr Šabata 81d24c
+			if (maj_status != GSS_S_COMPLETE)
Petr Šabata 81d24c
+				fatal("Not complete, and no token output");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * We _must_ have received a COMPLETE message in reply from the
Petr Šabata 81d24c
+	 * server, which will have set dh_server_pub and msg_tok
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (type != SSH2_MSG_KEXGSS_COMPLETE)
Petr Šabata 81d24c
+		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* 7. C verifies that the key Q_S is valid */
Petr Šabata 81d24c
+	/* 8. C computes shared secret */
Petr Šabata 81d24c
+	if ((buf = sshbuf_new()) == NULL ||
Petr Šabata 81d24c
+	    (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	sshbuf_free(buf);
Petr Šabata 81d24c
+	buf = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((shared_secret = sshbuf_new()) == NULL) {
Petr Šabata 81d24c
+		r = SSH_ERR_ALLOC_FAIL;
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	if ((empty = sshbuf_new()) == NULL) {
Petr Šabata 81d24c
+		r = SSH_ERR_ALLOC_FAIL;
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Petr Šabata 81d24c
+	hashlen = sizeof(hash);
Petr Šabata 81d24c
+	if ((r = kexgex_hash(
Petr Šabata 81d24c
+	    kex->hash_alg,
Petr Šabata 81d24c
+	    kex->client_version,
Petr Šabata 81d24c
+	    kex->server_version,
Petr Šabata 81d24c
+	    kex->my,
Petr Šabata 81d24c
+	    kex->peer,
Petr Šabata 81d24c
+	    (server_host_key_blob ? server_host_key_blob : empty),
Petr Šabata 81d24c
+ 	    kex->min, kex->nbits, kex->max,
Petr Šabata 81d24c
+	    dh_p, dh_g,
Petr Šabata 81d24c
+	    pub_key,
Petr Šabata 81d24c
+	    dh_server_pub,
Petr Šabata 81d24c
+	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Petr Šabata 81d24c
+	    hash, &hashlen)) != 0)
Petr Šabata 81d24c
+		fatal("Failed to calculate hash: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gssbuf.value = hash;
Petr Šabata 81d24c
+	gssbuf.length = hashlen;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Verify that the hash matches the MIC we just got. */
Petr Šabata 81d24c
+	if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
Petr Šabata 81d24c
+		sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_release_buffer(&min_status, &msg_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (kex->gss_deleg_creds)
Petr Šabata 81d24c
+		ssh_gssapi_credentials_updated(ctxt);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_kex_context == NULL)
Petr Šabata 81d24c
+		gss_kex_context = ctxt;
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		ssh_gssapi_delete_ctx(&ctxt);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Finally derive the keys and send them */
Petr Šabata 81d24c
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Petr Šabata 81d24c
+		r = kex_send_newkeys(ssh);
Petr Šabata 81d24c
+out:
Petr Šabata 81d24c
+	sshbuf_free(buf);
Petr Šabata 81d24c
+	sshbuf_free(server_blob);
Petr Šabata 81d24c
+	sshbuf_free(empty);
Petr Šabata 81d24c
+	explicit_bzero(hash, sizeof(hash));
Petr Šabata 81d24c
+	DH_free(kex->dh);
Petr Šabata 81d24c
+	kex->dh = NULL;
Petr Šabata 81d24c
+	BN_clear_free(dh_server_pub);
Petr Šabata 81d24c
+	sshbuf_free(shared_secret);
Petr Šabata 81d24c
+	sshbuf_free(server_host_key_blob);
Petr Šabata 81d24c
+	return r;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
Petr Šabata 81d24c
diff --git a/kexgsss.c b/kexgsss.c
Petr Šabata 81d24c
new file mode 100644
Petr Šabata 81d24c
index 00000000..60bc02de
Petr Šabata 81d24c
--- /dev/null
Petr Šabata 81d24c
+++ b/kexgsss.c
Dmitry Belyavskiy d075fa
@@ -0,0 +1,482 @@
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
+ * modification, are permitted provided that the following conditions
Petr Šabata 81d24c
+ * are met:
Petr Šabata 81d24c
+ * 1. Redistributions of source code must retain the above copyright
Petr Šabata 81d24c
+ *    notice, this list of conditions and the following disclaimer.
Petr Šabata 81d24c
+ * 2. Redistributions in binary form must reproduce the above copyright
Petr Šabata 81d24c
+ *    notice, this list of conditions and the following disclaimer in the
Petr Šabata 81d24c
+ *    documentation and/or other materials provided with the distribution.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
Petr Šabata 81d24c
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Petr Šabata 81d24c
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Petr Šabata 81d24c
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
Petr Šabata 81d24c
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Petr Šabata 81d24c
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Petr Šabata 81d24c
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Petr Šabata 81d24c
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Petr Šabata 81d24c
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Petr Šabata 81d24c
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "includes.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <string.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <openssl/crypto.h>
Petr Šabata 81d24c
+#include <openssl/bn.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "xmalloc.h"
Petr Šabata 81d24c
+#include "sshbuf.h"
Petr Šabata 81d24c
+#include "ssh2.h"
Petr Šabata 81d24c
+#include "sshkey.h"
Petr Šabata 81d24c
+#include "cipher.h"
Petr Šabata 81d24c
+#include "kex.h"
Petr Šabata 81d24c
+#include "log.h"
Petr Šabata 81d24c
+#include "packet.h"
Petr Šabata 81d24c
+#include "dh.h"
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
+#include "monitor_wrap.h"
Petr Šabata 81d24c
+#include "misc.h"      /* servconf.h needs misc.h for struct ForwardOptions */
Petr Šabata 81d24c
+#include "servconf.h"
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
+#include "digest.h"
Petr Šabata 81d24c
+#include "ssherr.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+extern ServerOptions options;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+kexgss_server(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct kex *kex = ssh->kex;
Petr Šabata 81d24c
+	OM_uint32 maj_status, min_status;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * Some GSSAPI implementations use the input value of ret_flags (an
Petr Šabata 81d24c
+	 * output variable) as a means of triggering mechanism specific
Petr Šabata 81d24c
+	 * features. Initializing it to zero avoids inadvertently
Petr Šabata 81d24c
+	 * activating this non-standard behaviour.
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	OM_uint32 ret_flags = 0;
Dmitry Belyavskiy d075fa
+	gss_buffer_desc gssbuf = {0, NULL}, recv_tok, msg_tok;
Petr Šabata 81d24c
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Petr Šabata 81d24c
+	Gssctxt *ctxt = NULL;
Petr Šabata 81d24c
+	struct sshbuf *shared_secret = NULL;
Petr Šabata 81d24c
+	struct sshbuf *client_pubkey = NULL;
Petr Šabata 81d24c
+	struct sshbuf *server_pubkey = NULL;
Petr Šabata 81d24c
+	struct sshbuf *empty = sshbuf_new();
Petr Šabata 81d24c
+	int type = 0;
Petr Šabata 81d24c
+	gss_OID oid;
Petr Šabata 81d24c
+	char *mechs;
Petr Šabata 81d24c
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Petr Šabata 81d24c
+	size_t hashlen;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Initialise GSSAPI */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* If we're rekeying, privsep means that some of the private structures
Petr Šabata 81d24c
+	 * in the GSSAPI code are no longer available. This kludges them back
Petr Šabata 81d24c
+	 * into life
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+	if (!ssh_gssapi_oid_table_ok()) {
Petr Šabata 81d24c
+		mechs = ssh_gssapi_server_mechanisms();
Petr Šabata 81d24c
+		free(mechs);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
DistroBaker d029bb
+	debug2_f("Identifying %s", kex->name);
Petr Šabata 81d24c
+	oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
Petr Šabata 81d24c
+	if (oid == GSS_C_NO_OID)
Petr Šabata 81d24c
+	   fatal("Unknown gssapi mechanism");
Petr Šabata 81d24c
+
DistroBaker d029bb
+	debug2_f("Acquiring credentials");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
Petr Šabata 81d24c
+		fatal("Unable to acquire credentials for the server");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	do {
Petr Šabata 81d24c
+		debug("Wait SSH2_MSG_KEXGSS_INIT");
Petr Šabata 81d24c
+		type = ssh_packet_read(ssh);
Petr Šabata 81d24c
+		switch(type) {
Petr Šabata 81d24c
+		case SSH2_MSG_KEXGSS_INIT:
Dmitry Belyavskiy d075fa
+			if (gssbuf.value != NULL)
Petr Šabata 81d24c
+				fatal("Received KEXGSS_INIT after initialising");
Petr Šabata 81d24c
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+			        &recv_tok)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			switch (kex->kex_type) {
Petr Šabata 81d24c
+			case KEX_GSS_GRP1_SHA1:
Petr Šabata 81d24c
+			case KEX_GSS_GRP14_SHA1:
Petr Šabata 81d24c
+			case KEX_GSS_GRP14_SHA256:
Petr Šabata 81d24c
+			case KEX_GSS_GRP16_SHA512:
Petr Šabata 81d24c
+				r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
Petr Šabata 81d24c
+				    &shared_secret);
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			case KEX_GSS_NISTP256_SHA256:
Petr Šabata 81d24c
+				r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
Petr Šabata 81d24c
+				    &shared_secret);
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			case KEX_GSS_C25519_SHA256:
Petr Šabata 81d24c
+				r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
Petr Šabata 81d24c
+				    &shared_secret);
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			default:
DistroBaker d029bb
+				fatal_f("Unexpected KEX type %d", kex->kex_type);
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			if (r != 0)
Petr Šabata 81d24c
+				goto out;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Dmitry Belyavskiy d075fa
+
Dmitry Belyavskiy d075fa
+			/* Calculate the hash early so we can free the
Dmitry Belyavskiy d075fa
+			* client_pubkey, which has reference to the parent
Dmitry Belyavskiy d075fa
+			* buffer state->incoming_packet
Dmitry Belyavskiy d075fa
+			*/
Dmitry Belyavskiy d075fa
+			hashlen = sizeof(hash);
Dmitry Belyavskiy d075fa
+			if ((r = kex_gen_hash(
Dmitry Belyavskiy d075fa
+			    kex->hash_alg,
Dmitry Belyavskiy d075fa
+			    kex->client_version,
Dmitry Belyavskiy d075fa
+			    kex->server_version,
Dmitry Belyavskiy d075fa
+			    kex->peer,
Dmitry Belyavskiy d075fa
+			    kex->my,
Dmitry Belyavskiy d075fa
+			    empty,
Dmitry Belyavskiy d075fa
+			    client_pubkey,
Dmitry Belyavskiy d075fa
+			    server_pubkey,
Dmitry Belyavskiy d075fa
+			    shared_secret,
Dmitry Belyavskiy d075fa
+			    hash, &hashlen)) != 0)
Dmitry Belyavskiy d075fa
+				goto out;
Dmitry Belyavskiy d075fa
+
Dmitry Belyavskiy d075fa
+			gssbuf.value = hash;
Dmitry Belyavskiy d075fa
+			gssbuf.length = hashlen;
Dmitry Belyavskiy d075fa
+
Dmitry Belyavskiy d075fa
+			sshbuf_free(client_pubkey);
Dmitry Belyavskiy d075fa
+			client_pubkey = NULL;
Dmitry Belyavskiy d075fa
+
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		case SSH2_MSG_KEXGSS_CONTINUE:
Petr Šabata 81d24c
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+			        &recv_tok)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		default:
Petr Šabata 81d24c
+			sshpkt_disconnect(ssh,
Petr Šabata 81d24c
+			    "Protocol error: didn't expect packet type %d",
Petr Šabata 81d24c
+			    type);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
Petr Šabata 81d24c
+		    &send_tok, &ret_flags));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		gss_release_buffer(&min_status, &recv_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
Petr Šabata 81d24c
+			fatal("Zero length token output when incomplete");
Petr Šabata 81d24c
+
Dmitry Belyavskiy d075fa
+		if (gssbuf.value == NULL)
Petr Šabata 81d24c
+			fatal("No client public key");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (maj_status & GSS_S_CONTINUE_NEEDED) {
Petr Šabata 81d24c
+			debug("Sending GSSAPI_CONTINUE");
Petr Šabata 81d24c
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			gss_release_buffer(&min_status, &send_tok);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(maj_status)) {
Petr Šabata 81d24c
+		if (send_tok.length > 0) {
Petr Šabata 81d24c
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		fatal("accept_ctx died");
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Petr Šabata 81d24c
+		fatal("Mutual Authentication flag wasn't set");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!(ret_flags & GSS_C_INTEG_FLAG))
Petr Šabata 81d24c
+		fatal("Integrity flag wasn't set");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
Petr Šabata 81d24c
+		fatal("Couldn't get MIC");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
Petr Šabata 81d24c
+		fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (send_tok.length != 0) {
Petr Šabata 81d24c
+		if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
Petr Šabata 81d24c
+		    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Petr Šabata 81d24c
+			fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
Petr Šabata 81d24c
+			fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if ((r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("sshpkt_send failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_release_buffer(&min_status, &send_tok);
Petr Šabata 81d24c
+	gss_release_buffer(&min_status, &msg_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_kex_context == NULL)
Petr Šabata 81d24c
+		gss_kex_context = ctxt;
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		ssh_gssapi_delete_ctx(&ctxt);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Petr Šabata 81d24c
+		r = kex_send_newkeys(ssh);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* If this was a rekey, then save out any delegated credentials we
Petr Šabata 81d24c
+	 * just exchanged.  */
Petr Šabata 81d24c
+	if (options.gss_store_rekey)
Petr Šabata 81d24c
+		ssh_gssapi_rekey_creds();
Petr Šabata 81d24c
+out:
Petr Šabata 81d24c
+	sshbuf_free(empty);
Petr Šabata 81d24c
+	explicit_bzero(hash, sizeof(hash));
Petr Šabata 81d24c
+	sshbuf_free(shared_secret);
Petr Šabata 81d24c
+	sshbuf_free(client_pubkey);
Petr Šabata 81d24c
+	sshbuf_free(server_pubkey);
Petr Šabata 81d24c
+	return r;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+kexgssgex_server(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct kex *kex = ssh->kex;
Petr Šabata 81d24c
+	OM_uint32 maj_status, min_status;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * Some GSSAPI implementations use the input value of ret_flags (an
Petr Šabata 81d24c
+	 * output variable) as a means of triggering mechanism specific
Petr Šabata 81d24c
+	 * features. Initializing it to zero avoids inadvertently
Petr Šabata 81d24c
+	 * activating this non-standard behaviour.
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	OM_uint32 ret_flags = 0;
Petr Šabata 81d24c
+	gss_buffer_desc gssbuf, recv_tok, msg_tok;
Petr Šabata 81d24c
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Petr Šabata 81d24c
+	Gssctxt *ctxt = NULL;
Petr Šabata 81d24c
+	struct sshbuf *shared_secret = NULL;
Petr Šabata 81d24c
+	int type = 0;
Petr Šabata 81d24c
+	gss_OID oid;
Petr Šabata 81d24c
+	char *mechs;
Petr Šabata 81d24c
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Petr Šabata 81d24c
+	size_t hashlen;
Petr Šabata 81d24c
+	BIGNUM *dh_client_pub = NULL;
Petr Šabata 81d24c
+	const BIGNUM *pub_key, *dh_p, *dh_g;
Petr Šabata 81d24c
+	int min = -1, max = -1, nbits = -1;
Petr Šabata 81d24c
+	int cmin = -1, cmax = -1; /* client proposal */
Petr Šabata 81d24c
+	struct sshbuf *empty = sshbuf_new();
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Initialise GSSAPI */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* If we're rekeying, privsep means that some of the private structures
Petr Šabata 81d24c
+	 * in the GSSAPI code are no longer available. This kludges them back
Petr Šabata 81d24c
+	 * into life
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+	if (!ssh_gssapi_oid_table_ok())
Petr Šabata 81d24c
+		if ((mechs = ssh_gssapi_server_mechanisms()))
Petr Šabata 81d24c
+			free(mechs);
Petr Šabata 81d24c
+
DistroBaker d029bb
+	debug2_f("Identifying %s", kex->name);
Petr Šabata 81d24c
+	oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
Petr Šabata 81d24c
+	if (oid == GSS_C_NO_OID)
Petr Šabata 81d24c
+	   fatal("Unknown gssapi mechanism");
Petr Šabata 81d24c
+
DistroBaker d029bb
+	debug2_f("Acquiring credentials");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
Petr Šabata 81d24c
+		fatal("Unable to acquire credentials for the server");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* 5. S generates an ephemeral key pair (do the allocations early) */
Petr Šabata 81d24c
+	debug("Doing group exchange");
Petr Šabata 81d24c
+	ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
Petr Šabata 81d24c
+	/* store client proposal to provide valid signature */
Petr Šabata 81d24c
+	if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+	kex->nbits = nbits;
Petr Šabata 81d24c
+	kex->min = cmin;
Petr Šabata 81d24c
+	kex->max = cmax;
Petr Šabata 81d24c
+	min = MAX(DH_GRP_MIN, cmin);
Petr Šabata 81d24c
+	max = MIN(DH_GRP_MAX, cmax);
Petr Šabata 81d24c
+	nbits = MAXIMUM(DH_GRP_MIN, nbits);
Petr Šabata 81d24c
+	nbits = MINIMUM(DH_GRP_MAX, nbits);
Petr Šabata 81d24c
+	if (max < min || nbits < min || max < nbits)
Petr Šabata 81d24c
+		fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
Petr Šabata 81d24c
+		    min, nbits, max);
Petr Šabata 81d24c
+	kex->dh = PRIVSEP(choose_dh(min, nbits, max));
Petr Šabata 81d24c
+	if (kex->dh == NULL) {
Petr Šabata 81d24c
+		sshpkt_disconnect(ssh, "Protocol error: no matching group found");
Petr Šabata 81d24c
+		fatal("Protocol error: no matching group found");
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Petr Šabata 81d24c
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = ssh_packet_write_wait(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("ssh_packet_write_wait: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Compute our exchange value in parallel with the client */
Petr Šabata 81d24c
+	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	do {
Petr Šabata 81d24c
+		debug("Wait SSH2_MSG_GSSAPI_INIT");
Petr Šabata 81d24c
+		type = ssh_packet_read(ssh);
Petr Šabata 81d24c
+		switch(type) {
Petr Šabata 81d24c
+		case SSH2_MSG_KEXGSS_INIT:
Petr Šabata 81d24c
+			if (dh_client_pub != NULL)
Petr Šabata 81d24c
+				fatal("Received KEXGSS_INIT after initialising");
Petr Šabata 81d24c
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+			        &recv_tok)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		case SSH2_MSG_KEXGSS_CONTINUE:
Petr Šabata 81d24c
+			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Petr Šabata 81d24c
+			        &recv_tok)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_get_end(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		default:
Petr Šabata 81d24c
+			sshpkt_disconnect(ssh,
Petr Šabata 81d24c
+			    "Protocol error: didn't expect packet type %d",
Petr Šabata 81d24c
+			    type);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
Petr Šabata 81d24c
+		    &send_tok, &ret_flags));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		gss_release_buffer(&min_status, &recv_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
Petr Šabata 81d24c
+			fatal("Zero length token output when incomplete");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (dh_client_pub == NULL)
Petr Šabata 81d24c
+			fatal("No client public key");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (maj_status & GSS_S_CONTINUE_NEEDED) {
Petr Šabata 81d24c
+			debug("Sending GSSAPI_CONTINUE");
Petr Šabata 81d24c
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+			gss_release_buffer(&min_status, &send_tok);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(maj_status)) {
Petr Šabata 81d24c
+		if (send_tok.length > 0) {
Petr Šabata 81d24c
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Petr Šabata 81d24c
+			    (r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+				fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		fatal("accept_ctx died");
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Petr Šabata 81d24c
+		fatal("Mutual Authentication flag wasn't set");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!(ret_flags & GSS_C_INTEG_FLAG))
Petr Šabata 81d24c
+		fatal("Integrity flag wasn't set");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* calculate shared secret */
Petr Šabata 81d24c
+	if ((shared_secret = sshbuf_new()) == NULL) {
Petr Šabata 81d24c
+		r = SSH_ERR_ALLOC_FAIL;
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	DH_get0_key(kex->dh, &pub_key, NULL);
Petr Šabata 81d24c
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Petr Šabata 81d24c
+	hashlen = sizeof(hash);
Petr Šabata 81d24c
+	if ((r = kexgex_hash(
Petr Šabata 81d24c
+	    kex->hash_alg,
Petr Šabata 81d24c
+	    kex->client_version,
Petr Šabata 81d24c
+	    kex->server_version,
Petr Šabata 81d24c
+	    kex->peer,
Petr Šabata 81d24c
+	    kex->my,
Petr Šabata 81d24c
+	    empty,
Petr Šabata 81d24c
+	    cmin, nbits, cmax,
Petr Šabata 81d24c
+	    dh_p, dh_g,
Petr Šabata 81d24c
+	    dh_client_pub,
Petr Šabata 81d24c
+	    pub_key,
Petr Šabata 81d24c
+	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Petr Šabata 81d24c
+	    hash, &hashlen)) != 0)
Petr Šabata 81d24c
+		fatal("kexgex_hash failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gssbuf.value = hash;
Petr Šabata 81d24c
+	gssbuf.length = hashlen;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
Petr Šabata 81d24c
+		fatal("Couldn't get MIC");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
Petr Šabata 81d24c
+		fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (send_tok.length != 0) {
Petr Šabata 81d24c
+		if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
Petr Šabata 81d24c
+		    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Petr Šabata 81d24c
+			fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
Petr Šabata 81d24c
+			fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if ((r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
+		fatal("sshpkt failed: %s", ssh_err(r));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_release_buffer(&min_status, &send_tok);
Petr Šabata 81d24c
+	gss_release_buffer(&min_status, &msg_tok);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_kex_context == NULL)
Petr Šabata 81d24c
+		gss_kex_context = ctxt;
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		ssh_gssapi_delete_ctx(&ctxt);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Finally derive the keys and send them */
Petr Šabata 81d24c
+	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Petr Šabata 81d24c
+		r = kex_send_newkeys(ssh);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* If this was a rekey, then save out any delegated credentials we
Petr Šabata 81d24c
+	 * just exchanged.  */
Petr Šabata 81d24c
+	if (options.gss_store_rekey)
Petr Šabata 81d24c
+		ssh_gssapi_rekey_creds();
Petr Šabata 81d24c
+out:
Petr Šabata 81d24c
+	sshbuf_free(empty);
Petr Šabata 81d24c
+	explicit_bzero(hash, sizeof(hash));
Petr Šabata 81d24c
+	DH_free(kex->dh);
Petr Šabata 81d24c
+	kex->dh = NULL;
Petr Šabata 81d24c
+	BN_clear_free(dh_client_pub);
Petr Šabata 81d24c
+	sshbuf_free(shared_secret);
Petr Šabata 81d24c
+	return r;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
Petr Šabata 81d24c
diff --git a/monitor.c b/monitor.c
Petr Šabata 81d24c
index 2ce89fe9..ebf76c7f 100644
Petr Šabata 81d24c
--- a/monitor.c
Petr Šabata 81d24c
+++ b/monitor.c
Petr Šabata 81d24c
@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
Petr Šabata 81d24c
 int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
Petr Šabata 81d24c
 int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
Petr Šabata 81d24c
 int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
Petr Šabata 81d24c
+int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
Petr Šabata 81d24c
+int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef SSH_AUDIT_EVENTS
Petr Šabata 81d24c
@@ -220,11 +222,18 @@ struct mon_table mon_dispatch_proto20[] = {
Petr Šabata 81d24c
     {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
Petr Šabata 81d24c
     {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
Petr Šabata 81d24c
     {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
Petr Šabata 81d24c
+    {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
     {0, 0, NULL}
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 struct mon_table mon_dispatch_postauth20[] = {
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+    {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
Petr Šabata 81d24c
+    {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
Petr Šabata 81d24c
+    {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
Petr Šabata 81d24c
+    {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 #ifdef WITH_OPENSSL
Petr Šabata 81d24c
     {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
@@ -293,6 +302,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
Petr Šabata 81d24c
 	/* Permit requests for moduli and signatures */
Petr Šabata 81d24c
 	monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
Petr Šabata 81d24c
 	monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	/* and for the GSSAPI key exchange */
Petr Šabata 81d24c
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* The first few requests do not require asynchronous access */
Petr Šabata 81d24c
 	while (!authenticated) {
Petr Šabata 81d24c
@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
Petr Šabata 81d24c
 	monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
Petr Šabata 81d24c
 	monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
Petr Šabata 81d24c
 	monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	/* and for the GSSAPI key exchange */
Petr Šabata 81d24c
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if (auth_opts->permit_pty_flag) {
Petr Šabata 81d24c
 		monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
Petr Šabata 81d24c
@@ -1713,6 +1730,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
Petr Šabata 81d24c
 # ifdef OPENSSL_HAS_ECC
DistroBaker d029bb
 	kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
Petr Šabata 81d24c
 # endif
Petr Šabata 81d24c
+# ifdef GSSAPI
DistroBaker d029bb
+	if (options.gss_keyex) {
DistroBaker d029bb
+		kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
DistroBaker d029bb
+		kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
DistroBaker d029bb
+		kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
DistroBaker d029bb
+		kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
DistroBaker d029bb
+		kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
DistroBaker d029bb
+		kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
DistroBaker d029bb
+		kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
DistroBaker d029bb
+	}
Petr Šabata 81d24c
+# endif
Petr Šabata 81d24c
 #endif /* WITH_OPENSSL */
DistroBaker d029bb
 	kex->kex[KEX_C25519_SHA256] = kex_gen_server;
DistroBaker d029bb
 	kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
Petr Šabata 81d24c
@@ -1806,8 +1834,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 	u_char *p;
Petr Šabata 81d24c
 	int r;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!options.gss_authentication)
DistroBaker d029bb
-		fatal_f("GSSAPI authentication not enabled");
Petr Šabata 81d24c
+	if (!options.gss_authentication && !options.gss_keyex)
DistroBaker d029bb
+		fatal_f("GSSAPI not enabled");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((r = sshbuf_get_string(m, &p, &len)) != 0)
DistroBaker d029bb
 		fatal_fr(r, "parse");
Petr Šabata 81d24c
@@ -1839,8 +1867,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 	OM_uint32 flags = 0; /* GSI needs this */
Petr Šabata 81d24c
 	int r;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!options.gss_authentication)
DistroBaker d029bb
-		fatal_f("GSSAPI authentication not enabled");
Petr Šabata 81d24c
+	if (!options.gss_authentication && !options.gss_keyex)
DistroBaker d029bb
+		fatal_f("GSSAPI not enabled");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
DistroBaker d029bb
 		fatal_fr(r, "ssh_gssapi_get_buffer_desc");
Petr Šabata 81d24c
@@ -1860,6 +1888,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 		monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
Petr Šabata 81d24c
 		monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
Petr Šabata 81d24c
 		monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
Petr Šabata 81d24c
+		monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	return (0);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
@@ -1871,8 +1900,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 	OM_uint32 ret;
Petr Šabata 81d24c
 	int r;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!options.gss_authentication)
DistroBaker d029bb
-		fatal_f("GSSAPI authentication not enabled");
Petr Šabata 81d24c
+	if (!options.gss_authentication && !options.gss_keyex)
DistroBaker d029bb
+		fatal_f("GSSAPI not enabled");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
Petr Šabata 81d24c
 	    (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
Petr Šabata 81d24c
@@ -1898,13 +1927,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
 mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	int r, authenticated;
Petr Šabata 81d24c
+	int r, authenticated, kex;
Petr Šabata 81d24c
 	const char *displayname;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!options.gss_authentication)
DistroBaker d029bb
-		fatal_f("GSSAPI authentication not enabled");
Petr Šabata 81d24c
+	if (!options.gss_authentication && !options.gss_keyex)
DistroBaker d029bb
+		fatal_f("GSSAPI not enabled");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
Petr Šabata 81d24c
+	if ((r = sshbuf_get_u32(m, &kex)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	authenticated = authctxt->valid &&
Petr Šabata 81d24c
+	    ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	sshbuf_reset(m);
Petr Šabata 81d24c
 	if ((r = sshbuf_put_u32(m, authenticated)) != 0)
Petr Šabata 81d24c
@@ -1913,7 +1946,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
DistroBaker d029bb
 	debug3_f("sending result %d", authenticated);
Petr Šabata 81d24c
 	mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	auth_method = "gssapi-with-mic";
Petr Šabata 81d24c
+	if (kex) {
Petr Šabata 81d24c
+		auth_method = "gssapi-keyex";
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		auth_method = "gssapi-with-mic";
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((displayname = ssh_gssapi_displayname()) != NULL)
Petr Šabata 81d24c
 		auth2_record_info(authctxt, "%s", displayname);
DistroBaker d029bb
@@ -1921,5 +1958,84 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
Petr Šabata 81d24c
 	/* Monitor loop will terminate if authenticated */
Petr Šabata 81d24c
 	return (authenticated);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	gss_buffer_desc data;
Petr Šabata 81d24c
+	gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
Petr Šabata 81d24c
+	OM_uint32 major, minor;
Petr Šabata 81d24c
+	size_t len;
Petr Šabata 81d24c
+	u_char *p = NULL;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!options.gss_authentication && !options.gss_keyex)
DistroBaker d029bb
+		fatal_f("GSSAPI not enabled");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshbuf_get_string(m, &p, &len)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+	data.value = p;
Petr Šabata 81d24c
+	data.length = len;
Petr Šabata 81d24c
+	/* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
Petr Šabata 81d24c
+	if (data.length != 20 && data.length != 32 && data.length != 64)
DistroBaker d029bb
+		fatal_f("data length incorrect: %d", (int) data.length);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Save the session ID on the first time around */
Petr Šabata 81d24c
+	if (session_id2_len == 0) {
Petr Šabata 81d24c
+		session_id2_len = data.length;
Petr Šabata 81d24c
+		session_id2 = xmalloc(session_id2_len);
Petr Šabata 81d24c
+		memcpy(session_id2, data.value, session_id2_len);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	major = ssh_gssapi_sign(gsscontext, &data, &hash);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	free(data.value);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_reset(m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshbuf_put_u32(m, major)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	gss_release_buffer(&minor, &hash);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Turn on getpwnam permissions */
Petr Šabata 81d24c
+	monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* And credential updating, for when rekeying */
Petr Šabata 81d24c
+	monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (0);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
Petr Šabata 81d24c
+	ssh_gssapi_ccache store;
Petr Šabata 81d24c
+	int r, ok;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (!options.gss_authentication && !options.gss_keyex)
DistroBaker d029bb
+		fatal_f("GSSAPI not enabled");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ok = ssh_gssapi_update_creds(&store);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	free(store.filename);
Petr Šabata 81d24c
+	free(store.envvar);
Petr Šabata 81d24c
+	free(store.envval);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_reset(m);
Petr Šabata 81d24c
+	if ((r = sshbuf_put_u32(m, ok)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return(0);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 #endif /* GSSAPI */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
diff --git a/monitor.h b/monitor.h
Petr Šabata 81d24c
index 683e5e07..2b1a2d59 100644
Petr Šabata 81d24c
--- a/monitor.h
Petr Šabata 81d24c
+++ b/monitor.h
Petr Šabata 81d24c
@@ -63,6 +63,8 @@ enum monitor_reqtype {
Petr Šabata 81d24c
 	MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
Petr Šabata 81d24c
 	MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
Petr Šabata 81d24c
+	MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 struct ssh;
Petr Šabata 81d24c
diff --git a/monitor_wrap.c b/monitor_wrap.c
Petr Šabata 81d24c
index 001a8fa1..6edb509a 100644
Petr Šabata 81d24c
--- a/monitor_wrap.c
Petr Šabata 81d24c
+++ b/monitor_wrap.c
Petr Šabata 81d24c
@@ -993,13 +993,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
-mm_ssh_gssapi_userok(char *user)
Petr Šabata 81d24c
+mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct sshbuf *m;
Petr Šabata 81d24c
 	int r, authenticated = 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((m = sshbuf_new()) == NULL)
DistroBaker d029bb
 		fatal_f("sshbuf_new failed");
Petr Šabata 81d24c
+	if ((r = sshbuf_put_u32(m, kex)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
Petr Šabata 81d24c
 	mm_request_receive_expect(pmonitor->m_recvfd,
Petr Šabata 81d24c
@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user)
DistroBaker d029bb
 	debug3_f("user %sauthenticated", authenticated ? "" : "not ");
Petr Šabata 81d24c
 	return (authenticated);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+OM_uint32
Petr Šabata 81d24c
+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct sshbuf *m;
Petr Šabata 81d24c
+	OM_uint32 major;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((m = sshbuf_new()) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_new failed");
Petr Šabata 81d24c
+	if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
Petr Šabata 81d24c
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshbuf_get_u32(m, &major)) != 0 ||
Petr Šabata 81d24c
+	    (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_free(m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (major);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct sshbuf *m;
Petr Šabata 81d24c
+	int r, ok;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((m = sshbuf_new()) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_new failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshbuf_put_cstring(m,
Petr Šabata 81d24c
+	    store->filename ? store->filename : "")) != 0 ||
Petr Šabata 81d24c
+	    (r = sshbuf_put_cstring(m,
Petr Šabata 81d24c
+	    store->envvar ? store->envvar : "")) != 0 ||
Petr Šabata 81d24c
+	    (r = sshbuf_put_cstring(m,
Petr Šabata 81d24c
+	    store->envval ? store->envval : "")) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
Petr Šabata 81d24c
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshbuf_get_u32(m, &ok)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "buffer error");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_free(m);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (ok);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 #endif /* GSSAPI */
Petr Šabata 81d24c
diff --git a/monitor_wrap.h b/monitor_wrap.h
Petr Šabata 81d24c
index 23ab096a..485590c1 100644
Petr Šabata 81d24c
--- a/monitor_wrap.h
Petr Šabata 81d24c
+++ b/monitor_wrap.h
Petr Šabata 81d24c
@@ -64,8 +64,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
Petr Šabata 81d24c
 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
Petr Šabata 81d24c
 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
Petr Šabata 81d24c
    gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
Petr Šabata 81d24c
-int mm_ssh_gssapi_userok(char *user);
Petr Šabata 81d24c
+int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
Petr Šabata 81d24c
 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
Petr Šabata 81d24c
+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
Petr Šabata 81d24c
+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef USE_PAM
Dmitry Belyavskiy f9e5de
diff -up a/readconf.c.gsskex b/readconf.c
Dmitry Belyavskiy f9e5de
--- a/readconf.c.gsskex	2021-08-20 06:03:49.000000000 +0200
Dmitry Belyavskiy f9e5de
+++ b/readconf.c	2021-08-27 12:25:42.556421509 +0200
Petr Šabata 81d24c
@@ -67,6 +67,7 @@
Petr Šabata 81d24c
 #include "uidswap.h"
Petr Šabata 81d24c
 #include "myproposal.h"
Petr Šabata 81d24c
 #include "digest.h"
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* Format of the configuration file:
Petr Šabata 81d24c
 
Dmitry Belyavskiy f9e5de
@@ -161,6 +162,8 @@ typedef enum {
Petr Šabata 81d24c
 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
Petr Šabata 81d24c
 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
Petr Šabata 81d24c
 	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
Petr Šabata 81d24c
+	oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
Petr Šabata 81d24c
+	oGssServerIdentity, oGssKexAlgorithms,
Petr Šabata 81d24c
 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
Petr Šabata 81d24c
 	oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
Petr Šabata 81d24c
 	oHashKnownHosts,
Dmitry Belyavskiy f9e5de
@@ -206,10 +209,22 @@ static struct {
Petr Šabata 81d24c
 	/* Sometimes-unsupported options */
Petr Šabata 81d24c
 #if defined(GSSAPI)
Petr Šabata 81d24c
 	{ "gssapiauthentication", oGssAuthentication },
Petr Šabata 81d24c
+	{ "gssapikeyexchange", oGssKeyEx },
Petr Šabata 81d24c
 	{ "gssapidelegatecredentials", oGssDelegateCreds },
Petr Šabata 81d24c
+	{ "gssapitrustdns", oGssTrustDns },
Petr Šabata 81d24c
+	{ "gssapiclientidentity", oGssClientIdentity },
Petr Šabata 81d24c
+	{ "gssapiserveridentity", oGssServerIdentity },
Petr Šabata 81d24c
+	{ "gssapirenewalforcesrekey", oGssRenewalRekey },
Petr Šabata 81d24c
+	{ "gssapikexalgorithms", oGssKexAlgorithms },
Petr Šabata 81d24c
 # else
Petr Šabata 81d24c
 	{ "gssapiauthentication", oUnsupported },
Petr Šabata 81d24c
+	{ "gssapikeyexchange", oUnsupported },
Petr Šabata 81d24c
 	{ "gssapidelegatecredentials", oUnsupported },
Petr Šabata 81d24c
+	{ "gssapitrustdns", oUnsupported },
Petr Šabata 81d24c
+	{ "gssapiclientidentity", oUnsupported },
Petr Šabata 81d24c
+	{ "gssapiserveridentity", oUnsupported },
Petr Šabata 81d24c
+	{ "gssapirenewalforcesrekey", oUnsupported },
Petr Šabata 81d24c
+	{ "gssapikexalgorithms", oUnsupported },
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 #ifdef ENABLE_PKCS11
Petr Šabata 81d24c
 	{ "pkcs11provider", oPKCS11Provider },
Dmitry Belyavskiy f9e5de
@@ -1113,10 +1128,42 @@ parse_time:
Petr Šabata 81d24c
 		intptr = &options->gss_authentication;
Petr Šabata 81d24c
 		goto parse_flag;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	case oGssKeyEx:
Petr Šabata 81d24c
+		intptr = &options->gss_keyex;
Petr Šabata 81d24c
+		goto parse_flag;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	case oGssDelegateCreds:
Petr Šabata 81d24c
 		intptr = &options->gss_deleg_creds;
Petr Šabata 81d24c
 		goto parse_flag;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	case oGssTrustDns:
Petr Šabata 81d24c
+		intptr = &options->gss_trust_dns;
Petr Šabata 81d24c
+		goto parse_flag;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	case oGssClientIdentity:
Petr Šabata 81d24c
+		charptr = &options->gss_client_identity;
Petr Šabata 81d24c
+		goto parse_string;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	case oGssServerIdentity:
Petr Šabata 81d24c
+		charptr = &options->gss_server_identity;
Petr Šabata 81d24c
+		goto parse_string;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	case oGssRenewalRekey:
Petr Šabata 81d24c
+		intptr = &options->gss_renewal_rekey;
Petr Šabata 81d24c
+		goto parse_flag;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	case oGssKexAlgorithms:
Dmitry Belyavskiy f9e5de
+		arg = argv_next(&ac, &av;;
Petr Šabata 81d24c
+		if (!arg || *arg == '\0')
Petr Šabata 81d24c
+			fatal("%.200s line %d: Missing argument.",
Petr Šabata 81d24c
+			    filename, linenum);
Petr Šabata 81d24c
+		if (!kex_gss_names_valid(arg))
Petr Šabata 81d24c
+			fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
Petr Šabata 81d24c
+			    filename, linenum, arg ? arg : "<NONE>");
Petr Šabata 81d24c
+		if (*activep && options->gss_kex_algorithms == NULL)
Petr Šabata 81d24c
+			options->gss_kex_algorithms = xstrdup(arg);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	case oBatchMode:
Petr Šabata 81d24c
 		intptr = &options->batch_mode;
Petr Šabata 81d24c
 		goto parse_flag;
Dmitry Belyavskiy f9e5de
@@ -2306,7 +2353,13 @@ initialize_options(Options * options)
Dmitry Belyavskiy f9e5de
 	options->fwd_opts.streamlocal_bind_unlink = -1;
Petr Šabata 81d24c
 	options->pubkey_authentication = -1;
Petr Šabata 81d24c
 	options->gss_authentication = -1;
Petr Šabata 81d24c
+	options->gss_keyex = -1;
Petr Šabata 81d24c
 	options->gss_deleg_creds = -1;
Petr Šabata 81d24c
+	options->gss_trust_dns = -1;
Petr Šabata 81d24c
+	options->gss_renewal_rekey = -1;
Petr Šabata 81d24c
+	options->gss_client_identity = NULL;
Petr Šabata 81d24c
+	options->gss_server_identity = NULL;
Petr Šabata 81d24c
+	options->gss_kex_algorithms = NULL;
Petr Šabata 81d24c
 	options->password_authentication = -1;
Petr Šabata 81d24c
 	options->kbd_interactive_authentication = -1;
Petr Šabata 81d24c
 	options->kbd_interactive_devices = NULL;
Dmitry Belyavskiy f9e5de
@@ -2463,8 +2516,18 @@ fill_default_options(Options * options)
Dmitry Belyavskiy f9e5de
 		options->pubkey_authentication = 1;
Petr Šabata 81d24c
 	if (options->gss_authentication == -1)
Petr Šabata 81d24c
 		options->gss_authentication = 0;
Petr Šabata 81d24c
+	if (options->gss_keyex == -1)
Petr Šabata 81d24c
+		options->gss_keyex = 0;
Petr Šabata 81d24c
 	if (options->gss_deleg_creds == -1)
Petr Šabata 81d24c
 		options->gss_deleg_creds = 0;
Petr Šabata 81d24c
+	if (options->gss_trust_dns == -1)
Petr Šabata 81d24c
+		options->gss_trust_dns = 0;
Petr Šabata 81d24c
+	if (options->gss_renewal_rekey == -1)
Petr Šabata 81d24c
+		options->gss_renewal_rekey = 0;
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	if (options->gss_kex_algorithms == NULL)
Petr Šabata 81d24c
+		options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	if (options->password_authentication == -1)
Petr Šabata 81d24c
 		options->password_authentication = 1;
Petr Šabata 81d24c
 	if (options->kbd_interactive_authentication == -1)
Dmitry Belyavskiy f9e5de
@@ -3246,7 +3309,14 @@ dump_client_config(Options *o, const cha
Petr Šabata 81d24c
 	dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
 	dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
Petr Šabata 81d24c
+	dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
Petr Šabata 81d24c
 	dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
Petr Šabata 81d24c
+	dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
Petr Šabata 81d24c
+	dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
Petr Šabata 81d24c
+	dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
Petr Šabata 81d24c
+	dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
Petr Šabata 81d24c
+	dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
Petr Šabata 81d24c
+	    o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
Petr Šabata 81d24c
 #endif /* GSSAPI */
Petr Šabata 81d24c
 	dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
Petr Šabata 81d24c
 	dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
Dmitry Belyavskiy f9e5de
diff -up a/readconf.h.gsskex b/readconf.h
Dmitry Belyavskiy f9e5de
--- a/readconf.h.gsskex	2021-08-27 12:05:29.248142431 +0200
Dmitry Belyavskiy f9e5de
+++ b/readconf.h	2021-08-27 12:22:19.270679852 +0200
Dmitry Belyavskiy f9e5de
@@ -39,7 +39,13 @@ typedef struct {
Dmitry Belyavskiy f9e5de
 	int     pubkey_authentication;	/* Try ssh2 pubkey authentication. */
Dmitry Belyavskiy f9e5de
 	int     hostbased_authentication;	/* ssh2's rhosts_rsa */
Petr Šabata 81d24c
 	int     gss_authentication;	/* Try GSS authentication */
Petr Šabata 81d24c
+	int     gss_keyex;		/* Try GSS key exchange */
Petr Šabata 81d24c
 	int     gss_deleg_creds;	/* Delegate GSS credentials */
Petr Šabata 81d24c
+	int	gss_trust_dns;		/* Trust DNS for GSS canonicalization */
Petr Šabata 81d24c
+	int	gss_renewal_rekey;	/* Credential renewal forces rekey */
Petr Šabata 81d24c
+	char    *gss_client_identity;   /* Principal to initiate GSSAPI with */
Petr Šabata 81d24c
+	char    *gss_server_identity;   /* GSSAPI target principal */
Petr Šabata 81d24c
+	char    *gss_kex_algorithms;	/* GSSAPI kex methods to be offered by client. */
Petr Šabata 81d24c
 	int     password_authentication;	/* Try password
Petr Šabata 81d24c
 						 * authentication. */
Petr Šabata 81d24c
 	int     kbd_interactive_authentication; /* Try keyboard-interactive auth. */
Dmitry Belyavskiy f9e5de
diff -up a/servconf.c.gsskex b/servconf.c
Dmitry Belyavskiy f9e5de
--- a/servconf.c.gsskex	2021-08-20 06:03:49.000000000 +0200
Dmitry Belyavskiy f9e5de
+++ b/servconf.c	2021-08-27 12:28:15.887735189 +0200
Dmitry Belyavskiy f9e5de
@@ -70,6 +70,7 @@
Petr Šabata 81d24c
 #include "auth.h"
Petr Šabata 81d24c
 #include "myproposal.h"
Petr Šabata 81d24c
 #include "digest.h"
Petr Šabata 81d24c
+#include "ssh-gss.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static void add_listen_addr(ServerOptions *, const char *,
Petr Šabata 81d24c
     const char *, int);
Dmitry Belyavskiy f9e5de
@@ -136,8 +137,11 @@ initialize_server_options(ServerOptions
Petr Šabata 81d24c
 	options->kerberos_ticket_cleanup = -1;
Petr Šabata 81d24c
 	options->kerberos_get_afs_token = -1;
Petr Šabata 81d24c
 	options->gss_authentication=-1;
Petr Šabata 81d24c
+	options->gss_keyex = -1;
Petr Šabata 81d24c
 	options->gss_cleanup_creds = -1;
Petr Šabata 81d24c
 	options->gss_strict_acceptor = -1;
Petr Šabata 81d24c
+	options->gss_store_rekey = -1;
Petr Šabata 81d24c
+	options->gss_kex_algorithms = NULL;
Petr Šabata 81d24c
 	options->password_authentication = -1;
Petr Šabata 81d24c
 	options->kbd_interactive_authentication = -1;
Dmitry Belyavskiy f9e5de
 	options->permit_empty_passwd = -1;
Dmitry Belyavskiy f9e5de
@@ -356,10 +360,18 @@ fill_default_server_options(ServerOption
Petr Šabata 81d24c
 		options->kerberos_get_afs_token = 0;
Petr Šabata 81d24c
 	if (options->gss_authentication == -1)
Petr Šabata 81d24c
 		options->gss_authentication = 0;
Petr Šabata 81d24c
+	if (options->gss_keyex == -1)
Petr Šabata 81d24c
+		options->gss_keyex = 0;
Petr Šabata 81d24c
 	if (options->gss_cleanup_creds == -1)
Petr Šabata 81d24c
 		options->gss_cleanup_creds = 1;
Petr Šabata 81d24c
 	if (options->gss_strict_acceptor == -1)
Petr Šabata 81d24c
 		options->gss_strict_acceptor = 1;
Petr Šabata 81d24c
+	if (options->gss_store_rekey == -1)
Petr Šabata 81d24c
+		options->gss_store_rekey = 0;
Petr Šabata 81d24c
+#ifdef GSSAPI
Petr Šabata 81d24c
+	if (options->gss_kex_algorithms == NULL)
Petr Šabata 81d24c
+		options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	if (options->password_authentication == -1)
Petr Šabata 81d24c
 		options->password_authentication = 1;
Petr Šabata 81d24c
 	if (options->kbd_interactive_authentication == -1)
Dmitry Belyavskiy f9e5de
@@ -506,6 +518,7 @@ typedef enum {
DistroBaker d029bb
 	sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize,
Petr Šabata 81d24c
 	sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
Petr Šabata 81d24c
 	sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
Petr Šabata 81d24c
+	sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
Petr Šabata 81d24c
 	sAcceptEnv, sSetEnv, sPermitTunnel,
Petr Šabata 81d24c
 	sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
Petr Šabata 81d24c
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
Dmitry Belyavskiy f9e5de
@@ -587,12 +600,22 @@ static struct {
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
 	{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
Petr Šabata 81d24c
 	{ "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
Petr Šabata 81d24c
 	{ "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
Petr Šabata 81d24c
 #else
Petr Šabata 81d24c
 	{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
Petr Šabata 81d24c
 	{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
 	{ "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
+	{ "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
+	{ "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
Petr Šabata 81d24c
 	{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
Petr Šabata 81d24c
 	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
Dmitry Belyavskiy f9e5de
 	{ "challengeresponseauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */
Dmitry Belyavskiy f9e5de
@@ -1576,6 +1599,10 @@ process_server_config_line_depth(ServerO
Petr Šabata 81d24c
 		intptr = &options->gss_authentication;
Petr Šabata 81d24c
 		goto parse_flag;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	case sGssKeyEx:
Petr Šabata 81d24c
+		intptr = &options->gss_keyex;
Petr Šabata 81d24c
+		goto parse_flag;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	case sGssCleanupCreds:
Petr Šabata 81d24c
 		intptr = &options->gss_cleanup_creds;
Petr Šabata 81d24c
 		goto parse_flag;
Dmitry Belyavskiy f9e5de
@@ -1584,6 +1611,22 @@ process_server_config_line_depth(ServerO
Petr Šabata 81d24c
 		intptr = &options->gss_strict_acceptor;
Petr Šabata 81d24c
 		goto parse_flag;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	case sGssStoreRekey:
Petr Šabata 81d24c
+		intptr = &options->gss_store_rekey;
Petr Šabata 81d24c
+		goto parse_flag;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	case sGssKexAlgorithms:
Dmitry Belyavskiy f9e5de
+		arg = argv_next(&ac, &av;;
Petr Šabata 81d24c
+		if (!arg || *arg == '\0')
Petr Šabata 81d24c
+			fatal("%.200s line %d: Missing argument.",
Petr Šabata 81d24c
+			    filename, linenum);
Petr Šabata 81d24c
+		if (!kex_gss_names_valid(arg))
Petr Šabata 81d24c
+			fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
Petr Šabata 81d24c
+			    filename, linenum, arg ? arg : "<NONE>");
Petr Šabata 81d24c
+		if (*activep && options->gss_kex_algorithms == NULL)
Petr Šabata 81d24c
+			options->gss_kex_algorithms = xstrdup(arg);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	case sPasswordAuthentication:
Petr Šabata 81d24c
 		intptr = &options->password_authentication;
Petr Šabata 81d24c
 		goto parse_flag;
Dmitry Belyavskiy f9e5de
@@ -2892,6 +2935,10 @@ dump_config(ServerOptions *o)
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
 	dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
Petr Šabata 81d24c
 	dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
Petr Šabata 81d24c
+	dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
Petr Šabata 81d24c
+	dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
Petr Šabata 81d24c
+	dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
Petr Šabata 81d24c
+	dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 	dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
Petr Šabata 81d24c
 	dump_cfg_fmtint(sKbdInteractiveAuthentication,
Petr Šabata 81d24c
diff --git a/servconf.h b/servconf.h
Petr Šabata 81d24c
index 4202a2d0..3f47ea25 100644
Petr Šabata 81d24c
--- a/servconf.h
Petr Šabata 81d24c
+++ b/servconf.h
Petr Šabata 81d24c
@@ -132,8 +132,11 @@ typedef struct {
Petr Šabata 81d24c
 	int     kerberos_get_afs_token;		/* If true, try to get AFS token if
Petr Šabata 81d24c
 						 * authenticated with Kerberos. */
Petr Šabata 81d24c
 	int     gss_authentication;	/* If true, permit GSSAPI authentication */
Petr Šabata 81d24c
+	int     gss_keyex;		/* If true, permit GSSAPI key exchange */
Petr Šabata 81d24c
 	int     gss_cleanup_creds;	/* If true, destroy cred cache on logout */
Petr Šabata 81d24c
 	int     gss_strict_acceptor;	/* If true, restrict the GSSAPI acceptor name */
Petr Šabata 81d24c
+	int 	gss_store_rekey;
Petr Šabata 81d24c
+	char   *gss_kex_algorithms;	/* GSSAPI kex methods to be offered by client. */
Petr Šabata 81d24c
 	int     password_authentication;	/* If true, permit password
Petr Šabata 81d24c
 						 * authentication. */
Petr Šabata 81d24c
 	int     kbd_interactive_authentication;	/* If true, permit */
Petr Šabata 81d24c
diff --git a/session.c b/session.c
Petr Šabata 81d24c
index 8c0e54f7..06a33442 100644
Petr Šabata 81d24c
--- a/session.c
Petr Šabata 81d24c
+++ b/session.c
Petr Šabata 81d24c
@@ -2678,13 +2678,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef KRB5
Petr Šabata 81d24c
 	if (options.kerberos_ticket_cleanup &&
Petr Šabata 81d24c
-	    authctxt->krb5_ctx)
Petr Šabata 81d24c
+	    authctxt->krb5_ctx) {
Petr Šabata 81d24c
+		temporarily_use_uid(authctxt->pw);
Petr Šabata 81d24c
 		krb5_cleanup_proc(authctxt);
Petr Šabata 81d24c
+		restore_uid();
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
-	if (options.gss_cleanup_creds)
Petr Šabata 81d24c
+	if (options.gss_cleanup_creds) {
Petr Šabata 81d24c
+		temporarily_use_uid(authctxt->pw);
Petr Šabata 81d24c
 		ssh_gssapi_cleanup_creds();
Petr Šabata 81d24c
+		restore_uid();
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* remove agent socket */
Petr Šabata 81d24c
diff --git a/ssh-gss.h b/ssh-gss.h
Petr Šabata 81d24c
index 36180d07..70dd3665 100644
Petr Šabata 81d24c
--- a/ssh-gss.h
Petr Šabata 81d24c
+++ b/ssh-gss.h
Petr Šabata 81d24c
@@ -1,6 +1,6 @@
DistroBaker d029bb
 /* $OpenBSD: ssh-gss.h,v 1.15 2021/01/27 10:05:28 djm Exp $ */
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
Petr Šabata 81d24c
  *
Petr Šabata 81d24c
  * Redistribution and use in source and binary forms, with or without
Petr Šabata 81d24c
  * modification, are permitted provided that the following conditions
Petr Šabata 81d24c
@@ -61,10 +61,34 @@
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #define SSH_GSS_OIDTYPE 0x06
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_INIT                            30
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_CONTINUE                        31
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_COMPLETE                        32
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_HOSTKEY                         33
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_ERROR                           34
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_GROUPREQ			40
Petr Šabata 81d24c
+#define SSH2_MSG_KEXGSS_GROUP				41
Petr Šabata 81d24c
+#define KEX_GSS_GRP1_SHA1_ID				"gss-group1-sha1-"
Petr Šabata 81d24c
+#define KEX_GSS_GRP14_SHA1_ID				"gss-group14-sha1-"
Petr Šabata 81d24c
+#define KEX_GSS_GRP14_SHA256_ID			"gss-group14-sha256-"
Petr Šabata 81d24c
+#define KEX_GSS_GRP16_SHA512_ID			"gss-group16-sha512-"
Petr Šabata 81d24c
+#define KEX_GSS_GEX_SHA1_ID				"gss-gex-sha1-"
Petr Šabata 81d24c
+#define KEX_GSS_NISTP256_SHA256_ID			"gss-nistp256-sha256-"
Petr Šabata 81d24c
+#define KEX_GSS_C25519_SHA256_ID			"gss-curve25519-sha256-"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define        GSS_KEX_DEFAULT_KEX \
Petr Šabata 81d24c
+	KEX_GSS_GRP14_SHA256_ID "," \
Petr Šabata 81d24c
+	KEX_GSS_GRP16_SHA512_ID	"," \
Petr Šabata 81d24c
+	KEX_GSS_NISTP256_SHA256_ID "," \
Petr Šabata 81d24c
+	KEX_GSS_C25519_SHA256_ID "," \
Petr Šabata 81d24c
+	KEX_GSS_GRP14_SHA1_ID "," \
Petr Šabata 81d24c
+	KEX_GSS_GEX_SHA1_ID
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 typedef struct {
Petr Šabata 81d24c
 	char *filename;
Petr Šabata 81d24c
 	char *envvar;
Petr Šabata 81d24c
 	char *envval;
Petr Šabata 81d24c
+	struct passwd *owner;
Petr Šabata 81d24c
 	void *data;
Petr Šabata 81d24c
 } ssh_gssapi_ccache;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -72,8 +92,11 @@ typedef struct {
Petr Šabata 81d24c
 	gss_buffer_desc displayname;
Petr Šabata 81d24c
 	gss_buffer_desc exportedname;
Petr Šabata 81d24c
 	gss_cred_id_t creds;
Petr Šabata 81d24c
+	gss_name_t name;
Petr Šabata 81d24c
 	struct ssh_gssapi_mech_struct *mech;
Petr Šabata 81d24c
 	ssh_gssapi_ccache store;
Petr Šabata 81d24c
+	int used;
Petr Šabata 81d24c
+	int updated;
Petr Šabata 81d24c
 } ssh_gssapi_client;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 typedef struct ssh_gssapi_mech_struct {
Petr Šabata 81d24c
@@ -84,6 +107,7 @@ typedef struct ssh_gssapi_mech_struct {
Petr Šabata 81d24c
 	int (*userok) (ssh_gssapi_client *, char *);
Petr Šabata 81d24c
 	int (*localname) (ssh_gssapi_client *, char **);
Petr Šabata 81d24c
 	void (*storecreds) (ssh_gssapi_client *);
Petr Šabata 81d24c
+	int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
Petr Šabata 81d24c
 } ssh_gssapi_mech;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 typedef struct {
Petr Šabata 81d24c
@@ -94,10 +118,11 @@ typedef struct {
Petr Šabata 81d24c
 	gss_OID		oid; /* client */
Petr Šabata 81d24c
 	gss_cred_id_t	creds; /* server */
Petr Šabata 81d24c
 	gss_name_t	client; /* server */
Petr Šabata 81d24c
-	gss_cred_id_t	client_creds; /* server */
Petr Šabata 81d24c
+	gss_cred_id_t	client_creds; /* both */
Petr Šabata 81d24c
 } Gssctxt;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 extern ssh_gssapi_mech *supported_mechs[];
Petr Šabata 81d24c
+extern Gssctxt *gss_kex_context;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int  ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
Petr Šabata 81d24c
 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
Petr Šabata 81d24c
@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 struct sshbuf;
Petr Šabata 81d24c
 int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
Petr Šabata 81d24c
+int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
Petr Šabata 81d24c
 OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
Petr Šabata 81d24c
@@ -123,17 +149,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
Petr Šabata 81d24c
 OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
Petr Šabata 81d24c
 void ssh_gssapi_buildmic(struct sshbuf *, const char *,
DistroBaker d029bb
     const char *, const char *, const struct sshbuf *);
Petr Šabata 81d24c
-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
Petr Šabata 81d24c
+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
Petr Šabata 81d24c
+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
Petr Šabata 81d24c
+int ssh_gssapi_credentials_updated(Gssctxt *);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* In the server */
Petr Šabata 81d24c
+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
Petr Šabata 81d24c
+    const char *);
Petr Šabata 81d24c
+char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
Petr Šabata 81d24c
+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
Petr Šabata 81d24c
+    const char *, const char *);
Petr Šabata 81d24c
+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
Petr Šabata 81d24c
+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
Petr Šabata 81d24c
+    const char *);
Petr Šabata 81d24c
 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
Petr Šabata 81d24c
-int ssh_gssapi_userok(char *name);
Petr Šabata 81d24c
+int ssh_gssapi_userok(char *name, struct passwd *, int kex);
Petr Šabata 81d24c
 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
Petr Šabata 81d24c
 void ssh_gssapi_do_child(char ***, u_int *);
Petr Šabata 81d24c
 void ssh_gssapi_cleanup_creds(void);
Petr Šabata 81d24c
 void ssh_gssapi_storecreds(void);
Petr Šabata 81d24c
 const char *ssh_gssapi_displayname(void);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+char *ssh_gssapi_server_mechanisms(void);
Petr Šabata 81d24c
+int ssh_gssapi_oid_table_ok(void);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
Petr Šabata 81d24c
+void ssh_gssapi_rekey_creds(void);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 #endif /* GSSAPI */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #endif /* _SSH_GSS_H */
Petr Šabata 81d24c
diff --git a/ssh.1 b/ssh.1
Petr Šabata 81d24c
index 60de6087..db5c65bc 100644
Petr Šabata 81d24c
--- a/ssh.1
Petr Šabata 81d24c
+++ b/ssh.1
Petr Šabata 81d24c
@@ -503,7 +503,13 @@ For full details of the options listed below, and their possible values, see
Petr Šabata 81d24c
 .It GatewayPorts
Petr Šabata 81d24c
 .It GlobalKnownHostsFile
Petr Šabata 81d24c
 .It GSSAPIAuthentication
Petr Šabata 81d24c
+.It GSSAPIKeyExchange
Petr Šabata 81d24c
+.It GSSAPIClientIdentity
Petr Šabata 81d24c
 .It GSSAPIDelegateCredentials
Petr Šabata 81d24c
+.It GSSAPIKexAlgorithms
Petr Šabata 81d24c
+.It GSSAPIRenewalForcesRekey
Petr Šabata 81d24c
+.It GSSAPIServerIdentity
Petr Šabata 81d24c
+.It GSSAPITrustDns
Petr Šabata 81d24c
 .It HashKnownHosts
Petr Šabata 81d24c
 .It Host
DistroBaker d029bb
 .It HostbasedAcceptedAlgorithms
Petr Šabata 81d24c
@@ -579,6 +585,8 @@ flag),
Petr Šabata 81d24c
 (supported message integrity codes),
Petr Šabata 81d24c
 .Ar kex
Petr Šabata 81d24c
 (key exchange algorithms),
Petr Šabata 81d24c
+.Ar kex-gss
Petr Šabata 81d24c
+(GSSAPI key exchange algorithms),
Petr Šabata 81d24c
 .Ar key
Petr Šabata 81d24c
 (key types),
Petr Šabata 81d24c
 .Ar key-cert
Petr Šabata 81d24c
diff --git a/ssh.c b/ssh.c
Petr Šabata 81d24c
index 15aee569..110cf9c1 100644
Petr Šabata 81d24c
--- a/ssh.c
Petr Šabata 81d24c
+++ b/ssh.c
Petr Šabata 81d24c
@@ -747,6 +747,8 @@ main(int ac, char **av)
Petr Šabata 81d24c
 			else if (strcmp(optarg, "kex") == 0 ||
Petr Šabata 81d24c
 			    strcasecmp(optarg, "KexAlgorithms") == 0)
Petr Šabata 81d24c
 				cp = kex_alg_list('\n');
Petr Šabata 81d24c
+			else if (strcmp(optarg, "kex-gss") == 0)
Petr Šabata 81d24c
+				cp = kex_gss_alg_list('\n');
Petr Šabata 81d24c
 			else if (strcmp(optarg, "key") == 0)
Petr Šabata 81d24c
 				cp = sshkey_alg_list(0, 0, 0, '\n');
Petr Šabata 81d24c
 			else if (strcmp(optarg, "key-cert") == 0)
Petr Šabata 81d24c
@@ -772,8 +774,8 @@ main(int ac, char **av)
Petr Šabata 81d24c
 			} else if (strcmp(optarg, "help") == 0) {
Petr Šabata 81d24c
 				cp = xstrdup(
Petr Šabata 81d24c
 				    "cipher\ncipher-auth\ncompression\nkex\n"
Petr Šabata 81d24c
-				    "key\nkey-cert\nkey-plain\nkey-sig\nmac\n"
Petr Šabata 81d24c
-				    "protocol-version\nsig");
Petr Šabata 81d24c
+				    "kex-gss\nkey\nkey-cert\nkey-plain\n"
Petr Šabata 81d24c
+				    "key-sig\nmac\nprotocol-version\nsig");
Petr Šabata 81d24c
 			}
Petr Šabata 81d24c
 			if (cp == NULL)
Petr Šabata 81d24c
 				fatal("Unsupported query \"%s\"", optarg);
Petr Šabata 81d24c
diff --git a/ssh_config b/ssh_config
Petr Šabata 81d24c
index 5e8ef548..1ff999b6 100644
Petr Šabata 81d24c
--- a/ssh_config
Petr Šabata 81d24c
+++ b/ssh_config
Petr Šabata 81d24c
@@ -24,6 +24,8 @@
Petr Šabata 81d24c
 #   HostbasedAuthentication no
Petr Šabata 81d24c
 #   GSSAPIAuthentication no
Petr Šabata 81d24c
 #   GSSAPIDelegateCredentials no
Petr Šabata 81d24c
+#   GSSAPIKeyExchange no
Petr Šabata 81d24c
+#   GSSAPITrustDNS no
Petr Šabata 81d24c
 #   BatchMode no
Petr Šabata 81d24c
 #   CheckHostIP yes
Petr Šabata 81d24c
 #   AddressFamily any
Petr Šabata 81d24c
diff --git a/ssh_config.5 b/ssh_config.5
Petr Šabata 81d24c
index 06a32d31..3f490697 100644
Petr Šabata 81d24c
--- a/ssh_config.5
Petr Šabata 81d24c
+++ b/ssh_config.5
Petr Šabata 81d24c
@@ -766,10 +766,68 @@ The default is
Petr Šabata 81d24c
 Specifies whether user authentication based on GSSAPI is allowed.
Petr Šabata 81d24c
 The default is
Petr Šabata 81d24c
 .Cm no .
Petr Šabata 81d24c
+.It Cm GSSAPIClientIdentity
Petr Šabata 81d24c
+If set, specifies the GSSAPI client identity that ssh should use when
Petr Šabata 81d24c
+connecting to the server. The default is unset, which means that the default
Petr Šabata 81d24c
+identity will be used.
Petr Šabata 81d24c
 .It Cm GSSAPIDelegateCredentials
Petr Šabata 81d24c
 Forward (delegate) credentials to the server.
Petr Šabata 81d24c
 The default is
Petr Šabata 81d24c
 .Cm no .
Petr Šabata 81d24c
+.It Cm GSSAPIKeyExchange
Petr Šabata 81d24c
+Specifies whether key exchange based on GSSAPI may be used. When using
Petr Šabata 81d24c
+GSSAPI key exchange the server need not have a host key.
Petr Šabata 81d24c
+The default is
Petr Šabata 81d24c
+.Dq no .
Petr Šabata 81d24c
+.It Cm GSSAPIRenewalForcesRekey
Petr Šabata 81d24c
+If set to
Petr Šabata 81d24c
+.Dq yes
Petr Šabata 81d24c
+then renewal of the client's GSSAPI credentials will force the rekeying of the
Petr Šabata 81d24c
+ssh connection. With a compatible server, this will delegate the renewed
Petr Šabata 81d24c
+credentials to a session on the server.
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+Checks are made to ensure that credentials are only propagated when the new
Petr Šabata 81d24c
+credentials match the old ones on the originating client and where the
Petr Šabata 81d24c
+receiving server still has the old set in its cache.
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+The default is
Petr Šabata 81d24c
+.Dq no .
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+For this to work
Petr Šabata 81d24c
+.Cm GSSAPIKeyExchange
Petr Šabata 81d24c
+needs to be enabled in the server and also used by the client.
Petr Šabata 81d24c
+.It Cm GSSAPIServerIdentity
Petr Šabata 81d24c
+If set, specifies the GSSAPI server identity that ssh should expect when
Petr Šabata 81d24c
+connecting to the server. The default is unset, which means that the
Petr Šabata 81d24c
+expected GSSAPI server identity will be determined from the target
Petr Šabata 81d24c
+hostname.
Petr Šabata 81d24c
+.It Cm GSSAPITrustDns
Petr Šabata 81d24c
+Set to
Petr Šabata 81d24c
+.Dq yes
Petr Šabata 81d24c
+to indicate that the DNS is trusted to securely canonicalize
Petr Šabata 81d24c
+the name of the host being connected to. If
Petr Šabata 81d24c
+.Dq no ,
Petr Šabata 81d24c
+the hostname entered on the
Petr Šabata 81d24c
+command line will be passed untouched to the GSSAPI library.
Petr Šabata 81d24c
+The default is
Petr Šabata 81d24c
+.Dq no .
Petr Šabata 81d24c
+.It Cm GSSAPIKexAlgorithms
Petr Šabata 81d24c
+The list of key exchange algorithms that are offered for GSSAPI
Petr Šabata 81d24c
+key exchange. Possible values are
Petr Šabata 81d24c
+.Bd -literal -offset 3n
Petr Šabata 81d24c
+gss-gex-sha1-,
Petr Šabata 81d24c
+gss-group1-sha1-,
Petr Šabata 81d24c
+gss-group14-sha1-,
Petr Šabata 81d24c
+gss-group14-sha256-,
Petr Šabata 81d24c
+gss-group16-sha512-,
Petr Šabata 81d24c
+gss-nistp256-sha256-,
Petr Šabata 81d24c
+gss-curve25519-sha256-
Petr Šabata 81d24c
+.Ed
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+The default is
Petr Šabata 81d24c
+.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
Petr Šabata 81d24c
+gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
Petr Šabata 81d24c
+This option only applies to connections using GSSAPI.
Petr Šabata 81d24c
 .It Cm HashKnownHosts
Petr Šabata 81d24c
 Indicates that
Petr Šabata 81d24c
 .Xr ssh 1
Petr Šabata 81d24c
diff --git a/sshconnect2.c b/sshconnect2.c
Petr Šabata 81d24c
index af00fb30..03bc87eb 100644
Petr Šabata 81d24c
--- a/sshconnect2.c
Petr Šabata 81d24c
+++ b/sshconnect2.c
Petr Šabata 81d24c
@@ -80,8 +80,6 @@
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* import */
Petr Šabata 81d24c
-extern char *client_version_string;
Petr Šabata 81d24c
-extern char *server_version_string;
Petr Šabata 81d24c
 extern Options options;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
Petr Šabata 81d24c
 	char *s, *all_key;
Petr Šabata 81d24c
 	int r, use_known_hosts_order = 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+	char *orig = NULL, *gss = NULL;
Petr Šabata 81d24c
+	char *gss_host = NULL;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	xxx_host = host;
Petr Šabata 81d24c
 	xxx_hostaddr = hostaddr;
DistroBaker d029bb
 	xxx_conn_info = cinfo;
Petr Šabata 81d24c
@@ -206,6 +209,42 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
DistroBaker d029bb
 		    compat_pkalg_proposal(ssh, options.hostkeyalgorithms);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+	if (options.gss_keyex) {
Petr Šabata 81d24c
+		/* Add the GSSAPI mechanisms currently supported on this
Petr Šabata 81d24c
+		 * client to the key exchange algorithm proposal */
Petr Šabata 81d24c
+		orig = myproposal[PROPOSAL_KEX_ALGS];
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (options.gss_server_identity) {
Petr Šabata 81d24c
+			gss_host = xstrdup(options.gss_server_identity);
Petr Šabata 81d24c
+		} else if (options.gss_trust_dns) {
Petr Šabata 81d24c
+			gss_host = remote_hostname(ssh);
Petr Šabata 81d24c
+			/* Fall back to specified host if we are using proxy command
Petr Šabata 81d24c
+			 * and can not use DNS on that socket */
Petr Šabata 81d24c
+			if (strcmp(gss_host, "UNKNOWN") == 0) {
Petr Šabata 81d24c
+				free(gss_host);
Petr Šabata 81d24c
+				gss_host = xstrdup(host);
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+		} else {
Petr Šabata 81d24c
+			gss_host = xstrdup(host);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		gss = ssh_gssapi_client_mechanisms(gss_host,
Petr Šabata 81d24c
+		    options.gss_client_identity, options.gss_kex_algorithms);
Petr Šabata 81d24c
+		if (gss) {
Petr Šabata 81d24c
+			debug("Offering GSSAPI proposal: %s", gss);
Petr Šabata 81d24c
+			xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
Petr Šabata 81d24c
+			    "%s,%s", gss, orig);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+			/* If we've got GSSAPI algorithms, then we also support the
Petr Šabata 81d24c
+			 * 'null' hostkey, as a last resort */
Petr Šabata 81d24c
+			orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
Petr Šabata 81d24c
+			xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
Petr Šabata 81d24c
+			    "%s,null", orig);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (options.rekey_limit || options.rekey_interval)
Petr Šabata 81d24c
 		ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
Petr Šabata 81d24c
 		    options.rekey_interval);
Petr Šabata 81d24c
@@ -224,16 +256,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
Petr Šabata 81d24c
 # ifdef OPENSSL_HAS_ECC
Petr Šabata 81d24c
 	ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
Petr Šabata 81d24c
 # endif
Petr Šabata 81d24c
-#endif
Petr Šabata 81d24c
+# ifdef GSSAPI
Petr Šabata 81d24c
+	if (options.gss_keyex) {
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
Petr Šabata 81d24c
+		ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+# endif
Petr Šabata 81d24c
+#endif /* WITH_OPENSSL */
Petr Šabata 81d24c
 	ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
DistroBaker d029bb
	ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
Petr Šabata 81d24c
 	ssh->kex->verify_host_key=&verify_host_key_callback;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+	if (options.gss_keyex) {
Petr Šabata 81d24c
+		ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
Petr Šabata 81d24c
+		ssh->kex->gss_trust_dns = options.gss_trust_dns;
Petr Šabata 81d24c
+		ssh->kex->gss_client = options.gss_client_identity;
Petr Šabata 81d24c
+		ssh->kex->gss_host = gss_host;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* remove ext-info from the KEX proposals for rekeying */
Petr Šabata 81d24c
 	myproposal[PROPOSAL_KEX_ALGS] =
DistroBaker d029bb
 	    compat_kex_proposal(ssh, options.kex_algorithms);
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+	/* repair myproposal after it was crumpled by the */
Petr Šabata 81d24c
+	/* ext-info removal above */
Petr Šabata 81d24c
+	if (gss) {
Petr Šabata 81d24c
+		orig = myproposal[PROPOSAL_KEX_ALGS];
Petr Šabata 81d24c
+		xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
Petr Šabata 81d24c
+		    "%s,%s", gss, orig);
Petr Šabata 81d24c
+		free(gss);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
DistroBaker d029bb
 		fatal_r(r, "kex_prop2buf");
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -330,6 +392,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
Petr Šabata 81d24c
 static int input_gssapi_token(int type, u_int32_t, struct ssh *);
Petr Šabata 81d24c
 static int input_gssapi_error(int, u_int32_t, struct ssh *);
Petr Šabata 81d24c
 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
Petr Šabata 81d24c
+static int userauth_gsskeyex(struct ssh *);
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 void	userauth(struct ssh *, char *);
Petr Šabata 81d24c
@@ -346,6 +409,11 @@ static char *authmethods_get(void);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 Authmethod authmethods[] = {
Petr Šabata 81d24c
 #ifdef GSSAPI
Petr Šabata 81d24c
+	{"gssapi-keyex",
Petr Šabata 81d24c
+		userauth_gsskeyex,
Petr Šabata 81d24c
+		NULL,
Petr Šabata 81d24c
+		&options.gss_keyex,
Petr Šabata 81d24c
+		NULL},
Petr Šabata 81d24c
 	{"gssapi-with-mic",
Petr Šabata 81d24c
 		userauth_gssapi,
Petr Šabata 81d24c
 		userauth_gssapi_cleanup,
Petr Šabata 81d24c
@@ -716,12 +784,32 @@ userauth_gssapi(struct ssh *ssh)
Petr Šabata 81d24c
 	OM_uint32 min;
Petr Šabata 81d24c
 	int r, ok = 0;
Petr Šabata 81d24c
 	gss_OID mech = NULL;
Petr Šabata 81d24c
+	char *gss_host = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (options.gss_server_identity) {
Petr Šabata 81d24c
+		gss_host = xstrdup(options.gss_server_identity);
Petr Šabata 81d24c
+	} else if (options.gss_trust_dns) {
Petr Šabata 81d24c
+		gss_host = remote_hostname(ssh);
Petr Šabata 81d24c
+		/* Fall back to specified host if we are using proxy command
Petr Šabata 81d24c
+		 * and can not use DNS on that socket */
Petr Šabata 81d24c
+		if (strcmp(gss_host, "UNKNOWN") == 0) {
Petr Šabata 81d24c
+			free(gss_host);
Petr Šabata 81d24c
+			gss_host = xstrdup(authctxt->host);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		gss_host = xstrdup(authctxt->host);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* Try one GSSAPI method at a time, rather than sending them all at
Petr Šabata 81d24c
 	 * once. */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if (authctxt->gss_supported_mechs == NULL)
Petr Šabata 81d24c
-		gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
Petr Šabata 81d24c
+		if (GSS_ERROR(gss_indicate_mechs(&min,
Petr Šabata 81d24c
+		    &authctxt->gss_supported_mechs))) {
Petr Šabata 81d24c
+			authctxt->gss_supported_mechs = NULL;
Petr Šabata 81d24c
+			free(gss_host);
Petr Šabata 81d24c
+			return 0;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* Check to see whether the mechanism is usable before we offer it */
Petr Šabata 81d24c
 	while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
Petr Šabata 81d24c
@@ -730,13 +811,15 @@ userauth_gssapi(struct ssh *ssh)
Petr Šabata 81d24c
 		    elements[authctxt->mech_tried];
Petr Šabata 81d24c
 		/* My DER encoding requires length<128 */
Petr Šabata 81d24c
 		if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
Petr Šabata 81d24c
-		    mech, authctxt->host)) {
Petr Šabata 81d24c
+		    mech, gss_host, options.gss_client_identity)) {
Petr Šabata 81d24c
 			ok = 1; /* Mechanism works */
Petr Šabata 81d24c
 		} else {
Petr Šabata 81d24c
 			authctxt->mech_tried++;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	free(gss_host);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (!ok || mech == NULL)
Petr Šabata 81d24c
 		return 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -976,6 +1059,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
Petr Šabata 81d24c
 	free(lang);
Petr Šabata 81d24c
 	return r;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+userauth_gsskeyex(struct ssh *ssh)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct sshbuf *b = NULL;
Petr Šabata 81d24c
+	Authctxt *authctxt = ssh->authctxt;
Petr Šabata 81d24c
+	gss_buffer_desc gssbuf;
Petr Šabata 81d24c
+	gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
Petr Šabata 81d24c
+	OM_uint32 ms;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	static int attempt = 0;
Petr Šabata 81d24c
+	if (attempt++ >= 1)
Petr Šabata 81d24c
+		return (0);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss_kex_context == NULL) {
Petr Šabata 81d24c
+		debug("No valid Key exchange context");
Petr Šabata 81d24c
+		return (0);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((b = sshbuf_new()) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_new failed");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
DistroBaker d029bb
+	    "gssapi-keyex", ssh->kex->session_id);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
DistroBaker d029bb
+		fatal_f("sshbuf_mutable_ptr failed");
Petr Šabata 81d24c
+	gssbuf.length = sshbuf_len(b);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
Petr Šabata 81d24c
+		sshbuf_free(b);
Petr Šabata 81d24c
+		return (0);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
Petr Šabata 81d24c
+	    (r = sshpkt_send(ssh)) != 0)
DistroBaker d029bb
+		fatal_fr(r, "parsing");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sshbuf_free(b);
Petr Šabata 81d24c
+	gss_release_buffer(&ms, &mic);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return (1);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 #endif /* GSSAPI */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
diff --git a/sshd.c b/sshd.c
Petr Šabata 81d24c
index 60b2aaf7..d92f03aa 100644
Petr Šabata 81d24c
--- a/sshd.c
Petr Šabata 81d24c
+++ b/sshd.c
Petr Šabata 81d24c
@@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh)
Petr Šabata 81d24c
 	}
DistroBaker d029bb
 	debug3_f("sent %u hostkeys", nkeys);
Petr Šabata 81d24c
 	if (nkeys == 0)
DistroBaker d029bb
-		fatal_f("no hostkeys");
Petr Šabata 81d24c
-	if ((r = sshpkt_send(ssh)) != 0)
DistroBaker d029bb
+		debug3_f("no hostkeys");
Petr Šabata 81d24c
+	else if ((r = sshpkt_send(ssh)) != 0)
Petr Šabata 81d24c
 		sshpkt_fatal(ssh, r, "%s: send", __func__);
Petr Šabata 81d24c
 	sshbuf_free(buf);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
@@ -1852,7 +1852,8 @@ main(int ac, char **av)
Petr Šabata 81d24c
 		free(fp);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	accumulate_host_timing_secret(cfg, NULL);
Petr Šabata 81d24c
-	if (!sensitive_data.have_ssh2_key) {
Petr Šabata 81d24c
+	/* The GSSAPI key exchange can run without a host key */
Petr Šabata 81d24c
+	if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
Petr Šabata 81d24c
 		logit("sshd: no hostkeys available -- exiting.");
Petr Šabata 81d24c
 		exit(1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
@@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh)
Petr Šabata 81d24c
 	myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
DistroBaker d029bb
 	    ssh, list_hostkey_types());
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#if defined(GSSAPI) && defined(WITH_OPENSSL)
Petr Šabata 81d24c
+	{
Petr Šabata 81d24c
+	char *orig;
Petr Šabata 81d24c
+	char *gss = NULL;
Petr Šabata 81d24c
+	char *newstr = NULL;
Petr Šabata 81d24c
+	orig = myproposal[PROPOSAL_KEX_ALGS];
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * If we don't have a host key, then there's no point advertising
Petr Šabata 81d24c
+	 * the other key exchange algorithms
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
Petr Šabata 81d24c
+		orig = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (options.gss_keyex)
Petr Šabata 81d24c
+		gss = ssh_gssapi_server_mechanisms();
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		gss = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (gss && orig)
Petr Šabata 81d24c
+		xasprintf(&newstr, "%s,%s", gss, orig);
Petr Šabata 81d24c
+	else if (gss)
Petr Šabata 81d24c
+		newstr = gss;
Petr Šabata 81d24c
+	else if (orig)
Petr Šabata 81d24c
+		newstr = orig;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/*
Petr Šabata 81d24c
+	 * If we've got GSSAPI mechanisms, then we've got the 'null' host
Petr Šabata 81d24c
+	 * key alg, but we can't tell people about it unless its the only
Petr Šabata 81d24c
+	 * host key algorithm we support
Petr Šabata 81d24c
+	 */
Petr Šabata 81d24c
+	if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
Petr Šabata 81d24c
+		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (newstr)
Petr Šabata 81d24c
+		myproposal[PROPOSAL_KEX_ALGS] = newstr;
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		fatal("No supported key exchange algorithms");
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	/* start key exchange */
Petr Šabata 81d24c
 	if ((r = kex_setup(ssh, myproposal)) != 0)
DistroBaker d029bb
 		fatal_r(r, "kex_setup");
Petr Šabata 81d24c
@@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh)
Petr Šabata 81d24c
 # ifdef OPENSSL_HAS_ECC
Petr Šabata 81d24c
 	kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
Petr Šabata 81d24c
 # endif
Petr Šabata 81d24c
-#endif
Petr Šabata 81d24c
+# ifdef GSSAPI
Petr Šabata 81d24c
+	if (options.gss_keyex) {
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
Petr Šabata 81d24c
+		kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+# endif
Petr Šabata 81d24c
+#endif /* WITH_OPENSSL */
Petr Šabata 81d24c
 	kex->kex[KEX_C25519_SHA256] = kex_gen_server;
DistroBaker d029bb
 	kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
Petr Šabata 81d24c
 	kex->load_host_public_key=&get_hostkey_public_by_type;
Petr Šabata 81d24c
diff --git a/sshd_config b/sshd_config
Petr Šabata 81d24c
index 19b7c91a..2c48105f 100644
Petr Šabata 81d24c
--- a/sshd_config
Petr Šabata 81d24c
+++ b/sshd_config
Petr Šabata 81d24c
@@ -69,6 +69,8 @@ AuthorizedKeysFile	.ssh/authorized_keys
Petr Šabata 81d24c
 # GSSAPI options
Petr Šabata 81d24c
 #GSSAPIAuthentication no
Petr Šabata 81d24c
 #GSSAPICleanupCredentials yes
Petr Šabata 81d24c
+#GSSAPIStrictAcceptorCheck yes
Petr Šabata 81d24c
+#GSSAPIKeyExchange no
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 # Set this to 'yes' to enable PAM authentication, account processing,
Petr Šabata 81d24c
 # and session processing. If this is enabled, PAM authentication will
Petr Šabata 81d24c
diff --git a/sshd_config.5 b/sshd_config.5
Petr Šabata 81d24c
index 70ccea44..f6b41a2f 100644
Petr Šabata 81d24c
--- a/sshd_config.5
Petr Šabata 81d24c
+++ b/sshd_config.5
Petr Šabata 81d24c
@@ -646,6 +646,11 @@ Specifies whether to automatically destroy the user's credentials cache
Petr Šabata 81d24c
 on logout.
Petr Šabata 81d24c
 The default is
Petr Šabata 81d24c
 .Cm yes .
Petr Šabata 81d24c
+.It Cm GSSAPIKeyExchange
Petr Šabata 81d24c
+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
Petr Šabata 81d24c
+doesn't rely on ssh keys to verify host identity.
Petr Šabata 81d24c
+The default is
Petr Šabata 81d24c
+.Cm no .
Petr Šabata 81d24c
 .It Cm GSSAPIStrictAcceptorCheck
Petr Šabata 81d24c
 Determines whether to be strict about the identity of the GSSAPI acceptor
Petr Šabata 81d24c
 a client authenticates against.
Petr Šabata 81d24c
@@ -660,6 +665,32 @@ machine's default store.
Petr Šabata 81d24c
 This facility is provided to assist with operation on multi homed machines.
Petr Šabata 81d24c
 The default is
Petr Šabata 81d24c
 .Cm yes .
Petr Šabata 81d24c
+.It Cm GSSAPIStoreCredentialsOnRekey
Petr Šabata 81d24c
+Controls whether the user's GSSAPI credentials should be updated following a
Petr Šabata 81d24c
+successful connection rekeying. This option can be used to accepted renewed
Petr Šabata 81d24c
+or updated credentials from a compatible client. The default is
Petr Šabata 81d24c
+.Dq no .
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+For this to work
Petr Šabata 81d24c
+.Cm GSSAPIKeyExchange
Petr Šabata 81d24c
+needs to be enabled in the server and also used by the client.
Petr Šabata 81d24c
+.It Cm GSSAPIKexAlgorithms
Petr Šabata 81d24c
+The list of key exchange algorithms that are accepted by GSSAPI
Petr Šabata 81d24c
+key exchange. Possible values are
Petr Šabata 81d24c
+.Bd -literal -offset 3n
Petr Šabata 81d24c
+gss-gex-sha1-,
Petr Šabata 81d24c
+gss-group1-sha1-,
Petr Šabata 81d24c
+gss-group14-sha1-,
Petr Šabata 81d24c
+gss-group14-sha256-,
Petr Šabata 81d24c
+gss-group16-sha512-,
Petr Šabata 81d24c
+gss-nistp256-sha256-,
Petr Šabata 81d24c
+gss-curve25519-sha256-
Petr Šabata 81d24c
+.Ed
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+The default is
Petr Šabata 81d24c
+.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
Petr Šabata 81d24c
+gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
Petr Šabata 81d24c
+This option only applies to connections using GSSAPI.
DistroBaker d029bb
 .It Cm HostbasedAcceptedAlgorithms
DistroBaker d029bb
 Specifies the signature algorithms that will be accepted for hostbased
DistroBaker d029bb
 authentication as a list of comma-separated patterns.
Petr Šabata 81d24c
diff --git a/sshkey.c b/sshkey.c
Petr Šabata 81d24c
index 57995ee6..fd5b7724 100644
Petr Šabata 81d24c
--- a/sshkey.c
Petr Šabata 81d24c
+++ b/sshkey.c
Petr Šabata 81d24c
@@ -154,6 +154,7 @@ static const struct keytype keytypes[] = {
Petr Šabata 81d24c
 	    KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 },
Petr Šabata 81d24c
 # endif /* OPENSSL_HAS_ECC */
Petr Šabata 81d24c
 #endif /* WITH_OPENSSL */
Petr Šabata 81d24c
+	{ "null", "null", NULL, KEY_NULL, 0, 0, 0 },
Petr Šabata 81d24c
 	{ NULL, NULL, NULL, -1, -1, 0, 0 }
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -255,7 +256,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
Petr Šabata 81d24c
 	const struct keytype *kt;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	for (kt = keytypes; kt->type != -1; kt++) {
Petr Šabata 81d24c
-		if (kt->name == NULL)
Petr Šabata 81d24c
+		if (kt->name == NULL || kt->type == KEY_NULL)
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
 		if (!include_sigonly && kt->sigonly)
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
diff --git a/sshkey.h b/sshkey.h
Petr Šabata 81d24c
index 71a3fddc..37a43a67 100644
Petr Šabata 81d24c
--- a/sshkey.h
Petr Šabata 81d24c
+++ b/sshkey.h
Petr Šabata 81d24c
@@ -69,6 +69,7 @@ enum sshkey_types {
Petr Šabata 81d24c
 	KEY_ECDSA_SK_CERT,
Petr Šabata 81d24c
 	KEY_ED25519_SK,
Petr Šabata 81d24c
 	KEY_ED25519_SK_CERT,
Petr Šabata 81d24c
+	KEY_NULL,
Petr Šabata 81d24c
 	KEY_UNSPEC
Petr Šabata 81d24c
 };
Petr Šabata 81d24c