1
1
libssh/tests/server/torture_server_config.c
Anderson Toshiyuki Sasaki 7aad964cef tests: Add test case for T191
Add a test case to verify that the server returns the correct signature
when it negotiated an RSA signature algorithm for the host key different
from the one it prefers (e.g. when the client prefers ssh-rsa over
rsa-sha2-256 and rsa-sha2-512).

Fixes T240

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2020-07-16 12:22:11 +02:00

833 строки
22 KiB
C

/*
* This file is part of the SSH Library
*
* Copyright (c) 2019 by Red Hat, Inc.
*
* Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#define LIBSSH_STATIC
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
#include "torture.h"
#include "torture_key.h"
#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/token.h"
#include "test_server.h"
#include "default_cb.h"
const char template[] = "temp_dir_XXXXXX";
struct test_server_st {
struct torture_state *state;
char *cwd;
char *temp_dir;
char ed25519_hostkey[1024];
char rsa_hostkey[1024];
char ecdsa_521_hostkey[1024];
char ecdsa_384_hostkey[1024];
char ecdsa_256_hostkey[1024];
#ifdef HAVE_DSA
char dsa_hostkey[1024];
#endif /* HAVE_DSA */
};
static int setup_files(void **state)
{
struct test_server_st *tss;
struct torture_state *s;
char sshd_path[1024];
int rc;
tss = (struct test_server_st*)calloc(1, sizeof(struct test_server_st));
assert_non_null(tss);
torture_setup_socket_dir((void **)&s);
assert_non_null(s->socket_dir);
/* Set the default interface for the server */
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
setenv("PAM_WRAPPER", "1", 1);
snprintf(sshd_path,
sizeof(sshd_path),
"%s/sshd",
s->socket_dir);
rc = mkdir(sshd_path, 0755);
assert_return_code(rc, errno);
snprintf(tss->rsa_hostkey,
sizeof(tss->rsa_hostkey),
"%s/sshd/ssh_host_rsa_key",
s->socket_dir);
torture_write_file(tss->rsa_hostkey, torture_get_testkey(SSH_KEYTYPE_RSA, 0));
snprintf(tss->ecdsa_521_hostkey,
sizeof(tss->ecdsa_521_hostkey),
"%s/sshd/ssh_host_ecdsa_521_key",
s->socket_dir);
torture_write_file(tss->ecdsa_521_hostkey,
torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
snprintf(tss->ecdsa_384_hostkey,
sizeof(tss->ecdsa_384_hostkey),
"%s/sshd/ssh_host_ecdsa_384_key",
s->socket_dir);
torture_write_file(tss->ecdsa_384_hostkey,
torture_get_testkey(SSH_KEYTYPE_ECDSA_P384, 0));
snprintf(tss->ecdsa_256_hostkey,
sizeof(tss->ecdsa_256_hostkey),
"%s/sshd/ssh_host_ecdsa_256_key",
s->socket_dir);
torture_write_file(tss->ecdsa_256_hostkey,
torture_get_testkey(SSH_KEYTYPE_ECDSA_P256, 0));
if (!ssh_fips_mode()) {
snprintf(tss->ed25519_hostkey,
sizeof(tss->ed25519_hostkey),
"%s/sshd/ssh_host_ed25519_key",
s->socket_dir);
torture_write_file(tss->ed25519_hostkey,
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
#ifdef HAVE_DSA
snprintf(tss->dsa_hostkey,
sizeof(tss->dsa_hostkey),
"%s/sshd/ssh_host_dsa_key",
s->socket_dir);
torture_write_file(tss->dsa_hostkey,
torture_get_testkey(SSH_KEYTYPE_DSS, 0));
#endif /* HAVE_DSA */
}
tss->state = s;
*state = tss;
return 0;
}
static int teardown_files(void **state)
{
struct torture_state *s;
struct test_server_st *tss;
tss = *state;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
torture_teardown_socket_dir((void **)&s);
SAFE_FREE(tss);
return 0;
}
static int setup_temp_dir(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
char *cwd = NULL;
char *tmp_dir = NULL;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
cwd = torture_get_current_working_dir();
assert_non_null(cwd);
tmp_dir = torture_make_temp_dir(template);
assert_non_null(tmp_dir);
tss->cwd = cwd;
tss->temp_dir = tmp_dir;
return 0;
}
static int teardown_temp_dir(void **state)
{
struct test_server_st *tss = *state;
int rc;
assert_non_null(tss);
rc = torture_change_dir(tss->cwd);
assert_int_equal(rc, 0);
rc = torture_rmdirs(tss->temp_dir);
assert_int_equal(rc, 0);
SAFE_FREE(tss->temp_dir);
SAFE_FREE(tss->cwd);
return 0;
}
static int start_server(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
/* Start the server using the default values */
torture_setup_libssh_server((void **)&s, "./test_server/test_server");
assert_non_null(s);
return 0;
}
static int stop_server(void **state)
{
struct torture_state *s;
struct test_server_st *tss;
int rc;
tss = *state;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
rc = torture_terminate_process(s->srv_pidfile);
assert_return_code(rc, errno);
unlink(s->srv_pidfile);
return 0;
}
static int session_setup(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
int verbosity = torture_libssh_verbosity();
const char *compat_hostkeys = ssh_kex_get_supported_method(SSH_HOSTKEYS);
struct passwd *pwd;
bool b = false;
int rc;
assert_non_null(tss);
/* Make sure we do not test the agent */
unsetenv("SSH_AUTH_SOCK");
s = tss->state;
assert_non_null(s);
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
assert_ssh_return_code(s->ssh.session, rc);
/* Make sure no other configuration options from system will get used */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOSTKEYS, compat_hostkeys);
assert_ssh_return_code(s->ssh.session, rc);
return 0;
}
static int session_teardown(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static int try_config_content(void **state, const char *config_content,
bool parse_global)
{
struct test_server_st *tss = *state;
struct torture_state *s;
int rc;
ssh_session session;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
assert_non_null(s->srv_config);
if (parse_global) {
fprintf(stderr, "Using system-wide configuration\n");
} else {
/* The string is duplicated to not break the cleanup on error */
s->srv_additional_config = strdup("-g");
}
torture_write_file(s->srv_config, config_content);
fprintf(stderr, "Config file %s content: \n\n%s\n", s->srv_config,
config_content);
rc = start_server(state);
assert_int_equal(rc, 0);
rc = session_setup(state);
assert_int_equal(rc, 0);
session = s->ssh.session;
assert_non_null(session);
/* Authenticate as alice with bob */
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_ssh_return_code(session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_none(session,NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
rc = session_teardown(state);
assert_int_equal(rc, 0);
rc = stop_server(state);
assert_int_equal(rc, 0);
SAFE_FREE(s->srv_additional_config);
return 0;
}
static char *hostkey_files[6] = {0};
static size_t setup_hostkey_files(struct test_server_st *tss)
{
size_t num_hostkey_files = 1;
hostkey_files[0] = tss->rsa_hostkey;
#ifdef TEST_ALL_CRYPTO_COMBINATIONS
hostkey_files[1] = tss->ecdsa_256_hostkey;
hostkey_files[2] = tss->ecdsa_384_hostkey;
hostkey_files[3] = tss->ecdsa_521_hostkey;
num_hostkey_files = 4;
if (!ssh_fips_mode()) {
hostkey_files[4] = tss->ed25519_hostkey;
num_hostkey_files++;
#ifdef HAVE_DSA
hostkey_files[5] = tss->dsa_hostkey;
num_hostkey_files++;
#endif
}
#endif /* TEST_ALL_CRYPTO_COMBINATIONS */
return num_hostkey_files;
}
static void torture_server_config_hostkey(void **state)
{
struct test_server_st *tss = *state;
size_t i, num_hostkey_files;
char config_content[4096];
int rc;
assert_non_null(tss);
num_hostkey_files = setup_hostkey_files(tss);
for (i = 0; i < num_hostkey_files; i++) {
snprintf(config_content,
sizeof(config_content),
"HostKey %s\n",
hostkey_files[i]);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
}
}
static void torture_server_config_ciphers(void **state)
{
struct test_server_st *tss = *state;
size_t i, j, num_hostkey_files = 1;
char config_content[4096];
const char *ciphers;
struct ssh_tokens_st *tokens;
int rc;
assert_non_null(tss);
num_hostkey_files = setup_hostkey_files(tss);
if (ssh_fips_mode()) {
ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_S_C);
assert_non_null(ciphers);
} else {
ciphers = ssh_kex_get_default_methods(SSH_CRYPT_S_C);
assert_non_null(ciphers);
}
tokens = ssh_tokenize(ciphers, ',');
assert_non_null(tokens);
for (i = 0; i < num_hostkey_files; i++) {
/* Try setting all default algorithms */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nCiphers %s\n",
hostkey_files[i], ciphers);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* Try each algorithm individually */
j = 0;
while(tokens->tokens[j] != NULL) {
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nCiphers %s\n",
hostkey_files[i], tokens->tokens[j]);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
j++;
}
}
ssh_tokens_free(tokens);
}
static void torture_server_config_macs(void **state)
{
struct test_server_st *tss = *state;
size_t i, j, num_hostkey_files = 1;
char config_content[4096];
const char *macs;
struct ssh_tokens_st *tokens;
int rc;
assert_non_null(tss);
num_hostkey_files = setup_hostkey_files(tss);
if (ssh_fips_mode()) {
macs = ssh_kex_get_fips_methods(SSH_MAC_S_C);
assert_non_null(macs);
} else {
macs = ssh_kex_get_default_methods(SSH_MAC_S_C);
assert_non_null(macs);
}
tokens = ssh_tokenize(macs, ',');
assert_non_null(tokens);
for (i = 0; i < num_hostkey_files; i++) {
/* Try setting all default algorithms */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nMACs %s\n",
hostkey_files[i], macs);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* Try each algorithm individually */
j = 0;
while(tokens->tokens[j] != NULL) {
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nMACs %s\n",
hostkey_files[i], tokens->tokens[j]);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
j++;
}
}
ssh_tokens_free(tokens);
}
static void torture_server_config_kex(void **state)
{
struct test_server_st *tss = *state;
size_t i, j, num_hostkey_files = 1;
char config_content[4096];
const char *kex;
struct ssh_tokens_st *tokens;
int rc;
assert_non_null(tss);
num_hostkey_files = setup_hostkey_files(tss);
if (ssh_fips_mode()) {
kex = ssh_kex_get_fips_methods(SSH_KEX);
assert_non_null(kex);
} else {
kex = ssh_kex_get_default_methods(SSH_KEX);
assert_non_null(kex);
}
tokens = ssh_tokenize(kex, ',');
assert_non_null(tokens);
for (i = 0; i < num_hostkey_files; i++) {
/* Try setting all default algorithms */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nKexAlgorithms %s\n",
hostkey_files[i], kex);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* Try each algorithm individually */
j = 0;
while(tokens->tokens[j] != NULL) {
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nKexAlgorithms %s\n",
hostkey_files[i], tokens->tokens[j]);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
j++;
}
}
ssh_tokens_free(tokens);
}
static void torture_server_config_hostkey_algorithms(void **state)
{
struct test_server_st *tss = *state;
size_t i, num_hostkey_files = 5;
char config_content[4096];
const char *allowed;
int rc;
assert_non_null(tss);
num_hostkey_files = setup_hostkey_files(tss);
if (ssh_fips_mode()) {
allowed = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
assert_non_null(allowed);
} else {
allowed = ssh_kex_get_default_methods(SSH_HOSTKEYS);
assert_non_null(allowed);
}
for (i = 0; i < num_hostkey_files; i++) {
/* Should work with all allowed */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostKeyAlgorithms %s\n",
hostkey_files[i], allowed);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
}
/* Should work with matching hostkey and allowed algorithm */
if (!ssh_fips_mode()) {
/* ed25519 */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->ed25519_hostkey, "ssh-ed25519");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* ssh-rsa */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->rsa_hostkey, "ssh-rsa");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
}
/* rsa-sha2-256 */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->rsa_hostkey, "rsa-sha2-256");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* ssh-sha2-512 */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->rsa_hostkey, "rsa-sha2-512");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* ecdsa-sha2-nistp256 */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->ecdsa_256_hostkey, "ecdsa-sha2-nistp256");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* ecdsa-sha2-nistp384 */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->ecdsa_384_hostkey, "ecdsa-sha2-nistp384");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
/* ecdsa-sha2-nistp521 */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->ecdsa_521_hostkey, "ecdsa-sha2-nistp521");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
#ifdef HAVE_DSA
if (!ssh_fips_mode()) {
/* ssh-dss */
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->dsa_hostkey, "ssh-dss");
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
}
#endif
}
static void torture_server_config_unknown(void **state)
{
struct test_server_st *tss = *state;
char config_content[4096];
int rc;
assert_non_null(tss);
assert_non_null(tss->rsa_hostkey);
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nUnknownOption unknown-value1,unknown-value2\n",
tss->rsa_hostkey);
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
}
/*
* Check that the server returns the correct signature when the negotiated host
* key is RSA but the signature algorithm is not the server's preferred
* algorithm (e.g. when the client prefers ssh-rsa over rsa-sha2-256 or
* rsa-sha2-512).
*
* Related: T191, T240
*/
static void torture_server_config_rsa_hostkey_order(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s = NULL;
char config_content[4096];
size_t num_hostkey_files;
const char *allowed = NULL;
ssh_session session = NULL;
int rc;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
/* Prepare key files */
num_hostkey_files = setup_hostkey_files(tss);
assert_true(num_hostkey_files > 0);
/* Create the server configuration file */
if (ssh_fips_mode()) {
allowed = "rsa-sha2-512,rsa-sha2-256";
} else {
allowed = "rsa-sha2-256,ssh-rsa";
}
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nHostkeyAlgorithms %s\n",
tss->rsa_hostkey, allowed);
assert_non_null(s->srv_config);
torture_write_file(s->srv_config, config_content);
fprintf(stderr, "Config file %s content: \n\n%s\n", s->srv_config,
config_content);
fflush(stderr);
/* Start server */
rc = start_server(state);
assert_int_equal(rc, 0);
/* Setup session */
rc = session_setup(state);
assert_int_equal(rc, 0);
session = s->ssh.session;
assert_non_null(session);
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
/* Set client order of preference different from the server */
if (ssh_fips_mode()) {
/* Set the host keys with rsa-sha2-256 before rsa-sha2-512 */
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
"rsa-sha2-256,rsa-sha2-512");
assert_int_equal(rc, SSH_OK);
} else {
/* Set the host keys with ssh-rsa before rsa-sha2-256 */
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
"ssh-rsa,rsa-sha2-256");
assert_int_equal(rc, SSH_OK);
}
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_none(session, NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
assert_ssh_return_code(session, rc);
rc = session_teardown(state);
assert_int_equal(rc, 0);
rc = stop_server(state);
assert_int_equal(rc, 0);
SAFE_FREE(s->srv_additional_config);
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_server_config_hostkey,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_server_config_ciphers,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_server_config_macs,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_server_config_kex,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_server_config_hostkey_algorithms,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_server_config_unknown,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_server_config_rsa_hostkey_order,
setup_temp_dir, teardown_temp_dir),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests,
setup_files,
teardown_files);
ssh_finalize();
return rc;
}