knownhosts: Avoid memory leaks on invalid entries

When `known_hosts` file contained matching valid entry followed by
invalid entry, the first record was already allocated in
`ssh_known_hosts_read_entries()`, but not freed on error.

This could cause possible memory leaks in client, but we do not
consider them as security relevant as the leaks do not add up and
successful exploitaition is hard or impossible.

Originally reported by Kang Yang.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <norbertpocs0@gmail.com>
(cherry picked from commit 1525ea3dda)
This commit is contained in:
Jakub Jelen
2026-01-15 11:32:51 +01:00
parent c4681304cc
commit 441dfa5327

View File

@@ -216,11 +216,22 @@ ssh_known_hosts_entries_compare(struct ssh_knownhosts_entry *k1,
return 0; return 0;
} }
/* This method reads the known_hosts file referenced by the path /**
* @internal
*
* @brief Read entries from filename to provided list
*
* This method reads the known_hosts file referenced by the path
* in filename argument, and entries matching the match argument * in filename argument, and entries matching the match argument
* will be added to the list in entries argument. * will be added to the list in entries argument.
* If the entries list is NULL, it will allocate a new list. Caller * If the entries list is NULL, it will allocate a new list. Caller
* is responsible to free it even if an error occurs. * is responsible to free it even if an error occurs.
*
* @param match[in] The host name (with port) to match against
* @param filename[in] The known hosts file to parse
* @param entries[in,out] The list of entries to append matching ones
* @return `SSH_OK` on missing file or success parsing,
* `SSH_ERROR` on error
*/ */
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,
@@ -346,6 +357,33 @@ static char *ssh_session_get_host_port(ssh_session session)
/** /**
* @internal * @internal
*
* @brief Free known hosts entries list
*
* @param[in] entry_list The list of ssh_knownhosts_entry items
*/
static void ssh_knownhosts_entries_free(struct ssh_list *entry_list)
{
struct ssh_iterator *it = NULL;
if (entry_list == NULL) {
return;
}
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);
}
/**
* @internal
*
* @brief Check which host keys should be preferred for the session. * @brief Check which host keys should be preferred for the session.
* *
* This checks the known_hosts file to find out which algorithms should be * This checks the known_hosts file to find out which algorithms should be
@@ -453,7 +491,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
return list; return list;
error: error:
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
ssh_list_free(list); ssh_list_free(list);
return NULL; return NULL;
} }
@@ -505,6 +543,7 @@ static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e typ
/** /**
* @internal * @internal
*
* @brief Get the host keys algorithms identifiers from the known_hosts files * @brief Get the host keys algorithms identifiers from the known_hosts files
* *
* This expands the signatures types that can be generated from the keys types * This expands the signatures types that can be generated from the keys types
@@ -549,7 +588,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return NULL; return NULL;
} }
@@ -558,7 +597,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list); &entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return NULL; return NULL;
} }
@@ -799,7 +838,6 @@ out:
enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
{ {
struct ssh_list *entry_list = NULL; struct ssh_list *entry_list = NULL;
struct ssh_iterator *it = NULL;
char *host_port = NULL; char *host_port = NULL;
bool global_known_hosts_found = false; bool global_known_hosts_found = false;
bool known_hosts_found = false; bool known_hosts_found = false;
@@ -860,7 +898,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR; return SSH_KNOWN_HOSTS_ERROR;
} }
} }
@@ -871,7 +909,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR; return SSH_KNOWN_HOSTS_ERROR;
} }
} }
@@ -883,16 +921,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
for (it = ssh_list_get_iterator(entry_list); ssh_knownhosts_entries_free(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; return SSH_KNOWN_HOSTS_OK;
} }
@@ -1079,13 +1108,13 @@ 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); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
it = ssh_list_get_iterator(entry_list); it = ssh_list_get_iterator(entry_list);
if (it == NULL) { if (it == NULL) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
@@ -1115,16 +1144,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
} }
} }
for (it = ssh_list_get_iterator(entry_list); ssh_knownhosts_entries_free(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; return found;
} }
@@ -1196,6 +1216,8 @@ ssh_session_get_known_hosts_entry(ssh_session session,
} }
/** /**
* @internal
*
* @brief Get the known_hosts entry for the current connected session * @brief Get the known_hosts entry for the current connected session
* from the given known_hosts file. * from the given known_hosts file.
* *