diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 68cbf1bf..3ee0a380 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -259,6 +259,20 @@ else() set(PUTTYGEN_EXECUTABLE "/bin/puttygen-not-found") endif() +find_program(TINYSSHD_EXECUTABLE + NAMES + tinysshd + PATHS + /sbin + /usr/sbin + /usr/local/sbin) + +if (TINYSSHD_EXECUTABLE) + message(STATUS "Found TinySSH server: ${TINYSSHD_EXECUTABLE}") +else() + message(STATUS "Could NOT find TinySSH server") +endif() + find_program(SSHD_EXECUTABLE NAME sshd @@ -391,6 +405,14 @@ if (CLIENT_TESTING OR SERVER_TESTING) file(COPY keys/id_ed25519_sk DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE) file(COPY keys/id_ed25519_sk.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE) + # TINYSSH Key Setup + set(TINYSSH_KEY_DIR "${CMAKE_CURRENT_BINARY_DIR}/etc/tinyssh") + file(MAKE_DIRECTORY ${TINYSSH_KEY_DIR}) + + # Copy the TINYSSH hostkeys + file(COPY keys/ed25519.pk DESTINATION ${TINYSSH_KEY_DIR}) + file(COPY keys/.ed25519.sk DESTINATION ${TINYSSH_KEY_DIR}) + # Allow to auth with bob's public keys on alice and doe account configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/alice/.ssh/authorized_keys @ONLY) configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/authorized_keys @ONLY) diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt index 52e8d8a5..c0560666 100644 --- a/tests/client/CMakeLists.txt +++ b/tests/client/CMakeLists.txt @@ -36,6 +36,10 @@ if (WITH_PKCS11_URI) torture_auth_pkcs11) endif() +if (TINYSSHD_EXECUTABLE AND NCAT_EXECUTABLE) + set(LIBSSH_CLIENT_TESTS ${LIBSSH_CLIENT_TESTS} torture_tinyssh) +endif() + if (HAVE_PTHREAD) set(LIBSSH_CLIENT_TESTS ${LIBSSH_CLIENT_TESTS} @@ -92,6 +96,12 @@ foreach(_CLI_TEST ${LIBSSH_CLIENT_TESTS}) LINK_LIBRARIES ${TORTURE_LIBRARY} util ) + if (_CLI_TEST STREQUAL "torture_tinyssh") + target_compile_definitions(${_CLI_TEST} PRIVATE + TINYSSH_KEYS_DIR="${TINYSSH_KEY_DIR}" + ) + endif() + if (OSX) set_property( TEST diff --git a/tests/client/torture_tinyssh.c b/tests/client/torture_tinyssh.c new file mode 100644 index 00000000..07b6832c --- /dev/null +++ b/tests/client/torture_tinyssh.c @@ -0,0 +1,328 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2026 by Your Bulitha Kawushika De Zoysa + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "tests_config.h" + +#define LIBSSH_STATIC + +#include "libssh/libssh.h" +#include "libssh/priv.h" +#include "libssh/session.h" +#include "torture.h" + +#include +#include +#include +#include +#include +#include + +#define TINYSSH_PIDFILE "tinyssh.pid" +#define TINYSSH_PORT 22 + +/* TINYSSH Server Setup and Teardown */ + +static int tinyssh_setup(void **state) +{ + struct torture_state *s = NULL; + char cmd[4096]; + char pid_path[1024]; + int rc; + + torture_setup_socket_dir(state); + s = *state; + + snprintf(pid_path, + sizeof(pid_path), + "%s/%s", + s->socket_dir, + TINYSSH_PIDFILE); + free(s->srv_pidfile); + s->srv_pidfile = strdup(pid_path); + if (s->srv_pidfile == NULL) { + return -1; + } + + snprintf(cmd, + sizeof(cmd), + "%s -l %s %d -k -c \"%s %s -v %s\" " + "> %s/tinyssh.log 2>&1 & echo $! > %s", + NCAT_EXECUTABLE, + TORTURE_SSH_SERVER, + TINYSSH_PORT, + TINYSSHD_EXECUTABLE, + "", + TINYSSH_KEYS_DIR, + s->socket_dir, + s->srv_pidfile); + + SSH_LOG(SSH_LOG_DEBUG, "Executing: %s\n", cmd); + + rc = system(cmd); + if (rc != 0) { + return -1; + } + + rc = torture_wait_for_daemon(15); + if (rc != 0) { + return -1; + } + + return 0; +} + +static int tinyssh_teardown(void **state) +{ + struct torture_state *s = *state; + torture_terminate_process(s->srv_pidfile); + torture_teardown_socket_dir(state); + return 0; +} + +/* LIBSSH Client Setup and Teardown */ + +static int session_setup(void **state) +{ + struct torture_state *s = *state; + int verbosity = torture_libssh_verbosity(); + bool process_config = false; + int port = TINYSSH_PORT; + struct passwd *pwd = NULL; + 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); + + ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); + ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT, &port); + ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, "bob"); + ssh_options_set(s->ssh.session, + SSH_OPTIONS_PROCESS_CONFIG, + &process_config); + + return 0; +} + +static int session_teardown(void **state) +{ + struct torture_state *s = *state; + if (s->ssh.session) { + ssh_disconnect(s->ssh.session); + ssh_free(s->ssh.session); + } + return 0; +} + +/* Algorithms Helper */ + +static void test_specific_algorithm(ssh_session session, + const char *kex, + const char *cipher, + const char *hostkey, + int expected_rc) +{ + int rc; + char data[256]; + size_t len_to_test[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15, + 16, 20, 31, 32, 33, 63, 64, 65, 100, 127, 128}; + unsigned int i; + + /* Set Key Exchange */ + if (kex != NULL) { + rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex); + assert_ssh_return_code(session, rc); + } + + /* Set Ciphers */ + if (cipher != NULL) { + rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher); + assert_ssh_return_code(session, rc); + rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher); + assert_ssh_return_code(session, rc); + } + + /* Set Hostkey */ + if (hostkey != NULL) { + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, hostkey); + assert_ssh_return_code(session, rc); + } + + rc = ssh_connect(session); + + if (expected_rc == SSH_OK) { + assert_ssh_return_code(session, rc); + + if (cipher != NULL) { + const char *used_cipher = ssh_get_cipher_out(session); + assert_non_null(used_cipher); + assert_string_equal(used_cipher, cipher); + } + + if (hostkey != NULL) { + ssh_key pubkey = NULL; + const char *type_str = NULL; + + rc = ssh_get_server_publickey(session, &pubkey); + assert_int_equal(rc, SSH_OK); + assert_non_null(pubkey); + + type_str = ssh_key_type_to_char(ssh_key_type(pubkey)); + assert_non_null(type_str); + assert_string_equal(type_str, hostkey); + ssh_key_free(pubkey); + } + + memset(data, 0, sizeof(data)); + for (i = 0; i < (sizeof(len_to_test) / sizeof(size_t)); i++) { + memset(data, 'A', len_to_test[i]); + ssh_send_ignore(session, data); + ssh_handle_packets(session, 50); + } + + rc = ssh_userauth_none(session, NULL); + if (rc != SSH_OK) { + rc = ssh_get_error_code(session); + assert_int_equal(rc, SSH_REQUEST_DENIED); + } + + } else { + assert_int_not_equal(rc, SSH_OK); + } +} + +/* Test Cases */ + +static void torture_tinyssh_curve25519(void **state) +{ + struct torture_state *s = *state; + test_specific_algorithm(s->ssh.session, + "curve25519-sha256", + NULL, + NULL, + SSH_OK); +} + +static void torture_tinyssh_curve25519_libssh(void **state) +{ + struct torture_state *s = *state; + test_specific_algorithm(s->ssh.session, + "curve25519-sha256@libssh.org", + NULL, + NULL, + SSH_OK); +} + +static void torture_tinyssh_sntrup761(void **state) +{ + struct torture_state *s = *state; + test_specific_algorithm(s->ssh.session, + "sntrup761x25519-sha512@openssh.com", + NULL, + NULL, + SSH_OK); +} + +static void torture_tinyssh_chacha20(void **state) +{ + struct torture_state *s = *state; + test_specific_algorithm(s->ssh.session, + NULL, + "chacha20-poly1305@openssh.com", + NULL, + SSH_OK); +} + +static void torture_tinyssh_neg_cipher(void **state) +{ + struct torture_state *s = *state; + + /* TinySSH does not support older ciphers like aes128-cbc.*/ + + test_specific_algorithm(s->ssh.session, + NULL, + "aes128-cbc", + NULL, + SSH_ERROR); +} + +static void torture_tinyssh_hostkey_ed25519(void **state) +{ + struct torture_state *s = *state; + test_specific_algorithm(s->ssh.session, NULL, NULL, "ssh-ed25519", SSH_OK); +} + +static void torture_tinyssh_neg_kex(void **state) +{ + struct torture_state *s = *state; + + /* TinySSH does not support legacy Diffie-Hellman groups or NIST curves.*/ + + test_specific_algorithm(s->ssh.session, + "diffie-hellman-group1-sha1", + NULL, + NULL, + SSH_ERROR); +} + +int torture_run_tests(void) +{ + int rc; + + struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(torture_tinyssh_curve25519, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_tinyssh_curve25519_libssh, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_tinyssh_sntrup761, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_tinyssh_hostkey_ed25519, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_tinyssh_chacha20, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_tinyssh_neg_cipher, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_tinyssh_neg_kex, + session_setup, + session_teardown), + }; + + ssh_init(); + + torture_filter_tests(tests); + rc = cmocka_run_group_tests(tests, tinyssh_setup, tinyssh_teardown); + + ssh_finalize(); + return rc; +} \ No newline at end of file diff --git a/tests/keys/.ed25519.sk b/tests/keys/.ed25519.sk new file mode 100644 index 00000000..04239009 Binary files /dev/null and b/tests/keys/.ed25519.sk differ diff --git a/tests/keys/ed25519.pk b/tests/keys/ed25519.pk new file mode 100644 index 00000000..3d8fbc5c --- /dev/null +++ b/tests/keys/ed25519.pk @@ -0,0 +1 @@ +CKݛ1'j &e \ No newline at end of file diff --git a/tests/tests_config.h.cmake b/tests/tests_config.h.cmake index f448df63..445b0e5e 100644 --- a/tests/tests_config.h.cmake +++ b/tests/tests_config.h.cmake @@ -86,3 +86,7 @@ #cmakedefine PKCS11SPY "${PKCS11SPY}" #cmakedefine HAVE_SK_DUMMY 1 #cmakedefine SK_DUMMY_LIBRARY_PATH "${SK_DUMMY_LIBRARY_PATH}" + +/* TinySSH Executable */ + +#cmakedefine TINYSSHD_EXECUTABLE "${TINYSSHD_EXECUTABLE}" \ No newline at end of file