From 8c04f0d56d88ebea808d5505dcee07e8d197e360 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 20 Jun 2018 13:23:48 +0200 Subject: Made a working ssh proxy server --- src/session.c | 256 ++++++++++++---------------------------------------------- 1 file changed, 53 insertions(+), 203 deletions(-) (limited to 'src/session.c') diff --git a/src/session.c b/src/session.c index 6186e6c..90e2855 100644 --- a/src/session.c +++ b/src/session.c @@ -8,178 +8,76 @@ #include #include -#include "pty.h" +#include "../config.h" +#include "proxy.h" #include "session.h" -#define USER "julien" -#define PASS "graou" -#define BUF_SIZE 1048576 -#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) -#define SFTP_SERVER_PATH "/usr/lib/sftp-server" - -// callback function for channel data and exceptions -static int data_function(ssh_session session, ssh_channel channel, void *data, - uint32_t len, int is_stderr, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - (void) session; - (void) channel; - (void) is_stderr; - - if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) { - return 0; - } - - return write(cdata->child_stdin, (char *) data, len); -} - -static int exec_request(ssh_session session, ssh_channel channel, - const char *command, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - - (void) session; - (void) channel; - - if(cdata->pid > 0) { - return SSH_ERROR; - } - - if (cdata->pty_master != -1 && cdata->pty_slave != -1) { - return exec_pty("-c", command, cdata); - } - return exec_nopty(command, cdata); -} - -static int shell_request(ssh_session session, ssh_channel channel, - void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - +int auth_pubkey(ssh_session session, const char *user, + struct ssh_key_struct *pubkey, + char signature_state, void *userdata) { + struct session_data_struct *sdata = (struct session_data_struct *) userdata; (void) session; - (void) channel; - if(cdata->pid > 0) { + // For some reason, libssh can call this twice for the same key + if (sdata->authenticated == 1) return SSH_ERROR; - } - if (cdata->pty_master != -1 && cdata->pty_slave != -1) { - return exec_pty("-l", NULL, cdata); + if (signature_state != SSH_PUBLICKEY_STATE_NONE && signature_state != SSH_PUBLICKEY_STATE_VALID) { + fprintf(stderr, "Invalid signature state\n"); + sdata->auth_attempts++; + return SSH_AUTH_DENIED; } - /* Client requested a shell without a pty, let's pretend we allow that */ - return SSH_OK; -} -static int subsystem_request(ssh_session session, ssh_channel channel, - const char *subsystem, void *userdata) { - /* subsystem requests behave simillarly to exec requests. */ - if (strcmp(subsystem, "sftp") == 0) { - return exec_request(session, channel, SFTP_SERVER_PATH, userdata); - } - return SSH_ERROR; -} + // TODO check for an invite -static int auth_password(ssh_session session, const char *user, - const char *pass, void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - (void) session; - - if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) { + ssh_key reference_key = ssh_key_new(); + ssh_pki_import_pubkey_base64(USER_RSA_PUBKEY, SSH_KEYTYPE_RSA, &reference_key); // TODO fetch all pubkeys from db + if (!ssh_key_cmp(pubkey, reference_key, SSH_KEY_CMP_PUBLIC)) { sdata->authenticated = 1; + ssh_key_free(reference_key); + size_t len = strnlen(user, MAX_HOSTNAME_LENGTH + 1); + if (len == MAX_HOSTNAME_LENGTH + 1) { + fprintf(stderr, "Hostname too long, max length is %d.\n", MAX_HOSTNAME_LENGTH); + return SSH_ERROR; + } + sdata->login_username = malloc(len+1); + memset(sdata->login_username, 0, len+1); + strncpy(sdata->login_username, user, len); return SSH_AUTH_SUCCESS; + } else { + ssh_key_free(reference_key); + sdata->auth_attempts++; + return SSH_AUTH_DENIED; } - - sdata->auth_attempts++; - return SSH_AUTH_DENIED; } -static ssh_channel channel_open(ssh_session session, void *userdata) { +ssh_channel channel_open(ssh_session session, void *userdata) { struct session_data_struct *sdata = (struct session_data_struct *) userdata; - sdata->channel = ssh_channel_new(session); - return sdata->channel; -} - -static int process_stdout(socket_t fd, int revents, void *userdata) { - char buf[BUF_SIZE]; - int n = -1; - ssh_channel channel = (ssh_channel) userdata; - - if (channel != NULL && (revents & POLLIN) != 0) { - n = read(fd, buf, BUF_SIZE); - if (n > 0) { - ssh_channel_write(channel, buf, n); - // TODO implement logging here - } - } - - return n; -} - -static int process_stderr(socket_t fd, int revents, void *userdata) { - char buf[BUF_SIZE]; - int n = -1; - ssh_channel channel = (ssh_channel) userdata; - - if (channel != NULL && (revents & POLLIN) != 0) { - n = read(fd, buf, BUF_SIZE); - if (n > 0) { - ssh_channel_write_stderr(channel, buf, n); - } + if (sdata->channel == NULL) { + sdata->channel = ssh_channel_new(session); + return sdata->channel; + } else { + // Only one channel allowed + return NULL; } - - return n; } void handle_session(ssh_event event, ssh_session session) { - int n, rc; - - /* Structure for storing the pty size. */ - struct winsize wsize = { - .ws_row = 0, - .ws_col = 0, - .ws_xpixel = 0, - .ws_ypixel = 0 - }; - - /* Our struct holding information about the channel. */ - struct channel_data_struct cdata = { - .pid = 0, - .pty_master = -1, - .pty_slave = -1, - .child_stdin = -1, - .child_stdout = -1, - .child_stderr = -1, - .event = NULL, - .winsize = &wsize - }; - /* Our struct holding information about the session. */ struct session_data_struct sdata = { .channel = NULL, .auth_attempts = 0, - .authenticated = 0 - }; - - struct ssh_channel_callbacks_struct channel_cb = { - .userdata = &cdata, - .channel_pty_request_function = pty_request, - .channel_pty_window_change_function = pty_resize, - .channel_shell_request_function = shell_request, - .channel_exec_request_function = exec_request, - .channel_data_function = data_function, - .channel_subsystem_request_function = subsystem_request + .authenticated = 0, + .login_username = NULL }; struct ssh_server_callbacks_struct server_cb = { .userdata = &sdata, - .auth_password_function = auth_password, + .auth_pubkey_function = auth_pubkey, .channel_open_request_session_function = channel_open, }; - ssh_callbacks_init(&server_cb); - ssh_callbacks_init(&channel_cb); - ssh_set_server_callbacks(session, &server_cb); if (ssh_handle_key_exchange(session) != SSH_OK) { @@ -187,14 +85,18 @@ void handle_session(ssh_event event, ssh_session session) { return; } - ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); + ssh_set_auth_methods(session, SSH_AUTH_METHOD_PUBLICKEY); ssh_event_add_session(event, session); - n = 0; - while (sdata.authenticated == 0 || sdata.channel == NULL) { + for (int n=0; sdata.authenticated == 0 || sdata.channel == NULL; n++) { /* If the user has used up all attempts, or if he hasn't been able to * authenticate in 10 seconds (n * 100ms), disconnect. */ - if (sdata.auth_attempts >= 3 || n >= 100) { + if (sdata.auth_attempts >= 3) { + fprintf(stderr, "Closing connection after 3 failed auth attempts\n"); + return; + } + if (n >= 100) { + fprintf(stderr, "Closing connection after 10 seconds without successfull authentication\n"); return; } @@ -202,70 +104,18 @@ void handle_session(ssh_event event, ssh_session session) { fprintf(stderr, "%s\n", ssh_get_error(session)); return; } - n++; } - ssh_set_channel_callbacks(sdata.channel, &channel_cb); - - do { - /* Poll the main event which takes care of the session, the channel and - * even our child process's stdout/stderr (once it's started). */ - if (ssh_event_dopoll(event, -1) == SSH_ERROR) { - ssh_channel_close(sdata.channel); - } + handle_proxy_session(event, session, sdata.channel, sdata.login_username); - /* If child process's stdout/stderr has been registered with the event, - * or the child process hasn't started yet, continue. */ - if (cdata.event != NULL || cdata.pid == 0) { - continue; - } - /* Executed only once, once the child process starts. */ - cdata.event = event; - /* If stdout valid, add stdout to be monitored by the poll event. */ - if (cdata.child_stdout != -1) { - if (ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout, - sdata.channel) != SSH_OK) { - fprintf(stderr, "Failed to register stdout to poll context\n"); - ssh_channel_close(sdata.channel); - } - } - - /* If stderr valid, add stderr to be monitored by the poll event. */ - if (cdata.child_stderr != -1){ - if (ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr, - sdata.channel) != SSH_OK) { - fprintf(stderr, "Failed to register stderr to poll context\n"); - ssh_channel_close(sdata.channel); - } - } - } while(ssh_channel_is_open(sdata.channel) && - (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0)); - - close(cdata.pty_master); - close(cdata.child_stdin); - close(cdata.child_stdout); - close(cdata.child_stderr); - - /* Remove the descriptors from the polling context, since they are now - * closed, they will always trigger during the poll calls. */ - ssh_event_remove_fd(event, cdata.child_stdout); - ssh_event_remove_fd(event, cdata.child_stderr); - - /* If the child process exited. */ - if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) { - rc = WEXITSTATUS(rc); - ssh_channel_request_send_exit_status(sdata.channel, rc); - /* If client terminated the channel or the process did not exit nicely, - * but only if something has been forked. */ - } else if (cdata.pid > 0) { - kill(cdata.pid, SIGKILL); + if (ssh_channel_is_open(sdata.channel)) { + ssh_channel_close(sdata.channel); } - ssh_channel_send_eof(sdata.channel); - ssh_channel_close(sdata.channel); - /* Wait up to 5 seconds for the client to terminate the session. */ - for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { + for (int n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { ssh_event_dopoll(event, 100); } + free(sdata.login_username); + ssh_event_remove_session(event, session); } -- cgit v1.2.3