diff --git a/ssh/kex.h b/ssh/kex.h index 2a3dbb6..bac70ba 100644 --- a/ssh/kex.h +++ b/ssh/kex.h @@ -164,10 +164,10 @@ int kexgex_hash(const EVP_MD *, char *, char *, char *, size_t, char *, size_t, u_char *, size_t, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, u_char **, size_t *); -void -kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, int, - char *, int, u_char *, int, const EC_POINT *, const EC_POINT *, - const BIGNUM *, u_char **, u_int *); +int +kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, size_t, + char *, size_t, u_char *, size_t, const EC_POINT *, const EC_POINT *, + const BIGNUM *, u_char **, size_t *); int kex_ecdh_name_to_nid(const char *); const EVP_MD *kex_ecdh_name_to_evpmd(const char *); diff --git a/ssh/kexecdh.c b/ssh/kexecdh.c index 08544ff..1f5b2f5 100644 --- a/ssh/kexecdh.c +++ b/ssh/kexecdh.c @@ -40,12 +40,13 @@ #include "cipher.h" #include "kex.h" #include "log.h" +#include "err.h" int kex_ecdh_name_to_nid(const char *kexname) { if (strlen(kexname) < sizeof(KEX_ECDH_SHA2_STEM) - 1) - fatal("%s: kexname too short \"%s\"", __func__, kexname); + return -1; return sshkey_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA2_STEM) - 1); } @@ -60,54 +61,57 @@ kex_ecdh_name_to_evpmd(const char *kexname) return sshkey_ec_nid_to_evpmd(nid); } -void +int kex_ecdh_hash( const EVP_MD *evp_md, const EC_GROUP *ec_group, char *client_version_string, char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - u_char *serverhostkeyblob, int sbloblen, + char *ckexinit, size_t ckexinitlen, + char *skexinit, size_t skexinitlen, + u_char *serverhostkeyblob, size_t sbloblen, const EC_POINT *client_dh_pub, const EC_POINT *server_dh_pub, const BIGNUM *shared_secret, - u_char **hash, u_int *hashlen) + u_char **hash, size_t *hashlen) { - Buffer b; + struct sshbuf *b; EVP_MD_CTX md; static u_char digest[EVP_MAX_MD_SIZE]; + int r; - buffer_init(&b); - buffer_put_cstring(&b, client_version_string); - buffer_put_cstring(&b, server_version_string); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - buffer_put_ecpoint(&b, ec_group, client_dh_pub); - buffer_put_ecpoint(&b, ec_group, server_dh_pub); - buffer_put_bignum2(&b, shared_secret); - + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || + (r = sshbuf_put_cstring(b, server_version_string)) != 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, ckexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 || + (r = sshbuf_put_u32(b, skexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, skexinit, skexinitlen)) != 0 || + (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 || + (r = sshbuf_put_ec(b, client_dh_pub, ec_group)) != 0 || + (r = sshbuf_put_ec(b, server_dh_pub, ec_group)) != 0 || + (r = sshbuf_put_bignum2(b, shared_secret)) != 0) { + sshbuf_free(b); + return r; + } #ifdef DEBUG_KEX buffer_dump(&b); #endif - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); - EVP_DigestFinal(&md, digest, NULL); - - buffer_free(&b); - + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, sshbuf_ptr(b), sshbuf_len(b)) != 1 || + EVP_DigestFinal(&md, digest, NULL) != 1) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); #ifdef DEBUG_KEX dump_digest("hash", digest, EVP_MD_size(evp_md)); #endif *hash = digest; *hashlen = EVP_MD_size(evp_md); + return 0; } - diff --git a/ssh/kexecdhc.c b/ssh/kexecdhc.c index 1e58dd6..d7ec900 100644 --- a/ssh/kexecdhc.c +++ b/ssh/kexecdhc.c @@ -51,33 +51,46 @@ void kexecdh_client(struct ssh *ssh) { Kex *kex = ssh->kex; - EC_KEY *client_key; + EC_KEY *client_key = NULL; const EC_GROUP *group; - int curve_nid; + const EC_POINT *public_key; + int r, curve_nid; - if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) - fatal("%s: unsupported ECDH curve \"%s\"", __func__, kex->name); - if ((client_key = EC_KEY_new_by_curve_name(curve_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 ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((client_key = EC_KEY_new_by_curve_name(curve_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); - ssh_packet_start(ssh, SSH2_MSG_KEX_ECDH_INIT); - ssh_packet_put_ecpoint(ssh, group, EC_KEY_get0_public_key(client_key)); - ssh_packet_send(ssh); + 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); sshkey_dump_ec_key(client_key); #endif - kex->ec_client_key = client_key; kex->ec_group = group; debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_ecdh_reply); + return; + out: + if (client_key) + EC_KEY_free(client_key); + fatal("%s: %s", __func__, ssh_err(r)); } static int @@ -85,68 +98,77 @@ input_kex_ecdh_reply(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; const EC_GROUP *group; - EC_POINT *server_public; + EC_POINT *server_public = NULL; EC_KEY *client_key; - BIGNUM *shared_secret; - struct sshkey *server_host_key; + BIGNUM *shared_secret = NULL; + struct sshkey *server_host_key = NULL; u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *kbuf, *hash; - u_int klen, slen, sbloblen, hashlen; + u_char *kbuf = NULL, *hash; + 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 = ssh_packet_get_string(ssh, &sbloblen); - if ((r = sshkey_from_blob(server_host_key_blob, sbloblen, + 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) - fatal("%s: could not parse server host key: %s", - __func__, ssh_err(r)); - 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, ssh) == -1) - fatal("server_host_key verification failed"); + 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__); - ssh_packet_get_ecpoint(ssh, group, server_public); - - if (sshkey_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); sshkey_dump_ec_point(group, server_public); #endif - - /* signed H */ - signature = ssh_packet_get_string(ssh, &slen); - ssh_packet_check_eom(ssh); + 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__); - memset(kbuf, 0, klen); - xfree(kbuf); - /* calc and verify H */ - kex_ecdh_hash( + if ((r = kex_ecdh_hash( kex->evp_md, group, kex->client_version_string, @@ -157,28 +179,46 @@ input_kex_ecdh_reply(int type, u_int32_t seq, struct ssh *ssh) EC_KEY_get0_public_key(client_key), server_public, shared_secret, - &hash, &hashlen - ); - xfree(server_host_key_blob); - EC_POINT_clear_free(server_public); - EC_KEY_free(kex->ec_client_key); - kex->ec_client_key = NULL; + &hash, &hashlen)) != 0) + goto out; if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, datafellows)) != 0) fatal("key_verify failed for server_host_key: %s", ssh_err(r)); - sshkey_free(server_host_key); - xfree(signature); /* 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); } + /* XXX check error */ kex_derive_keys(ssh, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); kex_finish(ssh); - return 0; + r = 0; + out: + if (kex->ec_client_key) { + EC_KEY_free(kex->ec_client_key); + kex->ec_client_key = NULL; + } + if (server_host_key_blob) + free(server_host_key_blob); + if (server_host_key) + sshkey_free(server_host_key); + if (server_public) + EC_POINT_clear_free(server_public); + if (kbuf) { + bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + if (signature) + free(signature); + return r; } diff --git a/ssh/kexecdhs.c b/ssh/kexecdhs.c index 245ac00..f35a744 100644 --- a/ssh/kexecdhs.c +++ b/ssh/kexecdhs.c @@ -61,21 +61,29 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; EC_POINT *client_public; - EC_KEY *server_key; + EC_KEY *server_key = NULL; const EC_GROUP *group; - BIGNUM *shared_secret; + const EC_POINT *public_key; + BIGNUM *shared_secret = NULL; struct sshkey *server_host_private, *server_host_public; u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *kbuf, *hash; - u_int klen, slen, sbloblen, hashlen; + u_char *kbuf = NULL, *hash; + u_int slen, sbloblen; + size_t klen = 0, hashlen; int curve_nid, r; - if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) - fatal("%s: unsupported ECDH curve \"%s\"", __func__, kex->name); - if ((server_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_generate_key(server_key) != 1) - fatal("%s: EC_KEY_generate_key failed", __func__); + if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((server_key = EC_KEY_new_by_curve_name(curve_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); #ifdef DEBUG_KEXECDH @@ -84,51 +92,57 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) #endif if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) - fatal("Cannot load hostkey"); - server_host_public = kex->load_host_public_key(kex->hostkey_type, ssh); - if (server_host_public == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - server_host_private = kex->load_host_private_key(kex->hostkey_type, ssh); - if (server_host_private == NULL) - fatal("Missing private key for hostkey type %d", - kex->hostkey_type); - - if ((client_public = EC_POINT_new(group)) == NULL) - fatal("%s: EC_POINT_new failed", __func__); - ssh_packet_get_ecpoint(ssh, group, client_public); - ssh_packet_check_eom(ssh); - - if (sshkey_ec_validate_public(group, client_public) != 0) - fatal("%s: invalid client public key", __func__); + kex->load_host_private_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((server_host_public = kex->load_host_public_key(kex->hostkey_type, + ssh)) == NULL || + (server_host_private = kex->load_host_private_key(kex->hostkey_type, + ssh)) == NULL) { + r = SSH_ERR_KEY_TYPE_MISMATCH; /* XXX */ + goto out; + } + if ((client_public = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_ec(ssh, client_public, group)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXECDH fputs("client public key:\n", stderr); sshkey_dump_ec_point(group, client_public); #endif + if (sshkey_ec_validate_public(group, client_public) != 0) { + sshpkt_disconnect(ssh, "invalid client public key"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } /* Calculate shared_secret */ 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, client_public, - server_key, NULL) != (int)klen) - fatal("%s: ECDH_compute_key failed", __func__); + server_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } -#ifdef DEBUG_KEXDH +#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__); - memset(kbuf, 0, klen); - xfree(kbuf); - /* calc H */ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, &sbloblen)) != 0) - fatal("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); - kex_ecdh_hash( + goto out; + if ((r = kex_ecdh_hash( kex->evp_md, group, kex->client_version_string, @@ -139,14 +153,17 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) client_public, EC_KEY_get0_public_key(server_key), shared_secret, - &hash, &hashlen - ); - EC_POINT_clear_free(client_public); + &hash, &hashlen)) != 0) + goto out; /* save session id := H */ 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); } @@ -157,20 +174,35 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) /* destroy_sensitive_data(); */ + public_key = EC_KEY_get0_public_key(server_key); /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ - ssh_packet_start(ssh, SSH2_MSG_KEX_ECDH_REPLY); - ssh_packet_put_string(ssh, server_host_key_blob, sbloblen); - ssh_packet_put_ecpoint(ssh, group, EC_KEY_get0_public_key(server_key)); - ssh_packet_put_string(ssh, signature, slen); - ssh_packet_send(ssh); - - xfree(signature); - xfree(server_host_key_blob); - /* have keys, free server key */ - EC_KEY_free(server_key); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + /* have keys */ kex_derive_keys(ssh, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); kex_finish(ssh); - return 0; + r = 0; + out: + if (kex->ec_client_key) { + EC_KEY_free(kex->ec_client_key); + kex->ec_client_key = NULL; + } + if (server_host_key_blob) + free(server_host_key_blob); + if (server_key) + EC_KEY_free(server_key); + if (kbuf) { + bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + if (signature) + free(signature); + return r; }