aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libssh/libssh.h10
-rw-r--r--src/knownhosts.c91
-rw-r--r--tests/unittests/torture_knownhosts_parsing.c26
3 files changed, 127 insertions, 0 deletions
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();