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