=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/kexgexs.c,v retrieving revision 1.20 retrieving revision 1.21 diff -u -r1.20 -r1.21 --- src/usr.bin/ssh/kexgexs.c 2015/01/19 19:52:16 1.20 +++ src/usr.bin/ssh/kexgexs.c 2015/01/19 20:16:15 1.21 @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexs.c,v 1.20 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexgexs.c,v 1.21 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -32,10 +32,9 @@ #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" @@ -46,33 +45,43 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" +#include "dispatch.h" +#include "ssherr.h" +#include "sshbuf.h" -void -kexgex_server(Kex *kex) +static int input_kex_dh_gex_request(int, u_int32_t, void *); +static int input_kex_dh_gex_init(int, u_int32_t, void *); + +int +kexgex_server(struct ssh *ssh) { - BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; - Key *server_host_public, *server_host_private; - DH *dh; - u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; - u_int sbloblen, klen, slen, hashlen; - int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; - int type, kout; + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, + &input_kex_dh_gex_request); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, + &input_kex_dh_gex_request); + debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST"); + return 0; +} - 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); - if (server_host_public == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - server_host_private = kex->load_host_private_key(kex->hostkey_type); +static int +input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + int r; + u_int min = 0, max = 0, nbits = 0; - type = packet_read(); switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); - omin = min = packet_get_int(); - onbits = nbits = packet_get_int(); - omax = max = packet_get_int(); + if ((r = sshpkt_get_u32(ssh, &min)) != 0 || + (r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_u32(ssh, &max)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; + kex->min = min; + kex->max = max; min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); @@ -80,45 +89,88 @@ break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); - onbits = nbits = packet_get_int(); + if ((r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; /* unused for old GEX */ - omin = min = DH_GRP_MIN; - omax = max = DH_GRP_MAX; + kex->min = min = DH_GRP_MIN; + kex->max = max = DH_GRP_MAX; break; default: - fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; } - packet_check_eom(); - if (omax < omin || onbits < omin || omax < onbits) - fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", - omin, onbits, omax); + if (kex->max < kex->min || kex->nbits < kex->min || + kex->max < kex->nbits) { + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } /* Contact privileged parent */ - dh = PRIVSEP(choose_dh(min, nbits, max)); - if (dh == NULL) - packet_disconnect("Protocol error: no matching DH grp found"); - + kex->dh = PRIVSEP(choose_dh(min, nbits, max)); + if (kex->dh == NULL) { + sshpkt_disconnect(ssh, "no matching DH grp found"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); - packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); - packet_put_bignum2(dh->p); - packet_put_bignum2(dh->g); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; - /* flush */ - packet_write_wait(); - /* Compute our exchange value in parallel with the client */ - dh_gen_key(dh, kex->we_need * 8); + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) + goto out; + /* old KEX does not use min/max in kexgex_hash() */ + if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) + kex->min = kex->max = -1; + debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); - packet_read_expect(SSH2_MSG_KEX_DH_GEX_INIT); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); + r = 0; + out: + return r; +} +static int +input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + struct sshkey *server_host_public, *server_host_private; + u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t sbloblen, slen; + size_t klen = 0, hashlen; + int kout, r; + + if (kex->load_host_public_key == NULL || + 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_NO_HOSTKEY_LOADED; + goto out; + } + /* key, cert */ - if ((dh_client_pub = BN_new()) == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub); - packet_check_eom(); + if ((dh_client_pub = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); @@ -128,78 +180,91 @@ #endif #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, dh); + DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); - BN_print_fp(stderr, dh->pub_key); + BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); + if (!dh_pub_is_valid(kex->dh, dh_client_pub)) { + sshpkt_disconnect(ssh, "bad client public DH value"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } - klen = DH_size(dh); - kbuf = xmalloc(klen); - if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) - fatal("DH_compute_key: failed"); + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("kexgex_server: BN_new failed"); - if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) - fatal("kexgex_server: BN_bin2bn failed"); - explicit_bzero(kbuf, klen); - free(kbuf); - - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); - - if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) - omin = min = omax = max = -1; - + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + goto out; /* calc H */ - kexgex_hash( + hashlen = sizeof(hash); + if ((r = kexgex_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), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, - omin, onbits, omax, - dh->p, dh->g, + kex->min, kex->nbits, kex->max, + kex->dh->p, kex->dh->g, dh_client_pub, - dh->pub_key, + kex->dh->pub_key, shared_secret, - &hash, &hashlen - ); - BN_clear_free(dh_client_pub); + 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); } /* sign H */ - kex->sign(server_host_private, server_host_public, &signature, &slen, - hash, hashlen); + if ((r = kex->sign(server_host_private, server_host_public, + &signature, &slen, hash, hashlen, ssh->compat)) < 0) + goto out; /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ - debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); - packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); - packet_put_string(server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string(signature, slen); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; - free(signature); + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + DH_free(kex->dh); + kex->dh = NULL; + if (dh_client_pub) + BN_clear_free(dh_client_pub); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); free(server_host_key_blob); - /* have keys, free DH */ - DH_free(dh); - - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - - kex_finish(kex); + free(signature); + return r; }