Archived
1
0
Fork 0
This commit is contained in:
Julien Dessaux 2018-08-26 15:55:38 +02:00
parent 4da77dcb10
commit beff818f25
13 changed files with 291 additions and 25 deletions

View file

@ -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]

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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

126
src/recording.c Normal file
View file

@ -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

12
src/recording.h Normal file
View file

@ -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

View file

@ -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);
}

View file

@ -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);

83
src/state.c Normal file
View file

@ -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;
}

12
src/state.h Normal file
View file

@ -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