aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Dessaux2018-08-26 15:55:38 +0200
committerJulien Dessaux2018-08-26 16:04:11 +0200
commitbeff818f25ae69cfe8501e18271b2509320de8df (patch)
treed132e9d366aaebac9f7b91b8a63fcafbbe550af8
parentAdded address sanitization and fixed found bugs (diff)
downloadbastion-beff818f25ae69cfe8501e18271b2509320de8df.tar.gz
bastion-beff818f25ae69cfe8501e18271b2509320de8df.tar.bz2
bastion-beff818f25ae69cfe8501e18271b2509320de8df.zip
Added session recording with https://github.com/kilobyte/termrec
Diffstat (limited to '')
-rw-r--r--GNUmakefile3
-rw-r--r--config.h13
-rw-r--r--src/client.c29
-rw-r--r--src/client.h2
-rw-r--r--src/main.c8
-rw-r--r--src/proxy.c7
-rw-r--r--src/proxy.h2
-rw-r--r--src/recording.c126
-rw-r--r--src/recording.h12
-rw-r--r--src/session.c17
-rw-r--r--src/session.h2
-rw-r--r--src/state.c83
-rw-r--r--src/state.h12
13 files changed, 291 insertions, 25 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 00f18ad..52ee070 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -8,8 +8,7 @@ OBJ=$(sources:.c=.o)
all: bastion
bastion: $(OBJ)
- #$(CC) ${DEBUG} -o bastion $(OBJ) -lssh -lutil -lpthread -lssh_threads
- $(CC) ${DEBUG} -o bastion $(OBJ) -lssh -lutil
+ $(CC) ${DEBUG} -o bastion $(OBJ) -lssh -lutil -ltty
clean:
$(RM) bastion *.[do] src/*.[do]
diff --git a/config.h b/config.h
index a1555f7..6e93bf4 100644
--- a/config.h
+++ b/config.h
@@ -1,14 +1,23 @@
#ifndef CONFIG_H_
#define CONFIG_H_
+#define USER_TO_LOGIN_AS "root"
+
#define LISTEN_PORT 2222
-#define MAX_HOSTNAME_LENGTH 48
+#define MAX_HOSTNAME_LENGTH 255
+#define MAX_USERNAME_LENGTH 255
#define USER_RSA_PUBKEY "AAAAB3NzaC1yc2EAAAADAQABAAACAQDMdBAFjENiPMTtq90GT3+NZ68nfGxQiRExaYYnLzm1ecmulCvsuA4AOpeLY6f+FWe+ludiw7nhrXzssDdsKBy0QL+XQyvjjjW4X+k9MYhP1gAWXEOGJnjJ/1ovEsMt++6fLyNKLUTA46kErbEehDs22r+rIiEKatrn0BNrJcRI94H44oEL1/ImzVam0cSBL0tPiaJxe60sBs7M76zfyFtVdMGkeuBpS7ee+FLA58fsS3/sEZmkas8MT0QdvZz1y/66MknXYbIaqDSOUACXGF4yVKpogLRRJ1SgNo1Ujo/U3VOR1O4CiQczsZOcbSdjgl0x3fJb7BaIxrZy9iW2I7G/L/chfTvRws+x1s1y5FNZOOiXMCdZjhgLaRwb6p5gMsMVn9sJbhDjmejcAkBKQDkzbvxxhfVkH225FoVXA9YF0msWLyOEyZQYbA8autLDJsAOT5RDfw/G82DQBufAPEBR/bPby0Hl5kjqW75bpSVxDvzmKwt3EpITg9iuYEhvYZ/Zq5qC1UJ54ZfOvaf0PsTUzFePty6ve/JzfxCV1XgFQ+B8l4NSz11loDfNXSUngf7lL4qu5X4aN6WmLFO1YbyFlfpvt3K1CekJmWVeE5mV9EFTUJ4ParVWRGiA4W+zaCOsHgRkcGkp4eYGyWW8gOR/lVxYU2IFl9mbMrC9bkdRbQ=="
#define PRIVKEY_PATH "./id_rsa"
-#define USER_TO_LOGIN_AS "root"
#define DSAKEY_PATH "./ssh_host_dsa_key"
#define RSAKEY_PATH "./ssh_host_rsa_key"
#define ECDSAKEY_PATH "./ssh_host_ecdsa_key"
+#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/client.c b/src/client.c
index d7ecca6..6c3cbd0 100644
--- a/src/client.c
+++ b/src/client.c
@@ -4,6 +4,10 @@
#include "../config.h"
#include "client.h"
+#ifdef SESSION_RECORDING
+#include "recording.h"
+#endif
+#include "state.h"
// callback function for channel data and exceptions
static int client_data_function(ssh_session session, ssh_channel channel, void *data,
@@ -13,9 +17,12 @@ static int client_data_function(ssh_session session, ssh_channel channel, void *
(void) channel;
(void) is_stderr;
- if (ssh_channel_is_open(cdata->proxy_channel))
+ 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
+ } else
return SSH_ERROR;
}
@@ -69,8 +76,9 @@ static void client_channel_exit_signal_callback (ssh_session session, ssh_channe
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)
+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;
@@ -90,9 +98,11 @@ struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_cha
cdata->my_session = ssh_new();
ssh_options_set(cdata->my_session, SSH_OPTIONS_HOST, hostname);
- ssh_options_set(cdata->my_session, SSH_OPTIONS_USER, USER_TO_LOGIN_AS);
+ ssh_options_set(cdata->my_session, SSH_OPTIONS_USER, state_get_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));
@@ -152,6 +162,14 @@ struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_cha
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_string_free_char(hexa);
ssh_clean_pubkey_hash(&hash);
ssh_key_free(server_pub_key);
@@ -179,6 +197,9 @@ privkey_clean:
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);
diff --git a/src/client.h b/src/client.h
index 3826b5f..307b115 100644
--- a/src/client.h
+++ b/src/client.h
@@ -16,7 +16,7 @@ struct client_channel_data_struct {
struct ssh_channel_callbacks_struct * client_channel_cb;
};
-struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_channel_data_struct *pdata, const char * hostname);
+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
index c3d7108..4f475ee 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,12 +14,13 @@ static void sigchld_handler(int signo) {
}
/* SIGINT handler for cleaning up on forced exit. */
-static ssh_bind sshbind;
-static ssh_session session;
+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();
@@ -75,8 +76,10 @@ int main()
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){
@@ -87,6 +90,7 @@ int main()
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;
ssh_event event = ssh_event_new();
if (event != NULL) {
diff --git a/src/proxy.c b/src/proxy.c
index 3bbecf3..5718a8a 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -4,6 +4,7 @@
#include "client.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,
@@ -30,6 +31,7 @@ static int proxy_pty_request(ssh_session session, ssh_channel 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;
@@ -51,6 +53,7 @@ static int proxy_pty_resize(ssh_session session, ssh_channel channel, int cols,
(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;
@@ -158,7 +161,7 @@ static void proxy_channel_exit_signal_callback (ssh_session session, ssh_channel
printf("proxy exit signal callback\n");
}
-void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel, const char * hostname)
+void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel)
{
struct client_channel_data_struct * cdata;
@@ -169,7 +172,7 @@ void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_c
.client_channel = NULL,
};
- cdata = client_dial(event, &pdata, hostname);
+ cdata = client_dial(event, &pdata);
if (cdata == NULL) {
return;
diff --git a/src/proxy.h b/src/proxy.h
index 3756544..0e42bdc 100644
--- a/src/proxy.h
+++ b/src/proxy.h
@@ -13,6 +13,6 @@ struct proxy_channel_data_struct {
ssh_channel my_channel;
ssh_channel client_channel;
};
-void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_channel, const char * hostname);
+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
new file mode 100644
index 0000000..6c0cdab
--- /dev/null
+++ b/src/recording.c
@@ -0,0 +1,126 @@
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <ttyrec.h>
+
+#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 * // 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_username();
+ size_t len = strlen(username);
+ strcpy(filename + fname_pos, username);
+ fname_pos += len;
+ } else if (format[format_pos] == 'i') {
+ sprintf(filename + fname_pos, "%d", 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
new file mode 100644
index 0000000..fec76af
--- /dev/null
+++ b/src/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/src/session.c b/src/session.c
index 1031f7d..f5827e8 100644
--- a/src/session.c
+++ b/src/session.c
@@ -11,6 +11,7 @@
#include "../config.h"
#include "proxy.h"
#include "session.h"
+#include "state.h"
static int auth_pubkey(ssh_session session, const char *user,
struct ssh_key_struct *pubkey,
@@ -35,13 +36,12 @@ static int auth_pubkey(ssh_session session, const char *user,
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);
+ if (state_set_ssh_destination(user) != 0)
return SSH_ERROR;
- }
- sdata->login_username = malloc(len+1);
- strncpy(sdata->login_username, user, len+1);
+ // TODO check access rights and host configs
+ state_set_username(USER_TO_LOGIN_AS);
+ // TODO log session creation in db
+ state_set_session_id(1337);
return SSH_AUTH_SUCCESS;
} else {
ssh_key_free(reference_key);
@@ -68,7 +68,6 @@ void handle_session(ssh_event event, ssh_session session) {
.channel = NULL,
.auth_attempts = 0,
.authenticated = 0,
- .login_username = NULL
};
struct ssh_server_callbacks_struct server_cb = {
@@ -105,7 +104,7 @@ void handle_session(ssh_event event, ssh_session session) {
}
}
- handle_proxy_session(event, session, sdata.channel, sdata.login_username);
+ handle_proxy_session(event, session, sdata.channel);
if (ssh_channel_is_open(sdata.channel)) {
ssh_channel_close(sdata.channel);
@@ -115,6 +114,6 @@ void handle_session(ssh_event event, ssh_session session) {
for (int n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
ssh_event_dopoll(event, 100);
}
- free(sdata.login_username);
+ state_clean();
ssh_event_remove_session(event, session);
}
diff --git a/src/session.h b/src/session.h
index 50b5fe8..d0c8eab 100644
--- a/src/session.h
+++ b/src/session.h
@@ -11,8 +11,6 @@ struct session_data_struct {
ssh_channel channel;
int auth_attempts;
int authenticated;
- // ssh user name when login
- char * login_username;
};
void handle_session(ssh_event event, ssh_session session);
diff --git a/src/state.c b/src/state.c
new file mode 100644
index 0000000..646fabe
--- /dev/null
+++ b/src/state.c
@@ -0,0 +1,83 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../config.h"
+#include "state.h"
+
+struct state {
+ char * destination;
+ char * username;
+ int session_id;
+ int padding;
+};
+
+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_username(const char * name)
+{
+ if (state.username != NULL) {
+ fprintf(stderr, "BUG found, attempting to overwrite state.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.username = malloc(len+1);
+ strncpy(state.username, name, len+1);
+ return 0;
+}
+
+const char * state_get_username(void)
+{
+ return state.username;
+}
+
+char // return 0 if ok, greater than 0 otherwise
+state_set_session_id(const int id)
+{
+ if (state.session_id != 0) {
+ fprintf(stderr, "BUG found, attempting to overwrite state.username that has already been set\n");
+ return 1;
+ }
+ state.session_id = id;
+ return 0;
+}
+
+int state_get_session_id(void)
+{
+ return state.session_id;
+}
+
+void state_clean(void)
+{
+ free(state.destination);
+ state.destination = NULL;
+ free(state.username);
+ state.username = NULL;
+}
diff --git a/src/state.h b/src/state.h
new file mode 100644
index 0000000..79c5861
--- /dev/null
+++ b/src/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_username(const char * name);
+const char * state_get_username(void);
+char state_set_session_id(const int id);
+int state_get_session_id(void);
+void state_clean(void);
+
+#endif