From a3f34674c6317930362c99f0fc40d835234843e5 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Thu, 14 Feb 2019 20:51:41 +0100 Subject: Moved code around --- CMakeLists.txt | 4 +- bastion/CMakeLists.txt | 10 +++ bastion/client.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++ bastion/client.h | 22 +++++ bastion/main.c | 133 +++++++++++++++++++++++++++++ bastion/proxy.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++ bastion/proxy.h | 18 ++++ bastion/recording.c | 126 ++++++++++++++++++++++++++++ bastion/recording.h | 12 +++ bastion/session.c | 118 ++++++++++++++++++++++++++ bastion/session.h | 18 ++++ bastion/state.c | 80 ++++++++++++++++++ bastion/state.h | 12 +++ common/CMakeLists.txt | 3 + common/mysql.c | 188 +++++++++++++++++++++++++++++++++++++++++ common/mysql.h | 22 +++++ config.h | 3 - src/CMakeLists.txt | 9 -- src/client.c | 224 ------------------------------------------------- src/client.h | 22 ----- src/main.c | 133 ----------------------------- src/mysql.c | 188 ----------------------------------------- src/mysql.h | 22 ----- src/proxy.c | 219 ----------------------------------------------- src/proxy.h | 18 ---- src/recording.c | 126 ---------------------------- src/recording.h | 12 --- src/session.c | 118 -------------------------- src/session.h | 18 ---- src/state.c | 80 ------------------ src/state.h | 12 --- 31 files changed, 1208 insertions(+), 1205 deletions(-) create mode 100644 bastion/CMakeLists.txt create mode 100644 bastion/client.c create mode 100644 bastion/client.h create mode 100644 bastion/main.c create mode 100644 bastion/proxy.c create mode 100644 bastion/proxy.h create mode 100644 bastion/recording.c create mode 100644 bastion/recording.h create mode 100644 bastion/session.c create mode 100644 bastion/session.h create mode 100644 bastion/state.c create mode 100644 bastion/state.h create mode 100644 common/CMakeLists.txt create mode 100644 common/mysql.c create mode 100644 common/mysql.h delete mode 100644 src/CMakeLists.txt delete mode 100644 src/client.c delete mode 100644 src/client.h delete mode 100644 src/main.c delete mode 100644 src/mysql.c delete mode 100644 src/mysql.h delete mode 100644 src/proxy.c delete mode 100644 src/proxy.h delete mode 100644 src/recording.c delete mode 100644 src/recording.h delete mode 100644 src/session.c delete mode 100644 src/session.h delete mode 100644 src/state.c delete mode 100644 src/state.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d3234c1..d542ec0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,5 +10,7 @@ set(CMAKE_C_FLAGS_RELEASE "-Os") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") option(LIBSSH_VERBOSE_OUTPOUT "whether or not verbose output for libssh mode is activated" OFF) +option(SESSION_RECORDING "whether or not recording feature based on lib termrec is activated" ON) -add_subdirectory(src) +add_subdirectory(bastion) +add_subdirectory(common) diff --git a/bastion/CMakeLists.txt b/bastion/CMakeLists.txt new file mode 100644 index 0000000..aab97e4 --- /dev/null +++ b/bastion/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories("${bastion_SOURCE_DIR}/termrec/libtty/") +include_directories("${bastion_SOURCE_DIR}/common") + +file(GLOB_RECURSE SOURCES *.c) + +add_executable(bastion ${SOURCES}) +add_library(libtty.a STATIC IMPORTED) +set_property(TARGET libtty.a PROPERTY IMPORTED_LOCATION "${bastion_SOURCE_DIR}/libtty.a") +target_link_libraries(bastion libtty.a common) +target_link_libraries(bastion bz2 curl lzma mysqlclient pthread ssh z) diff --git a/bastion/client.c b/bastion/client.c new file mode 100644 index 0000000..11757fc --- /dev/null +++ b/bastion/client.c @@ -0,0 +1,224 @@ +#include +#include +#include + +#include "../config.h" +#include "client.h" +#ifdef SESSION_RECORDING +#include "recording.h" +#endif +#include "mysql.h" +#include "state.h" + +// callback function for channel data and exceptions +static int client_data_function(ssh_session session, ssh_channel channel, void *data, + uint32_t len, int is_stderr, void *userdata) { + struct client_channel_data_struct *cdata = (struct client_channel_data_struct *) userdata; + (void) session; + (void) channel; + (void) is_stderr; + + if (ssh_channel_is_open(cdata->proxy_channel)) { +#ifdef SESSION_RECORDING + record(data, len); +#endif + return ssh_channel_write(cdata->proxy_channel, (char*) data, len); + } else + return SSH_ERROR; +} + +static void client_channel_eof_callback (ssh_session session, ssh_channel channel, void *userdata) +{ + struct client_channel_data_struct *cdata = (struct client_channel_data_struct *) userdata; + (void) session; + (void) channel; + + if (ssh_channel_is_open(cdata->proxy_channel)) + ssh_channel_send_eof(cdata->proxy_channel); +} + +static void client_channel_close_callback (ssh_session session, ssh_channel channel, void *userdata) +{ + struct client_channel_data_struct *cdata = (struct client_channel_data_struct *) userdata; + (void) session; + (void) channel; + + if (ssh_channel_is_open(cdata->proxy_channel)) + ssh_channel_close(cdata->proxy_channel); +} + +static void client_channel_exit_status_callback (ssh_session session, ssh_channel channel, int exit_status, void *userdata) +{ + (void) session; + (void) channel; + (void) userdata; + printf("client exit status callback %d\n", exit_status); +} + +static void client_channel_signal_callback (ssh_session session, ssh_channel channel, + const char *signal, void *userdata) { + (void) session; + (void) channel; + (void) signal; + (void) userdata; + printf("client signal callback\n"); +} + +static void client_channel_exit_signal_callback (ssh_session session, ssh_channel channel, + const char *signal, int core, const char *errmsg, + const char *lang, void *userdata) { + (void) session; + (void) channel; + (void) signal; + (void) core; + (void) errmsg; + (void) lang; + (void) userdata; + printf("client exit signal callback\n"); +} + +struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_channel_data_struct *pdata) +{ + const char * hostname = state_get_ssh_destination(); + struct client_channel_data_struct *cdata = malloc(sizeof(*cdata)); + cdata->event = event; + cdata->my_session = NULL; + cdata->my_channel = NULL; + cdata->proxy_channel = pdata->my_channel; + cdata->client_channel_cb = NULL; + + /* First we try to add the private key that the server will accept */ + struct db_host_info * info = db_get_host_info(hostname); + if (info == NULL) + goto host_info_clean; + + ssh_key privkey = NULL; + if (ssh_pki_import_privkey_base64(info->privkeytxt, NULL, NULL, NULL, &privkey) != SSH_OK) { + printf("Error importing private key"); + goto privkey_clean; + } + + /* We try to connect to the remote server */ + printf("Connecting to %s\n", hostname); + cdata->my_session = ssh_new(); + + ssh_options_set(cdata->my_session, SSH_OPTIONS_HOST, info->address); + ssh_options_set(cdata->my_session, SSH_OPTIONS_USER, info->username); +#ifdef LIBSSH_VERBOSE_OUTPOUT + int verbosity = SSH_LOG_PROTOCOL; + ssh_options_set(cdata->my_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); +#endif + + if (ssh_connect(cdata->my_session) != SSH_OK) { + printf("Error connecting to %s: %s\n", hostname, ssh_get_error(cdata->my_session)); + goto session_clean; + } + + /* We now validate the remote server's public key */ + ssh_key server_pub_key = NULL; + unsigned char * hash = NULL; + size_t hlen; + char * hexa = NULL; + if (ssh_get_server_publickey(cdata->my_session, &server_pub_key) != SSH_OK) { + fprintf(stderr, "Error getting server publickey: %s\n", ssh_get_error(cdata->my_session)); + goto pubkey_clean; + } + if (ssh_get_publickey_hash(server_pub_key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen) != SSH_OK) { + fprintf(stderr, "Error getting publickey hash: %s\n", ssh_get_error(cdata->my_session)); + goto pubkey_hash_clean; + } + hexa = ssh_get_hexa(hash, hlen); + if (strlen(info->hostkeyhash) > 0) { + if (strcmp(hexa, info->hostkeyhash) != 0) { + fprintf(stderr, "Error invalid host key for %s\n", hostname); + goto pubkey_hexa_clean; + } + } else { + // TODO we got a broken sshportal record, we need to fix it but only + // after we completed the migration from sshportal + //db_set_host_publickey_hash(hostname, hexa); + } + ssh_string_free_char(hexa); + ssh_clean_pubkey_hash(&hash); + ssh_key_free(server_pub_key); + + /* With the server checked, we can authenticate */ + if(ssh_userauth_publickey(cdata->my_session, NULL, privkey) == SSH_AUTH_SUCCESS){ + printf("Authentication success\n"); + } else { + printf("Error private key was rejected\n"); + goto session_clean; + } + + /* we open the client channel */ + cdata->my_channel = ssh_channel_new(cdata->my_session); + if (cdata->my_channel == NULL) { + printf("Couldn't open client channel to %s\n", hostname); + goto channel_clean; + } + + /* we open a session channel for the future shell, not suitable for tcp + * forwarding */ + if (ssh_channel_open_session(cdata->my_channel) != SSH_OK) { + printf("Couldn't open the session channel\n"); + goto channel_clean; + } + + cdata->client_channel_cb = malloc(sizeof(*cdata->client_channel_cb)); + memset(cdata->client_channel_cb, 0, sizeof(*cdata->client_channel_cb)); + cdata->client_channel_cb->userdata = cdata; + cdata->client_channel_cb->channel_data_function = client_data_function; + cdata->client_channel_cb->channel_eof_function = client_channel_eof_callback; + cdata->client_channel_cb->channel_close_function = client_channel_close_callback; + cdata->client_channel_cb->channel_exit_status_function = client_channel_exit_status_callback; + cdata->client_channel_cb->channel_signal_function = client_channel_signal_callback; + cdata->client_channel_cb->channel_exit_signal_function = client_channel_exit_signal_callback; + + ssh_callbacks_init(cdata->client_channel_cb); + ssh_set_channel_callbacks(cdata->my_channel, cdata->client_channel_cb); + ssh_event_add_session(event, cdata->my_session); + + // TODO only start recording upong shell_exec or pty_request in proxy.c. + // It will be important when we start supporting scp +#ifdef SESSION_RECORDING + if (init_recorder() != 0) { + goto channel_clean; + } +#endif + + ssh_key_free(privkey); + db_free_host_info(info); + return cdata; + +channel_clean: + ssh_channel_free(cdata->my_channel); + goto session_clean; +pubkey_hexa_clean: + ssh_string_free_char(hexa); +pubkey_hash_clean: + ssh_clean_pubkey_hash(&hash); +pubkey_clean: + ssh_key_free(server_pub_key); +session_clean: + ssh_disconnect(cdata->my_session); + ssh_free(cdata->my_session); + db_free_host_info(info); +privkey_clean: + ssh_key_free(privkey); +host_info_clean: + free(cdata); + return NULL; +} + +void client_cleanup(struct client_channel_data_struct *cdata) +{ +#ifdef SESSION_RECORDING + clean_recorder(); +#endif + ssh_event_remove_session(cdata->event, cdata->my_session); + ssh_channel_free(cdata->my_channel); + ssh_disconnect(cdata->my_session); + ssh_free(cdata->my_session); + free(cdata->client_channel_cb); + free(cdata); +} diff --git a/bastion/client.h b/bastion/client.h new file mode 100644 index 0000000..307b115 --- /dev/null +++ b/bastion/client.h @@ -0,0 +1,22 @@ +#ifndef CLIENT_H_ +#define CLIENT_H_ + +#include + +#include "proxy.h" +#include "session.h" + +/* A userdata struct for channel. */ +struct client_channel_data_struct { + /* Event which is used to poll */ + ssh_event event; + ssh_session my_session; + ssh_channel my_channel; + ssh_channel proxy_channel; + struct ssh_channel_callbacks_struct * client_channel_cb; +}; + +struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_channel_data_struct *pdata); +void client_cleanup(struct client_channel_data_struct *cdata); + +#endif diff --git a/bastion/main.c b/bastion/main.c new file mode 100644 index 0000000..6ea6bcb --- /dev/null +++ b/bastion/main.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include + +#include "../config.h" +#include "mysql.h" +#include "session.h" + +/* SIGCHLD handler for cleaning up dead children. */ +static void sigchld_handler(int signo) { + (void) signo; + while (waitpid(-1, NULL, WNOHANG) > 0); +} + +/* SIGINT handler for cleaning up on forced exit. */ +static ssh_bind sshbind = NULL; +static ssh_session session = NULL; + +__attribute__((noreturn)) static void sigint_handler(int signo) +{ + (void) signo; + ssh_disconnect(session); + ssh_free(session); + ssh_bind_free(sshbind); + ssh_finalize(); + db_clean(); + exit(0); +} + +int main() +{ + // Set up SIGCHLD handler + struct sigaction sa; + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa, NULL) != 0) { + fprintf(stderr, "Failed to register SIGCHLD handler\n"); + return 1; + } + // Set up SIGINT handler + struct sigaction sa2; + sa2.sa_handler = sigint_handler; + sigemptyset(&sa2.sa_mask); + sa2.sa_flags = 0; + if (sigaction(SIGINT, &sa2, NULL) != 0) { + fprintf(stderr, "Failed to register SIGINT handler\n"); + return 1; + } + + // Initializing ssh context + ssh_init(); + + // Initializing ssh_bind + sshbind = ssh_bind_new(); + if (sshbind == NULL) { + fprintf(stderr, "Error initializing ssh_bind\n"); + exit(-1); + } + int listen_port = LISTEN_PORT; + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &listen_port); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, DSAKEY_PATH); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, RSAKEY_PATH); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, ECDSAKEY_PATH); + + if (ssh_bind_listen(sshbind) < 0) { + printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); + ssh_bind_free(sshbind); + ssh_finalize(); + return 1; + } + + while (1) { + session = ssh_new(); + if (session == NULL) { + fprintf(stderr, "Error initializing ssh_session\n"); + break; + } +#ifdef LIBSSH_VERBOSE_OUTPOUT + int verbosity = SSH_LOG_PROTOCOL; + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); +#endif + + // Blocks until there is a new incoming connection + if (ssh_bind_accept(sshbind,session) == SSH_OK){ + switch(fork()) { + case 0: + /* Remove the SIGCHLD handler inherited from parent. */ + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + /* Remove socket binding, which allows us to restart the parent process, without terminating existing sessions. */ + ssh_bind_free(sshbind); + sshbind = NULL; + + if (db_init() !=0) + goto child_cleaning; + + ssh_event event = ssh_event_new(); + if (event != NULL) { + /* Blocks until the SSH session ends */ + handle_session(event, session); + ssh_event_free(event); + } else { + fprintf(stderr, "Could not create polling context\n"); + } +child_cleaning: + ssh_disconnect(session); + ssh_free(session); + ssh_finalize(); + + return 0; + case -1: + fprintf(stderr, "Failed to fork\n"); + } + } else { + fprintf(stderr, "Error accepting a connection : %s\n", ssh_get_error(sshbind)); + ssh_disconnect(session); + ssh_free(session); + ssh_bind_free(sshbind); + ssh_finalize(); + return 1; + } + /* Since the session has been passed to a child fork, do some cleaning up at the parent process. */ + ssh_disconnect(session); + ssh_free(session); + } + ssh_bind_free(sshbind); + ssh_finalize(); + db_clean(); + return 0; +} diff --git a/bastion/proxy.c b/bastion/proxy.c new file mode 100644 index 0000000..7d410db --- /dev/null +++ b/bastion/proxy.c @@ -0,0 +1,219 @@ +#include +#include +#include + +#include "client.h" +#include "mysql.h" +#include "proxy.h" +#include "state.h" + +// callback function for channel data and exceptions +static int proxy_data_function(ssh_session session, ssh_channel channel, void *data, + uint32_t len, int is_stderr, void *userdata) { + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; + (void) session; + (void) channel; + (void) is_stderr; + + if (ssh_channel_is_open(pdata->client_channel)) + return ssh_channel_write(pdata->client_channel, (char*) data, len); + else + return SSH_ERROR; +} + +// callback function for SSH channel PTY request from a client +static int proxy_pty_request(ssh_session session, ssh_channel channel, + const char *term, int cols, int rows, int py, int px, + void *userdata) { + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *)userdata; + + (void) session; + (void) channel; + (void) py; + (void) px; + + // TODO record pty size in recorder + if (ssh_channel_is_open(pdata->client_channel)) { + if (ssh_channel_request_pty_size(pdata->client_channel, term, cols, rows) == SSH_OK) + return SSH_OK; + else + fprintf(stderr, "pty request failed\n"); + } else { + fprintf(stderr, "pty request while client_channel not opened\n"); + } + return SSH_ERROR; +} + +// callback function for SSH channel PTY resize from a client +static int proxy_pty_resize(ssh_session session, ssh_channel channel, int cols, + int rows, int py, int px, void *userdata) { + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *)userdata; + + (void) session; + (void) channel; + (void) py; + (void) px; + + // TODO record pty size in recorder + if (ssh_channel_is_open(pdata->client_channel)) { + if (ssh_channel_change_pty_size(pdata->client_channel, cols, rows) == SSH_OK) + return SSH_OK; + else + fprintf(stderr, "pty resize failed\n"); + } else { + fprintf(stderr, "pty resize while client_channel not opened\n"); + } + return SSH_ERROR; +} + +static int proxy_exec_request(ssh_session session, ssh_channel channel, + const char *command, void *userdata) { + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; + + (void) session; + (void) channel; + + if (ssh_channel_is_open(pdata->client_channel)) { + if (ssh_channel_request_exec(pdata->client_channel, command) == SSH_OK) + return SSH_OK; + else + printf("exec request failed\n"); + } else { + fprintf(stderr, "exec request while client_channel not opened\n"); + } + return SSH_ERROR; +} + +static int proxy_shell_request(ssh_session session, ssh_channel channel, + void *userdata) { + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; + + (void) session; + (void) channel; + + if (ssh_channel_is_open(pdata->client_channel)) { + if (ssh_channel_request_shell(pdata->client_channel) == SSH_OK) + return SSH_OK; + else + fprintf(stderr, "shell request failed\n"); + } else { + fprintf(stderr, "shell request while client channel not opened\n"); + } + return SSH_ERROR; +} + +static int proxy_subsystem_request(ssh_session session, ssh_channel channel, + const char *subsystem, void *userdata) { + (void) session; + (void) channel; + (void) subsystem; + (void) userdata; + return SSH_ERROR; // TODO ssh subsystem request + //if (ssh_channel_is_open(pdata->client_channel)) { + //} +} + +static void proxy_channel_eof_callback (ssh_session session, ssh_channel channel, void *userdata) +{ + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; + (void) session; + (void) channel; + if (ssh_channel_is_open(pdata->client_channel)) + ssh_channel_send_eof(pdata->client_channel); +} + +static void proxy_channel_close_callback (ssh_session session, ssh_channel channel, void *userdata) +{ + struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; + (void) session; + (void) channel; + if (ssh_channel_is_open(pdata->client_channel)) + ssh_channel_close(pdata->client_channel); +} + +static void proxy_channel_exit_status_callback (ssh_session session, ssh_channel channel, int exit_status, void *userdata) +{ + (void) session; + (void) channel; + (void) exit_status; + (void) userdata; + printf("proxy exit status callback\n"); +} + +static void proxy_channel_signal_callback (ssh_session session, ssh_channel channel, + const char *signal, void *userdata) { + (void) session; + (void) channel; + (void) signal; + (void) userdata; + printf("proxy signal callback\n"); +} + +static void proxy_channel_exit_signal_callback (ssh_session session, ssh_channel channel, + const char *signal, int core, const char *errmsg, + const char *lang, void *userdata) { + (void) session; + (void) channel; + (void) signal; + (void) core; + (void) errmsg; + (void) lang; + (void) userdata; + printf("proxy exit signal callback\n"); +} + +void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel) +{ + struct client_channel_data_struct * cdata; + + struct proxy_channel_data_struct pdata = { + .event = event, + .my_session = session, + .my_channel = my_channel, + .client_channel = NULL, + }; + + cdata = client_dial(event, &pdata); + + if (cdata == NULL) { + return; + } + pdata.client_channel = cdata->my_channel; + + /* We tie everything together */ + struct ssh_channel_callbacks_struct channel_cb = { + .userdata = &pdata, + .channel_data_function = proxy_data_function, + .channel_eof_function = proxy_channel_eof_callback, + .channel_close_function = proxy_channel_close_callback, + .channel_signal_function = proxy_channel_signal_callback, + .channel_exit_status_function = proxy_channel_exit_status_callback, + .channel_exit_signal_function = proxy_channel_exit_signal_callback, + .channel_pty_request_function = proxy_pty_request, + .channel_shell_request_function = proxy_shell_request, + .channel_pty_window_change_function = proxy_pty_resize, + .channel_exec_request_function = proxy_exec_request, + .channel_subsystem_request_function = proxy_subsystem_request, + .channel_auth_agent_req_function = NULL, + .channel_x11_req_function = NULL, + .channel_env_request_function = NULL, + .channel_write_wontblock_function = NULL, + }; + ssh_callbacks_init(&channel_cb); + ssh_set_channel_callbacks(my_channel, &channel_cb); + + db_clean(); // we close the mysql connection before the main loop, as to not waste ressources + + do { + /* Poll the main event which takes care of the sessions and channels */ + if (ssh_event_dopoll(event, -1) == SSH_ERROR) { + break; + } + } while(ssh_channel_is_open(my_channel) && ssh_channel_is_open(pdata.client_channel)); + if (ssh_channel_is_open(my_channel)) + ssh_channel_close(my_channel); + if (ssh_channel_is_open(cdata->my_channel)) + ssh_channel_close(cdata->my_channel); + + client_cleanup(cdata); +} diff --git a/bastion/proxy.h b/bastion/proxy.h new file mode 100644 index 0000000..0e42bdc --- /dev/null +++ b/bastion/proxy.h @@ -0,0 +1,18 @@ +#ifndef PROXY_H_ +#define PROXY_H_ + +#include + +#include "session.h" + +/* A userdata struct for channel. */ +struct proxy_channel_data_struct { + /* Event which is used to poll */ + ssh_event event; + ssh_session my_session; + ssh_channel my_channel; + ssh_channel client_channel; +}; +void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel); + +#endif diff --git a/bastion/recording.c b/bastion/recording.c new file mode 100644 index 0000000..7692ff1 --- /dev/null +++ b/bastion/recording.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "recording.h" +#include "state.h" + +#ifdef SESSION_RECORDING +static recorder recorder_handle = NULL; + +void clean_recorder(void) +{ + ttyrec_w_close(recorder_handle); + recorder_handle = NULL; +} + +static char * // returns NULL if error, this char * is to be freed from the calling code +make_filename(void) +{ + char * format = LOG_FILENAME_FORMAT; + char * filename = NULL; + unsigned int fname_pos = 0; + unsigned int format_pos = 0; + + filename = malloc(LOG_FILENAME_MAX_LEN+1); + + size_t format_len = strlen(format); + while (format_pos < format_len + 1 && fname_pos < LOG_FILENAME_MAX_LEN +1) { + if (format[format_pos] == '$') { + format_pos++; + if (format[format_pos] == 'd') { + time_t t; + struct tm * tm; + time(&t); + tm = localtime(&t); + fname_pos += strftime(filename + fname_pos, LOG_FILENAME_MAX_LEN - fname_pos, "%F", tm); + } else if (format[format_pos] == 'h') { + const char * hostname = state_get_ssh_destination(); + size_t len = strlen(hostname); + strcpy(filename + fname_pos, hostname); + fname_pos += len; + } else if (format[format_pos] == 'u') { + const char * username = state_get_bastion_username(); + size_t len = strlen(username); + strcpy(filename + fname_pos, username); + fname_pos += len; + } else if (format[format_pos] == 'i') { + sprintf(filename + fname_pos, "%llu", state_get_session_id()); + fname_pos += strlen(filename + fname_pos); + } + format_pos++; + } else { + filename[fname_pos] = format[format_pos]; + if (filename[fname_pos] == '/') { // We create the corresponding directory if it doesn't exist + filename[fname_pos+1] = '\0'; + DIR* dir = opendir(filename); + if (dir) + closedir(dir); + else { + int ret = mkdir(filename, LOG_DIRECTORY_MODE); + if (ret != 0) { + fprintf(stderr, "Couldn't create log directory %s : %s\n", filename, strerror( errno )); + } + } + } + format_pos++; + fname_pos++; + } + } + + if (filename[fname_pos-1] != '\0') { + fprintf(stderr, "Log file name is too long, check LOG_FILENAME_FORMAT and LOG_FILENAME_MAX_LEN\n"); + free(filename); + filename = NULL; + } + return filename; +} + +char // returns 0 if ok, 1 otherwise +init_recorder(void) +{ + char * filename = make_filename(); + if (filename == NULL) + return 1; + struct timeval tm; + if (gettimeofday(&tm, NULL) != 0) { + fprintf(stderr, "OUPS gettimeofday failed!\n"); + return 1; + } + recorder_handle = ttyrec_w_open(-1, "ttyrec", filename, &tm); + free(filename); + if (recorder_handle == NULL) { + fprintf(stderr, "Couldn't open the session termrec log file.\n"); + return 1; + } + + return 0; +} + +char // returns 0 if ok, greater than 0 otherwise +record(void* data, size_t len) +{ + if(recorder_handle == NULL) + return 0; + + struct timeval tm; + if (gettimeofday(&tm, NULL) != 0) { + fprintf(stderr, "OUPS gettimeofday failed!\n"); + return 1; + } + if (ttyrec_w_write(recorder_handle, &tm, data, (int) len) == 0) { + fprintf(stderr, "OUPS ttyrec_w_write failed!\n"); + return 2; + } + return 0; +} +#endif diff --git a/bastion/recording.h b/bastion/recording.h new file mode 100644 index 0000000..fec76af --- /dev/null +++ b/bastion/recording.h @@ -0,0 +1,12 @@ +#include "../config.h" + +#ifdef SESSION_RECORDING +#ifndef RECORDING_H_ +#define RECORDING_H_ + +void clean_recorder(void); +char init_recorder(void); +char record(void* data, size_t len); + +#endif +#endif diff --git a/bastion/session.c b/bastion/session.c new file mode 100644 index 0000000..3d3b1b7 --- /dev/null +++ b/bastion/session.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "mysql.h" +#include "proxy.h" +#include "session.h" +#include "state.h" + +static int auth_pubkey(ssh_session session, const char *user, ssh_key pubkey, char signature_state, + void *userdata) { + struct session_data_struct *sdata = (struct session_data_struct *) userdata; + (void) session; + + // For some reason, libssh can call this twice for the same key + if (sdata->authenticated == 1) + return SSH_ERROR; + + 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; + } + + // TODO check for an invite + + char * bastion_username = db_get_username_from_pubkey(pubkey); + if (bastion_username != NULL) { + sdata->authenticated = 1; + if (state_set_ssh_destination(user) != 0) + return SSH_ERROR; + // TODO check access rights and host configs + state_set_bastion_username(bastion_username); + unsigned long long session_id = db_init_session_and_get_id(user, bastion_username); + state_set_session_id(session_id); + free(bastion_username); + return SSH_AUTH_SUCCESS; + } else { + free(bastion_username); + sdata->auth_attempts++; + return SSH_AUTH_DENIED; + } +} + +static ssh_channel channel_open(ssh_session session, void *userdata) { + struct session_data_struct *sdata = (struct session_data_struct *) userdata; + + if (sdata->channel == NULL) { + sdata->channel = ssh_channel_new(session); + return sdata->channel; + } else { + // Only one channel allowed + return NULL; + } +} + +void handle_session(ssh_event event, ssh_session session) { + /* Our struct holding information about the session. */ + struct session_data_struct sdata = { + .channel = NULL, + .auth_attempts = 0, + .authenticated = 0, + }; + + struct ssh_server_callbacks_struct server_cb = { + .userdata = &sdata, + .auth_pubkey_function = auth_pubkey, + .channel_open_request_session_function = channel_open, + }; + ssh_callbacks_init(&server_cb); + ssh_set_server_callbacks(session, &server_cb); + + if (ssh_handle_key_exchange(session) != SSH_OK) { + fprintf(stderr, "%s\n", ssh_get_error(session)); + return; + } + + ssh_set_auth_methods(session, SSH_AUTH_METHOD_PUBLICKEY); + ssh_event_add_session(event, session); + + 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) { + 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; + } + + if (ssh_event_dopoll(event, 100) == SSH_ERROR) { + fprintf(stderr, "%s\n", ssh_get_error(session)); + return; + } + } + + handle_proxy_session(event, session, sdata.channel); + + if (ssh_channel_is_open(sdata.channel)) { + ssh_channel_close(sdata.channel); + } + + /* Wait up to 5 seconds for the client to terminate the session. */ + for (int n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { + ssh_event_dopoll(event, 100); + } + state_clean(); + ssh_event_remove_session(event, session); +} diff --git a/bastion/session.h b/bastion/session.h new file mode 100644 index 0000000..d0c8eab --- /dev/null +++ b/bastion/session.h @@ -0,0 +1,18 @@ +#ifndef SESSION_H_ +#define SESSION_H_ + +#include + +#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) + +/* A userdata struct for session. */ +struct session_data_struct { + /* Pointer to the channel the session will allocate. */ + ssh_channel channel; + int auth_attempts; + int authenticated; +}; + +void handle_session(ssh_event event, ssh_session session); + +#endif diff --git a/bastion/state.c b/bastion/state.c new file mode 100644 index 0000000..dd9cbf3 --- /dev/null +++ b/bastion/state.c @@ -0,0 +1,80 @@ +#include +#include +#include + +#include "../config.h" +#include "state.h" + +struct state { + unsigned long long session_id; + char * destination; + char * bastion_username; +}; + +static struct state state = {0}; + +char // returns 0 if ok, greater than 0 otherwise +state_set_ssh_destination(const char * name) +{ + if (state.destination != NULL) { + fprintf(stderr, "BUG found, attempting to overwrite state.destination that has already been set\n"); + return 1; + } + size_t len = strnlen(name, MAX_HOSTNAME_LENGTH + 1); + if (len >= MAX_HOSTNAME_LENGTH + 1) { + fprintf(stderr, "Hostname too long, max length is %d.\n", MAX_HOSTNAME_LENGTH); + return 2; + } + state.destination = malloc(len+1); + strncpy(state.destination, name, len+1); + return 0; +} + +const char * state_get_ssh_destination(void) +{ + return state.destination; +} + +char // return 0 if ok, greater than 0 otherwise +state_set_bastion_username(const char * name) +{ + if (state.bastion_username != NULL) { + fprintf(stderr, "BUG found, attempting to overwrite state.bastion_username that has already been set\n"); + return 1; + } + size_t len = strnlen(name, MAX_USERNAME_LENGTH + 1); + if (len >= MAX_USERNAME_LENGTH + 1) { + fprintf(stderr, "Username too long, max length is %d.\n", MAX_USERNAME_LENGTH); + return 1; + } + state.bastion_username = malloc(len+1); + strncpy(state.bastion_username, name, len+1); + return 0; +} + +const char * state_get_bastion_username(void) +{ + return state.bastion_username; +} + +char // return 0 if ok, greater than 0 otherwise +state_set_session_id(const unsigned long long id) +{ + if (state.session_id != 0) { + fprintf(stderr, "BUG found, attempting to set a state.session_id that has already been set\n"); + return 1; + } + state.session_id = id; + return 0; +} + +unsigned long long state_get_session_id(void) +{ + return state.session_id; +} + +void state_clean(void) +{ + free(state.destination); + state.destination = NULL; +} diff --git a/bastion/state.h b/bastion/state.h new file mode 100644 index 0000000..0ab70bc --- /dev/null +++ b/bastion/state.h @@ -0,0 +1,12 @@ +#ifndef STATE_H_ +#define STATE_H_ + +char state_set_ssh_destination(const char * dest); +const char * state_get_ssh_destination(void); +char state_set_bastion_username(const char * name); +const char * state_get_bastion_username(void); +char state_set_session_id(const unsigned long long id); +unsigned long long state_get_session_id(void); +void state_clean(void); + +#endif diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..8636758 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB_RECURSE SOURCES *.c) + +ADD_LIBRARY(common STATIC ${SOURCES}) diff --git a/common/mysql.c b/common/mysql.c new file mode 100644 index 0000000..0931116 --- /dev/null +++ b/common/mysql.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include + +#include "../config.h" +#include "mysql.h" + +static MYSQL *db; + +char // returns 0 if ok, greater than 0 otherwise +db_init(void) +{ + printf("MySQL client version: %s\n", mysql_get_client_info()); + db = mysql_init(NULL); + if (db == NULL) { + fprintf(stderr, "%s\n", mysql_error(db)); + return 1; + } + if (mysql_real_connect(db, MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, 0, NULL, 0) == NULL) { + fprintf(stderr, "%s\n", mysql_error(db)); + mysql_close(db); + return 1; + } + return 0; +} + +void db_clean(void) +{ + mysql_close(db); + db = NULL; +} + +char * // returns NULL if no user found, this char * is to be freed from the calling code +db_get_username_from_pubkey(ssh_key pubkey) +{ + int res = mysql_query(db, "SELECT name, authorized_key FROM users, user_keys WHERE users.id = user_keys.user_id"); + if (res != 0) { + fprintf(stderr, "WARNING: Couldn't get usernames from database.\n"); + return NULL; + } + MYSQL_RES *result = mysql_store_result(db); + if (result == NULL) { + fprintf(stderr, "FATAL: Couldn't retrieve public keys from database.\n"); + return NULL; + } + + MYSQL_ROW row; + while ((row = mysql_fetch_row(result))) { + char * rsa = "ssh-rsa "; + if (strncmp (row[1], rsa, strlen(rsa)) != 0) { + fprintf(stderr, "Unsupported public key type for user %s : %s\n", row[0], row[1]); + } else { + ssh_key tmp_key; + if (ssh_pki_import_pubkey_base64(row[1] + strlen(rsa), SSH_KEYTYPE_RSA, &tmp_key) != SSH_OK) { + fprintf(stderr, "Error importing public key for user %s : %s\n", row[0], row[1]); + } else if (!ssh_key_cmp(pubkey, tmp_key, SSH_KEY_CMP_PUBLIC)) { + size_t len = strlen(row[0]); + char * username = malloc(len+1); + strcpy(username, row[0]); + ssh_key_free(tmp_key); + mysql_free_result(result); + return username; + } else { + ssh_key_free(tmp_key); + } + } + } + + fprintf(stderr, "ERROR: Didn't find public key in database.\n"); + mysql_free_result(result); + return NULL; +} + +struct db_host_info * // returns NULL if no key found, this char * is to be freed from the calling code +db_get_host_info(const char * hostname) +{ + char buff[255]; + sprintf(buff, "SELECT priv_key, url, host_key FROM ssh_keys, hosts WHERE ssh_keys.id = hosts.ssh_key_id and hosts.name = \"%s\"", hostname); + int res = mysql_query(db, buff); + if (res != 0) { + fprintf(stderr, "WARNING: Couldn't query db for server infos for host %s\n", hostname); + return NULL; + } + MYSQL_RES *result = mysql_store_result(db); + if (result == NULL) { + fprintf(stderr, "FATAL: Couldn't retrieve server infos for %s from database.\n", hostname); + return NULL; + } + + MYSQL_ROW row = mysql_fetch_row(result); + if (row == NULL) { + fprintf(stderr, "FATAL: Couldn't retrieve server db results for %s from database.\n", hostname); + mysql_free_result(result); + return NULL; + } + + struct db_host_info * info = malloc(sizeof(struct db_host_info)); + memset(info, 0, sizeof(struct db_host_info)); + + size_t len = strlen(row[0]); + info->privkeytxt = malloc(len+1); + strcpy(info->privkeytxt, row[0]); + + if (strncmp(row[1], "ssh://", 6) != 0) { + fprintf(stderr, "FATAL: invalid host url %s\n", row[1]); + return NULL; + } + size_t at_pos = 0; + char done = 0; + for(size_t i = 6; !done; ++i) { + switch(*(row[1]+i)) { + case '@': + info->username = malloc(i-6+1); + strncpy(info->username, row[1]+6, i-6); + info->username[i-6] = '\0'; + at_pos = i; + break; + case '\0': + info->address = malloc(i-at_pos); + strncpy(info->address, row[1]+at_pos+1, i-at_pos-1); + info->address[i-at_pos-1] = '\0'; + done = 1; + break; + } + if (i > MAX_HOSTNAME_LENGTH + MAX_USERNAME_LENGTH + 6 + 1) { + fprintf(stderr, "FATAL: Couldn't parse host url for host %s, too long.\n", hostname); + if (info->username != NULL) + free(info->username); + return NULL; + } + } + + len = strlen(row[2]); + info->hostkeyhash = malloc(len+1); + strcpy(info->hostkeyhash, row[2]); + + mysql_free_result(result); + return info; +} + +void db_set_host_publickey_hash(const char * hostname, const char * hash) +{ + char buff[255]; + sprintf(buff, "UPDATE ssh_keys, hosts SET host_key = \"%s\" WHERE ssh_keys.id = hosts.ssh_key_id and hosts.name = \"%s\"", hash, hostname); + int res = mysql_query(db, buff); + if (res != 0) { + fprintf(stderr, "WARNING: Couldn't set host key for host %s: %s\n", hostname, hash); + return; + } + res = mysql_commit(db); + if (res != 0) { + fprintf(stderr, "WARNING: Couldn't commit after setting host key for host %s: %s\n", hostname, hash); + } +} + +unsigned long long // returns 0 on error, or the session_id +db_init_session_and_get_id(const char * hostname, const char * username) +{ + char buff[255]; + sprintf(buff, "INSERT INTO sessions (created_at, status, user_id, host_id) SELECT NOW(), \"opened\", users.id, hosts.id from users, hosts WHERE users.name = \"%s\" and hosts.name = \"%s\"", username, hostname); + int res = mysql_query(db, buff); + if (res != 0) { + fprintf(stderr, "FATAL: Couldn't insert new session in database for %s to %s\n", username, hostname); + return 0; + } + unsigned long long id = mysql_insert_id(db); + if (id == 0) { + fprintf(stderr, "FATAL: Didn't get proper mysql last insert id after inserting new session for %s to %s\n", username, hostname); + return 0; + } + res = mysql_commit(db); + if (res != 0) { + fprintf(stderr, "FATAL: Couldn't commit after inserting session for %s to %s\n", username, hostname); + return 0; + } + return id; +} + +void db_free_host_info(struct db_host_info * info) +{ + free(info->privkeytxt); + free(info->address); + free(info->username); + free(info->hostkeyhash); + free(info); +} diff --git a/common/mysql.h b/common/mysql.h new file mode 100644 index 0000000..8c6040f --- /dev/null +++ b/common/mysql.h @@ -0,0 +1,22 @@ +#ifndef MYSQL_H_ +#define MYSQL_H_ + +struct db_host_info { + char * privkeytxt; + char * address; + char * username; + char * hostkeyhash; +}; + +char db_init(void); +void db_clean(void); +char * // returns NULL if no user found, this char * is to be freed from the calling code +db_get_username_from_pubkey(ssh_key pubkey); +struct db_host_info * // returns NULL if no key found, this char * is to be freed from the calling code +db_get_host_info(const char * hostname); +void db_set_host_publickey_hash(const char * hostname, const char * hash); +unsigned long long // returns 0 on error, or the session_id +db_init_session_and_get_id(const char * hostname, const char * username); +void db_free_host_info(struct db_host_info * info); + +#endif diff --git a/config.h b/config.h index 2823095..6f773cb 100644 --- a/config.h +++ b/config.h @@ -14,11 +14,8 @@ #define MYSQL_PASS "graou" #define MYSQL_DB "sshportal" -#define SESSION_RECORDING // comment this to deactivate #define LOG_FILENAME_FORMAT "./log/$d/$h/$u/$i.gz" // $d : date in iso format, $h : hostname, $u : username : $i session id #define LOG_FILENAME_MAX_LEN 255 #define LOG_DIRECTORY_MODE S_IRUSR | S_IWUSR | S_IXUSR -#define LIBSSH_VERBOSE_OUTPOUT // comment this to deactivate - #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index f885850..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -include_directories("${bastion_SOURCE_DIR}/termrec/libtty/") - -file(GLOB_RECURSE SOURCES *.c) - -add_executable(bastion ${SOURCES}) -add_library(libtty.a STATIC IMPORTED) -set_property(TARGET libtty.a PROPERTY IMPORTED_LOCATION "${bastion_SOURCE_DIR}/libtty.a") -target_link_libraries(bastion libtty.a) -target_link_libraries(bastion bz2 curl lzma mysqlclient pthread ssh z) diff --git a/src/client.c b/src/client.c deleted file mode 100644 index 11757fc..0000000 --- a/src/client.c +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include - -#include "../config.h" -#include "client.h" -#ifdef SESSION_RECORDING -#include "recording.h" -#endif -#include "mysql.h" -#include "state.h" - -// callback function for channel data and exceptions -static int client_data_function(ssh_session session, ssh_channel channel, void *data, - uint32_t len, int is_stderr, void *userdata) { - struct client_channel_data_struct *cdata = (struct client_channel_data_struct *) userdata; - (void) session; - (void) channel; - (void) is_stderr; - - if (ssh_channel_is_open(cdata->proxy_channel)) { -#ifdef SESSION_RECORDING - record(data, len); -#endif - return ssh_channel_write(cdata->proxy_channel, (char*) data, len); - } else - return SSH_ERROR; -} - -static void client_channel_eof_callback (ssh_session session, ssh_channel channel, void *userdata) -{ - struct client_channel_data_struct *cdata = (struct client_channel_data_struct *) userdata; - (void) session; - (void) channel; - - if (ssh_channel_is_open(cdata->proxy_channel)) - ssh_channel_send_eof(cdata->proxy_channel); -} - -static void client_channel_close_callback (ssh_session session, ssh_channel channel, void *userdata) -{ - struct client_channel_data_struct *cdata = (struct client_channel_data_struct *) userdata; - (void) session; - (void) channel; - - if (ssh_channel_is_open(cdata->proxy_channel)) - ssh_channel_close(cdata->proxy_channel); -} - -static void client_channel_exit_status_callback (ssh_session session, ssh_channel channel, int exit_status, void *userdata) -{ - (void) session; - (void) channel; - (void) userdata; - printf("client exit status callback %d\n", exit_status); -} - -static void client_channel_signal_callback (ssh_session session, ssh_channel channel, - const char *signal, void *userdata) { - (void) session; - (void) channel; - (void) signal; - (void) userdata; - printf("client signal callback\n"); -} - -static void client_channel_exit_signal_callback (ssh_session session, ssh_channel channel, - const char *signal, int core, const char *errmsg, - const char *lang, void *userdata) { - (void) session; - (void) channel; - (void) signal; - (void) core; - (void) errmsg; - (void) lang; - (void) userdata; - printf("client exit signal callback\n"); -} - -struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_channel_data_struct *pdata) -{ - const char * hostname = state_get_ssh_destination(); - struct client_channel_data_struct *cdata = malloc(sizeof(*cdata)); - cdata->event = event; - cdata->my_session = NULL; - cdata->my_channel = NULL; - cdata->proxy_channel = pdata->my_channel; - cdata->client_channel_cb = NULL; - - /* First we try to add the private key that the server will accept */ - struct db_host_info * info = db_get_host_info(hostname); - if (info == NULL) - goto host_info_clean; - - ssh_key privkey = NULL; - if (ssh_pki_import_privkey_base64(info->privkeytxt, NULL, NULL, NULL, &privkey) != SSH_OK) { - printf("Error importing private key"); - goto privkey_clean; - } - - /* We try to connect to the remote server */ - printf("Connecting to %s\n", hostname); - cdata->my_session = ssh_new(); - - ssh_options_set(cdata->my_session, SSH_OPTIONS_HOST, info->address); - ssh_options_set(cdata->my_session, SSH_OPTIONS_USER, info->username); -#ifdef LIBSSH_VERBOSE_OUTPOUT - int verbosity = SSH_LOG_PROTOCOL; - ssh_options_set(cdata->my_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); -#endif - - if (ssh_connect(cdata->my_session) != SSH_OK) { - printf("Error connecting to %s: %s\n", hostname, ssh_get_error(cdata->my_session)); - goto session_clean; - } - - /* We now validate the remote server's public key */ - ssh_key server_pub_key = NULL; - unsigned char * hash = NULL; - size_t hlen; - char * hexa = NULL; - if (ssh_get_server_publickey(cdata->my_session, &server_pub_key) != SSH_OK) { - fprintf(stderr, "Error getting server publickey: %s\n", ssh_get_error(cdata->my_session)); - goto pubkey_clean; - } - if (ssh_get_publickey_hash(server_pub_key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen) != SSH_OK) { - fprintf(stderr, "Error getting publickey hash: %s\n", ssh_get_error(cdata->my_session)); - goto pubkey_hash_clean; - } - hexa = ssh_get_hexa(hash, hlen); - if (strlen(info->hostkeyhash) > 0) { - if (strcmp(hexa, info->hostkeyhash) != 0) { - fprintf(stderr, "Error invalid host key for %s\n", hostname); - goto pubkey_hexa_clean; - } - } else { - // TODO we got a broken sshportal record, we need to fix it but only - // after we completed the migration from sshportal - //db_set_host_publickey_hash(hostname, hexa); - } - ssh_string_free_char(hexa); - ssh_clean_pubkey_hash(&hash); - ssh_key_free(server_pub_key); - - /* With the server checked, we can authenticate */ - if(ssh_userauth_publickey(cdata->my_session, NULL, privkey) == SSH_AUTH_SUCCESS){ - printf("Authentication success\n"); - } else { - printf("Error private key was rejected\n"); - goto session_clean; - } - - /* we open the client channel */ - cdata->my_channel = ssh_channel_new(cdata->my_session); - if (cdata->my_channel == NULL) { - printf("Couldn't open client channel to %s\n", hostname); - goto channel_clean; - } - - /* we open a session channel for the future shell, not suitable for tcp - * forwarding */ - if (ssh_channel_open_session(cdata->my_channel) != SSH_OK) { - printf("Couldn't open the session channel\n"); - goto channel_clean; - } - - cdata->client_channel_cb = malloc(sizeof(*cdata->client_channel_cb)); - memset(cdata->client_channel_cb, 0, sizeof(*cdata->client_channel_cb)); - cdata->client_channel_cb->userdata = cdata; - cdata->client_channel_cb->channel_data_function = client_data_function; - cdata->client_channel_cb->channel_eof_function = client_channel_eof_callback; - cdata->client_channel_cb->channel_close_function = client_channel_close_callback; - cdata->client_channel_cb->channel_exit_status_function = client_channel_exit_status_callback; - cdata->client_channel_cb->channel_signal_function = client_channel_signal_callback; - cdata->client_channel_cb->channel_exit_signal_function = client_channel_exit_signal_callback; - - ssh_callbacks_init(cdata->client_channel_cb); - ssh_set_channel_callbacks(cdata->my_channel, cdata->client_channel_cb); - ssh_event_add_session(event, cdata->my_session); - - // TODO only start recording upong shell_exec or pty_request in proxy.c. - // It will be important when we start supporting scp -#ifdef SESSION_RECORDING - if (init_recorder() != 0) { - goto channel_clean; - } -#endif - - ssh_key_free(privkey); - db_free_host_info(info); - return cdata; - -channel_clean: - ssh_channel_free(cdata->my_channel); - goto session_clean; -pubkey_hexa_clean: - ssh_string_free_char(hexa); -pubkey_hash_clean: - ssh_clean_pubkey_hash(&hash); -pubkey_clean: - ssh_key_free(server_pub_key); -session_clean: - ssh_disconnect(cdata->my_session); - ssh_free(cdata->my_session); - db_free_host_info(info); -privkey_clean: - ssh_key_free(privkey); -host_info_clean: - free(cdata); - return NULL; -} - -void client_cleanup(struct client_channel_data_struct *cdata) -{ -#ifdef SESSION_RECORDING - clean_recorder(); -#endif - ssh_event_remove_session(cdata->event, cdata->my_session); - ssh_channel_free(cdata->my_channel); - ssh_disconnect(cdata->my_session); - ssh_free(cdata->my_session); - free(cdata->client_channel_cb); - free(cdata); -} diff --git a/src/client.h b/src/client.h deleted file mode 100644 index 307b115..0000000 --- a/src/client.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CLIENT_H_ -#define CLIENT_H_ - -#include - -#include "proxy.h" -#include "session.h" - -/* A userdata struct for channel. */ -struct client_channel_data_struct { - /* Event which is used to poll */ - ssh_event event; - ssh_session my_session; - ssh_channel my_channel; - ssh_channel proxy_channel; - struct ssh_channel_callbacks_struct * client_channel_cb; -}; - -struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_channel_data_struct *pdata); -void client_cleanup(struct client_channel_data_struct *cdata); - -#endif diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 6ea6bcb..0000000 --- a/src/main.c +++ /dev/null @@ -1,133 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../config.h" -#include "mysql.h" -#include "session.h" - -/* SIGCHLD handler for cleaning up dead children. */ -static void sigchld_handler(int signo) { - (void) signo; - while (waitpid(-1, NULL, WNOHANG) > 0); -} - -/* SIGINT handler for cleaning up on forced exit. */ -static ssh_bind sshbind = NULL; -static ssh_session session = NULL; - -__attribute__((noreturn)) static void sigint_handler(int signo) -{ - (void) signo; - ssh_disconnect(session); - ssh_free(session); - ssh_bind_free(sshbind); - ssh_finalize(); - db_clean(); - exit(0); -} - -int main() -{ - // Set up SIGCHLD handler - struct sigaction sa; - sa.sa_handler = sigchld_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa, NULL) != 0) { - fprintf(stderr, "Failed to register SIGCHLD handler\n"); - return 1; - } - // Set up SIGINT handler - struct sigaction sa2; - sa2.sa_handler = sigint_handler; - sigemptyset(&sa2.sa_mask); - sa2.sa_flags = 0; - if (sigaction(SIGINT, &sa2, NULL) != 0) { - fprintf(stderr, "Failed to register SIGINT handler\n"); - return 1; - } - - // Initializing ssh context - ssh_init(); - - // Initializing ssh_bind - sshbind = ssh_bind_new(); - if (sshbind == NULL) { - fprintf(stderr, "Error initializing ssh_bind\n"); - exit(-1); - } - int listen_port = LISTEN_PORT; - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &listen_port); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, DSAKEY_PATH); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, RSAKEY_PATH); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, ECDSAKEY_PATH); - - if (ssh_bind_listen(sshbind) < 0) { - printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); - ssh_bind_free(sshbind); - ssh_finalize(); - return 1; - } - - while (1) { - session = ssh_new(); - if (session == NULL) { - fprintf(stderr, "Error initializing ssh_session\n"); - break; - } -#ifdef LIBSSH_VERBOSE_OUTPOUT - int verbosity = SSH_LOG_PROTOCOL; - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); -#endif - - // Blocks until there is a new incoming connection - if (ssh_bind_accept(sshbind,session) == SSH_OK){ - switch(fork()) { - case 0: - /* Remove the SIGCHLD handler inherited from parent. */ - sa.sa_handler = SIG_DFL; - sigaction(SIGCHLD, &sa, NULL); - /* Remove socket binding, which allows us to restart the parent process, without terminating existing sessions. */ - ssh_bind_free(sshbind); - sshbind = NULL; - - if (db_init() !=0) - goto child_cleaning; - - ssh_event event = ssh_event_new(); - if (event != NULL) { - /* Blocks until the SSH session ends */ - handle_session(event, session); - ssh_event_free(event); - } else { - fprintf(stderr, "Could not create polling context\n"); - } -child_cleaning: - ssh_disconnect(session); - ssh_free(session); - ssh_finalize(); - - return 0; - case -1: - fprintf(stderr, "Failed to fork\n"); - } - } else { - fprintf(stderr, "Error accepting a connection : %s\n", ssh_get_error(sshbind)); - ssh_disconnect(session); - ssh_free(session); - ssh_bind_free(sshbind); - ssh_finalize(); - return 1; - } - /* Since the session has been passed to a child fork, do some cleaning up at the parent process. */ - ssh_disconnect(session); - ssh_free(session); - } - ssh_bind_free(sshbind); - ssh_finalize(); - db_clean(); - return 0; -} diff --git a/src/mysql.c b/src/mysql.c deleted file mode 100644 index 0931116..0000000 --- a/src/mysql.c +++ /dev/null @@ -1,188 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../config.h" -#include "mysql.h" - -static MYSQL *db; - -char // returns 0 if ok, greater than 0 otherwise -db_init(void) -{ - printf("MySQL client version: %s\n", mysql_get_client_info()); - db = mysql_init(NULL); - if (db == NULL) { - fprintf(stderr, "%s\n", mysql_error(db)); - return 1; - } - if (mysql_real_connect(db, MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, 0, NULL, 0) == NULL) { - fprintf(stderr, "%s\n", mysql_error(db)); - mysql_close(db); - return 1; - } - return 0; -} - -void db_clean(void) -{ - mysql_close(db); - db = NULL; -} - -char * // returns NULL if no user found, this char * is to be freed from the calling code -db_get_username_from_pubkey(ssh_key pubkey) -{ - int res = mysql_query(db, "SELECT name, authorized_key FROM users, user_keys WHERE users.id = user_keys.user_id"); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't get usernames from database.\n"); - return NULL; - } - MYSQL_RES *result = mysql_store_result(db); - if (result == NULL) { - fprintf(stderr, "FATAL: Couldn't retrieve public keys from database.\n"); - return NULL; - } - - MYSQL_ROW row; - while ((row = mysql_fetch_row(result))) { - char * rsa = "ssh-rsa "; - if (strncmp (row[1], rsa, strlen(rsa)) != 0) { - fprintf(stderr, "Unsupported public key type for user %s : %s\n", row[0], row[1]); - } else { - ssh_key tmp_key; - if (ssh_pki_import_pubkey_base64(row[1] + strlen(rsa), SSH_KEYTYPE_RSA, &tmp_key) != SSH_OK) { - fprintf(stderr, "Error importing public key for user %s : %s\n", row[0], row[1]); - } else if (!ssh_key_cmp(pubkey, tmp_key, SSH_KEY_CMP_PUBLIC)) { - size_t len = strlen(row[0]); - char * username = malloc(len+1); - strcpy(username, row[0]); - ssh_key_free(tmp_key); - mysql_free_result(result); - return username; - } else { - ssh_key_free(tmp_key); - } - } - } - - fprintf(stderr, "ERROR: Didn't find public key in database.\n"); - mysql_free_result(result); - return NULL; -} - -struct db_host_info * // returns NULL if no key found, this char * is to be freed from the calling code -db_get_host_info(const char * hostname) -{ - char buff[255]; - sprintf(buff, "SELECT priv_key, url, host_key FROM ssh_keys, hosts WHERE ssh_keys.id = hosts.ssh_key_id and hosts.name = \"%s\"", hostname); - int res = mysql_query(db, buff); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't query db for server infos for host %s\n", hostname); - return NULL; - } - MYSQL_RES *result = mysql_store_result(db); - if (result == NULL) { - fprintf(stderr, "FATAL: Couldn't retrieve server infos for %s from database.\n", hostname); - return NULL; - } - - MYSQL_ROW row = mysql_fetch_row(result); - if (row == NULL) { - fprintf(stderr, "FATAL: Couldn't retrieve server db results for %s from database.\n", hostname); - mysql_free_result(result); - return NULL; - } - - struct db_host_info * info = malloc(sizeof(struct db_host_info)); - memset(info, 0, sizeof(struct db_host_info)); - - size_t len = strlen(row[0]); - info->privkeytxt = malloc(len+1); - strcpy(info->privkeytxt, row[0]); - - if (strncmp(row[1], "ssh://", 6) != 0) { - fprintf(stderr, "FATAL: invalid host url %s\n", row[1]); - return NULL; - } - size_t at_pos = 0; - char done = 0; - for(size_t i = 6; !done; ++i) { - switch(*(row[1]+i)) { - case '@': - info->username = malloc(i-6+1); - strncpy(info->username, row[1]+6, i-6); - info->username[i-6] = '\0'; - at_pos = i; - break; - case '\0': - info->address = malloc(i-at_pos); - strncpy(info->address, row[1]+at_pos+1, i-at_pos-1); - info->address[i-at_pos-1] = '\0'; - done = 1; - break; - } - if (i > MAX_HOSTNAME_LENGTH + MAX_USERNAME_LENGTH + 6 + 1) { - fprintf(stderr, "FATAL: Couldn't parse host url for host %s, too long.\n", hostname); - if (info->username != NULL) - free(info->username); - return NULL; - } - } - - len = strlen(row[2]); - info->hostkeyhash = malloc(len+1); - strcpy(info->hostkeyhash, row[2]); - - mysql_free_result(result); - return info; -} - -void db_set_host_publickey_hash(const char * hostname, const char * hash) -{ - char buff[255]; - sprintf(buff, "UPDATE ssh_keys, hosts SET host_key = \"%s\" WHERE ssh_keys.id = hosts.ssh_key_id and hosts.name = \"%s\"", hash, hostname); - int res = mysql_query(db, buff); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't set host key for host %s: %s\n", hostname, hash); - return; - } - res = mysql_commit(db); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't commit after setting host key for host %s: %s\n", hostname, hash); - } -} - -unsigned long long // returns 0 on error, or the session_id -db_init_session_and_get_id(const char * hostname, const char * username) -{ - char buff[255]; - sprintf(buff, "INSERT INTO sessions (created_at, status, user_id, host_id) SELECT NOW(), \"opened\", users.id, hosts.id from users, hosts WHERE users.name = \"%s\" and hosts.name = \"%s\"", username, hostname); - int res = mysql_query(db, buff); - if (res != 0) { - fprintf(stderr, "FATAL: Couldn't insert new session in database for %s to %s\n", username, hostname); - return 0; - } - unsigned long long id = mysql_insert_id(db); - if (id == 0) { - fprintf(stderr, "FATAL: Didn't get proper mysql last insert id after inserting new session for %s to %s\n", username, hostname); - return 0; - } - res = mysql_commit(db); - if (res != 0) { - fprintf(stderr, "FATAL: Couldn't commit after inserting session for %s to %s\n", username, hostname); - return 0; - } - return id; -} - -void db_free_host_info(struct db_host_info * info) -{ - free(info->privkeytxt); - free(info->address); - free(info->username); - free(info->hostkeyhash); - free(info); -} diff --git a/src/mysql.h b/src/mysql.h deleted file mode 100644 index 8c6040f..0000000 --- a/src/mysql.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MYSQL_H_ -#define MYSQL_H_ - -struct db_host_info { - char * privkeytxt; - char * address; - char * username; - char * hostkeyhash; -}; - -char db_init(void); -void db_clean(void); -char * // returns NULL if no user found, this char * is to be freed from the calling code -db_get_username_from_pubkey(ssh_key pubkey); -struct db_host_info * // returns NULL if no key found, this char * is to be freed from the calling code -db_get_host_info(const char * hostname); -void db_set_host_publickey_hash(const char * hostname, const char * hash); -unsigned long long // returns 0 on error, or the session_id -db_init_session_and_get_id(const char * hostname, const char * username); -void db_free_host_info(struct db_host_info * info); - -#endif diff --git a/src/proxy.c b/src/proxy.c deleted file mode 100644 index 7d410db..0000000 --- a/src/proxy.c +++ /dev/null @@ -1,219 +0,0 @@ -#include -#include -#include - -#include "client.h" -#include "mysql.h" -#include "proxy.h" -#include "state.h" - -// callback function for channel data and exceptions -static int proxy_data_function(ssh_session session, ssh_channel channel, void *data, - uint32_t len, int is_stderr, void *userdata) { - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; - (void) session; - (void) channel; - (void) is_stderr; - - if (ssh_channel_is_open(pdata->client_channel)) - return ssh_channel_write(pdata->client_channel, (char*) data, len); - else - return SSH_ERROR; -} - -// callback function for SSH channel PTY request from a client -static int proxy_pty_request(ssh_session session, ssh_channel channel, - const char *term, int cols, int rows, int py, int px, - void *userdata) { - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *)userdata; - - (void) session; - (void) channel; - (void) py; - (void) px; - - // TODO record pty size in recorder - if (ssh_channel_is_open(pdata->client_channel)) { - if (ssh_channel_request_pty_size(pdata->client_channel, term, cols, rows) == SSH_OK) - return SSH_OK; - else - fprintf(stderr, "pty request failed\n"); - } else { - fprintf(stderr, "pty request while client_channel not opened\n"); - } - return SSH_ERROR; -} - -// callback function for SSH channel PTY resize from a client -static int proxy_pty_resize(ssh_session session, ssh_channel channel, int cols, - int rows, int py, int px, void *userdata) { - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *)userdata; - - (void) session; - (void) channel; - (void) py; - (void) px; - - // TODO record pty size in recorder - if (ssh_channel_is_open(pdata->client_channel)) { - if (ssh_channel_change_pty_size(pdata->client_channel, cols, rows) == SSH_OK) - return SSH_OK; - else - fprintf(stderr, "pty resize failed\n"); - } else { - fprintf(stderr, "pty resize while client_channel not opened\n"); - } - return SSH_ERROR; -} - -static int proxy_exec_request(ssh_session session, ssh_channel channel, - const char *command, void *userdata) { - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; - - (void) session; - (void) channel; - - if (ssh_channel_is_open(pdata->client_channel)) { - if (ssh_channel_request_exec(pdata->client_channel, command) == SSH_OK) - return SSH_OK; - else - printf("exec request failed\n"); - } else { - fprintf(stderr, "exec request while client_channel not opened\n"); - } - return SSH_ERROR; -} - -static int proxy_shell_request(ssh_session session, ssh_channel channel, - void *userdata) { - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; - - (void) session; - (void) channel; - - if (ssh_channel_is_open(pdata->client_channel)) { - if (ssh_channel_request_shell(pdata->client_channel) == SSH_OK) - return SSH_OK; - else - fprintf(stderr, "shell request failed\n"); - } else { - fprintf(stderr, "shell request while client channel not opened\n"); - } - return SSH_ERROR; -} - -static int proxy_subsystem_request(ssh_session session, ssh_channel channel, - const char *subsystem, void *userdata) { - (void) session; - (void) channel; - (void) subsystem; - (void) userdata; - return SSH_ERROR; // TODO ssh subsystem request - //if (ssh_channel_is_open(pdata->client_channel)) { - //} -} - -static void proxy_channel_eof_callback (ssh_session session, ssh_channel channel, void *userdata) -{ - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; - (void) session; - (void) channel; - if (ssh_channel_is_open(pdata->client_channel)) - ssh_channel_send_eof(pdata->client_channel); -} - -static void proxy_channel_close_callback (ssh_session session, ssh_channel channel, void *userdata) -{ - struct proxy_channel_data_struct *pdata = (struct proxy_channel_data_struct *) userdata; - (void) session; - (void) channel; - if (ssh_channel_is_open(pdata->client_channel)) - ssh_channel_close(pdata->client_channel); -} - -static void proxy_channel_exit_status_callback (ssh_session session, ssh_channel channel, int exit_status, void *userdata) -{ - (void) session; - (void) channel; - (void) exit_status; - (void) userdata; - printf("proxy exit status callback\n"); -} - -static void proxy_channel_signal_callback (ssh_session session, ssh_channel channel, - const char *signal, void *userdata) { - (void) session; - (void) channel; - (void) signal; - (void) userdata; - printf("proxy signal callback\n"); -} - -static void proxy_channel_exit_signal_callback (ssh_session session, ssh_channel channel, - const char *signal, int core, const char *errmsg, - const char *lang, void *userdata) { - (void) session; - (void) channel; - (void) signal; - (void) core; - (void) errmsg; - (void) lang; - (void) userdata; - printf("proxy exit signal callback\n"); -} - -void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel) -{ - struct client_channel_data_struct * cdata; - - struct proxy_channel_data_struct pdata = { - .event = event, - .my_session = session, - .my_channel = my_channel, - .client_channel = NULL, - }; - - cdata = client_dial(event, &pdata); - - if (cdata == NULL) { - return; - } - pdata.client_channel = cdata->my_channel; - - /* We tie everything together */ - struct ssh_channel_callbacks_struct channel_cb = { - .userdata = &pdata, - .channel_data_function = proxy_data_function, - .channel_eof_function = proxy_channel_eof_callback, - .channel_close_function = proxy_channel_close_callback, - .channel_signal_function = proxy_channel_signal_callback, - .channel_exit_status_function = proxy_channel_exit_status_callback, - .channel_exit_signal_function = proxy_channel_exit_signal_callback, - .channel_pty_request_function = proxy_pty_request, - .channel_shell_request_function = proxy_shell_request, - .channel_pty_window_change_function = proxy_pty_resize, - .channel_exec_request_function = proxy_exec_request, - .channel_subsystem_request_function = proxy_subsystem_request, - .channel_auth_agent_req_function = NULL, - .channel_x11_req_function = NULL, - .channel_env_request_function = NULL, - .channel_write_wontblock_function = NULL, - }; - ssh_callbacks_init(&channel_cb); - ssh_set_channel_callbacks(my_channel, &channel_cb); - - db_clean(); // we close the mysql connection before the main loop, as to not waste ressources - - do { - /* Poll the main event which takes care of the sessions and channels */ - if (ssh_event_dopoll(event, -1) == SSH_ERROR) { - break; - } - } while(ssh_channel_is_open(my_channel) && ssh_channel_is_open(pdata.client_channel)); - if (ssh_channel_is_open(my_channel)) - ssh_channel_close(my_channel); - if (ssh_channel_is_open(cdata->my_channel)) - ssh_channel_close(cdata->my_channel); - - client_cleanup(cdata); -} diff --git a/src/proxy.h b/src/proxy.h deleted file mode 100644 index 0e42bdc..0000000 --- a/src/proxy.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PROXY_H_ -#define PROXY_H_ - -#include - -#include "session.h" - -/* A userdata struct for channel. */ -struct proxy_channel_data_struct { - /* Event which is used to poll */ - ssh_event event; - ssh_session my_session; - ssh_channel my_channel; - ssh_channel client_channel; -}; -void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel); - -#endif diff --git a/src/recording.c b/src/recording.c deleted file mode 100644 index 7692ff1..0000000 --- a/src/recording.c +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../config.h" -#include "recording.h" -#include "state.h" - -#ifdef SESSION_RECORDING -static recorder recorder_handle = NULL; - -void clean_recorder(void) -{ - ttyrec_w_close(recorder_handle); - recorder_handle = NULL; -} - -static char * // returns NULL if error, this char * is to be freed from the calling code -make_filename(void) -{ - char * format = LOG_FILENAME_FORMAT; - char * filename = NULL; - unsigned int fname_pos = 0; - unsigned int format_pos = 0; - - filename = malloc(LOG_FILENAME_MAX_LEN+1); - - size_t format_len = strlen(format); - while (format_pos < format_len + 1 && fname_pos < LOG_FILENAME_MAX_LEN +1) { - if (format[format_pos] == '$') { - format_pos++; - if (format[format_pos] == 'd') { - time_t t; - struct tm * tm; - time(&t); - tm = localtime(&t); - fname_pos += strftime(filename + fname_pos, LOG_FILENAME_MAX_LEN - fname_pos, "%F", tm); - } else if (format[format_pos] == 'h') { - const char * hostname = state_get_ssh_destination(); - size_t len = strlen(hostname); - strcpy(filename + fname_pos, hostname); - fname_pos += len; - } else if (format[format_pos] == 'u') { - const char * username = state_get_bastion_username(); - size_t len = strlen(username); - strcpy(filename + fname_pos, username); - fname_pos += len; - } else if (format[format_pos] == 'i') { - sprintf(filename + fname_pos, "%llu", state_get_session_id()); - fname_pos += strlen(filename + fname_pos); - } - format_pos++; - } else { - filename[fname_pos] = format[format_pos]; - if (filename[fname_pos] == '/') { // We create the corresponding directory if it doesn't exist - filename[fname_pos+1] = '\0'; - DIR* dir = opendir(filename); - if (dir) - closedir(dir); - else { - int ret = mkdir(filename, LOG_DIRECTORY_MODE); - if (ret != 0) { - fprintf(stderr, "Couldn't create log directory %s : %s\n", filename, strerror( errno )); - } - } - } - format_pos++; - fname_pos++; - } - } - - if (filename[fname_pos-1] != '\0') { - fprintf(stderr, "Log file name is too long, check LOG_FILENAME_FORMAT and LOG_FILENAME_MAX_LEN\n"); - free(filename); - filename = NULL; - } - return filename; -} - -char // returns 0 if ok, 1 otherwise -init_recorder(void) -{ - char * filename = make_filename(); - if (filename == NULL) - return 1; - struct timeval tm; - if (gettimeofday(&tm, NULL) != 0) { - fprintf(stderr, "OUPS gettimeofday failed!\n"); - return 1; - } - recorder_handle = ttyrec_w_open(-1, "ttyrec", filename, &tm); - free(filename); - if (recorder_handle == NULL) { - fprintf(stderr, "Couldn't open the session termrec log file.\n"); - return 1; - } - - return 0; -} - -char // returns 0 if ok, greater than 0 otherwise -record(void* data, size_t len) -{ - if(recorder_handle == NULL) - return 0; - - struct timeval tm; - if (gettimeofday(&tm, NULL) != 0) { - fprintf(stderr, "OUPS gettimeofday failed!\n"); - return 1; - } - if (ttyrec_w_write(recorder_handle, &tm, data, (int) len) == 0) { - fprintf(stderr, "OUPS ttyrec_w_write failed!\n"); - return 2; - } - return 0; -} -#endif diff --git a/src/recording.h b/src/recording.h deleted file mode 100644 index fec76af..0000000 --- a/src/recording.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "../config.h" - -#ifdef SESSION_RECORDING -#ifndef RECORDING_H_ -#define RECORDING_H_ - -void clean_recorder(void); -char init_recorder(void); -char record(void* data, size_t len); - -#endif -#endif diff --git a/src/session.c b/src/session.c deleted file mode 100644 index 3d3b1b7..0000000 --- a/src/session.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../config.h" -#include "mysql.h" -#include "proxy.h" -#include "session.h" -#include "state.h" - -static int auth_pubkey(ssh_session session, const char *user, ssh_key pubkey, char signature_state, - void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - (void) session; - - // For some reason, libssh can call this twice for the same key - if (sdata->authenticated == 1) - return SSH_ERROR; - - 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; - } - - // TODO check for an invite - - char * bastion_username = db_get_username_from_pubkey(pubkey); - if (bastion_username != NULL) { - sdata->authenticated = 1; - if (state_set_ssh_destination(user) != 0) - return SSH_ERROR; - // TODO check access rights and host configs - state_set_bastion_username(bastion_username); - unsigned long long session_id = db_init_session_and_get_id(user, bastion_username); - state_set_session_id(session_id); - free(bastion_username); - return SSH_AUTH_SUCCESS; - } else { - free(bastion_username); - sdata->auth_attempts++; - return SSH_AUTH_DENIED; - } -} - -static ssh_channel channel_open(ssh_session session, void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - if (sdata->channel == NULL) { - sdata->channel = ssh_channel_new(session); - return sdata->channel; - } else { - // Only one channel allowed - return NULL; - } -} - -void handle_session(ssh_event event, ssh_session session) { - /* Our struct holding information about the session. */ - struct session_data_struct sdata = { - .channel = NULL, - .auth_attempts = 0, - .authenticated = 0, - }; - - struct ssh_server_callbacks_struct server_cb = { - .userdata = &sdata, - .auth_pubkey_function = auth_pubkey, - .channel_open_request_session_function = channel_open, - }; - ssh_callbacks_init(&server_cb); - ssh_set_server_callbacks(session, &server_cb); - - if (ssh_handle_key_exchange(session) != SSH_OK) { - fprintf(stderr, "%s\n", ssh_get_error(session)); - return; - } - - ssh_set_auth_methods(session, SSH_AUTH_METHOD_PUBLICKEY); - ssh_event_add_session(event, session); - - 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) { - 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; - } - - if (ssh_event_dopoll(event, 100) == SSH_ERROR) { - fprintf(stderr, "%s\n", ssh_get_error(session)); - return; - } - } - - handle_proxy_session(event, session, sdata.channel); - - if (ssh_channel_is_open(sdata.channel)) { - ssh_channel_close(sdata.channel); - } - - /* Wait up to 5 seconds for the client to terminate the session. */ - for (int n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { - ssh_event_dopoll(event, 100); - } - state_clean(); - ssh_event_remove_session(event, session); -} diff --git a/src/session.h b/src/session.h deleted file mode 100644 index d0c8eab..0000000 --- a/src/session.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SESSION_H_ -#define SESSION_H_ - -#include - -#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) - -/* A userdata struct for session. */ -struct session_data_struct { - /* Pointer to the channel the session will allocate. */ - ssh_channel channel; - int auth_attempts; - int authenticated; -}; - -void handle_session(ssh_event event, ssh_session session); - -#endif diff --git a/src/state.c b/src/state.c deleted file mode 100644 index dd9cbf3..0000000 --- a/src/state.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -#include "../config.h" -#include "state.h" - -struct state { - unsigned long long session_id; - char * destination; - char * bastion_username; -}; - -static struct state state = {0}; - -char // returns 0 if ok, greater than 0 otherwise -state_set_ssh_destination(const char * name) -{ - if (state.destination != NULL) { - fprintf(stderr, "BUG found, attempting to overwrite state.destination that has already been set\n"); - return 1; - } - size_t len = strnlen(name, MAX_HOSTNAME_LENGTH + 1); - if (len >= MAX_HOSTNAME_LENGTH + 1) { - fprintf(stderr, "Hostname too long, max length is %d.\n", MAX_HOSTNAME_LENGTH); - return 2; - } - state.destination = malloc(len+1); - strncpy(state.destination, name, len+1); - return 0; -} - -const char * state_get_ssh_destination(void) -{ - return state.destination; -} - -char // return 0 if ok, greater than 0 otherwise -state_set_bastion_username(const char * name) -{ - if (state.bastion_username != NULL) { - fprintf(stderr, "BUG found, attempting to overwrite state.bastion_username that has already been set\n"); - return 1; - } - size_t len = strnlen(name, MAX_USERNAME_LENGTH + 1); - if (len >= MAX_USERNAME_LENGTH + 1) { - fprintf(stderr, "Username too long, max length is %d.\n", MAX_USERNAME_LENGTH); - return 1; - } - state.bastion_username = malloc(len+1); - strncpy(state.bastion_username, name, len+1); - return 0; -} - -const char * state_get_bastion_username(void) -{ - return state.bastion_username; -} - -char // return 0 if ok, greater than 0 otherwise -state_set_session_id(const unsigned long long id) -{ - if (state.session_id != 0) { - fprintf(stderr, "BUG found, attempting to set a state.session_id that has already been set\n"); - return 1; - } - state.session_id = id; - return 0; -} - -unsigned long long state_get_session_id(void) -{ - return state.session_id; -} - -void state_clean(void) -{ - free(state.destination); - state.destination = NULL; -} diff --git a/src/state.h b/src/state.h deleted file mode 100644 index 0ab70bc..0000000 --- a/src/state.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef STATE_H_ -#define STATE_H_ - -char state_set_ssh_destination(const char * dest); -const char * state_get_ssh_destination(void); -char state_set_bastion_username(const char * name); -const char * state_get_bastion_username(void); -char state_set_session_id(const unsigned long long id); -unsigned long long state_get_session_id(void); -void state_clean(void); - -#endif -- cgit v1.2.3