kwonhosts: Add functions to check if servers public key is known
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
родитель
f23dbe6f42
Коммит
a209f928d2
@ -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);
|
||||
|
156
src/knownhosts.c
156
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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user