From 16217454d576511f37f39c3169963629f9d5082f Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Fri, 10 Nov 2017 17:35:38 +0100 Subject: [PATCH] crypto: Change the type of server_pubkey to ssh_key Signed-off-by: Andreas Schneider --- include/libssh/crypto.h | 3 +- include/libssh/dh.h | 12 ++++- src/curve25519.c | 29 +++++++++--- src/dh.c | 98 ++++++++++++++++++++++++++++++++++------- src/ecdh.c | 18 ++++---- src/known_hosts.c | 54 +++++++++++++---------- src/legacy.c | 25 ++++++++--- src/packet_cb.c | 16 +++---- src/server.c | 9 +++- 9 files changed, 191 insertions(+), 73 deletions(-) diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h index 81915e20..fab39ed1 100644 --- a/include/libssh/crypto.h +++ b/include/libssh/crypto.h @@ -109,8 +109,7 @@ struct ssh_crypto_struct { struct ssh_cipher_struct *in_cipher, *out_cipher; /* the cipher structures/objects */ enum ssh_hmac_e in_hmac, out_hmac; /* the MAC algorithms used */ - ssh_string server_pubkey; - const char *server_pubkey_type; + ssh_key server_pubkey; int do_compress_out; /* idem */ int do_compress_in; /* don't set them, set the option instead */ int delayed_compress_in; /* Use of zlib@openssh.org */ diff --git a/include/libssh/dh.h b/include/libssh/dh.h index 8e0d5aa6..c54595ef 100644 --- a/include/libssh/dh.h +++ b/include/libssh/dh.h @@ -37,11 +37,21 @@ ssh_string ssh_dh_get_e(ssh_session session); ssh_string ssh_dh_get_f(ssh_session session); int ssh_dh_import_f(ssh_session session,ssh_string f_string); int ssh_dh_import_e(ssh_session session, ssh_string e_string); -void ssh_dh_import_pubkey(ssh_session session,ssh_string pubkey_string); + +int ssh_dh_import_pubkey_blob(ssh_session session, ssh_string pubkey_blob); +int ssh_dh_import_next_pubkey_blob(ssh_session session, ssh_string pubkey_blob); + int ssh_dh_build_k(ssh_session session); int ssh_client_dh_init(ssh_session session); int ssh_client_dh_reply(ssh_session session, ssh_buffer packet); +ssh_key ssh_dh_get_current_server_publickey(ssh_session session); +int ssh_dh_get_current_server_publickey_blob(ssh_session session, + ssh_string *pubkey_blob); +ssh_key ssh_dh_get_next_server_publickey(ssh_session session); +int ssh_dh_get_next_server_publickey_blob(ssh_session session, + ssh_string *pubkey_blob); + int ssh_make_sessionid(ssh_session session); /* add data for the final cookie */ int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie); diff --git a/src/curve25519.c b/src/curve25519.c index 6d9a409c..8e08f512 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -118,17 +118,24 @@ static int ssh_curve25519_build_k(ssh_session session) { */ int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet){ ssh_string q_s_string = NULL; - ssh_string pubkey = NULL; + ssh_string pubkey_blob = NULL; ssh_string signature = NULL; int rc; - pubkey = ssh_buffer_get_ssh_string(packet); - if (pubkey == NULL){ + + pubkey_blob = ssh_buffer_get_ssh_string(packet); + if (pubkey_blob == NULL) { ssh_set_error(session,SSH_FATAL, "No public key in packet"); goto error; } - /* this is the server host key */ - session->next_crypto->server_pubkey = pubkey; - pubkey = NULL; + + rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); + ssh_string_free(pubkey_blob); + if (rc != 0) { + ssh_set_error(session, + SSH_FATAL, + "Failed to import next public key"); + goto error; + } q_s_string = ssh_buffer_get_ssh_string(packet); if (q_s_string == NULL) { @@ -178,6 +185,7 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ /* ECDH keys */ ssh_string q_c_string; ssh_string q_s_string; + ssh_string server_pubkey_blob = NULL; /* SSH host keys (rsa,dsa,ecdsa) */ ssh_key privkey; @@ -236,9 +244,16 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ goto error; } + rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, "Could not export server public key"); + goto error; + } + /* add host's public key */ rc = ssh_buffer_add_ssh_string(session->out_buffer, - session->next_crypto->server_pubkey); + server_pubkey_blob); + ssh_string_free(server_pubkey_blob); if (rc < 0) { ssh_set_error_oom(session); goto error; diff --git a/src/dh.c b/src/dh.c index 26e47994..d2ddfabd 100644 --- a/src/dh.c +++ b/src/dh.c @@ -387,8 +387,17 @@ ssh_string ssh_dh_get_f(ssh_session session) { return ssh_make_bignum_string(session->next_crypto->f); } -void ssh_dh_import_pubkey(ssh_session session, ssh_string pubkey_string) { - session->next_crypto->server_pubkey = pubkey_string; +int ssh_dh_import_pubkey_blob(ssh_session session, ssh_string pubkey_blob) +{ + return ssh_pki_import_pubkey_blob(pubkey_blob, + &session->current_crypto->server_pubkey); +} + +int ssh_dh_import_next_pubkey_blob(ssh_session session, ssh_string pubkey_blob) +{ + return ssh_pki_import_pubkey_blob(pubkey_blob, + &session->next_crypto->server_pubkey); + } int ssh_dh_import_f(ssh_session session, ssh_string f_string) { @@ -517,15 +526,21 @@ int ssh_client_dh_init(ssh_session session){ int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){ ssh_string f; - ssh_string pubkey = NULL; + ssh_string pubkey_blob = NULL; ssh_string signature = NULL; int rc; - pubkey = ssh_buffer_get_ssh_string(packet); - if (pubkey == NULL){ + + pubkey_blob = ssh_buffer_get_ssh_string(packet); + if (pubkey_blob == NULL){ ssh_set_error(session,SSH_FATAL, "No public key in packet"); goto error; } - ssh_dh_import_pubkey(session, pubkey); + + rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); + ssh_string_free(pubkey_blob); + if (rc != 0) { + goto error; + } f = ssh_buffer_get_ssh_string(packet); if (f == NULL) { @@ -569,6 +584,7 @@ int ssh_make_sessionid(ssh_session session) { ssh_buffer server_hash = NULL; ssh_buffer client_hash = NULL; ssh_buffer buf = NULL; + ssh_string server_pubkey_blob = NULL; int rc = SSH_ERROR; buf = ssh_buffer_new(); @@ -619,6 +635,11 @@ int ssh_make_sessionid(ssh_session session) { } } + rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob); + if (rc != SSH_OK) { + goto error; + } + rc = ssh_buffer_pack(buf, "dPdPS", ssh_buffer_get_len(client_hash), @@ -627,8 +648,8 @@ int ssh_make_sessionid(ssh_session session) { ssh_buffer_get_len(server_hash), ssh_buffer_get_len(server_hash), ssh_buffer_get(server_hash), - session->next_crypto->server_pubkey); - + server_pubkey_blob); + ssh_string_free(server_pubkey_blob); if(rc != SSH_OK){ goto error; } @@ -959,9 +980,11 @@ error: * @deprecated Use ssh_get_publickey_hash() */ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) { - ssh_string pubkey; + ssh_key pubkey = NULL; + ssh_string pubkey_blob = NULL; MD5CTX ctx; unsigned char *h; + int rc; if (session == NULL || hash == NULL) { return SSH_ERROR; @@ -984,9 +1007,19 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) { return SSH_ERROR; } - pubkey = session->current_crypto->server_pubkey; + rc = ssh_get_server_publickey(session, &pubkey); + if (rc != 0) { + SAFE_FREE(h); + return SSH_ERROR; + } - md5_update(ctx, ssh_string_data(pubkey), ssh_string_len(pubkey)); + rc = ssh_pki_export_pubkey_blob(pubkey, + &pubkey_blob); + ssh_key_free(pubkey); + if (rc != 0) { + } + md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob)); + ssh_string_free(pubkey_blob); md5_final(h, ctx); *hash = h; @@ -1023,14 +1056,49 @@ void ssh_clean_pubkey_hash(unsigned char **hash) { */ int ssh_get_server_publickey(ssh_session session, ssh_key *key) { - if (session==NULL || - session->current_crypto ==NULL || + ssh_key pubkey = NULL; + + if (session == NULL || + session->current_crypto == NULL || session->current_crypto->server_pubkey == NULL) { return SSH_ERROR; } - return ssh_pki_import_pubkey_blob(session->current_crypto->server_pubkey, - key); + pubkey = ssh_key_dup(session->current_crypto->server_pubkey); + if (pubkey == NULL) { + return SSH_ERROR; + } + + *key = pubkey; + return SSH_OK; +} + +ssh_key ssh_dh_get_current_server_publickey(ssh_session session) +{ + return session->current_crypto->server_pubkey; +} + +/* Caller need to free the blob */ +int ssh_dh_get_current_server_publickey_blob(ssh_session session, + ssh_string *pubkey_blob) +{ + const ssh_key pubkey = ssh_dh_get_current_server_publickey(session); + + return ssh_pki_export_pubkey_blob(pubkey, pubkey_blob); +} + +ssh_key ssh_dh_get_next_server_publickey(ssh_session session) +{ + return session->next_crypto->server_pubkey; +} + +/* Caller need to free the blob */ +int ssh_dh_get_next_server_publickey_blob(ssh_session session, + ssh_string *pubkey_blob) +{ + const ssh_key pubkey = ssh_dh_get_next_server_publickey(session); + + return ssh_pki_export_pubkey_blob(pubkey, pubkey_blob); } /** diff --git a/src/ecdh.c b/src/ecdh.c index ada7b184..f7fcaf13 100644 --- a/src/ecdh.c +++ b/src/ecdh.c @@ -30,25 +30,27 @@ #ifdef HAVE_ECDH -static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) { - session->next_crypto->server_pubkey = pubkey_string; -} - /** @internal * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back * a SSH_MSG_NEWKEYS */ int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){ ssh_string q_s_string = NULL; - ssh_string pubkey = NULL; + ssh_string pubkey_blob = NULL; ssh_string signature = NULL; int rc; - pubkey = ssh_buffer_get_ssh_string(packet); - if (pubkey == NULL){ + + pubkey_blob = ssh_buffer_get_ssh_string(packet); + if (pubkey_blob == NULL) { ssh_set_error(session,SSH_FATAL, "No public key in packet"); goto error; } - ecdh_import_pubkey(session, pubkey); + + rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); + ssh_string_free(pubkey_blob); + if (rc != 0) { + goto error; + } q_s_string = ssh_buffer_get_ssh_string(packet); if (q_s_string == NULL) { diff --git a/src/known_hosts.c b/src/known_hosts.c index 2f0584d8..d89aca19 100644 --- a/src/known_hosts.c +++ b/src/known_hosts.c @@ -32,6 +32,7 @@ #include "libssh/session.h" #include "libssh/buffer.h" #include "libssh/misc.h" +#include "libssh/dh.h" #include "libssh/pki.h" #include "libssh/options.h" #include "libssh/knownhosts.h" @@ -188,9 +189,10 @@ static char **ssh_get_knownhost_line(FILE **file, const char *filename, * on error. */ static int check_public_key(ssh_session session, char **tokens) { - ssh_string pubkey = session->current_crypto->server_pubkey; + ssh_string pubkey_blob = NULL; ssh_buffer pubkey_buffer; char *pubkey_64; + int rc; /* ok we found some public key in known hosts file. now un-base64it */ if (alldigits(tokens[1])) { @@ -270,18 +272,26 @@ static int check_public_key(ssh_session session, char **tokens) { return -1; } - if (ssh_buffer_get_len(pubkey_buffer) != ssh_string_len(pubkey)) { + rc = ssh_dh_get_current_server_publickey_blob(session, &pubkey_blob); + if (rc != 0) { + return -1; + } + + if (ssh_buffer_get_len(pubkey_buffer) != ssh_string_len(pubkey_blob)) { + ssh_string_free(pubkey_blob); ssh_buffer_free(pubkey_buffer); return 0; } /* now test that they are identical */ - if (memcmp(ssh_buffer_get(pubkey_buffer), ssh_string_data(pubkey), + if (memcmp(ssh_buffer_get(pubkey_buffer), ssh_string_data(pubkey_blob), ssh_buffer_get_len(pubkey_buffer)) != 0) { + ssh_string_free(pubkey_blob); ssh_buffer_free(pubkey_buffer); return 0; } + ssh_string_free(pubkey_blob); ssh_buffer_free(pubkey_buffer); return 1; } @@ -485,12 +495,21 @@ int ssh_is_server_known(ssh_session session) { match = match_hashed_host(hostport, tokens[0]); } if (match) { + ssh_key pubkey = ssh_dh_get_current_server_publickey(session); + const char *pubkey_type = NULL; + + if (ssh_key_type(pubkey) == SSH_KEYTYPE_ECDSA) { + pubkey_type = ssh_pki_key_ecdsa_name(pubkey); + } else { + pubkey_type = ssh_key_type_to_char(ssh_key_type(pubkey)); + } + /* We got a match. Now check the key type */ - if (strcmp(session->current_crypto->server_pubkey_type, type) != 0) { + if (strcmp(pubkey_type, type) != 0) { SSH_LOG(SSH_LOG_PACKET, "ssh_is_server_known: server type [%s] doesn't match the " "type [%s] in known_hosts file", - session->current_crypto->server_pubkey_type, + pubkey_type, type); /* Different type. We don't override the known_changed error which is * more important */ @@ -545,8 +564,7 @@ int ssh_is_server_known(ssh_session session) { * @return string on success, NULL on error. */ char * ssh_dump_knownhost(ssh_session session) { - ssh_string pubkey_s; - ssh_key key; + ssh_key server_pubkey = NULL; char *host; char *hostport; size_t len = 4096; @@ -578,40 +596,31 @@ char * ssh_dump_knownhost(ssh_session session) { return NULL; } - pubkey_s = session->current_crypto->server_pubkey; - if (pubkey_s == NULL){ + server_pubkey = ssh_dh_get_current_server_publickey(session); + if (server_pubkey == NULL){ ssh_set_error(session, SSH_FATAL, "No public key present"); SAFE_FREE(host); return NULL; } - rc = ssh_pki_import_pubkey_blob(pubkey_s, &key); - if (rc < 0) { - SAFE_FREE(host); - return NULL; - } - buffer = calloc (1, 4096); if (!buffer) { - ssh_key_free(key); SAFE_FREE(host); return NULL; } - if (strcmp(session->current_crypto->server_pubkey_type, "ssh-rsa1") == 0) { + if (ssh_key_type(server_pubkey) == SSH_KEYTYPE_RSA1) { /* openssh uses a different format for ssh-rsa1 keys. Be compatible --kv */ - rc = ssh_pki_export_pubkey_rsa1(key, host, buffer, len); - ssh_key_free(key); + rc = ssh_pki_export_pubkey_rsa1(server_pubkey, host, buffer, len); SAFE_FREE(host); if (rc < 0) { SAFE_FREE(buffer); return NULL; } } else { - rc = ssh_pki_export_pubkey_base64(key, &b64_key); + rc = ssh_pki_export_pubkey_base64(server_pubkey, &b64_key); if (rc < 0) { - ssh_key_free(key); SAFE_FREE(buffer); SAFE_FREE(host); return NULL; @@ -620,10 +629,9 @@ char * ssh_dump_knownhost(ssh_session session) { snprintf(buffer, len, "%s %s %s\n", host, - key->type_c, + server_pubkey->type_c, b64_key); - ssh_key_free(key); SAFE_FREE(host); SAFE_FREE(b64_key); } diff --git a/src/legacy.c b/src/legacy.c index 139f81b5..2be95099 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "libssh/pki_priv.h" #include @@ -701,12 +702,24 @@ int ssh_try_publickey_from_file(ssh_session session, return 0; } -ssh_string ssh_get_pubkey(ssh_session session){ - if(session==NULL || session->current_crypto ==NULL || - session->current_crypto->server_pubkey==NULL) - return NULL; - else - return ssh_string_copy(session->current_crypto->server_pubkey); +ssh_string ssh_get_pubkey(ssh_session session) +{ + ssh_string pubkey_blob = NULL; + int rc; + + if (session == NULL || + session->current_crypto == NULL || + session->current_crypto->server_pubkey == NULL) { + return NULL; + } + + rc = ssh_dh_get_current_server_publickey_blob(session, + &pubkey_blob); + if (rc != 0) { + return NULL; + } + + return pubkey_blob; } /**************************************************************************** diff --git a/src/packet_cb.c b/src/packet_cb.c index 106c5d9b..2b407416 100644 --- a/src/packet_cb.c +++ b/src/packet_cb.c @@ -154,7 +154,8 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ /* server things are done in server.c */ session->dh_handshake_state=DH_STATE_FINISHED; } else { - ssh_key key; + ssh_key server_key; + /* client */ rc = ssh_make_sessionid(session); if (rc != SSH_OK) { @@ -178,7 +179,7 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ session->next_crypto->dh_server_signature = NULL; /* get the server public key */ - rc = ssh_pki_import_pubkey_blob(session->next_crypto->server_pubkey, &key); + server_key = ssh_dh_get_next_server_publickey(session); if (rc < 0) { return SSH_ERROR; } @@ -186,27 +187,22 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ /* check if public key from server matches user preferences */ if (session->opts.wanted_methods[SSH_HOSTKEYS]) { if(!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS], - key->type_c)) { + server_key->type_c)) { ssh_set_error(session, SSH_FATAL, "Public key from server (%s) doesn't match user " "preference (%s)", - key->type_c, + server_key->type_c, session->opts.wanted_methods[SSH_HOSTKEYS]); - ssh_key_free(key); return -1; } } rc = ssh_pki_signature_verify_blob(session, sig_blob, - key, + server_key, session->next_crypto->secret_hash, session->next_crypto->digest_len); - /* Set the server public key type for known host checking */ - session->next_crypto->server_pubkey_type = key->type_c; - - ssh_key_free(key); ssh_string_burn(sig_blob); ssh_string_free(sig_blob); sig_blob = NULL; diff --git a/src/server.c b/src/server.c index 7d807c3c..2ae87792 100644 --- a/src/server.c +++ b/src/server.c @@ -277,7 +277,14 @@ int ssh_get_key_params(ssh_session session, ssh_key *privkey){ return -1; } - ssh_dh_import_pubkey(session, pubkey_blob); + rc = ssh_dh_import_pubkey_blob(session, pubkey_blob); + if (rc != 0) { + ssh_set_error(session, + SSH_FATAL, + "Could not import server public key"); + return -1; + } + return SSH_OK; }