mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-11 18:50:28 +09:00
tests: Invoke all combinations of wrong guesses during rekey
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit d357a9f3e2)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.12.0)
|
cmake_minimum_required(VERSION 3.14.0)
|
||||||
|
|
||||||
# Specify search path for CMake modules to be loaded by include()
|
# Specify search path for CMake modules to be loaded by include()
|
||||||
# and find_package()
|
# and find_package()
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ add_subdirectory(unittests)
|
|||||||
# OpenSSH Capabilities are required for all unit tests
|
# OpenSSH Capabilities are required for all unit tests
|
||||||
find_program(SSH_EXECUTABLE NAMES ssh)
|
find_program(SSH_EXECUTABLE NAMES ssh)
|
||||||
if (SSH_EXECUTABLE)
|
if (SSH_EXECUTABLE)
|
||||||
|
file(SIZE ${SSH_EXECUTABLE} SSH_EXECUTABLE_SIZE)
|
||||||
execute_process(COMMAND ${SSH_EXECUTABLE} -V ERROR_VARIABLE OPENSSH_VERSION_STR)
|
execute_process(COMMAND ${SSH_EXECUTABLE} -V ERROR_VARIABLE OPENSSH_VERSION_STR)
|
||||||
string(REGEX REPLACE "^.*OpenSSH_([0-9]+).[0-9].*$" "\\1" OPENSSH_VERSION_MAJOR "${OPENSSH_VERSION_STR}")
|
string(REGEX REPLACE "^.*OpenSSH_([0-9]+).[0-9].*$" "\\1" OPENSSH_VERSION_MAJOR "${OPENSSH_VERSION_STR}")
|
||||||
string(REGEX REPLACE "^.*OpenSSH_[0-9]+.([0-9]).*$" "\\1" OPENSSH_VERSION_MINOR "${OPENSSH_VERSION_STR}")
|
string(REGEX REPLACE "^.*OpenSSH_[0-9]+.([0-9]).*$" "\\1" OPENSSH_VERSION_MINOR "${OPENSSH_VERSION_STR}")
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "libssh/priv.h"
|
#include "libssh/priv.h"
|
||||||
#include "libssh/session.h"
|
#include "libssh/session.h"
|
||||||
#include "libssh/crypto.h"
|
#include "libssh/crypto.h"
|
||||||
|
#include "libssh/token.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -96,6 +97,7 @@ static int session_teardown(void **state)
|
|||||||
struct torture_state *s = *state;
|
struct torture_state *s = *state;
|
||||||
|
|
||||||
ssh_free(s->ssh.session);
|
ssh_free(s->ssh.session);
|
||||||
|
s->ssh.session = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -148,7 +150,7 @@ static void torture_rekey_default(void **state)
|
|||||||
ssh_disconnect(s->ssh.session);
|
ssh_disconnect(s->ssh.session);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sanity_check_session(void **state)
|
static void sanity_check_session_size(void **state, uint64_t rekey_limit)
|
||||||
{
|
{
|
||||||
struct torture_state *s = *state;
|
struct torture_state *s = *state;
|
||||||
struct ssh_crypto_struct *c = NULL;
|
struct ssh_crypto_struct *c = NULL;
|
||||||
@@ -156,9 +158,9 @@ static void sanity_check_session(void **state)
|
|||||||
c = s->ssh.session->current_crypto;
|
c = s->ssh.session->current_crypto;
|
||||||
assert_non_null(c);
|
assert_non_null(c);
|
||||||
assert_int_equal(c->in_cipher->max_blocks,
|
assert_int_equal(c->in_cipher->max_blocks,
|
||||||
bytes / c->in_cipher->blocksize);
|
rekey_limit / c->in_cipher->blocksize);
|
||||||
assert_int_equal(c->out_cipher->max_blocks,
|
assert_int_equal(c->out_cipher->max_blocks,
|
||||||
bytes / c->out_cipher->blocksize);
|
rekey_limit / c->out_cipher->blocksize);
|
||||||
/* when strict kex is used, the newkeys reset the sequence number */
|
/* when strict kex is used, the newkeys reset the sequence number */
|
||||||
if ((s->ssh.session->flags & SSH_SESSION_FLAG_KEX_STRICT) != 0) {
|
if ((s->ssh.session->flags & SSH_SESSION_FLAG_KEX_STRICT) != 0) {
|
||||||
assert_int_equal(c->out_cipher->packets, s->ssh.session->send_seq);
|
assert_int_equal(c->out_cipher->packets, s->ssh.session->send_seq);
|
||||||
@@ -170,6 +172,10 @@ static void sanity_check_session(void **state)
|
|||||||
assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
|
assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static void sanity_check_session(void **state)
|
||||||
|
{
|
||||||
|
sanity_check_session_size(state, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/* We lower the rekey limits manually and check that the rekey
|
/* We lower the rekey limits manually and check that the rekey
|
||||||
* really happens when sending data
|
* really happens when sending data
|
||||||
@@ -275,7 +281,7 @@ static int session_setup_sftp_client(void **state)
|
|||||||
/* To trigger rekey by receiving data, the easiest thing is probably to
|
/* To trigger rekey by receiving data, the easiest thing is probably to
|
||||||
* use sftp
|
* use sftp
|
||||||
*/
|
*/
|
||||||
static void torture_rekey_recv(void **state)
|
static void torture_rekey_recv_size(void **state, uint64_t rekey_limit)
|
||||||
{
|
{
|
||||||
struct torture_state *s = *state;
|
struct torture_state *s = *state;
|
||||||
struct ssh_crypto_struct *c = NULL;
|
struct ssh_crypto_struct *c = NULL;
|
||||||
@@ -290,7 +296,7 @@ static void torture_rekey_recv(void **state)
|
|||||||
mode_t mask;
|
mode_t mask;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
sanity_check_session(state);
|
sanity_check_session_size(state, rekey_limit);
|
||||||
/* Copy the initial secret hash = session_id so we know we changed keys later */
|
/* Copy the initial secret hash = session_id so we know we changed keys later */
|
||||||
c = s->ssh.session->current_crypto;
|
c = s->ssh.session->current_crypto;
|
||||||
assert_non_null(c);
|
assert_non_null(c);
|
||||||
@@ -324,8 +330,10 @@ static void torture_rekey_recv(void **state)
|
|||||||
|
|
||||||
/* The rekey limit was restored in the new crypto to the same value */
|
/* The rekey limit was restored in the new crypto to the same value */
|
||||||
c = s->ssh.session->current_crypto;
|
c = s->ssh.session->current_crypto;
|
||||||
assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
|
assert_int_equal(c->in_cipher->max_blocks,
|
||||||
assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
|
rekey_limit / c->in_cipher->blocksize);
|
||||||
|
assert_int_equal(c->out_cipher->max_blocks,
|
||||||
|
rekey_limit / c->out_cipher->blocksize);
|
||||||
/* Check that the secret hash is different than initially */
|
/* Check that the secret hash is different than initially */
|
||||||
assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
|
assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
|
||||||
free(secret_hash);
|
free(secret_hash);
|
||||||
@@ -333,6 +341,11 @@ static void torture_rekey_recv(void **state)
|
|||||||
torture_sftp_close(s->ssh.tsftp);
|
torture_sftp_close(s->ssh.tsftp);
|
||||||
ssh_disconnect(s->ssh.session);
|
ssh_disconnect(s->ssh.session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void torture_rekey_recv(void **state)
|
||||||
|
{
|
||||||
|
torture_rekey_recv_size(state, bytes);
|
||||||
|
}
|
||||||
#endif /* WITH_SFTP */
|
#endif /* WITH_SFTP */
|
||||||
|
|
||||||
/* Rekey time requires rekey after specified time and is off by default.
|
/* Rekey time requires rekey after specified time and is off by default.
|
||||||
@@ -836,6 +849,81 @@ static void torture_rekey_guess_wrong_recv(void **state)
|
|||||||
|
|
||||||
torture_rekey_recv(state);
|
torture_rekey_recv(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void torture_rekey_guess_all_combinations(void **state)
|
||||||
|
{
|
||||||
|
struct torture_state *s = *state;
|
||||||
|
char sshd_config[256] = "";
|
||||||
|
char client_kex[256] = "";
|
||||||
|
const char *supported = NULL;
|
||||||
|
struct ssh_tokens_st *s_tok = NULL;
|
||||||
|
uint64_t rekey_limit = 0;
|
||||||
|
int rc, i, j;
|
||||||
|
|
||||||
|
/* The rekey limit is 1/2 of the transferred file size so we will likely get
|
||||||
|
* 2 rekeys per test, which still runs for acceptable time */
|
||||||
|
rekey_limit = atoll(SSH_EXECUTABLE_SIZE);
|
||||||
|
rekey_limit /= 2;
|
||||||
|
|
||||||
|
if (ssh_fips_mode()) {
|
||||||
|
supported = ssh_kex_get_fips_methods(SSH_KEX);
|
||||||
|
} else {
|
||||||
|
supported = ssh_kex_get_supported_method(SSH_KEX);
|
||||||
|
}
|
||||||
|
assert_non_null(supported);
|
||||||
|
|
||||||
|
s_tok = ssh_tokenize(supported, ',');
|
||||||
|
assert_non_null(s_tok);
|
||||||
|
for (i = 0; s_tok->tokens[i]; i++) {
|
||||||
|
/* Skip algorithms not supported by the OpenSSH server */
|
||||||
|
if (strstr(OPENSSH_KEX, s_tok->tokens[i]) == NULL) {
|
||||||
|
SSH_LOG(SSH_LOG_INFO, "Server: %s [skipping]", s_tok->tokens[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SSH_LOG(SSH_LOG_INFO, "Server: %s", s_tok->tokens[i]);
|
||||||
|
snprintf(sshd_config,
|
||||||
|
sizeof(sshd_config),
|
||||||
|
"KexAlgorithms %s",
|
||||||
|
s_tok->tokens[i]);
|
||||||
|
/* This sets an only supported kex algorithm that we do not have as
|
||||||
|
* a first option in the client */
|
||||||
|
torture_update_sshd_config(state, sshd_config);
|
||||||
|
|
||||||
|
for (j = 0; s_tok->tokens[j]; j++) {
|
||||||
|
if (i == j) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_setup(state);
|
||||||
|
/* Make the client send the first_kex_packet_follows flag during key
|
||||||
|
* exchange as well as during the rekey */
|
||||||
|
s->ssh.session->send_first_kex_follows = true;
|
||||||
|
|
||||||
|
rc = ssh_options_set(s->ssh.session,
|
||||||
|
SSH_OPTIONS_REKEY_DATA,
|
||||||
|
&rekey_limit);
|
||||||
|
assert_ssh_return_code(s->ssh.session, rc);
|
||||||
|
|
||||||
|
/* Client kex preference will have the second of the pair and the
|
||||||
|
* server one as a second to negotiate on the second attempt */
|
||||||
|
snprintf(client_kex,
|
||||||
|
sizeof(client_kex),
|
||||||
|
"%s,%s",
|
||||||
|
s_tok->tokens[j],
|
||||||
|
s_tok->tokens[i]);
|
||||||
|
SSH_LOG(SSH_LOG_INFO, "Client: %s", client_kex);
|
||||||
|
rc = ssh_options_set(s->ssh.session,
|
||||||
|
SSH_OPTIONS_KEY_EXCHANGE,
|
||||||
|
client_kex);
|
||||||
|
assert_ssh_return_code(s->ssh.session, rc);
|
||||||
|
session_setup_sftp(state);
|
||||||
|
torture_rekey_recv_size(state, rekey_limit);
|
||||||
|
session_teardown(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_tokens_free(s_tok);
|
||||||
|
}
|
||||||
#endif /* WITH_SFTP */
|
#endif /* WITH_SFTP */
|
||||||
|
|
||||||
int torture_run_tests(void) {
|
int torture_run_tests(void) {
|
||||||
@@ -905,6 +993,7 @@ int torture_run_tests(void) {
|
|||||||
cmocka_unit_test_setup_teardown(torture_rekey_guess_wrong_recv,
|
cmocka_unit_test_setup_teardown(torture_rekey_guess_wrong_recv,
|
||||||
session_setup,
|
session_setup,
|
||||||
session_teardown),
|
session_teardown),
|
||||||
|
cmocka_unit_test(torture_rekey_guess_all_combinations),
|
||||||
#endif /* WITH_SFTP */
|
#endif /* WITH_SFTP */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
#cmakedefine NCAT_EXECUTABLE "${NCAT_EXECUTABLE}"
|
#cmakedefine NCAT_EXECUTABLE "${NCAT_EXECUTABLE}"
|
||||||
#cmakedefine SSHD_EXECUTABLE "${SSHD_EXECUTABLE}"
|
#cmakedefine SSHD_EXECUTABLE "${SSHD_EXECUTABLE}"
|
||||||
#cmakedefine SSH_EXECUTABLE "${SSH_EXECUTABLE}"
|
#cmakedefine SSH_EXECUTABLE "${SSH_EXECUTABLE}"
|
||||||
|
#cmakedefine SSH_EXECUTABLE_SIZE "${SSH_EXECUTABLE_SIZE}"
|
||||||
#cmakedefine DROPBEAR_EXECUTABLE "${DROPBEAR_EXECUTABLE}"
|
#cmakedefine DROPBEAR_EXECUTABLE "${DROPBEAR_EXECUTABLE}"
|
||||||
#cmakedefine WITH_TIMEOUT ${WITH_TIMEOUT}
|
#cmakedefine WITH_TIMEOUT ${WITH_TIMEOUT}
|
||||||
#cmakedefine TIMEOUT_EXECUTABLE "${TIMEOUT_EXECUTABLE}"
|
#cmakedefine TIMEOUT_EXECUTABLE "${TIMEOUT_EXECUTABLE}"
|
||||||
|
|||||||
Reference in New Issue
Block a user