int key_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { int r; u_char *sig; size_t siglen; if (sigp != NULL) *sigp = NULL; if (lenp != NULL) *lenp = 0; if ((r = sshkey_sign(key, &sig, &siglen, data, datalen, datafellows)) != 0) { fatal_on_fatal_errors(r, __func__, 0); error("%s: %s", __func__, ssh_err(r)); return -1; } if (siglen > INT_MAX) fatal("%s: giant len %zu", __func__, siglen); if (sigp != NULL) *sigp = sig; if (lenp != NULL) *lenp = siglen; return 0; }
static void build_cert(struct sshbuf *b, const struct sshkey *k, const char *type, const struct sshkey *sign_key, const struct sshkey *ca_key, const char *sig_alg) { struct sshbuf *ca_buf, *pk, *principals, *critopts, *exts; u_char *sigblob; size_t siglen; ca_buf = sshbuf_new(); ASSERT_PTR_NE(ca_buf, NULL); ASSERT_INT_EQ(sshkey_putb(ca_key, ca_buf), 0); /* * Get the public key serialisation by rendering the key and skipping * the type string. This is a bit of a hack :/ */ pk = sshbuf_new(); ASSERT_PTR_NE(pk, NULL); ASSERT_INT_EQ(sshkey_putb_plain(k, pk), 0); ASSERT_INT_EQ(sshbuf_skip_string(pk), 0); principals = sshbuf_new(); ASSERT_PTR_NE(principals, NULL); ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gsamsa"), 0); ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gregor"), 0); critopts = sshbuf_new(); ASSERT_PTR_NE(critopts, NULL); put_opt(critopts, "force-command", "/usr/local/bin/nethack"); put_opt(critopts, "source-address", "192.168.0.0/24,127.0.0.1,::1"); exts = sshbuf_new(); ASSERT_PTR_NE(exts, NULL); put_opt(critopts, "permit-X11-forwarding", NULL); ASSERT_INT_EQ(sshbuf_put_cstring(b, type), 0); ASSERT_INT_EQ(sshbuf_put_cstring(b, "noncenoncenonce!"), 0); /* nonce */ ASSERT_INT_EQ(sshbuf_putb(b, pk), 0); /* public key serialisation */ ASSERT_INT_EQ(sshbuf_put_u64(b, 1234), 0); /* serial */ ASSERT_INT_EQ(sshbuf_put_u32(b, SSH2_CERT_TYPE_USER), 0); /* type */ ASSERT_INT_EQ(sshbuf_put_cstring(b, "gregor"), 0); /* key ID */ ASSERT_INT_EQ(sshbuf_put_stringb(b, principals), 0); /* principals */ ASSERT_INT_EQ(sshbuf_put_u64(b, 0), 0); /* start */ ASSERT_INT_EQ(sshbuf_put_u64(b, 0xffffffffffffffffULL), 0); /* end */ ASSERT_INT_EQ(sshbuf_put_stringb(b, critopts), 0); /* options */ ASSERT_INT_EQ(sshbuf_put_stringb(b, exts), 0); /* extensions */ ASSERT_INT_EQ(sshbuf_put_string(b, NULL, 0), 0); /* reserved */ ASSERT_INT_EQ(sshbuf_put_stringb(b, ca_buf), 0); /* signature key */ ASSERT_INT_EQ(sshkey_sign(sign_key, &sigblob, &siglen, sshbuf_ptr(b), sshbuf_len(b), sig_alg, 0), 0); ASSERT_INT_EQ(sshbuf_put_string(b, sigblob, siglen), 0); /* signature */ free(sigblob); sshbuf_free(ca_buf); sshbuf_free(exts); sshbuf_free(critopts); sshbuf_free(principals); sshbuf_free(pk); }
static void sig_fuzz(struct sshkey *k, const char *sig_alg) { struct fuzz *fuzz; u_char *sig, c[] = "some junk to be signed"; size_t l; u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; if (test_is_fast()) fuzzers &= ~FUZZ_2_BYTE_FLIP; if (test_is_slow()) fuzzers |= FUZZ_2_BIT_FLIP; ASSERT_INT_EQ(sshkey_sign(k, &sig, &l, c, sizeof(c), sig_alg, 0), 0); ASSERT_SIZE_T_GT(l, 0); fuzz = fuzz_begin(fuzzers, sig, l); ASSERT_INT_EQ(sshkey_verify(k, sig, l, c, sizeof(c), NULL, 0), 0); free(sig); TEST_ONERROR(onerror, fuzz); for(; !fuzz_done(fuzz); fuzz_next(fuzz)) { /* Ensure 1-bit difference at least */ if (fuzz_matches_original(fuzz)) continue; ASSERT_INT_NE(sshkey_verify(k, fuzz_ptr(fuzz), fuzz_len(fuzz), c, sizeof(c), NULL, 0), 0); } fuzz_cleanup(fuzz); }
static void build_cert(struct sshbuf *b, const struct sshkey *k, const char *type, const struct sshkey *sign_key, const struct sshkey *ca_key) { struct sshbuf *ca_buf, *pk, *principals, *critopts, *exts; u_char *sigblob; size_t siglen; ca_buf = sshbuf_new(); ASSERT_INT_EQ(sshkey_to_blob_buf(ca_key, ca_buf), 0); /* * Get the public key serialisation by rendering the key and skipping * the type string. This is a bit of a hack :/ */ pk = sshbuf_new(); ASSERT_INT_EQ(sshkey_plain_to_blob_buf(k, pk), 0); ASSERT_INT_EQ(sshbuf_skip_string(pk), 0); principals = sshbuf_new(); ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gsamsa"), 0); ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gregor"), 0); critopts = sshbuf_new(); /* XXX fill this in */ exts = sshbuf_new(); /* XXX fill this in */ ASSERT_INT_EQ(sshbuf_put_cstring(b, type), 0); ASSERT_INT_EQ(sshbuf_put_cstring(b, "noncenoncenonce!"), 0); /* nonce */ ASSERT_INT_EQ(sshbuf_putb(b, pk), 0); /* public key serialisation */ ASSERT_INT_EQ(sshbuf_put_u64(b, 1234), 0); /* serial */ ASSERT_INT_EQ(sshbuf_put_u32(b, SSH2_CERT_TYPE_USER), 0); /* type */ ASSERT_INT_EQ(sshbuf_put_cstring(b, "gregor"), 0); /* key ID */ ASSERT_INT_EQ(sshbuf_put_stringb(b, principals), 0); /* principals */ ASSERT_INT_EQ(sshbuf_put_u64(b, 0), 0); /* start */ ASSERT_INT_EQ(sshbuf_put_u64(b, 0xffffffffffffffffULL), 0); /* end */ ASSERT_INT_EQ(sshbuf_put_stringb(b, critopts), 0); /* options */ ASSERT_INT_EQ(sshbuf_put_stringb(b, exts), 0); /* extensions */ ASSERT_INT_EQ(sshbuf_put_string(b, NULL, 0), 0); /* reserved */ ASSERT_INT_EQ(sshbuf_put_stringb(b, ca_buf), 0); /* signature key */ ASSERT_INT_EQ(sshkey_sign(sign_key, &sigblob, &siglen, sshbuf_ptr(b), sshbuf_len(b), 0), 0); ASSERT_INT_EQ(sshbuf_put_string(b, sigblob, siglen), 0); /* signature */ free(sigblob); sshbuf_free(ca_buf); sshbuf_free(exts); sshbuf_free(critopts); sshbuf_free(principals); sshbuf_free(pk); }
static void signature_test(struct sshkey *k, struct sshkey *bad, const u_char *d, size_t l) { size_t len; u_char *sig; ASSERT_INT_EQ(sshkey_sign(k, &sig, &len, d, l, 0), 0); ASSERT_SIZE_T_GT(len, 8); ASSERT_PTR_NE(sig, NULL); ASSERT_INT_EQ(sshkey_verify(k, sig, len, d, l, 0), 0); ASSERT_INT_NE(sshkey_verify(bad, sig, len, d, l, 0), 0); /* Fuzz test is more comprehensive, this is just a smoke test */ sig[len - 5] ^= 0x10; ASSERT_INT_NE(sshkey_verify(k, sig, len, d, l, 0), 0); free(sig); }
static void sig_fuzz(struct sshkey *k) { struct fuzz *fuzz; u_char *sig, c[] = "some junk to be signed"; size_t l; ASSERT_INT_EQ(sshkey_sign(k, &sig, &l, c, sizeof(c), 0), 0); ASSERT_SIZE_T_GT(l, 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | /* too slow FUZZ_2_BIT_FLIP | */ FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END, sig, l); ASSERT_INT_EQ(sshkey_verify(k, sig, l, c, sizeof(c), 0), 0); free(sig); TEST_ONERROR(onerror, fuzz); for(; !fuzz_done(fuzz); fuzz_next(fuzz)) { sshkey_verify(k, fuzz_ptr(fuzz), fuzz_len(fuzz), c, sizeof(c), 0); } fuzz_cleanup(fuzz); }
int main(int argc, char **argv) { struct sshbuf *b; Options options; #define NUM_KEYTYPES 4 struct sshkey *keys[NUM_KEYTYPES], *key = NULL; struct passwd *pw; int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; u_char *signature, *data, rver; char *host, *fp; size_t slen, dlen; #ifdef WITH_OPENSSL u_int32_t rnd[256]; #endif if (pledge("stdio rpath getpw dns id", NULL) != 0) fatal("%s: pledge: %s", __progname, strerror(errno)); /* Ensure that stdin and stdout are connected */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) exit(1); /* Leave /dev/null fd iff it is attached to stderr */ if (fd > 2) close(fd); i = 0; /* XXX This really needs to read sshd_config for the paths */ key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); original_real_uid = getuid(); /* XXX readconf.c needs this */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("getpwuid failed"); pw = pwcopy(pw); permanently_set_uid(pw); seed_rng(); #ifdef DEBUG_SSH_KEYSIGN log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); #endif /* verify that ssh-keysign is enabled by the admin */ initialize_options(&options); (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", &options, 0); fill_default_options(&options); if (options.enable_ssh_keysign != 1) fatal("ssh-keysign not enabled in %s", _PATH_HOST_CONFIG_FILE); for (i = found = 0; i < NUM_KEYTYPES; i++) { if (key_fd[i] != -1) found = 1; } if (found == 0) fatal("could not open any host key"); #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); #endif found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { keys[i] = NULL; if (key_fd[i] == -1) continue; r = sshkey_load_private_type_fd(key_fd[i], KEY_UNSPEC, NULL, &key, NULL); close(key_fd[i]); if (r != 0) debug("parse key %d: %s", i, ssh_err(r)); else if (key != NULL) { keys[i] = key; found = 1; } } if (!found) fatal("no hostkey found"); if (pledge("stdio dns", NULL) != 0) fatal("%s: pledge: %s", __progname, strerror(errno)); if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __progname); if (ssh_msg_recv(STDIN_FILENO, b) < 0) fatal("ssh_msg_recv failed"); if ((r = sshbuf_get_u8(b, &rver)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (rver != version) fatal("bad version: received %d, expected %d", rver, version); if ((r = sshbuf_get_u32(b, (u_int *)&fd)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (fd < 0 || fd == STDIN_FILENO || fd == STDOUT_FILENO) fatal("bad fd"); if ((host = get_local_name(fd)) == NULL) fatal("cannot get local name for fd"); if ((r = sshbuf_get_string(b, &data, &dlen)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (valid_request(pw, host, &key, data, dlen) < 0) fatal("not a valid request"); free(host); found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { if (keys[i] != NULL && sshkey_equal_public(key, keys[i])) { found = 1; break; } } if (!found) { if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __progname); fatal("no matching hostkey found for key %s %s", sshkey_type(key), fp ? fp : ""); } if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, NULL, 0)) != 0) fatal("sshkey_sign failed: %s", ssh_err(r)); free(data); /* send reply */ sshbuf_reset(b); if ((r = sshbuf_put_string(b, signature, slen)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (ssh_msg_send(STDOUT_FILENO, version, b) == -1) fatal("ssh_msg_send failed"); return (0); }
int main(int argc, char **argv) { struct sshbuf *b; Options options; #define NUM_KEYTYPES 3 struct sshkey *keys[NUM_KEYTYPES], *key = NULL; struct passwd *pw; int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; u_char *signature, *data, rver; char *host; size_t slen, dlen; u_int32_t rnd[256]; /* Ensure that stdin and stdout are connected */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) exit(1); /* Leave /dev/null fd iff it is attached to stderr */ if (fd > 2) close(fd); i = 0; key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); original_real_uid = getuid(); /* XXX readconf.c needs this */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("getpwuid failed"); pw = pwcopy(pw); permanently_set_uid(pw); #ifdef DEBUG_SSH_KEYSIGN log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); #endif /* verify that ssh-keysign is enabled by the admin */ initialize_options(&options); (void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0); fill_default_options(&options); if (options.enable_ssh_keysign != 1) fatal("ssh-keysign not enabled in %s", _PATH_HOST_CONFIG_FILE); for (i = found = 0; i < NUM_KEYTYPES; i++) { if (key_fd[i] != -1) found = 1; } if (found == 0) fatal("could not open any host key"); OpenSSL_add_all_algorithms(); for (i = 0; i < 256; i++) rnd[i] = arc4random(); RAND_seed(rnd, sizeof(rnd)); found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { keys[i] = NULL; if (key_fd[i] == -1) continue; r = sshkey_load_private_pem(key_fd[i], KEY_UNSPEC, NULL, &(keys[i]), NULL); if (r != 0) error("Load private: %s", ssh_err(r)); close(key_fd[i]); if (keys[i] != NULL) found = 1; } if (!found) fatal("no hostkey found"); if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if (ssh_msg_recv(STDIN_FILENO, b) < 0) fatal("ssh_msg_recv failed"); if ((r = sshbuf_get_u8(b, &rver)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (rver != version) fatal("bad version: received %d, expected %d", rver, version); if ((r = sshbuf_get_u32(b, &fd)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) fatal("bad fd"); if ((host = get_local_name(fd)) == NULL) fatal("cannot get local name for fd"); if ((r = sshbuf_get_string(b, &data, &dlen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (valid_request(pw, host, &key, data, dlen) < 0) fatal("not a valid request"); xfree(host); found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { if (keys[i] != NULL && sshkey_equal_public(key, keys[i])) { found = 1; break; } } if (!found) fatal("no matching hostkey found"); if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, 0)) != 0) fatal("sshkey_sign failed: %s", ssh_err(r)); xfree(data); /* send reply */ sshbuf_reset(b); if ((r = sshbuf_put_string(b, signature, slen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (ssh_msg_send(STDOUT_FILENO, version, b) == -1) fatal("ssh_msg_send failed"); return (0); }
int mm_answer_sign(int sock, struct sshbuf *m) { struct ssh *ssh = active_state; /* XXX */ extern int auth_sock; /* XXX move to state struct? */ struct sshkey *key; u_char *p; u_char *signature; size_t datlen, siglen; u_int keyid; int r; debug3("%s", __func__); if ((r = sshbuf_get_u32(m, &keyid)) != 0 || (r = sshbuf_get_string(m, &p, &datlen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); /* * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), * SHA384 (48 bytes) and SHA512 (64 bytes). */ if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) fatal("%s: data length incorrect: %zu", __func__, datlen); /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { session_id2_len = datlen; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, p, session_id2_len); } if ((key = get_hostkey_by_index(keyid, ssh)) != NULL) { if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, ssh->compat)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, ssh->compat)) != 0) { fatal("%s: ssh_agent_sign failed: %s", __func__, ssh_err(r)); } } else fatal("%s: no hostkey from index %d", __func__, keyid); debug3("%s: signature %p(%zu)", __func__, signature, siglen); sshbuf_reset(m); if ((r = sshbuf_put_string(m, signature, siglen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(p); free(signature); mm_request_send(sock, MONITOR_ANS_SIGN, m); /* Turn on permissions for getpwnam */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); return (0); }
int mm_answer_sign(int sock, Buffer *m) { struct ssh *ssh = active_state; /* XXX */ extern int auth_sock; /* XXX move to state struct? */ struct sshkey *key; struct sshbuf *sigbuf = NULL; u_char *p = NULL, *signature = NULL; char *alg = NULL; size_t datlen, siglen, alglen; int r, is_proof = 0; u_int keyid; const char proof_req[] = "*****@*****.**"; debug3("%s", __func__); if ((r = sshbuf_get_u32(m, &keyid)) != 0 || (r = sshbuf_get_string(m, &p, &datlen)) != 0 || (r = sshbuf_get_cstring(m, &alg, &alglen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (keyid > INT_MAX) fatal("%s: invalid key ID", __func__); /* * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), * SHA384 (48 bytes) and SHA512 (64 bytes). * * Otherwise, verify the signature request is for a hostkey * proof. * * XXX perform similar check for KEX signature requests too? * it's not trivial, since what is signed is the hash, rather * than the full kex structure... */ if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) { /* * Construct expected hostkey proof and compare it to what * the client sent us. */ if (session_id2_len == 0) /* hostkeys is never first */ fatal("%s: bad data length: %zu", __func__, datlen); if ((key = get_hostkey_public_by_index(keyid, ssh)) == NULL) fatal("%s: no hostkey for index %d", __func__, keyid); if ((sigbuf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); if ((r = sshbuf_put_cstring(sigbuf, proof_req)) != 0 || (r = sshbuf_put_string(sigbuf, session_id2, session_id2_len)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0) fatal("%s: couldn't prepare private key " "proof buffer: %s", __func__, ssh_err(r)); if (datlen != sshbuf_len(sigbuf) || memcmp(p, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf)) != 0) fatal("%s: bad data length: %zu, hostkey proof len %zu", __func__, datlen, sshbuf_len(sigbuf)); sshbuf_free(sigbuf); is_proof = 1; } /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { session_id2_len = datlen; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, p, session_id2_len); } if ((key = get_hostkey_by_index(keyid)) != NULL) { if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg, datafellows)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, alg, datafellows)) != 0) { fatal("%s: ssh_agent_sign failed: %s", __func__, ssh_err(r)); } } else fatal("%s: no hostkey from index %d", __func__, keyid); debug3("%s: %s signature %p(%zu)", __func__, is_proof ? "KEX" : "hostkey proof", signature, siglen); sshbuf_reset(m); if ((r = sshbuf_put_string(m, signature, siglen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(alg); free(p); free(signature); mm_request_send(sock, MONITOR_ANS_SIGN, m); /* Turn on permissions for getpwnam */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); return (0); }
static int input_kex_dh_gex_init(int type, u_int32_t seq, struct ssh *ssh) { 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; u_int 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) { 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 */ if ((r = kexgex_hash( kex->evp_md, 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, kex->min, kex->nbits, kex->max, kex->dh->p, kex->dh->g, 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 = PRIVSEP(sshkey_sign(server_host_private, &signature, &slen, hash, hashlen, ssh->compat))) < 0) goto out; /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ 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; 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 (dh_client_pub) BN_clear_free(dh_client_pub); if (kbuf) { bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); if (server_host_key_blob) free(server_host_key_blob); if (signature) free(signature); return r; }