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; debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); 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); nbits = MIN(DH_GRP_MAX, nbits); 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 */ 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"); 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; /* Compute our exchange value in parallel with the client */ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) goto out; debug("expecting 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; }
int ssh_dispatch_run(struct ssh *ssh, int mode, volatile sig_atomic_t *done) { int r; u_char type; u_int32_t seqnr; for (;;) { if (mode == DISPATCH_BLOCK) { r = ssh_packet_read_seqnr(ssh, &type, &seqnr); if (r != 0) return r; } else { r = ssh_packet_read_poll_seqnr(ssh, &type, &seqnr); if (r != 0) return r; if (type == SSH_MSG_NONE) return 0; } if (type > 0 && type < DISPATCH_MAX && ssh->dispatch[type] != NULL) { if (ssh->skip_packets) { debug2("skipped packet (type %u)", type); ssh->skip_packets--; continue; } r = (*ssh->dispatch[type])(type, seqnr, ssh); if (r != 0) return r; } else { r = sshpkt_disconnect(ssh, "protocol error: rcvd type %d", type); if (r != 0) return r; return SSH_ERR_DISCONNECTED; } if (done != NULL && *done) return 0; } }
static int input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; struct sshkey *server_host_key; u_char *kbuf = NULL, *hash, *signature = NULL, *server_host_key_blob = NULL; size_t klen = 0, slen, sbloblen, hashlen; int kout, r; debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); if (kex->verify_host_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } /* key, cert */ 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; } /* DH parameter f, server public DH key */ if ((dh_server_pub = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* signed H */ if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 || (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_server_pub= "); BN_print_fp(stderr, dh_server_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #endif if (!dh_pub_is_valid(kex->dh, dh_server_pub)) { sshpkt_disconnect(ssh, "bad server public DH value"); r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } 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_server_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 (ssh->compat & SSH_OLD_DHGEX) kex->min = kex->max = -1; /* calc and verify H */ if ((r = kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->my), sshbuf_len(kex->my), sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, kex->min, kex->nbits, kex->max, kex->dh->p, kex->dh->g, kex->dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen)) != 0) goto out; 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 = 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); } if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: DH_free(kex->dh); kex->dh = NULL; if (server_host_key_blob) free(server_host_key_blob); if (server_host_key) sshkey_free(server_host_key); if (dh_server_pub) BN_clear_free(dh_server_pub); if (kbuf) { bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); if (signature) free(signature); return r; }
static int input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; EC_POINT *client_public; EC_KEY *server_key = NULL; const EC_GROUP *group; 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 = NULL; u_char hash[SSH_DIGEST_MAX_LENGTH]; size_t slen, sbloblen; size_t klen = 0, hashlen; int r; if ((server_key = EC_KEY_new_by_curve_name(kex->ec_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 fputs("server private key:\n", stderr); sshkey_dump_ec_key(server_key); #endif if (kex->load_host_public_key == NULL || kex->load_host_private_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } server_host_public = kex->load_host_public_key(kex->hostkey_type, kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; 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; 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 || BN_bin2bn(kbuf, klen, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif /* calc H */ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, &sbloblen)) != 0) goto out; hashlen = sizeof(hash); if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, client_public, EC_KEY_get0_public_key(server_key), shared_secret, hash, &hashlen)) != 0) goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; 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 */ if ((r = kex->sign(server_host_private, server_host_public, &signature, &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) goto out; /* destroy_sensitive_data(); */ public_key = EC_KEY_get0_public_key(server_key); /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ 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; 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_key) EC_KEY_free(server_key); if (kbuf) { explicit_bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); free(server_host_key_blob); free(signature); 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 */ 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 || (kex->hostkey_type == KEY_ECDSA && server_host_key->ecdsa_nid != kex->hostkey_nid)) { 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 */ /* 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 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; 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 || BN_bin2bn(kbuf, klen, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif /* calc and verify H */ hashlen = sizeof(hash); if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, 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)) != 0) goto out; 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 = 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); } 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; }
int input_kex_dh_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; } server_host_public = kex->load_host_public_key(kex->hostkey_type, kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; } /* key, cert */ 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= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif 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(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 ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, &sbloblen)) != 0) goto out; /* calc H */ hashlen = sizeof(hash); if ((r = kex_dh_hash( kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, dh_client_pub, kex->dh->pub_key, shared_secret, hash, &hashlen)) != 0) goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; 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 */ if ((r = kex->sign(server_host_private, server_host_public, &signature, &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) goto out; /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_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; if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: explicit_bzero(hash, sizeof(hash)); 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); free(signature); return r; }
static int input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; int r, min = -1, max = -1, nbits = -1; switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); 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); nbits = MIN(DH_GRP_MAX, nbits); break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); if ((r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; /* unused for old GEX */ kex->min = min = DH_GRP_MIN; kex->max = max = DH_GRP_MAX; break; default: r = SSH_ERR_INVALID_ARGUMENT; goto out; } 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 */ 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"); 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; /* Compute our exchange value in parallel with the client */ 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"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); r = 0; out: return r; }