GUAC-1171: Initial refactor to require that user credentials be provided prior to connect.
This commit is contained in:
parent
ff287aee52
commit
98503a9fb7
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user