diff options
Diffstat (limited to 'kexecdhc.c')
-rw-r--r-- | kexecdhc.c | 207 |
1 files changed, 137 insertions, 70 deletions
diff --git a/kexecdhc.c b/kexecdhc.c index df811c1c8..3f362c5b1 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhc.c,v 1.8 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexecdhc.c,v 1.9 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -34,126 +34,193 @@ #include <string.h> #include <signal.h> -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include <openssl/ecdh.h> + +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" +#include "dispatch.h" +#include "compat.h" +#include "ssherr.h" +#include "sshbuf.h" -#include <openssl/ecdh.h> +static int input_kex_ecdh_reply(int, u_int32_t, void *); -void -kexecdh_client(Kex *kex) +int +kexecdh_client(struct ssh *ssh) { - EC_KEY *client_key; - EC_POINT *server_public; + struct kex *kex = ssh->kex; + EC_KEY *client_key = NULL; const EC_GROUP *group; - BIGNUM *shared_secret; - Key *server_host_key; - u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *kbuf, *hash; - u_int klen, slen, sbloblen, hashlen; + const EC_POINT *public_key; + int r; - if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_generate_key(client_key) != 1) - fatal("%s: EC_KEY_generate_key failed", __func__); + if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(client_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } group = EC_KEY_get0_group(client_key); + public_key = EC_KEY_get0_public_key(client_key); - packet_start(SSH2_MSG_KEX_ECDH_INIT); - packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key)); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_INIT)) != 0 || + (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; debug("sending SSH2_MSG_KEX_ECDH_INIT"); #ifdef DEBUG_KEXECDH fputs("client private key:\n", stderr); - key_dump_ec_key(client_key); + sshkey_dump_ec_key(client_key); #endif + kex->ec_client_key = client_key; + kex->ec_group = group; + client_key = NULL; /* owned by the kex */ debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); - packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_ecdh_reply); + r = 0; + out: + if (client_key) + EC_KEY_free(client_key); + return r; +} + +static int +input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + const EC_GROUP *group; + EC_POINT *server_public = NULL; + EC_KEY *client_key; + BIGNUM *shared_secret = NULL; + struct sshkey *server_host_key = NULL; + u_char *server_host_key_blob = NULL, *signature = NULL; + u_char *kbuf = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, sbloblen; + size_t klen = 0, hashlen; + int r; + + if (kex->verify_host_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + group = kex->ec_group; + client_key = kex->ec_client_key; /* hostkey */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - if (server_host_key->type != kex->hostkey_type) - fatal("type mismatch for decoded server_host_key_blob"); - if (kex->verify_host_key == NULL) - fatal("cannot verify server_host_key"); - if (kex->verify_host_key(server_host_key) == -1) - fatal("server_host_key verification failed"); + if ((r = sshpkt_get_string(ssh, &server_host_key_blob, + &sbloblen)) != 0 || + (r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + goto out; + if (server_host_key->type != kex->hostkey_type) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (kex->verify_host_key(server_host_key, ssh) == -1) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } /* Q_S, server public key */ - if ((server_public = EC_POINT_new(group)) == NULL) - fatal("%s: EC_POINT_new failed", __func__); - packet_get_ecpoint(group, server_public); - - if (key_ec_validate_public(group, server_public) != 0) - fatal("%s: invalid server public key", __func__); + /* signed H */ + if ((server_public = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_ec(ssh, server_public, group)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXECDH fputs("server public key:\n", stderr); - key_dump_ec_point(group, server_public); + sshkey_dump_ec_point(group, server_public); #endif - - /* signed H */ - signature = packet_get_string(&slen); - packet_check_eom(); + if (sshkey_ec_validate_public(group, server_public) != 0) { + sshpkt_disconnect(ssh, "invalid server public key"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } klen = (EC_GROUP_get_degree(group) + 7) / 8; - kbuf = xmalloc(klen); + 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, - client_key, NULL) != (int)klen) - fatal("%s: ECDH_compute_key failed", __func__); + 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 - if ((shared_secret = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) - fatal("%s: BN_bin2bn failed", __func__); - explicit_bzero(kbuf, klen); - free(kbuf); - /* calc and verify H */ - kex_ecdh_hash( + hashlen = sizeof(hash); + if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->my), buffer_len(kex->my), - buffer_ptr(kex->peer), buffer_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, EC_KEY_get0_public_key(client_key), server_public, shared_secret, - &hash, &hashlen - ); - free(server_host_key_blob); - EC_POINT_clear_free(server_public); - EC_KEY_free(client_key); + hash, &hashlen)) != 0) + goto out; - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - free(signature); + if ((r = sshkey_verify(server_host_key, signature, slen, hash, + hashlen, ssh->compat)) != 0) + goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - kex_finish(kex); + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + explicit_bzero(hash, sizeof(hash)); + 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); + sshkey_free(server_host_key); + free(server_host_key_blob); + free(signature); + return r; } #endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ + |