aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Schneider <asn@cryptomilk.org>2018-02-03 16:54:58 +0100
committerAndreas Schneider <asn@cryptomilk.org>2018-06-04 11:20:28 +0200
commita209f928d2f4dfde96c04423df9cc246d4accdb0 (patch)
tree3a730df3f42997624ad59a93a04d4a8e4c581b58
parentf23dbe6f4221fd59337087298ed9536c1a05ea63 (diff)
downloadlibssh-a209f928d2f4dfde96c04423df9cc246d4accdb0.tar.gz
libssh-a209f928d2f4dfde96c04423df9cc246d4accdb0.tar.xz
libssh-a209f928d2f4dfde96c04423df9cc246d4accdb0.zip
kwonhosts: Add functions to check if servers public key is known
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r--include/libssh/libssh.h6
-rw-r--r--src/knownhosts.c156
-rw-r--r--tests/client/torture_knownhosts_verify.c20
3 files changed, 181 insertions, 1 deletions
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 336e31da..03241493 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -540,9 +540,13 @@ LIBSSH_API enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session
LIBSSH_API int ssh_session_export_known_hosts_entry(ssh_session session,
char **pentry_string);
-
LIBSSH_API int ssh_session_update_known_hosts(ssh_session session);
+LIBSSH_API enum ssh_known_hosts_e
+ssh_session_get_known_hosts_entry(ssh_session session,
+ struct ssh_knownhosts_entry **pentry);
+LIBSSH_API enum ssh_known_hosts_e ssh_session_is_known_server(ssh_session session);
+
/* LOGGING */
LIBSSH_API int ssh_set_log_level(int level);
LIBSSH_API int ssh_get_log_level(void);
diff --git a/src/knownhosts.c b/src/knownhosts.c
index bc789afc..6cb14989 100644
--- a/src/knownhosts.c
+++ b/src/knownhosts.c
@@ -32,6 +32,7 @@
#include <netinet/in.h>
#include "libssh/priv.h"
+#include "libssh/dh.h"
#include "libssh/session.h"
#include "libssh/options.h"
#include "libssh/misc.h"
@@ -634,3 +635,158 @@ int ssh_session_update_known_hosts(ssh_session session)
return SSH_OK;
}
+
+static enum ssh_known_hosts_e
+ssh_known_hosts_check_server_key(const char *hosts_entry,
+ const char *filename,
+ ssh_key server_key,
+ struct ssh_knownhosts_entry **pentry)
+{
+ struct ssh_list *entry_list = NULL;
+ struct ssh_iterator *it = NULL;
+ enum ssh_known_hosts_e found = SSH_KNOWN_HOSTS_UNKNOWN;
+ int rc;
+
+ rc = ssh_known_hosts_read_entries(hosts_entry,
+ filename,
+ &entry_list);
+ if (rc != 0) {
+ return SSH_KNOWN_HOSTS_NOT_FOUND;
+ }
+
+ it = ssh_list_get_iterator(entry_list);
+ if (it == NULL) {
+ ssh_list_free(entry_list);
+ return SSH_KNOWN_HOSTS_UNKNOWN;
+ }
+
+ for (;it != NULL; it = it->next) {
+ struct ssh_knownhosts_entry *entry = NULL;
+ int cmp;
+
+ entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
+
+ cmp = ssh_key_cmp(server_key, entry->publickey, SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found = SSH_KNOWN_HOSTS_OK;
+ if (pentry != NULL) {
+ *pentry = entry;
+ ssh_list_remove(entry_list, it);
+ }
+ break;
+ }
+
+ if (ssh_key_type(server_key) == ssh_key_type(entry->publickey)) {
+ found = SSH_KNOWN_HOSTS_CHANGED;
+ } else {
+ found = SSH_KNOWN_HOSTS_OTHER;
+ }
+ }
+
+ 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 found;
+}
+
+/**
+ * @brief Get the known_hosts entry for the current connected session.
+ *
+ * @param[in] session The session to validate.
+ *
+ * @param[in] pentry A pointer to store the allocated known hosts entry.
+ *
+ * @returns SSH_KNOWN_HOSTS_OK: The server is known and has not changed.\n
+ * SSH_KNOWN_HOSTS_CHANGED: The server key has changed. Either you
+ * are under attack or the administrator
+ * changed the key. You HAVE to warn the
+ * user about a possible attack.\n
+ * SSH_KNOWN_HOSTS_OTHER: The server gave use a key of a type while
+ * we had an other type recorded. It is a
+ * possible attack.\n
+ * SSH_KNOWN_HOSTS_UNKNOWN: The server is unknown. User should
+ * confirm the public key hash is correct.\n
+ * SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
+ * host is thus unknown. File will be
+ * created if host key is accepted.\n
+ * SSH_KNOWN_HOSTS_ERROR: There had been an eror checking the host.
+ *
+ * @see ssh_knownhosts_entry_free()
+ */
+enum ssh_known_hosts_e
+ssh_session_get_known_hosts_entry(ssh_session session,
+ struct ssh_knownhosts_entry **pentry)
+{
+ ssh_key server_pubkey = NULL;
+ char *host_port = NULL;
+ enum ssh_known_hosts_e found = SSH_KNOWN_HOSTS_UNKNOWN;
+
+ 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;
+ }
+ }
+
+ server_pubkey = ssh_dh_get_current_server_publickey(session);
+ if (server_pubkey == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "ssh_session_is_known_host called without a "
+ "server_key!");
+
+ return SSH_KNOWN_HOSTS_ERROR;
+ }
+
+ host_port = ssh_session_get_host_port(session);
+ if (host_port == NULL) {
+ return SSH_KNOWN_HOSTS_ERROR;
+ }
+
+ found = ssh_known_hosts_check_server_key(host_port,
+ session->opts.knownhosts,
+ server_pubkey,
+ pentry);
+
+ return found;
+}
+
+/**
+ * @brief Check if the servers public key for the connected session is known.
+ *
+ * This checks if we already know the public key of the server we want to
+ * connect to. This allows to detect if there is a MITM attach going on
+ * of if there have been changes on the server we don't know about.
+ *
+ * @param[in] session The SSH to validate.
+ *
+ * @returns SSH_KNOWN_HOSTS_OK: The server is known and has not changed.\n
+ * SSH_KNOWN_HOSTS_CHANGED: The server key has changed. Either you
+ * are under attack or the administrator
+ * changed the key. You HAVE to warn the
+ * user about a possible attack.\n
+ * SSH_KNOWN_HOSTS_OTHER: The server gave use a key of a type while
+ * we had an other type recorded. It is a
+ * possible attack.\n
+ * SSH_KNOWN_HOSTS_UNKNOWN: The server is unknown. User should
+ * confirm the public key hash is correct.\n
+ * SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
+ * host is thus unknown. File will be
+ * created if host key is accepted.\n
+ * SSH_KNOWN_HOSTS_ERROR: There had been an eror checking the host.
+ */
+enum ssh_known_hosts_e ssh_session_is_known_server(ssh_session session)
+{
+ return ssh_session_get_known_hosts_entry(session, NULL);
+}
diff --git a/tests/client/torture_knownhosts_verify.c b/tests/client/torture_knownhosts_verify.c
index a266e92a..323b8e2b 100644
--- a/tests/client/torture_knownhosts_verify.c
+++ b/tests/client/torture_knownhosts_verify.c
@@ -103,12 +103,32 @@ static void torture_knownhosts_export(void **state)
SAFE_FREE(entry);
}
+static void torture_knownhosts_write_and_verify(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ enum ssh_known_hosts_e found;
+ int rc;
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_session_update_known_hosts(session);
+ assert_int_equal(rc, SSH_OK);
+
+ found = ssh_session_is_known_server(session);
+ assert_int_equal(found, SSH_KNOWN_HOSTS_OK);
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_knownhosts_export,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_knownhosts_write_and_verify,
+ session_setup,
+ session_teardown),
};
ssh_init();