diff options
author | Anderson Toshiyuki Sasaki <ansasaki@redhat.com> | 2019-07-02 13:48:17 +0200 |
---|---|---|
committer | Anderson Toshiyuki Sasaki <ansasaki@redhat.com> | 2019-07-04 10:29:20 +0200 |
commit | 65a38759ca872e8bec0158ab3676e74b6afd336f (patch) | |
tree | 44a6060c5ecea9d686c1777a7a38fb7ed794cc92 | |
parent | 548753b3389518ebce98a7ddbf0640db3ad72de8 (diff) | |
download | libssh-65a38759ca872e8bec0158ab3676e74b6afd336f.tar.gz libssh-65a38759ca872e8bec0158ab3676e74b6afd336f.tar.xz libssh-65a38759ca872e8bec0158ab3676e74b6afd336f.zip |
knownhosts: Introduced ssh_known_hosts_get_algorithms_names()
The added internal function obtain a newly allocated string containing a
list of the signature types that can be generated by the keys present in
the known_hosts files, separated by commas.
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
-rw-r--r-- | include/libssh/knownhosts.h | 1 | ||||
-rw-r--r-- | src/knownhosts.c | 141 | ||||
-rw-r--r-- | tests/unittests/torture_knownhosts_parsing.c | 28 |
3 files changed, 170 insertions, 0 deletions
diff --git a/include/libssh/knownhosts.h b/include/libssh/knownhosts.h index dcaa6c24..44e434c0 100644 --- a/include/libssh/knownhosts.h +++ b/include/libssh/knownhosts.h @@ -23,6 +23,7 @@ #define SSH_KNOWNHOSTS_H_ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session); +char *ssh_known_hosts_get_algorithms_names(ssh_session session); enum ssh_known_hosts_e ssh_session_get_known_hosts_entry_file(ssh_session session, const char *filename, diff --git a/src/knownhosts.c b/src/knownhosts.c index 8040a0c0..cf9d8a6b 100644 --- a/src/knownhosts.c +++ b/src/knownhosts.c @@ -42,6 +42,7 @@ #include "libssh/pki.h" #include "libssh/dh.h" #include "libssh/knownhosts.h" +#include "libssh/token.h" /** * @addtogroup libssh_session @@ -452,6 +453,146 @@ error: } /** + * @internal + * + * @brief Returns a static string containing a list of the signature types the + * given key type can generate. + * + * @returns A static cstring containing the signature types the key is able to + * generate separated by commas; NULL in case of error + */ +static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e type) +{ + switch (type) { + case SSH_KEYTYPE_RSA: + return "rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + case SSH_KEYTYPE_ED25519: + return "ssh-ed25519"; +#ifdef HAVE_DSA + case SSH_KEYTYPE_DSS: + return "ssh-dss"; +#endif +#ifdef HAVE_ECDH + case SSH_KEYTYPE_ECDSA_P256: + return "ecdsa-sha2-nistp256"; + case SSH_KEYTYPE_ECDSA_P384: + return "ecdsa-sha2-nistp384"; + case SSH_KEYTYPE_ECDSA_P521: + return "ecdsa-sha2-nistp521"; +#endif + case SSH_KEYTYPE_UNKNOWN: + default: + SSH_LOG(SSH_LOG_WARN, "The given type %d is not a base private key type " + "or is unsupported", type); + return NULL; + } +} + +/** + * @internal + * @brief Get the host keys algorithms identifiers from the known_hosts files + * + * This expands the signatures types that can be generated from the keys types + * present in the known_hosts files + * + * @param[in] session The ssh session to use. + * + * @return A newly allocated cstring containing a list of signature algorithms + * that can be generated by the host using the keys listed in the known_hosts + * files, NULL on error. + */ +char *ssh_known_hosts_get_algorithms_names(ssh_session session) +{ + char methods_buffer[256 + 1] = {0}; + struct ssh_list *entry_list = NULL; + struct ssh_iterator *it = NULL; + char *host_port = NULL; + size_t count; + bool needcomma = false; + char *names; + + int rc; + + if (session->opts.knownhosts == NULL || + session->opts.global_knownhosts == NULL) { + if (ssh_options_apply(session) < 0) { + ssh_set_error(session, + SSH_REQUEST_DENIED, + "Can't find a known_hosts file"); + + return NULL; + } + } + + host_port = ssh_session_get_host_port(session); + if (host_port == NULL) { + return NULL; + } + + rc = ssh_known_hosts_read_entries(host_port, + session->opts.knownhosts, + &entry_list); + if (rc != 0) { + SAFE_FREE(host_port); + ssh_list_free(entry_list); + return NULL; + } + + rc = ssh_known_hosts_read_entries(host_port, + session->opts.global_knownhosts, + &entry_list); + SAFE_FREE(host_port); + if (rc != 0) { + ssh_list_free(entry_list); + return NULL; + } + + if (entry_list == NULL) { + return NULL; + } + + count = ssh_list_count(entry_list); + if (count == 0) { + ssh_list_free(entry_list); + return NULL; + } + + for (it = ssh_list_get_iterator(entry_list); + it != NULL; + it = ssh_list_get_iterator(entry_list)) + { + struct ssh_knownhosts_entry *entry = NULL; + const char *algo = NULL; + + entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it); + algo = ssh_known_host_sigs_from_hostkey_type(entry->publickey->type); + if (algo == NULL) { + continue; + } + + if (needcomma) { + strncat(methods_buffer, + ",", + sizeof(methods_buffer) - strlen(methods_buffer) - 1); + } + + strncat(methods_buffer, + algo, + sizeof(methods_buffer) - strlen(methods_buffer) - 1); + needcomma = true; + + ssh_knownhosts_entry_free(entry); + ssh_list_remove(entry_list, it); + } + + ssh_list_free(entry_list); + + names = ssh_remove_duplicates(methods_buffer); + + return names; +} + +/** * @brief Parse a line from a known_hosts entry into a structure * * This parses an known_hosts entry into a structure with the key in a libssh diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c index a087caef..6952e858 100644 --- a/tests/unittests/torture_knownhosts_parsing.c +++ b/tests/unittests/torture_knownhosts_parsing.c @@ -369,6 +369,31 @@ static void torture_knownhosts_read_file(void **state) ssh_list_free(entry_list); } +static void torture_knownhosts_get_algorithms_names(void **state) +{ + const char *knownhosts_file = *state; + ssh_session session; + const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + char *names = NULL; + bool process_config = false; + + session = ssh_new(); + assert_non_null(session); + + /* This makes sure the global configuration file is not processed */ + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); + + names = ssh_known_hosts_get_algorithms_names(session); + assert_non_null(names); + assert_string_equal(names, expect); + + SAFE_FREE(names); + ssh_free(session); +} + #ifndef _WIN32 /* There is no /dev/null on Windows */ static void torture_knownhosts_host_exists(void **state) { @@ -510,6 +535,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_knownhosts_read_file, setup_knownhosts_file_duplicate, teardown_knownhosts_file), + cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names, + setup_knownhosts_file, + teardown_knownhosts_file), #ifndef _WIN32 cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists, setup_knownhosts_file, |