Blame examples/ssh_server_fork.c

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