From 6ff8f667f792052fd47689c3e421fcd6ddca1cd0 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Fri, 25 Aug 2017 19:15:48 +0200 Subject: [PATCH 1/3] GSSAPI Key exchange methods with DH and SHA2 --- gss-genr.c | 10 ++++++++++ kex.c | 2 ++ kex.h | 2 ++ kexgssc.c | 6 ++++++ kexgsss.c | 6 ++++++ monitor.c | 2 ++ regress/kextype.sh | 4 +++- regress/rekey.sh | 8 ++++++-- ssh-gss.h | 2 ++ ssh_config.5 | 4 +++- sshconnect2.c | 2 ++ sshd.c | 2 ++ sshd_config.5 | 4 +++- 13 files changed, 49 insertions(+), 5 deletions(-) diff --git a/gss-genr.c b/gss-genr.c index dc63682d..c6eff3d7 100644 --- a/gss-genr.c +++ b/gss-genr.c @@ -183,6 +183,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { return GSS_C_NO_OID; name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; break; + case KEX_GSS_GRP14_SHA256: + if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; + break; + case KEX_GSS_GRP16_SHA512: + if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; + break; case KEX_GSS_GEX_SHA1: if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) return GSS_C_NO_OID; diff --git a/kex.c b/kex.c index 63e028fa..e798fecb 100644 --- a/kex.c +++ b/kex.c @@ -112,6 +112,8 @@ static const struct kexalg kexalgs[] = { { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, + { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, #endif { NULL, -1, -1, -1}, }; diff --git a/kex.h b/kex.h index 8a2b37c5..f27958ae 100644 --- a/kex.h +++ b/kex.h @@ -102,6 +102,8 @@ enum kex_exchange { #ifdef GSSAPI KEX_GSS_GRP1_SHA1, KEX_GSS_GRP14_SHA1, + KEX_GSS_GRP14_SHA256, + KEX_GSS_GRP16_SHA512, KEX_GSS_GEX_SHA1, #endif KEX_MAX diff --git a/kexgssc.c b/kexgssc.c index 132df8b5..ed23f06d 100644 --- a/kexgssc.c +++ b/kexgssc.c @@ -88,8 +88,12 @@ kexgss_client(struct ssh *ssh) { dh = dh_new_group1(); break; case KEX_GSS_GRP14_SHA1: + case KEX_GSS_GRP14_SHA256: dh = dh_new_group14(); break; + case KEX_GSS_GRP16_SHA512: + dh = dh_new_group16(); + break; case KEX_GSS_GEX_SHA1: debug("Doing group exchange\n"); nbits = dh_estimate(ssh->kex->we_need * 8); @@ -272,6 +276,8 @@ kexgss_client(struct ssh *ssh) { switch (ssh->kex->kex_type) { case KEX_GSS_GRP1_SHA1: case KEX_GSS_GRP14_SHA1: + case KEX_GSS_GRP14_SHA256: + case KEX_GSS_GRP16_SHA512: kex_dh_hash(ssh->kex->hash_alg, ssh->kex->client_version_string, ssh->kex->server_version_string, sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), diff --git a/kexgsss.c b/kexgsss.c index 82a715cc..b7da8823 100644 --- a/kexgsss.c +++ b/kexgsss.c @@ -104,8 +104,12 @@ kexgss_server(struct ssh *ssh) dh = dh_new_group1(); break; case KEX_GSS_GRP14_SHA1: + case KEX_GSS_GRP14_SHA256: dh = dh_new_group14(); break; + case KEX_GSS_GRP16_SHA512: + dh = dh_new_group16(); + break; case KEX_GSS_GEX_SHA1: debug("Doing group exchange"); packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); @@ -223,6 +227,8 @@ kexgss_server(struct ssh *ssh) switch (ssh->kex->kex_type) { case KEX_GSS_GRP1_SHA1: case KEX_GSS_GRP14_SHA1: + case KEX_GSS_GRP14_SHA256: + case KEX_GSS_GRP16_SHA512: kex_dh_hash(ssh->kex->hash_alg, ssh->kex->client_version_string, ssh->kex->server_version_string, sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), diff --git a/monitor.c b/monitor.c index 17046936..d6bc7ac7 100644 --- a/monitor.c +++ b/monitor.c @@ -1648,6 +1648,8 @@ monitor_apply_keystate(struct monitor *pmonitor) if (options.gss_keyex) { kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; + kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; } #endif diff --git a/regress/kextype.sh b/regress/kextype.sh index 780362ca..45f4f16d 100644 --- a/regress/kextype.sh +++ b/regress/kextype.sh @@ -14,7 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy tries="1 2 3 4" for k in `${SSH} -Q kex`; do - if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then + if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ + $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ + $k = "gss-group16-sha512-" ]; then continue fi verbose "kex $k" diff --git a/regress/rekey.sh b/regress/rekey.sh index 9fbe9b38..a2921bef 100644 --- a/regress/rekey.sh +++ b/regress/rekey.sh @@ -38,7 +38,9 @@ increase_datafile_size 300 opts="" for i in `${SSH} -Q kex`; do - if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then + if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ + $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ + $i = "gss-group16-sha512-" ]; then continue fi opts="$opts KexAlgorithms=$i" @@ -59,7 +61,9 @@ done if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then for c in `${SSH} -Q cipher-auth`; do for kex in `${SSH} -Q kex`; do - if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then + if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ + $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ + $kex = "gss-group16-sha512-" ]; then continue fi verbose "client rekey $c $kex" diff --git a/ssh-gss.h b/ssh-gss.h index 6b6adb2b..7bf8d75e 100644 --- a/ssh-gss.h +++ b/ssh-gss.h @@ -70,6 +70,8 @@ #define SSH2_MSG_KEXGSS_GROUP 41 #define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" +#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" +#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" #define GSS_KEX_DEFAULT_KEX \ diff --git a/ssh_config.5 b/ssh_config.5 index 6b24649e..3d6da510 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -760,7 +760,9 @@ key exchange. Possible values are .Bd -literal -offset 3n gss-gex-sha1-, gss-group1-sha1-, -gss-group14-sha1- +gss-group14-sha1-, +gss-group14-sha256-, +gss-group16-sha512- .Ed .Pp The default is diff --git a/sshconnect2.c b/sshconnect2.c index 8db98293..5d6b8be0 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -253,6 +253,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) if (options.gss_keyex) { kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; + kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; + kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; } #endif diff --git a/sshd.c b/sshd.c index 895df26f..e4c879a2 100644 --- a/sshd.c +++ b/sshd.c @@ -2244,6 +2244,8 @@ do_ssh2_kex(void) if (options.gss_keyex) { kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; + kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; } #endif diff --git a/sshd_config.5 b/sshd_config.5 index bf81f6af..0793418b 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -675,7 +675,9 @@ key exchange. Possible values are .Bd -literal -offset 3n gss-gex-sha1-, gss-group1-sha1-, -gss-group14-sha1- +gss-group14-sha1-, +gss-group14-sha256-, +gss-group16-sha512- .Ed .Pp The default is -- 2.13.5 From 7d56144903fc625c33da7fabf103f4f6bba4d43a Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Tue, 29 Aug 2017 15:32:14 +0200 Subject: [PATCH 2/3] GSSAPI Key exchange using ECDH and SHA2 --- gss-genr.c | 10 ++ kex.c | 3 + kex.h | 4 + kexgssc.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++- kexgsss.c | 333 +++++++++++++++++++++++++++++++++++++++++++++ monitor.c | 5 +- regress/kextype.sh | 1 + regress/rekey.sh | 2 + ssh-gss.h | 2 + ssh_config.5 | 4 +- sshconnect2.c | 2 + sshd.c | 2 + sshd_config.5 | 4 +- 13 files changed, 754 insertions(+), 10 deletions(-) diff --git a/gss-genr.c b/gss-genr.c index c6eff3d7..22040244 100644 --- a/gss-genr.c +++ b/gss-genr.c @@ -198,6 +198,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { return GSS_C_NO_OID; name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; break; + case KEX_GSS_NISTP256_SHA256: + if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; + break; + case KEX_GSS_C25519_SHA256: + if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; + break; default: return GSS_C_NO_OID; } diff --git a/kex.c b/kex.c index e798fecb..bdeeada9 100644 --- a/kex.c +++ b/kex.c @@ -114,6 +114,9 @@ static const struct kexalg kexalgs[] = { { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, + { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, + NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, + { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, #endif { NULL, -1, -1, -1}, }; diff --git a/kex.h b/kex.h index f27958ae..7def8561 100644 --- a/kex.h +++ b/kex.h @@ -105,6 +105,8 @@ enum kex_exchange { KEX_GSS_GRP14_SHA256, KEX_GSS_GRP16_SHA512, KEX_GSS_GEX_SHA1, + KEX_GSS_NISTP256_SHA256, + KEX_GSS_C25519_SHA256, #endif KEX_MAX }; @@ -211,6 +213,8 @@ int kexecdh_server(struct ssh *); int kexc25519_client(struct ssh *); int kexc25519_server(struct ssh *); #ifdef GSSAPI +int kexecgss_client(struct ssh *); +int kexecgss_server(struct ssh *); int kexgss_client(struct ssh *); int kexgss_server(struct ssh *); #endif diff --git a/kexgssc.c b/kexgssc.c index ed23f06d..bdb3109a 100644 --- a/kexgssc.c +++ b/kexgssc.c @@ -43,6 +43,7 @@ #include "packet.h" #include "dh.h" #include "digest.h" +#include "ssherr.h" #include "ssh-gss.h" @@ -52,7 +53,7 @@ kexgss_client(struct ssh *ssh) { gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; Gssctxt *ctxt; OM_uint32 maj_status, min_status, ret_flags; - u_int klen, kout, slen = 0, strlen; + u_int klen, kout, slen = 0, packet_len; DH *dh; BIGNUM *dh_server_pub = NULL; BIGNUM *shared_secret = NULL; @@ -201,20 +202,20 @@ kexgss_client(struct ssh *ssh) { debug("Received GSSAPI_CONTINUE"); if (maj_status == GSS_S_COMPLETE) fatal("GSSAPI Continue received from server when complete"); - recv_tok.value = packet_get_string(&strlen); - recv_tok.length = strlen; + recv_tok.value = packet_get_string(&packet_len); + recv_tok.length = packet_len; break; case SSH2_MSG_KEXGSS_COMPLETE: debug("Received GSSAPI_COMPLETE"); packet_get_bignum2(dh_server_pub); - msg_tok.value = packet_get_string(&strlen); - msg_tok.length = strlen; + msg_tok.value = packet_get_string(&packet_len); + msg_tok.length = packet_len; /* Is there a token included? */ if (packet_get_char()) { recv_tok.value= - packet_get_string(&strlen); - recv_tok.length = strlen; + packet_get_string(&packet_len); + recv_tok.length = packet_len; /* If we're already complete - protocol error */ if (maj_status == GSS_S_COMPLETE) packet_disconnect("Protocol error: received token when complete"); @@ -344,4 +345,381 @@ kexgss_client(struct ssh *ssh) { return kex_send_newkeys(ssh); } +int +kexecgss_client(struct ssh *ssh) { + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; + Gssctxt *ctxt; + OM_uint32 maj_status, min_status, ret_flags; + u_int klen = 0, slen = 0, packet_len; + u_char *server_pub = NULL; + u_int server_pub_len = 0; + BIGNUM *shared_secret = NULL; + u_char *kbuf; + u_char *serverhostkey = NULL; + u_char *empty = ""; + char *msg; + char *lang; + int type = 0; + int first = 1; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t hashlen; + const EC_GROUP *group = NULL; + const EC_POINT *public_key; + struct sshbuf *Q_C = NULL; + struct kex *kex = ssh->kex; + EC_POINT *server_public = NULL; + struct sshbuf *c25519_shared_secret = NULL; + 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"); + + if ((Q_C = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if ((kex->ec_client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(kex->ec_client_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + group = EC_KEY_get0_group(kex->ec_client_key); + public_key = EC_KEY_get0_public_key(kex->ec_client_key); +#ifdef DEBUG_KEXECDH + fputs("client private key:\n", stderr); + sshkey_dump_ec_key(kex->ec_client_key); +#endif + + sshbuf_put_ec(Q_C, public_key, group); + break; + case KEX_GSS_C25519_SHA256: + kexc25519_keygen(kex->c25519_client_key, kex->c25519_client_pubkey); +#ifdef DEBUG_KEXECDH + dump_digest("client private key:", kex->c25519_client_key, + sizeof(kex->c25519_client_key)); +#endif + + sshbuf_put_string(Q_C, kex->c25519_client_pubkey, + sizeof(kex->c25519_client_pubkey)); + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + token_ptr = GSS_C_NO_BUFFER; + + 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)) { + if (send_tok.length != 0) { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value, + send_tok.length); + } + fatal("gss_init_context failed"); + } + + /* If we've got an old receive buffer get rid of it */ + if (token_ptr != GSS_C_NO_BUFFER) + free(recv_tok.value); + + 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) { + const u_char * ptr; + size_t len; + + packet_start(SSH2_MSG_KEXGSS_INIT); + packet_put_string(send_tok.value, + send_tok.length); + sshbuf_get_string_direct(Q_C, &ptr, &len); + packet_put_string(ptr, len); + first = 0; + } else { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value, + send_tok.length); + } + packet_send(); + gss_release_buffer(&min_status, &send_tok); + + /* If we've sent them data, they should reply */ + do { + type = packet_read(); + if (type == SSH2_MSG_KEXGSS_HOSTKEY) { + debug("Received KEXGSS_HOSTKEY"); + if (serverhostkey) + fatal("Server host key received more than once"); + serverhostkey = + packet_get_string(&slen); + } + } 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"); + recv_tok.value = packet_get_string(&packet_len); + recv_tok.length = packet_len; + break; + case SSH2_MSG_KEXGSS_COMPLETE: + debug("Received GSSAPI_COMPLETE"); + server_pub = packet_get_string(&server_pub_len); + msg_tok.value = packet_get_string(&packet_len); + msg_tok.length = packet_len; + + /* Is there a token included? */ + if (packet_get_char()) { + recv_tok.value= + packet_get_string(&packet_len); + recv_tok.length = packet_len; + /* If we're already complete - protocol error */ + if (maj_status == GSS_S_COMPLETE) + packet_disconnect("Protocol error: received token when complete"); + } else { + /* No token included */ + if (maj_status != GSS_S_COMPLETE) + packet_disconnect("Protocol error: did not receive final token"); + } + break; + case SSH2_MSG_KEXGSS_ERROR: + debug("Received Error"); + maj_status = packet_get_int(); + min_status = packet_get_int(); + msg = packet_get_string(NULL); + lang = packet_get_string(NULL); + fatal("GSSAPI Error: \n%.400s",msg); + default: + packet_disconnect("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 dh_server_pub 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 */ + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if (server_pub_len != 65) + fatal("The received NIST-P256 key did not match" + "expected length (expected 65, got %d)", server_pub_len); + + if (server_pub[0] != POINT_CONVERSION_UNCOMPRESSED) + fatal("The received NIST-P256 key does not have first octet 0x04"); + + if ((server_public = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if (!EC_POINT_oct2point(group, server_public, server_pub, + server_pub_len, NULL)) + fatal("Can not decode received NIST-P256 client key"); +#ifdef DEBUG_KEXECDH + fputs("server public key:\n", stderr); + sshkey_dump_ec_point(group, server_public); +#endif + + if (sshkey_ec_validate_public(group, server_public) != 0) { + sshpkt_disconnect(ssh, "invalid client public key"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } + + if (!EC_POINT_is_on_curve(group, server_public, NULL)) + fatal("Received NIST-P256 client key is not on curve"); + + /* Calculate shared_secret */ + klen = (EC_GROUP_get_degree(group) + 7) / 8; + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (ECDH_compute_key(kbuf, klen, server_public, + kex->ec_client_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +#ifdef DEBUG_KEXECDH + dump_digest("shared secret", kbuf, klen); +#endif + break; + case KEX_GSS_C25519_SHA256: + if (server_pub_len != 32) + fatal("The received curve25519 key did not match" + "expected length (expected 32, got %d)", server_pub_len); + + if (server_pub[server_pub_len-1] & 0x80) + fatal("The received key has MSB of last octet set!"); +#ifdef DEBUG_KEXECDH + dump_digest("server public key:", server_pub, CURVE25519_SIZE); +#endif + + /* generate shared secret */ + if ((c25519_shared_secret = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kexc25519_shared_key(kex->c25519_client_key, + server_pub, c25519_shared_secret)) < 0) + goto out; + + /* if all octets of the shared secret are zero octets, + * is already checked in kexc25519_shared_key() */ + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + hashlen = sizeof(hash); + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + kex_ecdh_hash( + kex->hash_alg, + group, + kex->client_version_string, + kex->server_version_string, + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + (serverhostkey ? serverhostkey : empty), slen, + EC_KEY_get0_public_key(kex->ec_client_key), + server_public, + shared_secret, + hash, &hashlen + ); + break; + case KEX_GSS_C25519_SHA256: + kex_c25519_hash( + kex->hash_alg, + kex->client_version_string, kex->server_version_string, + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + (serverhostkey ? serverhostkey : empty), slen, + kex->c25519_client_pubkey, server_pub, + sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), + hash, &hashlen + ); + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + gssbuf.value = hash; + gssbuf.length = hashlen; + + /* Verify that the hash matches the MIC we just got. */ + if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) + packet_disconnect("Hash's MIC didn't verify"); + + free(msg_tok.value); + + /* save session id */ + if (kex->session_id == NULL) { + kex->session_id_len = hashlen; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + if (kex->gss_deleg_creds) + ssh_gssapi_credentials_updated(ctxt); + + if (gss_kex_context == NULL) + gss_kex_context = ctxt; + else + ssh_gssapi_delete_ctx(&ctxt); + + /* Finally derive the keys and send them */ + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) + goto out; + break; + case KEX_GSS_C25519_SHA256: + if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) + goto out; + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + r = kex_send_newkeys(ssh); +out: + free(serverhostkey); + explicit_bzero(hash, sizeof(hash)); + sshbuf_free(Q_C); + if (server_pub) + free(server_pub); + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if (kex->ec_client_key) { + EC_KEY_free(kex->ec_client_key); + kex->ec_client_key = NULL; + } + if (server_public) + EC_POINT_clear_free(server_public); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + break; + case KEX_GSS_C25519_SHA256: + explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); + sshbuf_free(c25519_shared_secret); + break; + } + return r; +} #endif /* GSSAPI */ diff --git a/kexgsss.c b/kexgsss.c index b7da8823..a7c42803 100644 --- a/kexgsss.c +++ b/kexgsss.c @@ -46,6 +46,7 @@ #include "servconf.h" #include "ssh-gss.h" #include "digest.h" +#include "ssherr.h" extern ServerOptions options; @@ -303,4 +304,336 @@ kexgss_server(struct ssh *ssh) ssh_gssapi_rekey_creds(); return 0; } + +int +kexecgss_server(struct ssh *ssh) +{ + 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, recv_tok, msg_tok; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + Gssctxt *ctxt = NULL; + u_int slen, klen = 0; + u_char *kbuf; + BIGNUM *shared_secret = NULL; + int type = 0; + gss_OID oid; + char *mechs; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t hashlen; + u_char *client_pub = NULL; + u_int client_pub_len = 0; + const EC_GROUP *group = NULL; + EC_POINT *client_public = NULL; + EC_KEY *server_key = NULL; + const EC_POINT *public_key; + u_char c25519_server_key[CURVE25519_SIZE]; + u_char c25519_server_pubkey[CURVE25519_SIZE]; + struct sshbuf *c25519_shared_secret = NULL; + struct sshbuf *Q_S; + struct kex *kex = ssh->kex; + int r; + + /* Initialise GSSAPI */ + + /* If we're rekeying, privsep means that some of the private structures + * 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); + + debug2("%s: Identifying %s", __func__, kex->name); + oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); + if (oid == GSS_C_NO_OID) + fatal("Unknown gssapi mechanism"); + + debug2("%s: Acquiring credentials", __func__); + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) + fatal("Unable to acquire credentials for the server"); + + if ((Q_S = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* 5. S generates an ephemeral key pair (do the allocations early) */ + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(server_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + group = EC_KEY_get0_group(server_key); + public_key = EC_KEY_get0_public_key(server_key); + + sshbuf_put_ec(Q_S, public_key, group); + break; + case KEX_GSS_C25519_SHA256: + kexc25519_keygen(c25519_server_key, c25519_server_pubkey); +#ifdef DEBUG_KEXECDH + dump_digest("server private key:", c25519_server_key, + sizeof(c25519_server_key)); +#endif + sshbuf_put_string(Q_S, c25519_server_pubkey, + sizeof(c25519_server_pubkey)); + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + do { + debug("Wait SSH2_MSG_GSSAPI_INIT"); + type = packet_read(); + switch(type) { + case SSH2_MSG_KEXGSS_INIT: + if (client_pub != NULL) + fatal("Received KEXGSS_INIT after initialising"); + recv_tok.value = packet_get_string(&slen); + recv_tok.length = slen; + + client_pub = packet_get_string(&client_pub_len); + + /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ + break; + case SSH2_MSG_KEXGSS_CONTINUE: + recv_tok.value = packet_get_string(&slen); + recv_tok.length = slen; + break; + default: + packet_disconnect( + "Protocol error: didn't expect packet type %d", + type); + } + + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, + &send_tok, &ret_flags)); + + free(recv_tok.value); + + if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) + fatal("Zero length token output when incomplete"); + + if (client_pub == NULL) + fatal("No client public key"); + + if (maj_status & GSS_S_CONTINUE_NEEDED) { + debug("Sending GSSAPI_CONTINUE"); + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value, send_tok.length); + packet_send(); + gss_release_buffer(&min_status, &send_tok); + } + } while (maj_status & GSS_S_CONTINUE_NEEDED); + + if (GSS_ERROR(maj_status)) { + if (send_tok.length > 0) { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value, send_tok.length); + packet_send(); + } + fatal("accept_ctx died"); + } + + if (!(ret_flags & GSS_C_MUTUAL_FLAG)) + fatal("Mutual Authentication flag wasn't set"); + + if (!(ret_flags & GSS_C_INTEG_FLAG)) + fatal("Integrity flag wasn't set"); + + /* 3. S verifies that the (client) key is valid */ + /* calculate shared secret */ + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if (client_pub_len != 65) + fatal("The received NIST-P256 key did not match" + "expected length (expected 65, got %d)", client_pub_len); + + if (client_pub[0] != POINT_CONVERSION_UNCOMPRESSED) + fatal("The received NIST-P256 key does not have first octet 0x04"); + + if ((client_public = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if (!EC_POINT_oct2point(group, client_public, client_pub, + client_pub_len, NULL)) + fatal("Can not decode received NIST-P256 client key"); + + if (sshkey_ec_validate_public(group, client_public) != 0) { + sshpkt_disconnect(ssh, "invalid client public key"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } + + if (!EC_POINT_is_on_curve(group, client_public, NULL)) + fatal("Received NIST-P256 client key is not on curve"); + + /* Calculate shared_secret */ + klen = (EC_GROUP_get_degree(group) + 7) / 8; + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (ECDH_compute_key(kbuf, klen, client_public, + server_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + break; + case KEX_GSS_C25519_SHA256: + if (client_pub_len != 32) + fatal("The received curve25519 key did not match" + "expected length (expected 32, got %d)", client_pub_len); + + if (client_pub[client_pub_len-1] & 0x80) + fatal("The received key has MSB of last octet set!"); + + /* generate shared secret */ + if ((c25519_shared_secret = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kexc25519_shared_key(c25519_server_key, + client_pub, c25519_shared_secret)) < 0) + goto out; + + /* if all octets of the shared secret are zero octets, + * is already checked in kexc25519_shared_key() */ + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + hashlen = sizeof(hash); + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + kex_ecdh_hash( + kex->hash_alg, + group, + kex->client_version_string, + kex->server_version_string, + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + NULL, 0, + client_public, + EC_KEY_get0_public_key(server_key), + shared_secret, + hash, &hashlen + ); + break; + case KEX_GSS_C25519_SHA256: + kex_c25519_hash( + kex->hash_alg, + kex->client_version_string, kex->server_version_string, + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + NULL, 0, + client_pub, c25519_server_pubkey, + sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), + hash, &hashlen + ); + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + if (kex->session_id == NULL) { + kex->session_id_len = hashlen; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + gssbuf.value = hash; + gssbuf.length = hashlen; + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) + fatal("Couldn't get MIC"); + + packet_start(SSH2_MSG_KEXGSS_COMPLETE); + { + const u_char *ptr; + size_t len; + sshbuf_get_string_direct(Q_S, &ptr, &len); + packet_put_string(ptr, len); + } + packet_put_string(msg_tok.value, msg_tok.length); + + if (send_tok.length != 0) { + packet_put_char(1); /* true */ + packet_put_string(send_tok.value, send_tok.length); + } else { + packet_put_char(0); /* false */ + } + packet_send(); + + gss_release_buffer(&min_status, &send_tok); + gss_release_buffer(&min_status, &msg_tok); + + if (gss_kex_context == NULL) + gss_kex_context = ctxt; + else + ssh_gssapi_delete_ctx(&ctxt); + + /* Finally derive the keys and send them */ + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) + goto out; + break; + case KEX_GSS_C25519_SHA256: + if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) + goto out; + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + if ((r = kex_send_newkeys(ssh)) != 0) + goto out; + + /* If this was a rekey, then save out any delegated credentials we + * just exchanged. */ + if (options.gss_store_rekey) + ssh_gssapi_rekey_creds(); +out: + explicit_bzero(hash, sizeof(hash)); + if (Q_S) + sshbuf_free(Q_S); + if (client_pub) + free(client_pub); + switch (kex->kex_type) { + case KEX_GSS_NISTP256_SHA256: + if (server_key) + EC_KEY_free(server_key); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + break; + case KEX_GSS_C25519_SHA256: + explicit_bzero(c25519_server_key, sizeof(c25519_server_key)); + sshbuf_free(c25519_shared_secret); + break; + } + return r; +} #endif /* GSSAPI */ diff --git a/monitor.c b/monitor.c index d6bc7ac7..b11616c8 100644 --- a/monitor.c +++ b/monitor.c @@ -1651,6 +1651,8 @@ monitor_apply_keystate(struct monitor *pmonitor) kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; + kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; + kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; } #endif kex->load_host_public_key=&get_hostkey_public_by_type; @@ -1867,7 +1869,8 @@ mm_answer_gss_sign(int socket, Buffer *m) if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); - if (data.length != 20) + /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */ + if (data.length != 20 && data.length != 32 && data.length != 64) fatal("%s: data length incorrect: %d", __func__, (int) data.length); diff --git a/regress/kextype.sh b/regress/kextype.sh index 45f4f16d..d5b4a713 100644 --- a/regress/kextype.sh +++ b/regress/kextype.sh @@ -15,6 +15,7 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy tries="1 2 3 4" for k in `${SSH} -Q kex`; do if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ + $k = "gss-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ $k = "gss-group16-sha512-" ]; then continue diff --git a/regress/rekey.sh b/regress/rekey.sh index a2921bef..b118c6c8 100644 --- a/regress/rekey.sh +++ b/regress/rekey.sh @@ -39,6 +39,7 @@ increase_datafile_size 300 opts="" for i in `${SSH} -Q kex`; do if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ + $i = "gss-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ $i = "gss-group16-sha512-" ]; then continue @@ -62,6 +63,7 @@ if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then for c in `${SSH} -Q cipher-auth`; do for kex in `${SSH} -Q kex`; do if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ + $kex = "gss-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ $kex = "gss-group16-sha512-" ]; then continue diff --git a/ssh-gss.h b/ssh-gss.h index 7bf8d75e..1f73721d 100644 --- a/ssh-gss.h +++ b/ssh-gss.h @@ -73,6 +73,8 @@ #define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" #define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" +#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-" +#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-" #define GSS_KEX_DEFAULT_KEX \ KEX_GSS_GEX_SHA1_ID "," \ diff --git a/ssh_config.5 b/ssh_config.5 index 3d6da510..1dc29bf1 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -762,7 +762,9 @@ gss-gex-sha1-, gss-group1-sha1-, gss-group14-sha1-, gss-group14-sha256-, -gss-group16-sha512- +gss-group16-sha512-, +gss-nistp256-sha256-, +gss-curve25519-sha256- .Ed .Pp The default is diff --git a/sshconnect2.c b/sshconnect2.c index 5d6b8be0..280ae5a6 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -256,6 +256,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; + kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_client; + kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_client; } #endif kex->kex[KEX_C25519_SHA256] = kexc25519_client; diff --git a/sshd.c b/sshd.c index e4c879a2..a35735d8 100644 --- a/sshd.c +++ b/sshd.c @@ -2247,6 +2247,8 @@ do_ssh2_kex(void) kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; + kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; + kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; } #endif kex->server = 1; diff --git a/sshd_config.5 b/sshd_config.5 index 0793418b..888316bf 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -677,7 +677,9 @@ gss-gex-sha1-, gss-group1-sha1-, gss-group14-sha1-, gss-group14-sha256-, -gss-group16-sha512- +gss-group16-sha512-, +gss-nistp256-sha256-, +gss-curve25519-sha256- .Ed .Pp The default is -- 2.13.5 From 0431695660d5eb1dd1169d42a1624c75a92aa5d2 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Wed, 30 Aug 2017 15:30:51 +0200 Subject: [PATCH 3/3] Simplify rough edges of GSSAPI Kex --- gss-genr.c | 53 +++++++++++++++++------------------------------------ regress/kextype.sh | 10 ++++------ regress/rekey.sh | 20 ++++++++------------ 3 files changed, 29 insertions(+), 54 deletions(-) diff --git a/gss-genr.c b/gss-genr.c index 22040244..c671be31 100644 --- a/gss-genr.c +++ b/gss-genr.c @@ -171,47 +171,28 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { int i = 0; - - switch (kex_type) { - case KEX_GSS_GRP1_SHA1: - if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; - break; - case KEX_GSS_GRP14_SHA1: - if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; - break; - case KEX_GSS_GRP14_SHA256: - if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; - break; - case KEX_GSS_GRP16_SHA512: - if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; - break; - case KEX_GSS_GEX_SHA1: - if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; - break; - case KEX_GSS_NISTP256_SHA256: - if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; - break; - case KEX_GSS_C25519_SHA256: - if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; + +#define SKIP_KEX_NAME(type) \ + case type: \ + if (strlen(name) < sizeof(type##_ID)) \ + return GSS_C_NO_OID; \ + name += sizeof(type##_ID) - 1; \ break; + + switch (kex_type) { + SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1) + SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1) + SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256) + SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512) + SKIP_KEX_NAME(KEX_GSS_GEX_SHA1) + SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256) + SKIP_KEX_NAME(KEX_GSS_C25519_SHA256) default: return GSS_C_NO_OID; } +#undef SKIP_KEX_NAME + while (gss_enc2oid[i].encoded != NULL && strcmp(name, gss_enc2oid[i].encoded) != 0) i++; diff --git a/regress/kextype.sh b/regress/kextype.sh index d5b4a713..6b4af28a 100644 --- a/regress/kextype.sh +++ b/regress/kextype.sh @@ -14,12 +14,10 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy tries="1 2 3 4" for k in `${SSH} -Q kex`; do - if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ - $k = "gss-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ - $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ - $k = "gss-group16-sha512-" ]; then - continue - fi + # ignore GSSAPI key exchange mechanisms (all of them start with gss-) + case $k in + gss-* ) continue ;; + esac verbose "kex $k" for i in $tries; do ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true diff --git a/regress/rekey.sh b/regress/rekey.sh index b118c6c8..d6a8742f 100644 --- a/regress/rekey.sh +++ b/regress/rekey.sh @@ -38,12 +38,10 @@ increase_datafile_size 300 opts="" for i in `${SSH} -Q kex`; do - if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ - $i = "gss-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ - $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ - $i = "gss-group16-sha512-" ]; then - continue - fi + # ignore GSSAPI key exchange mechanisms (all of them start with gss-) + case $i in + gss-* ) continue ;; + esac opts="$opts KexAlgorithms=$i" done for i in `${SSH} -Q cipher`; do @@ -62,12 +60,10 @@ done if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then for c in `${SSH} -Q cipher-auth`; do for kex in `${SSH} -Q kex`; do - if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ - $kex = "gss-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ - $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ - $kex = "gss-group16-sha512-" ]; then - continue - fi + # ignore GSSAPI key exchange mechanisms (all of them start with gss-) + case $kex in + gss-* ) continue ;; + esac verbose "client rekey $c $kex" ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c done -- 2.13.5