diff --git a/contrib/win32/openssh/libssh.vcxproj b/contrib/win32/openssh/libssh.vcxproj index 49889c4..499cfa3 100644 --- a/contrib/win32/openssh/libssh.vcxproj +++ b/contrib/win32/openssh/libssh.vcxproj @@ -258,6 +258,7 @@ + diff --git a/contrib/win32/openssh/libssh.vcxproj.filters b/contrib/win32/openssh/libssh.vcxproj.filters index 730bc3d..00d4658 100644 --- a/contrib/win32/openssh/libssh.vcxproj.filters +++ b/contrib/win32/openssh/libssh.vcxproj.filters @@ -288,5 +288,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/kex.h b/kex.h index d71b532..98a2b42 100644 --- a/kex.h +++ b/kex.h @@ -121,6 +121,26 @@ struct newkeys { struct ssh; +//KEX DH interface start +struct sshbuf; +typedef struct kexdhi_ +{ + //consume p and g from "in" + int(*read_p_g)(struct kexdhi_*, struct sshbuf* in); + //encode p and g in "out" + int(*get_p_g)(struct kexdhi_*, struct sshbuf* out); + //encode public key in pub + int(*get_pub_key)(struct kexdhi_*, struct sshbuf* pub); + //read other public key from other_pub and encode secretagreement in secret + int(*get_secret)(struct kexdhi_*, struct sshbuf* other_pub, struct sshbuf* secret); + void(*done)(struct kexdhi_*); +}kexdhi; + +kexdhi* kexdh_openssl_init(struct ssh* ssh); +//KEX DH interface end + + + struct kex { u_char *session_id; size_t session_id_len; @@ -150,6 +170,8 @@ struct kex { u_char **, size_t *, const u_char *, size_t, u_int); int (*kex[KEX_MAX])(struct ssh *); /* kex specific state */ + kexdhi* kexdh; + DH *dh; /* DH */ u_int min, max, nbits; /* GEX */ EC_KEY *ec_client_key; /* ECDH */ @@ -191,6 +213,12 @@ int kex_dh_hash(const char *, const char *, const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); +//version of kex_dh_hash with public keys and secret passed as sshbuf instead of BIGNUM +int kex_dh_hash_( + const char *, const char *, const u_char *, size_t , const u_char *, size_t , + const u_char *, size_t , const struct sshbuf *, const struct sshbuf *, const struct sshbuf *, + u_char *hash, size_t *); + int kexgex_hash(int, const char *, const char *, const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, int, int, int, diff --git a/kexdh-openssl.c b/kexdh-openssl.c new file mode 100644 index 0000000..cf5aed8 --- /dev/null +++ b/kexdh-openssl.c @@ -0,0 +1,152 @@ +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include + +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#include "dispatch.h" +#include "compat.h" +#include "ssherr.h" +#include "sshbuf.h" + +typedef struct kexdh_openssl_ +{ + kexdhi dhi; + struct ssh* ssh; + DH* dh; +}kexdh_openssl; + + +static int kexdh_get_pub_key(kexdhi* dh_, struct sshbuf* pub) +{ + int r = 0; + kexdh_openssl* dh = (kexdh_openssl*)dh_; + struct kex* kex = dh->ssh->kex; + + /* generate and send 'e', client DH public key */ + switch (kex->kex_type) { + case KEX_DH_GRP1_SHA1: + dh->dh = dh_new_group1(); + break; + case KEX_DH_GRP14_SHA1: + dh->dh = dh_new_group14(); + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + + if ((r = dh_gen_key(dh->dh, kex->we_need * 8)) != 0 || + (r = sshpkt_put_bignum2(pub, dh->dh->pub_key)) != 0) + goto out; +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh->dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->dh->pub_key); + fprintf(stderr, "\n"); +#endif + return 0; +out: + return r; +} + +static int kexdh_get_secret(kexdhi* dh_, struct sshbuf* other_pub, struct sshbuf* secret) +{ + int r = 0, kout; + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + kexdh_openssl* dh = (kexdh_openssl*)dh_; + struct kex* kex = dh->ssh->kex; + size_t klen = 0; + u_char *kbuf = NULL; + + /* DH parameter f, server public DH key */ + if ((dh_server_pub = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((r = sshbuf_get_bignum2(other_pub, dh_server_pub)) != 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(dh->dh, dh_server_pub)) { + sshpkt_disconnect(dh->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 ((r == sshbuf_put_bignum2(secret, shared_secret)) != 0) { + goto out; + } +out: + if (dh_server_pub) + BN_clear_free(dh_server_pub); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + + return r; +} + +static void kexdh_done(kexdhi* dh_) +{ + kexdh_openssl* dh = (kexdh_openssl*)dh_; + + if (dh->dh) + DH_free(dh->dh); + free(dh_); +} + + +kexdhi* kexdh_openssl_init(struct ssh* ssh) +{ + + kexdh_openssl *dh = malloc(sizeof(kexdh_openssl)); + + if (dh) + { + dh->ssh = ssh; + dh->dhi.get_pub_key = kexdh_get_pub_key; + dh->dhi.get_secret = kexdh_get_secret; + dh->dhi.done = kexdh_done; + dh->dhi.get_p_g = NULL; + dh->dhi.read_p_g = NULL; + } + + return dh; +} + + +#endif \ No newline at end of file diff --git a/kexdh.c b/kexdh.c index feea669..a6c092c 100644 --- a/kexdh.c +++ b/kexdh.c @@ -90,4 +90,52 @@ kex_dh_hash( #endif return 0; } +kex_dh_hash_( + const char *client_version_string, + const char *server_version_string, + const u_char *ckexinit, size_t ckexinitlen, + const u_char *skexinit, size_t skexinitlen, + const u_char *serverhostkeyblob, size_t sbloblen, + const struct sshbuf *client_dh_pub, + const struct sshbuf *server_dh_pub, + const struct sshbuf *shared_secret, + u_char *hash, size_t *hashlen) +{ + struct sshbuf *b; + int r; + + if (*hashlen < ssh_digest_bytes(SSH_DIGEST_SHA1)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || + (r = sshbuf_put_cstring(b, server_version_string)) != 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, ckexinitlen + 1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 || + (r = sshbuf_put_u32(b, skexinitlen + 1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, skexinit, skexinitlen)) != 0 || + (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 || + (r = sshbuf_put_b(b, client_dh_pub)) != 0 || + (r = sshbuf_put_b(b, server_dh_pub)) != 0 || + (r = sshbuf_put_b(b, shared_secret)) != 0) { + sshbuf_free(b); + return r; + } +#ifdef DEBUG_KEX + sshbuf_dump(b, stderr); +#endif + if (ssh_digest_buffer(SSH_DIGEST_SHA1, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); + *hashlen = ssh_digest_bytes(SSH_DIGEST_SHA1); +#ifdef DEBUG_KEX + dump_digest("hash", hash, *hashlen); +#endif + return 0; +} #endif /* WITH_OPENSSL */ diff --git a/kexdhc.c b/kexdhc.c index af259f1..bc646a5 100644 --- a/kexdhc.c +++ b/kexdhc.c @@ -56,39 +56,29 @@ kexdh_client(struct ssh *ssh) { struct kex *kex = ssh->kex; int r; + struct sshbuf *client_pub = NULL; + + kex->kexdh = kexdh_openssl_init(ssh); - /* generate and send 'e', client DH public key */ - switch (kex->kex_type) { - case KEX_DH_GRP1_SHA1: - kex->dh = dh_new_group1(); - break; - case KEX_DH_GRP14_SHA1: - kex->dh = dh_new_group14(); - break; - default: - r = SSH_ERR_INVALID_ARGUMENT; - goto out; - } - if (kex->dh == NULL) { + if (kex->kexdh == NULL || + (client_pub = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } + debug("sending SSH2_MSG_KEXDH_INIT"); - if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || - (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || - (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || + (r = kex->kexdh->get_pub_key(kex->kexdh, client_pub)) != 0 || + (r = sshpkt_putb(ssh, client_pub)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; -#ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, kex->dh); - fprintf(stderr, "pub= "); - BN_print_fp(stderr, kex->dh->pub_key); - fprintf(stderr, "\n"); -#endif debug("expecting SSH2_MSG_KEXDH_REPLY"); ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh); r = 0; - out: + +out: + if (client_pub) + sshbuf_free(client_pub); return r; } @@ -97,12 +87,14 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; - BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; struct sshkey *server_host_key = NULL; - u_char *kbuf = NULL, *server_host_key_blob = NULL, *signature = NULL; + u_char *server_host_key_blob = NULL, *signature = NULL; u_char hash[SSH_DIGEST_MAX_LENGTH]; - size_t klen = 0, slen, sbloblen, hashlen; - int kout, r; + size_t slen, sbloblen, hashlen; + const u_char* server_pub_ptr = NULL; + size_t server_pub_len = 0; + struct sshbuf *server_pub = NULL, *client_pub = NULL, *secret = NULL; + int r; if (kex->verify_host_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; @@ -110,13 +102,13 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) } /* 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) + &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)) { + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } @@ -124,54 +116,38 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) 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) + if ((r = sshpkt_get_string_direct(ssh, &server_pub_ptr, &server_pub_len)) != 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) { + if ((server_pub = sshbuf_from(server_pub_ptr - 4, server_pub_len + 4)) == NULL || + (secret = sshbuf_new()) == NULL || + (client_pub = sshbuf_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; + + + if ((r = kex->kexdh->get_secret(kex->kexdh, server_pub, secret)) != 0 || + (r = kex->kexdh->get_pub_key(kex->kexdh, client_pub)) != 0) + { goto out; } -#ifdef DEBUG_KEXDH - dump_digest("shared secret", kbuf, kout); -#endif - + /* calc and verify H */ hashlen = sizeof(hash); - if ((r = kex_dh_hash( + if ((r = kex_dh_hash_( 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->dh->pub_key, - dh_server_pub, - shared_secret, + client_pub, + server_pub, + secret, hash, &hashlen)) != 0) goto out; @@ -190,20 +166,18 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) memcpy(kex->session_id, hash, kex->session_id_len); } - if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + if ((r = kex_derive_keys(ssh, hash, hashlen, secret)) == 0) r = kex_send_newkeys(ssh); out: explicit_bzero(hash, sizeof(hash)); - DH_free(kex->dh); - kex->dh = NULL; - if (dh_server_pub) - BN_clear_free(dh_server_pub); - if (kbuf) { - explicit_bzero(kbuf, klen); - free(kbuf); - } - if (shared_secret) - BN_clear_free(shared_secret); + if (client_pub) + sshbuf_free(client_pub); + if (server_pub) + sshbuf_free(server_pub); + if (secret) + sshbuf_free(secret); + kex->kexdh->done(kex->kexdh); + kex->kexdh = NULL; sshkey_free(server_host_key); free(server_host_key_blob); free(signature);