knownhosts: Consult also the global known hosts file

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
Jakub Jelen
2018-10-19 14:20:40 +02:00
committed by Andreas Schneider
parent ae6b0e0f49
commit f622c4309b
2 changed files with 110 additions and 16 deletions

View File

@@ -23,5 +23,9 @@
#define SSH_KNOWNHOSTS_H_ #define SSH_KNOWNHOSTS_H_
struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session); struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session);
enum ssh_known_hosts_e
ssh_session_get_known_hosts_entry_file(ssh_session session,
const char *filename,
struct ssh_knownhosts_entry **pentry);
#endif /* SSH_KNOWNHOSTS_H_ */ #endif /* SSH_KNOWNHOSTS_H_ */

View File

@@ -182,11 +182,16 @@ static int known_hosts_read_line(FILE *fp,
return -1; return -1;
} }
/* This method reads the known_hosts file referenced by the path
* in filename argument, and entries matching the match argument
* will be added to the list in entries argument.
* If the entries list is NULL, it will allocate a new list. Caller
* is responsible to free it even if an error occurs.
*/
static int ssh_known_hosts_read_entries(const char *match, static int ssh_known_hosts_read_entries(const char *match,
const char *filename, const char *filename,
struct ssh_list **entries) struct ssh_list **entries)
{ {
struct ssh_list *entry_list;
char line[8192]; char line[8192];
size_t lineno = 0; size_t lineno = 0;
size_t len = 0; size_t len = 0;
@@ -195,13 +200,18 @@ static int ssh_known_hosts_read_entries(const char *match,
fp = fopen(filename, "r"); fp = fopen(filename, "r");
if (fp == NULL) { if (fp == NULL) {
return SSH_ERROR; SSH_LOG(SSH_LOG_WARN, "Failed to open the known_hosts file '%s': %s",
filename, strerror(errno));
/* The missing file is not an error here */
return SSH_OK;
} }
entry_list = ssh_list_new(); if (*entries == NULL) {
if (entry_list == NULL) { *entries = ssh_list_new();
fclose(fp); if (*entries == NULL) {
return SSH_ERROR; fclose(fp);
return SSH_ERROR;
}
} }
for (rc = known_hosts_read_line(fp, line, sizeof(line), &len, &lineno); for (rc = known_hosts_read_line(fp, line, sizeof(line), &len, &lineno);
@@ -231,15 +241,12 @@ static int ssh_known_hosts_read_entries(const char *match,
} else if (rc != SSH_OK) { } else if (rc != SSH_OK) {
goto error; goto error;
} }
ssh_list_append(entry_list, entry); ssh_list_append(*entries, entry);
} }
*entries = entry_list;
fclose(fp); fclose(fp);
return SSH_OK; return SSH_OK;
error: error:
ssh_list_free(entry_list);
fclose(fp); fclose(fp);
return SSH_ERROR; return SSH_ERROR;
} }
@@ -323,8 +330,23 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
rc = ssh_known_hosts_read_entries(host_port, rc = ssh_known_hosts_read_entries(host_port,
session->opts.knownhosts, session->opts.knownhosts,
&entry_list); &entry_list);
if (rc != 0) {
ssh_list_free(entry_list);
ssh_list_free(list);
return NULL;
}
rc = ssh_known_hosts_read_entries(host_port,
session->opts.global_knownhosts,
&entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list);
ssh_list_free(list);
return NULL;
}
if (entry_list == NULL) {
ssh_list_free(list); ssh_list_free(list);
return NULL; return NULL;
} }
@@ -554,8 +576,17 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
rc = ssh_known_hosts_read_entries(host_port, rc = ssh_known_hosts_read_entries(host_port,
session->opts.knownhosts, session->opts.knownhosts,
&entry_list); &entry_list);
if (rc != 0) {
ssh_list_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN;
}
rc = ssh_known_hosts_read_entries(host_port,
session->opts.global_knownhosts,
&entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
@@ -655,10 +686,11 @@ int ssh_session_export_known_hosts_entry(ssh_session session,
} }
/** /**
* @brief Add the current connected server to the known_hosts file. * @brief Add the current connected server to the user known_hosts file.
* *
* This adds the currently connected server to the known_hosts file by * This adds the currently connected server to the known_hosts file by
* appending a new line at the end. * appending a new line at the end. The global known_hosts file is considered
* read-only so it is not touched by this function.
* *
* @param[in] session The session to use to write the entry. * @param[in] session The session to use to write the entry.
* *
@@ -745,6 +777,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
filename, filename,
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
@@ -822,9 +855,7 @@ enum ssh_known_hosts_e
ssh_session_get_known_hosts_entry(ssh_session session, ssh_session_get_known_hosts_entry(ssh_session session,
struct ssh_knownhosts_entry **pentry) struct ssh_knownhosts_entry **pentry)
{ {
ssh_key server_pubkey = NULL; enum ssh_known_hosts_e old_rv, rv = SSH_KNOWN_HOSTS_UNKNOWN;
char *host_port = NULL;
enum ssh_known_hosts_e found = SSH_KNOWN_HOSTS_UNKNOWN;
if (session->opts.knownhosts == NULL) { if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) { if (ssh_options_apply(session) < 0) {
@@ -836,6 +867,65 @@ ssh_session_get_known_hosts_entry(ssh_session session,
} }
} }
rv = ssh_session_get_known_hosts_entry_file(session,
session->opts.knownhosts,
pentry);
if (rv == SSH_KNOWN_HOSTS_OK) {
/* We already found a match in the first file: return */
return rv;
}
old_rv = rv;
rv = ssh_session_get_known_hosts_entry_file(session,
session->opts.global_knownhosts,
pentry);
/* If we did not find any match at all: we report the previous result */
if (rv == SSH_KNOWN_HOSTS_UNKNOWN) {
return old_rv;
}
/* We found some match: return it */
return rv;
}
/**
* @brief Get the known_hosts entry for the current connected session
* from the given known_hosts file.
*
* @param[in] session The session to validate.
*
* @param[in] filename The filename to parse.
*
* @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_file(ssh_session session,
const char *filename,
struct ssh_knownhosts_entry **pentry)
{
ssh_key server_pubkey = NULL;
char *host_port = NULL;
enum ssh_known_hosts_e found = SSH_KNOWN_HOSTS_UNKNOWN;
server_pubkey = ssh_dh_get_current_server_publickey(session); server_pubkey = ssh_dh_get_current_server_publickey(session);
if (server_pubkey == NULL) { if (server_pubkey == NULL) {
ssh_set_error(session, ssh_set_error(session,
@@ -852,7 +942,7 @@ ssh_session_get_known_hosts_entry(ssh_session session,
} }
found = ssh_known_hosts_check_server_key(host_port, found = ssh_known_hosts_check_server_key(host_port,
session->opts.knownhosts, filename,
server_pubkey, server_pubkey,
pentry); pentry);
SAFE_FREE(host_port); SAFE_FREE(host_port);