From 29dd7874cda3a54b922d0fea8089c75964d7d216 Mon Sep 17 00:00:00 2001 From: Eshan Kelkar Date: Sun, 17 Aug 2025 23:34:07 +0530 Subject: [PATCH] sftp_aio: Test sftp aio with libssh proxyjump Signed-off-by: Eshan Kelkar Reviewed-by: Jakub Jelen Reviewed-by: Andreas Schneider --- tests/client/torture_sftp_aio.c | 87 +++++++++++--------- tests/torture.c | 137 ++++++++++++++++++++++++++++++++ tests/torture.h | 2 + 3 files changed, 189 insertions(+), 37 deletions(-) diff --git a/tests/client/torture_sftp_aio.c b/tests/client/torture_sftp_aio.c index 7c598b18..9bd5ff4e 100644 --- a/tests/client/torture_sftp_aio.c +++ b/tests/client/torture_sftp_aio.c @@ -11,19 +11,37 @@ #define MAX_XFER_BUF_SIZE 16384 +#define DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(TEST_NAME) \ + { \ + #TEST_NAME, \ + TEST_NAME, \ + session_setup, \ + session_teardown, \ + NULL \ + }, \ + { \ + #TEST_NAME"_proxyjump", \ + TEST_NAME, \ + session_proxyjump_setup, \ + session_teardown, \ + NULL \ + } + static int sshd_setup(void **state) { torture_setup_sshd_server(state, false); + torture_setup_sshd_servers(state, false); return 0; } static int sshd_teardown(void **state) { + /* this will take care of the server1 teardown too */ torture_teardown_sshd_server(state); return 0; } -static int session_setup(void **state) +static int session_setup_helper(void **state, bool with_proxyjump) { struct torture_state *s = *state; struct passwd *pwd = NULL; @@ -35,11 +53,15 @@ static int session_setup(void **state) rc = setuid(pwd->pw_uid); assert_return_code(rc, errno); - s->ssh.session = torture_ssh_session(s, - TORTURE_SSH_SERVER, - NULL, - TORTURE_SSH_USER_ALICE, - NULL); + if (with_proxyjump) { + s->ssh.session = torture_ssh_session_proxyjump(); + } else { + s->ssh.session = torture_ssh_session(s, + TORTURE_SSH_SERVER, + NULL, + TORTURE_SSH_USER_ALICE, + NULL); + } assert_non_null(s->ssh.session); s->ssh.tsftp = torture_sftp_session(s->ssh.session); @@ -48,6 +70,16 @@ static int session_setup(void **state) return 0; } +static int session_setup(void **state) +{ + return session_setup_helper(state, false); +} + +static int session_proxyjump_setup(void **state) +{ + return session_setup_helper(state, true); +} + static int session_teardown(void **state) { struct torture_state *s = *state; @@ -722,37 +754,18 @@ int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(torture_sftp_aio_read_file, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_read_more_than_cap, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_write_file, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_write_more_than_cap, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_read_negative, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_write_negative, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_read_unordered_wait, - session_setup, - session_teardown), - - cmocka_unit_test_setup_teardown(torture_sftp_aio_write_unordered_wait, - session_setup, - session_teardown), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_file), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN( + torture_sftp_aio_read_more_than_cap), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_file), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN( + torture_sftp_aio_write_more_than_cap), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_negative), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_negative), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN( + torture_sftp_aio_read_unordered_wait), + DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN( + torture_sftp_aio_write_unordered_wait), }; ssh_init(); diff --git a/tests/torture.c b/tests/torture.c index 7b72e843..0c5a218f 100644 --- a/tests/torture.c +++ b/tests/torture.c @@ -389,6 +389,143 @@ failed: return NULL; } +/* always return verification successful */ +static int verify_knownhost_trust_all(UNUSED_PARAM(ssh_session jump_session), + UNUSED_PARAM(void *user)) +{ + return SSH_OK; +} + +/** + * @brief Create a session connected to server via proxyjump + * + * @param[in] state A pointer to a pointer to an initialized torture_state + * structure + * + * @warning It is expected that both sshd servers are setup before calling + * this, see torture_setup_sshd_server() and + * torture_setup_sshd_servers() + * + * TODO: If needed, in future, we can extend this function to: + * - allow caller to pass server host port, user, password similar to + * torture_ssh_session() or club this with that function + * + * - allow caller to customize jump hosts and callbacks for each of them + */ +ssh_session torture_ssh_session_proxyjump(void) +{ + /* + * We'll setup the connection chain: + * - client + * - jump host 1: doe (sshd server, IPV4) + * - jump host 2: alice (sshd server1, IPV6) + * - server: alice (sshd server, IPV4) + */ + char jump_host_list[1024] = {0}; + int jump_host_count = 2; + const char *jump_host_1_address = torture_server_address(AF_INET); + const char *jump_host_2_address = torture_server1_address(AF_INET6); + struct ssh_jump_callbacks_struct jump_host_callbacks = { + .before_connection = NULL, + .verify_knownhost = verify_knownhost_trust_all, + .authenticate = NULL, + }; + + ssh_session session = NULL; + bool process_config = false; + int rc, i; + + session = ssh_new(); + if (session == NULL) { + fprintf(stderr, "Failed to create new ssh session\n"); + goto failed; + } + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); + if (rc < 0) { + fprintf(stderr, + "Failed to set session host: %s\n", + ssh_get_error(session)); + goto failed; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE); + if (rc < 0) { + fprintf(stderr, + "Failed to set session user: %s\n", + ssh_get_error(session)); + goto failed; + } + + rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + if (rc < 0) { + fprintf(stderr, + "Failed to set process config option: %s\n", + ssh_get_error(session)); + goto failed; + } + + rc = snprintf(jump_host_list, + sizeof(jump_host_list), + "doe@%s:22,alice@%s:22", + jump_host_1_address, + jump_host_2_address); + if (rc < 0) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + goto failed; + } + + if (rc >= (int)sizeof(jump_host_list)) { + fprintf(stderr, "Insufficient jump host list buffer size\n"); + goto failed; + } + + rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP, jump_host_list); + if (rc < 0) { + fprintf(stderr, + "Failed to set jump hosts for the session: %s\n", + ssh_get_error(session)); + goto failed; + } + + for (i = 0; i < jump_host_count; ++i) { + rc = ssh_options_set(session, + SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, + &jump_host_callbacks); + if (rc < 0) { + fprintf(stderr, + "Failed to set jump callbacks for jump host %d: %s\n", + i + 1, + ssh_get_error(session)); + goto failed; + } + } + + rc = ssh_connect(session); + if (rc != SSH_OK) { + fprintf(stderr, + "Failed to connect to ssh server: %s\n", + ssh_get_error(session)); + goto failed; + } + + rc = ssh_userauth_publickey_auto(session, NULL, NULL); + if (rc != SSH_AUTH_SUCCESS) { + fprintf(stderr, "Public key authentication did not succeed\n"); + goto failed; + } + + return session; + +failed: + if (ssh_is_connected(session)) { + ssh_disconnect(session); + } + ssh_free(session); + + return NULL; +} + #ifdef WITH_SERVER ssh_bind torture_ssh_bind(const char *addr, diff --git a/tests/torture.h b/tests/torture.h index 7bedd975..0120bd6f 100644 --- a/tests/torture.h +++ b/tests/torture.h @@ -115,6 +115,8 @@ ssh_session torture_ssh_session(struct torture_state *s, const char *user, const char *password); +ssh_session torture_ssh_session_proxyjump(void); + ssh_bind torture_ssh_bind(const char *addr, const unsigned int port, enum ssh_keytypes_e key_type,