GUAC-1171: Initial refactor to require that user credentials be provided prior to connect.

This commit is contained in:
Michael Jumper 2015-07-10 02:00:23 -07:00
parent ff287aee52
commit 98503a9fb7
2 changed files with 147 additions and 92 deletions

View File

@ -137,8 +137,126 @@ void guac_common_ssh_uninit() {
guac_common_ssh_openssl_free_locks(CRYPTO_num_locks());
}
static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session,
unsigned char** sig, size_t* sig_len,
const unsigned char* data, size_t data_len, void **abstract) {
guac_common_ssh_key* key = (guac_common_ssh_key*) abstract;
int length;
/* Allocate space for signature */
*sig = malloc(4096);
/* Sign with key */
length = guac_common_ssh_key_sign(key, (const char*) data, data_len, *sig);
if (length < 0)
return 1;
*sig_len = length;
return 0;
}
/**
* Callback for the keyboard-interactive authentication method. Currently
* suports just one prompt for the password.
*/
static void guac_common_ssh_kbd_callback(const char *name, int name_len,
const char *instruction, int instruction_len, int num_prompts,
const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
void **abstract) {
guac_common_ssh_session* common_session =
(guac_common_ssh_session*) *abstract;
guac_client* client = common_session->client;
/* Send password if only one prompt */
if (num_prompts == 1) {
char* password = common_session->user->password;
responses[0].text = strdup(password);
responses[0].length = strlen(password);
}
/* If more than one prompt, a single password is not enough */
else
guac_client_log(client, GUAC_LOG_WARNING,
"Unsupported number of keyboard-interactive prompts: %i",
num_prompts);
}
static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) {
guac_client* client = common_session->client;
guac_common_ssh_user* user = common_session->user;
LIBSSH2_SESSION* session = common_session->session;
/* Get user credentials */
char* username = user->username;
char* password = user->password;
guac_common_ssh_key* key = user->private_key;
/* Get list of supported authentication methods */
char* user_authlist = libssh2_userauth_list(session, username,
strlen(username));
guac_client_log(client, GUAC_LOG_DEBUG,
"Supported authentication methods: %s", user_authlist);
/* Authenticate with private key, if provided */
if (key != NULL) {
/* Check if public key auth is supported on the server */
if (strstr(user_authlist, "publickey") == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
"Public key authentication not supported");
return 1;
}
/* Attempt public key auth */
if (libssh2_userauth_publickey(session, username,
(unsigned char*) key->public_key, key->public_key_length,
guac_common_ssh_sign_callback, (void**) key)) {
/* Abort on failure */
char* error_message;
libssh2_session_last_error(session, &error_message, NULL, 0);
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
"Public key authentication failed: %s", error_message);
return 1;
}
/* Private key authentication succeeded */
return 0;
}
/* Authenticate with password */
if (strstr(user_authlist, "password") != NULL) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Using password authentication method");
return libssh2_userauth_password(session, username, password);
}
/* Authenticate with password via keyboard-interactive auth */
if (strstr(user_authlist, "keyboard-interactive") != NULL) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Using keyboard-interactive authentication method");
return libssh2_userauth_keyboard_interactive(session, username,
&guac_common_ssh_kbd_callback);
}
/* No known authentication types available */
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE,
"No known authentication methods");
return 1;
}
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
const char* hostname, const char* port) {
const char* hostname, const char* port, guac_common_ssh_user* user) {
int retval;
@ -212,12 +330,17 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
/* Free addrinfo */
freeaddrinfo(addresses);
/* Allocate new session */
guac_common_ssh_session* common_session =
malloc(sizeof(guac_common_ssh_session));
/* Open SSH session */
LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
NULL, client);
NULL, common_session);
if (session == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Session allocation failed.");
free(common_session);
return NULL;
}
@ -225,18 +348,22 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
if (libssh2_session_handshake(session, fd)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
"SSH handshake failed.");
free(common_session);
return NULL;
}
/* Allocate new session */
guac_common_ssh_session* common_session =
malloc(sizeof(guac_common_ssh_session));
/* Store basic session data */
common_session->client = client;
common_session->user = user;
common_session->session = session;
common_session->fd = fd;
/* Attempt authentication */
if (guac_common_ssh_authenticate(common_session)) {
free(common_session);
return NULL;
}
/* Return created session */
return common_session;
@ -248,65 +375,3 @@ void guac_common_ssh_destroy_session(guac_common_ssh_session* session) {
free(session);
}
static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session,
unsigned char** sig, size_t* sig_len,
const unsigned char* data, size_t data_len, void **abstract) {
guac_common_ssh_key* key = (guac_common_ssh_key*) abstract;
int length;
/* Allocate space for signature */
*sig = malloc(4096);
/* Sign with key */
length = guac_common_ssh_key_sign(key, (const char*) data, data_len, *sig);
if (length < 0)
return 1;
*sig_len = length;
return 0;
}
int guac_common_ssh_authenticate(guac_common_ssh_session* common_session,
guac_common_ssh_user* user) {
guac_client* client = common_session->client;
LIBSSH2_SESSION* session = common_session->session;
/* Get user credentials */
char* username = user->username;
guac_common_ssh_key* key = user->private_key;
/* Get list of supported authentication methods */
char* user_authlist = libssh2_userauth_list(session, username,
strlen(username));
guac_client_log(client, GUAC_LOG_DEBUG,
"Supported authentication methods: %s", user_authlist);
/* Check if public key auth is supported on the server */
if (strstr(user_authlist, "publickey") == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
"Public key authentication not supported");
return 1;
}
/* Attempt public key auth */
if (libssh2_userauth_publickey(session, username,
(unsigned char*) key->public_key, key->public_key_length,
guac_common_ssh_sign_callback, (void**) key)) {
/* Abort on failure */
char* error_message;
libssh2_session_last_error(session, &error_message, NULL, 0);
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
"Public key authentication failed: %s", error_message);
return 1;
}
/* Authentication succeeded */
return 0;
}

View File

@ -39,6 +39,11 @@ typedef struct guac_common_ssh_session {
*/
guac_client* client;
/**
* The user that will be authenticating via SSH.
*/
guac_common_ssh_user* user;
/**
* The underlying SSH session from libssh2.
*/
@ -72,9 +77,9 @@ int guac_common_ssh_init(guac_client* client);
void guac_common_ssh_uninit();
/**
* Connects to the SSH server running at the given hostname and port but does
* not perform any authentication. If an error occurs while connecting, the
* Guacamole client will automatically and fatally abort.
* Connects to the SSH server running at the given hostname and port, and
* authenticates as the given user. If an error occurs while connecting or
* authenticating, the Guacamole client will automatically and fatally abort.
*
* @param client
* The Guacamole client that will be using SSH.
@ -85,12 +90,15 @@ void guac_common_ssh_uninit();
* @param port
* The port to connect to on the given hostname.
*
* @param user
* The user to authenticate as, once connected.
*
* @return
* A new SSH session if the connection succeeds, or NULL if the connection
* was not successful.
* A new SSH session if the connection and authentication succeed, or NULL
* if the connection or authentication were not successful.
*/
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
const char* hostname, const char* port);
const char* hostname, const char* port, guac_common_ssh_user* user);
/**
* Disconnects and destroys the given SSH session, freeing all associated
@ -101,23 +109,5 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
*/
void guac_common_ssh_destroy_session(guac_common_ssh_session* session);
/**
* Authenticates the given user with the given, existing SSH session. If
* authentication fails, the Guacamole client will automatically and fatally
* abort.
*
* @param session
* The SSH session to authenticate with.
*
* @param user
* The user object describing the current user and their associated
* credentials.
*
* @return
* Zero if authentication succeeds, non-zero if authentication fails.
*/
int guac_common_ssh_authenticate(guac_common_ssh_session* session,
guac_common_ssh_user* user);
#endif