diff options
Diffstat (limited to 'crypto/openssh/kex.c')
-rw-r--r-- | crypto/openssh/kex.c | 168 |
1 files changed, 134 insertions, 34 deletions
diff --git a/crypto/openssh/kex.c b/crypto/openssh/kex.c index b777b7d..d371f47 100644 --- a/crypto/openssh/kex.c +++ b/crypto/openssh/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.109 2015/07/30 00:01:34 djm Exp $ */ +/* $OpenBSD: kex.c,v 1.117 2016/02/08 10:57:07 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -49,7 +49,6 @@ #include "misc.h" #include "dispatch.h" #include "monitor.h" -#include "roaming.h" #include "ssherr.h" #include "sshbuf.h" @@ -67,6 +66,19 @@ extern const EVP_MD *evp_ssh_sha256(void); static int kex_choose_conf(struct ssh *); static int kex_input_newkeys(int, u_int32_t, void *); +static const char *proposal_names[PROPOSAL_MAX] = { + "KEX algorithms", + "host key algorithms", + "ciphers ctos", + "ciphers stoc", + "MACs ctos", + "MACs stoc", + "compression ctos", + "compression stoc", + "languages ctos", + "languages stoc", +}; + struct kexalg { char *name; u_int type; @@ -267,7 +279,7 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) goto out; - debug2("kex_parse_kexinit: %s", proposal[i]); + debug2("%s: %s", proposal_names[i], proposal[i]); } /* first kex follows / reserved */ if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ @@ -302,7 +314,14 @@ kex_prop_free(char **proposal) static int kex_protocol_error(int type, u_int32_t seq, void *ctxt) { - error("Hm, kex protocol error: type %d seq %u", type, seq); + struct ssh *ssh = active_state; /* XXX */ + int r; + + error("kex protocol error: type %d seq %u", type, seq); + if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || + (r = sshpkt_put_u32(ssh, seq)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; return 0; } @@ -314,6 +333,20 @@ kex_reset_dispatch(struct ssh *ssh) ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); } +static int +kex_send_ext_info(struct ssh *ssh) +{ + int r; + + if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || + (r = sshpkt_put_u32(ssh, 1)) != 0 || + (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || + (r = sshpkt_put_cstring(ssh, "rsa-sha2-256,rsa-sha2-512")) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + return 0; +} + int kex_send_newkeys(struct ssh *ssh) { @@ -326,9 +359,51 @@ kex_send_newkeys(struct ssh *ssh) debug("SSH2_MSG_NEWKEYS sent"); debug("expecting SSH2_MSG_NEWKEYS"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); + if (ssh->kex->ext_info_c) + if ((r = kex_send_ext_info(ssh)) != 0) + return r; return 0; } +int +kex_input_ext_info(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + u_int32_t i, ninfo; + char *name, *val, *found; + int r; + + debug("SSH2_MSG_EXT_INFO received"); + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); + if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) + return r; + for (i = 0; i < ninfo; i++) { + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) + return r; + if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) { + free(name); + return r; + } + debug("%s: %s=<%s>", __func__, name, val); + if (strcmp(name, "server-sig-algs") == 0) { + found = match_list("rsa-sha2-256", val, NULL); + if (found) { + kex->rsa_sha2 = 256; + free(found); + } + found = match_list("rsa-sha2-512", val, NULL); + if (found) { + kex->rsa_sha2 = 512; + free(found); + } + } + free(name); + free(val); + } + return sshpkt_get_end(ssh); +} + static int kex_input_newkeys(int type, u_int32_t seq, void *ctxt) { @@ -468,7 +543,7 @@ kex_free_newkeys(struct newkeys *newkeys) newkeys->enc.key = NULL; } if (newkeys->enc.iv) { - explicit_bzero(newkeys->enc.iv, newkeys->enc.block_size); + explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); free(newkeys->enc.iv); newkeys->enc.iv = NULL; } @@ -511,6 +586,8 @@ kex_free(struct kex *kex) free(kex->client_version_string); free(kex->server_version_string); free(kex->failed_choice); + free(kex->hostkey_alg); + free(kex->name); free(kex); } @@ -529,6 +606,25 @@ kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) return 0; } +/* + * Request key re-exchange, returns 0 on success or a ssherr.h error + * code otherwise. Must not be called if KEX is incomplete or in-progress. + */ +int +kex_start_rekex(struct ssh *ssh) +{ + if (ssh->kex == NULL) { + error("%s: no kex", __func__); + return SSH_ERR_INTERNAL_ERROR; + } + if (ssh->kex->done == 0) { + error("%s: requested twice", __func__); + return SSH_ERR_INTERNAL_ERROR; + } + ssh->kex->done = 0; + return kex_send_kexinit(ssh); +} + static int choose_enc(struct sshenc *enc, char *client, char *server) { @@ -593,6 +689,7 @@ choose_kex(struct kex *k, char *client, char *server) k->name = match_list(client, server, NULL); + debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); if (k->name == NULL) return SSH_ERR_NO_KEX_ALG_MATCH; if ((kexalg = kex_alg_by_name(k->name)) == NULL) @@ -606,15 +703,16 @@ choose_kex(struct kex *k, char *client, char *server) static int choose_hostkeyalg(struct kex *k, char *client, char *server) { - char *hostkeyalg = match_list(client, server, NULL); + k->hostkey_alg = match_list(client, server, NULL); - if (hostkeyalg == NULL) + debug("kex: host key algorithm: %s", + k->hostkey_alg ? k->hostkey_alg : "(no match)"); + if (k->hostkey_alg == NULL) return SSH_ERR_NO_HOSTKEY_ALG_MATCH; - k->hostkey_type = sshkey_type_from_name(hostkeyalg); + k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); if (k->hostkey_type == KEY_UNSPEC) return SSH_ERR_INTERNAL_ERROR; - k->hostkey_nid = sshkey_ecdsa_nid_from_name(hostkeyalg); - free(hostkeyalg); + k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); return 0; } @@ -653,8 +751,11 @@ kex_choose_conf(struct ssh *ssh) u_int mode, ctos, need, dh_need, authlen; int r, first_kex_follows; - if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0 || - (r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) + debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); + if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) + goto out; + debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); + if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) goto out; if (kex->server) { @@ -665,18 +766,30 @@ kex_choose_conf(struct ssh *ssh) sprop=peer; } - /* Check whether server offers roaming */ - if (!kex->server) { - char *roaming = match_list(KEX_RESUME, - peer[PROPOSAL_KEX_ALGS], NULL); + /* Check whether client supports ext_info_c */ + if (kex->server) { + char *ext; - if (roaming) { - kex->roaming = 1; - free(roaming); + ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); + if (ext) { + kex->ext_info_c = 1; + free(ext); } } /* Algorithm Negotiation */ + if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], + sprop[PROPOSAL_KEX_ALGS])) != 0) { + kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; + peer[PROPOSAL_KEX_ALGS] = NULL; + goto out; + } + if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { + kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; + peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; + goto out; + } for (mode = 0; mode < MODE_MAX; mode++) { if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { r = SSH_ERR_ALLOC_FAIL; @@ -709,24 +822,12 @@ kex_choose_conf(struct ssh *ssh) peer[ncomp] = NULL; goto out; } - debug("kex: %s %s %s %s", + debug("kex: %s cipher: %s MAC: %s compression: %s", ctos ? "client->server" : "server->client", newkeys->enc.name, authlen == 0 ? newkeys->mac.name : "<implicit>", newkeys->comp.name); } - if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], - sprop[PROPOSAL_KEX_ALGS])) != 0) { - kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; - peer[PROPOSAL_KEX_ALGS] = NULL; - goto out; - } - if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], - sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { - kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; - peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; - goto out; - } need = dh_need = 0; for (mode = 0; mode < MODE_MAX; mode++) { newkeys = kex->newkeys[mode]; @@ -812,8 +913,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, digest = NULL; r = 0; out: - if (digest) - free(digest); + free(digest); ssh_digest_free(hashctx); return r; } |