#include "config.h"
#define LIBSSH_STATIC
#include "torture.h"
#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include <sys/types.h>
#include <pwd.h>
#include <errno.h>
#define BUFLEN 4096
static char buffer[BUFLEN];
static int sshd_setup(void **state)
{
torture_setup_sshd_server(state, false);
return 0;
}
static int sshd_teardown(void **state) {
torture_teardown_sshd_server(state);
return 0;
}
static int session_setup(void **state)
{
struct torture_state *s = *state;
struct passwd *pwd;
int rc;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = torture_ssh_session(s,
TORTURE_SSH_SERVER,
NULL,
TORTURE_SSH_USER_ALICE,
NULL);
assert_non_null(s->ssh.session);
return 0;
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static void torture_channel_read_error(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
int rc;
int fd;
int i;
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, "hexdump -C /dev/urandom");
assert_ssh_return_code(session, rc);
fd = ssh_get_fd(session);
assert_true(fd > 2);
rc = write(fd, "AAAA", 4);
assert_int_equal(rc, 4);
for (i=0;i<20;++i){
rc = ssh_channel_read(channel,buffer,sizeof(buffer),0);
if (rc == SSH_ERROR)
break;
}
#if OPENSSH_VERSION_MAJOR == 6 && OPENSSH_VERSION_MINOR >= 7
assert_ssh_return_code(session, rc);
#else
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
#endif
ssh_channel_free(channel);
}
static void torture_channel_poll_timeout_valid(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
int rc;
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, "echo -n ABCD");
assert_ssh_return_code(session, rc);
rc = ssh_channel_poll_timeout(channel, 500, 0);
assert_int_equal(rc, strlen("ABCD"));
}
static void torture_channel_poll_timeout(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
int rc;
int fd;
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
fd = ssh_get_fd(session);
assert_true(fd > 2);
rc = ssh_channel_poll_timeout(channel, 500, 0);
assert_int_equal(rc, SSH_OK);
rc = write(fd, "AAAA", 4);
assert_int_equal(rc, 4);
rc = ssh_channel_poll_timeout(channel, 500, 0);
assert_int_equal(rc, SSH_ERROR);
ssh_channel_free(channel);
}
static void torture_max_sessions(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
char max_session_config[32] = {0};
#define MAX_CHANNELS 10
ssh_channel channels[MAX_CHANNELS + 1];
size_t i;
int rc;
snprintf(max_session_config,
sizeof(max_session_config),
"MaxSessions %u",
MAX_CHANNELS);
torture_update_sshd_config(state, max_session_config);
for (i = 0; i < MAX_CHANNELS; i++) {
channels[i] = ssh_channel_new(session);
assert_non_null(channels[i]);
rc = ssh_channel_open_session(channels[i]);
assert_ssh_return_code(session, rc);
}
channels[i] = ssh_channel_new(session);
assert_non_null(channels[i]);
rc = ssh_channel_open_session(channels[i]);
assert_int_equal(rc, SSH_ERROR);
ssh_channel_free(channels[i]);
for (i = 0; i < MAX_CHANNELS; i++) {
ssh_channel_close(channels[i]);
ssh_channel_free(channels[i]);
}
#undef MAX_CHANNELS
}
static void torture_no_more_sessions(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channels[2];
int rc;
channels[0] = ssh_channel_new(session);
assert_non_null(channels[0]);
rc = ssh_channel_open_session(channels[0]);
assert_ssh_return_code(session, rc);
rc = ssh_request_no_more_sessions(session);
assert_ssh_return_code(session, rc);
channels[1] = ssh_channel_new(session);
assert_non_null(channels[1]);
rc = ssh_channel_open_session(channels[1]);
assert_int_equal(rc, SSH_ERROR);
ssh_channel_free(channels[1]);
ssh_channel_close(channels[0]);
ssh_channel_free(channels[0]);
}
static void torture_channel_delayed_close(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
char request[256];
char buff[256] = {0};
int rc;
int fd;
snprintf(request, 256,
"dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
fd = ssh_get_fd(session);
assert_true(fd > 2);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
do {
rc = ssh_channel_read(channel, buff, 256, 0);
} while(rc > 0);
assert_ssh_return_code(session, rc);
rc = ssh_channel_poll_timeout(channel, 500, 0);
assert_int_equal(rc, SSH_EOF);
ssh_channel_free(channel);
}
static void torture_freed_channel_poll(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
char request[256];
int rc;
snprintf(request, 256,
"dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
ssh_channel_free(channel);
rc = ssh_channel_poll(channel, 0);
assert_int_equal(rc, SSH_ERROR);
}
static void torture_freed_channel_poll_timeout(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
bool channel_freed = false;
char request[256];
char buff[256] = {0};
int rc;
snprintf(request, 256,
"dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
do {
rc = ssh_channel_read(channel, buff, 256, 0);
} while(rc > 0);
assert_ssh_return_code(session, rc);
if ((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
(channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)) {
channel_freed = true;
}
ssh_channel_free(channel);
if (!channel_freed) {
rc = ssh_channel_poll_timeout(channel, 500, 0);
assert_int_equal(rc, SSH_ERROR);
}
}
static void torture_freed_channel_read_nonblocking(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
char request[256];
char buff[256] = {0};
int rc;
snprintf(request, 256,
"dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
ssh_channel_free(channel);
rc = ssh_channel_read_nonblocking(channel, buff, 256, 0);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
}
static void torture_channel_exit_status(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel = NULL;
char request[256];
uint32_t exit_status = (uint32_t)-1;
int rc;
rc = snprintf(request, sizeof(request), "true");
assert_return_code(rc, errno);
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
exit_status = ssh_channel_get_exit_state(channel, &exit_status, NULL, NULL);
assert_ssh_return_code(session, rc);
assert_int_equal(exit_status, 0);
}
static void torture_channel_exit_signal(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel = NULL;
char request[256];
uint32_t exit_status = (uint32_t)-1;
char *exit_signal = NULL;
int core_dumped = false;
int rc;
rc = snprintf(request, sizeof(request), "cat");
assert_return_code(rc, errno);
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_send_signal(channel, "TERM");
assert_ssh_return_code(session, rc);
rc = ssh_channel_get_exit_state(channel,
&exit_status,
&exit_signal,
&core_dumped);
assert_ssh_return_code(session, rc);
assert_int_equal(exit_status, (uint32_t)-1);
assert_string_equal(exit_signal, "TERM");
SAFE_FREE(exit_signal);
}
static void torture_freed_channel_get_exit_status(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
bool channel_freed = false;
char request[256];
char buff[256] = {0};
int rc;
snprintf(request, 256,
"dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, request);
assert_ssh_return_code(session, rc);
do {
rc = ssh_channel_read(channel, buff, 256, 0);
} while(rc > 0);
assert_ssh_return_code(session, rc);
if ((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
(channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)) {
channel_freed = true;
}
SSH_CHANNEL_FREE(channel);
if (!channel_freed) {
rc = ssh_channel_get_exit_status(channel);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
}
}
static void
torture_channel_read_stderr(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel channel;
int rc;
channel = ssh_channel_new(session);
assert_non_null(channel);
rc = ssh_channel_open_session(channel);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(channel, "echo -n ABCD >&2");
assert_ssh_return_code(session, rc);
rc = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
assert_int_equal(rc, 0);
rc = ssh_channel_poll(channel, 1);
assert_int_equal(rc, strlen("ABCD"));
rc = ssh_channel_read(channel, buffer, sizeof(buffer), 1);
assert_int_equal(rc, strlen("ABCD"));
buffer[rc] = '\0';
assert_string_equal("ABCD", buffer);
ssh_channel_free(channel);
}
static void torture_pubkey_hash(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
char *hash = NULL;
char *hexa = NULL;
int rc = 0;
rc = ssh_get_pubkey_hash(session, NULL);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_get_pubkey_hash(NULL, (unsigned char **)&hash);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_get_pubkey_hash(session, (unsigned char **)&hash);
if (ssh_fips_mode()) {
assert_int_equal(rc, SSH_ERROR);
} else {
assert_int_equal(rc, MD5_DIGEST_LEN);
hexa = ssh_get_hexa((unsigned char *)hash, rc);
SSH_STRING_FREE_CHAR(hash);
assert_string_equal(hexa,
"ee:80:7f:61:f9:d5:be:f1:96:86:cc:96:7a:db:7a:7b");
SSH_STRING_FREE_CHAR(hexa);
}
}
static void torture_openssh_banner_version(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int openssh_version = ssh_get_openssh_version(session);
int cmake_openssh_version = SSH_VERSION_INT(OPENSSH_VERSION_MAJOR, OPENSSH_VERSION_MINOR, 0);
assert_int_equal(openssh_version, cmake_openssh_version);
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_channel_read_error,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_poll_timeout_valid,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_poll_timeout,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_max_sessions,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_no_more_sessions,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_delayed_close,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_freed_channel_poll,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_freed_channel_poll_timeout,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_freed_channel_read_nonblocking,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_exit_status,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_exit_signal,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_freed_channel_get_exit_status,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_read_stderr,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_pubkey_hash,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_openssh_banner_version,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
ssh_finalize();
return rc;
}