|
Packit |
6c0a39 |
/*
|
|
Packit |
6c0a39 |
* This file is part of the SSH Library
|
|
Packit |
6c0a39 |
*
|
|
Packit |
6c0a39 |
* Copyright (c) 2018 by Red Hat, Inc.
|
|
Packit |
6c0a39 |
*
|
|
Packit |
6c0a39 |
* Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
|
Packit |
6c0a39 |
*
|
|
Packit |
6c0a39 |
* The SSH Library is free software; you can redistribute it and/or modify
|
|
Packit |
6c0a39 |
* it under the terms of the GNU Lesser General Public License as published by
|
|
Packit |
6c0a39 |
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
|
Packit |
6c0a39 |
* option) any later version.
|
|
Packit |
6c0a39 |
*
|
|
Packit |
6c0a39 |
* The SSH Library is distributed in the hope that it will be useful, but
|
|
Packit |
6c0a39 |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
Packit |
6c0a39 |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
Packit |
6c0a39 |
* License for more details.
|
|
Packit |
6c0a39 |
*
|
|
Packit |
6c0a39 |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
6c0a39 |
* along with the SSH Library; see the file COPYING. If not, write to
|
|
Packit |
6c0a39 |
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
Packit |
6c0a39 |
* MA 02111-1307, USA.
|
|
Packit |
6c0a39 |
*/
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
#include "test_server.h"
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
#include <libssh/priv.h>
|
|
Packit |
6c0a39 |
#include <libssh/libssh.h>
|
|
Packit |
6c0a39 |
#include <libssh/server.h>
|
|
Packit |
6c0a39 |
#include <libssh/callbacks.h>
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
#include <signal.h>
|
|
Packit |
6c0a39 |
#include <stdio.h>
|
|
Packit |
6c0a39 |
#include <stdlib.h>
|
|
Packit |
6c0a39 |
#include <string.h>
|
|
Packit |
6c0a39 |
#include <stdbool.h>
|
|
Packit |
6c0a39 |
#include <errno.h>
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
#include <sys/wait.h>
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
void free_server_state(struct server_state_st *state)
|
|
Packit |
6c0a39 |
{
|
|
Packit |
6c0a39 |
if (state == NULL) {
|
|
Packit |
6c0a39 |
goto end;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
SAFE_FREE(state->address);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
SAFE_FREE(state->ecdsa_key);
|
|
Packit |
6c0a39 |
SAFE_FREE(state->dsa_key);
|
|
Packit |
6c0a39 |
SAFE_FREE(state->ed25519_key);
|
|
Packit |
6c0a39 |
SAFE_FREE(state->rsa_key);
|
|
Packit |
6c0a39 |
SAFE_FREE(state->host_key);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
SAFE_FREE(state->pcap_file);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
SAFE_FREE(state->expected_username);
|
|
Packit |
6c0a39 |
SAFE_FREE(state->expected_password);
|
|
Packit |
6c0a39 |
SAFE_FREE(state->config_file);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
end:
|
|
Packit |
6c0a39 |
return;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* SIGCHLD handler for cleaning up dead children. */
|
|
Packit |
6c0a39 |
static void sigchld_handler(int signo) {
|
|
Packit |
6c0a39 |
(void) signo;
|
|
Packit |
6c0a39 |
while (waitpid(-1, NULL, WNOHANG) > 0);
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
int run_server(struct server_state_st *state)
|
|
Packit |
6c0a39 |
{
|
|
Packit |
6c0a39 |
ssh_session session = NULL;
|
|
Packit |
6c0a39 |
ssh_bind sshbind = NULL;
|
|
Packit |
6c0a39 |
ssh_event event = NULL;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
struct sigaction sa = {0};
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
int rc;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* Check provided state */
|
|
Packit |
6c0a39 |
if (state == NULL) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Invalid state\n");
|
|
Packit |
6c0a39 |
return SSH_ERROR;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* Set up SIGCHLD handler. */
|
|
Packit |
6c0a39 |
sa.sa_handler = sigchld_handler;
|
|
Packit |
6c0a39 |
sigemptyset(&sa.sa_mask);
|
|
Packit |
6c0a39 |
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (sigaction(SIGCHLD, &sa, NULL) != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Failed to register SIGCHLD handler\n");
|
|
Packit |
6c0a39 |
return SSH_ERROR;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->address == NULL) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Missing bind address\n");
|
|
Packit |
6c0a39 |
return SSH_ERROR;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->address == NULL) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Missing bind address\n");
|
|
Packit |
6c0a39 |
return SSH_ERROR;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
sshbind = ssh_bind_new();
|
|
Packit |
6c0a39 |
if (sshbind == NULL) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Out of memory\n");
|
|
Packit |
6c0a39 |
return SSH_ERROR;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->verbosity) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_LOG_VERBOSITY,
|
|
Packit |
6c0a39 |
&state->verbosity);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting verbosity level: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (!state->parse_global_config) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_PROCESS_CONFIG,
|
|
Packit |
6c0a39 |
&(state->parse_global_config));
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->config_file) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_parse_config(sshbind, state->config_file);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_BINDADDR,
|
|
Packit |
6c0a39 |
state->address);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting bind address: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_BINDPORT,
|
|
Packit |
6c0a39 |
&(state->port));
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting bind port: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->dsa_key != NULL) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_DSAKEY,
|
|
Packit |
6c0a39 |
state->dsa_key);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting DSA key: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->rsa_key != NULL) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_RSAKEY,
|
|
Packit |
6c0a39 |
state->rsa_key);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting RSA key: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->ecdsa_key != NULL) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_ECDSAKEY,
|
|
Packit |
6c0a39 |
state->ecdsa_key);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting ECDSA key: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (state->host_key) {
|
|
Packit |
6c0a39 |
rc = ssh_bind_options_set(sshbind,
|
|
Packit |
6c0a39 |
SSH_BIND_OPTIONS_HOSTKEY,
|
|
Packit |
6c0a39 |
state->host_key);
|
|
Packit |
6c0a39 |
if (rc) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error setting hostkey: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
rc = ssh_bind_listen(sshbind);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error listening to socket: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
printf("Started libssh test server on port %d\n", state->port);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
for (;;) {
|
|
Packit |
6c0a39 |
session = ssh_new();
|
|
Packit |
6c0a39 |
if (session == NULL) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Out of memory\n");
|
|
Packit |
6c0a39 |
rc = SSH_ERROR;
|
|
Packit |
6c0a39 |
goto free_sshbind;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* Blocks until there is a new incoming connection. */
|
|
Packit |
6c0a39 |
rc = ssh_bind_accept(sshbind, session);
|
|
Packit |
6c0a39 |
if (rc != SSH_ERROR) {
|
|
Packit |
6c0a39 |
pid_t pid = fork();
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
switch(pid) {
|
|
Packit |
6c0a39 |
case 0:
|
|
Packit |
6c0a39 |
/* Remove the SIGCHLD handler inherited from parent. */
|
|
Packit |
6c0a39 |
sa.sa_handler = SIG_DFL;
|
|
Packit |
6c0a39 |
sigaction(SIGCHLD, &sa, NULL);
|
|
Packit |
6c0a39 |
/* Remove socket binding, which allows us to restart the
|
|
Packit |
6c0a39 |
* parent process, without terminating existing sessions. */
|
|
Packit |
6c0a39 |
ssh_bind_free(sshbind);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
event = ssh_event_new();
|
|
Packit |
6c0a39 |
if (event != NULL) {
|
|
Packit |
6c0a39 |
/* Blocks until the SSH session ends by either
|
|
Packit |
6c0a39 |
* child process exiting, or client disconnecting. */
|
|
Packit |
6c0a39 |
state->handle_session(event, session, state);
|
|
Packit |
6c0a39 |
ssh_event_free(event);
|
|
Packit |
6c0a39 |
} else {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Could not create polling context\n");
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
ssh_disconnect(session);
|
|
Packit |
6c0a39 |
ssh_free(session);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
free_server_state(state);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
exit(0);
|
|
Packit |
6c0a39 |
case -1:
|
|
Packit |
6c0a39 |
fprintf(stderr, "Failed to fork\n");
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
} else {
|
|
Packit |
6c0a39 |
fprintf(stderr,
|
|
Packit |
6c0a39 |
"Error accepting a connection: %s\n",
|
|
Packit |
6c0a39 |
ssh_get_error(sshbind));
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* Since the session has been passed to a child fork, do some cleaning
|
|
Packit |
6c0a39 |
* up at the parent process. */
|
|
Packit |
6c0a39 |
ssh_disconnect(session);
|
|
Packit |
6c0a39 |
ssh_free(session);
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
rc = 0;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
free_sshbind:
|
|
Packit |
6c0a39 |
ssh_bind_free(sshbind);
|
|
Packit |
6c0a39 |
return rc;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
pid_t fork_run_server(struct server_state_st *state)
|
|
Packit |
6c0a39 |
{
|
|
Packit |
6c0a39 |
pid_t pid;
|
|
Packit |
6c0a39 |
int rc;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
char err_str[1024] = {0};
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
struct sigaction sa;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* Check provided state */
|
|
Packit |
6c0a39 |
if (state == NULL) {
|
|
Packit |
6c0a39 |
fprintf(stderr, "Invalid state\n");
|
|
Packit |
6c0a39 |
return -1;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* Set up SIGCHLD handler. */
|
|
Packit |
6c0a39 |
sa.sa_handler = sigchld_handler;
|
|
Packit |
6c0a39 |
sigemptyset(&sa.sa_mask);
|
|
Packit |
6c0a39 |
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
if (sigaction(SIGCHLD, &sa, NULL) != 0) {
|
|
Packit |
6c0a39 |
strerror_r(errno, err_str, 1024);
|
|
Packit |
6c0a39 |
fprintf(stderr, "Failed to register SIGCHLD handler: %s\n",
|
|
Packit |
6c0a39 |
err_str);
|
|
Packit |
6c0a39 |
return -1;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
pid = fork();
|
|
Packit |
6c0a39 |
switch(pid) {
|
|
Packit |
6c0a39 |
case 0:
|
|
Packit |
6c0a39 |
/* Remove the SIGCHLD handler inherited from parent. */
|
|
Packit |
6c0a39 |
sa.sa_handler = SIG_DFL;
|
|
Packit |
6c0a39 |
sigaction(SIGCHLD, &sa, NULL);
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
/* The child process starts a server which will listen for connections */
|
|
Packit |
6c0a39 |
rc = run_server(state);
|
|
Packit |
6c0a39 |
if (rc != 0) {
|
|
Packit |
6c0a39 |
exit(rc);
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
|
|
Packit |
6c0a39 |
exit(0);
|
|
Packit |
6c0a39 |
case -1:
|
|
Packit |
6c0a39 |
strerror_r(errno, err_str, 1024);
|
|
Packit |
6c0a39 |
fprintf(stderr, "Failed to fork: %s\n",
|
|
Packit |
6c0a39 |
err_str);
|
|
Packit |
6c0a39 |
return -1;
|
|
Packit |
6c0a39 |
default:
|
|
Packit |
6c0a39 |
/* Return the child pid */
|
|
Packit |
6c0a39 |
return pid;
|
|
Packit |
6c0a39 |
}
|
|
Packit |
6c0a39 |
}
|