Blame examples/ssh_server_fork.c

Packit Service 31306d
/* This is a sample implementation of a libssh based SSH server */
Packit Service 31306d
/*
Packit Service 31306d
Copyright 2014 Audrius Butkevicius
Packit Service 31306d
Packit Service 31306d
This file is part of the SSH Library
Packit Service 31306d
Packit Service 31306d
You are free to copy this file, modify it in any way, consider it being public
Packit Service 31306d
domain. This does not apply to the rest of the library though, but it is
Packit Service 31306d
allowed to cut-and-paste working code from this file to any license of
Packit Service 31306d
program.
Packit Service 31306d
The goal is to show the API in action.
Packit Service 31306d
*/
Packit Service 31306d
Packit Service 31306d
#include "config.h"
Packit Service 31306d
Packit Service 31306d
#include <libssh/callbacks.h>
Packit Service 31306d
#include <libssh/server.h>
Packit Service 31306d
Packit Service 31306d
#include <poll.h>
Packit Service 31306d
#ifdef HAVE_ARGP_H
Packit Service 31306d
#include <argp.h>
Packit Service 31306d
#endif
Packit Service 31306d
#include <fcntl.h>
Packit Service 31306d
#ifdef HAVE_LIBUTIL_H
Packit Service 31306d
#include <libutil.h>
Packit Service 31306d
#endif
Packit Service 31306d
#ifdef HAVE_PTY_H
Packit Service 31306d
#include <pty.h>
Packit Service 31306d
#endif
Packit Service 31306d
#include <signal.h>
Packit Service 31306d
#include <stdlib.h>
Packit Service 31306d
#ifdef HAVE_UTMP_H
Packit Service 31306d
#include <utmp.h>
Packit Service 31306d
#endif
Packit Service 31306d
#ifdef HAVE_UTIL_H
Packit Service 31306d
#include <util.h>
Packit Service 31306d
#endif
Packit Service 31306d
#include <sys/ioctl.h>
Packit Service 31306d
#include <sys/wait.h>
Packit Service 31306d
#include <sys/stat.h>
Packit Service 31306d
#include <stdio.h>
Packit Service 31306d
Packit Service 31306d
#ifndef KEYS_FOLDER
Packit Service 31306d
#ifdef _WIN32
Packit Service 31306d
#define KEYS_FOLDER
Packit Service 31306d
#else
Packit Service 31306d
#define KEYS_FOLDER "/etc/ssh/"
Packit Service 31306d
#endif
Packit Service 31306d
#endif
Packit Service 31306d
Packit Service 31306d
#define USER "myuser"
Packit Service 31306d
#define PASS "mypassword"
Packit Service 31306d
#define BUF_SIZE 1048576
Packit Service 31306d
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
Packit Service 31306d
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
Packit Service 31306d
Packit Service 31306d
static void set_default_keys(ssh_bind sshbind,
Packit Service 31306d
                             int rsa_already_set,
Packit Service 31306d
                             int dsa_already_set,
Packit Service 31306d
                             int ecdsa_already_set) {
Packit Service 31306d
    if (!rsa_already_set) {
Packit Service 31306d
        ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY,
Packit Service 31306d
                             KEYS_FOLDER "ssh_host_rsa_key");
Packit Service 31306d
    }
Packit Service 31306d
    if (!dsa_already_set) {
Packit Service 31306d
        ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY,
Packit Service 31306d
                             KEYS_FOLDER "ssh_host_dsa_key");
Packit Service 31306d
    }
Packit Service 31306d
    if (!ecdsa_already_set) {
Packit Service 31306d
        ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY,
Packit Service 31306d
                             KEYS_FOLDER "ssh_host_ecdsa_key");
Packit Service 31306d
    }
Packit Service 31306d
    ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
Packit Service 31306d
                         KEYS_FOLDER "ssh_host_ed25519_key");
Packit Service 31306d
}
Packit Service 31306d
#define DEF_STR_SIZE 1024
Packit Service 31306d
char authorizedkeys[DEF_STR_SIZE] = {0};
Packit Service 31306d
#ifdef HAVE_ARGP_H
Packit Service 31306d
const char *argp_program_version = "libssh server example "
Packit Service 31306d
SSH_STRINGIFY(LIBSSH_VERSION);
Packit Service 31306d
const char *argp_program_bug_address = "<libssh@libssh.org>";
Packit Service 31306d
Packit Service 31306d
/* Program documentation. */
Packit Service 31306d
static char doc[] = "libssh -- a Secure Shell protocol implementation";
Packit Service 31306d
Packit Service 31306d
/* A description of the arguments we accept. */
Packit Service 31306d
static char args_doc[] = "BINDADDR";
Packit Service 31306d
Packit Service 31306d
/* The options we understand. */
Packit Service 31306d
static struct argp_option options[] = {
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "port",
Packit Service 31306d
        .key   = 'p',
Packit Service 31306d
        .arg   = "PORT",
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Set the port to bind.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "hostkey",
Packit Service 31306d
        .key   = 'k',
Packit Service 31306d
        .arg   = "FILE",
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Set a host key.  Can be used multiple times.  "
Packit Service 31306d
                 "Implies no default keys.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "dsakey",
Packit Service 31306d
        .key   = 'd',
Packit Service 31306d
        .arg   = "FILE",
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Set the dsa key.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "rsakey",
Packit Service 31306d
        .key   = 'r',
Packit Service 31306d
        .arg   = "FILE",
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Set the rsa key.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "ecdsakey",
Packit Service 31306d
        .key   = 'e',
Packit Service 31306d
        .arg   = "FILE",
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Set the ecdsa key.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "authorizedkeys",
Packit Service 31306d
        .key   = 'a',
Packit Service 31306d
        .arg   = "FILE",
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Set the authorized keys file.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "no-default-keys",
Packit Service 31306d
        .key   = 'n',
Packit Service 31306d
        .arg   = NULL,
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Do not set default key locations.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {
Packit Service 31306d
        .name  = "verbose",
Packit Service 31306d
        .key   = 'v',
Packit Service 31306d
        .arg   = NULL,
Packit Service 31306d
        .flags = 0,
Packit Service 31306d
        .doc   = "Get verbose output.",
Packit Service 31306d
        .group = 0
Packit Service 31306d
    },
Packit Service 31306d
    {NULL, 0, NULL, 0, NULL, 0}
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
/* Parse a single option. */
Packit Service 31306d
static error_t parse_opt (int key, char *arg, struct argp_state *state) {
Packit Service 31306d
    /* Get the input argument from argp_parse, which we
Packit Service 31306d
     * know is a pointer to our arguments structure. */
Packit Service 31306d
    ssh_bind sshbind = state->input;
Packit Service 31306d
    static int no_default_keys = 0;
Packit Service 31306d
    static int rsa_already_set = 0, dsa_already_set = 0, ecdsa_already_set = 0;
Packit Service 31306d
Packit Service 31306d
    switch (key) {
Packit Service 31306d
        case 'n':
Packit Service 31306d
            no_default_keys = 1;
Packit Service 31306d
            break;
Packit Service 31306d
        case 'p':
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
Packit Service 31306d
            break;
Packit Service 31306d
        case 'd':
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
Packit Service 31306d
            dsa_already_set = 1;
Packit Service 31306d
            break;
Packit Service 31306d
        case 'k':
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
Packit Service 31306d
            /* We can't track the types of keys being added with this
Packit Service 31306d
               option, so let's ensure we keep the keys we're adding
Packit Service 31306d
               by just not setting the default keys */
Packit Service 31306d
            no_default_keys = 1;
Packit Service 31306d
            break;
Packit Service 31306d
        case 'r':
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
Packit Service 31306d
            rsa_already_set = 1;
Packit Service 31306d
            break;
Packit Service 31306d
        case 'e':
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, arg);
Packit Service 31306d
            ecdsa_already_set = 1;
Packit Service 31306d
            break;
Packit Service 31306d
        case 'a':
Packit Service 31306d
            strncpy(authorizedkeys, arg, DEF_STR_SIZE-1);
Packit Service 31306d
            break;
Packit Service 31306d
        case 'v':
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
Packit Service 31306d
                                 "3");
Packit Service 31306d
            break;
Packit Service 31306d
        case ARGP_KEY_ARG:
Packit Service 31306d
            if (state->arg_num >= 1) {
Packit Service 31306d
                /* Too many arguments. */
Packit Service 31306d
                argp_usage (state);
Packit Service 31306d
            }
Packit Service 31306d
            ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg);
Packit Service 31306d
            break;
Packit Service 31306d
        case ARGP_KEY_END:
Packit Service 31306d
            if (state->arg_num < 1) {
Packit Service 31306d
                /* Not enough arguments. */
Packit Service 31306d
                argp_usage (state);
Packit Service 31306d
            }
Packit Service 31306d
Packit Service 31306d
            if (!no_default_keys) {
Packit Service 31306d
                set_default_keys(sshbind,
Packit Service 31306d
                                 rsa_already_set,
Packit Service 31306d
                                 dsa_already_set,
Packit Service 31306d
                                 ecdsa_already_set);
Packit Service 31306d
            }
Packit Service 31306d
Packit Service 31306d
            break;
Packit Service 31306d
        default:
Packit Service 31306d
            return ARGP_ERR_UNKNOWN;
Packit Service 31306d
    }
Packit Service 31306d
    return 0;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/* Our argp parser. */
Packit Service 31306d
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
Packit Service 31306d
#endif /* HAVE_ARGP_H */
Packit Service 31306d
Packit Service 31306d
/* A userdata struct for channel. */
Packit Service 31306d
struct channel_data_struct {
Packit Service 31306d
    /* pid of the child process the channel will spawn. */
Packit Service 31306d
    pid_t pid;
Packit Service 31306d
    /* For PTY allocation */
Packit Service 31306d
    socket_t pty_master;
Packit Service 31306d
    socket_t pty_slave;
Packit Service 31306d
    /* For communication with the child process. */
Packit Service 31306d
    socket_t child_stdin;
Packit Service 31306d
    socket_t child_stdout;
Packit Service 31306d
    /* Only used for subsystem and exec requests. */
Packit Service 31306d
    socket_t child_stderr;
Packit Service 31306d
    /* Event which is used to poll the above descriptors. */
Packit Service 31306d
    ssh_event event;
Packit Service 31306d
    /* Terminal size struct. */
Packit Service 31306d
    struct winsize *winsize;
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
/* A userdata struct for session. */
Packit Service 31306d
struct session_data_struct {
Packit Service 31306d
    /* Pointer to the channel the session will allocate. */
Packit Service 31306d
    ssh_channel channel;
Packit Service 31306d
    int auth_attempts;
Packit Service 31306d
    int authenticated;
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
static int data_function(ssh_session session, ssh_channel channel, void *data,
Packit Service 31306d
                         uint32_t len, int is_stderr, void *userdata) {
Packit Service 31306d
    struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
Packit Service 31306d
Packit Service 31306d
    (void) session;
Packit Service 31306d
    (void) channel;
Packit Service 31306d
    (void) is_stderr;
Packit Service 31306d
Packit Service 31306d
    if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) {
Packit Service 31306d
        return 0;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    return write(cdata->child_stdin, (char *) data, len);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int pty_request(ssh_session session, ssh_channel channel,
Packit Service 31306d
                       const char *term, int cols, int rows, int py, int px,
Packit Service 31306d
                       void *userdata) {
Packit Service 31306d
    struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
Packit Service 31306d
Packit Service 31306d
    (void) session;
Packit Service 31306d
    (void) channel;
Packit Service 31306d
    (void) term;
Packit Service 31306d
Packit Service 31306d
    cdata->winsize->ws_row = rows;
Packit Service 31306d
    cdata->winsize->ws_col = cols;
Packit Service 31306d
    cdata->winsize->ws_xpixel = px;
Packit Service 31306d
    cdata->winsize->ws_ypixel = py;
Packit Service 31306d
Packit Service 31306d
    if (openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL,
Packit Service 31306d
                cdata->winsize) != 0) {
Packit Service 31306d
        fprintf(stderr, "Failed to open pty\n");
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int pty_resize(ssh_session session, ssh_channel channel, int cols,
Packit Service 31306d
                      int rows, int py, int px, void *userdata) {
Packit Service 31306d
    struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
Packit Service 31306d
Packit Service 31306d
    (void) session;
Packit Service 31306d
    (void) channel;
Packit Service 31306d
Packit Service 31306d
    cdata->winsize->ws_row = rows;
Packit Service 31306d
    cdata->winsize->ws_col = cols;
Packit Service 31306d
    cdata->winsize->ws_xpixel = px;
Packit Service 31306d
    cdata->winsize->ws_ypixel = py;
Packit Service 31306d
Packit Service 31306d
    if (cdata->pty_master != -1) {
Packit Service 31306d
        return ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    return SSH_ERROR;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int exec_pty(const char *mode, const char *command,
Packit Service 31306d
                    struct channel_data_struct *cdata) {
Packit Service 31306d
    switch(cdata->pid = fork()) {
Packit Service 31306d
        case -1:
Packit Service 31306d
            close(cdata->pty_master);
Packit Service 31306d
            close(cdata->pty_slave);
Packit Service 31306d
            fprintf(stderr, "Failed to fork\n");
Packit Service 31306d
            return SSH_ERROR;
Packit Service 31306d
        case 0:
Packit Service 31306d
            close(cdata->pty_master);
Packit Service 31306d
            if (login_tty(cdata->pty_slave) != 0) {
Packit Service 31306d
                exit(1);
Packit Service 31306d
            }
Packit Service 31306d
            execl("/bin/sh", "sh", mode, command, NULL);
Packit Service 31306d
            exit(0);
Packit Service 31306d
        default:
Packit Service 31306d
            close(cdata->pty_slave);
Packit Service 31306d
            /* pty fd is bi-directional */
Packit Service 31306d
            cdata->child_stdout = cdata->child_stdin = cdata->pty_master;
Packit Service 31306d
    }
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int exec_nopty(const char *command, struct channel_data_struct *cdata) {
Packit Service 31306d
    int in[2], out[2], err[2];
Packit Service 31306d
Packit Service 31306d
    /* Do the plumbing to be able to talk with the child process. */
Packit Service 31306d
    if (pipe(in) != 0) {
Packit Service 31306d
        goto stdin_failed;
Packit Service 31306d
    }
Packit Service 31306d
    if (pipe(out) != 0) {
Packit Service 31306d
        goto stdout_failed;
Packit Service 31306d
    }
Packit Service 31306d
    if (pipe(err) != 0) {
Packit Service 31306d
        goto stderr_failed;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    switch(cdata->pid = fork()) {
Packit Service 31306d
        case -1:
Packit Service 31306d
            goto fork_failed;
Packit Service 31306d
        case 0:
Packit Service 31306d
            /* Finish the plumbing in the child process. */
Packit Service 31306d
            close(in[1]);
Packit Service 31306d
            close(out[0]);
Packit Service 31306d
            close(err[0]);
Packit Service 31306d
            dup2(in[0], STDIN_FILENO);
Packit Service 31306d
            dup2(out[1], STDOUT_FILENO);
Packit Service 31306d
            dup2(err[1], STDERR_FILENO);
Packit Service 31306d
            close(in[0]);
Packit Service 31306d
            close(out[1]);
Packit Service 31306d
            close(err[1]);
Packit Service 31306d
            /* exec the requested command. */
Packit Service 31306d
            execl("/bin/sh", "sh", "-c", command, NULL);
Packit Service 31306d
            exit(0);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    close(in[0]);
Packit Service 31306d
    close(out[1]);
Packit Service 31306d
    close(err[1]);
Packit Service 31306d
Packit Service 31306d
    cdata->child_stdin = in[1];
Packit Service 31306d
    cdata->child_stdout = out[0];
Packit Service 31306d
    cdata->child_stderr = err[0];
Packit Service 31306d
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
Packit Service 31306d
fork_failed:
Packit Service 31306d
    close(err[0]);
Packit Service 31306d
    close(err[1]);
Packit Service 31306d
stderr_failed:
Packit Service 31306d
    close(out[0]);
Packit Service 31306d
    close(out[1]);
Packit Service 31306d
stdout_failed:
Packit Service 31306d
    close(in[0]);
Packit Service 31306d
    close(in[1]);
Packit Service 31306d
stdin_failed:
Packit Service 31306d
    return SSH_ERROR;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int exec_request(ssh_session session, ssh_channel channel,
Packit Service 31306d
                        const char *command, void *userdata) {
Packit Service 31306d
    struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
    (void) session;
Packit Service 31306d
    (void) channel;
Packit Service 31306d
Packit Service 31306d
    if(cdata->pid > 0) {
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (cdata->pty_master != -1 && cdata->pty_slave != -1) {
Packit Service 31306d
        return exec_pty("-c", command, cdata);
Packit Service 31306d
    }
Packit Service 31306d
    return exec_nopty(command, cdata);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int shell_request(ssh_session session, ssh_channel channel,
Packit Service 31306d
                         void *userdata) {
Packit Service 31306d
    struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
Packit Service 31306d
Packit Service 31306d
    (void) session;
Packit Service 31306d
    (void) channel;
Packit Service 31306d
Packit Service 31306d
    if(cdata->pid > 0) {
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (cdata->pty_master != -1 && cdata->pty_slave != -1) {
Packit Service 31306d
        return exec_pty("-l", NULL, cdata);
Packit Service 31306d
    }
Packit Service 31306d
    /* Client requested a shell without a pty, let's pretend we allow that */
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int subsystem_request(ssh_session session, ssh_channel channel,
Packit Service 31306d
                             const char *subsystem, void *userdata) {
Packit Service 31306d
    /* subsystem requests behave simillarly to exec requests. */
Packit Service 31306d
    if (strcmp(subsystem, "sftp") == 0) {
Packit Service 31306d
        return exec_request(session, channel, SFTP_SERVER_PATH, userdata);
Packit Service 31306d
    }
Packit Service 31306d
    return SSH_ERROR;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int auth_password(ssh_session session, const char *user,
Packit Service 31306d
                         const char *pass, void *userdata) {
Packit Service 31306d
    struct session_data_struct *sdata = (struct session_data_struct *) userdata;
Packit Service 31306d
Packit Service 31306d
    (void) session;
Packit Service 31306d
Packit Service 31306d
    if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) {
Packit Service 31306d
        sdata->authenticated = 1;
Packit Service 31306d
        return SSH_AUTH_SUCCESS;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    sdata->auth_attempts++;
Packit Service 31306d
    return SSH_AUTH_DENIED;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int auth_publickey(ssh_session session,
Packit Service 31306d
                          const char *user,
Packit Service 31306d
                          struct ssh_key_struct *pubkey,
Packit Service 31306d
                          char signature_state,
Packit Service 31306d
                          void *userdata)
Packit Service 31306d
{
Packit Service 31306d
    struct session_data_struct *sdata = (struct session_data_struct *) userdata;
Packit Service 31306d
Packit Service 31306d
    (void) user;
Packit Service 31306d
    (void) session;
Packit Service 31306d
Packit Service 31306d
    if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Packit Service 31306d
        return SSH_AUTH_SUCCESS;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (signature_state != SSH_PUBLICKEY_STATE_VALID) {
Packit Service 31306d
        return SSH_AUTH_DENIED;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    // valid so far.  Now look through authorized keys for a match
Packit Service 31306d
    if (authorizedkeys[0]) {
Packit Service 31306d
        ssh_key key = NULL;
Packit Service 31306d
        int result;
Packit Service 31306d
        struct stat buf;
Packit Service 31306d
Packit Service 31306d
        if (stat(authorizedkeys, &buf) == 0) {
Packit Service 31306d
            result = ssh_pki_import_pubkey_file( authorizedkeys, &key );
Packit Service 31306d
            if ((result != SSH_OK) || (key==NULL)) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Unable to import public key file %s\n",
Packit Service 31306d
                        authorizedkeys);
Packit Service 31306d
            } else {
Packit Service 31306d
                result = ssh_key_cmp( key, pubkey, SSH_KEY_CMP_PUBLIC );
Packit Service 31306d
                ssh_key_free(key);
Packit Service 31306d
                if (result == 0) {
Packit Service 31306d
                    sdata->authenticated = 1;
Packit Service 31306d
                    return SSH_AUTH_SUCCESS;
Packit Service 31306d
                }
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    // no matches
Packit Service 31306d
    sdata->authenticated = 0;
Packit Service 31306d
    return SSH_AUTH_DENIED;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static ssh_channel channel_open(ssh_session session, void *userdata) {
Packit Service 31306d
    struct session_data_struct *sdata = (struct session_data_struct *) userdata;
Packit Service 31306d
Packit Service 31306d
    sdata->channel = ssh_channel_new(session);
Packit Service 31306d
    return sdata->channel;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int process_stdout(socket_t fd, int revents, void *userdata) {
Packit Service 31306d
    char buf[BUF_SIZE];
Packit Service 31306d
    int n = -1;
Packit Service 31306d
    ssh_channel channel = (ssh_channel) userdata;
Packit Service 31306d
Packit Service 31306d
    if (channel != NULL && (revents & POLLIN) != 0) {
Packit Service 31306d
        n = read(fd, buf, BUF_SIZE);
Packit Service 31306d
        if (n > 0) {
Packit Service 31306d
            ssh_channel_write(channel, buf, n);
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    return n;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int process_stderr(socket_t fd, int revents, void *userdata) {
Packit Service 31306d
    char buf[BUF_SIZE];
Packit Service 31306d
    int n = -1;
Packit Service 31306d
    ssh_channel channel = (ssh_channel) userdata;
Packit Service 31306d
Packit Service 31306d
    if (channel != NULL && (revents & POLLIN) != 0) {
Packit Service 31306d
        n = read(fd, buf, BUF_SIZE);
Packit Service 31306d
        if (n > 0) {
Packit Service 31306d
            ssh_channel_write_stderr(channel, buf, n);
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    return n;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static void handle_session(ssh_event event, ssh_session session) {
Packit Service 31306d
    int n;
Packit Service 31306d
    int rc = 0;
Packit Service 31306d
Packit Service 31306d
    /* Structure for storing the pty size. */
Packit Service 31306d
    struct winsize wsize = {
Packit Service 31306d
        .ws_row = 0,
Packit Service 31306d
        .ws_col = 0,
Packit Service 31306d
        .ws_xpixel = 0,
Packit Service 31306d
        .ws_ypixel = 0
Packit Service 31306d
    };
Packit Service 31306d
Packit Service 31306d
    /* Our struct holding information about the channel. */
Packit Service 31306d
    struct channel_data_struct cdata = {
Packit Service 31306d
        .pid = 0,
Packit Service 31306d
        .pty_master = -1,
Packit Service 31306d
        .pty_slave = -1,
Packit Service 31306d
        .child_stdin = -1,
Packit Service 31306d
        .child_stdout = -1,
Packit Service 31306d
        .child_stderr = -1,
Packit Service 31306d
        .event = NULL,
Packit Service 31306d
        .winsize = &wsize
Packit Service 31306d
    };
Packit Service 31306d
Packit Service 31306d
    /* Our struct holding information about the session. */
Packit Service 31306d
    struct session_data_struct sdata = {
Packit Service 31306d
        .channel = NULL,
Packit Service 31306d
        .auth_attempts = 0,
Packit Service 31306d
        .authenticated = 0
Packit Service 31306d
    };
Packit Service 31306d
Packit Service 31306d
    struct ssh_channel_callbacks_struct channel_cb = {
Packit Service 31306d
        .userdata = &cdata,
Packit Service 31306d
        .channel_pty_request_function = pty_request,
Packit Service 31306d
        .channel_pty_window_change_function = pty_resize,
Packit Service 31306d
        .channel_shell_request_function = shell_request,
Packit Service 31306d
        .channel_exec_request_function = exec_request,
Packit Service 31306d
        .channel_data_function = data_function,
Packit Service 31306d
        .channel_subsystem_request_function = subsystem_request
Packit Service 31306d
    };
Packit Service 31306d
Packit Service 31306d
    struct ssh_server_callbacks_struct server_cb = {
Packit Service 31306d
        .userdata = &sdata,
Packit Service 31306d
        .auth_password_function = auth_password,
Packit Service 31306d
        .channel_open_request_session_function = channel_open,
Packit Service 31306d
    };
Packit Service 31306d
Packit Service 31306d
    if (authorizedkeys[0]) {
Packit Service 31306d
        server_cb.auth_pubkey_function = auth_publickey;
Packit Service 31306d
        ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY);
Packit Service 31306d
    } else
Packit Service 31306d
        ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
Packit Service 31306d
Packit Service 31306d
    ssh_callbacks_init(&server_cb);
Packit Service 31306d
    ssh_callbacks_init(&channel_cb);
Packit Service 31306d
Packit Service 31306d
    ssh_set_server_callbacks(session, &server_cb);
Packit Service 31306d
Packit Service 31306d
    if (ssh_handle_key_exchange(session) != SSH_OK) {
Packit Service 31306d
        fprintf(stderr, "%s\n", ssh_get_error(session));
Packit Service 31306d
        return;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ssh_event_add_session(event, session);
Packit Service 31306d
Packit Service 31306d
    n = 0;
Packit Service 31306d
    while (sdata.authenticated == 0 || sdata.channel == NULL) {
Packit Service 31306d
        /* If the user has used up all attempts, or if he hasn't been able to
Packit Service 31306d
         * authenticate in 10 seconds (n * 100ms), disconnect. */
Packit Service 31306d
        if (sdata.auth_attempts >= 3 || n >= 100) {
Packit Service 31306d
            return;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
Packit Service 31306d
            fprintf(stderr, "%s\n", ssh_get_error(session));
Packit Service 31306d
            return;
Packit Service 31306d
        }
Packit Service 31306d
        n++;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ssh_set_channel_callbacks(sdata.channel, &channel_cb);
Packit Service 31306d
Packit Service 31306d
    do {
Packit Service 31306d
        /* Poll the main event which takes care of the session, the channel and
Packit Service 31306d
         * even our child process's stdout/stderr (once it's started). */
Packit Service 31306d
        if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
Packit Service 31306d
          ssh_channel_close(sdata.channel);
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        /* If child process's stdout/stderr has been registered with the event,
Packit Service 31306d
         * or the child process hasn't started yet, continue. */
Packit Service 31306d
        if (cdata.event != NULL || cdata.pid == 0) {
Packit Service 31306d
            continue;
Packit Service 31306d
        }
Packit Service 31306d
        /* Executed only once, once the child process starts. */
Packit Service 31306d
        cdata.event = event;
Packit Service 31306d
        /* If stdout valid, add stdout to be monitored by the poll event. */
Packit Service 31306d
        if (cdata.child_stdout != -1) {
Packit Service 31306d
            if (ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout,
Packit Service 31306d
                                 sdata.channel) != SSH_OK) {
Packit Service 31306d
                fprintf(stderr, "Failed to register stdout to poll context\n");
Packit Service 31306d
                ssh_channel_close(sdata.channel);
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        /* If stderr valid, add stderr to be monitored by the poll event. */
Packit Service 31306d
        if (cdata.child_stderr != -1){
Packit Service 31306d
            if (ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr,
Packit Service 31306d
                                 sdata.channel) != SSH_OK) {
Packit Service 31306d
                fprintf(stderr, "Failed to register stderr to poll context\n");
Packit Service 31306d
                ssh_channel_close(sdata.channel);
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
    } while(ssh_channel_is_open(sdata.channel) &&
Packit Service 31306d
            (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0));
Packit Service 31306d
Packit Service 31306d
    close(cdata.pty_master);
Packit Service 31306d
    close(cdata.child_stdin);
Packit Service 31306d
    close(cdata.child_stdout);
Packit Service 31306d
    close(cdata.child_stderr);
Packit Service 31306d
Packit Service 31306d
    /* Remove the descriptors from the polling context, since they are now
Packit Service 31306d
     * closed, they will always trigger during the poll calls. */
Packit Service 31306d
    ssh_event_remove_fd(event, cdata.child_stdout);
Packit Service 31306d
    ssh_event_remove_fd(event, cdata.child_stderr);
Packit Service 31306d
Packit Service 31306d
    /* If the child process exited. */
Packit Service 31306d
    if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) {
Packit Service 31306d
        rc = WEXITSTATUS(rc);
Packit Service 31306d
        ssh_channel_request_send_exit_status(sdata.channel, rc);
Packit Service 31306d
    /* If client terminated the channel or the process did not exit nicely,
Packit Service 31306d
     * but only if something has been forked. */
Packit Service 31306d
    } else if (cdata.pid > 0) {
Packit Service 31306d
        kill(cdata.pid, SIGKILL);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ssh_channel_send_eof(sdata.channel);
Packit Service 31306d
    ssh_channel_close(sdata.channel);
Packit Service 31306d
Packit Service 31306d
    /* Wait up to 5 seconds for the client to terminate the session. */
Packit Service 31306d
    for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
Packit Service 31306d
        ssh_event_dopoll(event, 100);
Packit Service 31306d
    }
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/* SIGCHLD handler for cleaning up dead children. */
Packit Service 31306d
static void sigchld_handler(int signo) {
Packit Service 31306d
    (void) signo;
Packit Service 31306d
    while (waitpid(-1, NULL, WNOHANG) > 0);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
int main(int argc, char **argv) {
Packit Service 31306d
    ssh_bind sshbind;
Packit Service 31306d
    ssh_session session;
Packit Service 31306d
    ssh_event event;
Packit Service 31306d
    struct sigaction sa;
Packit Service 31306d
    int rc;
Packit Service 31306d
Packit Service 31306d
    /* Set up SIGCHLD handler. */
Packit Service 31306d
    sa.sa_handler = sigchld_handler;
Packit Service 31306d
    sigemptyset(&sa.sa_mask);
Packit Service 31306d
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
Packit Service 31306d
    if (sigaction(SIGCHLD, &sa, NULL) != 0) {
Packit Service 31306d
        fprintf(stderr, "Failed to register SIGCHLD handler\n");
Packit Service 31306d
        return 1;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = ssh_init();
Packit Service 31306d
    if (rc < 0) {
Packit Service 31306d
        fprintf(stderr, "ssh_init failed\n");
Packit Service 31306d
        return 1;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    sshbind = ssh_bind_new();
Packit Service 31306d
    if (sshbind == NULL) {
Packit Service 31306d
        fprintf(stderr, "ssh_bind_new failed\n");
Packit Service 31306d
        return 1;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
#ifdef HAVE_ARGP_H
Packit Service 31306d
    argp_parse(&argp, argc, argv, 0, 0, sshbind);
Packit Service 31306d
#else
Packit Service 31306d
    (void) argc;
Packit Service 31306d
    (void) argv;
Packit Service 31306d
Packit Service 31306d
    set_default_keys(sshbind, 0, 0, 0);
Packit Service 31306d
#endif /* HAVE_ARGP_H */
Packit Service 31306d
Packit Service 31306d
    if(ssh_bind_listen(sshbind) < 0) {
Packit Service 31306d
        fprintf(stderr, "%s\n", ssh_get_error(sshbind));
Packit Service 31306d
        return 1;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    while (1) {
Packit Service 31306d
        session = ssh_new();
Packit Service 31306d
        if (session == NULL) {
Packit Service 31306d
            fprintf(stderr, "Failed to allocate session\n");
Packit Service 31306d
            continue;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        /* Blocks until there is a new incoming connection. */
Packit Service 31306d
        if(ssh_bind_accept(sshbind, session) != SSH_ERROR) {
Packit Service 31306d
            switch(fork()) {
Packit Service 31306d
                case 0:
Packit Service 31306d
                    /* Remove the SIGCHLD handler inherited from parent. */
Packit Service 31306d
                    sa.sa_handler = SIG_DFL;
Packit Service 31306d
                    sigaction(SIGCHLD, &sa, NULL);
Packit Service 31306d
                    /* Remove socket binding, which allows us to restart the
Packit Service 31306d
                     * parent process, without terminating existing sessions. */
Packit Service 31306d
                    ssh_bind_free(sshbind);
Packit Service 31306d
Packit Service 31306d
                    event = ssh_event_new();
Packit Service 31306d
                    if (event != NULL) {
Packit Service 31306d
                        /* Blocks until the SSH session ends by either
Packit Service 31306d
                         * child process exiting, or client disconnecting. */
Packit Service 31306d
                        handle_session(event, session);
Packit Service 31306d
                        ssh_event_free(event);
Packit Service 31306d
                    } else {
Packit Service 31306d
                        fprintf(stderr, "Could not create polling context\n");
Packit Service 31306d
                    }
Packit Service 31306d
                    ssh_disconnect(session);
Packit Service 31306d
                    ssh_free(session);
Packit Service 31306d
Packit Service 31306d
                    exit(0);
Packit Service 31306d
                case -1:
Packit Service 31306d
                    fprintf(stderr, "Failed to fork\n");
Packit Service 31306d
            }
Packit Service 31306d
        } else {
Packit Service 31306d
            fprintf(stderr, "%s\n", ssh_get_error(sshbind));
Packit Service 31306d
        }
Packit Service 31306d
        /* Since the session has been passed to a child fork, do some cleaning
Packit Service 31306d
         * up at the parent process. */
Packit Service 31306d
        ssh_disconnect(session);
Packit Service 31306d
        ssh_free(session);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ssh_bind_free(sshbind);
Packit Service 31306d
    ssh_finalize();
Packit Service 31306d
    return 0;
Packit Service 31306d
}