diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 346d8abc..698fac17 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -26,21 +26,16 @@ #include /** - * Handler for retrieving additional credentials. + * A handler function for retrieving additional credentials for the client. * * @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(). + * The connection parameter that is being requested from the client. */ -typedef char* guac_ssh_credential_handler(guac_client* client, char* cred_name); +typedef void guac_ssh_credential_handler(guac_client* client, char* cred_name); /** * An SSH session, backed by libssh2 and associated with a particular diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index c03e984d..eed4c003 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -360,7 +360,7 @@ 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: "); + common_session->credential_handler(client, "password"); /* Authenticate with password, if provided */ if (user->password != NULL) { diff --git a/src/libguac/guacamole/protocol.h b/src/libguac/guacamole/protocol.h index f4e02880..54f7b072 100644 --- a/src/libguac/guacamole/protocol.h +++ b/src/libguac/guacamole/protocol.h @@ -794,6 +794,22 @@ int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer); int guac_protocol_send_rect(guac_socket* socket, const guac_layer* layer, int x, int y, int width, int height); +/** + * Sends a "required" instruction over the given guac_socket connection. This + * instruction indicates to the client that one or more additional parameters + * is needed to continue the connection. + * + * @param socket + * The guac_socket connection to use. + * + * @param required + * The name of the parameter that is required. + * + * @return + * Zero on success, non-zero on error. + */ +int guac_protocol_send_required(guac_socket* socket, const char* required); + /** * Sends a reset instruction over the given guac_socket connection. * diff --git a/src/libguac/protocol.c b/src/libguac/protocol.c index ff73a558..972ac547 100644 --- a/src/libguac/protocol.c +++ b/src/libguac/protocol.c @@ -961,6 +961,21 @@ int guac_protocol_send_rect(guac_socket* socket, } +int guac_protocol_send_required(guac_socket* socket, const char* required) { + + int ret_val; + + guac_socket_instruction_begin(socket); + ret_val = + guac_socket_write_string(socket, "8.required,") + || __guac_socket_write_length_string(socket, required) + || guac_socket_write_string(socket, ";"); + + guac_socket_instruction_end(socket); + return ret_val; + +} + int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) { int ret_val; diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index ad949b89..fcee5422 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -38,6 +38,7 @@ nodist_libguac_client_rdp_la_SOURCES = \ _generated_keymaps.c libguac_client_rdp_la_SOURCES = \ + argv.c \ beep.c \ bitmap.c \ channels/audio-input/audio-buffer.c \ @@ -82,6 +83,7 @@ libguac_client_rdp_la_SOURCES = \ user.c noinst_HEADERS = \ + argv.h \ beep.h \ bitmap.h \ channels/audio-input/audio-buffer.h \ diff --git a/src/protocols/rdp/argv.c b/src/protocols/rdp/argv.c new file mode 100644 index 00000000..a79a64c4 --- /dev/null +++ b/src/protocols/rdp/argv.c @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" +#include "argv.h" +#include "rdp.h" + +#include +#include +#include + +#include +#include +#include + +/** + * All RDP connection settings which may be updated by unprivileged users + * through "argv" streams. + */ +typedef enum guac_rdp_argv_setting { + + /** + * The username of the connection. + */ + GUAC_RDP_ARGV_SETTING_USERNAME, + + /** + * The password to authenticate the connection. + */ + GUAC_RDP_ARGV_SETTING_PASSWORD, + + /** + * The domain to use for connection authentication. + */ + GUAC_RDP_ARGV_SETTING_DOMAIN + +} guac_rdp_argv_setting; + +/** + * The value or current status of a connection parameter received over an + * "argv" stream. + */ +typedef struct guac_rdp_argv { + + /** + * The specific setting being updated. + */ + guac_rdp_argv_setting setting; + + /** + * Buffer space for containing the received argument value. + */ + char buffer[GUAC_RDP_ARGV_MAX_LENGTH]; + + /** + * The number of bytes received so far. + */ + int length; + +} guac_rdp_argv; + +/** + * Handler for "blob" instructions which appends the data from received blobs + * to the end of the in-progress argument value buffer. + * + * @see guac_user_blob_handler + */ +static int guac_rdp_argv_blob_handler(guac_user* user, + guac_stream* stream, void* data, int length) { + + guac_rdp_argv* argv = (guac_rdp_argv*) stream->data; + + /* Calculate buffer size remaining, including space for null terminator, + * adjusting received length accordingly */ + int remaining = sizeof(argv->buffer) - argv->length - 1; + if (length > remaining) + length = remaining; + + /* Append received data to end of buffer */ + memcpy(argv->buffer + argv->length, data, length); + argv->length += length; + + return 0; + +} + +/** + * Handler for "end" instructions which applies the changes specified by the + * argument value buffer associated with the stream. + * + * @see guac_user_end_handler + */ +static int guac_rdp_argv_end_handler(guac_user* user, + guac_stream* stream) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_settings* settings = rdp_client->settings; + + /* Append null terminator to value */ + guac_rdp_argv* argv = (guac_rdp_argv*) stream->data; + argv->buffer[argv->length] = '\0'; + + /* Apply changes to chosen setting */ + switch (argv->setting) { + + /* Update RDP username. */ + case GUAC_RDP_ARGV_SETTING_USERNAME: + free(settings->username); + settings->username = malloc(strlen(argv->buffer) * sizeof(char)); + strcpy(settings->username, argv->buffer); + pthread_cond_broadcast(&(rdp_client->rdp_cond)); + break; + + case GUAC_RDP_ARGV_SETTING_PASSWORD: + free(settings->password); + settings->password = malloc(strlen(argv->buffer) * sizeof(char)); + strcpy(settings->password, argv->buffer); + pthread_cond_broadcast(&(rdp_client->rdp_cond)); + break; + + case GUAC_RDP_ARGV_SETTING_DOMAIN: + free(settings->domain); + settings->domain = malloc(strlen(argv->buffer) * sizeof(char)); + strcpy(settings->domain, argv->buffer); + pthread_cond_broadcast(&(rdp_client->rdp_cond)); + break; + + } + + free(argv); + return 0; + +} + +int guac_rdp_argv_handler(guac_user* user, guac_stream* stream, + char* mimetype, char* name) { + + guac_rdp_argv_setting setting; + + /* Allow users to update authentication details */ + if (strcmp(name, "username") == 0) + setting = GUAC_RDP_ARGV_SETTING_USERNAME; + else if (strcmp(name, "password") == 0) + setting = GUAC_RDP_ARGV_SETTING_PASSWORD; + else if (strcmp(name, "domain") == 0) + setting = GUAC_RDP_ARGV_SETTING_DOMAIN; + + /* No other connection parameters may be updated */ + else { + guac_protocol_send_ack(user->socket, stream, "Not allowed.", + GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); + guac_socket_flush(user->socket); + return 0; + } + + guac_rdp_argv* argv = malloc(sizeof(guac_rdp_argv)); + argv->setting = setting; + argv->length = 0; + + /* Prepare stream to receive argument value */ + stream->blob_handler = guac_rdp_argv_blob_handler; + stream->end_handler = guac_rdp_argv_end_handler; + stream->data = argv; + + /* Signal stream is ready */ + guac_protocol_send_ack(user->socket, stream, "Ready for updated " + "parameter.", GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + return 0; + +} + diff --git a/src/protocols/rdp/argv.h b/src/protocols/rdp/argv.h new file mode 100644 index 00000000..b265517b --- /dev/null +++ b/src/protocols/rdp/argv.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#ifndef GUAC_RDP_ARGV_H +#define GUAC_RDP_ARGV_H + +#include "config.h" + +#include + +/** + * The maximum number of bytes to allow for any argument value received via an + * argv stream, including null terminator. + */ +#define GUAC_RDP_ARGV_MAX_LENGTH 16384 + +/** + * Handles an incoming stream from a Guacamole "argv" instruction, updating the + * given connection parameter if that parameter is allowed to be updated. + */ +guac_user_argv_handler guac_rdp_argv_handler; + +#endif + diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c index 07c90a33..14b4c7f2 100644 --- a/src/protocols/rdp/client.c +++ b/src/protocols/rdp/client.c @@ -158,6 +158,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Initalize the lock */ pthread_rwlock_init(&(rdp_client->lock), NULL); + /* Init RDP credential lock and condition */ + pthread_mutex_init(&(rdp_client->rdp_credential_lock), &(rdp_client->attributes)); + pthread_cond_init(&(rdp_client->rdp_credential_cond), NULL);; + /* Set handlers */ client->join_handler = guac_rdp_user_join_handler; client->free_handler = guac_rdp_client_free_handler; @@ -218,6 +222,9 @@ int guac_rdp_client_free_handler(guac_client* client) { /* Clean up audio input buffer, if allocated */ if (rdp_client->audio_input != NULL) guac_rdp_audio_buffer_free(rdp_client->audio_input); + + /* Destroy the pthread conditional handler */ + pthread_cond_destroy(&(rdp_client->rdp_cond)); pthread_rwlock_destroy(&(rdp_client->lock)); diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 3d7702c6..8b3bdebe 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -227,10 +228,34 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username, rdpContext* context = instance->context; guac_client* client = ((rdp_freerdp_context*) context)->client; - - /* Warn if connection is likely to fail due to lack of credentials */ - guac_client_log(client, GUAC_LOG_INFO, - "Authentication requested but username or password not given"); + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_settings* settings = rdp_client->settings; + + pthread_mutex_lock(&(rdp_client->rdp_lock)); + + while (settings->username == NULL || strcmp(settings->username, "") == 0) { + guac_protocol_send_required(client->socket, "username"); + guac_socket_flush(client->socket); + pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock)); + *username = settings->username; + } + + while (settings->password == NULL || strcmp(settings->password, "") == 0) { + guac_protocol_send_required(client->socket, "password"); + guac_socket_flush(client->socket); + pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock)); + *password = settings->password; + } + + while (settings->domain == NULL || strcmp(settings->domain, "") == 0) { + guac_protocol_send_required(client->socket, "domain"); + guac_socket_flush(client->socket); + pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock)); + *domain = settings->domain; + } + + pthread_mutex_unlock(&(rdp_client->rdp_lock)); + return TRUE; } diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h index e65f702c..68c4cb00 100644 --- a/src/protocols/rdp/rdp.h +++ b/src/protocols/rdp/rdp.h @@ -153,6 +153,18 @@ typedef struct guac_rdp_client { */ guac_common_list* available_svc; + /** + * Lock which is locked and unlocked for each credential required + * during the connection process. + */ + pthread_mutex_t rdp_credential_lock; + + /** + * Condition which is used when the pthread needs to wait for a + * particular credential to be provided. + */ + pthread_cond_t rdp_credential_cond; + /** * Common attributes for locks. */ diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index ef401965..f5eed822 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -116,6 +116,9 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) { /* Inbound arbitrary named pipes */ user->pipe_handler = guac_rdp_pipe_svc_pipe_handler; + + /* Handler for updating parameters during connection. */ + user->argv_handler = guac_rdp_argv_handler; } diff --git a/src/protocols/ssh/argv.c b/src/protocols/ssh/argv.c index 9b3cf653..ff32afde 100644 --- a/src/protocols/ssh/argv.c +++ b/src/protocols/ssh/argv.c @@ -36,6 +36,7 @@ int guac_ssh_argv_callback(guac_user* user, const char* mimetype, guac_client* client = user->client; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; guac_terminal* terminal = ssh_client->term; + guac_ssh_settings* settings = ssh_client->settings; /* Update color scheme */ if (strcmp(name, GUAC_SSH_ARGV_COLOR_SCHEME) == 0) diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 03f5b0b6..7ee18c4b 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -57,6 +57,32 @@ #include #include +/** + * A function used to generate a prompt to gather additional credentials from + * the guac_client during a connection, and using the specified parameter to + * generate the prompt to the client. + * + * @param client + * The guac_client object associated with the current connection + * where additional credentials are required. + * + * @param cred_name + * The name of the parameter to prompt for in the client. + */ +static void guac_ssh_get_credential(guac_client *client, char* cred_name) { + + guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; + + pthread_mutex_lock(&(ssh_client->term_channel_lock)); + + guac_protocol_send_required(client->socket, cred_name); + guac_socket_flush(client->socket); + + pthread_cond_wait(&(ssh_client->ssh_cond), &(ssh_client->term_channel_lock)); + pthread_mutex_unlock(&(ssh_client->term_channel_lock)); + +} + /** * Produces a new user object containing a username and password or private * key, prompting the user as necessary to obtain that information. @@ -77,9 +103,8 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { guac_common_ssh_user* user; /* Get username */ - if (settings->username == NULL) - settings->username = guac_terminal_prompt(ssh_client->term, - "Login as: ", true); + while (settings->username == NULL) + guac_ssh_get_credential(client, "username"); /* Create user object from username */ user = guac_common_ssh_create_user(settings->username); @@ -103,10 +128,8 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { "Re-attempting private key import (WITH passphrase)"); /* Prompt for passphrase if missing */ - if (settings->key_passphrase == NULL) - settings->key_passphrase = - guac_terminal_prompt(ssh_client->term, - "Key passphrase: ", false); + while (settings->key_passphrase == NULL) + guac_ssh_get_credential(client, "passphrase"); /* Reattempt import with passphrase */ if (guac_common_ssh_user_import_key(user, @@ -144,29 +167,6 @@ 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; @@ -281,6 +281,7 @@ void* ssh_client_thread(void* data) { } pthread_mutex_init(&ssh_client->term_channel_lock, NULL); + pthread_cond_init(&(ssh_client->ssh_cond), NULL); /* Open channel for terminal */ ssh_client->term_channel = @@ -495,6 +496,7 @@ void* ssh_client_thread(void* data) { guac_client_stop(client); pthread_join(input_thread, NULL); + pthread_cond_destroy(&(ssh_client->ssh_cond)); pthread_mutex_destroy(&ssh_client->term_channel_lock); guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended."); diff --git a/src/protocols/ssh/ssh.h b/src/protocols/ssh/ssh.h index cb5d8bc5..47189d24 100644 --- a/src/protocols/ssh/ssh.h +++ b/src/protocols/ssh/ssh.h @@ -89,6 +89,12 @@ typedef struct guac_ssh_client { * Lock dictating access to the SSH terminal channel. */ pthread_mutex_t term_channel_lock; + + /** + * Condition used when SSH client thread needs to wait for Guacamole + * client response. + */ + pthread_cond_t ssh_cond; /** * The current clipboard contents. diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index fcba33f1..419e643e 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -29,6 +29,7 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-vnc.la libguac_client_vnc_la_SOURCES = \ + argv.c \ auth.c \ client.c \ clipboard.c \ @@ -41,6 +42,7 @@ libguac_client_vnc_la_SOURCES = \ vnc.c noinst_HEADERS = \ + argv.h \ auth.h \ client.h \ clipboard.h \ diff --git a/src/protocols/vnc/argv.c b/src/protocols/vnc/argv.c new file mode 100644 index 00000000..3b147c7f --- /dev/null +++ b/src/protocols/vnc/argv.c @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" +#include "argv.h" +#include "vnc.h" + +#include +#include +#include + +#include +#include +#include + +/** + * All VNC connection settings which may be updated by unprivileged users + * through "argv" streams. + */ +typedef enum guac_vnc_argv_setting { + + /** + * The password for the connection. + */ + GUAC_VNC_ARGV_SETTING_PASSWORD + +} guac_vnc_argv_setting; + +/** + * The value or current status of a connection parameter received over an + * "argv" stream. + */ +typedef struct guac_vnc_argv { + + /** + * The specific setting being updated. + */ + guac_vnc_argv_setting setting; + + /** + * Buffer space for containing the received argument value. + */ + char buffer[GUAC_VNC_ARGV_MAX_LENGTH]; + + /** + * The number of bytes received so far. + */ + int length; + +} guac_vnc_argv; + +/** + * Handler for "blob" instructions which appends the data from received blobs + * to the end of the in-progress argument value buffer. + * + * @see guac_user_blob_handler + */ +static int guac_vnc_argv_blob_handler(guac_user* user, guac_stream* stream, + void* data, int length) { + + guac_vnc_argv* argv = (guac_vnc_argv*) stream->data; + + /* Calculate buffer size remaining, including space for null terminator, + * adjusting received length accordingly */ + int remaining = sizeof(argv->buffer) - argv->length - 1; + if (length > remaining) + length = remaining; + + /* Append received data to end of buffer */ + memcpy(argv->buffer + argv->length, data, length); + argv->length += length; + + return 0; + +} + +/** + * Handler for "end" instructions which applies the changes specified by the + * argument value buffer associated with the stream. + * + * @see guac_user_end_handler + */ +static int guac_vnc_argv_end_handler(guac_user* user, guac_stream* stream) { + + guac_client* client = user->client; + guac_vnc_client* vnc_client = (guac_vnc_client*) client->data; + guac_vnc_settings* settings = vnc_client->settings; + + /* Append null terminator to value */ + guac_vnc_argv* argv = (guac_vnc_argv*) stream->data; + argv->buffer[argv->length] = '\0'; + + /* Apply changes to chosen setting */ + switch (argv->setting) { + + /* Update password */ + case GUAC_VNC_ARGV_SETTING_PASSWORD: + + /* Update password in settings */ + if (settings->password != NULL) + free(settings->password); + settings->password = malloc(strlen(argv->buffer) * sizeof(char)); + strcpy(settings->password, argv->buffer); + + pthread_cond_broadcast(&(vnc_client->argv_cond)); + break; + + } + + free(argv); + return 0; + +} + +int guac_vnc_argv_handler(guac_user* user, guac_stream* stream, char* mimetype, + char* name) { + + guac_vnc_argv_setting setting; + + /* Allow users to update authentication information */ + if (strcmp(name, "password") == 0) + setting = GUAC_VNC_ARGV_SETTING_PASSWORD; + + /* No other connection parameters may be updated */ + else { + guac_protocol_send_ack(user->socket, stream, "Not allowed.", + GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); + guac_socket_flush(user->socket); + return 0; + } + + guac_vnc_argv* argv = malloc(sizeof(guac_vnc_argv)); + argv->setting = setting; + argv->length = 0; + + /* Prepare stream to receive argument value */ + stream->blob_handler = guac_vnc_argv_blob_handler; + stream->end_handler = guac_vnc_argv_end_handler; + stream->data = argv; + + /* Signal stream is ready */ + guac_protocol_send_ack(user->socket, stream, "Ready for updated " + "parameter.", GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + return 0; + +} diff --git a/src/protocols/vnc/argv.h b/src/protocols/vnc/argv.h new file mode 100644 index 00000000..599eaaa7 --- /dev/null +++ b/src/protocols/vnc/argv.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef ARGV_H +#define ARGV_H + +#include "config.h" + +#include + + +/** + * The maximum number of bytes to allow for any argument value received via an + * argv stream, including null terminator. + */ +#define GUAC_VNC_ARGV_MAX_LENGTH 16384 + +/** + * Handles an incoming stream from a Guacamole "argv" instruction, updating the + * given connection parameter if that parameter is allowed to be updated. + */ +guac_user_argv_handler guac_vnc_argv_handler; + +#endif /* ARGV_H */ + diff --git a/src/protocols/vnc/auth.c b/src/protocols/vnc/auth.c index 8cc6b1d7..54d2f2e7 100644 --- a/src/protocols/vnc/auth.c +++ b/src/protocols/vnc/auth.c @@ -23,12 +23,29 @@ #include "vnc.h" #include +#include +#include #include #include +#include +#include + char* guac_vnc_get_password(rfbClient* client) { guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); - return ((guac_vnc_client*) gc->data)->settings->password; + guac_vnc_client* vnc_client = ((guac_vnc_client*) gc->data); + guac_vnc_settings* settings = vnc_client->settings; + + if (settings->password == NULL || strcmp(settings->password, "") == 0) { + pthread_mutex_lock(&(vnc_client->argv_lock)); + guac_protocol_send_required(gc->socket, "password"); + guac_socket_flush(gc->socket); + pthread_cond_wait(&(vnc_client->argv_cond), &(vnc_client->argv_lock)); + pthread_mutex_unlock(&(vnc_client->argv_lock)); + } + + return settings->password; + } rfbCredential* guac_vnc_get_credentials(rfbClient* client, int credentialType) { diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c index cee1d4d5..691c490c 100644 --- a/src/protocols/vnc/client.c +++ b/src/protocols/vnc/client.c @@ -50,9 +50,13 @@ int guac_client_init(guac_client* client) { client->data = vnc_client; #ifdef ENABLE_VNC_TLS_LOCKING - /* Initialize the write lock */ + /* Initialize the TLS write lock */ pthread_mutex_init(&(vnc_client->tls_lock), NULL); #endif + + /* Initialize argv lock and condition */ + pthread_mutex_init(&(vnc_client->argv_lock), NULL); + pthread_cond_init(&(vnc_client->argv_cond), NULL); /* Init clipboard */ vnc_client->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH); @@ -135,6 +139,10 @@ int guac_vnc_client_free_handler(guac_client* client) { /* Clean up TLS lock mutex. */ pthread_mutex_destroy(&(vnc_client->tls_lock)); #endif + + /* Clean up argv mutex */ + pthread_cond_destroy(&(vnc_client->argv_cond)); + pthread_mutex_destroy(&(vnc_client->argv_lock)); /* Free generic data struct */ free(client->data); diff --git a/src/protocols/vnc/vnc.h b/src/protocols/vnc/vnc.h index 7c189cfb..e36ac66d 100644 --- a/src/protocols/vnc/vnc.h +++ b/src/protocols/vnc/vnc.h @@ -61,6 +61,16 @@ typedef struct guac_vnc_client { */ pthread_mutex_t tls_lock; #endif + + /** + * The lock for tracking changes via argv. + */ + pthread_mutex_t argv_lock; + + /** + * The condition for signaling argv updates. + */ + pthread_cond_t argv_cond; /** * The underlying VNC client.