diff options
author | Jakub Jelen <jjelen@redhat.com> | 2021-06-24 10:47:33 +0200 |
---|---|---|
committer | Jakub Jelen <jjelen@redhat.com> | 2021-08-18 14:13:56 +0200 |
commit | f8817c0c355350cd6ca55a91f815d082b3bd9f73 (patch) | |
tree | fc09186697595caa74e008fa353a32846f9810d4 | |
parent | f5211239f918acf405d104b200891ca58130e23e (diff) | |
download | libssh-f8817c0c355350cd6ca55a91f815d082b3bd9f73.tar.gz libssh-f8817c0c355350cd6ca55a91f815d082b3bd9f73.tar.xz libssh-f8817c0c355350cd6ca55a91f815d082b3bd9f73.zip |
tests: Simple reproducer for rekeying with different kex
We do not use SHA1 as it is disabled in many systems
Verifies CVE-2021-3634
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r-- | tests/client/torture_rekey.c | 158 |
1 files changed, 155 insertions, 3 deletions
diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c index c599332d..e7813e1e 100644 --- a/tests/client/torture_rekey.c +++ b/tests/client/torture_rekey.c @@ -38,6 +38,8 @@ #include <fcntl.h> #include <pwd.h> +static uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */ + static int sshd_setup(void **state) { torture_setup_sshd_server(state, false); @@ -153,7 +155,6 @@ static void torture_rekey_send(void **state) int rc; char data[256]; unsigned int i; - uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */ struct ssh_crypto_struct *c = NULL; unsigned char *secret_hash = NULL; @@ -234,8 +235,6 @@ static void session_setup_sftp(void **state) assert_non_null(s->ssh.tsftp); } -uint64_t bytes = 2048; /* 2KB */ - static int session_setup_sftp_client(void **state) { struct torture_state *s = *state; @@ -442,6 +441,153 @@ static void torture_rekey_server_send(void **state) ssh_disconnect(s->ssh.session); } +static void torture_rekey_different_kex(void **state) +{ + struct torture_state *s = *state; + int rc; + char data[256]; + unsigned int i; + struct ssh_crypto_struct *c = NULL; + unsigned char *secret_hash = NULL; + size_t secret_hash_len = 0; + const char *kex1 = "diffie-hellman-group14-sha256,curve25519-sha256,ecdh-sha2-nistp256"; + const char *kex2 = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,ecdh-sha2-nistp521"; + + /* Use short digest for initial key exchange */ + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex1); + assert_ssh_return_code(s->ssh.session, rc); + + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes); + assert_ssh_return_code(s->ssh.session, rc); + + rc = ssh_connect(s->ssh.session); + assert_ssh_return_code(s->ssh.session, rc); + + /* The blocks limit is set correctly */ + c = s->ssh.session->current_crypto; + assert_int_equal(c->in_cipher->max_blocks, + bytes / c->in_cipher->blocksize); + assert_int_equal(c->out_cipher->max_blocks, + bytes / c->out_cipher->blocksize); + /* We should have less encrypted packets than transfered (first are not encrypted) */ + assert_true(c->out_cipher->packets < s->ssh.session->send_seq); + assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); + /* Copy the initial secret hash = session_id so we know we changed keys later */ + secret_hash = malloc(c->digest_len); + assert_non_null(secret_hash); + memcpy(secret_hash, c->secret_hash, c->digest_len); + secret_hash_len = c->digest_len; + assert_int_equal(secret_hash_len, 32); /* SHA256 len */ + + /* OpenSSH can not rekey before authentication so authenticate here */ + rc = ssh_userauth_none(s->ssh.session, NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED); + } + rc = ssh_userauth_list(s->ssh.session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); + + rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL); + assert_int_equal(rc, SSH_AUTH_SUCCESS); + + /* Now try to change preference of key exchange algorithm to something with larger digest */ + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex2); + assert_ssh_return_code(s->ssh.session, rc); + + /* send ignore packets of up to 1KB to trigger rekey. Send litle bit more + * 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++) { + ssh_send_ignore(s->ssh.session, data); + ssh_handle_packets(s->ssh.session, 50); + } + + /* The rekey limit was restored in the new crypto to the same value */ + c = s->ssh.session->current_crypto; + assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize); + assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize); + /* Check that the secret hash is different than initially */ + assert_int_equal(c->digest_len, 64); /* SHA512 len */ + assert_memory_not_equal(secret_hash, c->secret_hash, secret_hash_len); + /* Session ID stays same after one rekey */ + assert_memory_equal(secret_hash, c->session_id, secret_hash_len); + free(secret_hash); + + assert_int_equal(ssh_is_connected(s->ssh.session), 1); + assert_int_equal(s->ssh.session->session_state, SSH_SESSION_STATE_AUTHENTICATED); + + ssh_disconnect(s->ssh.session); +} + +static void torture_rekey_server_different_kex(void **state) +{ + struct torture_state *s = *state; + int rc; + char data[256]; + unsigned int i; + struct ssh_crypto_struct *c = NULL; + unsigned char *secret_hash = NULL; + size_t secret_hash_len = 0; + const char *sshd_config = "RekeyLimit 2K none"; + const char *kex1 = "diffie-hellman-group14-sha256,curve25519-sha256,ecdh-sha2-nistp256"; + const char *kex2 = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512"; + + /* Use short digest for initial key exchange */ + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex1); + assert_ssh_return_code(s->ssh.session, rc); + + torture_update_sshd_config(state, sshd_config); + + rc = ssh_connect(s->ssh.session); + assert_ssh_return_code(s->ssh.session, rc); + + /* Copy the initial secret hash = session_id so we know we changed keys later */ + c = s->ssh.session->current_crypto; + secret_hash = malloc(c->digest_len); + assert_non_null(secret_hash); + memcpy(secret_hash, c->secret_hash, c->digest_len); + secret_hash_len = c->digest_len; + assert_int_equal(secret_hash_len, 32); /* SHA256 len */ + + /* OpenSSH can not rekey before authentication so authenticate here */ + rc = ssh_userauth_none(s->ssh.session, NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED); + } + rc = ssh_userauth_list(s->ssh.session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); + + rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL); + assert_int_equal(rc, SSH_AUTH_SUCCESS); + + /* Now try to change preference of key exchange algorithm to something with larger digest */ + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex2); + assert_ssh_return_code(s->ssh.session, rc); + + /* send ignore packets of up to 1KB to trigger rekey. Send litle bit more + * 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++) { + ssh_send_ignore(s->ssh.session, data); + ssh_handle_packets(s->ssh.session, 50); + } + + /* Check that the secret hash is different than initially */ + c = s->ssh.session->current_crypto; + assert_int_equal(c->digest_len, 64); /* SHA512 len */ + assert_memory_not_equal(secret_hash, c->secret_hash, secret_hash_len); + /* Session ID stays same after one rekey */ + assert_memory_equal(secret_hash, c->session_id, secret_hash_len); + free(secret_hash); + + ssh_disconnect(s->ssh.session); +} + + #ifdef WITH_SFTP static int session_setup_sftp_server(void **state) { @@ -528,6 +674,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_rekey_send, session_setup, session_teardown), + cmocka_unit_test_setup_teardown(torture_rekey_different_kex, + session_setup, + session_teardown), /* Note, that this modifies the sshd_config */ cmocka_unit_test_setup_teardown(torture_rekey_server_send, session_setup, @@ -537,6 +686,9 @@ int torture_run_tests(void) { session_setup_sftp_server, session_teardown), #endif /* WITH_SFTP */ + cmocka_unit_test_setup_teardown(torture_rekey_server_different_kex, + session_setup, + session_teardown), /* TODO verify the two rekey are possible and the states are not broken after rekey */ }; |