123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- /*
- * Server side of key exchange for the SSH-2 transport protocol (RFC 4253).
- */
- #include <assert.h>
- #include "putty.h"
- #include "ssh.h"
- #include "bpp.h"
- #include "ppl.h"
- #include "sshcr.h"
- #include "server.h"
- #include "sshkeygen.h"
- #include "storage.h"
- #include "transport2.h"
- #include "mpint.h"
- void ssh2_transport_provide_hostkeys(PacketProtocolLayer *ppl,
- ssh_key *const *hostkeys, int nhostkeys)
- {
- struct ssh2_transport_state *s =
- container_of(ppl, struct ssh2_transport_state, ppl);
- s->hostkeys = hostkeys;
- s->nhostkeys = nhostkeys;
- }
- static strbuf *finalise_and_sign_exhash(struct ssh2_transport_state *s)
- {
- strbuf *sb;
- ssh2transport_finalise_exhash(s);
- sb = strbuf_new();
- ssh_key_sign(
- s->hkey, make_ptrlen(s->exchange_hash, s->kex_alg->hash->hlen),
- s->hkflags, BinarySink_UPCAST(sb));
- return sb;
- }
- void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
- {
- PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
- PktIn *pktin;
- PktOut *pktout;
- crBegin(s->crStateKex);
- {
- int i;
- for (i = 0; i < s->nhostkeys; i++)
- if (ssh_key_alg(s->hostkeys[i]) == s->hostkey_alg) {
- s->hkey = s->hostkeys[i];
- break;
- }
- assert(s->hkey);
- }
- strbuf_clear(s->hostkeyblob);
- ssh_key_public_blob(s->hkey, BinarySink_UPCAST(s->hostkeyblob));
- s->hostkeydata = ptrlen_from_strbuf(s->hostkeyblob);
- put_stringpl(s->exhash, s->hostkeydata);
- if (s->kex_alg->main_type == KEXTYPE_DH) {
- /*
- * If we're doing Diffie-Hellman group exchange, start by
- * waiting for the group request.
- */
- if (dh_is_gex(s->kex_alg)) {
- ppl_logevent("Doing Diffie-Hellman group exchange");
- s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGEX;
- crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
- if (pktin->type != SSH2_MSG_KEX_DH_GEX_REQUEST &&
- pktin->type != SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) {
- ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
- "expecting Diffie-Hellman group exchange "
- "request, type %d (%s)", pktin->type,
- ssh2_pkt_type(s->ppl.bpp->pls->kctx,
- s->ppl.bpp->pls->actx,
- pktin->type));
- *aborted = true;
- return;
- }
- if (pktin->type != SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) {
- s->dh_got_size_bounds = true;
- s->dh_min_size = get_uint32(pktin);
- s->pbits = get_uint32(pktin);
- s->dh_max_size = get_uint32(pktin);
- } else {
- s->dh_got_size_bounds = false;
- s->pbits = get_uint32(pktin);
- }
- /*
- * This is a hopeless strategy for making a secure DH
- * group! It's good enough for testing a client against,
- * but not for serious use.
- */
- PrimeGenerationContext *pgc = primegen_new_context(
- &primegen_probabilistic);
- ProgressReceiver null_progress;
- null_progress.vt = &null_progress_vt;
- s->p = primegen_generate(pgc, pcs_new(s->pbits), &null_progress);
- primegen_free_context(pgc);
- s->g = mp_from_integer(2);
- s->dh_ctx = dh_setup_gex(s->p, s->g);
- s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
- s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEX_DH_GEX_GROUP);
- put_mp_ssh2(pktout, s->p);
- put_mp_ssh2(pktout, s->g);
- pq_push(s->ppl.out_pq, pktout);
- } else {
- s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGROUP;
- s->dh_ctx = dh_setup_group(s->kex_alg);
- s->kex_init_value = SSH2_MSG_KEXDH_INIT;
- s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
- ppl_logevent("Using Diffie-Hellman with standard group \"%s\"",
- s->kex_alg->groupname);
- }
- ppl_logevent("Doing Diffie-Hellman key exchange with hash %s",
- ssh_hash_alg(s->exhash)->text_name);
- /*
- * Generate e for Diffie-Hellman.
- */
- s->e = dh_create_e(s->dh_ctx);
- /*
- * Wait to receive f.
- */
- crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
- if (pktin->type != s->kex_init_value) {
- ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
- "expecting Diffie-Hellman initial packet, "
- "type %d (%s)", pktin->type,
- ssh2_pkt_type(s->ppl.bpp->pls->kctx,
- s->ppl.bpp->pls->actx,
- pktin->type));
- *aborted = true;
- return;
- }
- s->f = get_mp_ssh2(pktin);
- if (get_err(pktin)) {
- ssh_proto_error(s->ppl.ssh,
- "Unable to parse Diffie-Hellman initial packet");
- *aborted = true;
- return;
- }
- {
- const char *err = dh_validate_f(s->dh_ctx, s->f);
- if (err) {
- ssh_proto_error(s->ppl.ssh, "Diffie-Hellman initial packet "
- "failed validation: %s", err);
- *aborted = true;
- return;
- }
- }
- mp_int *K = dh_find_K(s->dh_ctx, s->f);
- put_mp_ssh2(s->kex_shared_secret, K);
- mp_free(K);
- if (dh_is_gex(s->kex_alg)) {
- if (s->dh_got_size_bounds)
- put_uint32(s->exhash, s->dh_min_size);
- put_uint32(s->exhash, s->pbits);
- if (s->dh_got_size_bounds)
- put_uint32(s->exhash, s->dh_max_size);
- put_mp_ssh2(s->exhash, s->p);
- put_mp_ssh2(s->exhash, s->g);
- }
- put_mp_ssh2(s->exhash, s->f);
- put_mp_ssh2(s->exhash, s->e);
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_reply_value);
- put_stringpl(pktout, s->hostkeydata);
- put_mp_ssh2(pktout, s->e);
- put_stringsb(pktout, finalise_and_sign_exhash(s));
- pq_push(s->ppl.out_pq, pktout);
- dh_cleanup(s->dh_ctx);
- s->dh_ctx = NULL;
- mp_free(s->f); s->f = NULL;
- if (dh_is_gex(s->kex_alg)) {
- mp_free(s->g); s->g = NULL;
- mp_free(s->p); s->p = NULL;
- }
- } else if (s->kex_alg->main_type == KEXTYPE_ECDH) {
- char *desc = ecdh_keyalg_description(s->kex_alg);
- ppl_logevent("Doing %s, using hash %s", desc,
- ssh_hash_alg(s->exhash)->text_name);
- sfree(desc);
- s->ecdh_key = ecdh_key_new(s->kex_alg, true);
- if (!s->ecdh_key) {
- ssh_sw_abort(s->ppl.ssh, "Unable to generate key for ECDH");
- *aborted = true;
- return;
- }
- crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
- if (pktin->type != SSH2_MSG_KEX_ECDH_INIT) {
- ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
- "expecting ECDH initial packet, type %d (%s)",
- pktin->type,
- ssh2_pkt_type(s->ppl.bpp->pls->kctx,
- s->ppl.bpp->pls->actx,
- pktin->type));
- *aborted = true;
- return;
- }
- {
- ptrlen keydata = get_string(pktin);
- put_stringpl(s->exhash, keydata);
- bool ok = ecdh_key_getkey(s->ecdh_key, keydata,
- BinarySink_UPCAST(s->kex_shared_secret));
- if (!get_err(pktin) && !ok) {
- ssh_proto_error(s->ppl.ssh, "Received invalid elliptic curve "
- "point in ECDH initial packet");
- *aborted = true;
- return;
- }
- }
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEX_ECDH_REPLY);
- put_stringpl(pktout, s->hostkeydata);
- {
- strbuf *pubpoint = strbuf_new();
- ecdh_key_getpublic(s->ecdh_key, BinarySink_UPCAST(pubpoint));
- put_string(s->exhash, pubpoint->u, pubpoint->len);
- put_stringsb(pktout, pubpoint);
- }
- put_stringsb(pktout, finalise_and_sign_exhash(s));
- pq_push(s->ppl.out_pq, pktout);
- ecdh_key_free(s->ecdh_key);
- s->ecdh_key = NULL;
- } else if (s->kex_alg->main_type == KEXTYPE_GSS) {
- ssh_sw_abort(s->ppl.ssh, "GSS key exchange not supported in server");
- } else {
- assert(s->kex_alg->main_type == KEXTYPE_RSA);
- ppl_logevent("Doing RSA key exchange with hash %s",
- ssh_hash_alg(s->exhash)->text_name);
- s->ppl.bpp->pls->kctx = SSH2_PKTCTX_RSAKEX;
- const struct ssh_rsa_kex_extra *extra =
- (const struct ssh_rsa_kex_extra *)s->kex_alg->extra;
- if (s->ssc && s->ssc->rsa_kex_key) {
- int klen = ssh_rsakex_klen(s->ssc->rsa_kex_key);
- if (klen >= extra->minklen) {
- ppl_logevent("Using configured %d-bit RSA key", klen);
- s->rsa_kex_key = s->ssc->rsa_kex_key;
- } else {
- ppl_logevent("Configured %d-bit RSA key is too short (min %d)",
- klen, extra->minklen);
- }
- }
- if (!s->rsa_kex_key) {
- ppl_logevent("Generating a %d-bit RSA key", extra->minklen);
- s->rsa_kex_key = snew(RSAKey);
- PrimeGenerationContext *pgc = primegen_new_context(
- &primegen_probabilistic);
- ProgressReceiver null_progress;
- null_progress.vt = &null_progress_vt;
- rsa_generate(s->rsa_kex_key, extra->minklen, false,
- pgc, &null_progress);
- primegen_free_context(pgc);
- s->rsa_kex_key->comment = NULL;
- s->rsa_kex_key_needs_freeing = true;
- }
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_PUBKEY);
- put_stringpl(pktout, s->hostkeydata);
- {
- strbuf *pubblob = strbuf_new();
- ssh_key_public_blob(&s->rsa_kex_key->sshk,
- BinarySink_UPCAST(pubblob));
- put_string(s->exhash, pubblob->u, pubblob->len);
- put_stringsb(pktout, pubblob);
- }
- pq_push(s->ppl.out_pq, pktout);
- crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
- if (pktin->type != SSH2_MSG_KEXRSA_SECRET) {
- ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
- "expecting RSA kex secret, type %d (%s)",
- pktin->type,
- ssh2_pkt_type(s->ppl.bpp->pls->kctx,
- s->ppl.bpp->pls->actx,
- pktin->type));
- *aborted = true;
- return;
- }
- mp_int *K;
- {
- ptrlen encrypted_secret = get_string(pktin);
- put_stringpl(s->exhash, encrypted_secret);
- K = ssh_rsakex_decrypt(
- s->rsa_kex_key, s->kex_alg->hash, encrypted_secret);
- }
- if (!K) {
- ssh_proto_error(s->ppl.ssh, "Unable to decrypt RSA kex secret");
- *aborted = true;
- return;
- }
- put_mp_ssh2(s->kex_shared_secret, K);
- mp_free(K);
- if (s->rsa_kex_key_needs_freeing) {
- ssh_rsakex_freekey(s->rsa_kex_key);
- sfree(s->rsa_kex_key);
- }
- s->rsa_kex_key = NULL;
- s->rsa_kex_key_needs_freeing = false;
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_DONE);
- put_stringsb(pktout, finalise_and_sign_exhash(s));
- pq_push(s->ppl.out_pq, pktout);
- }
- crFinishV;
- }
|