mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-07-31 00:03:07 +03:00
knownhosts: Introduced ssh_known_hosts_get_algorithms_names()
The added internal function obtain a newly allocated string containing a list of the signature types that can be generated by the keys present in the known_hosts files, separated by commas. Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
@ -23,6 +23,7 @@
|
||||
#define SSH_KNOWNHOSTS_H_
|
||||
|
||||
struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session);
|
||||
char *ssh_known_hosts_get_algorithms_names(ssh_session session);
|
||||
enum ssh_known_hosts_e
|
||||
ssh_session_get_known_hosts_entry_file(ssh_session session,
|
||||
const char *filename,
|
||||
|
141
src/knownhosts.c
141
src/knownhosts.c
@ -42,6 +42,7 @@
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/knownhosts.h"
|
||||
#include "libssh/token.h"
|
||||
|
||||
/**
|
||||
* @addtogroup libssh_session
|
||||
@ -451,6 +452,146 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Returns a static string containing a list of the signature types the
|
||||
* given key type can generate.
|
||||
*
|
||||
* @returns A static cstring containing the signature types the key is able to
|
||||
* generate separated by commas; NULL in case of error
|
||||
*/
|
||||
static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e type)
|
||||
{
|
||||
switch (type) {
|
||||
case SSH_KEYTYPE_RSA:
|
||||
return "rsa-sha2-512,rsa-sha2-256,ssh-rsa";
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
return "ssh-ed25519";
|
||||
#ifdef HAVE_DSA
|
||||
case SSH_KEYTYPE_DSS:
|
||||
return "ssh-dss";
|
||||
#endif
|
||||
#ifdef HAVE_ECDH
|
||||
case SSH_KEYTYPE_ECDSA_P256:
|
||||
return "ecdsa-sha2-nistp256";
|
||||
case SSH_KEYTYPE_ECDSA_P384:
|
||||
return "ecdsa-sha2-nistp384";
|
||||
case SSH_KEYTYPE_ECDSA_P521:
|
||||
return "ecdsa-sha2-nistp521";
|
||||
#endif
|
||||
case SSH_KEYTYPE_UNKNOWN:
|
||||
default:
|
||||
SSH_LOG(SSH_LOG_WARN, "The given type %d is not a base private key type "
|
||||
"or is unsupported", type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @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
|
||||
* present in the known_hosts files
|
||||
*
|
||||
* @param[in] session The ssh session to use.
|
||||
*
|
||||
* @return A newly allocated cstring containing a list of signature algorithms
|
||||
* that can be generated by the host using the keys listed in the known_hosts
|
||||
* files, NULL on error.
|
||||
*/
|
||||
char *ssh_known_hosts_get_algorithms_names(ssh_session session)
|
||||
{
|
||||
char methods_buffer[256 + 1] = {0};
|
||||
struct ssh_list *entry_list = NULL;
|
||||
struct ssh_iterator *it = NULL;
|
||||
char *host_port = NULL;
|
||||
size_t count;
|
||||
bool needcomma = false;
|
||||
char *names;
|
||||
|
||||
int rc;
|
||||
|
||||
if (session->opts.knownhosts == NULL ||
|
||||
session->opts.global_knownhosts == NULL) {
|
||||
if (ssh_options_apply(session) < 0) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Can't find a known_hosts file");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
host_port = ssh_session_get_host_port(session);
|
||||
if (host_port == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_known_hosts_read_entries(host_port,
|
||||
session->opts.knownhosts,
|
||||
&entry_list);
|
||||
if (rc != 0) {
|
||||
SAFE_FREE(host_port);
|
||||
ssh_list_free(entry_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_known_hosts_read_entries(host_port,
|
||||
session->opts.global_knownhosts,
|
||||
&entry_list);
|
||||
SAFE_FREE(host_port);
|
||||
if (rc != 0) {
|
||||
ssh_list_free(entry_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (entry_list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = ssh_list_count(entry_list);
|
||||
if (count == 0) {
|
||||
ssh_list_free(entry_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (it = ssh_list_get_iterator(entry_list);
|
||||
it != NULL;
|
||||
it = ssh_list_get_iterator(entry_list))
|
||||
{
|
||||
struct ssh_knownhosts_entry *entry = NULL;
|
||||
const char *algo = NULL;
|
||||
|
||||
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
|
||||
algo = ssh_known_host_sigs_from_hostkey_type(entry->publickey->type);
|
||||
if (algo == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needcomma) {
|
||||
strncat(methods_buffer,
|
||||
",",
|
||||
sizeof(methods_buffer) - strlen(methods_buffer) - 1);
|
||||
}
|
||||
|
||||
strncat(methods_buffer,
|
||||
algo,
|
||||
sizeof(methods_buffer) - strlen(methods_buffer) - 1);
|
||||
needcomma = true;
|
||||
|
||||
ssh_knownhosts_entry_free(entry);
|
||||
ssh_list_remove(entry_list, it);
|
||||
}
|
||||
|
||||
ssh_list_free(entry_list);
|
||||
|
||||
names = ssh_remove_duplicates(methods_buffer);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a line from a known_hosts entry into a structure
|
||||
*
|
||||
|
@ -369,6 +369,31 @@ static void torture_knownhosts_read_file(void **state)
|
||||
ssh_list_free(entry_list);
|
||||
}
|
||||
|
||||
static void torture_knownhosts_get_algorithms_names(void **state)
|
||||
{
|
||||
const char *knownhosts_file = *state;
|
||||
ssh_session session;
|
||||
const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa";
|
||||
char *names = NULL;
|
||||
bool process_config = false;
|
||||
|
||||
session = ssh_new();
|
||||
assert_non_null(session);
|
||||
|
||||
/* This makes sure the global configuration file is not processed */
|
||||
ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config);
|
||||
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
|
||||
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file);
|
||||
|
||||
names = ssh_known_hosts_get_algorithms_names(session);
|
||||
assert_non_null(names);
|
||||
assert_string_equal(names, expect);
|
||||
|
||||
SAFE_FREE(names);
|
||||
ssh_free(session);
|
||||
}
|
||||
|
||||
#ifndef _WIN32 /* There is no /dev/null on Windows */
|
||||
static void torture_knownhosts_host_exists(void **state)
|
||||
{
|
||||
@ -510,6 +535,9 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_knownhosts_read_file,
|
||||
setup_knownhosts_file_duplicate,
|
||||
teardown_knownhosts_file),
|
||||
cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names,
|
||||
setup_knownhosts_file,
|
||||
teardown_knownhosts_file),
|
||||
#ifndef _WIN32
|
||||
cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists,
|
||||
setup_knownhosts_file,
|
||||
|
Reference in New Issue
Block a user