From 72514f764462deea63aa3a5eb40bf767812b0f8a Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Wed, 13 Sep 2017 14:44:31 +0200 Subject: [PATCH] Add newer gssapi kex methods, but leave them disabled out of the box yet --- openssh-7.5p1-gssapi-kex-with-ec.patch | 1374 ++++++++++++++++++++++++ openssh.spec | 3 + 2 files changed, 1377 insertions(+) create mode 100644 openssh-7.5p1-gssapi-kex-with-ec.patch diff --git a/openssh-7.5p1-gssapi-kex-with-ec.patch b/openssh-7.5p1-gssapi-kex-with-ec.patch new file mode 100644 index 0000000..437cce6 --- /dev/null +++ b/openssh-7.5p1-gssapi-kex-with-ec.patch @@ -0,0 +1,1374 @@ +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, + buffer_ptr(ssh->kex->my), buffer_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, + buffer_ptr(ssh->kex->peer), buffer_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, ++ buffer_ptr(kex->my), buffer_len(kex->my), ++ buffer_ptr(kex->peer), buffer_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, ++ buffer_ptr(kex->peer), buffer_len(kex->peer), ++ buffer_ptr(kex->my), buffer_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) + + data.value = buffer_get_string(m, &len); + data.length = len; +- 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 + diff --git a/openssh.spec b/openssh.spec index 5859183..cd13a08 100644 --- a/openssh.spec +++ b/openssh.spec @@ -187,6 +187,8 @@ Patch804: openssh-6.3p1-krb5-use-default_ccache_name.patch Patch805: openssh-7.2p2-k5login_directory.patch # Do not export KRBCCNAME if the default path is used (#1199363) Patch806: openssh-7.5p1-gss-environment.patch +# Support SHA2 in GSS key exchanges from draft-ssorce-gss-keyex-sha2-02 +Patch807: openssh-7.5p1-gssapi-kex-with-ec.patch Patch900: openssh-6.1p1-gssapi-canohost.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1780 @@ -453,6 +455,7 @@ popd %patch944 -p1 -b .x11max %patch948 -p1 -b .systemd %patch949 -p1 -b .sandbox +%patch807 -p1 -b .gsskex-ec %patch200 -p1 -b .audit %patch201 -p1 -b .audit-race