1
1
The server can be configured through command line options or by
providing a state structure with the desired values set.

Currently supports only password based authentication.

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Этот коммит содержится в:
Anderson Toshiyuki Sasaki 2018-11-07 17:11:04 +01:00 коммит произвёл Andreas Schneider
родитель e91e221d02
Коммит 37262b98ef
9 изменённых файлов: 2440 добавлений и 0 удалений

Просмотреть файл

@ -148,6 +148,7 @@ endif (WITH_BENCHMARKS)
if (WITH_SERVER AND SERVER_TESTING)
add_subdirectory(pkd)
add_subdirectory(server)
endif (WITH_SERVER AND SERVER_TESTING)
if (FUZZ_TESTING)

45
tests/server/CMakeLists.txt Обычный файл
Просмотреть файл

@ -0,0 +1,45 @@
project(servertests C)
if (WITH_SERVER AND UNIX AND NOT WIN32)
find_package(socket_wrapper)
add_subdirectory(test_server)
set(LIBSSH_SERVER_TESTS
torture_server
)
include_directories(
${LIBSSH_PUBLIC_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}
test_server
)
if (ARGP_INCLUDE_DIR)
include_directories(${ARGP_INCLUDE_DIR})
endif ()
foreach(_SRV_TEST ${LIBSSH_SERVER_TESTS})
add_cmocka_test(${_SRV_TEST}
SOURCES ${_SRV_TEST}.c
COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS}
LINK_LIBRARIES ${TORTURE_LIBRARY} testserver util
)
if (OSX)
set_property(
TEST
${_SRV_TEST}
PROPERTY
ENVIRONMENT DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${SOCKET_WRAPPER_LIBRARY})
else ()
set_property(
TEST
${_SRV_TEST}
PROPERTY
ENVIRONMENT ${TORTURE_ENVIRONMENT})
endif()
endforeach()
endif (WITH_SERVER AND UNIX AND NOT WIN32)

38
tests/server/test_server/CMakeLists.txt Обычный файл
Просмотреть файл

@ -0,0 +1,38 @@
project(test_server C)
if (WITH_SERVER AND UNIX AND NOT WIN32)
find_package(socket_wrapper)
set(server_SRCS
main.c
)
add_library(testserver STATIC
test_server.c
default_cb.c)
set(LIBSSH_SERVER_TESTS
# torture_server_kbdint
)
include_directories(
${LIBSSH_PUBLIC_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}
)
if (ARGP_INCLUDE_DIR)
include_directories(${ARGP_INCLUDE_DIR})
endif ()
if (UNIX AND NOT WIN32)
add_executable(test_server ${server_SRCS})
target_compile_options(test_server PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
target_link_libraries(test_server
${LIBSSH_SHARED_LIBRARY}
${ARGP_LIBRARY}
util
testserver)
endif ()
endif (WITH_SERVER AND UNIX AND NOT WIN32)

880
tests/server/test_server/default_cb.c Обычный файл
Просмотреть файл

@ -0,0 +1,880 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 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"
#include "test_server.h"
#include "default_cb.h"
#include <libssh/callbacks.h>
#include <libssh/server.h>
#include <libssh/priv.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <poll.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
#endif
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
#ifdef HAVE_UTMP_H
#include <utmp.h>
#endif
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
/* TODO implement proper pam authentication cb */
int auth_password_cb(UNUSED_PARAM(ssh_session session),
const char *user,
const char *password,
void *userdata)
{
bool known_user = false;
bool valid_password = false;
struct session_data_st *sdata;
sdata = (struct session_data_st *)userdata;
if (sdata == NULL) {
fprintf(stderr, "Error: NULL userdata\n");
goto null_userdata;
}
if (sdata->username == NULL) {
fprintf(stderr, "Error: expected username not set\n");
goto denied;
}
if (sdata->password == NULL) {
fprintf(stderr, "Error: expected password not set\n");
goto denied;
}
printf("Password authentication\n");
known_user = !(strcmp(user, sdata->username));
valid_password = !(strcmp(password, sdata->password));
if (known_user && valid_password) {
sdata->authenticated = 1;
sdata->auth_attempts = 0;
printf("Authenticated\n");
return SSH_AUTH_SUCCESS;
}
denied:
sdata->auth_attempts++;
null_userdata:
return SSH_AUTH_DENIED;
}
#if WITH_GSSAPI
int auth_gssapi_mic_cb(ssh_session session,
UNUSED_PARAM(const char *user),
UNUSED_PARAM(const char *principal),
void *userdata)
{
ssh_gssapi_creds creds;
struct session_data_st *sdata;
sdata = (struct session_data_st *)userdata;
if (sdata == NULL) {
fprintf(stderr, "Error: NULL userdata\n");
goto null_userdata;
}
printf("GSSAPI authentication\n");
creds = ssh_gssapi_get_creds(session);
if (creds != NULL) {
printf("Received some gssapi credentials\n");
} else {
printf("Not received any forwardable creds\n");
goto denied;
}
printf("Authenticated\n");
sdata->authenticated = 1;
sdata->auth_attempts = 0;
return SSH_AUTH_SUCCESS;
denied:
sdata->auth_attempts++;
null_userdata:
return SSH_AUTH_DENIED;
}
#endif
int channel_data_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
void *data,
uint32_t len,
UNUSED_PARAM(int is_stderr),
void *userdata)
{
struct channel_data_st *cdata;
int rc;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) {
rc = SSH_OK;
goto end;
}
rc = write(cdata->child_stdin, (char *) data, len);
end:
return rc;
}
void channel_eof_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
void *userdata)
{
struct channel_data_st *cdata;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
goto end;
}
end:
return;
}
void channel_close_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
void *userdata)
{
struct channel_data_st *cdata;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
goto end;
}
end:
return;
}
void channel_signal_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(const char *signal),
void *userdata)
{
struct channel_data_st *cdata;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
goto end;
}
end:
return;
}
void channel_exit_status_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(int exit_status),
void *userdata)
{
struct channel_data_st *cdata;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
goto end;
}
end:
return;
}
void channel_exit_signal_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(const char *signal),
UNUSED_PARAM(int core),
UNUSED_PARAM(const char *errmsg),
UNUSED_PARAM(const char *lang),
void *userdata)
{
struct channel_data_st *cdata;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
goto end;
}
end:
return;
}
int channel_pty_request_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(const char *term),
int cols,
int rows,
int py,
int px,
void *userdata)
{
struct channel_data_st *cdata;
int rc;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
cdata->winsize->ws_row = rows;
cdata->winsize->ws_col = cols;
cdata->winsize->ws_xpixel = px;
cdata->winsize->ws_ypixel = py;
rc = openpty(&cdata->pty_master,
&cdata->pty_slave,
NULL,
NULL,
cdata->winsize);
if (rc != 0) {
fprintf(stderr, "Failed to open pty\n");
rc = SSH_ERROR;
goto end;
}
rc = SSH_OK;
end:
return rc;
}
int channel_pty_resize_cb(ssh_session session,
ssh_channel channel,
int cols,
int rows,
int py,
int px,
void *userdata)
{
struct channel_data_st *cdata;
int rc;
(void) session;
(void) channel;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
cdata->winsize->ws_row = rows;
cdata->winsize->ws_col = cols;
cdata->winsize->ws_xpixel = px;
cdata->winsize->ws_ypixel = py;
if (cdata->pty_master != -1) {
rc = ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize);
goto end;
}
rc = SSH_ERROR;
end:
return rc;
}
void channel_auth_agent_req_callback(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(void *userdata))
{
/* TODO */
}
void channel_x11_req_callback(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(int single_connection),
UNUSED_PARAM(const char *auth_protocol),
UNUSED_PARAM(const char *auth_cookie),
UNUSED_PARAM(uint32_t screen_number),
UNUSED_PARAM(void *userdata))
{
/* TODO */
}
static int exec_pty(const char *mode,
const char *command,
struct channel_data_st *cdata)
{
int rc;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
cdata->pid = fork();
switch(cdata->pid) {
case -1:
close(cdata->pty_master);
close(cdata->pty_slave);
fprintf(stderr, "Failed to fork\n");
rc = SSH_ERROR;
goto end;
case 0:
close(cdata->pty_master);
if (login_tty(cdata->pty_slave) != 0) {
exit(1);
}
execl("/bin/sh", "sh", mode, command, NULL);
exit(0);
default:
close(cdata->pty_slave);
/* pty fd is bi-directional */
cdata->child_stdout = cdata->child_stdin = cdata->pty_master;
}
rc = SSH_OK;
end:
return rc;
}
static int exec_nopty(const char *command, struct channel_data_st *cdata)
{
int in[2], out[2], err[2];
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
goto stdin_failed;
}
/* Do the plumbing to be able to talk with the child process. */
if (pipe(in) != 0) {
goto stdin_failed;
}
if (pipe(out) != 0) {
goto stdout_failed;
}
if (pipe(err) != 0) {
goto stderr_failed;
}
switch(cdata->pid = fork()) {
case -1:
goto fork_failed;
case 0:
/* Finish the plumbing in the child process. */
close(in[1]);
close(out[0]);
close(err[0]);
dup2(in[0], STDIN_FILENO);
dup2(out[1], STDOUT_FILENO);
dup2(err[1], STDERR_FILENO);
close(in[0]);
close(out[1]);
close(err[1]);
/* exec the requested command. */
execl("/bin/sh", "sh", "-c", command, NULL);
exit(0);
}
close(in[0]);
close(out[1]);
close(err[1]);
cdata->child_stdin = in[1];
cdata->child_stdout = out[0];
cdata->child_stderr = err[0];
return SSH_OK;
fork_failed:
close(err[0]);
close(err[1]);
stderr_failed:
close(out[0]);
close(out[1]);
stdout_failed:
close(in[0]);
close(in[1]);
stdin_failed:
return SSH_ERROR;
}
int channel_shell_request_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
void *userdata)
{
struct channel_data_st *cdata;
int rc;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
if(cdata->pid > 0) {
rc = SSH_ERROR;
goto end;
}
if (cdata->pty_master != -1 && cdata->pty_slave != -1) {
rc = exec_pty("-l", NULL, cdata);
goto end;
}
/* Client requested a shell without a pty, let's pretend we allow that */
rc = SSH_OK;
end:
return rc;
}
int channel_exec_request_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
const char *command,
void *userdata)
{
struct channel_data_st *cdata;
int rc;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
if(cdata->pid > 0) {
rc = SSH_ERROR;
goto end;
}
if (cdata->pty_master != -1 && cdata->pty_slave != -1) {
rc = exec_pty("-c", command, cdata);
goto end;
}
rc = exec_nopty(command, cdata);
end:
return rc;
}
int channel_env_request_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(const char *env_name),
UNUSED_PARAM(const char *env_value),
void *userdata)
{
struct channel_data_st *cdata;
int rc;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
rc = SSH_OK;
end:
return rc;
}
int channel_subsystem_request_cb(ssh_session session,
ssh_channel channel,
const char *subsystem,
void *userdata)
{
struct channel_data_st *cdata;
int rc;
cdata = (struct channel_data_st *)userdata;
if (cdata == NULL) {
fprintf(stderr, "NULL userdata\n");
rc = SSH_ERROR;
goto end;
}
rc = strcmp(subsystem, "sftp");
if (rc == 0) {
rc = channel_exec_request_cb(session,
channel,
SFTP_SERVER_PATH,
userdata);
goto end;
}
/* TODO add other subsystems */
rc = SSH_ERROR;
end:
return rc;
}
int channel_write_wontblock_cb(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(ssh_channel channel),
UNUSED_PARAM(size_t bytes),
UNUSED_PARAM(void *userdata))
{
/* TODO */
return 0;
}
ssh_channel channel_new_session_cb(ssh_session session, void *userdata)
{
struct session_data_st *sdata = NULL;
ssh_channel chan = NULL;
sdata = (struct session_data_st *)userdata;
if (sdata == NULL) {
fprintf(stderr, "NULL userdata");
goto end;
}
chan = ssh_channel_new(session);
if (chan == NULL) {
fprintf(stderr, "Error creating channel: %s\n",
ssh_get_error(session));
goto end;
}
sdata->channel = chan;
end:
return chan;
}
#ifdef WITH_PCAP
static void set_pcap(struct session_data_st *sdata,
ssh_session session,
char *pcap_file)
{
int rc = 0;
if (sdata == NULL) {
return;
}
if (pcap_file == NULL) {
return;
}
sdata->pcap = ssh_pcap_file_new();
if (sdata->pcap == NULL) {
return;
}
rc = ssh_pcap_file_open(sdata->pcap, pcap_file);
if (rc == SSH_ERROR) {
fprintf(stderr, "Error opening pcap file\n");
ssh_pcap_file_free(sdata->pcap);
sdata->pcap = NULL;
return;
}
ssh_set_pcap_file(session, sdata->pcap);
}
static void cleanup_pcap(struct session_data_st *sdata)
{
if (sdata == NULL) {
return;
}
if (sdata->pcap == NULL) {
return;
}
ssh_pcap_file_free(sdata->pcap);
sdata->pcap = NULL;
}
#endif
static int process_stdout(socket_t fd, int revents, void *userdata)
{
char buf[BUF_SIZE];
int n = -1;
ssh_channel channel = (ssh_channel) userdata;
if (channel != NULL && (revents & POLLIN) != 0) {
n = read(fd, buf, BUF_SIZE);
if (n > 0) {
ssh_channel_write(channel, buf, n);
}
}
return n;
}
static int process_stderr(socket_t fd, int revents, void *userdata)
{
char buf[BUF_SIZE];
int n = -1;
ssh_channel channel = (ssh_channel) userdata;
if (channel != NULL && (revents & POLLIN) != 0) {
n = read(fd, buf, BUF_SIZE);
if (n > 0) {
ssh_channel_write_stderr(channel, buf, n);
}
}
return n;
}
void default_handle_session_cb(ssh_event event,
ssh_session session,
struct server_state_st *state)
{
int n;
int rc = 0;
/* Structure for storing the pty size. */
struct winsize wsize = {
.ws_row = 0,
.ws_col = 0,
.ws_xpixel = 0,
.ws_ypixel = 0
};
/* Our struct holding information about the channel. */
struct channel_data_st cdata = {
.pid = 0,
.pty_master = -1,
.pty_slave = -1,
.child_stdin = -1,
.child_stdout = -1,
.child_stderr = -1,
.event = NULL,
.winsize = &wsize
};
/* Our struct holding information about the session. */
struct session_data_st sdata = {
.channel = NULL,
.auth_attempts = 0,
.authenticated = 0,
.username = SSHD_DEFAULT_USER,
.password = SSHD_DEFAULT_PASSWORD
};
struct ssh_channel_callbacks_struct default_channel_cb = {
.userdata = &cdata,
.channel_pty_request_function = channel_pty_request_cb,
.channel_pty_window_change_function = channel_pty_resize_cb,
.channel_shell_request_function = channel_shell_request_cb,
.channel_env_request_function = channel_env_request_cb,
.channel_subsystem_request_function = channel_subsystem_request_cb,
.channel_exec_request_function = channel_exec_request_cb,
.channel_data_function = channel_data_cb
};
struct ssh_server_callbacks_struct default_server_cb = {
.userdata = &sdata,
.auth_password_function = auth_password_cb,
.channel_open_request_session_function = channel_new_session_cb,
#if WITH_GSSAPI
.auth_gssapi_mic_function = auth_gssapi_mic_cb
#endif
};
if (state == NULL) {
fprintf(stderr, "NULL server state provided\n");
goto end;
}
#ifdef WITH_PCAP
set_pcap(&sdata, session, state->pcap_file);
#endif
if (state->expected_username != NULL) {
sdata.username = state->expected_username;
}
if (state->expected_password != NULL) {
sdata.password = state->expected_password;
}
/* If callbacks were provided use them. Otherwise, use default callbacks */
if (state->server_cb != NULL) {
/* TODO check return values */
ssh_callbacks_init(state->server_cb);
ssh_set_server_callbacks(session, state->server_cb);
} else {
/* TODO check return values */
ssh_callbacks_init(&default_server_cb);
ssh_set_server_callbacks(session, &default_server_cb);
}
if (ssh_handle_key_exchange(session) != SSH_OK) {
fprintf(stderr, "%s\n", ssh_get_error(session));
return;
}
/* Set the supported authentication methods */
if (state->auth_methods) {
ssh_set_auth_methods(session, state->auth_methods);
} else {
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
}
ssh_event_add_session(event, session);
n = 0;
while (sdata.authenticated == 0 || sdata.channel == NULL) {
/* If the user has used up all attempts, or if he hasn't been able to
* authenticate in 10 seconds (n * 100ms), disconnect. */
if (sdata.auth_attempts >= state->max_tries || n >= 100) {
return;
}
if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
fprintf(stderr, "do_poll error: %s\n", ssh_get_error(session));
return;
}
n++;
}
/* TODO check return values */
if (state->channel_cb != NULL) {
ssh_callbacks_init(state->channel_cb);
ssh_set_channel_callbacks(sdata.channel, state->channel_cb);
} else {
ssh_callbacks_init(&default_channel_cb);
ssh_set_channel_callbacks(sdata.channel, &default_channel_cb);
}
do {
/* Poll the main event which takes care of the session, the channel and
* even our child process's stdout/stderr (once it's started). */
if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
ssh_channel_close(sdata.channel);
}
/* If child process's stdout/stderr has been registered with the event,
* or the child process hasn't started yet, continue. */
if (cdata.event != NULL || cdata.pid == 0) {
continue;
}
/* Executed only once, once the child process starts. */
cdata.event = event;
/* If stdout valid, add stdout to be monitored by the poll event. */
if (cdata.child_stdout != -1) {
if (ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout,
sdata.channel) != SSH_OK) {
fprintf(stderr, "Failed to register stdout to poll context\n");
ssh_channel_close(sdata.channel);
}
}
/* If stderr valid, add stderr to be monitored by the poll event. */
if (cdata.child_stderr != -1){
if (ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr,
sdata.channel) != SSH_OK) {
fprintf(stderr, "Failed to register stderr to poll context\n");
ssh_channel_close(sdata.channel);
}
}
} while(ssh_channel_is_open(sdata.channel) &&
(cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0));
close(cdata.pty_master);
close(cdata.child_stdin);
close(cdata.child_stdout);
close(cdata.child_stderr);
/* Remove the descriptors from the polling context, since they are now
* closed, they will always trigger during the poll calls. */
ssh_event_remove_fd(event, cdata.child_stdout);
ssh_event_remove_fd(event, cdata.child_stderr);
/* If the child process exited. */
if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) {
rc = WEXITSTATUS(rc);
ssh_channel_request_send_exit_status(sdata.channel, rc);
/* If client terminated the channel or the process did not exit nicely,
* but only if something has been forked. */
} else if (cdata.pid > 0) {
kill(cdata.pid, SIGKILL);
}
ssh_channel_send_eof(sdata.channel);
ssh_channel_close(sdata.channel);
/* Wait up to 5 seconds for the client to terminate the session. */
for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
ssh_event_dopoll(event, 100);
}
end:
#ifdef WITH_PCAP
cleanup_pcap(&sdata);
#endif
return;
}

160
tests/server/test_server/default_cb.h Обычный файл
Просмотреть файл

@ -0,0 +1,160 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 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"
#include <libssh/libssh.h>
#include <libssh/callbacks.h>
#define SSHD_DEFAULT_USER "libssh"
#define SSHD_DEFAULT_PASSWORD "libssh"
#define SSHD_DEFAULT_PORT 2222
#define SSHD_DEFAULT_ADDRESS "127.0.0.1"
#define SSHD_DEFAULT_PCAP_FILE "debug.server.pcap"
#ifndef KEYS_FOLDER
#ifdef _WIN32
#define KEYS_FOLDER
#else
#define KEYS_FOLDER "/etc/ssh/"
#endif
#endif
#define BUF_SIZE 1048576
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
/* A userdata struct for channel. */
struct channel_data_st {
/* pid of the child process the channel will spawn. */
pid_t pid;
/* For PTY allocation */
socket_t pty_master;
socket_t pty_slave;
/* For communication with the child process. */
socket_t child_stdin;
socket_t child_stdout;
/* Only used for subsystem and exec requests. */
socket_t child_stderr;
/* Event which is used to poll the above descriptors. */
ssh_event event;
/* Terminal size struct. */
struct winsize *winsize;
};
/* A userdata struct for session. */
struct session_data_st {
/* Pointer to the channel the session will allocate. */
ssh_channel channel;
int auth_attempts;
int authenticated;
const char *username;
const char *password;
#ifdef WITH_PCAP
ssh_pcap_file pcap;
#endif
};
int auth_password_cb(ssh_session session, const char *user,
const char *password, void *userdata);
#if WITH_GSSAPI
int auth_gssapi_mic_cb(ssh_session session, const char *user,
const char *principal, void *userdata);
#endif
int channel_data_cb(ssh_session session, ssh_channel channel,
void *data, uint32_t len, int is_stderr, void *userdata);
void channel_eof_cb(ssh_session session, ssh_channel channel,
void *userdata);
void channel_close_cb(ssh_session session, ssh_channel channel,
void *userdata);
void channel_signal_cb (ssh_session session,
ssh_channel channel,
const char *signal,
void *userdata);
void channel_exit_status_cb (ssh_session session,
ssh_channel channel,
int exit_status,
void *userdata);
void channel_exit_signal_cb(ssh_session session,
ssh_channel channel,
const char *signal,
int core,
const char *errmsg,
const char *lang,
void *userdata);
int channel_pty_request_cb(ssh_session session, ssh_channel channel,
const char *term, int cols, int rows, int py, int px, void *userdata);
int channel_pty_resize_cb(ssh_session session, ssh_channel channel,
int cols, int rows, int py, int px, void *userdata);
int channel_shell_request_cb(ssh_session session, ssh_channel channel,
void *userdata);
void channel_auth_agent_req_callback(ssh_session session,
ssh_channel channel, void *userdata);
void channel_x11_req_callback(ssh_session session,
ssh_channel channel,
int single_connection,
const char *auth_protocol,
const char *auth_cookie,
uint32_t screen_number,
void *userdata);
int channel_exec_request_cb(ssh_session session,
ssh_channel channel,
const char *command,
void *userdata);
int channel_env_request_cb(ssh_session session,
ssh_channel channel, const char *env_name, const char *env_value,
void *userdata);
int channel_subsystem_request_cb(ssh_session session,
ssh_channel channel, const char *subsystem,
void *userdata);
int channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
size_t bytes,
void *userdata);
ssh_channel channel_new_session_cb(ssh_session session, void *userdata);
struct ssh_server_callbacks_struct *get_default_server_cb(void);
void default_handle_session_cb(ssh_event event, ssh_session session,
struct server_state_st *state);

588
tests/server/test_server/main.c Обычный файл
Просмотреть файл

@ -0,0 +1,588 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 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"
#include "test_server.h"
#include "default_cb.h"
#include <libssh/priv.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
#ifdef HAVE_ARGP_H
#include <argp.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
struct arguments_st {
char *address;
char *port;
char *ecdsa_key;
char *dsa_key;
char *ed25519_key;
char *rsa_key;
char *host_key;
char *verbosity;
char *auth_methods;
bool with_pcap;
char *pcap_file;
char *username;
char *password;
};
static void free_arguments(struct arguments_st *arguments)
{
if (arguments == NULL) {
goto end;
}
SAFE_FREE(arguments->address);
SAFE_FREE(arguments->port);
SAFE_FREE(arguments->ecdsa_key);
SAFE_FREE(arguments->dsa_key);
SAFE_FREE(arguments->ed25519_key);
SAFE_FREE(arguments->rsa_key);
SAFE_FREE(arguments->host_key);
SAFE_FREE(arguments->verbosity);
SAFE_FREE(arguments->auth_methods);
SAFE_FREE(arguments->pcap_file);
SAFE_FREE(arguments->username);
SAFE_FREE(arguments->password);
end:
return;
}
#ifdef HAVE_ARGP_H
static void print_auth_methods(int auth_methods)
{
printf("auth_methods = \n");
if (auth_methods & SSH_AUTH_METHOD_NONE) {
printf("\tSSH_AUTH_METHOD_NONE\n");
}
if (auth_methods & SSH_AUTH_METHOD_PASSWORD) {
printf("\tSSH_AUTH_METHOD_PASSWORD\n");
}
if (auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
printf("\tSSH_AUTH_METHOD_PUBLICKEY\n");
}
if (auth_methods & SSH_AUTH_METHOD_HOSTBASED) {
printf("\tSSH_AUTH_METHOD_HOSTBASED\n");
}
if (auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
printf("\tSSH_AUTH_METHOD_INTERACTIVE\n");
}
if (auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
printf("\tSSH_AUTH_METHOD_GSSAPI_MIC\n");
}
}
static void print_verbosity(int verbosity)
{
printf("verbosity = ");
switch(verbosity) {
case SSH_LOG_NOLOG:
printf("NO LOG\n");
break;
case SSH_LOG_WARNING:
printf("WARNING\n");
break;
case SSH_LOG_PROTOCOL:
printf("PROTOCOL\n");
break;
case SSH_LOG_PACKET:
printf("PACKET\n");
break;
case SSH_LOG_FUNCTIONS:
printf("FUNCTIONS\n");
break;
default:
printf("UNKNOWN\n");;
break;
}
}
static void print_server_state(struct server_state_st *state)
{
if (state) {
printf("===================| STATE |=====================\n");
printf("address = %s\n",
state->address? state->address: "NULL");
printf("port = %d\n",
state->port? state->port: 0);
printf("=================================================\n");
printf("ecdsa_key = %s\n",
state->ecdsa_key? state->ecdsa_key: "NULL");
printf("dsa_key = %s\n",
state->dsa_key? state->dsa_key: "NULL");
printf("ed25519_key = %s\n",
state->ed25519_key? state->ed25519_key: "NULL");
printf("rsa_key = %s\n",
state->rsa_key? state->rsa_key: "NULL");
printf("host_key = %s\n",
state->host_key? state->host_key: "NULL");
printf("=================================================\n");
print_auth_methods(state->auth_methods);
print_verbosity(state->verbosity);
printf("with_pcap = %s\n",
state->with_pcap? "TRUE": "FALSE");
printf("pcap_file = %s\n",
state->pcap_file? state->pcap_file: "NULL");
printf("=================================================\n");
printf("username = %s\n",
state->expected_username? state->expected_username: "NULL");
printf("password = %s\n",
state->expected_password? state->expected_password: "NULL");
printf("=================================================\n");
}
}
static int init_server_state(struct server_state_st *state,
struct arguments_st *arguments)
{
int rc = 0;
if (state == NULL) {
rc = SSH_ERROR;
goto end;
}
/* Initialize server state. The "arguments structure" */
if (arguments->address) {
state->address = arguments->address;
arguments->address = NULL;
} else {
state->address = strdup(SSHD_DEFAULT_ADDRESS);
if (state->address == NULL) {
fprintf(stderr, "Out of memory\n");
rc = SSH_ERROR;
goto end;
}
}
if (arguments->port) {
state->port = atoi(arguments->port);
} else {
state->port = SSHD_DEFAULT_PORT;
}
if (arguments->ecdsa_key) {
state->ecdsa_key = arguments->ecdsa_key;
arguments->ecdsa_key = NULL;
} else {
state->ecdsa_key = NULL;
}
if (arguments->dsa_key) {
state->dsa_key = arguments->dsa_key;
arguments->dsa_key = NULL;
} else {
state->dsa_key = NULL;
}
if (arguments->ed25519_key) {
state->ed25519_key = arguments->ed25519_key;
arguments->ed25519_key = NULL;
} else {
state->ed25519_key = NULL;
}
if (arguments->rsa_key) {
state->rsa_key = arguments->rsa_key;
arguments->rsa_key = NULL;
} else {
state->rsa_key = NULL;
}
if (arguments->host_key) {
state->host_key = arguments->host_key;
arguments->host_key = NULL;
} else {
state->host_key = NULL;
}
if (arguments->username) {
state->expected_username = arguments->username;
arguments->username = NULL;
} else {
state->expected_username = strdup(SSHD_DEFAULT_USER);
if (state->expected_username == NULL) {
fprintf(stderr, "Out of memory\n");
rc = SSH_ERROR;
goto end;
}
}
if (arguments->password) {
state->expected_password = arguments->password;
arguments->password = NULL;
} else {
state->expected_password = strdup(SSHD_DEFAULT_PASSWORD);
if (state->expected_password == NULL) {
fprintf(stderr, "Out of memory\n");
rc = SSH_ERROR;
goto end;
}
}
if (arguments->verbosity) {
state->verbosity = atoi(arguments->verbosity);
} else {
state->verbosity = 0;
}
if (arguments->auth_methods) {
state->auth_methods = atoi(arguments->auth_methods);
} else {
state->auth_methods = 0;
}
state->with_pcap = arguments->with_pcap;
if (arguments->pcap_file) {
state->pcap_file = arguments->pcap_file;
arguments->pcap_file = NULL;
} else {
if (arguments->with_pcap) {
state->pcap_file = strdup(SSHD_DEFAULT_PCAP_FILE);
if (state->pcap_file == NULL) {
fprintf(stderr, "Out of memory\n");
rc = SSH_ERROR;
goto end;
}
} else {
state->pcap_file = NULL;
}
}
/* TODO make configurable */
state->max_tries = 3;
state->error = 0;
if (state) {
print_server_state(state);
}
/* TODO make callbacks configurable through command line ? */
/* Set callbacks to be used */
state->handle_session = default_handle_session_cb;
/* Check required parameters */
if (state->address == NULL) {
rc = SSH_ERROR;
goto end;
}
end:
if (rc != 0) {
free_server_state(state);
}
return rc;
}
const char *argp_program_version = "libssh test server "
SSH_STRINGIFY(LIBSSH_VERSION);
const char *argp_program_bug_address = "<libssh@libssh.org>";
/* Program documentation. */
static char doc[] = "libssh -- a Secure Shell protocol implementation";
/* A description of the arguments we accept. */
static char args_doc[] = "BINDADDR";
/* The options we understand. */
static struct argp_option options[] = {
{
.name = "port",
.key = 'p',
.arg = "PORT",
.flags = 0,
.doc = "Set the port to bind.",
.group = 0
},
{
.name = "ecdsakey",
.key = 'c',
.arg = "FILE",
.flags = 0,
.doc = "Set the ECDSA key.",
.group = 0
},
{
.name = "dsakey",
.key = 'd',
.arg = "FILE",
.flags = 0,
.doc = "Set the DSA key.",
.group = 0
},
{
.name = "ed25519key",
.key = 'e',
.arg = "FILE",
.flags = 0,
.doc = "Set the ed25519 key.",
.group = 0
},
{
.name = "rsakey",
.key = 'r',
.arg = "FILE",
.flags = 0,
.doc = "Set the RSA key.",
.group = 0
},
{
.name = "hostkey",
.key = 'k',
.arg = "FILE",
.flags = 0,
.doc = "Set the host key.",
.group = 0
},
{
.name = "pcapfile",
.key = 'f',
.arg = "FILE",
.flags = 0,
.doc = "Set the pcap output file.",
.group = 0
},
{
.name = "auth-methods",
.key = 'a',
.arg = "METHODS",
.flags = 0,
.doc = "Set supported authentication methods.",
.group = 0
},
{
.name = "user",
.key = 'u',
.arg = "USERNAME",
.flags = 0,
.doc = "Set expected username.",
.group = 0
},
{
.name = "verbosity",
.key = 'v',
.arg = "VERBOSITY",
.flags = 0,
.doc = "Set output verbosity [0-4].",
.group = 0
},
{
.name = "with-pcap",
.key = 'w',
.arg = NULL,
.flags = 0,
.doc = "Use PCAP.",
.group = 0
},
{ .name = NULL }
};
/* Parse a single option. */
static error_t parse_opt (int key, char *arg, struct argp_state *state)
{
/* Get the input argument from argp_parse, which we
* know is a pointer to our arguments structure.
*/
struct arguments_st *arguments = state->input;
error_t rc = 0;
if (arguments == NULL) {
fprintf(stderr, "NULL pointer to arguments structure provided\n");
rc = EINVAL;
goto end;
}
switch (key) {
case 'c':
arguments->ecdsa_key = strdup(arg);
if (arguments->ecdsa_key == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'd':
arguments->dsa_key = strdup(arg);
if (arguments->dsa_key == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'e':
arguments->ed25519_key = strdup(arg);
if (arguments->ed25519_key == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'f':
arguments->pcap_file = strdup(arg);
if (arguments->pcap_file == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'k':
arguments->host_key = strdup(arg);
if (arguments->host_key == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'a':
arguments->auth_methods = strdup(arg);
if (arguments->auth_methods == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'p':
arguments->port = strdup(arg);
if (arguments->port == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'r':
arguments->rsa_key = strdup(arg);
if (arguments->rsa_key == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'u':
arguments->username = strdup(arg);
if (arguments->username == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'v':
arguments->verbosity = strdup(arg);
if (arguments->verbosity == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case 'w':
arguments->with_pcap = true;
break;
case ARGP_KEY_ARG:
if (state->arg_num >= 1) {
/* Too many arguments. */
printf("Too many arguments\n");
argp_usage(state);
}
arguments->address = strdup(arg);
if (arguments->address == NULL) {
fprintf(stderr, "Out of memory\n");
rc = ENOMEM;
goto end;
}
break;
case ARGP_KEY_END:
if (state->arg_num < 1) {
printf("Too few arguments\n");
/* Not enough arguments. */
argp_usage(state);
}
break;
default:
return ARGP_ERR_UNKNOWN;
}
end:
return rc;
}
/* Our argp parser. */
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
#endif /* HAVE_ARGP_H */
int main(UNUSED_PARAM(int argc), UNUSED_PARAM(char **argv))
{
int rc;
struct arguments_st arguments = {
.address = NULL,
};
struct server_state_st state = {
.address = NULL,
};
#ifdef HAVE_ARGP_H
argp_parse (&argp, argc, argv, 0, 0, &arguments);
#endif
/* Initialize the state using default or given parameters */
rc = init_server_state(&state, &arguments);
if (rc != 0) {
goto free_arguments;
}
/* Free the arguments used to initialize the state before fork */
free_arguments(&arguments);
/* Run the server */
rc = run_server(&state);
if (rc != 0) {
goto free_state;
}
free_state:
free_server_state(&state);
free_arguments:
free_arguments(&arguments);
return rc;
}

310
tests/server/test_server/test_server.c Обычный файл
Просмотреть файл

@ -0,0 +1,310 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 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 "test_server.h"
#include <libssh/priv.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/wait.h>
void free_server_state(struct server_state_st *state)
{
if (state == NULL) {
goto end;
}
SAFE_FREE(state->address);
SAFE_FREE(state->ecdsa_key);
SAFE_FREE(state->dsa_key);
SAFE_FREE(state->ed25519_key);
SAFE_FREE(state->rsa_key);
SAFE_FREE(state->host_key);
SAFE_FREE(state->pcap_file);
SAFE_FREE(state->expected_username);
SAFE_FREE(state->expected_password);
end:
return;
}
/* SIGCHLD handler for cleaning up dead children. */
static void sigchld_handler(int signo) {
(void) signo;
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int run_server(struct server_state_st *state)
{
ssh_session session = NULL;
ssh_bind sshbind = NULL;
ssh_event event = NULL;
struct sigaction sa = {0};
int rc;
/* Check provided state */
if (state == NULL) {
fprintf(stderr, "Invalid state\n");
return SSH_ERROR;
}
/* Set up SIGCHLD handler. */
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, NULL) != 0) {
fprintf(stderr, "Failed to register SIGCHLD handler\n");
return SSH_ERROR;
}
if (state->address == NULL) {
fprintf(stderr, "Missing bind address\n");
return SSH_ERROR;
}
if (state->address == NULL) {
fprintf(stderr, "Missing bind address\n");
return SSH_ERROR;
}
sshbind = ssh_bind_new();
if (sshbind == NULL) {
fprintf(stderr, "Out of memory\n");
return SSH_ERROR;
}
if (state->verbosity) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_LOG_VERBOSITY,
&state->verbosity);
if (rc != 0) {
fprintf(stderr,
"Error setting verbosity level: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
}
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_BINDADDR,
state->address);
if (rc != 0) {
fprintf(stderr,
"Error setting bind address: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_BINDPORT,
&(state->port));
if (rc != 0) {
fprintf(stderr,
"Error setting bind port: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
if (state->dsa_key != NULL) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_DSAKEY,
state->dsa_key);
if (rc != 0) {
fprintf(stderr,
"Error setting DSA key: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
}
if (state->rsa_key != NULL) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_RSAKEY,
state->rsa_key);
if (rc != 0) {
fprintf(stderr,
"Error setting RSA key: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
}
if (state->ecdsa_key != NULL) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_ECDSAKEY,
state->ecdsa_key);
if (rc != 0) {
fprintf(stderr,
"Error setting ECDSA key: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
}
if (state->host_key) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_HOSTKEY,
state->host_key);
if (rc) {
fprintf(stderr,
"Error setting hostkey: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
}
rc = ssh_bind_listen(sshbind);
if (rc != 0) {
fprintf(stderr,
"Error listening to socket: %s\n",
ssh_get_error(sshbind));
goto free_sshbind;
}
printf("Started libssh test server on port %d\n", state->port);
for (;;) {
session = ssh_new();
if (session == NULL) {
fprintf(stderr, "Out of memory\n");
rc = SSH_ERROR;
goto free_sshbind;
}
/* Blocks until there is a new incoming connection. */
rc = ssh_bind_accept(sshbind, session);
if (rc != SSH_ERROR) {
pid_t pid = fork();
switch(pid) {
case 0:
/* Remove the SIGCHLD handler inherited from parent. */
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
/* Remove socket binding, which allows us to restart the
* parent process, without terminating existing sessions. */
ssh_bind_free(sshbind);
event = ssh_event_new();
if (event != NULL) {
/* Blocks until the SSH session ends by either
* child process exiting, or client disconnecting. */
state->handle_session(event, session, state);
ssh_event_free(event);
} else {
fprintf(stderr, "Could not create polling context\n");
}
ssh_disconnect(session);
ssh_free(session);
free_server_state(state);
exit(0);
case -1:
fprintf(stderr, "Failed to fork\n");
}
} else {
fprintf(stderr,
"Error accepting a connection: %s\n",
ssh_get_error(sshbind));
}
/* Since the session has been passed to a child fork, do some cleaning
* up at the parent process. */
ssh_disconnect(session);
ssh_free(session);
}
rc = 0;
free_sshbind:
ssh_bind_free(sshbind);
return rc;
}
pid_t fork_run_server(struct server_state_st *state)
{
pid_t pid;
int rc;
char err_str[1024] = {0};
struct sigaction sa;
/* Check provided state */
if (state == NULL) {
fprintf(stderr, "Invalid state\n");
return -1;
}
/* Set up SIGCHLD handler. */
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, NULL) != 0) {
strerror_r(errno, err_str, 1024);
fprintf(stderr, "Failed to register SIGCHLD handler: %s\n",
err_str);
return -1;
}
pid = fork();
switch(pid) {
case 0:
/* Remove the SIGCHLD handler inherited from parent. */
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
/* The child process starts a server which will listen for connections */
rc = run_server(state);
if (rc != 0) {
exit(rc);
}
exit(0);
case -1:
strerror_r(errno, err_str, 1024);
fprintf(stderr, "Failed to fork: %s\n",
err_str);
return -1;
default:
/* Return the child pid */
return pid;
}
}

73
tests/server/test_server/test_server.h Обычный файл
Просмотреть файл

@ -0,0 +1,73 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 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"
#include <stdbool.h>
#include <fcntl.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
struct server_state_st {
/* Arguments */
char *address;
int port;
char *ecdsa_key;
char *dsa_key;
char *ed25519_key;
char *rsa_key;
char *host_key;
int verbosity;
int auth_methods;
bool with_pcap;
char *pcap_file;
char *expected_username;
char *expected_password;
/* State */
int max_tries;
int error;
struct ssh_server_callbacks_struct *server_cb;
struct ssh_channel_callbacks_struct *channel_cb;
/* Callback to handle the session, should block until disconnected */
void (*handle_session)(ssh_event event,
ssh_session session,
struct server_state_st *state);
};
/*TODO: Add documentation */
void free_server_state(struct server_state_st *state);
/*TODO: Add documentation */
int run_server(struct server_state_st *state);
/*TODO: Add documentation */
pid_t fork_run_server(struct server_state_st *state);

345
tests/server/torture_server.c Обычный файл
Просмотреть файл

@ -0,0 +1,345 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 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 "test_server.h"
#include "default_cb.h"
struct test_server_st {
struct torture_state *state;
struct server_state_st *ss;
};
static int setup_default_server(void **state)
{
struct torture_state *s;
struct server_state_st *ss;
struct test_server_st *tss;
#ifdef HAVE_DSA
char dsa_hostkey[1024];
#endif /* HAVE_DSA */
char ed25519_hostkey[1024] = {0};
char rsa_hostkey[1024];
char ecdsa_hostkey[1024];
//char trusted_ca_pubkey[1024];
char sshd_path[1024];
struct stat sb;
const char *sftp_server_locations[] = {
"/usr/lib/ssh/sftp-server",
"/usr/libexec/sftp-server",
"/usr/libexec/openssh/sftp-server",
"/usr/lib/openssh/sftp-server", /* Debian */
};
size_t sftp_sl_size = ARRAY_SIZE(sftp_server_locations);
const char *sftp_server;
size_t i;
int rc;
char pid_str[1024];
pid_t pid;
assert_non_null(state);
tss = (struct test_server_st*)malloc(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(ed25519_hostkey,
sizeof(ed25519_hostkey),
"%s/sshd/ssh_host_ed25519_key",
s->socket_dir);
torture_write_file(ed25519_hostkey,
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0, 0));
#ifdef HAVE_DSA
snprintf(dsa_hostkey,
sizeof(dsa_hostkey),
"%s/sshd/ssh_host_dsa_key",
s->socket_dir);
torture_write_file(dsa_hostkey, torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0));
#endif /* HAVE_DSA */
snprintf(rsa_hostkey,
sizeof(rsa_hostkey),
"%s/sshd/ssh_host_rsa_key",
s->socket_dir);
torture_write_file(rsa_hostkey, torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0));
snprintf(ecdsa_hostkey,
sizeof(ecdsa_hostkey),
"%s/sshd/ssh_host_ecdsa_key",
s->socket_dir);
torture_write_file(ecdsa_hostkey,
torture_get_testkey(SSH_KEYTYPE_ECDSA, 521, 0));
sftp_server = getenv("TORTURE_SFTP_SERVER");
if (sftp_server == NULL) {
for (i = 0; i < sftp_sl_size; i++) {
sftp_server = sftp_server_locations[i];
rc = lstat(sftp_server, &sb);
if (rc == 0) {
break;
}
}
}
assert_non_null(sftp_server);
/* Create default server state */
ss = (struct server_state_st *)malloc(sizeof(struct server_state_st));
assert_non_null(ss);
ss->address = strdup("127.0.0.10");
assert_non_null(ss->address);
ss->port = 22;
ss->ecdsa_key = strdup(ecdsa_hostkey);
assert_non_null(ss->ecdsa_key);
#ifdef HAVE_DSA
ss->dsa_key = strdup(dsa_hostkey);
assert_non_null(ss->dsa_key);
#endif /* HAVE_DSA */
ss->ed25519_key = strdup(ed25519_hostkey);
assert_non_null(ed25519_hostkey);
ss->rsa_key = strdup(rsa_hostkey);
assert_non_null(ss->rsa_key);
ss->host_key = NULL;
/* Use default username and password (set in default_handle_session_cb) */
ss->expected_username = NULL;
ss->expected_password = NULL;
ss->verbosity = torture_libssh_verbosity();
ss->auth_methods = SSH_AUTH_METHOD_PASSWORD;
#ifdef WITH_PCAP
ss->with_pcap = 1;
ss->pcap_file = strdup(s->pcap_file);
assert_non_null(ss->pcap_file);
#endif
/* TODO make configurable */
ss->max_tries = 3;
ss->error = 0;
/* Use the default session handling function */
ss->handle_session = default_handle_session_cb;
assert_non_null(ss->handle_session);
/* Start the server using the default values */
pid = fork_run_server(ss);
if (pid < 0) {
fail();
}
snprintf(pid_str, sizeof(pid_str), "%d", pid);
torture_write_file(s->srv_pidfile, (const char *)pid_str);
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
unsetenv("PAM_WRAPPER");
/* Wait until the sshd is ready to accept connections */
//rc = torture_wait_for_daemon(5);
//assert_int_equal(rc, 0);
/* TODO properly wait for the server (use ping approach) */
/* Wait 200ms */
usleep(200 * 1000);
tss->state = s;
tss->ss = ss;
*state = tss;
return 0;
}
static int teardown_default_server(void **state)
{
struct torture_state *s;
struct server_state_st *ss;
struct test_server_st *tss;
tss = *state;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
ss = tss->ss;
assert_non_null(ss);
/* This function can be reused */
torture_teardown_sshd_server((void **)&s);
free_server_state(tss->ss);
SAFE_FREE(tss->ss);
SAFE_FREE(tss);
return 0;
}
static int session_setup(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
int verbosity = torture_libssh_verbosity();
struct passwd *pwd;
bool b = false;
int rc;
assert_non_null(tss);
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);
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
/* 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);
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 void torture_server_auth_password(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
ssh_session session;
int rc;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
session = s->ssh.session;
assert_non_null(session);
/* TODO: implement proper pam authentication function */
/* Using the default user for the server */
rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
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_AUTH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
/* TODO: implement proper pam authentication function */
/* Using the default password for the server */
rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_server_auth_password,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests,
setup_default_server,
teardown_default_server);
ssh_finalize();
return rc;
}