mirror of
				https://git.libssh.org/projects/libssh.git
				synced 2025-11-03 21:53:14 +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_
 | 
					#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);
 | 
				
			||||||
 | 
					char *ssh_known_hosts_get_algorithms_names(ssh_session session);
 | 
				
			||||||
enum ssh_known_hosts_e
 | 
					enum ssh_known_hosts_e
 | 
				
			||||||
ssh_session_get_known_hosts_entry_file(ssh_session session,
 | 
					ssh_session_get_known_hosts_entry_file(ssh_session session,
 | 
				
			||||||
                                       const char *filename,
 | 
					                                       const char *filename,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										141
									
								
								src/knownhosts.c
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								src/knownhosts.c
									
									
									
									
									
								
							@@ -42,6 +42,7 @@
 | 
				
			|||||||
#include "libssh/pki.h"
 | 
					#include "libssh/pki.h"
 | 
				
			||||||
#include "libssh/dh.h"
 | 
					#include "libssh/dh.h"
 | 
				
			||||||
#include "libssh/knownhosts.h"
 | 
					#include "libssh/knownhosts.h"
 | 
				
			||||||
 | 
					#include "libssh/token.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @addtogroup libssh_session
 | 
					 * @addtogroup libssh_session
 | 
				
			||||||
@@ -451,6 +452,146 @@ error:
 | 
				
			|||||||
    return NULL;
 | 
					    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
 | 
					 * @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);
 | 
					    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 */
 | 
					#ifndef _WIN32 /* There is no /dev/null on Windows */
 | 
				
			||||||
static void torture_knownhosts_host_exists(void **state)
 | 
					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,
 | 
					        cmocka_unit_test_setup_teardown(torture_knownhosts_read_file,
 | 
				
			||||||
                                        setup_knownhosts_file_duplicate,
 | 
					                                        setup_knownhosts_file_duplicate,
 | 
				
			||||||
                                        teardown_knownhosts_file),
 | 
					                                        teardown_knownhosts_file),
 | 
				
			||||||
 | 
					        cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names,
 | 
				
			||||||
 | 
					                                        setup_knownhosts_file,
 | 
				
			||||||
 | 
					                                        teardown_knownhosts_file),
 | 
				
			||||||
#ifndef _WIN32
 | 
					#ifndef _WIN32
 | 
				
			||||||
        cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists,
 | 
					        cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists,
 | 
				
			||||||
                                        setup_knownhosts_file,
 | 
					                                        setup_knownhosts_file,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user