diff --color -ru ../libssh-0.9.6/examples/sshnetcat.c ./examples/sshnetcat.c --- ../libssh-0.9.6/examples/sshnetcat.c 2021-08-26 14:27:42.000000000 +0200 +++ ./examples/sshnetcat.c 2023-05-02 10:36:00.793381735 +0200 @@ -233,9 +233,10 @@ } void cleanup_pcap(void); -void cleanup_pcap(){ +void cleanup_pcap(void) +{ ssh_pcap_file_free(pcap); - pcap=NULL; + pcap = NULL; } #endif diff --color -ru ../libssh-0.9.6/src/init.c ./src/init.c --- ../libssh-0.9.6/src/init.c 2021-03-15 08:11:33.000000000 +0100 +++ ./src/init.c 2023-05-02 10:36:00.793381735 +0200 @@ -269,7 +269,7 @@ * * @see ssh_init() */ -bool is_ssh_initialized() { +bool is_ssh_initialized(void) { bool is_initialized = false; diff --color -ru ../libssh-0.9.6/tests/client/torture_auth.c ./tests/client/torture_auth.c --- ../libssh-0.9.6/tests/client/torture_auth.c 2021-08-26 14:27:42.000000000 +0200 +++ ./tests/client/torture_auth.c 2023-05-02 10:36:00.815381960 +0200 @@ -200,7 +200,8 @@ assert_non_null(ssh_agent_pidfile); /* kill agent pid */ - torture_terminate_process(ssh_agent_pidfile); + rc = torture_terminate_process(ssh_agent_pidfile); + assert_return_code(rc, errno); unlink(ssh_agent_pidfile); @@ -551,6 +552,7 @@ static void torture_auth_agent_cert(void **state) { +#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0) struct torture_state *s = *state; ssh_session session = s->ssh.session; int rc; @@ -570,6 +572,7 @@ "ssh-rsa-cert-v01@openssh.com"); assert_int_equal(rc, SSH_OK); } +#endif /* OPENSSH_VERSION_MAJOR < 8.1 */ /* Setup loads a different key, tests are exactly the same. */ torture_auth_agent(state); @@ -577,6 +580,7 @@ static void torture_auth_agent_cert_nonblocking(void **state) { +#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0) struct torture_state *s = *state; ssh_session session = s->ssh.session; int rc; @@ -596,6 +600,7 @@ "ssh-rsa-cert-v01@openssh.com"); assert_int_equal(rc, SSH_OK); } +#endif /* OPENSSH_VERSION_MAJOR < 8.1 */ torture_auth_agent_nonblocking(state); } diff --color -ru ../libssh-0.9.6/tests/client/torture_rekey.c ./tests/client/torture_rekey.c --- ../libssh-0.9.6/tests/client/torture_rekey.c 2023-04-28 17:26:41.472315318 +0200 +++ ./tests/client/torture_rekey.c 2023-05-02 10:36:00.805381857 +0200 @@ -38,6 +38,8 @@ #include #include +#define KEX_RETRY 32 + static uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */ static int sshd_setup(void **state) @@ -190,10 +192,11 @@ rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL); assert_int_equal(rc, SSH_AUTH_SUCCESS); - /* send ignore packets of up to 1KB to trigger rekey */ + /* send ignore packets of up to 1KB to trigger rekey. Send little bit more + * to make sure it completes with all different ciphers */ memset(data, 0, sizeof(data)); memset(data, 'A', 128); - for (i = 0; i < 16; i++) { + for (i = 0; i < KEX_RETRY; i++) { ssh_send_ignore(s->ssh.session, data); ssh_handle_packets(s->ssh.session, 50); } @@ -496,9 +499,15 @@ * to make sure the rekey it completes with all different ciphers (paddings */ memset(data, 0, sizeof(data)); memset(data, 'A', 128); - for (i = 0; i < 20; i++) { + for (i = 0; i < KEX_RETRY; i++) { ssh_send_ignore(s->ssh.session, data); - ssh_handle_packets(s->ssh.session, 50); + ssh_handle_packets(s->ssh.session, 100); + + c = s->ssh.session->current_crypto; + /* SHA256 len */ + if (c->digest_len != 32) { + break; + } } /* The rekey limit was restored in the new crypto to the same value */ @@ -568,9 +577,15 @@ * to make sure the rekey it completes with all different ciphers (paddings */ memset(data, 0, sizeof(data)); memset(data, 'A', 128); - for (i = 0; i < 25; i++) { + for (i = 0; i < KEX_RETRY; i++) { ssh_send_ignore(s->ssh.session, data); - ssh_handle_packets(s->ssh.session, 50); + ssh_handle_packets(s->ssh.session, 100); + + c = s->ssh.session->current_crypto; + /* SHA256 len */ + if (c->digest_len != 32) { + break; + } } /* Check that the secret hash is different than initially */ diff --color -ru ../libssh-0.9.6/tests/CMakeLists.txt ./tests/CMakeLists.txt --- ../libssh-0.9.6/tests/CMakeLists.txt 2021-08-26 14:27:42.000000000 +0200 +++ ./tests/CMakeLists.txt 2023-05-02 10:32:03.964511860 +0200 @@ -153,6 +153,17 @@ execute_process(COMMAND ${ID_EXECUTABLE} -u OUTPUT_VARIABLE LOCAL_UID OUTPUT_STRIP_TRAILING_WHITESPACE) endif() + find_program(TIMEOUT_EXECUTABLE + NAME + timeout + PATHS + /bin + /usr/bin + /usr/local/bin) + if (TIMEOUT_EXECUTABLE) + set(WITH_TIMEOUT "1") + endif() + # chroot_wrapper add_library(chroot_wrapper SHARED chroot_wrapper.c) set(CHROOT_WRAPPER_LIBRARY ${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}chroot_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX}) diff --color -ru ../libssh-0.9.6/tests/pkd/pkd_keyutil.c ./tests/pkd/pkd_keyutil.c --- ../libssh-0.9.6/tests/pkd/pkd_keyutil.c 2021-03-15 08:11:33.000000000 +0100 +++ ./tests/pkd/pkd_keyutil.c 2023-05-02 10:36:00.793381735 +0200 @@ -22,7 +22,7 @@ #include "pkd_keyutil.h" #include "pkd_util.h" -void setup_rsa_key() { +void setup_rsa_key(void) { int rc = 0; if (access(LIBSSH_RSA_TESTKEY, F_OK) != 0) { rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f " @@ -31,7 +31,7 @@ assert_int_equal(rc, 0); } -void setup_ed25519_key() { +void setup_ed25519_key(void) { int rc = 0; if (access(LIBSSH_ED25519_TESTKEY, F_OK) != 0) { rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f " @@ -41,7 +41,7 @@ } #ifdef HAVE_DSA -void setup_dsa_key() { +void setup_dsa_key(void) { int rc = 0; if (access(LIBSSH_DSA_TESTKEY, F_OK) != 0) { rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f " @@ -51,7 +51,7 @@ } #endif -void setup_ecdsa_keys() { +void setup_ecdsa_keys(void) { int rc = 0; if (access(LIBSSH_ECDSA_256_TESTKEY, F_OK) != 0) { @@ -71,27 +71,27 @@ } } -void cleanup_rsa_key() { +void cleanup_rsa_key(void) { cleanup_key(LIBSSH_RSA_TESTKEY); } -void cleanup_ed25519_key() { +void cleanup_ed25519_key(void) { cleanup_key(LIBSSH_ED25519_TESTKEY); } #ifdef HAVE_DSA -void cleanup_dsa_key() { +void cleanup_dsa_key(void) { cleanup_key(LIBSSH_DSA_TESTKEY); } #endif -void cleanup_ecdsa_keys() { +void cleanup_ecdsa_keys(void) { cleanup_key(LIBSSH_ECDSA_256_TESTKEY); cleanup_key(LIBSSH_ECDSA_384_TESTKEY); cleanup_key(LIBSSH_ECDSA_521_TESTKEY); } -void setup_openssh_client_keys() { +void setup_openssh_client_keys(void) { int rc = 0; if (access(OPENSSH_CA_TESTKEY, F_OK) != 0) { @@ -184,7 +184,7 @@ } } -void cleanup_openssh_client_keys() { +void cleanup_openssh_client_keys(void) { cleanup_key(OPENSSH_CA_TESTKEY); cleanup_key(OPENSSH_RSA_TESTKEY); cleanup_file(OPENSSH_RSA_TESTKEY "-sha256-cert.pub"); @@ -199,7 +199,7 @@ } } -void setup_dropbear_client_rsa_key() { +void setup_dropbear_client_rsa_key(void) { int rc = 0; if (access(DROPBEAR_RSA_TESTKEY, F_OK) != 0) { rc = system_checked(DROPBEAR_KEYGEN " -t rsa -f " @@ -208,6 +208,6 @@ assert_int_equal(rc, 0); } -void cleanup_dropbear_client_rsa_key() { +void cleanup_dropbear_client_rsa_key(void) { unlink(DROPBEAR_RSA_TESTKEY); } diff --color -ru ../libssh-0.9.6/tests/server/torture_server_config.c ./tests/server/torture_server_config.c --- ../libssh-0.9.6/tests/server/torture_server_config.c 2021-08-26 14:27:42.000000000 +0200 +++ ./tests/server/torture_server_config.c 2023-05-02 10:36:00.815381960 +0200 @@ -285,9 +285,7 @@ assert_non_null(s); rc = torture_terminate_process(s->srv_pidfile); - if (rc != 0) { - fprintf(stderr, "XXXXXX Failed to terminate sshd\n"); - } + assert_return_code(rc, errno); unlink(s->srv_pidfile); @@ -513,6 +511,12 @@ /* Try each algorithm individually */ j = 0; while(tokens->tokens[j] != NULL) { + char *cmp = strstr(OPENSSH_CIPHERS, tokens->tokens[j]); + if (cmp == NULL) { + /* This cipher is not supported by the OpenSSH. Skip it */ + j++; + continue; + } snprintf(config_content, sizeof(config_content), "HostKey %s\nCiphers %s\n", diff --color -ru ../libssh-0.9.6/tests/tests_config.h.cmake ./tests/tests_config.h.cmake --- ../libssh-0.9.6/tests/tests_config.h.cmake 2021-08-26 14:27:42.000000000 +0200 +++ ./tests/tests_config.h.cmake 2023-05-02 10:32:03.964511860 +0200 @@ -66,4 +66,6 @@ #cmakedefine NC_EXECUTABLE "${NC_EXECUTABLE}" #cmakedefine SSHD_EXECUTABLE "${SSHD_EXECUTABLE}" -#cmakedefine SSH_EXECUTABLE "${SSH_EXECUTABLE}" \ No newline at end of file +#cmakedefine SSH_EXECUTABLE "${SSH_EXECUTABLE}" +#cmakedefine WITH_TIMEOUT ${WITH_TIMEOUT} +#cmakedefine TIMEOUT_EXECUTABLE "${TIMEOUT_EXECUTABLE}" diff --color -ru ../libssh-0.9.6/tests/torture.c ./tests/torture.c --- ../libssh-0.9.6/tests/torture.c 2021-08-26 14:27:44.000000000 +0200 +++ ./tests/torture.c 2023-05-02 10:36:00.815381960 +0200 @@ -51,6 +51,7 @@ #include "torture.h" #include "torture_key.h" #include "libssh/misc.h" +#include "libssh/token.h" #define TORTURE_SSHD_SRV_IPV4 "127.0.0.10" /* socket wrapper IPv6 prefix fd00::5357:5fxx */ @@ -250,8 +251,12 @@ rc = kill(pid, 0); if (rc != 0) { - is_running = 0; - break; + /* Process not found */ + if (errno == ESRCH) { + is_running = 0; + rc = 0; + break; + } } } @@ -260,7 +265,7 @@ "WARNING: The process with pid %u is still running!\n", pid); } - return 0; + return rc; } ssh_session torture_ssh_session(struct torture_state *s, @@ -611,6 +616,112 @@ *state = s; } +/** + * @brief Create a libssh server configuration file + * + * It is expected the socket directory to be already created before by calling + * torture_setup_socket_dir(). The created configuration file will be stored in + * the socket directory and the srv_config pointer in the state will be + * initialized. + * + * @param[in] state A pointer to a pointer to an initialized torture_state + * structure + */ +void torture_setup_create_libssh_config(void **state) +{ + struct torture_state *s = *state; + char ed25519_hostkey[1024] = {0}; +#ifdef HAVE_DSA + char dsa_hostkey[1024]; +#endif /* HAVE_DSA */ + char rsa_hostkey[1024]; + char ecdsa_hostkey[1024]; + char sshd_config[2048]; + char sshd_path[1024]; + const char *additional_config = NULL; + struct stat sb; + const char config_string[]= + "LogLevel DEBUG3\n" + "Port 22\n" + "ListenAddress 127.0.0.10\n" + "%s %s\n" + "%s %s\n" + "%s %s\n" +#ifdef HAVE_DSA + "%s %s\n" +#endif /* HAVE_DSA */ + "%s\n"; /* The space for test-specific options */ + bool written = false; + int rc; + + assert_non_null(s->socket_dir); + + snprintf(sshd_path, + sizeof(sshd_path), + "%s/sshd", + s->socket_dir); + + rc = lstat(sshd_path, &sb); + if (rc == 0 ) { /* The directory is already in place */ + written = true; + } + + if (!written) { + rc = mkdir(sshd_path, 0755); + assert_return_code(rc, errno); + } + + snprintf(ed25519_hostkey, + sizeof(ed25519_hostkey), + "%s/sshd/ssh_host_ed25519_key", + s->socket_dir); + + snprintf(rsa_hostkey, + sizeof(rsa_hostkey), + "%s/sshd/ssh_host_rsa_key", + s->socket_dir); + + snprintf(ecdsa_hostkey, + sizeof(ecdsa_hostkey), + "%s/sshd/ssh_host_ecdsa_key", + s->socket_dir); + +#ifdef HAVE_DSA + snprintf(dsa_hostkey, + sizeof(dsa_hostkey), + "%s/sshd/ssh_host_dsa_key", + s->socket_dir); +#endif /* HAVE_DSA */ + + if (!written) { + torture_write_file(ed25519_hostkey, + torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0)); + torture_write_file(rsa_hostkey, + torture_get_testkey(SSH_KEYTYPE_RSA, 0)); + torture_write_file(ecdsa_hostkey, + torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0)); +#ifdef HAVE_DSA + torture_write_file(dsa_hostkey, + torture_get_testkey(SSH_KEYTYPE_DSS, 0)); +#endif /* HAVE_DSA */ + } + + additional_config = (s->srv_additional_config != NULL ? + s->srv_additional_config : ""); + + snprintf(sshd_config, sizeof(sshd_config), + config_string, + "HostKey", ed25519_hostkey, + "HostKey", rsa_hostkey, + "HostKey", ecdsa_hostkey, +#ifdef HAVE_DSA + "HostKey", dsa_hostkey, +#endif /* HAVE_DSA */ + additional_config); + + torture_write_file(s->srv_config, sshd_config); +} + static void torture_setup_create_sshd_config(void **state, bool pam) { struct torture_state *s = *state; @@ -856,21 +967,140 @@ return 1; } -void torture_setup_sshd_server(void **state, bool pam) +/** + * @brief Run a libssh based server under timeout. + * + * It is expected that the socket directory and libssh configuration file were + * already created before by calling torture_setup_socket_dir() and + * torture_setup_create_libssh_config() (or alternatively setup the state with + * the correct values). + * + * @param[in] state The content of the address pointed by this variable must be + * a pointer to an initialized instance of torture_state + * structure; it can be obtained by calling + * torture_setup_socket_dir() and + * torture_setup_create_libssh_config(). + * @param[in] server_path The path to the server executable. + * + * @note This function will use the state->srv_additional_config field as + * additional command line option used when starting the server instead of extra + * configuration file options. + * */ +void torture_setup_libssh_server(void **state, const char *server_path) { struct torture_state *s; - char sshd_start_cmd[1024]; + char start_cmd[1024]; + char timeout_cmd[512]; + char env[1024]; + char extra_options[1024]; int rc; + char *ld_preload = NULL; + const char *force_fips = NULL; - torture_setup_socket_dir(state); - torture_setup_create_sshd_config(state, pam); + struct ssh_tokens_st *env_tokens; + struct ssh_tokens_st *arg_tokens; + + pid_t pid; + ssize_t printed; + + s = *state; + + /* Get all the wrapper libraries to be pre-loaded */ + ld_preload = getenv("LD_PRELOAD"); + + if (s->srv_additional_config != NULL) { + printed = snprintf(extra_options, sizeof(extra_options), " %s ", + s->srv_additional_config); + if (printed < 0) { + fail_msg("Failed to print additional config!"); + } + } else { + printed = snprintf(extra_options, sizeof(extra_options), " "); + if (printed < 0) { + fail_msg("Failed to print empty additional config!"); + } + } + + if (ssh_fips_mode()) { + force_fips = "OPENSSL_FORCE_FIPS_MODE=1 "; + } else { + force_fips = ""; + } + + /* Write the environment setting */ + printed = snprintf(env, sizeof(env), + "SOCKET_WRAPPER_DIR=%s " + "SOCKET_WRAPPER_DEFAULT_IFACE=10 " + "LD_PRELOAD=%s " + "%s", + s->socket_dir, ld_preload, force_fips); + if (printed < 0) { + fail_msg("Failed to print env!"); + } + +#ifdef WITH_TIMEOUT + snprintf(timeout_cmd, sizeof(timeout_cmd), + "%s %s ", TIMEOUT_EXECUTABLE, "5m"); +#else + timeout_cmd[0] = '\0'; +#endif + + /* Write the start command */ + printed = snprintf(start_cmd, sizeof(start_cmd), + "%s" + "%s -f%s -v4 -p22 -i%s -C%s%s%s", + timeout_cmd, + server_path, s->pcap_file, s->srv_pidfile, + s->srv_config, extra_options, TORTURE_SSH_SERVER); + if (printed < 0) { + fail_msg("Failed to print start command!"); + } + + pid = fork(); + switch(pid) { + case 0: + env_tokens = ssh_tokenize(env, ' '); + if (env_tokens == NULL || env_tokens->tokens == NULL) { + fail_msg("Failed to tokenize env!"); + } + + arg_tokens = ssh_tokenize(start_cmd, ' '); + if (arg_tokens == NULL || arg_tokens->tokens == NULL) { + ssh_tokens_free(env_tokens); + fail_msg("Failed to tokenize args!"); + } + + rc = execve(arg_tokens->tokens[0], (char **)arg_tokens->tokens, + (char **)env_tokens->tokens); + + /* execve returns only in case of error */ + ssh_tokens_free(env_tokens); + ssh_tokens_free(arg_tokens); + fail_msg("Error in execve: %s", strerror(errno)); + case -1: + fail_msg("Failed to fork!"); + default: + /* The parent continues the execution of the tests */ + setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1); + unsetenv("PAM_WRAPPER"); + + /* Wait until the server is ready to accept connections */ + rc = torture_wait_for_daemon(15); + assert_int_equal(rc, 0); + break; + } +} + +static int torture_start_sshd_server(void **state) +{ + struct torture_state *s = *state; + char sshd_start_cmd[1024]; + int rc; /* Set the default interface for the server */ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1); setenv("PAM_WRAPPER", "1", 1); - s = *state; - snprintf(sshd_start_cmd, sizeof(sshd_start_cmd), SSHD_EXECUTABLE " -r -f %s -E %s/sshd/daemon.log 2> %s/sshd/cwrap.log", s->srv_config, s->socket_dir, s->socket_dir); @@ -882,7 +1112,20 @@ unsetenv("PAM_WRAPPER"); /* Wait until the sshd is ready to accept connections */ - rc = torture_wait_for_daemon(5); + rc = torture_wait_for_daemon(15); + assert_int_equal(rc, 0); + + return SSH_OK; +} + +void torture_setup_sshd_server(void **state, bool pam) +{ + int rc; + + torture_setup_socket_dir(state); + torture_setup_create_sshd_config(state, pam); + + rc = torture_start_sshd_server(state); assert_int_equal(rc, 0); } @@ -922,29 +1165,12 @@ torture_reload_sshd_server(void **state) { struct torture_state *s = *state; - pid_t pid; int rc; - /* read the pidfile */ - pid = torture_read_pidfile(s->srv_pidfile); - assert_int_not_equal(pid, -1); - - kill(pid, SIGHUP); - - /* 10 ms */ - usleep(10 * 1000); - - rc = kill(pid, 0); - if (rc != 0) { - fprintf(stderr, - "ERROR: SSHD process %u died during reload!\n", pid); - return SSH_ERROR; - } + rc = torture_terminate_process(s->srv_pidfile); + assert_return_code(rc, errno); - /* Wait until the sshd is ready to accept connections */ - rc = torture_wait_for_daemon(5); - assert_int_equal(rc, 0); - return SSH_OK; + return torture_start_sshd_server(state); } /* @brief: Updates SSHD server configuration with more options and @@ -980,9 +1206,7 @@ int rc; rc = torture_terminate_process(s->srv_pidfile); - if (rc != 0) { - fprintf(stderr, "XXXXXX Failed to terminate sshd\n"); - } + assert_return_code(rc, errno); torture_teardown_socket_dir(state); } diff --color -ru ../libssh-0.9.6/tests/torture.h ./tests/torture.h --- ../libssh-0.9.6/tests/torture.h 2021-08-26 14:27:44.000000000 +0200 +++ ./tests/torture.h 2023-05-02 10:32:03.964511860 +0200 @@ -132,6 +132,10 @@ void torture_reset_config(ssh_session session); +void torture_setup_create_libssh_config(void **state); + +void torture_setup_libssh_server(void **state, const char *server_path); + /* * This function must be defined in every unit test file. */