aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormilo <milo@r0ot.me>2011-02-20 13:07:34 +0100
committermilo <milo@r0ot.me>2011-02-20 14:37:43 +0100
commit000d659ea2eb709bf99e27c4c76233032869294f (patch)
tree8eaf5333f858a388119ebd9a392eac23d5113180
parentddb1c1838f35818db92809d92c999a1352be5521 (diff)
downloadlibssh-000d659ea2eb709bf99e27c4c76233032869294f.tar.gz
libssh-000d659ea2eb709bf99e27c4c76233032869294f.tar.xz
libssh-000d659ea2eb709bf99e27c4c76233032869294f.zip
examples: Added a event context based sshd example.
-rw-r--r--ConfigureChecks.cmake1
-rw-r--r--examples/CMakeLists.txt6
-rw-r--r--examples/samplesshd-tty.c453
3 files changed, 460 insertions, 0 deletions
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index fecfe34..28862b2 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -111,6 +111,7 @@ if (UNIX)
endif (HAVE_LIBRT)
endif (NOT LINUX)
+ check_library_exists(util forkpty "" HAVE_LIBUTIL)
check_function_exists(getaddrinfo HAVE_GETADDRINFO)
check_function_exists(poll HAVE_POLL)
check_function_exists(select HAVE_SELECT)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 1ccd8f2..5513b75 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -35,6 +35,12 @@ if (LINUX)
add_executable(samplesshd-kbdint samplesshd-kbdint.c)
target_link_libraries(samplesshd-kbdint ${LIBSSH_SHARED_LIBRARY})
+
+ if (HAVE_LIBUTIL)
+ add_executable(samplesshd-tty samplesshd-tty.c)
+ target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} util)
+ endif (HAVE_LIBUTIL)
+
endif (WITH_SERVER)
endif (LINUX)
diff --git a/examples/samplesshd-tty.c b/examples/samplesshd-tty.c
new file mode 100644
index 0000000..c70f6bd
--- /dev/null
+++ b/examples/samplesshd-tty.c
@@ -0,0 +1,453 @@
+/* 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 "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>
+
+#define SSHD_USER "libssh"
+#define SSHD_PASSWORD "libssh"
+
+#ifndef KEYS_FOLDER
+#ifdef _WIN32
+#define KEYS_FOLDER
+#else
+#define KEYS_FOLDER "/etc/ssh/"
+#endif
+#endif
+
+#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(char *user, char *password){
+ 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 */
+
+static int authenticate(ssh_session session) {
+ ssh_message message;
+
+ do {
+ message=ssh_message_get(session);
+ if(!message)
+ break;
+ switch(ssh_message_type(message)){
+ case SSH_REQUEST_AUTH:
+ switch(ssh_message_subtype(message)){
+ 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))){
+ ssh_message_auth_reply_success(message,0);
+ ssh_message_free(message);
+ return 1;
+ }
+ ssh_message_auth_set_methods(message,
+ SSH_AUTH_METHOD_PASSWORD |
+ SSH_AUTH_METHOD_INTERACTIVE);
+ // not authenticated, send default message
+ ssh_message_reply_default(message);
+ break;
+
+ 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_PASSWORD |
+ SSH_AUTH_METHOD_INTERACTIVE);
+ ssh_message_reply_default(message);
+ break;
+ }
+ break;
+ default:
+ ssh_message_auth_set_methods(message,
+ SSH_AUTH_METHOD_PASSWORD |
+ SSH_AUTH_METHOD_INTERACTIVE);
+ ssh_message_reply_default(message);
+ }
+ ssh_message_free(message);
+ } while (1);
+ return 0;
+}
+
+static int copy_fd_to_chan(socket_t fd, int revents, void *userdata) {
+ ssh_channel chan = (ssh_channel)userdata;
+ char buf[2048];
+ int sz = 0;
+
+ if(!chan) {
+ close(fd);
+ return -1;
+ }
+ if(revents & POLLIN) {
+ sz = read(fd, buf, 2048);
+ if(sz > 0) {
+ ssh_channel_write(chan, buf, sz);
+ }
+ }
+ if(revents & POLLHUP) {
+ ssh_channel_close(chan);
+ sz = -1;
+ }
+ return sz;
+}
+
+static int copy_chan_to_fd(ssh_session session,
+ ssh_channel channel,
+ void *data,
+ uint32_t len,
+ int is_stderr,
+ void *userdata) {
+ int fd = *(int*)userdata;
+ int sz;
+ (void)session;
+ (void)channel;
+ (void)is_stderr;
+
+ sz = write(fd, data, len);
+ return sz;
+}
+
+static void chan_close(ssh_session session, ssh_channel channel, void *userdata) {
+ int fd = *(int*)userdata;
+ (void)session;
+ (void)channel;
+
+ close(fd);
+}
+
+struct ssh_channel_callbacks_struct cb = {
+ .channel_data_function = copy_chan_to_fd,
+ .channel_eof_function = chan_close,
+ .channel_close_function = chan_close,
+ .userdata = NULL
+};
+
+static int main_loop(ssh_channel chan) {
+ ssh_session session = ssh_channel_get_session(chan);
+ socket_t fd;
+ struct termios *term = NULL;
+ struct winsize *win = NULL;
+ pid_t childpid;
+ ssh_event event;
+ short events;
+
+
+ childpid = forkpty(&fd, NULL, term, win);
+ if(childpid == 0) {
+ execl("/bin/bash", "/bin/bash", (char *)NULL);
+ abort();
+ }
+
+ cb.userdata = &fd;
+ ssh_callbacks_init(&cb);
+ ssh_set_channel_callbacks(chan, &cb);
+
+ events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
+
+ event = ssh_event_new();
+ if(event == NULL) {
+ printf("Couldn't get a event\n");
+ return -1;
+ }
+ if(ssh_event_add_fd(event, fd, events, copy_fd_to_chan, chan) != SSH_OK) {
+ printf("Couldn't add an fd to the event\n");
+ return -1;
+ }
+ if(ssh_event_add_session(event, session) != SSH_OK) {
+ printf("Couldn't add the session to the event\n");
+ return -1;
+ }
+
+ do {
+ ssh_event_dopoll(event, 1000);
+ } while(!ssh_channel_is_closed(chan));
+
+ ssh_event_remove_fd(event, fd);
+
+ ssh_event_remove_session(event, session);
+
+ ssh_event_free(event);
+ return 0;
+}
+
+
+int main(int argc, char **argv){
+ ssh_session session;
+ ssh_bind sshbind;
+ ssh_message message;
+ ssh_channel chan=0;
+ int auth=0;
+ int shell=0;
+ int r;
+
+ sshbind=ssh_bind_new();
+ 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(session);
+#endif
+
+ if(ssh_bind_listen(sshbind)<0){
+ printf("Error listening to socket: %s\n", ssh_get_error(sshbind));
+ return 1;
+ }
+ 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);
+ r = ssh_bind_accept(sshbind, session);
+ if(r==SSH_ERROR){
+ printf("Error accepting a connection: %s\n", ssh_get_error(sshbind));
+ return 1;
+ }
+ if (ssh_handle_key_exchange(session)) {
+ printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session));
+ return 1;
+ }
+
+ /* proceed to authentication */
+ auth = authenticate(session);
+ if(!auth){
+ printf("Authentication error: %s\n", ssh_get_error(session));
+ ssh_disconnect(session);
+ return 1;
+ }
+
+
+ /* wait for a channel session */
+ do {
+ message = ssh_message_get(session);
+ if(message){
+ if(ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN &&
+ ssh_message_subtype(message) == SSH_CHANNEL_SESSION) {
+ chan = ssh_message_channel_request_open_reply_accept(message);
+ ssh_message_free(message);
+ break;
+ } else {
+ ssh_message_reply_default(message);
+ ssh_message_free(message);
+ }
+ } else {
+ break;
+ }
+ } while(!chan);
+
+ if(!chan) {
+ printf("Error: cleint did not ask for a channel session (%s)\n",
+ ssh_get_error(session));
+ ssh_finalize();
+ return 1;
+ }
+
+
+ /* wait for a shell */
+ do {
+ message = ssh_message_get(session);
+ if(message != NULL) {
+ if(ssh_message_type(message) == SSH_REQUEST_CHANNEL) {
+ if(ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_SHELL) {
+ shell = 1;
+ ssh_message_channel_request_reply_success(message);
+ ssh_message_free(message);
+ break;
+ } else if(ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_PTY) {
+ ssh_message_channel_request_reply_success(message);
+ ssh_message_free(message);
+ continue;
+ }
+ }
+ ssh_message_reply_default(message);
+ ssh_message_free(message);
+ } else {
+ break;
+ }
+ } while(!shell);
+
+ if(!shell) {
+ printf("Error: No shell requested (%s)\n", ssh_get_error(session));
+ return 1;
+ }
+
+ printf("it works !\n");
+
+ main_loop(chan);
+
+ ssh_disconnect(session);
+ ssh_bind_free(sshbind);
+#ifdef WITH_PCAP
+ cleanup_pcap();
+#endif
+ ssh_finalize();
+ return 0;
+}
+