diff options
author | milo <milo@r0ot.me> | 2011-04-20 04:24:11 +0200 |
---|---|---|
committer | milo <milo@r0ot.me> | 2011-04-20 14:10:59 +0200 |
commit | b81f4546c25039b284688d0f54859f101890e7dc (patch) | |
tree | 3bd2a0856b7ae407e777559fbd537e0a9964a1d8 | |
parent | 56394917b15e41603c641c22a4e29c33b096d673 (diff) | |
download | libssh-sshd.tar.gz libssh-sshd.tar.xz libssh-sshd.zip |
Started to make a full sshd serversshd
-rw-r--r-- | examples/CMakeLists.txt | 3 | ||||
-rw-r--r-- | examples/samplesshd-full.c | 682 |
2 files changed, 685 insertions, 0 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5513b75..36ac047 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -39,6 +39,9 @@ if (LINUX) if (HAVE_LIBUTIL) add_executable(samplesshd-tty samplesshd-tty.c) target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} util) + + add_executable(samplesshd-full samplesshd-full.c) + target_link_libraries(samplesshd-full ${LIBSSH_SHARED_LIBRARY} util) endif (HAVE_LIBUTIL) endif (WITH_SERVER) diff --git a/examples/samplesshd-full.c b/examples/samplesshd-full.c new file mode 100644 index 0000000..ee8fa39 --- /dev/null +++ b/examples/samplesshd-full.c @@ -0,0 +1,682 @@ +/* This is a sample implementation of a libssh based SSH server */ +/* + Copyright 2003-2011 Aris Adamantiadis + + This file is part of the SSH Library + + You are free to copy this file, modify it in any way, consider it being public + domain. This does not apply to the rest of the library though, but it is + allowed to cut-and-paste working code from this file to any license of + program. + The goal is to show the API in action. It's not a reference on how terminal + clients must be made or how a client should react. + */ + +#include <signal.h> +#include "config.h" + +#include <libssh/libssh.h> +#include <libssh/server.h> +#include <libssh/callbacks.h> + +#ifdef HAVE_ARGP_H +#include <argp.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <poll.h> +#include <pty.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> + +#define SSHD_USER "libssh" +#define SSHD_PASSWORD "libssh" + +#ifndef KEYS_FOLDER +#ifdef _WIN32 +#define KEYS_FOLDER +#else +#define KEYS_FOLDER "/etc/ssh/" +#endif +#endif + +struct sshd_struct { + char **argv; + int bind_fd; + int session_fd; + ssh_session session; + ssh_event event; + ssh_channel channel; + enum { + ct_unknown=0, + ct_shell, + ct_exec + } channel_type; + int verbose; + int childpid; +} sshd = { + NULL, + 0, + 0, + NULL, + NULL, + NULL, + ct_unknown, + 0, + 0 +}; + +static void reexec(void) { + /* this block to avoid piling up fds at re-exec */ + printf("reexec %s\n", sshd.argv[0]); + { + int i, j[2]; + if(pipe(j) < 0) { + exit(-1); + } + for(i = 3; i <= 100; i++) { + close(i); + } + } + execv(sshd.argv[0], sshd.argv); +} + +#ifdef WITH_PCAP +const char *pcap_file="debug.server.pcap"; +ssh_pcap_file pcap; + + static void set_pcap(ssh_session session){ + if(!pcap_file) + return; + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ + printf("Error opening pcap file\n"); + ssh_pcap_file_free(pcap); + pcap=NULL; + return; + } + ssh_set_pcap_file(session,pcap); + } + +static void cleanup_pcap(){ + ssh_pcap_file_free(pcap); + pcap=NULL; +} +#endif + + +static int auth_password(const char *user, const char *password){ + return 1; + if(strcmp(user, SSHD_USER)) + return 0; + if(strcmp(password, SSHD_PASSWORD)) + return 0; + return 1; // authenticated +} +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh server example " +SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = "<libssh@libssh.org>"; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +static int port = 22; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set the host key.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, 0, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + ssh_bind sshbind = state->input; + + switch (key) { + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + port = atoi(arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +#define BUFSIZE (8*1024) +#define MAX_TRANSMIT (BUFSIZE * 4) +static int fd_data(socket_t fd, int revents, void *userdata) { + ssh_channel chan = (ssh_channel)userdata; + char buf[BUFSIZE]; + int sz = 0; + int bytes = 0; + int status; + static pid_t p = 0; + + printf("fd received event 0x%x\n", revents); + if(p) { + return -1; + } + if(!chan) { + printf("chan is NULL\n"); + close(fd); + return -1; + } + if(revents & POLLIN) { + int ws; + + do { + ws = ssh_channel_window_size(chan); + ws = ws < BUFSIZE ? ws : BUFSIZE; + if(ws && (bytes = read(fd, buf, ws)) > 0) { + sz += bytes; + ssh_channel_write(chan, buf, bytes); + } + printf("received %d bytes on fd\n", bytes); + } while(ws > 0 && bytes == BUFSIZE); // && sz < MAX_TRANSMIT) + printf("received total %d bytes on fd\n", sz); + } + if(revents & (POLLHUP | POLLERR | POLLNVAL)) { + if((p = waitpid(sshd.childpid, &status, WNOHANG)) > 0) { + ssh_channel_request_send_exit_status(chan, WEXITSTATUS(status)); + } else { + printf("wait: error\n"); + } + printf("fd has been closed %d %x\n", status, status); + ssh_channel_send_eof(chan); + printf("Sent EOF on chan\n"); + ssh_channel_close(chan); + printf("Closed chan\n"); + sz = -1; + ssh_event_remove_fd(sshd.event, fd); + } + return sz; +} + +static int chan_data(ssh_session session, + ssh_channel channel, + void *data, + uint32_t len, + int is_stderr, + void *userdata) { + int fd = (long)userdata; + int sz; + (void)session; + (void)channel; + (void)is_stderr; + + printf("received %d bytes on chan\n", len); + if(!len) { + return 0; + } + sz = write(fd, data, len); + printf("received %d from write(fd=%d)\n", sz, fd); + return sz; +} + +static void chan_eof(ssh_session session, ssh_channel channel, void *userdata) { + int fd = (long)userdata; + (void)session; + (void)channel; + + printf("chan received eof\n"); + shutdown(fd, SHUT_WR); +} + +static void chan_close(ssh_session session, ssh_channel channel, + void *userdata) { + int fd = (long)userdata; + (void)session; + (void)channel; + + printf("chan has been closed\n"); + close(fd); +} + +static void chan_exit(ssh_session session, ssh_channel channel, int exitcode, + void *userdata) { + int fd = (long)userdata; + (void)session; + (void)channel; + (void)fd; + + printf("chan exit %d\n", exitcode); +} + +static void chan_signal(ssh_session session, ssh_channel channel, + const char*sig, void *userdata) { + int fd = (long)userdata; + (void)session; + (void)channel; + (void)fd; + + printf("chan signal %s\n", sig); +} + +static void chan_exit_signal(ssh_session session, ssh_channel channel, + const char *sig, + int core, + const char *errmsg, + const char *lang, void *userdata) { + int fd = (long)userdata; + (void)session; + (void)channel; + (void)fd; + (void)core; + (void)lang; + + printf("chan exit_signal %s %s%s\n", sig, core?"(core dumped)":"", errmsg); +} + +struct ssh_channel_callbacks_struct cb = { + .channel_data_function = chan_data, + .channel_eof_function = chan_eof, + .channel_close_function = chan_close, + .channel_exit_status_function = chan_exit, + .channel_signal_function = chan_signal, + .channel_exit_signal_function = chan_exit_signal, + .userdata = NULL +}; + +static int do_shell(ssh_event event, ssh_channel chan) { + socket_t fd; + struct termios *term = NULL; + struct winsize *win = NULL; + short events; + int fd_status; + + + sshd.childpid = forkpty(&fd, NULL, term, win); + if(sshd.childpid == 0) { + close(sshd.bind_fd); + close(sshd.session_fd); + + execl("/bin/bash", "/bin/bash", (char *)NULL); + abort(); + } else if(sshd.childpid < 0) { + printf("Couldn't start shell subprocess\n"); + return -1; + } + + fd_status = fcntl(fd, F_GETFL, 0); + if(fcntl(fd, F_SETFL, fd_status | O_NONBLOCK) < 0) { + printf("Couldn't set non-blocking mode on fd\n"); + return -1; + } + + cb.userdata = (void *)(long)fd; + ssh_callbacks_init(&cb); + ssh_set_channel_callbacks(chan, &cb); + + events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; + + if(ssh_event_add_fd(event, fd, events, fd_data, chan) != SSH_OK) { + printf("Couldn't add an fd to the event\n"); + return -1; + } + + return 0; +} + +static int fork_exec(const char *cmd) { + int spair[2]; + int fd_status; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, spair) < 0) { + return -1; + } + + sshd.childpid = fork(); + + if(sshd.childpid == 0) { + close(sshd.bind_fd); + close(sshd.session_fd); + + close(0); + close(1); + close(2); + close(spair[1]); + dup2(spair[0], 0); + dup2(spair[0], 1); + dup2(spair[0], 2); + close(spair[0]); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + exit(0); + } else if(sshd.childpid < 0) { + return -1; + } + + close(spair[0]); + + fd_status = fcntl(spair[1], F_GETFL, 0); + if(fcntl(spair[1], F_SETFL, fd_status | O_NONBLOCK) < 0) { + printf("Couldn't set non-blocking mode on fd\n"); + return -1; + } + return spair[1]; +} + +static int do_exec(ssh_event event, ssh_channel chan, const char *cmd) { + socket_t fd; + short events; + + + printf("execing cmd %s\n", cmd); + fd = fork_exec(cmd); + if(fd < 0) { + printf("Couldn't start cmd subprocess\n"); + return -1; + } + + printf("execed cmd %s\n", cmd); + cb.userdata = (void *)(long)fd; + ssh_callbacks_init(&cb); + ssh_set_channel_callbacks(chan, &cb); + + events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; + + if(ssh_event_add_fd(event, fd, events, fd_data, chan) != SSH_OK) { + printf("Couldn't add an fd to the event\n"); + return -1; + } + + return 0; +} + +static int channel_request_callback(ssh_session session, ssh_message message, void *data) { + const char *cmd; + (void)data; + (void)session; + + /* wait for a shell */ + switch(ssh_message_type(message)) { + case SSH_REQUEST_CHANNEL: + switch(ssh_message_subtype(message)) { + case SSH_CHANNEL_REQUEST_SHELL: + printf("Message subtype shell\n"); + sshd.channel_type = ct_shell; + if(do_shell(sshd.event, sshd.channel) < 0) { + goto deny; + } + goto accept_end; + + case SSH_CHANNEL_REQUEST_EXEC: + printf("Message subtype exec\n"); + sshd.channel_type = ct_exec; + cmd = ssh_message_channel_request_command(message); + if(do_exec(sshd.event, sshd.channel, cmd) < 0) { + goto deny; + } + goto accept_end; + + case SSH_CHANNEL_REQUEST_PTY: + printf("Message subtype pty\n"); + goto accept; + + default: + printf("Message subtype unknown\n"); + goto deny; + } + default: + printf("Message type unknown\n"); + goto deny; + } + +deny: + printf("channel_request denied\n"); + return 1; + +accept_end: + printf("Ending channel_request callback: "); + //ssh_set_message_callback(sshd.session, NULL, NULL); +accept: + printf("channel_request accepted\n"); + ssh_message_channel_request_reply_success(message); + return 0; +} + +static int channel_open_callback(ssh_session session, ssh_message message, void *data) { + ssh_channel *channel = data; + (void)session; + + /* wait for a channel session */ + switch(ssh_message_type(message)) { + case SSH_REQUEST_CHANNEL_OPEN: + switch(ssh_message_subtype(message)) { + case SSH_CHANNEL_SESSION: + goto accept; + default: + goto deny; + } + default: + goto deny; + } +deny: + return 1; + +accept: + ssh_set_message_callback(sshd.session, channel_request_callback, NULL); + *channel = ssh_message_channel_request_open_reply_accept(message); + return 0; +} + +static int authenticate_callback(ssh_session session, ssh_message message, void *data) { + (void)session; + (void)data; + + printf("Got a message %d:%d\n", ssh_message_type(message), ssh_message_subtype(message)); + switch(ssh_message_type(message)){ + case SSH_REQUEST_AUTH: + switch(ssh_message_subtype(message)){ + case SSH_AUTH_METHOD_PUBLICKEY: + /* allow anyone for the purpose of the test */ + goto accept; + + case SSH_AUTH_METHOD_PASSWORD: + printf("User %s wants to auth with pass %s\n", + ssh_message_auth_user(message), + ssh_message_auth_password(message)); + if(auth_password(ssh_message_auth_user(message), + ssh_message_auth_password(message))){ + goto accept; + } + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PUBLICKEY | + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + // not authenticated, send default message + goto deny; + + case SSH_AUTH_METHOD_NONE: + default: + printf("User %s wants to auth with unknown auth %d\n", + ssh_message_auth_user(message), + ssh_message_subtype(message)); + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PUBLICKEY | + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + goto deny; + } + break; + default: + printf("Unknown auth request %d\n", ssh_message_type(message)); + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PUBLICKEY | + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + } + +deny: + return 1; +accept: + ssh_set_message_callback(sshd.session, channel_open_callback, &sshd.channel); + ssh_message_auth_reply_success(message,0); + return 0; +} + +int main(int argc, char **argv){ + ssh_bind sshbind; + int r; + + sshd.argv = argv; + sshd.event = ssh_event_new(); + if(sshd.event == NULL) { + printf("Couldn't get a event\n"); + return -1; + } + + sshbind = ssh_bind_new(); + sshd.session = ssh_new(); + + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, + KEYS_FOLDER "ssh_host_dsa_key"); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, + KEYS_FOLDER "ssh_host_rsa_key"); + +#ifdef HAVE_ARGP_H + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ + argp_parse (&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; +#endif +#ifdef WITH_PCAP + set_pcap(sshd.session); +#endif + + printf("\n\n#############################################################\n"); + printf("\n\n#############################################################\n"); + + if(ssh_bind_listen(sshbind)<0){ + printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); + return 1; + } + sshd.bind_fd = ssh_bind_get_fd(sshbind); + printf("Started sample libssh sshd on port %d\n", port); + printf("You can login as the user %s with the password %s\n", SSHD_USER, + SSHD_PASSWORD); + + ssh_set_message_callback(sshd.session, authenticate_callback, NULL); + + r = ssh_bind_accept(sshbind, sshd.session); + if(r==SSH_ERROR){ + printf("Error accepting a connection: %s\n", ssh_get_error(sshbind)); + return 1; + } + + sshd.session_fd = ssh_get_fd(sshd.session); + + if (ssh_handle_key_exchange(sshd.session)) { + printf("ssh_handle_key_exchange: %s\n", ssh_get_error(sshd.session)); + return 1; + } + + if(ssh_event_add_session(sshd.event, sshd.session) != SSH_OK) { + printf("Couldn't add the session to the event\n"); + return -1; + } + + printf("it works !\n"); + + do { + printf("dopoll\n"); + ssh_event_dopoll(sshd.event, 10000); + } while(ssh_is_connected(sshd.session)); + + ssh_event_remove_session(sshd.event, sshd.session); + + ssh_event_free(sshd.event); + + ssh_disconnect(sshd.session); + ssh_bind_free(sshbind); +#ifdef WITH_PCAP + cleanup_pcap(); +#endif + ssh_finalize(); + + reexec(); + return 0; +} + |