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());
|
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,
|
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;
|
int retval;
|
||||||
|
|
||||||
@ -212,12 +330,17 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
|||||||
/* Free addrinfo */
|
/* Free addrinfo */
|
||||||
freeaddrinfo(addresses);
|
freeaddrinfo(addresses);
|
||||||
|
|
||||||
|
/* Allocate new session */
|
||||||
|
guac_common_ssh_session* common_session =
|
||||||
|
malloc(sizeof(guac_common_ssh_session));
|
||||||
|
|
||||||
/* Open SSH session */
|
/* Open SSH session */
|
||||||
LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
|
LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
|
||||||
NULL, client);
|
NULL, common_session);
|
||||||
if (session == NULL) {
|
if (session == NULL) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||||
"Session allocation failed.");
|
"Session allocation failed.");
|
||||||
|
free(common_session);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,18 +348,22 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
|||||||
if (libssh2_session_handshake(session, fd)) {
|
if (libssh2_session_handshake(session, fd)) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
"SSH handshake failed.");
|
"SSH handshake failed.");
|
||||||
|
free(common_session);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate new session */
|
|
||||||
guac_common_ssh_session* common_session =
|
|
||||||
malloc(sizeof(guac_common_ssh_session));
|
|
||||||
|
|
||||||
/* Store basic session data */
|
/* Store basic session data */
|
||||||
common_session->client = client;
|
common_session->client = client;
|
||||||
|
common_session->user = user;
|
||||||
common_session->session = session;
|
common_session->session = session;
|
||||||
common_session->fd = fd;
|
common_session->fd = fd;
|
||||||
|
|
||||||
|
/* Attempt authentication */
|
||||||
|
if (guac_common_ssh_authenticate(common_session)) {
|
||||||
|
free(common_session);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return created session */
|
/* Return created session */
|
||||||
return common_session;
|
return common_session;
|
||||||
|
|
||||||
@ -248,65 +375,3 @@ void guac_common_ssh_destroy_session(guac_common_ssh_session* session) {
|
|||||||
free(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;
|
guac_client* client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user that will be authenticating via SSH.
|
||||||
|
*/
|
||||||
|
guac_common_ssh_user* user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The underlying SSH session from libssh2.
|
* The underlying SSH session from libssh2.
|
||||||
*/
|
*/
|
||||||
@ -72,9 +77,9 @@ int guac_common_ssh_init(guac_client* client);
|
|||||||
void guac_common_ssh_uninit();
|
void guac_common_ssh_uninit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to the SSH server running at the given hostname and port but does
|
* Connects to the SSH server running at the given hostname and port, and
|
||||||
* not perform any authentication. If an error occurs while connecting, the
|
* authenticates as the given user. If an error occurs while connecting or
|
||||||
* Guacamole client will automatically and fatally abort.
|
* authenticating, the Guacamole client will automatically and fatally abort.
|
||||||
*
|
*
|
||||||
* @param client
|
* @param client
|
||||||
* The Guacamole client that will be using SSH.
|
* The Guacamole client that will be using SSH.
|
||||||
@ -85,12 +90,15 @@ void guac_common_ssh_uninit();
|
|||||||
* @param port
|
* @param port
|
||||||
* The port to connect to on the given hostname.
|
* The port to connect to on the given hostname.
|
||||||
*
|
*
|
||||||
|
* @param user
|
||||||
|
* The user to authenticate as, once connected.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* A new SSH session if the connection succeeds, or NULL if the connection
|
* A new SSH session if the connection and authentication succeed, or NULL
|
||||||
* was not successful.
|
* if the connection or authentication were not successful.
|
||||||
*/
|
*/
|
||||||
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
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
|
* 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);
|
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
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user