diff --git a/src/kex.c b/src/kex.c index 6eee6c27..6f678c46 100644 --- a/src/kex.c +++ b/src/kex.c @@ -36,6 +36,7 @@ #include "libssh/string.h" #include "libssh/curve25519.h" #include "libssh/knownhosts.h" +#include "libssh/misc.h" #ifdef HAVE_LIBGCRYPT # define BLOWFISH "blowfish-cbc," @@ -564,7 +565,8 @@ void ssh_list_kex(struct ssh_kex_struct *kex) { * @returns a cstring containing a comma-separated list of hostkey methods. * NULL if no method matches */ -static char *ssh_client_select_hostkeys(ssh_session session){ +static char *ssh_client_select_hostkeys(ssh_session session) +{ char methods_buffer[128]={0}; static const char *preferred_hostkeys[] = { "ssh-ed25519", @@ -577,42 +579,65 @@ static char *ssh_client_select_hostkeys(ssh_session session){ #endif NULL }; - char **methods; - int i,j; - int needcoma=0; + struct ssh_list *algo_list = NULL; + struct ssh_iterator *it = NULL; + size_t algo_count; + int needcomma = 0; + int i; - methods = ssh_knownhosts_algorithms(session); - if (methods == NULL || methods[0] == NULL){ - SAFE_FREE(methods); - return NULL; - } + algo_list = ssh_known_hosts_get_algorithms(session); + if (algo_list == NULL) { + return NULL; + } - for (i=0;preferred_hostkeys[i] != NULL; ++i){ - for (j=0; methods[j] != NULL; ++j){ - if(strcmp(preferred_hostkeys[i], methods[j]) == 0){ - if (ssh_verify_existing_algo(SSH_HOSTKEYS, methods[j])){ - if(needcoma) - strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1); - strncat(methods_buffer, methods[j], sizeof(methods_buffer)-strlen(methods_buffer)-1); - needcoma = 1; - } - } - } - } - for(i=0;methods[i]!= NULL; ++i){ - SAFE_FREE(methods[i]); - } - SAFE_FREE(methods); + algo_count = ssh_list_count(algo_list); + if (algo_count == 0) { + ssh_list_free(algo_list); + return NULL; + } - if(strlen(methods_buffer) > 0){ - SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer); - return strdup(methods_buffer); - } else { - SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file"); - return NULL; - } + for (i = 0; preferred_hostkeys[i] != NULL; ++i) { + for (it = ssh_list_get_iterator(algo_list); + it != NULL; + it = ssh_list_get_iterator(algo_list)) { + const char *algo = ssh_iterator_value(const char *, it); + int cmp; + int ok; + cmp = strcmp(preferred_hostkeys[i], algo); + if (cmp == 0) { + ok = ssh_verify_existing_algo(SSH_HOSTKEYS, algo); + if (ok) { + if (needcomma) { + strncat(methods_buffer, + ",", + sizeof(methods_buffer) - strlen(methods_buffer) - 1); + } + strncat(methods_buffer, + algo, + sizeof(methods_buffer) - strlen(methods_buffer) - 1); + needcomma = 1; + } + } + + ssh_list_remove(algo_list, it); + } + } + ssh_list_free(algo_list); + + if (strlen(methods_buffer) == 0) { + SSH_LOG(SSH_LOG_DEBUG, + "No supported kex method for existing key in known_hosts file"); + return NULL; + } + + SSH_LOG(SSH_LOG_DEBUG, + "Changing host key method to \"%s\"", + methods_buffer); + + return strdup(methods_buffer); } + /** * @brief sets the key exchange parameters to be sent to the server, * in function of the options and available methods. diff --git a/tests/client/torture_knownhosts.c b/tests/client/torture_knownhosts.c index cfa47deb..dc5c61b9 100644 --- a/tests/client/torture_knownhosts.c +++ b/tests/client/torture_knownhosts.c @@ -40,18 +40,7 @@ "YgIytryNn7LLiwYfoSxvWigFrTTZsrVtCOYyNgklmffpGdzuC43wdANvTewfI9G" \ "o71r8EXmEc228CrYPmb8Scv3mpXFK/BosohSGkPlEHu9lf3YjnknBicDaVtJOYp" \ "wnXJPjZo2EhG79HxDRpjJHH" -#ifdef HAVE_DSA -#define BADDSA "AAAAB3NzaC1kc3MAAACBAITDKqGQ5aC5wHySG6ZdL1+BVBY2nLP5vzw3i3pvZfP" \ - "yNUS0UCwrt5pajsMvDRGXXebTJhWVonDnv8tpSgiuIBXMZrma8CU1KCFGRzwb/n8" \ - "cc5tJmIphlOUTrObjBmsRz7u1eZmoaddXC9ask6BNnt0DmhzYi2esL3mbardy8IN" \ - "zAAAAFQDlPFCm410pgQQPb3X5FWjyVEIl+QAAAIAp0vqfir8K8p+zP4dzFG7ppnt" \ - "DjaXf3ge6URF7f5xPDo6CClGo2JQ2REF8NxM7K9cLgR9Ifx2ahO48UMgrXEl/BOp" \ - "IQHpeBqUz26a49O5J0WEW16YSUHxWwMxWVe/SRmyKdTUZJ6fcepH88JNqm3XudNn" \ - "s78grM+yx9mcXnK2AsAAAAIBxpF8ZQIlGrSgwCmCfwjP156bC3Ya6LYf9ZpLJ0dX" \ - "EcxqLVllrNEvd2EGD9p16BYO2yaalYon8im59PtOcul2ay5XQ6rVDQ2T0pgNUpsI" \ - "h0dSi8VJXI1wes5HTyLsv9VBmU1uCXUUvufoQKfF/OcSH0ufcCpnd62g1/adZcy2" \ - "WJg==" -#endif +#define BADED25519 "AAAAC3NzaC1lZDI1NTE5AAAAIE74wHmKKkrxpW/dZ69pKPlMoWG9VvWfrNnUkWRQqaDa" static int sshd_setup(void **state) { @@ -189,7 +178,6 @@ static void torture_knownhosts_fail(void **state) { assert_int_equal(rc, SSH_SERVER_KNOWN_CHANGED); } -#ifdef HAVE_DSA static void torture_knownhosts_other(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; @@ -206,7 +194,7 @@ static void torture_knownhosts_other(void **state) { rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_int_equal(rc, SSH_OK); - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss"); + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519"); assert_int_equal(rc, SSH_OK); file = fopen(known_hosts_file, "w"); @@ -239,7 +227,7 @@ static void torture_knownhosts_other_auto(void **state) { rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_int_equal(rc, SSH_OK); - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss"); + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519"); assert_int_equal(rc, SSH_OK); rc = ssh_connect(session); @@ -269,13 +257,12 @@ static void torture_knownhosts_other_auto(void **state) { rc = ssh_connect(session); assert_true(rc==SSH_OK); - /* ssh-rsa is the default but libssh should try ssh-dss instead */ + /* ssh-rsa is the default but libssh should try ssh-ed25519 instead */ rc = ssh_is_server_known(session); assert_int_equal(rc, SSH_SERVER_KNOWN_OK); /* session will be freed by session_teardown() */ } -#endif static void torture_knownhosts_conflict(void **state) { struct torture_state *s = *state; @@ -302,9 +289,7 @@ static void torture_knownhosts_conflict(void **state) { file = fopen(known_hosts_file, "w"); assert_true(file != NULL); fprintf(file, "127.0.0.10 ssh-rsa %s\n", BADRSA); -#ifdef HAVE_DSA - fprintf(file, "127.0.0.10 ssh-dss %s\n", BADDSA); -#endif + fprintf(file, "127.0.0.10 ssh-ed25519 %s\n", BADED25519); fclose(file); rc = ssh_connect(session); @@ -362,21 +347,15 @@ static void torture_knownhosts_precheck(void **state) { file = fopen(known_hosts_file, "w"); assert_true(file != NULL); fprintf(file, "127.0.0.10 ssh-rsa %s\n", BADRSA); -#ifdef HAVE_DSA - fprintf(file, "127.0.0.10 ssh-dss %s\n", BADDSA); -#endif + fprintf(file, "127.0.0.10 ssh-ed25519 %s\n", BADED25519); fclose(file); kex = ssh_knownhosts_algorithms(session); assert_true(kex != NULL); assert_string_equal(kex[0],"ssh-rsa"); -#ifdef HAVE_DSA - assert_string_equal(kex[1],"ssh-dss"); + assert_string_equal(kex[1],"ssh-ed25519"); assert_true(kex[2]==NULL); free(kex[1]); -#else - assert_true(kex[1]==NULL); -#endif free(kex[0]); free(kex); } @@ -390,14 +369,12 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_knownhosts_fail, session_setup, session_teardown), -#ifdef HAVE_DSA cmocka_unit_test_setup_teardown(torture_knownhosts_other, session_setup, session_teardown), cmocka_unit_test_setup_teardown(torture_knownhosts_other_auto, session_setup, session_teardown), -#endif cmocka_unit_test_setup_teardown(torture_knownhosts_conflict, session_setup, session_teardown),