diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 672e7767..346d8abc 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -25,6 +25,23 @@ #include #include +/** + * 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 * Guacamole client. @@ -50,6 +67,11 @@ typedef struct guac_common_ssh_session { * The file descriptor of the socket being used for the SSH connection. */ int fd; + + /** + * Callback function to retrieve credentials. + */ + guac_ssh_credential_handler* credential_handler; } guac_common_ssh_session; @@ -92,14 +114,32 @@ void guac_common_ssh_uninit(); * * @param user * 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 * 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, guac_common_ssh_user* user, int keepalive, - const char* host_key); + const char* hostname, const char* port, guac_common_ssh_user* user, + int keepalive, const char* host_key, + guac_ssh_credential_handler* credential_handler); /** * Disconnects and destroys the given SSH session, freeing all associated diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index 9dde5111..c03e984d 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -304,20 +304,26 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) 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; /* Validate username provided */ - if (username == NULL) { + if (user->username == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, "SSH authentication requires a username."); return 1; } /* Get list of supported authentication methods */ - char* user_authlist = libssh2_userauth_list(session, username, - strlen(username)); + char* user_authlist = libssh2_userauth_list(session, user->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, "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 */ - if (libssh2_userauth_publickey(session, username, + if (libssh2_userauth_publickey(session, user->username, (unsigned char*) key->public_key, key->public_key_length, 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 */ - else if (password != NULL) { + if (user->password != NULL) { /* Check if password auth is supported on the server */ if (strstr(user_authlist, "password") != NULL) { /* Attempt password authentication */ - if (libssh2_userauth_password(session, username, password)) { + if (libssh2_userauth_password(session, user->username, user->password)) { /* Abort on failure */ 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) { /* 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)) { /* 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, - const char* hostname, const char* port, guac_common_ssh_user* user, int keepalive, - const char* host_key) { + const char* hostname, const char* port, guac_common_ssh_user* user, + int keepalive, const char* host_key, + guac_ssh_credential_handler* credential_handler) { int retval; @@ -561,6 +572,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, common_session->user = user; common_session->session = session; common_session->fd = fd; + common_session->credential_handler = credential_handler; /* Attempt authentication */ if (guac_common_ssh_authenticate(common_session)) { diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 5b351c5b..d0df0531 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -977,7 +977,7 @@ void* guac_rdp_client_thread(void* data) { rdp_client->sftp_session = guac_common_ssh_create_session(client, settings->sftp_hostname, 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 */ if (rdp_client->sftp_session == NULL) { diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 777172ec..619ad286 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -130,19 +130,6 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { } /* 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 */ 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) { guac_client* client = (guac_client*) data; @@ -240,7 +250,7 @@ void* ssh_client_thread(void* data) { /* Open SSH session */ ssh_client->session = guac_common_ssh_create_session(client, 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) { /* Already aborted within guac_common_ssh_create_session() */ return NULL; @@ -292,7 +302,7 @@ void* ssh_client_thread(void* data) { ssh_client->sftp_session = guac_common_ssh_create_session(client, settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval, - settings->host_key); + settings->host_key, NULL); if (ssh_client->sftp_session == NULL) { /* Already aborted within guac_common_ssh_create_session() */ return NULL; diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index a3ae9b12..17033a67 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -328,7 +328,7 @@ void* guac_vnc_client_thread(void* data) { vnc_client->sftp_session = guac_common_ssh_create_session(client, settings->sftp_hostname, 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 */ if (vnc_client->sftp_session == NULL) {