From b7dca0ed16dda407f10a9d88fe86b9184ff4522b Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 11 Nov 2018 15:16:22 -0500 Subject: [PATCH 1/7] GUACAMOLE-547: Add support for SSH NONE authentication method. --- src/common-ssh/ssh.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index 9dde5111..2763840e 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -321,6 +321,10 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) guac_client_log(client, GUAC_LOG_DEBUG, "Supported authentication methods: %s", user_authlist); + /* If auth list is NULL, then authentication has succeeded with NONE */ + if (user_authlist == NULL) + return 0; + /* Authenticate with private key, if provided */ if (key != NULL) { From 4641da06aca96c04ed2b165627d35a710793ac90 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 11 Nov 2018 16:11:06 -0500 Subject: [PATCH 2/7] GUACAMOLE-547: Relocate NULL check and log when NONE succeeds. --- src/common-ssh/ssh.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index 2763840e..7691bf5d 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -318,12 +318,17 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) /* Get list of supported authentication methods */ char* user_authlist = libssh2_userauth_list(session, username, strlen(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); - /* If auth list is NULL, then authentication has succeeded with NONE */ - if (user_authlist == NULL) - return 0; /* Authenticate with private key, if provided */ if (key != NULL) { From 3d15454097d61156e6a0306caf12ac262ba11968 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Thu, 27 Dec 2018 01:01:06 -0500 Subject: [PATCH 3/7] GUACAMOLE-547: Use a call-back function for getting the password. --- src/common-ssh/common-ssh/ssh.h | 12 +++++++++++- src/common-ssh/ssh.c | 23 +++++++++++++---------- src/protocols/rdp/rdp.c | 2 +- src/protocols/ssh/ssh.c | 24 +++++++++--------------- src/protocols/vnc/vnc.c | 2 +- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 672e7767..ecc1c4b3 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -25,6 +25,11 @@ #include #include +/** + * Handler for retrieving additional credentials. + */ +typedef char* guac_ssh_credential_handler(guac_client* client, char* credName); + /** * An SSH session, backed by libssh2 and associated with a particular * Guacamole client. @@ -50,6 +55,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; @@ -99,7 +109,7 @@ void guac_common_ssh_uninit(); */ 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* host_key, guac_ssh_credential_handler* credential_callback); /** * 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 7691bf5d..3e6d9346 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -304,20 +304,18 @@ 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) { @@ -342,7 +340,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)) { @@ -361,14 +359,18 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) } + /* Down to username + password authentication. */ + 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; @@ -389,7 +391,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 */ @@ -425,7 +427,7 @@ 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* host_key, guac_ssh_credential_handler* credential_handler) { int retval; @@ -570,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 042d3893..86f50967 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -975,7 +975,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 9db545bf..0b7dd320 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,13 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { } +char* guac_ssh_get_credential(guac_client *client, char* credName) { + + guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; + return guac_terminal_prompt(ssh_client->term, credName, false); + +} + void* ssh_input_thread(void* data) { guac_client* client = (guac_client*) data; @@ -239,7 +233,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; @@ -291,7 +285,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 d9f9dbbb..afffaac4 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -262,7 +262,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) { From 9a51d513f244c21b10d00c0f6f73d2232dd40370 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Thu, 27 Dec 2018 13:07:02 -0500 Subject: [PATCH 4/7] GUACAMOLE-547: Provide documentation for the new callback function. --- src/protocols/ssh/ssh.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 0b7dd320..63361e6e 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -137,6 +137,22 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { } +/** + * A call-back function used to gather additional credentials from a client + * during a connection. It takes the guac_client object and a string to + * display to the user, and returns the credentials entered by the user. + * + * @param client + * The guac_client object associated with the current connection + * where additional credentials are required. + * + * @param credName + * The prompt text to display to the screen when prompting for the + * additional credentials. + * + * @return + * The string of credentials gathered from the user. + */ char* guac_ssh_get_credential(guac_client *client, char* credName) { guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; From 3511991e2fcaf354ff9ddc726153c759861901a7 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Tue, 18 Jun 2019 07:52:05 -0400 Subject: [PATCH 5/7] GUACAMOLE-547: Fixes for style and documentation. --- src/common-ssh/common-ssh/ssh.h | 32 +++++++++++++++++++++++++++++--- src/common-ssh/ssh.c | 8 ++++---- src/protocols/ssh/ssh.c | 12 ++++++------ 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index ecc1c4b3..73b86d6e 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -27,8 +27,17 @@ /** * 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. + * */ -typedef char* guac_ssh_credential_handler(guac_client* client, char* credName); +typedef char* guac_ssh_credential_handler(guac_client* client, char* cred_name); /** * An SSH session, backed by libssh2 and associated with a particular @@ -102,14 +111,31 @@ 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. * * @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, guac_ssh_credential_handler* credential_callback); + 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 3e6d9346..c03e984d 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -327,7 +327,6 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) guac_client_log(client, GUAC_LOG_DEBUG, "Supported authentication methods: %s", user_authlist); - /* Authenticate with private key, if provided */ if (key != NULL) { @@ -359,7 +358,7 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) } - /* Down to username + password authentication. */ + /* Attempt authentication with username + password. */ if (user->password == NULL && common_session->credential_handler) user->password = common_session->credential_handler(client, "Password: "); @@ -426,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, guac_ssh_credential_handler* credential_handler) { + 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; diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 63361e6e..0a67be08 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -138,25 +138,25 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { } /** - * A call-back function used to gather additional credentials from a client - * during a connection. It takes the guac_client object and a string to - * display to the user, and returns the credentials entered by the user. + * 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 credName + * @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. */ -char* guac_ssh_get_credential(guac_client *client, char* credName) { +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, credName, false); + return guac_terminal_prompt(ssh_client->term, cred_name, false); } From 22874e23881bb35be08970f362c8ae50d5fc3ec7 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Tue, 18 Jun 2019 17:59:33 -0400 Subject: [PATCH 6/7] GUACAMOLE-547: Document return value of credential handler. --- src/common-ssh/common-ssh/ssh.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 73b86d6e..62475d60 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -36,6 +36,9 @@ * The name of the credential being requested, which will be shared * with the client in order to generate a meaningful prompt. * + * @return + * The credential provided by the user, which should be a dynamically- + * allocated such that it can be freed as required. */ typedef char* guac_ssh_credential_handler(guac_client* client, char* cred_name); From 1baa91f85249cdf12a735a12e46cea353611520b Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 19 Jun 2019 12:38:05 -0400 Subject: [PATCH 7/7] GUACAMOLE-547: Minor changes to function documentation. --- src/common-ssh/common-ssh/ssh.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 62475d60..346d8abc 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -37,8 +37,8 @@ * with the client in order to generate a meaningful prompt. * * @return - * The credential provided by the user, which should be a dynamically- - * allocated such that it can be freed as required. + * 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); @@ -129,7 +129,8 @@ void guac_common_ssh_uninit(); * * @param credential_handler * The handler function for retrieving additional credentials from the user - * as required by the SSH server. + * 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