From cce600f98031fdf41aa28f2f2c9c5eacf509e75f Mon Sep 17 00:00:00 2001 From: Aditya Sinha Date: Tue, 4 Mar 2025 20:01:23 +0530 Subject: [PATCH] test for ssh_get_kex_algo() Signed-off-by: Aditya Sinha Reviewed-by: Jakub Jelen --- tests/client/CMakeLists.txt | 4 +- tests/client/torture_get_kex_algo.c | 245 ++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 tests/client/torture_get_kex_algo.c diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt index ae8e5198..538bf6b9 100755 --- a/tests/client/CMakeLists.txt +++ b/tests/client/CMakeLists.txt @@ -18,7 +18,9 @@ set(LIBSSH_CLIENT_TESTS torture_session torture_request_env torture_request_pty_modes - torture_client_global_requests) + torture_client_global_requests + torture_get_kex_algo + ) find_program(SCP_EXECUTABLE NAMES scp) if (SCP_EXECUTABLE) diff --git a/tests/client/torture_get_kex_algo.c b/tests/client/torture_get_kex_algo.c new file mode 100644 index 00000000..bf6d635a --- /dev/null +++ b/tests/client/torture_get_kex_algo.c @@ -0,0 +1,245 @@ +#include "config.h" +#include "libssh/libcrypto.h" +#include +#define LIBSSH_STATIC +#include "torture.h" +#include +#include + +#define ECDH_SHA2_NISTP256 "ecdh-sha2-nistp256" +#define CURVE25519_SHA256 "curve25519-sha256" +#define DIFFIE_HELLMAN_GROUP_14_SHA_1 "diffie-hellman-group14-sha1" +#define KEX_DH_GEX_SHA1 "diffie-hellman-group-exchange-sha1" +#define KEX_DH_GEX_SHA256 "diffie-hellman-group-exchange-sha256" + +static int sshd_setup(void **state) +{ + torture_setup_sshd_server(state, false); + + return 0; +} + +static int sshd_teardown(void **state) +{ + torture_teardown_sshd_server(state); + + return 0; +} + +static int session_setup(void **state) +{ + struct torture_state *s = *state; + int verbosity = torture_libssh_verbosity(); + struct passwd *pwd = NULL; + bool false_v = false; + int rc; + + pwd = getpwnam("bob"); + assert_non_null(pwd); + + rc = setuid(pwd->pw_uid); + assert_return_code(rc, errno); + + s->ssh.session = ssh_new(); + assert_non_null(s->ssh.session); + + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + assert_ssh_return_code(s->ssh.session, rc); + + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &false_v); + assert_ssh_return_code(s->ssh.session, rc); + + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); + assert_ssh_return_code(s->ssh.session, rc); + + return 0; +} + +static int session_teardown(void **state) +{ + struct torture_state *s = *state; + + ssh_disconnect(s->ssh.session); + ssh_free(s->ssh.session); + + return 0; +} + +static void torture_kex_basic_functionality(void **state) +{ + struct torture_state *s = *state; + ssh_session session = NULL; + const char *kex_algo = NULL; + const char *valid_algorithms[] = { + CURVE25519_SHA256, + ECDH_SHA2_NISTP256, + DIFFIE_HELLMAN_GROUP_14_SHA_1, + }; + size_t valid_algorithms_count, i; + int rc; + bool is_valid_algo; + + session = s->ssh.session; + + rc = ssh_connect(session); + assert_ssh_return_code(session, rc); + + kex_algo = ssh_get_kex_algo(session); + assert_non_null(kex_algo); + + is_valid_algo = false; + valid_algorithms_count = + sizeof(valid_algorithms) / sizeof(valid_algorithms[0]); + for (i = 0; i < valid_algorithms_count; i++) { + if (strcmp(kex_algo, valid_algorithms[i]) == 0) { + is_valid_algo = true; + break; + } + } + assert_true(is_valid_algo); +} + +static void torture_kex_algo_preference(void **state) +{ + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + const char *expected_kex = NULL; + const char *actual_kex = NULL; + int rc; + + if (ssh_fips_mode()) { + expected_kex = ECDH_SHA2_NISTP256; + } else { + expected_kex = CURVE25519_SHA256; + } + + rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, expected_kex); + assert_ssh_return_code(session, rc); + + rc = ssh_connect(session); + assert_ssh_return_code(session, rc); + + actual_kex = ssh_get_kex_algo(session); + assert_non_null(actual_kex); + assert_string_equal(actual_kex, expected_kex); +} + +static void torture_kex_algo_negotiation(void **state) +{ + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + const char *kex_list = + "non-existent-algo,not-supported-kex," CURVE25519_SHA256 + "," ECDH_SHA2_NISTP256 "," DIFFIE_HELLMAN_GROUP_14_SHA_1; + int rc, cmp; + const char *negotiated_kex = NULL; + bool found; + char *temp_list = NULL; + char *token = NULL; + + rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex_list); + assert_ssh_return_code(session, rc); + + rc = ssh_connect(session); + assert_ssh_return_code(session, rc); + + negotiated_kex = ssh_get_kex_algo(session); + assert_non_null(negotiated_kex); + + assert_string_not_equal(negotiated_kex, "non-existent-algo"); + assert_string_not_equal(negotiated_kex, "not-supported-kex"); + + found = false; + temp_list = strdup(kex_list); + + for (token = strtok(temp_list, ","); token != NULL; + token = strtok(NULL, ",")) { + cmp = strcmp(token, negotiated_kex); + if (cmp == 0) { + found = true; + break; + } + } + + free(temp_list); + assert_true(found); +} + +static void torture_kex_algo_before_connect(void **state) +{ + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + const char *kex_algo = NULL; + + kex_algo = ssh_get_kex_algo(session); + assert_null(kex_algo); +} + +#ifdef WITH_GEX +static void torture_dgex_algo(void **state) +{ + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + const char *kex_list = KEX_DH_GEX_SHA1 "," KEX_DH_GEX_SHA256; + int rc, cmp; + const char *negotiated_kex = NULL; + bool found; + char *temp_list = NULL; + char *token = NULL; + rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex_list); + assert_ssh_return_code(session, rc); + + rc = ssh_connect(session); + assert_ssh_return_code(session, rc); + + negotiated_kex = ssh_get_kex_algo(session); + assert_non_null(negotiated_kex); + + found = false; + temp_list = strdup(kex_list); + + for (token = strtok(temp_list, ","); token != NULL; + token = strtok(NULL, ",")) { + cmp = strcmp(token, negotiated_kex); + if (cmp == 0) { + found = true; + break; + } + } + + free(temp_list); + assert_true(found); +} +#endif /* WITH_GEX */ + +int torture_run_tests(void) +{ + int rc; + struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(torture_kex_basic_functionality, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_kex_algo_preference, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_kex_algo_negotiation, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_kex_algo_before_connect, + session_setup, + session_teardown), +#ifdef WITH_GEX + cmocka_unit_test_setup_teardown(torture_dgex_algo, + session_setup, + session_teardown), +#endif /* WITH_GEX */ + }; + + ssh_init(); + + torture_filter_tests(tests); + rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown); + + ssh_finalize(); + return rc; +}