From 963c46e4fbce513558c941de51e073fc07ad37ae Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sat, 3 Feb 2018 16:53:21 +0100 Subject: knownhosts: Add ssh_session_has_known_hosts_entry() Signed-off-by: Andreas Schneider --- include/libssh/libssh.h | 10 +++ src/knownhosts.c | 91 ++++++++++++++++++++++++++++ tests/unittests/torture_knownhosts_parsing.c | 26 ++++++++ 3 files changed, 127 insertions(+) diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index ac5daaab..0c65547c 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -238,6 +238,15 @@ enum ssh_server_known_e { SSH_SERVER_FILE_NOT_FOUND }; +enum ssh_known_hosts_e { + SSH_KNOWN_HOSTS_ERROR = -2, + SSH_KNOWN_HOSTS_NOT_FOUND = -1, + SSH_KNOWN_HOSTS_UNKNOWN = 0, + SSH_KNOWN_HOSTS_OK, + SSH_KNOWN_HOSTS_CHANGED, + SSH_KNOWN_HOSTS_OTHER, +}; + #ifndef MD5_DIGEST_LEN #define MD5_DIGEST_LEN 16 #endif @@ -527,6 +536,7 @@ LIBSSH_API void ssh_knownhosts_entry_free(struct ssh_knownhosts_entry *entry); LIBSSH_API int ssh_known_hosts_parse_line(const char *host, const char *line, struct ssh_knownhosts_entry **entry); +LIBSSH_API enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session); /* LOGGING */ LIBSSH_API int ssh_set_log_level(int level); diff --git a/src/knownhosts.c b/src/knownhosts.c index cf353ef5..7c49470c 100644 --- a/src/knownhosts.c +++ b/src/knownhosts.c @@ -229,6 +229,40 @@ error: return SSH_ERROR; } +static char *ssh_session_get_host_port(ssh_session session) +{ + char *host_port; + char *host; + + if (session->opts.host == NULL) { + ssh_set_error(session, + SSH_FATAL, + "Can't verify server inn known hosts if the host we " + "should connect to has not been set"); + + return NULL; + } + + host = ssh_lowercase(session->opts.host); + if (host == NULL) { + ssh_set_error_oom(session); + return NULL; + } + + if (session->opts.port == 0 || session->opts.port == 22) { + host_port = host; + } else { + host_port = ssh_hostport(host, session->opts.port); + SAFE_FREE(host); + if (host_port == NULL) { + ssh_set_error_oom(session); + return NULL; + } + } + + return host_port; +} + /** * @brief Parse a line from a known_hosts entry into a structure * @@ -380,3 +414,60 @@ out: ssh_knownhosts_entry_free(e); return rc; } + +/** + * @brief Check if the set hostname and port matches an entry in known_hosts. + * + * This check if the set hostname and port has an entry in the known_hosts file. + * You need to set at least the hostname using ssh_options_set(). + * + * @param[in] session The session with with the values set to check. + * + * @return A @ssh_known_hosts_e return value. + */ +enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) +{ + struct ssh_list *entry_list = NULL; + struct ssh_iterator *it = NULL; + char *host_port = NULL; + int rc; + + if (session->opts.knownhosts == NULL) { + if (ssh_options_apply(session) < 0) { + ssh_set_error(session, + SSH_REQUEST_DENIED, + "Can't find a known_hosts file"); + + return SSH_KNOWN_HOSTS_NOT_FOUND; + } + } + + host_port = ssh_session_get_host_port(session); + if (host_port == NULL) { + return SSH_KNOWN_HOSTS_NOT_FOUND; + } + + rc = ssh_known_hosts_read_entries(host_port, + session->opts.knownhosts, + &entry_list); + if (rc != 0) { + return SSH_KNOWN_HOSTS_NOT_FOUND; + } + + if (ssh_list_count(entry_list) == 0) { + return SSH_KNOWN_HOSTS_NOT_FOUND; + } + + for (it = ssh_list_get_iterator(entry_list); + it != NULL; + it = ssh_list_get_iterator(entry_list)) { + struct ssh_knownhosts_entry *entry = NULL; + + entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it); + ssh_knownhosts_entry_free(entry); + ssh_list_remove(entry_list, it); + } + ssh_list_free(entry_list); + + return SSH_KNOWN_HOSTS_OK; +} diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c index 51d816f2..05fef4df 100644 --- a/tests/unittests/torture_knownhosts_parsing.c +++ b/tests/unittests/torture_knownhosts_parsing.c @@ -226,6 +226,29 @@ static void torture_knownhosts_read_file(void **state) } } +static void torture_knownhosts_host_exists(void **state) +{ + const char *knownhosts_file = *state; + enum ssh_known_hosts_e found; + ssh_session session; + + session = ssh_new(); + assert_non_null(session); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); + + found = ssh_session_has_known_hosts_entry(session); + assert_int_equal(found, SSH_KNOWN_HOSTS_OK); + assert_true(found == SSH_KNOWN_HOSTS_OK); + + ssh_options_set(session, SSH_OPTIONS_HOST, "wurstbrot"); + found = ssh_session_has_known_hosts_entry(session); + assert_true(found == SSH_KNOWN_HOSTS_NOT_FOUND); + + ssh_free(session); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -238,6 +261,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_knownhosts_read_file, setup_knownhosts_file, teardown_knownhosts_file), + cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists, + setup_knownhosts_file, + teardown_knownhosts_file), }; ssh_init(); -- cgit v1.2.3