Compare commits

...

17 Commits

Author SHA1 Message Date
Samir Benmendil
39a62cef44 tests: suppress leaks from NSS modules
Signed-off-by: Samir Benmendil <me@rmz.io>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit ce45ba8c61)
2026-01-05 16:38:12 +01:00
Jakub Jelen
7969b6de3c Suppress remaining OpenSSL 3.5 memory leaks
Reported as

https://github.com/openssl/openssl/issues/29077

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit b042477f83)
2026-01-05 16:37:46 +01:00
Jakub Jelen
b207e39d28 tests: Adjust valgrind supressions for Fedora 43
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit a94df4bb8f)
2026-01-05 16:37:42 +01:00
Jakub Jelen
6230b24ff5 tests: Test proxyjump configuration parsing
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 82db6a7ab3)
2026-01-05 13:40:22 +01:00
Jakub Jelen
e668b03dd7 tests: Reproducer for missing value to LogLevel
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 163373c9d9)
2026-01-05 13:40:13 +01:00
Jakub Jelen
77ce02d809 config: Allow setting username from configuration
... file, even if it was already set before. The options
level handles what was already set.

The proxyJump implementation sets the username from the proxyjump, which
is setting it to NULL, effectively writing the current username to the
new session, which was not possible to override due to the following check.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 320844669a)
2026-01-05 13:38:49 +01:00
Jakub Jelen
d61b0dc7cc tests: Improve test coverage of comparing certificates
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 701a2155a7)
2026-01-05 13:19:20 +01:00
Jakub Jelen
d12eb770ac pki: Fix comparing public key of certificate
When the first key object is a certificate object, this match will
fall through to the generic key comparison that is unable to handle
the ed25519 keys and fails.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 38f3d158f6)
2026-01-05 13:15:53 +01:00
Jakub Jelen
03b29a6874 pki: Avoild false positive matches when comparing certificates in mbedtls and gcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 0d5a2652b4)
2026-01-05 13:15:09 +01:00
Jakub Jelen
99957fb561 ssh_known_hosts_get_algorithms: Simplify cleanup ...
...  and prevent memory leak of host_port on memory allocation failure.

Thanks Xiaoke Wang for the report!

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 9d6df9d0fa)
2026-01-05 13:10:19 +01:00
Jakub Jelen
3e9175e66a server: Check strdup allocation failure
Thanks Xiaoke Wang for the report!

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit ee180c660e)
2026-01-05 13:09:59 +01:00
Jakub Jelen
bf295abb5b tests: Remove the -E which is overridden by followed -E on ctest CLI
The threads_pki_rsa was running and working under valgrind for some
time already without anyone noticing this syntax does not work.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 950abbbd81)
2026-01-05 13:07:08 +01:00
Jakub Jelen
7f14df3eac tests: Avoid needless pthread_exit()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit b9c6701c68)
2026-01-05 13:06:56 +01:00
Pavol Žáčik
c206e5d84e client: Reset session packet state on disconnect
When reusing session structures for multiple
connections, the packet state could be SIZE_READ
before disconnect, causing initial packets of the
next connection to be misinterpreted.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 41b8b3326c)
2026-01-05 13:06:31 +01:00
Jakub Jelen
274b8f19b3 connector: Fix default connector flags
Originally reported by Jeremy Cross <jcross@beyondtrust.com> in #461

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit c932790b82)
2026-01-05 13:05:30 +01:00
Jakub Jelen
39a88d62c9 connector: Reformat
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 8a0aa17bca)
2026-01-05 13:05:02 +01:00
Francesco Rollo
94f12090b5 fix(bind): Remove code duplication in ssh_bind_listen
Signed-off-by: Francesco Rollo <eferollo@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit c94e2efcf1)
2026-01-05 13:04:10 +01:00
16 changed files with 406 additions and 152 deletions

View File

@@ -218,28 +218,12 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
return SSH_OK;
}
int ssh_bind_listen(ssh_bind sshbind) {
int ssh_bind_listen(ssh_bind sshbind)
{
const char *host = NULL;
socket_t fd;
int rc;
/* Apply global bind configurations, if it hasn't been applied before */
rc = ssh_bind_options_parse_config(sshbind, NULL);
if (rc != 0) {
ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
return SSH_ERROR;
}
/* Set default hostkey paths if no hostkey was found before */
if (sshbind->ecdsakey == NULL &&
sshbind->rsakey == NULL &&
sshbind->ed25519key == NULL) {
sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
}
/* Apply global bind configurations, if it hasn't been applied before */
rc = ssh_bind_options_parse_config(sshbind, NULL);
if (rc != 0) {

View File

@@ -836,6 +836,7 @@ error:
session->opts.fd = SSH_INVALID_SOCKET;
session->session_state = SSH_SESSION_STATE_DISCONNECTED;
session->pending_call_state = SSH_PENDING_CALL_NONE;
session->packet_state = PACKET_STATE_INIT;
while ((it = ssh_list_get_iterator(session->channels)) != NULL) {
ssh_channel_do_free(ssh_iterator_value(ssh_channel, it));

View File

@@ -1066,12 +1066,10 @@ ssh_config_parse_line(ssh_session session,
}
break;
case SOC_USERNAME:
if (session->opts.username == NULL) {
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_USER, p);
}
}
break;
case SOC_IDENTITY:
p = ssh_config_get_str_tok(&s, NULL);

View File

@@ -166,7 +166,7 @@ int ssh_connector_set_out_channel(ssh_connector connector,
/* Fallback to default value for invalid flags */
if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) {
connector->in_flags = SSH_CONNECTOR_STDOUT;
connector->out_flags = SSH_CONNECTOR_STDOUT;
}
return ssh_add_channel_callbacks(channel, &connector->out_channel_cb);
@@ -381,15 +381,13 @@ ssh_connector_fd_out_cb(ssh_connector connector)
*
* @returns 0
*/
static int ssh_connector_fd_cb(ssh_poll_handle p,
static int ssh_connector_fd_cb(UNUSED_PARAM(ssh_poll_handle p),
socket_t fd,
int revents,
void *userdata)
{
ssh_connector connector = userdata;
(void)p;
if (revents & POLLERR) {
ssh_connector_except(connector, fd);
} else if((revents & (POLLIN|POLLHUP)) && fd == connector->in_fd) {
@@ -419,7 +417,7 @@ static int ssh_connector_fd_cb(ssh_poll_handle p,
* @returns Amount of data bytes consumed
*/
static int ssh_connector_channel_data_cb(ssh_session session,
ssh_channel channel,
UNUSED_PARAM(ssh_channel channel),
void *data,
uint32_t len,
int is_stderr,
@@ -429,10 +427,6 @@ static int ssh_connector_channel_data_cb(ssh_session session,
int w;
uint32_t window;
(void) session;
(void) channel;
(void) is_stderr;
SSH_LOG(SSH_LOG_TRACE,"connector data on channel");
if (is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDERR)) {
@@ -510,8 +504,9 @@ static int ssh_connector_channel_data_cb(ssh_session session,
*
* @returns Amount of data bytes consumed
*/
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
static int
ssh_connector_channel_write_wontblock_cb(ssh_session session,
UNUSED_PARAM(ssh_channel channel),
uint32_t bytes,
void *userdata)
{

View File

@@ -376,25 +376,23 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
}
}
host_port = ssh_session_get_host_port(session);
if (host_port == NULL) {
return NULL;
}
list = ssh_list_new();
if (list == NULL) {
ssh_set_error_oom(session);
SAFE_FREE(host_port);
return NULL;
}
host_port = ssh_session_get_host_port(session);
if (host_port == NULL) {
goto error;
}
rc = ssh_known_hosts_read_entries(host_port,
session->opts.knownhosts,
&entry_list);
if (rc != 0) {
ssh_list_free(entry_list);
ssh_list_free(list);
return NULL;
SAFE_FREE(host_port);
goto error;
}
rc = ssh_known_hosts_read_entries(host_port,
@@ -402,21 +400,16 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
&entry_list);
SAFE_FREE(host_port);
if (rc != 0) {
ssh_list_free(entry_list);
ssh_list_free(list);
return NULL;
goto error;
}
if (entry_list == NULL) {
ssh_list_free(list);
return NULL;
goto error;
}
count = ssh_list_count(entry_list);
if (count == 0) {
ssh_list_free(list);
ssh_list_free(entry_list);
return NULL;
goto error;
}
for (it = ssh_list_get_iterator(entry_list);
@@ -460,6 +453,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
return list;
error:
ssh_list_free(entry_list);
ssh_list_free(list);
return NULL;
}

View File

@@ -701,8 +701,8 @@ int ssh_key_cmp(const ssh_key k1,
ssh_buffer_get_len(k1->cert));
}
if (k1->type == SSH_KEYTYPE_ED25519 ||
k1->type == SSH_KEYTYPE_SK_ED25519) {
if (ssh_key_type_plain(k1->type) == SSH_KEYTYPE_ED25519 ||
ssh_key_type_plain(k1->type) == SSH_KEYTYPE_SK_ED25519) {
return pki_ed25519_key_cmp(k1, k2, what);
}

View File

@@ -1355,7 +1355,7 @@ int pki_key_compare(const ssh_key k1,
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01:
/* ed25519 keys handled globally */
return 0;
return 1;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384:

View File

@@ -782,7 +782,7 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
/* ed25519 keys handled globally */
rc = 0;
rc = 1;
break;
default:
rc = 1;

View File

@@ -495,6 +495,11 @@ static size_t callback_receive_banner(const void *data, size_t len, void *user)
buffer[i] = '\0';
str = strdup(buffer);
if (str == NULL) {
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_set_error_oom(session);
return 0;
}
/* number of bytes read */
processed = i + 1;
session->clientbanner = str;

View File

@@ -373,10 +373,10 @@ if (FUZZ_TESTING)
endif()
add_custom_target(test_memcheck
# FIXME: The threads_pki_rsa test is skipped under valgrind as it times out
# FIXME: The pkd_hello_i1 test is skipped under valgrind as it times out
# Passing suppression file is also stupid so lets go with override here:
# https://stackoverflow.com/a/56116311
COMMAND ${CMAKE_CTEST_COMMAND} -E torture_threads_pki_rsa -E pkd_hello_i1
COMMAND ${CMAKE_CTEST_COMMAND} -E pkd_hello_i1
--output-on-failure --force-new-ctest-process --test-action memcheck
--overwrite MemoryCheckSuppressionFile=${CMAKE_SOURCE_DIR}/tests/valgrind.supp
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")

View File

@@ -53,6 +53,8 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TEST_NONEWLINEONELINE "libssh_test_NoNewLineOneline.tmp"
#define LIBSSH_TEST_RECURSIVE_INCLUDE "libssh_test_recursive_include.tmp"
#define LIBSSH_TESTCONFIG_MATCH_COMPLEX "libssh_test_match_complex.tmp"
#define LIBSSH_TESTCONFIG_LOGLEVEL_MISSING "libssh_test_loglevel_missing.tmp"
#define LIBSSH_TESTCONFIG_JUMP "libssh_test_jump.tmp"
#define LIBSSH_TESTCONFIG_STRING1 \
"User "USERNAME"\nInclude "LIBSSH_TESTCONFIG2"\n\n"
@@ -243,6 +245,26 @@ extern LIBSSH_THREAD int ssh_log_level;
"\tForwardAgent yes\n" \
"\tHostName complex-match\n"
#define LIBSSH_TESTCONFIG_LOGLEVEL_MISSING_STRING "LogLevel\n"
#define LIBSSH_TESTCONFIG_JUMP_STRING \
"# The jump host\n" \
"Host ub-jumphost\n" \
" HostName 1xxxxxx\n" \
" User ubuntu\n" \
" IdentityFile ~/of/temp-libssh.pem\n" \
" Port 23\n" \
" LogLevel DEBUG3\n" \
"\n" \
"# Cisco Router through Jump Host\n" \
"Host cisco-router\n" \
" HostName xx.xxxxxxxxx\n" \
" User username\n" \
" ProxyJump ub-jumphost\n" \
" Port 5555\n" \
" #RequiredRSASize 512\n" \
" PasswordAuthentication yes\n" \
" LogLevel DEBUG3\n"
/**
* @brief helper function loading configuration from either file or string
*/
@@ -293,6 +315,8 @@ static int setup_config_files(void **state)
unlink(LIBSSH_TEST_NONEWLINEEND);
unlink(LIBSSH_TEST_NONEWLINEONELINE);
unlink(LIBSSH_TESTCONFIG_MATCH_COMPLEX);
unlink(LIBSSH_TESTCONFIG_LOGLEVEL_MISSING);
unlink(LIBSSH_TESTCONFIG_JUMP);
torture_write_file(LIBSSH_TESTCONFIG1,
LIBSSH_TESTCONFIG_STRING1);
@@ -361,6 +385,10 @@ static int setup_config_files(void **state)
/* Match complex combinations */
torture_write_file(LIBSSH_TESTCONFIG_MATCH_COMPLEX,
LIBSSH_TESTCONFIG_MATCH_COMPLEX_STRING);
torture_write_file(LIBSSH_TESTCONFIG_LOGLEVEL_MISSING,
LIBSSH_TESTCONFIG_LOGLEVEL_MISSING_STRING);
torture_write_file(LIBSSH_TESTCONFIG_JUMP,
LIBSSH_TESTCONFIG_JUMP_STRING);
return 0;
}
@@ -390,6 +418,8 @@ static int teardown_config_files(void **state)
unlink(LIBSSH_TEST_NONEWLINEEND);
unlink(LIBSSH_TEST_NONEWLINEONELINE);
unlink(LIBSSH_TESTCONFIG_MATCH_COMPLEX);
unlink(LIBSSH_TESTCONFIG_LOGLEVEL_MISSING);
unlink(LIBSSH_TESTCONFIG_JUMP);
return 0;
}
@@ -2617,6 +2647,112 @@ static void torture_config_match_complex(void **state)
ssh_string_free_char(v);
}
/* Missing value to LogLevel configuration option
*/
static void torture_config_loglevel_missing_value(void **state)
{
ssh_session session = *state;
ssh_options_set(session, SSH_OPTIONS_HOST, "Bar");
_parse_config(session, LIBSSH_TESTCONFIG_LOGLEVEL_MISSING, NULL, SSH_OK);
}
static int before_connection(ssh_session jump_session, void *user)
{
char *v = NULL;
int ret;
(void)user;
/* During the connection, we force parsing the same configuration file
* (would be normally parsed automatically during the connection itself)
*/
ret = ssh_config_parse_file(jump_session, LIBSSH_TESTCONFIG_JUMP);
assert_return_code(ret, errno);
/* Test the variable presence */
ret = ssh_options_get(jump_session, SSH_OPTIONS_HOST, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "1xxxxxx");
ssh_string_free_char(v);
ret = ssh_options_get(jump_session, SSH_OPTIONS_USER, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "ubuntu");
ssh_string_free_char(v);
assert_int_equal(jump_session->opts.port, 23);
/* Fail the connection -- we are in unit tests so it would fail anyway */
return 1;
}
static int verify_knownhost(ssh_session jump_session, void *user)
{
(void)jump_session;
(void)user;
return 0;
}
static int authenticate(ssh_session jump_session, void *user)
{
(void)jump_session;
(void)user;
return 0;
}
/* Reproducer for complex proxy jump
*/
static void torture_config_jump(void **state)
{
ssh_session session = *state;
struct ssh_jump_callbacks_struct c = {
.before_connection = before_connection,
.verify_knownhost = verify_knownhost,
.authenticate = authenticate,
};
char *v = NULL;
int ret;
ssh_options_set(session, SSH_OPTIONS_HOST, "cisco-router");
_parse_config(session, LIBSSH_TESTCONFIG_JUMP, NULL, SSH_OK);
/* Test the variable presence */
ret = ssh_options_get(session, SSH_OPTIONS_HOST, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "xx.xxxxxxxxx");
ssh_string_free_char(v);
ret = ssh_options_get(session, SSH_OPTIONS_USER, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "username");
ssh_string_free_char(v);
assert_int_equal(session->opts.port, 5555);
/* At this point, the configuration file is not parsed for the jump host so
* we are getting just the the hostname -- the port and username will get
* pulled during the session connecting to this host */
assert_int_equal(ssh_list_count(session->opts.proxy_jumps), 1);
helper_proxy_jump_check(session->opts.proxy_jumps->root,
"ub-jumphost",
NULL,
NULL);
/* Set up the callbacks -- they should verify we are going to connect to the
* right host */
ret = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c);
assert_ssh_return_code(session, ret);
ret = ssh_connect(session);
assert_ssh_return_code_equal(session, ret, SSH_ERROR);
printf("%s: EOF\n", __func__);
}
int torture_run_tests(void)
{
int rc;
@@ -2713,6 +2849,12 @@ int torture_run_tests(void)
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_match_complex,
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_loglevel_missing_value,
setup,
teardown),
cmocka_unit_test_setup_teardown(torture_config_jump,
setup,
teardown),
};

View File

@@ -367,10 +367,14 @@ static void torture_pki_ecdsa_publickey_from_privatekey(void **state)
static void torture_pki_ecdsa_import_cert_file(void **state)
{
int rc;
ssh_key pubkey = NULL;
ssh_key privkey = NULL;
ssh_key cert = NULL;
enum ssh_keytypes_e type;
enum ssh_keytypes_e type, exp_cert_type;
struct pki_st *test_state = *((struct pki_st **)state);
exp_cert_type = test_state->type + 3;
/* Importing public key as cert should fail */
rc = ssh_pki_import_cert_file(LIBSSH_ECDSA_TESTKEY ".pub", &cert);
assert_int_equal(rc, SSH_ERROR);
@@ -380,13 +384,78 @@ static void torture_pki_ecdsa_import_cert_file(void **state)
assert_int_equal(rc, 0);
assert_non_null(cert);
rc = ssh_pki_import_pubkey_file(LIBSSH_ECDSA_TESTKEY ".pub", &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
type = ssh_key_type(cert);
assert_int_equal(type, test_state->type+3);
assert_int_equal(type, exp_cert_type);
rc = ssh_key_is_public(cert);
assert_int_equal(rc, 1);
/* Import matching private key file and verify the pubkey matches */
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY,
NULL,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
type = ssh_key_type(privkey);
assert_true(type == test_state->type);
/* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(pubkey, NULL);
assert_int_equal(rc, SSH_ERROR);
/* A public key doesn't have a cert, copy should fail. */
assert_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
assert_int_equal(rc, SSH_ERROR);
/* Copying the cert to non-cert keys should work fine. */
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_return_code(rc, errno);
assert_non_null(privkey->cert);
assert_true(privkey->cert_type == exp_cert_type);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 0);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 0);
/* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
/* Generate different key and try to assign it this certificate */
rc = ssh_pki_generate(test_state->type, 256, &privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_int_equal(rc, SSH_ERROR);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 1);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 1);
SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
}
static void torture_pki_ecdsa_publickey_base64(void **state)

View File

@@ -312,6 +312,8 @@ static void torture_pki_ed25519_publickey_from_privatekey(void **state)
static void torture_pki_ed25519_import_cert_file(void **state)
{
int rc;
ssh_key pubkey = NULL;
ssh_key privkey = NULL;
ssh_key cert = NULL;
enum ssh_keytypes_e type;
@@ -323,16 +325,88 @@ static void torture_pki_ed25519_import_cert_file(void **state)
assert_null(cert);
rc = ssh_pki_import_cert_file(LIBSSH_ED25519_TESTKEY "-cert.pub", &cert);
assert_true(rc == 0);
assert_return_code(rc, errno);
assert_non_null(cert);
rc = ssh_pki_import_pubkey_file(LIBSSH_ED25519_TESTKEY ".pub", &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
type = ssh_key_type(cert);
assert_true(type == SSH_KEYTYPE_ED25519_CERT01);
rc = ssh_key_is_public(cert);
assert_true(rc == 1);
assert_int_equal(rc, 1);
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
SSH_KEY_FREE(cert);
SSH_KEY_FREE(pubkey);
skip();
}
/* Import matching private key file and verify the pubkey matches */
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
NULL,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
type = ssh_key_type(privkey);
assert_true(type == SSH_KEYTYPE_ED25519);
/* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(pubkey, NULL);
assert_int_equal(rc, SSH_ERROR);
/* A public key doesn't have a cert, copy should fail. */
assert_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
assert_int_equal(rc, SSH_ERROR);
/* Copying the cert to non-cert keys should work fine. */
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_return_code(rc, errno);
assert_non_null(privkey->cert);
assert_true(privkey->cert_type == SSH_KEYTYPE_ED25519_CERT01);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 0);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 0);
/* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
/* Generate different key and try to assign it this certificate */
rc = ssh_pki_generate(SSH_KEYTYPE_ED25519, 0, &privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_int_equal(rc, SSH_ERROR);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 1);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 1);
SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
}
static void torture_pki_ed25519_publickey_base64(void **state)

View File

@@ -373,6 +373,7 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
ssh_key pubkey = NULL;
ssh_key privkey = NULL;
ssh_key cert = NULL;
enum ssh_keytypes_e type;
(void)state; /* unused */
@@ -389,6 +390,13 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
assert_return_code(rc, errno);
assert_non_null(pubkey);
type = ssh_key_type(cert);
assert_true(type == SSH_KEYTYPE_RSA_CERT01);
rc = ssh_key_is_public(cert);
assert_int_equal(rc, 1);
/* Import matching private key file and verify the pubkey matches */
rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0),
passphrase,
NULL,
@@ -397,6 +405,9 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
assert_return_code(rc, errno);
assert_non_null(privkey);
type = ssh_key_type(privkey);
assert_true(type == SSH_KEYTYPE_RSA);
/* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
assert_int_equal(rc, SSH_ERROR);
@@ -416,6 +427,10 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_return_code(rc, errno);
assert_non_null(privkey->cert);
assert_true(privkey->cert_type == SSH_KEYTYPE_RSA_CERT01);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 0);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 0);
/* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
@@ -437,6 +452,9 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_int_equal(rc, SSH_ERROR);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 1);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 1);
SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);

View File

@@ -58,14 +58,8 @@ static int run_on_threads(void *(*func)(void *))
}
for (i = 0; i < NUM_THREADS; ++i) {
void *p = NULL;
uint64_t *result;
rc = pthread_join(threads[i], &p);
rc = pthread_join(threads[i], NULL);
assert_int_equal(rc, 0);
result = (uint64_t *)p;
assert_null(result);
}
return rc;
@@ -164,7 +158,7 @@ static void *thread_pki_rsa_import_pubkey_file(void *threadid)
SSH_KEY_FREE(pubkey);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_import_pubkey_file(void **state)
@@ -197,8 +191,7 @@ static void *thread_pki_rsa_import_privkey_base64_NULL_key(void *threadid)
NULL,
NULL);
assert_true(rc == -1);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_import_privkey_base64_NULL_key(void **state){
@@ -225,7 +218,8 @@ static void *thread_pki_rsa_import_privkey_base64_NULL_str(void *threadid)
assert_true(rc == -1);
SSH_KEY_FREE(key);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_import_privkey_base64_NULL_str(void **state){
@@ -267,7 +261,7 @@ static void *thread_pki_rsa_import_privkey_base64(void *threadid)
free(key_str);
SSH_KEY_FREE(key);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_import_privkey_base64(void **state)
@@ -310,7 +304,8 @@ static void *thread_pki_rsa_publickey_from_privatekey(void *threadid)
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_publickey_from_privatekey(void **state)
@@ -383,7 +378,8 @@ static void *thread_pki_rsa_copy_cert_to_privkey(void *threadid)
SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_copy_cert_to_privkey(void **state)
@@ -416,7 +412,8 @@ static void *thread_pki_rsa_import_cert_file(void *threadid)
assert_true(rc == 1);
SSH_KEY_FREE(cert);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_import_cert_file(void **state)
@@ -467,7 +464,8 @@ static void *thread_pki_rsa_publickey_base64(void *threadid)
free(b64_key);
free(key_buf);
SSH_KEY_FREE(key);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_publickey_base64(void **state)
@@ -531,7 +529,8 @@ static void *thread_pki_rsa_duplicate_key(void *threadid)
SSH_KEY_FREE(privkey_dup);
SSH_STRING_FREE_CHAR(b64_key);
SSH_STRING_FREE_CHAR(b64_key_gen);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_duplicate_key(void **state)
@@ -614,7 +613,8 @@ static void *thread_pki_rsa_generate_key(void *threadid)
SSH_KEY_FREE(pubkey);
ssh_free(session);
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_generate_key(void **state)
@@ -672,7 +672,8 @@ static void *thread_pki_rsa_import_privkey_base64_passphrase(void *threadid)
assert_true(rc == -1);
SSH_KEY_FREE(key);
#endif
pthread_exit(NULL);
return NULL;
}
static void torture_pki_rsa_import_privkey_base64_passphrase(void **state)
@@ -723,14 +724,8 @@ static void torture_mixed(void **state)
for (f = 0; f < NUM_TESTS; f++) {
for (i = 0; i < NUM_THREADS; ++i) {
void *p = NULL;
uint64_t *result = NULL;
rc = pthread_join(threads[f][i], &p);
rc = pthread_join(threads[f][i], NULL);
assert_int_equal(rc, 0);
result = (uint64_t *)p;
assert_null(result);
}
}
}

View File

@@ -140,6 +140,40 @@
fun:FIPS_mode_set
fun:OPENSSL_init_library
}
{
Threads + Failed PEM decoder do not play well openssl/openssl#29077
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:CRYPTO_malloc
fun:CRYPTO_zalloc
fun:ossl_rcu_read_lock
fun:module_find
fun:module_run
fun:CONF_modules_load
fun:CONF_modules_load_file_ex
fun:ossl_config_int
fun:ossl_config_int
fun:ossl_init_config
fun:ossl_init_config_ossl_
fun:__pthread_once_slow.isra.0
fun:pthread_once@@GLIBC_2.34
fun:CRYPTO_THREAD_run_once
fun:OPENSSL_init_crypto
fun:ossl_provider_doall_activated
fun:ossl_algorithm_do_all
fun:ossl_method_construct.constprop.0
fun:inner_evp_generic_fetch.constprop.0
fun:evp_generic_do_all
fun:EVP_KEYMGMT_do_all_provided
fun:ossl_decoder_ctx_setup_for_pkey
fun:OSSL_DECODER_CTX_new_for_pkey
fun:pem_read_bio_key_decoder
fun:pem_read_bio_key
fun:PEM_read_bio_PrivateKey_ex
fun:pki_private_key_from_base64
...
}
# Cmocka
{
This looks like leak from cmocka when the forked server is not properly terminated
@@ -207,65 +241,12 @@
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
fun:malloc
fun:strdup
fun:_dl_load_cache_lookup
fun:_dl_map_object
fun:dl_open_worker_begin
fun:_dl_catch_exception
fun:dl_open_worker
fun:_dl_catch_exception
fun:_dl_open
fun:do_dlopen
fun:_dl_catch_exception
fun:_dl_catch_error
fun:dlerror_run
fun:__libc_dlopen_mode
fun:module_load
fun:__nss_module_get_function
fun:getaddrinfo
...
fun:krb5_sname_to_principal
...
fun:gss_init_sec_context
fun:ssh_packet_userauth_gssapi_response
fun:ssh_packet_process
fun:ssh_packet_socket_callback
fun:ssh_socket_pollcallback
fun:ssh_poll_ctx_dopoll
fun:ssh_handle_packets
fun:ssh_handle_packets_termination
fun:ssh_userauth_get_response
fun:ssh_userauth_gssapi
fun:torture_gssapi_auth_server_identity
...
fun:_cmocka_run_group_tests
fun:torture_run_tests
fun:main
}
{
Reachable memory from getaddrinfo
Memcheck:Leak
match-leak-kinds: reachable
...
fun:__nss_module_get_function
...
fun:getaddrinfo
...
fun:krb5_sname_to_principal
...
fun:gss_init_sec_context
fun:ssh_packet_userauth_gssapi_response
fun:ssh_packet_process
fun:ssh_packet_socket_callback
fun:ssh_socket_pollcallback
fun:ssh_poll_ctx_dopoll
fun:ssh_handle_packets
fun:ssh_handle_packets_termination
fun:ssh_userauth_get_response
fun:ssh_userauth_gssapi
fun:torture_gssapi_auth_server_identity
fun:torture_*
...
fun:_cmocka_run_group_tests
fun:torture_run_tests
@@ -290,13 +271,11 @@
fun:malloc
...
fun:krb5_gss_save_error_string
fun:UnknownInlinedFun
...
fun:acquire_cred_context.isra.0
fun:acquire_cred_from.isra.0
fun:gss_add_cred_from
fun:gss_acquire_cred_from
...
fun:gss_acquire_cred
}
{
error string from gss init sec context
@@ -305,7 +284,7 @@
fun:malloc
...
fun:krb5_gss_save_error_string
fun:UnknownInlinedFun
...
fun:krb5_gss_init_sec_context_ext
fun:krb5_gss_init_sec_context
fun:gss_init_sec_context