2018-05-22 17:31:56 +02:00
|
|
|
#include <libssh/callbacks.h>
|
|
|
|
#include <libssh/server.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <pty.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
#include "../config.h"
|
2018-08-26 18:54:19 +02:00
|
|
|
#include "mysql.h"
|
2018-06-20 13:23:48 +02:00
|
|
|
#include "proxy.h"
|
2018-05-22 17:31:56 +02:00
|
|
|
#include "session.h"
|
2018-08-26 15:55:38 +02:00
|
|
|
#include "state.h"
|
2018-05-22 17:31:56 +02:00
|
|
|
|
2018-08-26 18:54:19 +02:00
|
|
|
static int auth_pubkey(ssh_session session, const char *user, ssh_key pubkey, char signature_state,
|
|
|
|
void *userdata) {
|
2018-06-20 13:23:48 +02:00
|
|
|
struct session_data_struct *sdata = (struct session_data_struct *) userdata;
|
2018-05-22 17:31:56 +02:00
|
|
|
(void) session;
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
// For some reason, libssh can call this twice for the same key
|
|
|
|
if (sdata->authenticated == 1)
|
2018-05-22 17:31:56 +02:00
|
|
|
return SSH_ERROR;
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
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;
|
2018-05-22 17:31:56 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
// TODO check for an invite
|
2018-05-22 17:31:56 +02:00
|
|
|
|
2018-08-26 18:54:19 +02:00
|
|
|
char * bastion_username = db_get_username_from_pubkey(pubkey);
|
|
|
|
if (bastion_username != NULL) {
|
2018-05-22 17:31:56 +02:00
|
|
|
sdata->authenticated = 1;
|
2018-08-26 15:55:38 +02:00
|
|
|
if (state_set_ssh_destination(user) != 0)
|
2018-06-20 13:23:48 +02:00
|
|
|
return SSH_ERROR;
|
2018-08-26 15:55:38 +02:00
|
|
|
// TODO check access rights and host configs
|
2018-08-26 18:54:19 +02:00
|
|
|
state_set_bastion_username(bastion_username);
|
|
|
|
free(bastion_username);
|
2018-08-26 15:55:38 +02:00
|
|
|
// TODO log session creation in db
|
|
|
|
state_set_session_id(1337);
|
2018-05-22 17:31:56 +02:00
|
|
|
return SSH_AUTH_SUCCESS;
|
2018-06-20 13:23:48 +02:00
|
|
|
} else {
|
2018-08-26 18:54:19 +02:00
|
|
|
free(bastion_username);
|
2018-06-20 13:23:48 +02:00
|
|
|
sdata->auth_attempts++;
|
|
|
|
return SSH_AUTH_DENIED;
|
2018-05-22 17:31:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-31 08:52:51 +02:00
|
|
|
static ssh_channel channel_open(ssh_session session, void *userdata) {
|
2018-05-22 17:31:56 +02:00
|
|
|
struct session_data_struct *sdata = (struct session_data_struct *) userdata;
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
if (sdata->channel == NULL) {
|
|
|
|
sdata->channel = ssh_channel_new(session);
|
|
|
|
return sdata->channel;
|
|
|
|
} else {
|
|
|
|
// Only one channel allowed
|
|
|
|
return NULL;
|
2018-05-22 17:31:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2018-06-20 13:23:48 +02:00
|
|
|
.authenticated = 0,
|
2018-05-22 17:31:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ssh_server_callbacks_struct server_cb = {
|
|
|
|
.userdata = &sdata,
|
2018-06-20 13:23:48 +02:00
|
|
|
.auth_pubkey_function = auth_pubkey,
|
2018-05-22 17:31:56 +02:00
|
|
|
.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;
|
|
|
|
}
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PUBLICKEY);
|
2018-05-22 17:31:56 +02:00
|
|
|
ssh_event_add_session(event, session);
|
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
for (int n=0; sdata.authenticated == 0 || sdata.channel == NULL; n++) {
|
2018-05-22 17:31:56 +02:00
|
|
|
/* If the user has used up all attempts, or if he hasn't been able to
|
|
|
|
* authenticate in 10 seconds (n * 100ms), disconnect. */
|
2018-06-20 13:23:48 +02:00
|
|
|
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");
|
2018-05-22 17:31:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
|
|
|
|
fprintf(stderr, "%s\n", ssh_get_error(session));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-26 15:55:38 +02:00
|
|
|
handle_proxy_session(event, session, sdata.channel);
|
2018-05-22 17:31:56 +02:00
|
|
|
|
2018-06-20 13:23:48 +02:00
|
|
|
if (ssh_channel_is_open(sdata.channel)) {
|
|
|
|
ssh_channel_close(sdata.channel);
|
2018-05-22 17:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait up to 5 seconds for the client to terminate the session. */
|
2018-06-20 13:23:48 +02:00
|
|
|
for (int n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
|
2018-05-22 17:31:56 +02:00
|
|
|
ssh_event_dopoll(event, 100);
|
|
|
|
}
|
2018-08-26 15:55:38 +02:00
|
|
|
state_clean();
|
2018-06-20 13:23:48 +02:00
|
|
|
ssh_event_remove_session(event, session);
|
2018-05-22 17:31:56 +02:00
|
|
|
}
|