Compare commits

...

11 Commits

Author SHA1 Message Date
Praneeth Sarode
e8bbd194c7 refactor(pki): Define RSA_MIN_KEY_SIZE and update related checks
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-09-09 12:53:43 +02:00
Jakub Jelen
df4e907dff poll: Use is_locked helper where possible
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:33:58 +02:00
Philippe Antoine
c99261437f socket: do not free poll object if it is locked
As it may a cause a use after free if `send` fails when
ssh_poll_ctx_dopoll does its callback
ssh_poll_ctx_dopoll still wants to use the poll object later

Signed-off-by: Philippe Antoine <p.antoine@catenacyber.fr>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:33:57 +02:00
Andreas Schneider
53ac23ded4 CVE-2025-8114: Fix NULL pointer dereference after allocation failure
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-08-14 10:32:55 +02:00
Jakub Jelen
ffed80f8c0 CVE-2025-8277: mbedtls: Avoid leaking ecdh keys
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:32:24 +02:00
Jakub Jelen
9ada7aa0e4 CVE-2025-8277: wrapper: Free cv25519 private key on cleanup
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:32:19 +02:00
Jakub Jelen
d357a9f3e2 tests: Invoke all combinations of wrong guesses during rekey
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:32:13 +02:00
Jakub Jelen
c9d95ab0c7 CVE-2025-8277: ecdh: Free previously allocated pubkeys
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:32:10 +02:00
Francesco Rollo
ccff22d378 CVE-2025-8277: Fix memory leak of unused ephemeral key pair after client's wrong KEX guess
Signed-off-by: Francesco Rollo <eferollo@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:32:05 +02:00
Jakub Jelen
4310a696f2 CVE-2025-8277: packet: Adjust packet filter to work when DH-GEX is guessed wrongly
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:31:51 +02:00
Jakub Jelen
771e19a7a9 tests: Enable all key exchange methods in ssh_ping
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-08-14 10:31:30 +02:00
22 changed files with 208 additions and 29 deletions

View File

@@ -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()

View File

@@ -45,6 +45,7 @@
#define MAX_PUBKEY_SIZE 0x100000 /* 1M */ #define MAX_PUBKEY_SIZE 0x100000 /* 1M */
#define MAX_PRIVKEY_SIZE 0x400000 /* 4M */ #define MAX_PRIVKEY_SIZE 0x400000 /* 4M */
#define RSA_MIN_KEY_SIZE 768
#define SSH_KEY_FLAG_EMPTY 0x0 #define SSH_KEY_FLAG_EMPTY 0x0
#define SSH_KEY_FLAG_PUBLIC 0x0001 #define SSH_KEY_FLAG_PUBLIC 0x0001

View File

@@ -157,6 +157,7 @@ void ssh_poll_ctx_free(ssh_poll_ctx ctx);
int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p); int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p);
int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s); int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s);
void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p); void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p);
bool ssh_poll_is_locked(ssh_poll_handle p);
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout); int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout);
ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session); ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session);
int ssh_event_add_poll(ssh_event event, ssh_poll_handle p); int ssh_event_add_poll(ssh_event event, ssh_poll_handle p);

View File

@@ -79,6 +79,12 @@ int ssh_curve25519_init(ssh_session session)
return SSH_ERROR; return SSH_ERROR;
} }
/* Free any previously allocated privkey */
if (session->next_crypto->curve25519_privkey != NULL) {
EVP_PKEY_free(session->next_crypto->curve25519_privkey);
session->next_crypto->curve25519_privkey = NULL;
}
session->next_crypto->curve25519_privkey = pkey; session->next_crypto->curve25519_privkey = pkey;
pkey = NULL; pkey = NULL;

View File

@@ -89,6 +89,12 @@ int ssh_curve25519_init(ssh_session session)
memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE); memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE);
/* Free any previously allocated privkey */
if (session->next_crypto->curve25519_privkey != NULL) {
gcry_sexp_release(session->next_crypto->curve25519_privkey);
session->next_crypto->curve25519_privkey = NULL;
}
/* Store the private key */ /* Store the private key */
session->next_crypto->curve25519_privkey = keypair_sexp; session->next_crypto->curve25519_privkey = keypair_sexp;
keypair_sexp = NULL; keypair_sexp = NULL;

View File

@@ -407,6 +407,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
struct dh_ctx *ctx = NULL; struct dh_ctx *ctx = NULL;
int rc; int rc;
/* Cleanup any previously allocated dh_ctx */
if (crypto->dh_ctx != NULL) {
ssh_dh_cleanup(crypto);
}
ctx = calloc(1, sizeof(*ctx)); ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) { if (ctx == NULL) {
return SSH_ERROR; return SSH_ERROR;

View File

@@ -237,6 +237,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
struct dh_ctx *ctx = NULL; struct dh_ctx *ctx = NULL;
int rc; int rc;
/* Cleanup any previously allocated dh_ctx */
if (crypto->dh_ctx != NULL) {
ssh_dh_cleanup(crypto);
}
ctx = calloc(1, sizeof(*ctx)); ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) { if (ctx == NULL) {
return SSH_ERROR; return SSH_ERROR;

View File

@@ -191,6 +191,17 @@ static ssh_string ssh_ecdh_generate(ssh_session session)
#endif /* OPENSSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
return NULL; return NULL;
} }
/* Free any previously allocated privkey */
if (session->next_crypto->ecdh_privkey != NULL) {
#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_KEY_free(session->next_crypto->ecdh_privkey);
#else
EVP_PKEY_free(session->next_crypto->ecdh_privkey);
#endif
session->next_crypto->ecdh_privkey = NULL;
}
session->next_crypto->ecdh_privkey = key; session->next_crypto->ecdh_privkey = key;
return pubkey_string; return pubkey_string;
} }
@@ -219,6 +230,7 @@ int ssh_client_ecdh_init(ssh_session session)
return SSH_ERROR; return SSH_ERROR;
} }
ssh_string_free(session->next_crypto->ecdh_client_pubkey);
session->next_crypto->ecdh_client_pubkey = client_pubkey; session->next_crypto->ecdh_client_pubkey = client_pubkey;
/* register the packet callbacks */ /* register the packet callbacks */

View File

@@ -101,8 +101,15 @@ int ssh_client_ecdh_init(ssh_session session)
goto out; goto out;
} }
/* Free any previously allocated privkey */
if (session->next_crypto->ecdh_privkey != NULL) {
gcry_sexp_release(session->next_crypto->ecdh_privkey);
session->next_crypto->ecdh_privkey = NULL;
}
session->next_crypto->ecdh_privkey = key; session->next_crypto->ecdh_privkey = key;
key = NULL; key = NULL;
SSH_STRING_FREE(session->next_crypto->ecdh_client_pubkey);
session->next_crypto->ecdh_client_pubkey = client_pubkey; session->next_crypto->ecdh_client_pubkey = client_pubkey;
client_pubkey = NULL; client_pubkey = NULL;

View File

@@ -70,6 +70,12 @@ int ssh_client_ecdh_init(ssh_session session)
return SSH_ERROR; return SSH_ERROR;
} }
/* Free any previously allocated privkey */
if (session->next_crypto->ecdh_privkey != NULL) {
mbedtls_ecp_keypair_free(session->next_crypto->ecdh_privkey);
SAFE_FREE(session->next_crypto->ecdh_privkey);
}
session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair)); session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair));
if (session->next_crypto->ecdh_privkey == NULL) { if (session->next_crypto->ecdh_privkey == NULL) {
return SSH_ERROR; return SSH_ERROR;
@@ -110,6 +116,7 @@ int ssh_client_ecdh_init(ssh_session session)
goto out; goto out;
} }
SSH_STRING_FREE(session->next_crypto->ecdh_client_pubkey);
session->next_crypto->ecdh_client_pubkey = client_pubkey; session->next_crypto->ecdh_client_pubkey = client_pubkey;
client_pubkey = NULL; client_pubkey = NULL;

View File

@@ -1569,6 +1569,8 @@ int ssh_make_sessionid(ssh_session session)
ssh_log_hexdump("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf)); ssh_log_hexdump("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf));
#endif #endif
/* Set rc for the following switch statement in case we goto error. */
rc = SSH_ERROR;
switch (session->next_crypto->kex_type) { switch (session->next_crypto->kex_type) {
case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1: case SSH_KEX_DH_GROUP14_SHA1:
@@ -1629,6 +1631,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash); session->next_crypto->secret_hash);
break; break;
} }
/* During the first kex, secret hash and session ID are equal. However, after /* During the first kex, secret hash and session ID are equal. However, after
* a key re-exchange, a new secret hash is calculated. This hash will not replace * a key re-exchange, a new secret hash is calculated. This hash will not replace
* but complement existing session id. * but complement existing session id.
@@ -1637,6 +1640,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->session_id = malloc(session->next_crypto->digest_len); session->next_crypto->session_id = malloc(session->next_crypto->digest_len);
if (session->next_crypto->session_id == NULL) { if (session->next_crypto->session_id == NULL) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
rc = SSH_ERROR;
goto error; goto error;
} }
memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash, memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash,

View File

@@ -31,13 +31,14 @@
#else #else
#include <winsock2.h> #include <winsock2.h>
#endif #endif
#include <sys/types.h> #include "libssh/config_parser.h"
#include "libssh/misc.h"
#include "libssh/options.h"
#include "libssh/pki.h"
#include "libssh/pki_priv.h" #include "libssh/pki_priv.h"
#include "libssh/priv.h" #include "libssh/priv.h"
#include "libssh/session.h" #include "libssh/session.h"
#include "libssh/misc.h" #include <sys/types.h>
#include "libssh/options.h"
#include "libssh/config_parser.h"
#ifdef WITH_SERVER #ifdef WITH_SERVER
#include "libssh/server.h" #include "libssh/server.h"
#include "libssh/bind.h" #include "libssh/bind.h"
@@ -1288,11 +1289,13 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
/* (*x == 0) is allowed as it is used to revert to default */ /* (*x == 0) is allowed as it is used to revert to default */
if (*x > 0 && *x < 768) { if (*x > 0 && *x < RSA_MIN_KEY_SIZE) {
ssh_set_error(session, SSH_REQUEST_DENIED, ssh_set_error(session,
SSH_REQUEST_DENIED,
"The provided value (%d) for minimal RSA key " "The provided value (%d) for minimal RSA key "
"size is too small. Use at least 768 bits.", "size is too small. Use at least %d bits.",
*x); *x,
RSA_MIN_KEY_SIZE);
return -1; return -1;
} }
session->opts.rsa_min_size = *x; session->opts.rsa_min_size = *x;
@@ -2590,12 +2593,13 @@ ssh_bind_options_set(ssh_bind sshbind,
/* (*x == 0) is allowed as it is used to revert to default */ /* (*x == 0) is allowed as it is used to revert to default */
if (*x > 0 && *x < 768) { if (*x > 0 && *x < RSA_MIN_KEY_SIZE) {
ssh_set_error(sshbind, ssh_set_error(sshbind,
SSH_REQUEST_DENIED, SSH_REQUEST_DENIED,
"The provided value (%d) for minimal RSA key " "The provided value (%d) for minimal RSA key "
"size is too small. Use at least 768 bits.", "size is too small. Use at least %d bits.",
*x); *x,
RSA_MIN_KEY_SIZE);
return -1; return -1;
} }
sshbind->rsa_min_size = *x; sshbind->rsa_min_size = *x;

View File

@@ -294,6 +294,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* or session_state == SSH_SESSION_STATE_INITIAL_KEX * or session_state == SSH_SESSION_STATE_INITIAL_KEX
* - dh_handshake_state == DH_STATE_INIT * - dh_handshake_state == DH_STATE_INIT
* or dh_handshake_state == DH_STATE_INIT_SENT (re-exchange) * or dh_handshake_state == DH_STATE_INIT_SENT (re-exchange)
* or dh_handshake_state == DH_STATE_REQUEST_SENT (dh-gex)
* or dh_handshake_state == DH_STATE_FINISHED (re-exchange) * or dh_handshake_state == DH_STATE_FINISHED (re-exchange)
* *
* Transitions: * Transitions:
@@ -313,6 +314,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
if ((session->dh_handshake_state != DH_STATE_INIT) && if ((session->dh_handshake_state != DH_STATE_INIT) &&
(session->dh_handshake_state != DH_STATE_INIT_SENT) && (session->dh_handshake_state != DH_STATE_INIT_SENT) &&
(session->dh_handshake_state != DH_STATE_REQUEST_SENT) &&
(session->dh_handshake_state != DH_STATE_FINISHED)) (session->dh_handshake_state != DH_STATE_FINISHED))
{ {
rc = SSH_PACKET_DENIED; rc = SSH_PACKET_DENIED;

View File

@@ -447,7 +447,7 @@ bool ssh_key_size_allowed_rsa(int min_size, ssh_key key)
{ {
int key_size = ssh_key_size(key); int key_size = ssh_key_size(key);
if (min_size < 768) { if (min_size < RSA_MIN_KEY_SIZE) {
if (ssh_fips_mode()) { if (ssh_fips_mode()) {
min_size = 2048; min_size = 2048;
} else { } else {

View File

@@ -427,7 +427,7 @@ void ssh_poll_set_events(ssh_poll_handle p, short events)
{ {
p->events = events; p->events = events;
if (p->ctx != NULL) { if (p->ctx != NULL) {
if (p->lock_cnt == 0) { if (!ssh_poll_is_locked(p)) {
p->ctx->pollfds[p->x.idx].events = events; p->ctx->pollfds[p->x.idx].events = events;
} else if (!(p->ctx->pollfds[p->x.idx].events & POLLOUT)) { } else if (!(p->ctx->pollfds[p->x.idx].events & POLLOUT)) {
/* if locked, allow only setting POLLOUT to prevent recursive /* if locked, allow only setting POLLOUT to prevent recursive
@@ -682,6 +682,20 @@ void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p)
} }
} }
/**
* @brief Returns if a poll object is locked.
*
* @param p Pointer to an already allocated poll object.
* @returns true if the poll object is locked; false otherwise.
*/
bool ssh_poll_is_locked(ssh_poll_handle p)
{
if (p == NULL) {
return false;
}
return p->lock_cnt > 0;
}
/** /**
* @brief Poll all the sockets associated through a poll object with a * @brief Poll all the sockets associated through a poll object with a
* poll context. If any of the events are set after the poll, the * poll context. If any of the events are set after the poll, the
@@ -716,7 +730,7 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
* output buffer */ * output buffer */
for (i = 0; i < ctx->polls_used; i++) { for (i = 0; i < ctx->polls_used; i++) {
/* The lock allows only POLLOUT events: drop the rest */ /* The lock allows only POLLOUT events: drop the rest */
if (ctx->pollptrs[i]->lock_cnt > 0) { if (ssh_poll_is_locked(ctx->pollptrs[i])) {
ctx->pollfds[i].events &= POLLOUT; ctx->pollfds[i].events &= POLLOUT;
} }
} }

View File

@@ -481,7 +481,7 @@ void ssh_socket_close(ssh_socket s)
#endif #endif
} }
if (s->poll_handle != NULL) { if (s->poll_handle != NULL && !ssh_poll_is_locked(s->poll_handle)) {
ssh_poll_free(s->poll_handle); ssh_poll_free(s->poll_handle);
s->poll_handle = NULL; s->poll_handle = NULL;
} }

View File

@@ -182,12 +182,17 @@ void crypto_free(struct ssh_crypto_struct *crypto)
#endif /* OPENSSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
#elif defined HAVE_GCRYPT_ECC #elif defined HAVE_GCRYPT_ECC
gcry_sexp_release(crypto->ecdh_privkey); gcry_sexp_release(crypto->ecdh_privkey);
#endif #elif defined HAVE_LIBMBEDCRYPTO
mbedtls_ecp_keypair_free(crypto->ecdh_privkey);
SAFE_FREE(crypto->ecdh_privkey);
#endif /* HAVE_LIBGCRYPT */
crypto->ecdh_privkey = NULL; crypto->ecdh_privkey = NULL;
} }
#endif #endif
#ifdef HAVE_LIBCRYPTO #ifdef HAVE_LIBCRYPTO
EVP_PKEY_free(crypto->curve25519_privkey); EVP_PKEY_free(crypto->curve25519_privkey);
#elif defined(HAVE_GCRYPT_CURVE25519)
gcry_sexp_release(crypto->curve25519_privkey);
#endif #endif
SAFE_FREE(crypto->dh_server_signature); SAFE_FREE(crypto->dh_server_signature);
if (crypto->session_id != NULL) { if (crypto->session_id != NULL) {

View File

@@ -112,6 +112,7 @@ add_subdirectory(unittests)
find_program(SSH_EXECUTABLE NAMES ssh) find_program(SSH_EXECUTABLE NAMES ssh)
find_program(SSH_KEYGEN_EXECUTABLE NAMES ssh-keygen) find_program(SSH_KEYGEN_EXECUTABLE NAMES ssh-keygen)
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}")

View File

@@ -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 */
}; };

View File

@@ -27,6 +27,7 @@ int main(int argc, char **argv)
const char *banner = NULL; const char *banner = NULL;
ssh_session session = NULL; ssh_session session = NULL;
const char *hostkeys = NULL; const char *hostkeys = NULL;
const char *kex = NULL;
int rc = 1; int rc = 1;
bool process_config = false; bool process_config = false;
@@ -67,6 +68,13 @@ int main(int argc, char **argv)
goto out; goto out;
} }
/* Enable all supported kex algorithms */
kex = ssh_get_supported_methods(SSH_KEX);
rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex);
if (rc < 0) {
goto out;
}
rc = ssh_connect(session); rc = ssh_connect(session);
if (rc != SSH_OK) { if (rc != SSH_OK) {
fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session)); fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));

View File

@@ -67,6 +67,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 SSH_KEYGEN_EXECUTABLE "${SSH_KEYGEN_EXECUTABLE}" #cmakedefine SSH_KEYGEN_EXECUTABLE "${SSH_KEYGEN_EXECUTABLE}"
#cmakedefine DROPBEAR_EXECUTABLE "${DROPBEAR_EXECUTABLE}" #cmakedefine DROPBEAR_EXECUTABLE "${DROPBEAR_EXECUTABLE}"
#cmakedefine WITH_TIMEOUT ${WITH_TIMEOUT} #cmakedefine WITH_TIMEOUT ${WITH_TIMEOUT}

View File

@@ -8,13 +8,14 @@
#endif #endif
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h>
#include "torture.h" #include "torture.h"
#include "torture_key.h" #include "torture_key.h"
#include <libssh/session.h> #include <errno.h>
#include <libssh/misc.h> #include <libssh/misc.h>
#include <libssh/pki_priv.h>
#include <libssh/options.h> #include <libssh/options.h>
#include <libssh/pki.h>
#include <libssh/pki_priv.h>
#include <libssh/session.h>
#ifdef WITH_SERVER #ifdef WITH_SERVER
#include <libssh/bind.h> #include <libssh/bind.h>
#define LIBSSH_CUSTOM_BIND_CONFIG_FILE "my_bind_config" #define LIBSSH_CUSTOM_BIND_CONFIG_FILE "my_bind_config"
@@ -1997,7 +1998,7 @@ static void torture_options_set_verbosity (void **state)
static void torture_options_set_rsa_min_size(void **state) static void torture_options_set_rsa_min_size(void **state)
{ {
ssh_session session = *state; ssh_session session = *state;
int min_allowed = 768, key_size, rc; int min_allowed = RSA_MIN_KEY_SIZE, key_size, rc;
/* Check that passing NULL leads to failure */ /* Check that passing NULL leads to failure */
rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, NULL); rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, NULL);
@@ -2422,7 +2423,7 @@ static void torture_bind_options_set_rsa_min_size(void **state)
{ {
struct bind_st *test_state = NULL; struct bind_st *test_state = NULL;
ssh_bind bind = NULL; ssh_bind bind = NULL;
int rc, min_allowed = 768, key_size; int rc, min_allowed = RSA_MIN_KEY_SIZE, key_size;
assert_non_null(state); assert_non_null(state);
test_state = *((struct bind_st **)state); test_state = *((struct bind_st **)state);