Mercurial > dropbear
diff svr-kex.c @ 910:89555751c489 asm
merge up to 2013.63, improve ASM makefile rules a bit
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 27 Feb 2014 21:35:58 +0800 |
parents | cbc73a5aefb0 |
children | fd2e8bbb0333 |
line wrap: on
line diff
--- a/svr-kex.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-kex.c Thu Feb 27 21:35:58 2014 +0800 @@ -32,11 +32,12 @@ #include "ssh.h" #include "packet.h" #include "bignum.h" -#include "random.h" +#include "dbrandom.h" #include "runopts.h" +#include "ecc.h" +#include "gensignkey.h" - -static void send_msg_kexdh_reply(mp_int *dh_e); +static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs); /* Handle a diffie-hellman key exchange initialisation. This involves * calculating a session key reply value, and corresponding hash. These @@ -45,59 +46,192 @@ void recv_msg_kexdh_init() { DEF_MP_INT(dh_e); + buffer *ecdh_qs = NULL; TRACE(("enter recv_msg_kexdh_init")) if (!ses.kexstate.recvkexinit) { dropbear_exit("Premature kexdh_init message received"); } - m_mp_init(&dh_e); - if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) { - dropbear_exit("Failed to get kex value"); + switch (ses.newkeys->algo_kex->mode) { + case DROPBEAR_KEX_NORMAL_DH: + m_mp_init(&dh_e); + if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) { + dropbear_exit("Bad kex value"); + } + break; + case DROPBEAR_KEX_ECDH: + case DROPBEAR_KEX_CURVE25519: +#if defined(DROPBEAR_ECDH) || defined(DROPBEAR_CURVE25519) + ecdh_qs = buf_getstringbuf(ses.payload); +#endif + break; + } + if (ses.payload->pos != ses.payload->len) { + dropbear_exit("Bad kex value"); + } + + send_msg_kexdh_reply(&dh_e, ecdh_qs); + + mp_clear(&dh_e); + if (ecdh_qs) { + buf_free(ecdh_qs); + ecdh_qs = NULL; + } + + send_msg_newkeys(); + ses.requirenext = SSH_MSG_NEWKEYS; + TRACE(("leave recv_msg_kexdh_init")) +} + +#ifdef DROPBEAR_DELAY_HOSTKEY +static void svr_ensure_hostkey() { + + const char* fn = NULL; + char *fn_temp = NULL; + enum signkey_type type = ses.newkeys->algo_hostkey; + void **hostkey = signkey_key_ptr(svr_opts.hostkey, type); + int ret = DROPBEAR_FAILURE; + + if (hostkey && *hostkey) { + return; } - send_msg_kexdh_reply(&dh_e); + switch (type) + { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + fn = RSA_PRIV_FILENAME; + break; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + fn = DSS_PRIV_FILENAME; + break; +#endif +#ifdef DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + fn = ECDSA_PRIV_FILENAME; + break; +#endif + default: + (void)0; + } - mp_clear(&dh_e); + if (readhostkey(fn, svr_opts.hostkey, &type) == DROPBEAR_SUCCESS) { + return; + } + + fn_temp = m_malloc(strlen(fn) + 20); + snprintf(fn_temp, strlen(fn)+20, "%s.tmp%d", fn, getpid()); + + if (signkey_generate(type, 0, fn_temp) == DROPBEAR_FAILURE) { + goto out; + } - send_msg_newkeys(); - ses.requirenext[0] = SSH_MSG_NEWKEYS; - ses.requirenext[1] = 0; - TRACE(("leave recv_msg_kexdh_init")) + if (link(fn_temp, fn) < 0) { + /* It's OK to get EEXIST - we probably just lost a race + with another connection to generate the key */ + if (errno != EEXIST) { + dropbear_log(LOG_ERR, "Failed moving key file to %s: %s", fn, + strerror(errno)); + /* XXX fallback to non-atomic copy for some filesystems? */ + goto out; + } + } + + ret = readhostkey(fn, svr_opts.hostkey, &type); + + if (ret == DROPBEAR_SUCCESS) { + char *fp = NULL; + unsigned int len; + buffer *key_buf = buf_new(MAX_PUBKEY_SIZE); + buf_put_pub_key(key_buf, svr_opts.hostkey, type); + buf_setpos(key_buf, 4); + len = key_buf->len - key_buf->pos; + fp = sign_key_fingerprint(buf_getptr(key_buf, len), len); + dropbear_log(LOG_INFO, "Generated hostkey %s, fingerprint is %s", + fn, fp); + m_free(fp); + buf_free(key_buf); + } + +out: + if (fn_temp) { + unlink(fn_temp); + m_free(fn_temp); + } + + if (ret == DROPBEAR_FAILURE) + { + dropbear_exit("Couldn't read or generate hostkey %s", fn); + } } +#endif /* Generate our side of the diffie-hellman key exchange value (dh_f), and * calculate the session key using the diffie-hellman algorithm. Following * that, the session hash is calculated, and signed with RSA or DSS. The * result is sent to the client. * - * See the transport rfc 4253 section 8 for details */ -static void send_msg_kexdh_reply(mp_int *dh_e) { - - DEF_MP_INT(dh_y); - DEF_MP_INT(dh_f); - + * See the transport RFC4253 section 8 for details + * or RFC5656 section 4 for elliptic curve variant. */ +static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { TRACE(("enter send_msg_kexdh_reply")) - m_mp_init_multi(&dh_y, &dh_f, NULL); - - gen_kexdh_vals(&dh_f, &dh_y); - - kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey); - mp_clear(&dh_y); /* we can start creating the kexdh_reply packet */ CHECKCLEARTOWRITE(); + +#ifdef DROPBEAR_DELAY_HOSTKEY + if (svr_opts.delay_hostkey) + { + svr_ensure_hostkey(); + } +#endif + buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); buf_put_pub_key(ses.writepayload, svr_opts.hostkey, ses.newkeys->algo_hostkey); - /* put f */ - buf_putmpint(ses.writepayload, &dh_f); - mp_clear(&dh_f); + switch (ses.newkeys->algo_kex->mode) { + case DROPBEAR_KEX_NORMAL_DH: + { + struct kex_dh_param * dh_param = gen_kexdh_param(); + kexdh_comb_key(dh_param, dh_e, svr_opts.hostkey); + + /* put f */ + buf_putmpint(ses.writepayload, &dh_param->pub); + free_kexdh_param(dh_param); + } + break; + case DROPBEAR_KEX_ECDH: +#ifdef DROPBEAR_ECDH + { + struct kex_ecdh_param *ecdh_param = gen_kexecdh_param(); + kexecdh_comb_key(ecdh_param, ecdh_qs, svr_opts.hostkey); + + buf_put_ecc_raw_pubkey_string(ses.writepayload, &ecdh_param->key); + free_kexecdh_param(ecdh_param); + } +#endif + break; + case DROPBEAR_KEX_CURVE25519: +#ifdef DROPBEAR_CURVE25519 + { + struct kex_curve25519_param *param = gen_kexcurve25519_param(); + kexcurve25519_comb_key(param, ecdh_qs, svr_opts.hostkey); + buf_putstring(ses.writepayload, param->pub, CURVE25519_LEN); + free_kexcurve25519_param(param); + } +#endif + break; + } /* calc the signature */ buf_put_sign(ses.writepayload, svr_opts.hostkey, - ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE); + ses.newkeys->algo_hostkey, ses.hash); /* the SSH_MSG_KEXDH_REPLY is done */ encrypt_packet();