Blame tests/server/test_server/test_server.c

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
}