vishalmishra434 / rpms / openssh

Forked from rpms/openssh a month ago
Clone
Dmitry Belyavskiy 089d79
diff --color -ruNp a/gss-genr.c b/gss-genr.c
Dmitry Belyavskiy 089d79
--- a/gss-genr.c	2024-05-16 15:49:43.999411060 +0200
Dmitry Belyavskiy 089d79
+++ b/gss-genr.c	2024-06-26 12:17:55.586856954 +0200
Dmitry Belyavskiy 089d79
@@ -346,6 +346,7 @@ ssh_gssapi_build_ctx(Gssctxt **ctx)
Dmitry Belyavskiy 089d79
 	(*ctx)->creds = GSS_C_NO_CREDENTIAL;
Dmitry Belyavskiy 089d79
 	(*ctx)->client = GSS_C_NO_NAME;
Dmitry Belyavskiy 089d79
 	(*ctx)->client_creds = GSS_C_NO_CREDENTIAL;
Dmitry Belyavskiy 089d79
+	(*ctx)->first = 1;
Dmitry Belyavskiy 089d79
 }
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 /* Delete our context, providing it has been built correctly */
Dmitry Belyavskiy 089d79
@@ -371,6 +372,12 @@ ssh_gssapi_delete_ctx(Gssctxt **ctx)
Dmitry Belyavskiy 089d79
 		gss_release_name(&ms, &(*ctx)->client);
Dmitry Belyavskiy 089d79
 	if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL)
Dmitry Belyavskiy 089d79
 		gss_release_cred(&ms, &(*ctx)->client_creds);
Dmitry Belyavskiy 089d79
+	sshbuf_free((*ctx)->shared_secret);
Dmitry Belyavskiy 089d79
+	sshbuf_free((*ctx)->server_pubkey);
Dmitry Belyavskiy 089d79
+	sshbuf_free((*ctx)->server_host_key_blob);
Dmitry Belyavskiy 089d79
+	sshbuf_free((*ctx)->server_blob);
Dmitry Belyavskiy 089d79
+	explicit_bzero((*ctx)->hash, sizeof((*ctx)->hash));
Dmitry Belyavskiy 089d79
+        BN_clear_free((*ctx)->dh_client_pub);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	free(*ctx);
Dmitry Belyavskiy 089d79
 	*ctx = NULL;
Dmitry Belyavskiy 089d79
diff --color -ruNp a/kexgssc.c b/kexgssc.c
Dmitry Belyavskiy 089d79
--- a/kexgssc.c	2024-05-16 15:49:43.820407648 +0200
Dmitry Belyavskiy 089d79
+++ b/kexgssc.c	2024-07-02 16:26:25.628746744 +0200
Dmitry Belyavskiy 089d79
@@ -47,566 +47,658 @@
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 #include "ssh-gss.h"
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-int
Dmitry Belyavskiy 089d79
-kexgss_client(struct ssh *ssh)
Dmitry Belyavskiy 089d79
+static int input_kexgss_hostkey(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgss_continue(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgss_complete(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgss_error(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgssgex_group(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgssgex_continue(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgssgex_complete(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+kexgss_final(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 {
Dmitry Belyavskiy 089d79
 	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
-	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
Dmitry Belyavskiy 089d79
-	    recv_tok = GSS_C_EMPTY_BUFFER,
Dmitry Belyavskiy 089d79
-	    gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
Dmitry Belyavskiy 089d79
-	Gssctxt *ctxt;
Dmitry Belyavskiy 089d79
-	OM_uint32 maj_status, min_status, ret_flags;
Dmitry Belyavskiy 089d79
-	struct sshbuf *server_blob = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *server_host_key_blob = NULL;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
 	struct sshbuf *empty = NULL;
Dmitry Belyavskiy 089d79
-	u_char *msg;
Dmitry Belyavskiy 089d79
-	int type = 0;
Dmitry Belyavskiy 089d79
-	int first = 1;
Dmitry Belyavskiy 089d79
+	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
 	u_char hash[SSH_DIGEST_MAX_LENGTH];
Dmitry Belyavskiy 089d79
 	size_t hashlen;
Dmitry Belyavskiy 089d79
-	u_char c;
Dmitry Belyavskiy 089d79
 	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	/* Initialise our GSSAPI world */
Dmitry Belyavskiy 089d79
-	ssh_gssapi_build_ctx(&ctxt);
Dmitry Belyavskiy 089d79
-	if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
Dmitry Belyavskiy 089d79
-	    == GSS_C_NO_OID)
Dmitry Belyavskiy 089d79
-		fatal("Couldn't identify host exchange");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	if (ssh_gssapi_import_name(ctxt, kex->gss_host))
Dmitry Belyavskiy 089d79
-		fatal("Couldn't import hostname");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	if (kex->gss_client &&
Dmitry Belyavskiy 089d79
-	    ssh_gssapi_client_identity(ctxt, kex->gss_client))
Dmitry Belyavskiy 089d79
-		fatal("Couldn't acquire client credentials");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	/* Step 1 */
Dmitry Belyavskiy 089d79
-	switch (kex->kex_type) {
Dmitry Belyavskiy 089d79
-	case KEX_GSS_GRP1_SHA1:
Dmitry Belyavskiy 089d79
-	case KEX_GSS_GRP14_SHA1:
Dmitry Belyavskiy 089d79
-	case KEX_GSS_GRP14_SHA256:
Dmitry Belyavskiy 089d79
-	case KEX_GSS_GRP16_SHA512:
Dmitry Belyavskiy 089d79
-		r = kex_dh_keypair(kex);
Dmitry Belyavskiy 089d79
-		break;
Dmitry Belyavskiy 089d79
-	case KEX_GSS_NISTP256_SHA256:
Dmitry Belyavskiy 089d79
-		r = kex_ecdh_keypair(kex);
Dmitry Belyavskiy 089d79
-		break;
Dmitry Belyavskiy 089d79
-	case KEX_GSS_C25519_SHA256:
Dmitry Belyavskiy 089d79
-		r = kex_c25519_keypair(kex);
Dmitry Belyavskiy 089d79
-		break;
Dmitry Belyavskiy 089d79
-	default:
Dmitry Belyavskiy 089d79
-		fatal_f("Unexpected KEX type %d", kex->kex_type);
Dmitry Belyavskiy 089d79
-	}
Dmitry Belyavskiy 089d79
-	if (r != 0) {
Dmitry Belyavskiy 089d79
-		ssh_gssapi_delete_ctx(&ctxt);
Dmitry Belyavskiy 089d79
-		return r;
Dmitry Belyavskiy 089d79
-	}
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	token_ptr = GSS_C_NO_BUFFER;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	do {
Dmitry Belyavskiy 089d79
-		debug("Calling gss_init_sec_context");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		maj_status = ssh_gssapi_init_ctx(ctxt,
Dmitry Belyavskiy 089d79
-		    kex->gss_deleg_creds, token_ptr, &send_tok,
Dmitry Belyavskiy 089d79
-		    &ret_flags);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		if (GSS_ERROR(maj_status)) {
Dmitry Belyavskiy 089d79
-			/* XXX Useles code: Missing send? */
Dmitry Belyavskiy 089d79
-			if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_start(ssh,
Dmitry Belyavskiy 089d79
-				        SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_string(ssh, send_tok.value,
Dmitry Belyavskiy 089d79
-				        send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			fatal("gss_init_context failed");
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		/* If we've got an old receive buffer get rid of it */
Dmitry Belyavskiy 089d79
-		if (token_ptr != GSS_C_NO_BUFFER)
Dmitry Belyavskiy 089d79
-			gss_release_buffer(&min_status, &recv_tok);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		if (maj_status == GSS_S_COMPLETE) {
Dmitry Belyavskiy 089d79
-			/* If mutual state flag is not true, kex fails */
Dmitry Belyavskiy 089d79
-			if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
-				fatal("Mutual authentication failed");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			/* If integ avail flag is not true kex fails */
Dmitry Belyavskiy 089d79
-			if (!(ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
-				fatal("Integrity check failed");
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		/*
Dmitry Belyavskiy 089d79
-		 * If we have data to send, then the last message that we
Dmitry Belyavskiy 089d79
-		 * received cannot have been a 'complete'.
Dmitry Belyavskiy 089d79
-		 */
Dmitry Belyavskiy 089d79
-		if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
-			if (first) {
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_string(ssh, send_tok.value,
Dmitry Belyavskiy 089d79
-				        send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("failed to construct packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				first = 0;
Dmitry Belyavskiy 089d79
-			} else {
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_string(ssh, send_tok.value,
Dmitry Belyavskiy 089d79
-				        send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("failed to construct packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			if ((r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("failed to send packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			gss_release_buffer(&min_status, &send_tok);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			/* If we've sent them data, they should reply */
Dmitry Belyavskiy 089d79
-			do {
Dmitry Belyavskiy 089d79
-				type = ssh_packet_read(ssh);
Dmitry Belyavskiy 089d79
-				if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
Dmitry Belyavskiy 089d79
-					u_char *tmp = NULL;
Dmitry Belyavskiy 089d79
-					size_t tmp_len = 0;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-					debug("Received KEXGSS_HOSTKEY");
Dmitry Belyavskiy 089d79
-					if (server_host_key_blob)
Dmitry Belyavskiy 089d79
-						fatal("Server host key received more than once");
Dmitry Belyavskiy 089d79
-					if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Dmitry Belyavskiy 089d79
-						fatal("Failed to read server host key: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-					if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
Dmitry Belyavskiy 089d79
-						fatal("sshbuf_from failed");
Dmitry Belyavskiy 089d79
-				}
Dmitry Belyavskiy 089d79
-			} while (type == SSH2_MSG_KEXGSS_HOSTKEY);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			switch (type) {
Dmitry Belyavskiy 089d79
-			case SSH2_MSG_KEXGSS_CONTINUE:
Dmitry Belyavskiy 089d79
-				debug("Received GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
-				if (maj_status == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-					fatal("GSSAPI Continue received from server when complete");
Dmitry Belyavskiy 089d79
-				if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-				        &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("Failed to read token: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			case SSH2_MSG_KEXGSS_COMPLETE:
Dmitry Belyavskiy 089d79
-				debug("Received GSSAPI_COMPLETE");
Dmitry Belyavskiy 089d79
-				if (msg_tok.value != NULL)
Dmitry Belyavskiy 089d79
-				        fatal("Received GSSAPI_COMPLETE twice?");
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-				        &msg_tok)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("Failed to read message: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-				/* Is there a token included? */
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				if (c) {
Dmitry Belyavskiy 089d79
-					if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
Dmitry Belyavskiy 089d79
-					    ssh, &recv_tok)) != 0)
Dmitry Belyavskiy 089d79
-						fatal("Failed to read token: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-					/* If we're already complete - protocol error */
Dmitry Belyavskiy 089d79
-					if (maj_status == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-						sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Dmitry Belyavskiy 089d79
-				} else {
Dmitry Belyavskiy 089d79
-					/* No token included */
Dmitry Belyavskiy 089d79
-					if (maj_status != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-						sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Dmitry Belyavskiy 089d79
-				}
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_get_end(ssh)) != 0) {
Dmitry Belyavskiy 089d79
-					fatal("Expecting end of packet.");
Dmitry Belyavskiy 089d79
-				}
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			case SSH2_MSG_KEXGSS_ERROR:
Dmitry Belyavskiy 089d79
-				debug("Received Error");
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt_get failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				fatal("GSSAPI Error: \n%.400s", msg);
Dmitry Belyavskiy 089d79
-			default:
Dmitry Belyavskiy 089d79
-				sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
Dmitry Belyavskiy 089d79
-				    type);
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			token_ptr = &recv_tok;
Dmitry Belyavskiy 089d79
-		} else {
Dmitry Belyavskiy 089d79
-			/* No data, and not complete */
Dmitry Belyavskiy 089d79
-			if (maj_status != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-				fatal("Not complete, and no token output");
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
-	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
 	/*
Dmitry Belyavskiy 089d79
 	 * We _must_ have received a COMPLETE message in reply from the
Dmitry Belyavskiy 089d79
 	 * server, which will have set server_blob and msg_tok
Dmitry Belyavskiy 089d79
 	 */
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (type != SSH2_MSG_KEXGSS_COMPLETE)
Dmitry Belyavskiy 089d79
-		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
 	/* compute shared secret */
Dmitry Belyavskiy 089d79
 	switch (kex->kex_type) {
Dmitry Belyavskiy 089d79
 	case KEX_GSS_GRP1_SHA1:
Dmitry Belyavskiy 089d79
 	case KEX_GSS_GRP14_SHA1:
Dmitry Belyavskiy 089d79
 	case KEX_GSS_GRP14_SHA256:
Dmitry Belyavskiy 089d79
 	case KEX_GSS_GRP16_SHA512:
Dmitry Belyavskiy 089d79
-		r = kex_dh_dec(kex, server_blob, &shared_secret);
Dmitry Belyavskiy 089d79
+		r = kex_dh_dec(kex, gss->server_blob, &shared_secret);
Dmitry Belyavskiy 089d79
 		break;
Dmitry Belyavskiy 089d79
 	case KEX_GSS_C25519_SHA256:
Dmitry Belyavskiy 089d79
-		if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
Dmitry Belyavskiy 089d79
+		if (sshbuf_ptr(gss->server_blob)[sshbuf_len(gss->server_blob)] & 0x80)
Dmitry Belyavskiy 089d79
 			fatal("The received key has MSB of last octet set!");
Dmitry Belyavskiy 089d79
-		r = kex_c25519_dec(kex, server_blob, &shared_secret);
Dmitry Belyavskiy 089d79
+		r = kex_c25519_dec(kex, gss->server_blob, &shared_secret);
Dmitry Belyavskiy 089d79
 		break;
Dmitry Belyavskiy 089d79
 	case KEX_GSS_NISTP256_SHA256:
Dmitry Belyavskiy 089d79
-		if (sshbuf_len(server_blob) != 65)
Dmitry Belyavskiy 089d79
-			fatal("The received NIST-P256 key did not match"
Dmitry Belyavskiy 089d79
-			    "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
Dmitry Belyavskiy 089d79
+		if (sshbuf_len(gss->server_blob) != 65)
Dmitry Belyavskiy 089d79
+			fatal("The received NIST-P256 key did not match "
Dmitry Belyavskiy 089d79
+			      "expected length (expected 65, got %zu)",
Dmitry Belyavskiy 089d79
+			      sshbuf_len(gss->server_blob));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
Dmitry Belyavskiy 089d79
+		if (sshbuf_ptr(gss->server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
Dmitry Belyavskiy 089d79
 			fatal("The received NIST-P256 key does not have first octet 0x04");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		r = kex_ecdh_dec(kex, server_blob, &shared_secret);
Dmitry Belyavskiy 089d79
+		r = kex_ecdh_dec(kex, gss->server_blob, &shared_secret);
Dmitry Belyavskiy 089d79
 		break;
Dmitry Belyavskiy 089d79
 	default:
Dmitry Belyavskiy 089d79
 		r = SSH_ERR_INVALID_ARGUMENT;
Dmitry Belyavskiy 089d79
 		break;
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
-	if (r != 0)
Dmitry Belyavskiy 089d79
+	if (r != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if ((empty = sshbuf_new()) == NULL) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		r = SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	hashlen = sizeof(hash);
Dmitry Belyavskiy 089d79
-	if ((r = kex_gen_hash(
Dmitry Belyavskiy 089d79
-	    kex->hash_alg,
Dmitry Belyavskiy 089d79
-	    kex->client_version,
Dmitry Belyavskiy 089d79
-	    kex->server_version,
Dmitry Belyavskiy 089d79
-	    kex->my,
Dmitry Belyavskiy 089d79
-	    kex->peer,
Dmitry Belyavskiy 089d79
-	    (server_host_key_blob ? server_host_key_blob : empty),
Dmitry Belyavskiy 089d79
-	    kex->client_pub,
Dmitry Belyavskiy 089d79
-	    server_blob,
Dmitry Belyavskiy 089d79
-	    shared_secret,
Dmitry Belyavskiy 089d79
-	    hash, &hashlen)) != 0)
Dmitry Belyavskiy 089d79
+	r = kex_gen_hash(kex->hash_alg, kex->client_version,
Dmitry Belyavskiy 089d79
+			 kex->server_version, kex->my, kex->peer,
Dmitry Belyavskiy 089d79
+			 (gss->server_host_key_blob ? gss->server_host_key_blob : empty),
Dmitry Belyavskiy 089d79
+			 kex->client_pub, gss->server_blob, shared_secret,
Dmitry Belyavskiy 089d79
+			 hash, &hashlen);
Dmitry Belyavskiy 089d79
+	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
+	if (r != 0)
Dmitry Belyavskiy 089d79
 		fatal_f("Unexpected KEX type %d", kex->kex_type);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gssbuf.value = hash;
Dmitry Belyavskiy 089d79
-	gssbuf.length = hashlen;
Dmitry Belyavskiy 089d79
+	gss->buf.value = hash;
Dmitry Belyavskiy 089d79
+	gss->buf.length = hashlen;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Verify that the hash matches the MIC we just got. */
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(ssh_gssapi_checkmic(gss, &gss->buf, &gss->msg_tok)))
Dmitry Belyavskiy 089d79
 		sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gss_release_buffer(&min_status, &msg_tok);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, &gss->msg_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (kex->gss_deleg_creds)
Dmitry Belyavskiy 089d79
-		ssh_gssapi_credentials_updated(ctxt);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_credentials_updated(gss);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (gss_kex_context == NULL)
Dmitry Belyavskiy 089d79
-		gss_kex_context = ctxt;
Dmitry Belyavskiy 089d79
+		gss_kex_context = gss;
Dmitry Belyavskiy 089d79
 	else
Dmitry Belyavskiy 089d79
-		ssh_gssapi_delete_ctx(&ctxt);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Dmitry Belyavskiy 089d79
 		r = kex_send_newkeys(ssh);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+	if (kex->gss != NULL) {
Dmitry Belyavskiy 089d79
+		sshbuf_free(gss->server_host_key_blob);
Dmitry Belyavskiy 089d79
+		gss->server_host_key_blob = NULL;
Dmitry Belyavskiy 089d79
+		sshbuf_free(gss->server_blob);
Dmitry Belyavskiy 089d79
+		gss->server_blob = NULL;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 out:
Dmitry Belyavskiy 089d79
-	explicit_bzero(hash, sizeof(hash));
Dmitry Belyavskiy 089d79
 	explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
Dmitry Belyavskiy 089d79
-	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
-	sshbuf_free(server_host_key_blob);
Dmitry Belyavskiy 089d79
-	sshbuf_free(server_blob);
Dmitry Belyavskiy 089d79
+	explicit_bzero(hash, sizeof(hash));
Dmitry Belyavskiy 089d79
 	sshbuf_free(shared_secret);
Dmitry Belyavskiy 089d79
 	sshbuf_free(kex->client_pub);
Dmitry Belyavskiy 089d79
 	kex->client_pub = NULL;
Dmitry Belyavskiy 089d79
 	return r;
Dmitry Belyavskiy 089d79
 }
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+kexgss_init_ctx(struct ssh *ssh,
Dmitry Belyavskiy 089d79
+		gss_buffer_desc *token_ptr)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	OM_uint32 ret_flags;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Calling gss_init_sec_context");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	gss->major = ssh_gssapi_init_ctx(gss, kex->gss_deleg_creds,
Dmitry Belyavskiy 089d79
+					 token_ptr, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(gss->major)) {
Dmitry Belyavskiy 089d79
+		/* XXX Useless code: Missing send? */
Dmitry Belyavskiy 089d79
+		if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
+				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		}
Dmitry Belyavskiy 089d79
+		fatal("gss_init_context failed");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* If we've got an old receive buffer get rid of it */
Dmitry Belyavskiy 089d79
+	if (token_ptr != GSS_C_NO_BUFFER)
Dmitry Belyavskiy 089d79
+		gss_release_buffer(&gss->minor, token_ptr);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (gss->major == GSS_S_COMPLETE) {
Dmitry Belyavskiy 089d79
+		/* If mutual state flag is not true, kex fails */
Dmitry Belyavskiy 089d79
+		if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
+			fatal("Mutual authentication failed");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+		/* If integ avail flag is not true kex fails */
Dmitry Belyavskiy 089d79
+		if (!(ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
+			fatal("Integrity check failed");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/*
Dmitry Belyavskiy 089d79
+	 * If we have data to send, then the last message that we
Dmitry Belyavskiy 089d79
+	 * received cannot have been a 'complete'.
Dmitry Belyavskiy 089d79
+	 */
Dmitry Belyavskiy 089d79
+	if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
+		if (gss->first) {
Dmitry Belyavskiy 089d79
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
Dmitry Belyavskiy 089d79
+				fatal("failed to construct packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+			gss->first = 0;
Dmitry Belyavskiy 089d79
+		} else {
Dmitry Belyavskiy 089d79
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
+				fatal("failed to construct packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		}
Dmitry Belyavskiy 089d79
+		if ((r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
+			fatal("failed to send packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		gss_release_buffer(&gss->minor, &send_tok);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+		/* If we've sent them data, they should reply */
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, &input_kexgss_hostkey);
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgss_continue);
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, &input_kexgss_complete);
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, &input_kexgss_error);
Dmitry Belyavskiy 089d79
+		return 0;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	/* No data, and not complete */
Dmitry Belyavskiy 089d79
+	if (gss->major != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+		fatal("Not complete, and no token output");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if  (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return kexgss_init_ctx(ssh, token_ptr);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgss_final(ssh);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 int
Dmitry Belyavskiy 089d79
-kexgssgex_client(struct ssh *ssh)
Dmitry Belyavskiy 089d79
+kexgss_client(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 {
Dmitry Belyavskiy 089d79
 	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
-	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
Dmitry Belyavskiy 089d79
-	    recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
Dmitry Belyavskiy 089d79
-            msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
Dmitry Belyavskiy 089d79
-	Gssctxt *ctxt;
Dmitry Belyavskiy 089d79
-	OM_uint32 maj_status, min_status, ret_flags;
Dmitry Belyavskiy 089d79
-	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
-	BIGNUM *p = NULL;
Dmitry Belyavskiy 089d79
-	BIGNUM *g = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *buf = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *server_host_key_blob = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *server_blob = NULL;
Dmitry Belyavskiy 089d79
-	BIGNUM *dh_server_pub = NULL;
Dmitry Belyavskiy 089d79
-	u_char *msg;
Dmitry Belyavskiy 089d79
-	int type = 0;
Dmitry Belyavskiy 089d79
-	int first = 1;
Dmitry Belyavskiy 089d79
-	u_char hash[SSH_DIGEST_MAX_LENGTH];
Dmitry Belyavskiy 089d79
-	size_t hashlen;
Dmitry Belyavskiy 089d79
-	const BIGNUM *pub_key, *dh_p, *dh_g;
Dmitry Belyavskiy 089d79
-	int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
Dmitry Belyavskiy 089d79
-	struct sshbuf *empty = NULL;
Dmitry Belyavskiy 089d79
-	u_char c;
Dmitry Belyavskiy 089d79
 	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Initialise our GSSAPI world */
Dmitry Belyavskiy 089d79
-	ssh_gssapi_build_ctx(&ctxt);
Dmitry Belyavskiy 089d79
-	if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
Dmitry Belyavskiy 089d79
-	    == GSS_C_NO_OID)
Dmitry Belyavskiy 089d79
+	ssh_gssapi_build_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+	if (ssh_gssapi_id_kex(kex->gss, kex->name, kex->kex_type) == GSS_C_NO_OID)
Dmitry Belyavskiy 089d79
 		fatal("Couldn't identify host exchange");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (ssh_gssapi_import_name(ctxt, kex->gss_host))
Dmitry Belyavskiy 089d79
+	if (ssh_gssapi_import_name(kex->gss, kex->gss_host))
Dmitry Belyavskiy 089d79
 		fatal("Couldn't import hostname");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (kex->gss_client &&
Dmitry Belyavskiy 089d79
-	    ssh_gssapi_client_identity(ctxt, kex->gss_client))
Dmitry Belyavskiy 089d79
+	    ssh_gssapi_client_identity(kex->gss, kex->gss_client))
Dmitry Belyavskiy 089d79
 		fatal("Couldn't acquire client credentials");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	debug("Doing group exchange");
Dmitry Belyavskiy 089d79
-	nbits = dh_estimate(kex->dh_need * 8);
Dmitry Belyavskiy 089d79
+	/* Step 1 */
Dmitry Belyavskiy 089d79
+	switch (kex->kex_type) {
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP1_SHA1:
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP14_SHA1:
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP14_SHA256:
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP16_SHA512:
Dmitry Belyavskiy 089d79
+		r = kex_dh_keypair(kex);
Dmitry Belyavskiy 089d79
+		break;
Dmitry Belyavskiy 089d79
+	case KEX_GSS_NISTP256_SHA256:
Dmitry Belyavskiy 089d79
+		r = kex_ecdh_keypair(kex);
Dmitry Belyavskiy 089d79
+		break;
Dmitry Belyavskiy 089d79
+	case KEX_GSS_C25519_SHA256:
Dmitry Belyavskiy 089d79
+		r = kex_c25519_keypair(kex);
Dmitry Belyavskiy 089d79
+		break;
Dmitry Belyavskiy 089d79
+	default:
Dmitry Belyavskiy 089d79
+		fatal_f("Unexpected KEX type %d", kex->kex_type);
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	if (r != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		return r;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	return kexgss_init_ctx(ssh, GSS_C_NO_BUFFER);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	kex->min = DH_GRP_MIN;
Dmitry Belyavskiy 089d79
-	kex->max = DH_GRP_MAX;
Dmitry Belyavskiy 089d79
-	kex->nbits = nbits;
Dmitry Belyavskiy 089d79
-	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_put_u32(ssh, min)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_put_u32(ssh, max)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("Failed to construct a packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgss_hostkey(int type,
Dmitry Belyavskiy 089d79
+		     u_int32_t seq,
Dmitry Belyavskiy 089d79
+		     struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	u_char *tmp = NULL;
Dmitry Belyavskiy 089d79
+	size_t tmp_len = 0;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Received KEXGSS_HOSTKEY");
Dmitry Belyavskiy 089d79
+	if (gss->server_host_key_blob)
Dmitry Belyavskiy 089d79
+		fatal("Server host key received more than once");
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Failed to read server host key: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	if ((gss->server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
Dmitry Belyavskiy 089d79
+		fatal("sshbuf_from failed");
Dmitry Belyavskiy 089d79
+	return 0;
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("Error: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgss_continue(int type,
Dmitry Belyavskiy 089d79
+		      u_int32_t seq,
Dmitry Belyavskiy 089d79
+		      struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Received GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
+	if (gss->major == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+		fatal("GSSAPI Continue received from server when complete");
Dmitry Belyavskiy 089d79
+	if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
 	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		fatal("Failed to read token: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	if  (!(gss->major & GSS_S_CONTINUE_NEEDED))
Dmitry Belyavskiy 089d79
+		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Dmitry Belyavskiy 089d79
+	return kexgss_init_ctx(ssh, &recv_tok);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (BN_num_bits(p) < min || BN_num_bits(p) > max)
Dmitry Belyavskiy 089d79
-		fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
Dmitry Belyavskiy 089d79
-		    min, BN_num_bits(p), max);
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgss_complete(int type,
Dmitry Belyavskiy 089d79
+		      u_int32_t seq,
Dmitry Belyavskiy 089d79
+		      struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	u_char c;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if ((kex->dh = dh_new_group(g, p)) == NULL)
Dmitry Belyavskiy 089d79
-		fatal("dn_new_group() failed");
Dmitry Belyavskiy 089d79
-	p = g = NULL; /* belong to kex->dh now */
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Received GSSAPI_COMPLETE");
Dmitry Belyavskiy 089d79
+	if (gss->msg_tok.value != NULL)
Dmitry Belyavskiy 089d79
+	        fatal("Received GSSAPI_COMPLETE twice?");
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_getb_froms(ssh, &gss->server_blob)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &gss->msg_tok)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Failed to read message: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Is there a token included? */
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	if (c) {
Dmitry Belyavskiy 089d79
+		if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0)
Dmitry Belyavskiy 089d79
+			fatal("Failed to read token: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		/* If we're already complete - protocol error */
Dmitry Belyavskiy 089d79
+		if (gss->major == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+			sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Dmitry Belyavskiy 089d79
+	} else {
Dmitry Belyavskiy 089d79
+		if (gss->major != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+			sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Expecting end of packet.");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
Dmitry Belyavskiy 089d79
-		goto out;
Dmitry Belyavskiy 089d79
-	DH_get0_key(kex->dh, &pub_key, NULL);
Dmitry Belyavskiy 089d79
+	if  (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return kexgss_init_ctx(ssh, &recv_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	token_ptr = GSS_C_NO_BUFFER;
Dmitry Belyavskiy 089d79
+	return kexgss_final(ssh);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	do {
Dmitry Belyavskiy 089d79
-		/* Step 2 - call GSS_Init_sec_context() */
Dmitry Belyavskiy 089d79
-		debug("Calling gss_init_sec_context");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		maj_status = ssh_gssapi_init_ctx(ctxt,
Dmitry Belyavskiy 089d79
-		    kex->gss_deleg_creds, token_ptr, &send_tok,
Dmitry Belyavskiy 089d79
-		    &ret_flags);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		if (GSS_ERROR(maj_status)) {
Dmitry Belyavskiy 089d79
-			/* XXX Useles code: Missing send? */
Dmitry Belyavskiy 089d79
-			if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_start(ssh,
Dmitry Belyavskiy 089d79
-				        SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_string(ssh, send_tok.value,
Dmitry Belyavskiy 089d79
-				        send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			fatal("gss_init_context failed");
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgss_error(int type,
Dmitry Belyavskiy 089d79
+		   u_int32_t seq,
Dmitry Belyavskiy 089d79
+		   struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	u_char *msg;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		/* If we've got an old receive buffer get rid of it */
Dmitry Belyavskiy 089d79
-		if (token_ptr != GSS_C_NO_BUFFER)
Dmitry Belyavskiy 089d79
-			gss_release_buffer(&min_status, &recv_tok);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-		if (maj_status == GSS_S_COMPLETE) {
Dmitry Belyavskiy 089d79
-			/* If mutual state flag is not true, kex fails */
Dmitry Belyavskiy 089d79
-			if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
-				fatal("Mutual authentication failed");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			/* If integ avail flag is not true kex fails */
Dmitry Belyavskiy 089d79
-			if (!(ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
-				fatal("Integrity check failed");
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
+	debug("Received Error");
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_u32(ssh, &gss->major)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_u32(ssh, &gss->minor)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt_get failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	fatal("GSSAPI Error: \n%.400s", msg);
Dmitry Belyavskiy 089d79
+	return 0;
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		/*
Dmitry Belyavskiy 089d79
-		 * If we have data to send, then the last message that we
Dmitry Belyavskiy 089d79
-		 * received cannot have been a 'complete'.
Dmitry Belyavskiy 089d79
-		 */
Dmitry Belyavskiy 089d79
-		if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
-			if (first) {
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_string(ssh, send_tok.value,
Dmitry Belyavskiy 089d79
-				        send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				first = 0;
Dmitry Belyavskiy 089d79
-			} else {
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_put_string(ssh,send_tok.value,
Dmitry Belyavskiy 089d79
-				        send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			if ((r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt_send failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			gss_release_buffer(&min_status, &send_tok);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			/* If we've sent them data, they should reply */
Dmitry Belyavskiy 089d79
-			do {
Dmitry Belyavskiy 089d79
-				type = ssh_packet_read(ssh);
Dmitry Belyavskiy 089d79
-				if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
Dmitry Belyavskiy 089d79
-					u_char *tmp = NULL;
Dmitry Belyavskiy 089d79
-					size_t tmp_len = 0;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-					debug("Received KEXGSS_HOSTKEY");
Dmitry Belyavskiy 089d79
-					if (server_host_key_blob)
Dmitry Belyavskiy 089d79
-						fatal("Server host key received more than once");
Dmitry Belyavskiy 089d79
-					if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0)
Dmitry Belyavskiy 089d79
-						fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-					if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL)
Dmitry Belyavskiy 089d79
-						fatal("sshbuf_from failed");
Dmitry Belyavskiy 089d79
-				}
Dmitry Belyavskiy 089d79
-			} while (type == SSH2_MSG_KEXGSS_HOSTKEY);
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			switch (type) {
Dmitry Belyavskiy 089d79
-			case SSH2_MSG_KEXGSS_CONTINUE:
Dmitry Belyavskiy 089d79
-				debug("Received GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
-				if (maj_status == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-					fatal("GSSAPI Continue received from server when complete");
Dmitry Belyavskiy 089d79
-				if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-				        &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			case SSH2_MSG_KEXGSS_COMPLETE:
Dmitry Belyavskiy 089d79
-				debug("Received GSSAPI_COMPLETE");
Dmitry Belyavskiy 089d79
-				if (msg_tok.value != NULL)
Dmitry Belyavskiy 089d79
-				        fatal("Received GSSAPI_COMPLETE twice?");
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-				        &msg_tok)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-				/* Is there a token included? */
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				if (c) {
Dmitry Belyavskiy 089d79
-					if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
Dmitry Belyavskiy 089d79
-					        ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-					    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-						fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-					/* If we're already complete - protocol error */
Dmitry Belyavskiy 089d79
-					if (maj_status == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-						sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Dmitry Belyavskiy 089d79
-				} else {
Dmitry Belyavskiy 089d79
-					/* No token included */
Dmitry Belyavskiy 089d79
-					if (maj_status != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-						sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Dmitry Belyavskiy 089d79
-				}
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			case SSH2_MSG_KEXGSS_ERROR:
Dmitry Belyavskiy 089d79
-				debug("Received Error");
Dmitry Belyavskiy 089d79
-				if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
Dmitry Belyavskiy 089d79
-				    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-					fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-				fatal("GSSAPI Error: \n%.400s", msg);
Dmitry Belyavskiy 089d79
-			default:
Dmitry Belyavskiy 089d79
-				sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
Dmitry Belyavskiy 089d79
-				    type);
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			token_ptr = &recv_tok;
Dmitry Belyavskiy 089d79
-		} else {
Dmitry Belyavskiy 089d79
-			/* No data, and not complete */
Dmitry Belyavskiy 089d79
-			if (maj_status != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
-				fatal("Not complete, and no token output");
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
-	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Dmitry Belyavskiy 089d79
+/*******************************************************/
Dmitry Belyavskiy 089d79
+/******************** KEXGSSGEX ************************/
Dmitry Belyavskiy 089d79
+/*******************************************************/
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+int
Dmitry Belyavskiy 089d79
+kexgssgex_client(struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Initialise our GSSAPI world */
Dmitry Belyavskiy 089d79
+	ssh_gssapi_build_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+	if (ssh_gssapi_id_kex(kex->gss, kex->name, kex->kex_type) == GSS_C_NO_OID)
Dmitry Belyavskiy 089d79
+		fatal("Couldn't identify host exchange");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (ssh_gssapi_import_name(kex->gss, kex->gss_host))
Dmitry Belyavskiy 089d79
+		fatal("Couldn't import hostname");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (kex->gss_client &&
Dmitry Belyavskiy 089d79
+	    ssh_gssapi_client_identity(kex->gss, kex->gss_client))
Dmitry Belyavskiy 089d79
+		fatal("Couldn't acquire client credentials");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Doing group exchange");
Dmitry Belyavskiy 089d79
+	kex->min = DH_GRP_MIN;
Dmitry Belyavskiy 089d79
+	kex->max = DH_GRP_MAX;
Dmitry Belyavskiy 089d79
+	kex->nbits = dh_estimate(kex->dh_need * 8);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_put_u32(ssh, kex->min)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_put_u32(ssh, kex->max)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Failed to construct a packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Wait SSH2_MSG_KEXGSS_GROUP");
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUP, &input_kexgssgex_group);
Dmitry Belyavskiy 089d79
+	return 0;
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+kexgssgex_final(struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
+	struct sshbuf *buf = NULL;
Dmitry Belyavskiy 089d79
+	struct sshbuf *empty = NULL;
Dmitry Belyavskiy 089d79
+	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
+	BIGNUM *dh_server_pub = NULL;
Dmitry Belyavskiy 089d79
+	const BIGNUM *pub_key, *dh_p, *dh_g;
Dmitry Belyavskiy 089d79
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Dmitry Belyavskiy 089d79
+	size_t hashlen;
Dmitry Belyavskiy 089d79
+	int r = SSH_ERR_INTERNAL_ERROR;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/*
Dmitry Belyavskiy 089d79
 	 * We _must_ have received a COMPLETE message in reply from the
Dmitry Belyavskiy 089d79
-	 * server, which will have set dh_server_pub and msg_tok
Dmitry Belyavskiy 089d79
+	 * server, which will have set server_blob and msg_tok
Dmitry Belyavskiy 089d79
 	 */
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (type != SSH2_MSG_KEXGSS_COMPLETE)
Dmitry Belyavskiy 089d79
-		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
 	/* 7. C verifies that the key Q_S is valid */
Dmitry Belyavskiy 089d79
 	/* 8. C computes shared secret */
Dmitry Belyavskiy 089d79
 	if ((buf = sshbuf_new()) == NULL ||
Dmitry Belyavskiy 089d79
-	    (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
Dmitry Belyavskiy 089d79
+	    (r = sshbuf_put_stringb(buf, gss->server_blob)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 	sshbuf_free(buf);
Dmitry Belyavskiy 089d79
 	buf = NULL;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if ((shared_secret = sshbuf_new()) == NULL) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		r = SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
Dmitry Belyavskiy 089d79
+	if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 	if ((empty = sshbuf_new()) == NULL) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		r = SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+	DH_get0_key(kex->dh, &pub_key, NULL);
Dmitry Belyavskiy 089d79
 	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Dmitry Belyavskiy 089d79
 	hashlen = sizeof(hash);
Dmitry Belyavskiy 089d79
-	if ((r = kexgex_hash(
Dmitry Belyavskiy 089d79
-	    kex->hash_alg,
Dmitry Belyavskiy 089d79
-	    kex->client_version,
Dmitry Belyavskiy 089d79
-	    kex->server_version,
Dmitry Belyavskiy 089d79
-	    kex->my,
Dmitry Belyavskiy 089d79
-	    kex->peer,
Dmitry Belyavskiy 089d79
-	    (server_host_key_blob ? server_host_key_blob : empty),
Dmitry Belyavskiy 089d79
- 	    kex->min, kex->nbits, kex->max,
Dmitry Belyavskiy 089d79
-	    dh_p, dh_g,
Dmitry Belyavskiy 089d79
-	    pub_key,
Dmitry Belyavskiy 089d79
-	    dh_server_pub,
Dmitry Belyavskiy 089d79
-	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Dmitry Belyavskiy 089d79
-	    hash, &hashlen)) != 0)
Dmitry Belyavskiy 089d79
+	r = kexgex_hash(kex->hash_alg, kex->client_version,
Dmitry Belyavskiy 089d79
+			kex->server_version, kex->my, kex->peer,
Dmitry Belyavskiy 089d79
+			(gss->server_host_key_blob ? gss->server_host_key_blob : empty),
Dmitry Belyavskiy 089d79
+			kex->min, kex->nbits, kex->max, dh_p, dh_g, pub_key,
Dmitry Belyavskiy 089d79
+			dh_server_pub, sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Dmitry Belyavskiy 089d79
+			hash, &hashlen);
Dmitry Belyavskiy 089d79
+	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
+	if (r != 0)
Dmitry Belyavskiy 089d79
 		fatal("Failed to calculate hash: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gssbuf.value = hash;
Dmitry Belyavskiy 089d79
-	gssbuf.length = hashlen;
Dmitry Belyavskiy 089d79
+	gss->buf.value = hash;
Dmitry Belyavskiy 089d79
+	gss->buf.length = hashlen;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Verify that the hash matches the MIC we just got. */
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(ssh_gssapi_checkmic(gss, &gss->buf, &gss->msg_tok)))
Dmitry Belyavskiy 089d79
 		sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gss_release_buffer(&min_status, &msg_tok);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, &gss->msg_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (kex->gss_deleg_creds)
Dmitry Belyavskiy 089d79
-		ssh_gssapi_credentials_updated(ctxt);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_credentials_updated(gss);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (gss_kex_context == NULL)
Dmitry Belyavskiy 089d79
-		gss_kex_context = ctxt;
Dmitry Belyavskiy 089d79
+		gss_kex_context = gss;
Dmitry Belyavskiy 089d79
 	else
Dmitry Belyavskiy 089d79
-		ssh_gssapi_delete_ctx(&ctxt);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Finally derive the keys and send them */
Dmitry Belyavskiy 089d79
 	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Dmitry Belyavskiy 089d79
 		r = kex_send_newkeys(ssh);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (kex->gss != NULL) {
Dmitry Belyavskiy 089d79
+		sshbuf_free(gss->server_host_key_blob);
Dmitry Belyavskiy 089d79
+		gss->server_host_key_blob = NULL;
Dmitry Belyavskiy 089d79
+		sshbuf_free(gss->server_blob);
Dmitry Belyavskiy 089d79
+		gss->server_blob = NULL;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 out:
Dmitry Belyavskiy 089d79
-	sshbuf_free(buf);
Dmitry Belyavskiy 089d79
-	sshbuf_free(server_blob);
Dmitry Belyavskiy 089d79
-	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
 	explicit_bzero(hash, sizeof(hash));
Dmitry Belyavskiy 089d79
 	DH_free(kex->dh);
Dmitry Belyavskiy 089d79
 	kex->dh = NULL;
Dmitry Belyavskiy 089d79
 	BN_clear_free(dh_server_pub);
Dmitry Belyavskiy 089d79
 	sshbuf_free(shared_secret);
Dmitry Belyavskiy 089d79
-	sshbuf_free(server_host_key_blob);
Dmitry Belyavskiy 089d79
 	return r;
Dmitry Belyavskiy 089d79
 }
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+kexgssgex_init_ctx(struct ssh *ssh,
Dmitry Belyavskiy 089d79
+		   gss_buffer_desc *token_ptr)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
+	const BIGNUM *pub_key;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	OM_uint32 ret_flags;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Step 2 - call GSS_Init_sec_context() */
Dmitry Belyavskiy 089d79
+	debug("Calling gss_init_sec_context");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	gss->major = ssh_gssapi_init_ctx(gss, kex->gss_deleg_creds,
Dmitry Belyavskiy 089d79
+					 token_ptr, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(gss->major)) {
Dmitry Belyavskiy 089d79
+		/* XXX Useless code: Missing send? */
Dmitry Belyavskiy 089d79
+		if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
+				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		}
Dmitry Belyavskiy 089d79
+		fatal("gss_init_context failed");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* If we've got an old receive buffer get rid of it */
Dmitry Belyavskiy 089d79
+	if (token_ptr != GSS_C_NO_BUFFER)
Dmitry Belyavskiy 089d79
+		gss_release_buffer(&gss->minor, token_ptr);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (gss->major == GSS_S_COMPLETE) {
Dmitry Belyavskiy 089d79
+		/* If mutual state flag is not true, kex fails */
Dmitry Belyavskiy 089d79
+		if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
+			fatal("Mutual authentication failed");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+		/* If integ avail flag is not true kex fails */
Dmitry Belyavskiy 089d79
+		if (!(ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
+			fatal("Integrity check failed");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/*
Dmitry Belyavskiy 089d79
+	 * If we have data to send, then the last message that we
Dmitry Belyavskiy 089d79
+	 * received cannot have been a 'complete'.
Dmitry Belyavskiy 089d79
+	 */
Dmitry Belyavskiy 089d79
+	if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
+		if (gss->first) {
Dmitry Belyavskiy 089d79
+	                DH_get0_key(kex->dh, &pub_key, NULL);
Dmitry Belyavskiy 089d79
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
Dmitry Belyavskiy 089d79
+				fatal("failed to construct packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+			gss->first = 0;
Dmitry Belyavskiy 089d79
+		} else {
Dmitry Belyavskiy 089d79
+			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
+				fatal("failed to construct packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		}
Dmitry Belyavskiy 089d79
+		if ((r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
+			fatal("failed to send packet: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		gss_release_buffer(&gss->minor, &send_tok);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+		/* If we've sent them data, they should reply */
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, &input_kexgss_hostkey);
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgssgex_continue);
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, &input_kexgssgex_complete);
Dmitry Belyavskiy 089d79
+		ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, &input_kexgss_error);
Dmitry Belyavskiy 089d79
+		return 0;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	/* No data, and not complete */
Dmitry Belyavskiy 089d79
+	if (gss->major != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+		fatal("Not complete, and no token output");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if  (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return kexgssgex_init_ctx(ssh, token_ptr);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgssgex_final(ssh);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgssgex_group(int type,
Dmitry Belyavskiy 089d79
+		      u_int32_t seq,
Dmitry Belyavskiy 089d79
+		      struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	BIGNUM *p = NULL;
Dmitry Belyavskiy 089d79
+	BIGNUM *g = NULL;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Received SSH2_MSG_KEXGSS_GROUP");
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUP, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (BN_num_bits(p) < kex->min || BN_num_bits(p) > kex->max)
Dmitry Belyavskiy 089d79
+		fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
Dmitry Belyavskiy 089d79
+		    kex->min, BN_num_bits(p), kex->max);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((kex->dh = dh_new_group(g, p)) == NULL)
Dmitry Belyavskiy 089d79
+		fatal("dn_new_group() failed");
Dmitry Belyavskiy 089d79
+	p = g = NULL; /* belong to kex->dh now */
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		DH_free(kex->dh);
Dmitry Belyavskiy 089d79
+		kex->dh = NULL;
Dmitry Belyavskiy 089d79
+		return r;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgssgex_init_ctx(ssh, GSS_C_NO_BUFFER);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgssgex_continue(int type,
Dmitry Belyavskiy 089d79
+			 u_int32_t seq,
Dmitry Belyavskiy 089d79
+			 struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Received GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
+	if (gss->major == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+		fatal("GSSAPI Continue received from server when complete");
Dmitry Belyavskiy 089d79
+	if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Failed to read token: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	if  (!(gss->major & GSS_S_CONTINUE_NEEDED))
Dmitry Belyavskiy 089d79
+		fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
Dmitry Belyavskiy 089d79
+	return kexgssgex_init_ctx(ssh, &recv_tok);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgssgex_complete(int type,
Dmitry Belyavskiy 089d79
+		      u_int32_t seq,
Dmitry Belyavskiy 089d79
+		      struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	u_char c;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("Received GSSAPI_COMPLETE");
Dmitry Belyavskiy 089d79
+	if (gss->msg_tok.value != NULL)
Dmitry Belyavskiy 089d79
+	        fatal("Received GSSAPI_COMPLETE twice?");
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_getb_froms(ssh, &gss->server_blob)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &gss->msg_tok)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Failed to read message: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Is there a token included? */
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_u8(ssh, &c)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	if (c) {
Dmitry Belyavskiy 089d79
+		if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0)
Dmitry Belyavskiy 089d79
+			fatal("Failed to read token: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		/* If we're already complete - protocol error */
Dmitry Belyavskiy 089d79
+		if (gss->major == GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+			sshpkt_disconnect(ssh, "Protocol error: received token when complete");
Dmitry Belyavskiy 089d79
+	} else {
Dmitry Belyavskiy 089d79
+		if (gss->major != GSS_S_COMPLETE)
Dmitry Belyavskiy 089d79
+			sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("Expecting end of packet.");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if  (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return kexgssgex_init_ctx(ssh, &recv_tok);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgssgex_final(ssh);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
Dmitry Belyavskiy 089d79
diff --color -ruNp a/kexgsss.c b/kexgsss.c
Dmitry Belyavskiy 089d79
--- a/kexgsss.c	2024-05-16 15:49:43.820407648 +0200
Dmitry Belyavskiy 089d79
+++ b/kexgsss.c	2024-07-02 16:29:05.744790839 +0200
Dmitry Belyavskiy 089d79
@@ -50,33 +50,18 @@
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 extern ServerOptions options;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+static int input_kexgss_init(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgss_continue(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgssgex_groupreq(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgssgex_init(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+static int input_kexgssgex_continue(int, u_int32_t, struct ssh *);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 int
Dmitry Belyavskiy 089d79
 kexgss_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 {
Dmitry Belyavskiy 089d79
 	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
-	OM_uint32 maj_status, min_status;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	/*
Dmitry Belyavskiy 089d79
-	 * Some GSSAPI implementations use the input value of ret_flags (an
Dmitry Belyavskiy 089d79
-	 * output variable) as a means of triggering mechanism specific
Dmitry Belyavskiy 089d79
-	 * features. Initializing it to zero avoids inadvertently
Dmitry Belyavskiy 089d79
-	 * activating this non-standard behaviour.
Dmitry Belyavskiy 089d79
-	 */
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	OM_uint32 ret_flags = 0;
Dmitry Belyavskiy 089d79
-	gss_buffer_desc gssbuf = {0, NULL}, recv_tok, msg_tok;
Dmitry Belyavskiy 089d79
-	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
-	Gssctxt *ctxt = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *client_pubkey = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *server_pubkey = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *empty = sshbuf_new();
Dmitry Belyavskiy 089d79
-	int type = 0;
Dmitry Belyavskiy 089d79
 	gss_OID oid;
Dmitry Belyavskiy 089d79
 	char *mechs;
Dmitry Belyavskiy 089d79
-	u_char hash[SSH_DIGEST_MAX_LENGTH];
Dmitry Belyavskiy 089d79
-	size_t hashlen;
Dmitry Belyavskiy 089d79
-	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Initialise GSSAPI */
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
@@ -92,135 +77,91 @@ kexgss_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 	debug2_f("Identifying %s", kex->name);
Dmitry Belyavskiy 089d79
 	oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
Dmitry Belyavskiy 089d79
 	if (oid == GSS_C_NO_OID)
Dmitry Belyavskiy 089d79
-	   fatal("Unknown gssapi mechanism");
Dmitry Belyavskiy 089d79
+		fatal("Unknown gssapi mechanism");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	debug2_f("Acquiring credentials");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid)))
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&kex->gss, oid)))
Dmitry Belyavskiy 089d79
 		fatal("Unable to acquire credentials for the server");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	do {
Dmitry Belyavskiy 089d79
-		debug("Wait SSH2_MSG_KEXGSS_INIT");
Dmitry Belyavskiy 089d79
-		type = ssh_packet_read(ssh);
Dmitry Belyavskiy 089d79
-		switch(type) {
Dmitry Belyavskiy 089d79
-		case SSH2_MSG_KEXGSS_INIT:
Dmitry Belyavskiy 089d79
-			if (gssbuf.value != NULL)
Dmitry Belyavskiy 089d79
-				fatal("Received KEXGSS_INIT after initialising");
Dmitry Belyavskiy 089d79
-			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-			        &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+	ssh_gssapi_build_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+	if (kex->gss == NULL)
Dmitry Belyavskiy 089d79
+		fatal("Unable to allocate memory for gss context");
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, &input_kexgss_init);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgss_continue);
Dmitry Belyavskiy 089d79
+	debug("Wait SSH2_MSG_KEXGSS_INIT");
Dmitry Belyavskiy 089d79
+	return 0;
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-			switch (kex->kex_type) {
Dmitry Belyavskiy 089d79
-			case KEX_GSS_GRP1_SHA1:
Dmitry Belyavskiy 089d79
-			case KEX_GSS_GRP14_SHA1:
Dmitry Belyavskiy 089d79
-			case KEX_GSS_GRP14_SHA256:
Dmitry Belyavskiy 089d79
-			case KEX_GSS_GRP16_SHA512:
Dmitry Belyavskiy 089d79
-				r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
Dmitry Belyavskiy 089d79
-				    &shared_secret);
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			case KEX_GSS_NISTP256_SHA256:
Dmitry Belyavskiy 089d79
-				r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
Dmitry Belyavskiy 089d79
-				    &shared_secret);
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			case KEX_GSS_C25519_SHA256:
Dmitry Belyavskiy 089d79
-				r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
Dmitry Belyavskiy 089d79
-				    &shared_secret);
Dmitry Belyavskiy 089d79
-				break;
Dmitry Belyavskiy 089d79
-			default:
Dmitry Belyavskiy 089d79
-				fatal_f("Unexpected KEX type %d", kex->kex_type);
Dmitry Belyavskiy 089d79
-			}
Dmitry Belyavskiy 089d79
-			if (r != 0)
Dmitry Belyavskiy 089d79
-				goto out;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			/* Calculate the hash early so we can free the
Dmitry Belyavskiy 089d79
-			* client_pubkey, which has reference to the parent
Dmitry Belyavskiy 089d79
-			* buffer state->incoming_packet
Dmitry Belyavskiy 089d79
-			*/
Dmitry Belyavskiy 089d79
-			hashlen = sizeof(hash);
Dmitry Belyavskiy 089d79
-			if ((r = kex_gen_hash(
Dmitry Belyavskiy 089d79
-			    kex->hash_alg,
Dmitry Belyavskiy 089d79
-			    kex->client_version,
Dmitry Belyavskiy 089d79
-			    kex->server_version,
Dmitry Belyavskiy 089d79
-			    kex->peer,
Dmitry Belyavskiy 089d79
-			    kex->my,
Dmitry Belyavskiy 089d79
-			    empty,
Dmitry Belyavskiy 089d79
-			    client_pubkey,
Dmitry Belyavskiy 089d79
-			    server_pubkey,
Dmitry Belyavskiy 089d79
-			    shared_secret,
Dmitry Belyavskiy 089d79
-			    hash, &hashlen)) != 0)
Dmitry Belyavskiy 089d79
-				goto out;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			gssbuf.value = hash;
Dmitry Belyavskiy 089d79
-			gssbuf.length = hashlen;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			sshbuf_free(client_pubkey);
Dmitry Belyavskiy 089d79
-			client_pubkey = NULL;
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-			break;
Dmitry Belyavskiy 089d79
-		case SSH2_MSG_KEXGSS_CONTINUE:
Dmitry Belyavskiy 089d79
-			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-			        &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			break;
Dmitry Belyavskiy 089d79
-		default:
Dmitry Belyavskiy 089d79
-			sshpkt_disconnect(ssh,
Dmitry Belyavskiy 089d79
-			    "Protocol error: didn't expect packet type %d",
Dmitry Belyavskiy 089d79
-			    type);
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
+static inline void
Dmitry Belyavskiy 089d79
+kexgss_accept_ctx(struct ssh *ssh,
Dmitry Belyavskiy 089d79
+		  gss_buffer_desc *recv_tok,
Dmitry Belyavskiy 089d79
+		  gss_buffer_desc *send_tok,
Dmitry Belyavskiy 089d79
+		  OM_uint32 *ret_flags)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok,
Dmitry Belyavskiy 089d79
-		    &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+	gss->major = mm_ssh_gssapi_accept_ctx(gss, recv_tok, send_tok, ret_flags);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, recv_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		gss_release_buffer(&min_status, &recv_tok);
Dmitry Belyavskiy 089d79
+	if (gss->major != GSS_S_COMPLETE && send_tok->length == 0)
Dmitry Belyavskiy 089d79
+		fatal("Zero length token output when incomplete");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
Dmitry Belyavskiy 089d79
-			fatal("Zero length token output when incomplete");
Dmitry Belyavskiy 089d79
+	if (gss->buf.value == NULL)
Dmitry Belyavskiy 089d79
+		fatal("No client public key");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (gssbuf.value == NULL)
Dmitry Belyavskiy 089d79
-			fatal("No client public key");
Dmitry Belyavskiy 089d79
+	if (gss->major & GSS_S_CONTINUE_NEEDED) {
Dmitry Belyavskiy 089d79
+		debug("Sending GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
+		if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
+		    (r = sshpkt_put_string(ssh, send_tok->value, send_tok->length)) != 0 ||
Dmitry Belyavskiy 089d79
+		    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
+			fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		gss_release_buffer(&gss->minor, send_tok);
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (maj_status & GSS_S_CONTINUE_NEEDED) {
Dmitry Belyavskiy 089d79
-			debug("Sending GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
-			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			gss_release_buffer(&min_status, &send_tok);
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
-	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Dmitry Belyavskiy 089d79
+static inline int
Dmitry Belyavskiy 089d79
+kexgss_final(struct ssh *ssh,
Dmitry Belyavskiy 089d79
+	     gss_buffer_desc *send_tok,
Dmitry Belyavskiy 089d79
+	     OM_uint32 *ret_flags)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc msg_tok;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(maj_status)) {
Dmitry Belyavskiy 089d79
-		if (send_tok.length > 0) {
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(gss->major)) {
Dmitry Belyavskiy 089d79
+		if (send_tok->length > 0) {
Dmitry Belyavskiy 089d79
 			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok->value, send_tok->length)) != 0 ||
Dmitry Belyavskiy 089d79
 			    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
 				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 		}
Dmitry Belyavskiy 089d79
 		fatal("accept_ctx died");
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
+	if (!(*ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
 		fatal("Mutual Authentication flag wasn't set");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (!(ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
+	if (!(*ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
 		fatal("Integrity flag wasn't set");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_sign(gss, &gss->buf, &msg_tok)))
Dmitry Belyavskiy 089d79
 		fatal("Couldn't get MIC");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_put_stringb(ssh, gss->server_pubkey)) != 0 ||
Dmitry Belyavskiy 089d79
 	    (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
Dmitry Belyavskiy 089d79
 		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
+	if (send_tok->length != 0) {
Dmitry Belyavskiy 089d79
 		if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
Dmitry Belyavskiy 089d79
-		    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
+		    (r = sshpkt_put_string(ssh, send_tok->value, send_tok->length)) != 0)
Dmitry Belyavskiy 089d79
 			fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 	} else {
Dmitry Belyavskiy 089d79
 		if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
Dmitry Belyavskiy 089d79
@@ -229,59 +170,139 @@ kexgss_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 	if ((r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
 		fatal("sshpkt_send failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gss_release_buffer(&min_status, &send_tok);
Dmitry Belyavskiy 089d79
-	gss_release_buffer(&min_status, &msg_tok);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, send_tok);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, &msg_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (gss_kex_context == NULL)
Dmitry Belyavskiy 089d79
-		gss_kex_context = ctxt;
Dmitry Belyavskiy 089d79
+		gss_kex_context = gss;
Dmitry Belyavskiy 089d79
 	else
Dmitry Belyavskiy 089d79
-		ssh_gssapi_delete_ctx(&ctxt);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Dmitry Belyavskiy 089d79
+	if ((r = kex_derive_keys(ssh, gss->hash, gss->hashlen, gss->shared_secret)) == 0)
Dmitry Belyavskiy 089d79
 		r = kex_send_newkeys(ssh);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* If this was a rekey, then save out any delegated credentials we
Dmitry Belyavskiy 089d79
 	 * just exchanged.  */
Dmitry Belyavskiy 089d79
 	if (options.gss_store_rekey)
Dmitry Belyavskiy 089d79
 		ssh_gssapi_rekey_creds();
Dmitry Belyavskiy 089d79
-out:
Dmitry Belyavskiy 089d79
-	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
-	explicit_bzero(hash, sizeof(hash));
Dmitry Belyavskiy 089d79
-	sshbuf_free(shared_secret);
Dmitry Belyavskiy 089d79
-	sshbuf_free(client_pubkey);
Dmitry Belyavskiy 089d79
-	sshbuf_free(server_pubkey);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (kex->gss != NULL) {
Dmitry Belyavskiy 089d79
+		explicit_bzero(gss->hash, sizeof(gss->hash));
Dmitry Belyavskiy 089d79
+		sshbuf_free(gss->shared_secret);
Dmitry Belyavskiy 089d79
+		gss->shared_secret = NULL;
Dmitry Belyavskiy 089d79
+		sshbuf_free(gss->server_pubkey);
Dmitry Belyavskiy 089d79
+		gss->server_pubkey = NULL;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 	return r;
Dmitry Belyavskiy 089d79
 }
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-int
Dmitry Belyavskiy 089d79
-kexgssgex_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgss_init(int type,
Dmitry Belyavskiy 089d79
+		  u_int32_t seq,
Dmitry Belyavskiy 089d79
+		  struct ssh *ssh)
Dmitry Belyavskiy 089d79
 {
Dmitry Belyavskiy 089d79
 	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
-	OM_uint32 maj_status, min_status;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
+	struct sshbuf *empty;
Dmitry Belyavskiy 089d79
+	struct sshbuf *client_pubkey = NULL;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	OM_uint32 ret_flags = 0;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("SSH2_MSG_KEXGSS_INIT received");
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	/*
Dmitry Belyavskiy 089d79
-	 * Some GSSAPI implementations use the input value of ret_flags (an
Dmitry Belyavskiy 089d79
-	 * output variable) as a means of triggering mechanism specific
Dmitry Belyavskiy 089d79
-	 * features. Initializing it to zero avoids inadvertently
Dmitry Belyavskiy 089d79
-	 * activating this non-standard behaviour.
Dmitry Belyavskiy 089d79
+	if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	switch (kex->kex_type) {
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP1_SHA1:
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP14_SHA1:
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP14_SHA256:
Dmitry Belyavskiy 089d79
+	case KEX_GSS_GRP16_SHA512:
Dmitry Belyavskiy 089d79
+		r = kex_dh_enc(kex, client_pubkey, &gss->server_pubkey, &gss->shared_secret);
Dmitry Belyavskiy 089d79
+		break;
Dmitry Belyavskiy 089d79
+	case KEX_GSS_NISTP256_SHA256:
Dmitry Belyavskiy 089d79
+		r = kex_ecdh_enc(kex, client_pubkey, &gss->server_pubkey, &gss->shared_secret);
Dmitry Belyavskiy 089d79
+		break;
Dmitry Belyavskiy 089d79
+	case KEX_GSS_C25519_SHA256:
Dmitry Belyavskiy 089d79
+		r = kex_c25519_enc(kex, client_pubkey, &gss->server_pubkey, &gss->shared_secret);
Dmitry Belyavskiy 089d79
+		break;
Dmitry Belyavskiy 089d79
+	default:
Dmitry Belyavskiy 089d79
+		fatal_f("Unexpected KEX type %d", kex->kex_type);
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+	if (r != 0) {
Dmitry Belyavskiy 089d79
+		sshbuf_free(client_pubkey);
Dmitry Belyavskiy 089d79
+                ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		return r;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((empty = sshbuf_new()) == NULL) {
Dmitry Belyavskiy 089d79
+		sshbuf_free(client_pubkey);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		return SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Calculate the hash early so we can free the
Dmitry Belyavskiy 089d79
+	 * client_pubkey, which has reference to the parent
Dmitry Belyavskiy 089d79
+	 * buffer state->incoming_packet
Dmitry Belyavskiy 089d79
 	 */
Dmitry Belyavskiy 089d79
+	gss->hashlen = sizeof(gss->hash);
Dmitry Belyavskiy 089d79
+	r = kex_gen_hash(kex->hash_alg, kex->client_version, kex->server_version,
Dmitry Belyavskiy 089d79
+			 kex->peer, kex->my, empty, client_pubkey, gss->server_pubkey,
Dmitry Belyavskiy 089d79
+			 gss->shared_secret, gss->hash, &gss->hashlen);
Dmitry Belyavskiy 089d79
+	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
+	sshbuf_free(client_pubkey);
Dmitry Belyavskiy 089d79
+	if (r != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		return r;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	gss->buf.value = gss->hash;
Dmitry Belyavskiy 089d79
+	gss->buf.length = gss->hashlen;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	kexgss_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+	if (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return 0;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+	return kexgss_final(ssh, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgss_continue(int type,
Dmitry Belyavskiy 089d79
+		      u_int32_t seq,
Dmitry Belyavskiy 089d79
+		      struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
 	OM_uint32 ret_flags = 0;
Dmitry Belyavskiy 089d79
-	gss_buffer_desc gssbuf, recv_tok, msg_tok;
Dmitry Belyavskiy 089d79
-	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
-	Gssctxt *ctxt = NULL;
Dmitry Belyavskiy 089d79
-	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
-	int type = 0;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	kexgss_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+	if (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return 0;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgss_final(ssh, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+/*******************************************************/
Dmitry Belyavskiy 089d79
+/******************** KEXGSSGEX ************************/
Dmitry Belyavskiy 089d79
+/*******************************************************/
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+int
Dmitry Belyavskiy 089d79
+kexgssgex_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
 	gss_OID oid;
Dmitry Belyavskiy 089d79
 	char *mechs;
Dmitry Belyavskiy 089d79
-	u_char hash[SSH_DIGEST_MAX_LENGTH];
Dmitry Belyavskiy 089d79
-	size_t hashlen;
Dmitry Belyavskiy 089d79
-	BIGNUM *dh_client_pub = NULL;
Dmitry Belyavskiy 089d79
-	const BIGNUM *pub_key, *dh_p, *dh_g;
Dmitry Belyavskiy 089d79
-	int min = -1, max = -1, nbits = -1;
Dmitry Belyavskiy 089d79
-	int cmin = -1, cmax = -1; /* client proposal */
Dmitry Belyavskiy 089d79
-	struct sshbuf *empty = sshbuf_new();
Dmitry Belyavskiy 089d79
-	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Initialise GSSAPI */
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
@@ -289,153 +310,125 @@ kexgssgex_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 	 * in the GSSAPI code are no longer available. This kludges them back
Dmitry Belyavskiy 089d79
 	 * into life
Dmitry Belyavskiy 089d79
 	 */
Dmitry Belyavskiy 089d79
-	if (!ssh_gssapi_oid_table_ok())
Dmitry Belyavskiy 089d79
-		if ((mechs = ssh_gssapi_server_mechanisms()))
Dmitry Belyavskiy 089d79
-			free(mechs);
Dmitry Belyavskiy 089d79
+	if (!ssh_gssapi_oid_table_ok()) {
Dmitry Belyavskiy 089d79
+		mechs = ssh_gssapi_server_mechanisms();
Dmitry Belyavskiy 089d79
+		free(mechs);
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	debug2_f("Identifying %s", kex->name);
Dmitry Belyavskiy 089d79
 	oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
Dmitry Belyavskiy 089d79
 	if (oid == GSS_C_NO_OID)
Dmitry Belyavskiy 089d79
-	   fatal("Unknown gssapi mechanism");
Dmitry Belyavskiy 089d79
+		fatal("Unknown gssapi mechanism");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	debug2_f("Acquiring credentials");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid)))
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&kex->gss, oid)))
Dmitry Belyavskiy 089d79
 		fatal("Unable to acquire credentials for the server");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	/* 5. S generates an ephemeral key pair (do the allocations early) */
Dmitry Belyavskiy 089d79
-	debug("Doing group exchange");
Dmitry Belyavskiy 089d79
-	ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
Dmitry Belyavskiy 089d79
-	/* store client proposal to provide valid signature */
Dmitry Belyavskiy 089d79
-	if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-	kex->nbits = nbits;
Dmitry Belyavskiy 089d79
-	kex->min = cmin;
Dmitry Belyavskiy 089d79
-	kex->max = cmax;
Dmitry Belyavskiy 089d79
-	min = MAX(DH_GRP_MIN, cmin);
Dmitry Belyavskiy 089d79
-	max = MIN(DH_GRP_MAX, cmax);
Dmitry Belyavskiy 089d79
-	nbits = MAXIMUM(DH_GRP_MIN, nbits);
Dmitry Belyavskiy 089d79
-	nbits = MINIMUM(DH_GRP_MAX, nbits);
Dmitry Belyavskiy 089d79
-	if (max < min || nbits < min || max < nbits)
Dmitry Belyavskiy 089d79
-		fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
Dmitry Belyavskiy 089d79
-		    min, nbits, max);
Dmitry Belyavskiy 089d79
-	kex->dh = mm_choose_dh(min, nbits, max);
Dmitry Belyavskiy 089d79
-	if (kex->dh == NULL) {
Dmitry Belyavskiy 089d79
-		sshpkt_disconnect(ssh, "Protocol error: no matching group found");
Dmitry Belyavskiy 089d79
-		fatal("Protocol error: no matching group found");
Dmitry Belyavskiy 089d79
-	}
Dmitry Belyavskiy 089d79
+	ssh_gssapi_build_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+	if (kex->gss == NULL)
Dmitry Belyavskiy 089d79
+		fatal("Unable to allocate memory for gss context");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Dmitry Belyavskiy 089d79
-	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
Dmitry Belyavskiy 089d79
-	    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	if ((r = ssh_packet_write_wait(ssh)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("ssh_packet_write_wait: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-
Dmitry Belyavskiy 089d79
-	/* Compute our exchange value in parallel with the client */
Dmitry Belyavskiy 089d79
-	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
Dmitry Belyavskiy 089d79
-		goto out;
Dmitry Belyavskiy 089d79
+	debug("Doing group exchange");
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUPREQ, &input_kexgssgex_groupreq);
Dmitry Belyavskiy 089d79
+	return 0;
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	do {
Dmitry Belyavskiy 089d79
-		debug("Wait SSH2_MSG_GSSAPI_INIT");
Dmitry Belyavskiy 089d79
-		type = ssh_packet_read(ssh);
Dmitry Belyavskiy 089d79
-		switch(type) {
Dmitry Belyavskiy 089d79
-		case SSH2_MSG_KEXGSS_INIT:
Dmitry Belyavskiy 089d79
-			if (dh_client_pub != NULL)
Dmitry Belyavskiy 089d79
-				fatal("Received KEXGSS_INIT after initialising");
Dmitry Belyavskiy 089d79
-			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-			        &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+static inline void
Dmitry Belyavskiy 089d79
+kexgssgex_accept_ctx(struct ssh *ssh,
Dmitry Belyavskiy 089d79
+		     gss_buffer_desc *recv_tok,
Dmitry Belyavskiy 089d79
+		     gss_buffer_desc *send_tok,
Dmitry Belyavskiy 089d79
+		     OM_uint32 *ret_flags)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-			/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Dmitry Belyavskiy 089d79
-			break;
Dmitry Belyavskiy 089d79
-		case SSH2_MSG_KEXGSS_CONTINUE:
Dmitry Belyavskiy 089d79
-			if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
Dmitry Belyavskiy 089d79
-			        &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			break;
Dmitry Belyavskiy 089d79
-		default:
Dmitry Belyavskiy 089d79
-			sshpkt_disconnect(ssh,
Dmitry Belyavskiy 089d79
-			    "Protocol error: didn't expect packet type %d",
Dmitry Belyavskiy 089d79
-			    type);
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
+	gss->major = mm_ssh_gssapi_accept_ctx(gss, recv_tok, send_tok, ret_flags);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, recv_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok,
Dmitry Belyavskiy 089d79
-		    &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+	if (gss->major != GSS_S_COMPLETE && send_tok->length == 0)
Dmitry Belyavskiy 089d79
+		fatal("Zero length token output when incomplete");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		gss_release_buffer(&min_status, &recv_tok);
Dmitry Belyavskiy 089d79
+	if (gss->dh_client_pub == NULL)
Dmitry Belyavskiy 089d79
+		fatal("No client public key");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
Dmitry Belyavskiy 089d79
-			fatal("Zero length token output when incomplete");
Dmitry Belyavskiy 089d79
+	if (gss->major & GSS_S_CONTINUE_NEEDED) {
Dmitry Belyavskiy 089d79
+		debug("Sending GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
+		if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
+		    (r = sshpkt_put_string(ssh, send_tok->value, send_tok->length)) != 0 ||
Dmitry Belyavskiy 089d79
+		    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
+			fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		gss_release_buffer(&gss->minor, send_tok);
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (dh_client_pub == NULL)
Dmitry Belyavskiy 089d79
-			fatal("No client public key");
Dmitry Belyavskiy 089d79
+static inline int
Dmitry Belyavskiy 089d79
+kexgssgex_final(struct ssh *ssh,
Dmitry Belyavskiy 089d79
+		gss_buffer_desc *send_tok,
Dmitry Belyavskiy 089d79
+		OM_uint32 *ret_flags)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc msg_tok;
Dmitry Belyavskiy 089d79
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
Dmitry Belyavskiy 089d79
+	size_t hashlen;
Dmitry Belyavskiy 089d79
+	const BIGNUM *pub_key, *dh_p, *dh_g;
Dmitry Belyavskiy 089d79
+	struct sshbuf *shared_secret = NULL;
Dmitry Belyavskiy 089d79
+	struct sshbuf *empty = NULL;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-		if (maj_status & GSS_S_CONTINUE_NEEDED) {
Dmitry Belyavskiy 089d79
-			debug("Sending GSSAPI_CONTINUE");
Dmitry Belyavskiy 089d79
-			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
-			gss_release_buffer(&min_status, &send_tok);
Dmitry Belyavskiy 089d79
-		}
Dmitry Belyavskiy 089d79
-	} while (maj_status & GSS_S_CONTINUE_NEEDED);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(maj_status)) {
Dmitry Belyavskiy 089d79
-		if (send_tok.length > 0) {
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(gss->major)) {
Dmitry Belyavskiy 089d79
+		if (send_tok->length > 0) {
Dmitry Belyavskiy 089d79
 			if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
Dmitry Belyavskiy 089d79
-			    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
Dmitry Belyavskiy 089d79
+			    (r = sshpkt_put_string(ssh, send_tok->value, send_tok->length)) != 0 ||
Dmitry Belyavskiy 089d79
 			    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
 				fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 		}
Dmitry Belyavskiy 089d79
 		fatal("accept_ctx died");
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (!(ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
+	if (!(*ret_flags & GSS_C_MUTUAL_FLAG))
Dmitry Belyavskiy 089d79
 		fatal("Mutual Authentication flag wasn't set");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (!(ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
+	if (!(*ret_flags & GSS_C_INTEG_FLAG))
Dmitry Belyavskiy 089d79
 		fatal("Integrity flag wasn't set");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* calculate shared secret */
Dmitry Belyavskiy 089d79
-	if ((shared_secret = sshbuf_new()) == NULL) {
Dmitry Belyavskiy 089d79
+	shared_secret = sshbuf_new();
Dmitry Belyavskiy 089d79
+	if (shared_secret == NULL) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		r = SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
-	if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
Dmitry Belyavskiy 089d79
+	if ((r = kex_dh_compute_key(kex, gss->dh_client_pub, shared_secret)) != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 		goto out;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((empty = sshbuf_new()) == NULL) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		r = SSH_ERR_ALLOC_FAIL;
Dmitry Belyavskiy 089d79
+		goto out;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	DH_get0_key(kex->dh, &pub_key, NULL);
Dmitry Belyavskiy 089d79
 	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Dmitry Belyavskiy 089d79
 	hashlen = sizeof(hash);
Dmitry Belyavskiy 089d79
-	if ((r = kexgex_hash(
Dmitry Belyavskiy 089d79
-	    kex->hash_alg,
Dmitry Belyavskiy 089d79
-	    kex->client_version,
Dmitry Belyavskiy 089d79
-	    kex->server_version,
Dmitry Belyavskiy 089d79
-	    kex->peer,
Dmitry Belyavskiy 089d79
-	    kex->my,
Dmitry Belyavskiy 089d79
-	    empty,
Dmitry Belyavskiy 089d79
-	    cmin, nbits, cmax,
Dmitry Belyavskiy 089d79
-	    dh_p, dh_g,
Dmitry Belyavskiy 089d79
-	    dh_client_pub,
Dmitry Belyavskiy 089d79
-	    pub_key,
Dmitry Belyavskiy 089d79
-	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
Dmitry Belyavskiy 089d79
-	    hash, &hashlen)) != 0)
Dmitry Belyavskiy 089d79
+	r = kexgex_hash(kex->hash_alg, kex->client_version, kex->server_version,
Dmitry Belyavskiy 089d79
+			kex->peer, kex->my, empty, kex->min, kex->nbits, kex->max, dh_p, dh_g,
Dmitry Belyavskiy 089d79
+			gss->dh_client_pub, pub_key, sshbuf_ptr(shared_secret),
Dmitry Belyavskiy 089d79
+			sshbuf_len(shared_secret), hash, &hashlen);
Dmitry Belyavskiy 089d79
+	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
+	if (r != 0)
Dmitry Belyavskiy 089d79
 		fatal("kexgex_hash failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gssbuf.value = hash;
Dmitry Belyavskiy 089d79
-	gssbuf.length = hashlen;
Dmitry Belyavskiy 089d79
+	gss->buf.value = hash;
Dmitry Belyavskiy 089d79
+	gss->buf.length = hashlen;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))
Dmitry Belyavskiy 089d79
+	if (GSS_ERROR(mm_ssh_gssapi_sign(gss, &gss->buf, &msg_tok)))
Dmitry Belyavskiy 089d79
 		fatal("Couldn't get MIC");
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
Dmitry Belyavskiy 089d79
@@ -443,24 +436,24 @@ kexgssgex_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 	    (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
Dmitry Belyavskiy 089d79
 		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	if (send_tok.length != 0) {
Dmitry Belyavskiy 089d79
+	if (send_tok->length != 0) {
Dmitry Belyavskiy 089d79
 		if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
Dmitry Belyavskiy 089d79
-		    (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
Dmitry Belyavskiy 089d79
+		    (r = sshpkt_put_string(ssh, send_tok->value, send_tok->length)) != 0)
Dmitry Belyavskiy 089d79
 			fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 	} else {
Dmitry Belyavskiy 089d79
 		if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
Dmitry Belyavskiy 089d79
 			fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 	}
Dmitry Belyavskiy 089d79
 	if ((r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
-		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+		fatal("sshpkt_send failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
-	gss_release_buffer(&min_status, &send_tok);
Dmitry Belyavskiy 089d79
-	gss_release_buffer(&min_status, &msg_tok);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, send_tok);
Dmitry Belyavskiy 089d79
+	gss_release_buffer(&gss->minor, &msg_tok);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	if (gss_kex_context == NULL)
Dmitry Belyavskiy 089d79
-		gss_kex_context = ctxt;
Dmitry Belyavskiy 089d79
+		gss_kex_context = gss;
Dmitry Belyavskiy 089d79
 	else
Dmitry Belyavskiy 089d79
-		ssh_gssapi_delete_ctx(&ctxt);
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 	/* Finally derive the keys and send them */
Dmitry Belyavskiy 089d79
 	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
Dmitry Belyavskiy 089d79
@@ -470,13 +463,128 @@ kexgssgex_server(struct ssh *ssh)
Dmitry Belyavskiy 089d79
 	 * just exchanged.  */
Dmitry Belyavskiy 089d79
 	if (options.gss_store_rekey)
Dmitry Belyavskiy 089d79
 		ssh_gssapi_rekey_creds();
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (kex->gss != NULL)
Dmitry Belyavskiy 089d79
+		BN_clear_free(gss->dh_client_pub);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 out:
Dmitry Belyavskiy 089d79
-	sshbuf_free(empty);
Dmitry Belyavskiy 089d79
 	explicit_bzero(hash, sizeof(hash));
Dmitry Belyavskiy 089d79
 	DH_free(kex->dh);
Dmitry Belyavskiy 089d79
 	kex->dh = NULL;
Dmitry Belyavskiy 089d79
-	BN_clear_free(dh_client_pub);
Dmitry Belyavskiy 089d79
 	sshbuf_free(shared_secret);
Dmitry Belyavskiy 089d79
 	return r;
Dmitry Belyavskiy 089d79
 }
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgssgex_groupreq(int type,
Dmitry Belyavskiy 089d79
+			 u_int32_t seq,
Dmitry Belyavskiy 089d79
+			 struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	struct kex *kex = ssh->kex;
Dmitry Belyavskiy 089d79
+	const BIGNUM *dh_p, *dh_g;
Dmitry Belyavskiy 089d79
+	int min = -1, max = -1, nbits = -1;
Dmitry Belyavskiy 089d79
+	int cmin = -1, cmax = -1; /* client proposal */
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* 5. S generates an ephemeral key pair (do the allocations early) */
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("SSH2_MSG_KEXGSS_GROUPREQ received");
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUPREQ, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* store client proposal to provide valid signature */
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	kex->nbits = nbits;
Dmitry Belyavskiy 089d79
+	kex->min = cmin;
Dmitry Belyavskiy 089d79
+	kex->max = cmax;
Dmitry Belyavskiy 089d79
+	min = MAX(DH_GRP_MIN, cmin);
Dmitry Belyavskiy 089d79
+	max = MIN(DH_GRP_MAX, cmax);
Dmitry Belyavskiy 089d79
+	nbits = MAXIMUM(DH_GRP_MIN, nbits);
Dmitry Belyavskiy 089d79
+	nbits = MINIMUM(DH_GRP_MAX, nbits);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if (max < min || nbits < min || max < nbits)
Dmitry Belyavskiy 089d79
+		fatal("GSS_GEX, bad parameters: %d !< %d !< %d", min, nbits, max);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	kex->dh = mm_choose_dh(min, nbits, max);
Dmitry Belyavskiy 089d79
+	if (kex->dh == NULL) {
Dmitry Belyavskiy 089d79
+		sshpkt_disconnect(ssh, "Protocol error: no matching group found");
Dmitry Belyavskiy 089d79
+		fatal("Protocol error: no matching group found");
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
Dmitry Belyavskiy 089d79
+	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_send(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = ssh_packet_write_wait(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("ssh_packet_write_wait: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Compute our exchange value in parallel with the client */
Dmitry Belyavskiy 089d79
+	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) {
Dmitry Belyavskiy 089d79
+		ssh_gssapi_delete_ctx(&kex->gss);
Dmitry Belyavskiy 089d79
+		DH_free(kex->dh);
Dmitry Belyavskiy 089d79
+		kex->dh = NULL;
Dmitry Belyavskiy 089d79
+		return r;
Dmitry Belyavskiy 089d79
+	}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, &input_kexgssgex_init);
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgssgex_continue);
Dmitry Belyavskiy 089d79
+	debug("Wait SSH2_MSG_KEXGSS_INIT");
Dmitry Belyavskiy 089d79
+	return 0;
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgssgex_init(int type,
Dmitry Belyavskiy 089d79
+		     u_int32_t seq,
Dmitry Belyavskiy 089d79
+		     struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	OM_uint32 ret_flags = 0;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	debug("SSH2_MSG_KEXGSS_INIT received");
Dmitry Belyavskiy 089d79
+	ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL);
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_bignum2(ssh, &gss->dh_client_pub)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	kexgssgex_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+	if (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return 0;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgssgex_final(ssh, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+static int
Dmitry Belyavskiy 089d79
+input_kexgssgex_continue(int type,
Dmitry Belyavskiy 089d79
+			 u_int32_t seq,
Dmitry Belyavskiy 089d79
+			 struct ssh *ssh)
Dmitry Belyavskiy 089d79
+{
Dmitry Belyavskiy 089d79
+	Gssctxt *gss = ssh->kex->gss;
Dmitry Belyavskiy 089d79
+	gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER;
Dmitry Belyavskiy 089d79
+	OM_uint32 ret_flags = 0;
Dmitry Belyavskiy 089d79
+	int r;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 ||
Dmitry Belyavskiy 089d79
+	    (r = sshpkt_get_end(ssh)) != 0)
Dmitry Belyavskiy 089d79
+		fatal("sshpkt failed: %s", ssh_err(r));
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	kexgssgex_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+	if (gss->major & GSS_S_CONTINUE_NEEDED)
Dmitry Belyavskiy 089d79
+		return 0;
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
+	return kexgssgex_final(ssh, &send_tok, &ret_flags);
Dmitry Belyavskiy 089d79
+}
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
Dmitry Belyavskiy 089d79
diff --color -ruNp a/kex.h b/kex.h
Dmitry Belyavskiy 089d79
--- a/kex.h	2024-05-16 15:49:43.986410812 +0200
Dmitry Belyavskiy 089d79
+++ b/kex.h	2024-06-18 12:19:48.580347469 +0200
Dmitry Belyavskiy 089d79
@@ -29,6 +29,10 @@
Dmitry Belyavskiy 089d79
 #include "mac.h"
Dmitry Belyavskiy 089d79
 #include "crypto_api.h"
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+#ifdef GSSAPI
Dmitry Belyavskiy 089d79
+# include "ssh-gss.h" /* Gssctxt */
Dmitry Belyavskiy 089d79
+#endif
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 #ifdef WITH_OPENSSL
Dmitry Belyavskiy 089d79
 # include <openssl/bn.h>
Dmitry Belyavskiy 089d79
 # include <openssl/dh.h>
Dmitry Belyavskiy 089d79
@@ -177,6 +181,7 @@ struct kex {
Dmitry Belyavskiy 089d79
 	int	hash_alg;
Dmitry Belyavskiy 089d79
 	int	ec_nid;
Dmitry Belyavskiy 089d79
 #ifdef GSSAPI
Dmitry Belyavskiy 089d79
+	Gssctxt *gss;
Dmitry Belyavskiy 089d79
 	int	gss_deleg_creds;
Dmitry Belyavskiy 089d79
 	int	gss_trust_dns;
Dmitry Belyavskiy 089d79
 	char    *gss_host;
Dmitry Belyavskiy 089d79
diff --color -ruNp a/ssh-gss.h b/ssh-gss.h
Dmitry Belyavskiy 089d79
--- a/ssh-gss.h	2024-05-16 15:49:43.837407972 +0200
Dmitry Belyavskiy 089d79
+++ b/ssh-gss.h	2024-06-27 14:12:48.659866937 +0200
Dmitry Belyavskiy 089d79
@@ -88,6 +88,8 @@ extern char **k5users_allowed_cmds;
Dmitry Belyavskiy 089d79
 	KEX_GSS_GRP14_SHA1_ID "," \
Dmitry Belyavskiy 089d79
 	KEX_GSS_GEX_SHA1_ID
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
+#include "digest.h" /* SSH_DIGEST_MAX_LENGTH */
Dmitry Belyavskiy 089d79
+
Dmitry Belyavskiy 089d79
 typedef struct {
Dmitry Belyavskiy 089d79
 	char *filename;
Dmitry Belyavskiy 089d79
 	char *envvar;
Dmitry Belyavskiy 089d79
@@ -127,6 +129,16 @@ typedef struct {
Dmitry Belyavskiy 089d79
 	gss_cred_id_t	creds; /* server */
Dmitry Belyavskiy 089d79
 	gss_name_t	client; /* server */
Dmitry Belyavskiy 089d79
 	gss_cred_id_t	client_creds; /* both */
Dmitry Belyavskiy 089d79
+	struct sshbuf *shared_secret; /* both */
Dmitry Belyavskiy 089d79
+	struct sshbuf *server_pubkey; /* server */
Dmitry Belyavskiy 089d79
+	struct sshbuf *server_blob; /* client */
Dmitry Belyavskiy 089d79
+	struct sshbuf *server_host_key_blob; /* client */
Dmitry Belyavskiy 089d79
+	gss_buffer_desc msg_tok; /* client */
Dmitry Belyavskiy 089d79
+	gss_buffer_desc buf; /* both */
Dmitry Belyavskiy 089d79
+	u_char hash[SSH_DIGEST_MAX_LENGTH]; /* both */
Dmitry Belyavskiy 089d79
+	size_t hashlen; /* both */
Dmitry Belyavskiy 089d79
+	int first; /* client */
Dmitry Belyavskiy 089d79
+	BIGNUM *dh_client_pub; /* server (gex) */
Dmitry Belyavskiy 089d79
 } Gssctxt;
Dmitry Belyavskiy 089d79
 
Dmitry Belyavskiy 089d79
 extern ssh_gssapi_mech *supported_mechs[];