GUACAMOLE-547: Merge support for SSH NONE authentication method.
This commit is contained in:
commit
7e9b97007e
@ -25,6 +25,23 @@
|
|||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <libssh2.h>
|
#include <libssh2.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for retrieving additional credentials.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The Guacamole Client associated with this need for additional
|
||||||
|
* credentials.
|
||||||
|
*
|
||||||
|
* @param cred_name
|
||||||
|
* The name of the credential being requested, which will be shared
|
||||||
|
* with the client in order to generate a meaningful prompt.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A newly-allocated string containing the credentials provided by
|
||||||
|
* the user, which must be freed by a call to free().
|
||||||
|
*/
|
||||||
|
typedef char* guac_ssh_credential_handler(guac_client* client, char* cred_name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An SSH session, backed by libssh2 and associated with a particular
|
* An SSH session, backed by libssh2 and associated with a particular
|
||||||
* Guacamole client.
|
* Guacamole client.
|
||||||
@ -50,6 +67,11 @@ typedef struct guac_common_ssh_session {
|
|||||||
* The file descriptor of the socket being used for the SSH connection.
|
* The file descriptor of the socket being used for the SSH connection.
|
||||||
*/
|
*/
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to retrieve credentials.
|
||||||
|
*/
|
||||||
|
guac_ssh_credential_handler* credential_handler;
|
||||||
|
|
||||||
} guac_common_ssh_session;
|
} guac_common_ssh_session;
|
||||||
|
|
||||||
@ -92,14 +114,32 @@ void guac_common_ssh_uninit();
|
|||||||
*
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* The user to authenticate as, once connected.
|
* The user to authenticate as, once connected.
|
||||||
|
*
|
||||||
|
* @param keepalive
|
||||||
|
* How frequently the connection should send keepalive packets, in
|
||||||
|
* seconds. Zero disables keepalive packets, and 2 is the minimum
|
||||||
|
* configurable value.
|
||||||
|
*
|
||||||
|
* @param host_key
|
||||||
|
* The known public host key of the server, as provided by the client. If
|
||||||
|
* provided the identity of the server will be checked against this key,
|
||||||
|
* and a mis-match between this and the server identity will cause the
|
||||||
|
* connection to fail. If not provided, no checks will be done and the
|
||||||
|
* connection will proceed.
|
||||||
|
*
|
||||||
|
* @param credential_handler
|
||||||
|
* The handler function for retrieving additional credentials from the user
|
||||||
|
* as required by the SSH server, or NULL if the user will not be asked
|
||||||
|
* for additional credentials.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* A new SSH session if the connection and authentication succeed, or NULL
|
* A new SSH session if the connection and authentication succeed, or NULL
|
||||||
* if the connection or authentication were 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, guac_common_ssh_user* user, int keepalive,
|
const char* hostname, const char* port, guac_common_ssh_user* user,
|
||||||
const char* host_key);
|
int keepalive, const char* host_key,
|
||||||
|
guac_ssh_credential_handler* credential_handler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects and destroys the given SSH session, freeing all associated
|
* Disconnects and destroys the given SSH session, freeing all associated
|
||||||
|
@ -304,20 +304,26 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
|||||||
LIBSSH2_SESSION* session = common_session->session;
|
LIBSSH2_SESSION* session = common_session->session;
|
||||||
|
|
||||||
/* Get user credentials */
|
/* Get user credentials */
|
||||||
char* username = user->username;
|
|
||||||
char* password = user->password;
|
|
||||||
guac_common_ssh_key* key = user->private_key;
|
guac_common_ssh_key* key = user->private_key;
|
||||||
|
|
||||||
/* Validate username provided */
|
/* Validate username provided */
|
||||||
if (username == NULL) {
|
if (user->username == NULL) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
||||||
"SSH authentication requires a username.");
|
"SSH authentication requires a username.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get list of supported authentication methods */
|
/* Get list of supported authentication methods */
|
||||||
char* user_authlist = libssh2_userauth_list(session, username,
|
char* user_authlist = libssh2_userauth_list(session, user->username,
|
||||||
strlen(username));
|
strlen(user->username));
|
||||||
|
|
||||||
|
/* If auth list is NULL, then authentication has succeeded with NONE */
|
||||||
|
if (user_authlist == NULL) {
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"SSH NONE authentication succeeded.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
"Supported authentication methods: %s", user_authlist);
|
"Supported authentication methods: %s", user_authlist);
|
||||||
|
|
||||||
@ -333,7 +339,7 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt public key auth */
|
/* Attempt public key auth */
|
||||||
if (libssh2_userauth_publickey(session, username,
|
if (libssh2_userauth_publickey(session, user->username,
|
||||||
(unsigned char*) key->public_key, key->public_key_length,
|
(unsigned char*) key->public_key, key->public_key_length,
|
||||||
guac_common_ssh_sign_callback, (void**) key)) {
|
guac_common_ssh_sign_callback, (void**) key)) {
|
||||||
|
|
||||||
@ -352,14 +358,18 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Attempt authentication with username + password. */
|
||||||
|
if (user->password == NULL && common_session->credential_handler)
|
||||||
|
user->password = common_session->credential_handler(client, "Password: ");
|
||||||
|
|
||||||
/* Authenticate with password, if provided */
|
/* Authenticate with password, if provided */
|
||||||
else if (password != NULL) {
|
if (user->password != NULL) {
|
||||||
|
|
||||||
/* Check if password auth is supported on the server */
|
/* Check if password auth is supported on the server */
|
||||||
if (strstr(user_authlist, "password") != NULL) {
|
if (strstr(user_authlist, "password") != NULL) {
|
||||||
|
|
||||||
/* Attempt password authentication */
|
/* Attempt password authentication */
|
||||||
if (libssh2_userauth_password(session, username, password)) {
|
if (libssh2_userauth_password(session, user->username, user->password)) {
|
||||||
|
|
||||||
/* Abort on failure */
|
/* Abort on failure */
|
||||||
char* error_message;
|
char* error_message;
|
||||||
@ -380,7 +390,7 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
|||||||
if (strstr(user_authlist, "keyboard-interactive") != NULL) {
|
if (strstr(user_authlist, "keyboard-interactive") != NULL) {
|
||||||
|
|
||||||
/* Attempt keyboard-interactive auth using provided password */
|
/* Attempt keyboard-interactive auth using provided password */
|
||||||
if (libssh2_userauth_keyboard_interactive(session, username,
|
if (libssh2_userauth_keyboard_interactive(session, user->username,
|
||||||
&guac_common_ssh_kbd_callback)) {
|
&guac_common_ssh_kbd_callback)) {
|
||||||
|
|
||||||
/* Abort on failure */
|
/* Abort on failure */
|
||||||
@ -415,8 +425,9 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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, guac_common_ssh_user* user, int keepalive,
|
const char* hostname, const char* port, guac_common_ssh_user* user,
|
||||||
const char* host_key) {
|
int keepalive, const char* host_key,
|
||||||
|
guac_ssh_credential_handler* credential_handler) {
|
||||||
|
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
@ -561,6 +572,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
|||||||
common_session->user = user;
|
common_session->user = user;
|
||||||
common_session->session = session;
|
common_session->session = session;
|
||||||
common_session->fd = fd;
|
common_session->fd = fd;
|
||||||
|
common_session->credential_handler = credential_handler;
|
||||||
|
|
||||||
/* Attempt authentication */
|
/* Attempt authentication */
|
||||||
if (guac_common_ssh_authenticate(common_session)) {
|
if (guac_common_ssh_authenticate(common_session)) {
|
||||||
|
@ -975,7 +975,7 @@ void* guac_rdp_client_thread(void* data) {
|
|||||||
rdp_client->sftp_session =
|
rdp_client->sftp_session =
|
||||||
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
||||||
settings->sftp_port, rdp_client->sftp_user, settings->sftp_server_alive_interval,
|
settings->sftp_port, rdp_client->sftp_user, settings->sftp_server_alive_interval,
|
||||||
settings->sftp_host_key);
|
settings->sftp_host_key, NULL);
|
||||||
|
|
||||||
/* Fail if SSH connection does not succeed */
|
/* Fail if SSH connection does not succeed */
|
||||||
if (rdp_client->sftp_session == NULL) {
|
if (rdp_client->sftp_session == NULL) {
|
||||||
|
@ -130,19 +130,6 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
|||||||
|
|
||||||
} /* end if key given */
|
} /* end if key given */
|
||||||
|
|
||||||
/* Otherwise, use password */
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* Get password if not provided */
|
|
||||||
if (settings->password == NULL)
|
|
||||||
settings->password = guac_terminal_prompt(ssh_client->term,
|
|
||||||
"Password: ", false);
|
|
||||||
|
|
||||||
/* Set provided password */
|
|
||||||
guac_common_ssh_user_set_password(user, settings->password);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear screen of any prompts */
|
/* Clear screen of any prompts */
|
||||||
guac_terminal_printf(ssh_client->term, "\x1B[H\x1B[J");
|
guac_terminal_printf(ssh_client->term, "\x1B[H\x1B[J");
|
||||||
|
|
||||||
@ -150,6 +137,29 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function used to generate a terminal prompt to gather additional
|
||||||
|
* credentials from the guac_client during a connection, and using
|
||||||
|
* the specified string to generate the prompt for the user.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The guac_client object associated with the current connection
|
||||||
|
* where additional credentials are required.
|
||||||
|
*
|
||||||
|
* @param cred_name
|
||||||
|
* The prompt text to display to the screen when prompting for the
|
||||||
|
* additional credentials.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The string of credentials gathered from the user.
|
||||||
|
*/
|
||||||
|
static char* guac_ssh_get_credential(guac_client *client, char* cred_name) {
|
||||||
|
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
return guac_terminal_prompt(ssh_client->term, cred_name, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void* ssh_input_thread(void* data) {
|
void* ssh_input_thread(void* data) {
|
||||||
|
|
||||||
guac_client* client = (guac_client*) data;
|
guac_client* client = (guac_client*) data;
|
||||||
@ -239,7 +249,7 @@ void* ssh_client_thread(void* data) {
|
|||||||
/* Open SSH session */
|
/* Open SSH session */
|
||||||
ssh_client->session = guac_common_ssh_create_session(client,
|
ssh_client->session = guac_common_ssh_create_session(client,
|
||||||
settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval,
|
settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval,
|
||||||
settings->host_key);
|
settings->host_key, guac_ssh_get_credential);
|
||||||
if (ssh_client->session == NULL) {
|
if (ssh_client->session == NULL) {
|
||||||
/* Already aborted within guac_common_ssh_create_session() */
|
/* Already aborted within guac_common_ssh_create_session() */
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -291,7 +301,7 @@ void* ssh_client_thread(void* data) {
|
|||||||
ssh_client->sftp_session =
|
ssh_client->sftp_session =
|
||||||
guac_common_ssh_create_session(client, settings->hostname,
|
guac_common_ssh_create_session(client, settings->hostname,
|
||||||
settings->port, ssh_client->user, settings->server_alive_interval,
|
settings->port, ssh_client->user, settings->server_alive_interval,
|
||||||
settings->host_key);
|
settings->host_key, NULL);
|
||||||
if (ssh_client->sftp_session == NULL) {
|
if (ssh_client->sftp_session == NULL) {
|
||||||
/* Already aborted within guac_common_ssh_create_session() */
|
/* Already aborted within guac_common_ssh_create_session() */
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -262,7 +262,7 @@ void* guac_vnc_client_thread(void* data) {
|
|||||||
vnc_client->sftp_session =
|
vnc_client->sftp_session =
|
||||||
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
||||||
settings->sftp_port, vnc_client->sftp_user, settings->sftp_server_alive_interval,
|
settings->sftp_port, vnc_client->sftp_user, settings->sftp_server_alive_interval,
|
||||||
settings->sftp_host_key);
|
settings->sftp_host_key, NULL);
|
||||||
|
|
||||||
/* Fail if SSH connection does not succeed */
|
/* Fail if SSH connection does not succeed */
|
||||||
if (vnc_client->sftp_session == NULL) {
|
if (vnc_client->sftp_session == NULL) {
|
||||||
|
Loading…
Reference in New Issue
Block a user