From 0641ccf601dda415edeee265bbfb788041a4836e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Jul 2016 21:34:21 -0700 Subject: [PATCH 1/3] GUACAMOLE-5: Store connection parameters at user level. Maintain reference to owner's connection parameters at client level. --- src/protocols/rdp/user.c | 33 +++++++++++++++++++++++---------- src/protocols/ssh/user.c | 33 +++++++++++++++++++++++---------- src/protocols/telnet/user.c | 33 +++++++++++++++++++++++---------- src/protocols/vnc/user.c | 33 +++++++++++++++++++++++---------- 4 files changed, 92 insertions(+), 40 deletions(-) diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index ef13205f..638f2d11 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -44,19 +44,25 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) { guac_rdp_client* rdp_client = (guac_rdp_client*) user->client->data; + /* Parse provided arguments */ + guac_rdp_settings* settings = guac_rdp_parse_args(user, + argc, (const char**) argv); + + /* Fail if settings cannot be parsed */ + if (settings == NULL) { + guac_user_log(user, GUAC_LOG_INFO, + "Badly formatted client arguments."); + return 1; + } + + /* Store settings at user level */ + user->data = settings; + /* Connect via RDP if owner */ if (user->owner) { - /* Parse arguments into client */ - guac_rdp_settings* settings = rdp_client->settings = - guac_rdp_parse_args(user, argc, (const char**) argv); - - /* Fail if settings cannot be parsed */ - if (settings == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Badly formatted client arguments."); - return 1; - } + /* Store owner's settings at client level */ + rdp_client->settings = settings; /* Start client thread */ if (pthread_create(&rdp_client->client_thread, NULL, @@ -131,8 +137,15 @@ int guac_rdp_user_leave_handler(guac_user* user) { guac_rdp_client* rdp_client = (guac_rdp_client*) user->client->data; + /* Update shared cursor state */ guac_common_cursor_remove_user(rdp_client->display->cursor, user); + /* Free settings if not owner (owner settings will be freed with client) */ + if (!user->owner) { + guac_rdp_settings* settings = (guac_rdp_settings*) user->data; + guac_rdp_settings_free(settings); + } + return 0; } diff --git a/src/protocols/ssh/user.c b/src/protocols/ssh/user.c index 4a224a0b..cd62ad38 100644 --- a/src/protocols/ssh/user.c +++ b/src/protocols/ssh/user.c @@ -39,19 +39,25 @@ int guac_ssh_user_join_handler(guac_user* user, int argc, char** argv) { guac_client* client = user->client; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; + /* Parse provided arguments */ + guac_ssh_settings* settings = guac_ssh_parse_args(user, + argc, (const char**) argv); + + /* Fail if settings cannot be parsed */ + if (settings == NULL) { + guac_user_log(user, GUAC_LOG_INFO, + "Badly formatted client arguments."); + return 1; + } + + /* Store settings at user level */ + user->data = settings; + /* Connect via SSH if owner */ if (user->owner) { - /* Parse arguments into client */ - guac_ssh_settings* settings = ssh_client->settings = - guac_ssh_parse_args(user, argc, (const char**) argv); - - /* Fail if settings cannot be parsed */ - if (settings == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Badly formatted client arguments."); - return 1; - } + /* Store owner's settings at client level */ + ssh_client->settings = settings; /* Start client thread */ if (pthread_create(&(ssh_client->client_thread), NULL, @@ -86,8 +92,15 @@ int guac_ssh_user_leave_handler(guac_user* user) { guac_ssh_client* ssh_client = (guac_ssh_client*) user->client->data; + /* Update shared cursor state */ guac_common_cursor_remove_user(ssh_client->term->cursor, user); + /* Free settings if not owner (owner settings will be freed with client) */ + if (!user->owner) { + guac_ssh_settings* settings = (guac_ssh_settings*) user->data; + guac_ssh_settings_free(settings); + } + return 0; } diff --git a/src/protocols/telnet/user.c b/src/protocols/telnet/user.c index 427112b6..8df697f4 100644 --- a/src/protocols/telnet/user.c +++ b/src/protocols/telnet/user.c @@ -38,19 +38,25 @@ int guac_telnet_user_join_handler(guac_user* user, int argc, char** argv) { guac_client* client = user->client; guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; + /* Parse provided arguments */ + guac_telnet_settings* settings = guac_telnet_parse_args(user, + argc, (const char**) argv); + + /* Fail if settings cannot be parsed */ + if (settings == NULL) { + guac_user_log(user, GUAC_LOG_INFO, + "Badly formatted client arguments."); + return 1; + } + + /* Store settings at user level */ + user->data = settings; + /* Connect via telnet if owner */ if (user->owner) { - /* Parse arguments into client */ - guac_telnet_settings* settings = telnet_client->settings = - guac_telnet_parse_args(user, argc, (const char**) argv); - - /* Fail if settings cannot be parsed */ - if (settings == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Badly formatted client arguments."); - return 1; - } + /* Store owner's settings at client level */ + telnet_client->settings = settings; /* Start client thread */ if (pthread_create(&(telnet_client->client_thread), NULL, @@ -83,8 +89,15 @@ int guac_telnet_user_leave_handler(guac_user* user) { guac_telnet_client* telnet_client = (guac_telnet_client*) user->client->data; + /* Update shared cursor state */ guac_common_cursor_remove_user(telnet_client->term->cursor, user); + /* Free settings if not owner (owner settings will be freed with client) */ + if (!user->owner) { + guac_telnet_settings* settings = (guac_telnet_settings*) user->data; + guac_telnet_settings_free(settings); + } + return 0; } diff --git a/src/protocols/vnc/user.c b/src/protocols/vnc/user.c index 53423a11..75dff0d6 100644 --- a/src/protocols/vnc/user.c +++ b/src/protocols/vnc/user.c @@ -41,19 +41,25 @@ int guac_vnc_user_join_handler(guac_user* user, int argc, char** argv) { guac_vnc_client* vnc_client = (guac_vnc_client*) user->client->data; + /* Parse provided arguments */ + guac_vnc_settings* settings = guac_vnc_parse_args(user, + argc, (const char**) argv); + + /* Fail if settings cannot be parsed */ + if (settings == NULL) { + guac_user_log(user, GUAC_LOG_INFO, + "Badly formatted client arguments."); + return 1; + } + + /* Store settings at user level */ + user->data = settings; + /* Connect via VNC if owner */ if (user->owner) { - /* Parse arguments into client */ - guac_vnc_settings* settings = vnc_client->settings = - guac_vnc_parse_args(user, argc, (const char**) argv); - - /* Fail if settings cannot be parsed */ - if (settings == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Badly formatted client arguments."); - return 1; - } + /* Store owner's settings at client level */ + vnc_client->settings = settings; /* Start client thread */ if (pthread_create(&vnc_client->client_thread, NULL, guac_vnc_client_thread, user->client)) { @@ -101,8 +107,15 @@ int guac_vnc_user_leave_handler(guac_user* user) { guac_vnc_client* vnc_client = (guac_vnc_client*) user->client->data; + /* Update shared cursor state */ guac_common_cursor_remove_user(vnc_client->display->cursor, user); + /* Free settings if not owner (owner settings will be freed with client) */ + if (!user->owner) { + guac_vnc_settings* settings = (guac_vnc_settings*) user->data; + guac_vnc_settings_free(settings); + } + return 0; } From 59e66ddc779eea1b2827dc89110561a3f58fb313 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Jul 2016 21:38:53 -0700 Subject: [PATCH 2/3] GUACAMOLE-5: Handle VNC's "read-only" parameter on a per-user basis. --- src/protocols/vnc/user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/vnc/user.c b/src/protocols/vnc/user.c index 75dff0d6..e01ee758 100644 --- a/src/protocols/vnc/user.c +++ b/src/protocols/vnc/user.c @@ -85,7 +85,7 @@ int guac_vnc_user_join_handler(guac_user* user, int argc, char** argv) { } /* Only handle events if not read-only */ - if (vnc_client->settings->read_only == 0) { + if (!settings->read_only) { /* General mouse/keyboard/clipboard events */ user->mouse_handler = guac_vnc_user_mouse_handler; From 1ad99a312e1faa8297bde9f7e6fadd05f1630106 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Jul 2016 22:03:08 -0700 Subject: [PATCH 3/3] GUACAMOLE-5: Add "read-only" parameter to RDP, SSH, and telnet (per-user, just like VNC). --- src/protocols/rdp/rdp_settings.c | 12 ++++++++++++ src/protocols/rdp/rdp_settings.h | 5 +++++ src/protocols/rdp/user.c | 24 ++++++++++++++++++------ src/protocols/ssh/settings.c | 12 ++++++++++++ src/protocols/ssh/settings.h | 5 +++++ src/protocols/ssh/user.c | 21 ++++++++++++++------- src/protocols/telnet/settings.c | 12 ++++++++++++ src/protocols/telnet/settings.h | 5 +++++ src/protocols/telnet/user.c | 17 ++++++++++++----- 9 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/protocols/rdp/rdp_settings.c b/src/protocols/rdp/rdp_settings.c index 82af859b..fd77e92a 100644 --- a/src/protocols/rdp/rdp_settings.c +++ b/src/protocols/rdp/rdp_settings.c @@ -91,6 +91,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = { "create-recording-path", "resize-method", "enable-audio-input", + "read-only", NULL }; @@ -385,6 +386,12 @@ enum RDP_ARGS_IDX { */ IDX_ENABLE_AUDIO_INPUT, + /** + * "true" if this connection should be read-only (user input should be + * dropped), "false" or blank otherwise. + */ + IDX_READ_ONLY, + RDP_ARGS_COUNT }; @@ -528,6 +535,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_PASSWORD, NULL); + /* Read-only mode */ + settings->read_only = + guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_READ_ONLY, 0); + /* Client name */ settings->client_name = guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, diff --git a/src/protocols/rdp/rdp_settings.h b/src/protocols/rdp/rdp_settings.h index 90c79cee..bcbe9b66 100644 --- a/src/protocols/rdp/rdp_settings.h +++ b/src/protocols/rdp/rdp_settings.h @@ -141,6 +141,11 @@ typedef struct guac_rdp_settings { */ char* password; + /** + * Whether this connection is read-only, and user input should be dropped. + */ + int read_only; + /** * The color depth of the display to request, in bits. */ diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index 638f2d11..ae02a106 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -94,12 +94,24 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) { } - user->file_handler = guac_rdp_user_file_handler; - user->mouse_handler = guac_rdp_user_mouse_handler; - user->key_handler = guac_rdp_user_key_handler; - user->size_handler = guac_rdp_user_size_handler; - user->pipe_handler = guac_rdp_svc_pipe_handler; - user->clipboard_handler = guac_rdp_clipboard_handler; + /* Only handle events if not read-only */ + if (!settings->read_only) { + + /* General mouse/keyboard/clipboard events */ + user->mouse_handler = guac_rdp_user_mouse_handler; + user->key_handler = guac_rdp_user_key_handler; + user->clipboard_handler = guac_rdp_clipboard_handler; + + /* Display size change events */ + user->size_handler = guac_rdp_user_size_handler; + + /* Set generic (non-filesystem) file upload handler */ + user->file_handler = guac_rdp_user_file_handler; + + /* Inbound arbitrary named pipes */ + user->pipe_handler = guac_rdp_svc_pipe_handler; + + } return 0; diff --git a/src/protocols/ssh/settings.c b/src/protocols/ssh/settings.c index 417c114e..35f88e0f 100644 --- a/src/protocols/ssh/settings.c +++ b/src/protocols/ssh/settings.c @@ -50,6 +50,7 @@ const char* GUAC_SSH_CLIENT_ARGS[] = { "recording-path", "recording-name", "create-recording-path", + "read-only", NULL }; @@ -158,6 +159,12 @@ enum SSH_ARGS_IDX { */ IDX_CREATE_RECORDING_PATH, + /** + * "true" if this connection should be read-only (user input should be + * dropped), "false" or blank otherwise. + */ + IDX_READ_ONLY, + SSH_ARGS_COUNT }; @@ -232,6 +239,11 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, IDX_PORT, GUAC_SSH_DEFAULT_PORT); + /* Read-only mode */ + settings->read_only = + guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv, + IDX_READ_ONLY, false); + /* Read command, if any */ settings->command = guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, diff --git a/src/protocols/ssh/settings.h b/src/protocols/ssh/settings.h index 5fe10a5b..6d3e47a5 100644 --- a/src/protocols/ssh/settings.h +++ b/src/protocols/ssh/settings.h @@ -94,6 +94,11 @@ typedef struct guac_ssh_settings { */ char* key_passphrase; + /** + * Whether this connection is read-only, and user input should be dropped. + */ + bool read_only; + /** * The command to run instead of the default shell. If a normal shell * session is desired, this will be NULL. diff --git a/src/protocols/ssh/user.c b/src/protocols/ssh/user.c index cd62ad38..4ebbab2f 100644 --- a/src/protocols/ssh/user.c +++ b/src/protocols/ssh/user.c @@ -75,14 +75,21 @@ int guac_ssh_user_join_handler(guac_user* user, int argc, char** argv) { guac_socket_flush(user->socket); } - /* Set per-user event handlers */ - user->key_handler = guac_ssh_user_key_handler; - user->mouse_handler = guac_ssh_user_mouse_handler; - user->size_handler = guac_ssh_user_size_handler; - user->clipboard_handler = guac_ssh_clipboard_handler; + /* Only handle events if not read-only */ + if (!settings->read_only) { - /* Set generic (non-filesystem) file upload handler */ - user->file_handler = guac_sftp_file_handler; + /* General mouse/keyboard/clipboard events */ + user->key_handler = guac_ssh_user_key_handler; + user->mouse_handler = guac_ssh_user_mouse_handler; + user->clipboard_handler = guac_ssh_clipboard_handler; + + /* Display size change events */ + user->size_handler = guac_ssh_user_size_handler; + + /* Set generic (non-filesystem) file upload handler */ + user->file_handler = guac_sftp_file_handler; + + } return 0; diff --git a/src/protocols/telnet/settings.c b/src/protocols/telnet/settings.c index 6754211c..df7b2958 100644 --- a/src/protocols/telnet/settings.c +++ b/src/protocols/telnet/settings.c @@ -46,6 +46,7 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = { "recording-path", "recording-name", "create-recording-path", + "read-only", NULL }; @@ -138,6 +139,12 @@ enum TELNET_ARGS_IDX { */ IDX_CREATE_RECORDING_PATH, + /** + * "true" if this connection should be read-only (user input should be + * dropped), "false" or blank otherwise. + */ + IDX_READ_ONLY, + TELNET_ARGS_COUNT }; @@ -217,6 +224,11 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user, IDX_PASSWORD_REGEX, GUAC_TELNET_DEFAULT_PASSWORD_REGEX)); } + /* Read-only mode */ + settings->read_only = + guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_READ_ONLY, false); + /* Read font name */ settings->font_name = guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, diff --git a/src/protocols/telnet/settings.h b/src/protocols/telnet/settings.h index 062f4779..434e5938 100644 --- a/src/protocols/telnet/settings.h +++ b/src/protocols/telnet/settings.h @@ -112,6 +112,11 @@ typedef struct guac_telnet_settings { */ regex_t* password_regex; + /** + * Whether this connection is read-only, and user input should be dropped. + */ + bool read_only; + /** * The name of the font to use for display rendering. */ diff --git a/src/protocols/telnet/user.c b/src/protocols/telnet/user.c index 8df697f4..aab8f629 100644 --- a/src/protocols/telnet/user.c +++ b/src/protocols/telnet/user.c @@ -74,11 +74,18 @@ int guac_telnet_user_join_handler(guac_user* user, int argc, char** argv) { guac_socket_flush(user->socket); } - /* Set per-user event handlers */ - user->key_handler = guac_telnet_user_key_handler; - user->mouse_handler = guac_telnet_user_mouse_handler; - user->size_handler = guac_telnet_user_size_handler; - user->clipboard_handler = guac_telnet_clipboard_handler; + /* Only handle events if not read-only */ + if (!settings->read_only) { + + /* General mouse/keyboard/clipboard events */ + user->key_handler = guac_telnet_user_key_handler; + user->mouse_handler = guac_telnet_user_mouse_handler; + user->clipboard_handler = guac_telnet_clipboard_handler; + + /* Display size change events */ + user->size_handler = guac_telnet_user_size_handler; + + } return 0;