tests: Added test server
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>
Этот коммит содержится в:
родитель
e91e221d02
Коммит
37262b98ef
@ -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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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;
|
||||
}
|
Загрузка…
Ссылка в новой задаче
Block a user