From b26aefd48733a70957518832f5637f68f3a602a8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 20:39:33 -0700 Subject: [PATCH 01/11] GUAC-1171: Expose presence/absence of common SSH core with ENABLE_COMMON_SSH macro. --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 4cc773ea..18f4652f 100644 --- a/configure.ac +++ b/configure.ac @@ -840,6 +840,9 @@ fi AM_CONDITIONAL([ENABLE_COMMON_SSH], [test "x${have_libssh2}" = "xyes" \ -a "x${have_ssl}" = "xyes"]) +AM_COND_IF([ENABLE_COMMON_SSH], + [AC_DEFINE([ENABLE_COMMON_SSH],, + [Whether support for the common SSH core is enabled])]) AM_CONDITIONAL([ENABLE_SSH], [test "x${have_libssh2}" = "xyes" \ -a "x${have_terminal}" = "xyes" \ From 1500e63050187e8535fdd2cca52e95bd0b997eb4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 21:37:53 -0700 Subject: [PATCH 02/11] GUAC-1171: Add SFTP filesystem support to VNC. --- src/protocols/vnc/Makefile.am | 2 + src/protocols/vnc/client.c | 90 +++++++++++++++++++++++++++++++ src/protocols/vnc/client.h | 11 ++++ src/protocols/vnc/guac_handlers.c | 13 +++++ 4 files changed, 116 insertions(+) diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index 39b38e41..cd4a4366 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -46,6 +46,7 @@ endif libguac_client_vnc_la_CFLAGS = \ -Werror -Wall -pedantic -Iinclude \ @COMMON_INCLUDE@ \ + @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ libguac_client_vnc_la_LDFLAGS = \ @@ -56,5 +57,6 @@ libguac_client_vnc_la_LDFLAGS = \ libguac_client_vnc_la_LIBADD = \ @COMMON_LTLIB@ \ + @COMMON_SSH_LTLIB@ \ @LIBGUAC_LTLIB@ diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c index a36fb9a9..ce0bd55e 100644 --- a/src/protocols/vnc/client.c +++ b/src/protocols/vnc/client.c @@ -28,6 +28,8 @@ #include "guac_dot_cursor.h" #include "guac_handlers.h" #include "guac_pointer_cursor.h" +#include "guac_sftp.h" +#include "guac_ssh.h" #include "vnc_handlers.h" #ifdef ENABLE_PULSE @@ -71,6 +73,16 @@ const char* GUAC_CLIENT_ARGS[] = { "listen-timeout", #endif +#ifdef ENABLE_COMMON_SSH + "enable-sftp", + "sftp-hostname", + "sftp-port", + "sftp-username", + "sftp-password", + "sftp-private-key", + "sftp-passphrase", +#endif + NULL }; @@ -101,6 +113,16 @@ enum VNC_ARGS_IDX { IDX_LISTEN_TIMEOUT, #endif +#ifdef ENABLE_COMMON_SSH + IDX_ENABLE_SFTP, + IDX_SFTP_HOSTNAME, + IDX_SFTP_PORT, + IDX_SFTP_USERNAME, + IDX_SFTP_PASSWORD, + IDX_SFTP_PRIVATE_KEY, + IDX_SFTP_PASSPHRASE, +#endif + VNC_ARGS_COUNT }; @@ -337,6 +359,74 @@ int guac_client_init(guac_client* client, int argc, char** argv) { } /* end if audio enabled */ #endif +#ifdef ENABLE_COMMON_SSH + guac_common_ssh_init(client); + + /* Connect via SSH if SFTP is enabled */ + if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Connecting via SSH for SFTP filesystem access."); + + guac_common_ssh_user* user = + guac_common_ssh_create_user(argv[IDX_SFTP_USERNAME]); + + /* Import private key, if given */ + if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Authenticating with private key."); + + /* Abort if private key cannot be read */ + if (guac_common_ssh_user_import_key(user, + argv[IDX_SFTP_PRIVATE_KEY], + argv[IDX_SFTP_PASSPHRASE])) + return 1; + + } + + /* Otherwise, use specified password */ + else { + guac_client_log(client, GUAC_LOG_DEBUG, + "Authenticating with password."); + guac_common_ssh_user_set_password(user, argv[IDX_SFTP_PASSWORD]); + } + + /* Parse hostname - use VNC hostname by default */ + const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME]; + if (sftp_hostname[0] == '\0') + sftp_hostname = guac_client_data->hostname; + + /* Parse port, defaulting to standard SSH port */ + const char* sftp_port = argv[IDX_SFTP_PORT]; + if (sftp_port[0] == '\0') + sftp_port = "22"; + + /* Attempt SSH connection */ + guac_common_ssh_session* session = + guac_common_ssh_create_session(client, sftp_hostname, sftp_port, + user); + + /* Fail if SSH connection does not succeed */ + if (session == NULL) { + /* Already aborted within guac_common_ssh_create_session() */ + return 1; + } + + /* Load and expose filesystem */ + guac_client_data->sftp_filesystem = + guac_common_ssh_create_sftp_filesystem(session, "/"); + + /* Abort if SFTP connection fails */ + if (guac_client_data->sftp_filesystem == NULL) + return 1; + + guac_client_log(client, GUAC_LOG_DEBUG, + "SFTP connection succeeded."); + + } +#endif + /* Set remaining client data */ guac_client_data->rfb_client = rfb_client; guac_client_data->copy_rect_used = 0; diff --git a/src/protocols/vnc/client.h b/src/protocols/vnc/client.h index 4c13b54e..7ad322dd 100644 --- a/src/protocols/vnc/client.h +++ b/src/protocols/vnc/client.h @@ -36,6 +36,10 @@ #include #endif +#ifdef ENABLE_COMMON_SSH +#include "guac_sftp.h" +#endif + /** * The maximum duration of a frame in milliseconds. */ @@ -186,6 +190,13 @@ typedef struct vnc_guac_client_data { */ guac_common_surface* default_surface; +#ifdef ENABLE_COMMON_SSH + /** + * The exposed filesystem object, implemented with SFTP. + */ + guac_object* sftp_filesystem; +#endif + } vnc_guac_client_data; #endif diff --git a/src/protocols/vnc/guac_handlers.c b/src/protocols/vnc/guac_handlers.c index e8fea8bd..92667628 100644 --- a/src/protocols/vnc/guac_handlers.c +++ b/src/protocols/vnc/guac_handlers.c @@ -31,6 +31,11 @@ #include #include +#ifdef ENABLE_COMMON_SSH +#include +#include +#endif + #ifdef ENABLE_PULSE #include "pulse.h" #endif @@ -135,6 +140,14 @@ int vnc_guac_client_free_handler(guac_client* client) { guac_pa_stop_stream(client); #endif +#ifdef ENABLE_COMMON_SSH + /* Free SFTP filesystem, if loaded */ + if (guac_client_data->sftp_filesystem) + guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem); + + guac_common_ssh_uninit(); +#endif + /* Free encodings string, if used */ if (guac_client_data->encodings != NULL) free(guac_client_data->encodings); From 43a534c98b0f0e2d4ffc243769e19ad3fe4d0774 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 21:55:28 -0700 Subject: [PATCH 03/11] GUAC-1171: Add SFTP filesystem support to RDP. --- src/protocols/rdp/Makefile.am | 5 ++ src/protocols/rdp/client.c | 102 ++++++++++++++++++++++++++++++ src/protocols/rdp/client.h | 11 ++++ src/protocols/rdp/guac_handlers.c | 13 ++++ 4 files changed, 131 insertions(+) diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index d67c9976..327cadea 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -119,6 +119,7 @@ endif libguac_client_rdp_la_CFLAGS = \ -Werror -Wall -Iinclude \ @COMMON_INCLUDE@ \ + @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ libguac_client_rdp_la_LDFLAGS = \ @@ -129,6 +130,7 @@ libguac_client_rdp_la_LDFLAGS = \ libguac_client_rdp_la_LIBADD = \ @COMMON_LTLIB@ \ + @COMMON_SSH_LTLIB@ \ @LIBGUAC_LTLIB@ # @@ -138,6 +140,7 @@ libguac_client_rdp_la_LIBADD = \ guacdr_cflags = \ -Werror -Wall -Iinclude \ @COMMON_INCLUDE@ \ + @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ guacdr_ldflags = \ @@ -156,6 +159,7 @@ guacdr_libadd = \ guacsnd_cflags = \ -Werror -Wall -Iinclude \ @COMMON_INCLUDE@ \ + @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ guacsnd_ldflags = \ @@ -174,6 +178,7 @@ guacsnd_libadd = \ guacsvc_cflags = \ -Werror -Wall -Iinclude \ @COMMON_INCLUDE@ \ + @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ guacsvc_ldflags = \ diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c index b0966403..cf1b3295 100644 --- a/src/protocols/rdp/client.c +++ b/src/protocols/rdp/client.c @@ -122,6 +122,17 @@ const char* GUAC_CLIENT_ARGS[] = { "enable-full-window-drag", "enable-desktop-composition", "enable-menu-animations", + +#ifdef ENABLE_COMMON_SSH + "enable-sftp", + "sftp-hostname", + "sftp-port", + "sftp-username", + "sftp-password", + "sftp-private-key", + "sftp-passphrase", +#endif + NULL }; @@ -158,6 +169,17 @@ enum RDP_ARGS_IDX { IDX_ENABLE_FULL_WINDOW_DRAG, IDX_ENABLE_DESKTOP_COMPOSITION, IDX_ENABLE_MENU_ANIMATIONS, + +#ifdef ENABLE_COMMON_SSH + IDX_ENABLE_SFTP, + IDX_SFTP_HOSTNAME, + IDX_SFTP_PORT, + IDX_SFTP_USERNAME, + IDX_SFTP_PASSWORD, + IDX_SFTP_PRIVATE_KEY, + IDX_SFTP_PASSPHRASE, +#endif + RDP_ARGS_COUNT }; @@ -789,6 +811,86 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Load keymap into client */ __guac_rdp_client_load_keymap(client, settings->server_layout); +#ifdef ENABLE_COMMON_SSH + guac_common_ssh_init(client); + + /* Connect via SSH if SFTP is enabled */ + if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Connecting via SSH for SFTP filesystem access."); + + /* Parse username - use RDP username by default */ + const char* sftp_username = argv[IDX_SFTP_USERNAME]; + if (sftp_username[0] == '\0') + sftp_username = settings->username; + + guac_common_ssh_user* user = guac_common_ssh_create_user(sftp_username); + + /* Import private key, if given */ + if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Authenticating with private key."); + + /* Abort if private key cannot be read */ + if (guac_common_ssh_user_import_key(user, + argv[IDX_SFTP_PRIVATE_KEY], + argv[IDX_SFTP_PASSPHRASE])) + return 1; + + } + + /* Otherwise, use specified password */ + else { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Authenticating with password."); + + /* Parse password - use RDP password by default */ + const char* sftp_password = argv[IDX_SFTP_USERNAME]; + if (sftp_password[0] == '\0') + sftp_password = settings->password; + + guac_common_ssh_user_set_password(user, sftp_password); + + } + + /* Parse hostname - use RDP hostname by default */ + const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME]; + if (sftp_hostname[0] == '\0') + sftp_hostname = settings->hostname; + + /* Parse port, defaulting to standard SSH port */ + const char* sftp_port = argv[IDX_SFTP_PORT]; + if (sftp_port[0] == '\0') + sftp_port = "22"; + + /* Attempt SSH connection */ + guac_common_ssh_session* session = + guac_common_ssh_create_session(client, sftp_hostname, sftp_port, + user); + + /* Fail if SSH connection does not succeed */ + if (session == NULL) { + /* Already aborted within guac_common_ssh_create_session() */ + return 1; + } + + /* Load and expose filesystem */ + guac_client_data->sftp_filesystem = + guac_common_ssh_create_sftp_filesystem(session, "/"); + + /* Abort if SFTP connection fails */ + if (guac_client_data->sftp_filesystem == NULL) + return 1; + + guac_client_log(client, GUAC_LOG_DEBUG, + "SFTP connection succeeded."); + + } +#endif + /* Create default surface */ guac_client_data->default_surface = guac_common_surface_alloc(client->socket, GUAC_DEFAULT_LAYER, settings->width, settings->height); diff --git a/src/protocols/rdp/client.h b/src/protocols/rdp/client.h index 349c88a5..b23f8e79 100644 --- a/src/protocols/rdp/client.h +++ b/src/protocols/rdp/client.h @@ -33,6 +33,10 @@ #include "rdp_keymap.h" #include "rdp_settings.h" +#ifdef ENABLE_COMMON_SSH +#include "guac_sftp.h" +#endif + #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT #include "rdp_disp.h" #endif @@ -157,6 +161,13 @@ typedef struct rdp_guac_client_data { */ guac_rdp_fs* filesystem; +#ifdef ENABLE_COMMON_SSH + /** + * The exposed filesystem object, implemented with SFTP. + */ + guac_object* sftp_filesystem; +#endif + #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT /** * Display size update module. diff --git a/src/protocols/rdp/guac_handlers.c b/src/protocols/rdp/guac_handlers.c index 4693a4fd..a660f34e 100644 --- a/src/protocols/rdp/guac_handlers.c +++ b/src/protocols/rdp/guac_handlers.c @@ -33,6 +33,11 @@ #include "rdp_rail.h" #include "rdp_stream.h" +#ifdef ENABLE_COMMON_SSH +#include +#include +#endif + #include #include #include @@ -89,6 +94,14 @@ int rdp_guac_client_free_handler(guac_client* client) { if (guac_client_data->filesystem != NULL) guac_rdp_fs_free(guac_client_data->filesystem); +#ifdef ENABLE_COMMON_SSH + /* Free SFTP filesystem, if loaded */ + if (guac_client_data->sftp_filesystem) + guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem); + + guac_common_ssh_uninit(); +#endif + #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT /* Free display update module */ guac_rdp_disp_free(guac_client_data->disp); From 2e5a3606f3c89cadf5f9aea4ec7fe0ed6ca3c2f8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 22:12:30 -0700 Subject: [PATCH 04/11] GUAC-1171: Set up handler for "file" instruction for SFTP filesystem, if no other handler is more appropriate. --- src/protocols/rdp/Makefile.am | 6 ++++ src/protocols/rdp/client.c | 12 +++++++- src/protocols/rdp/sftp.c | 43 ++++++++++++++++++++++++++ src/protocols/rdp/sftp.h | 58 +++++++++++++++++++++++++++++++++++ src/protocols/vnc/Makefile.am | 6 ++++ src/protocols/vnc/client.c | 11 +++++-- src/protocols/vnc/sftp.c | 43 ++++++++++++++++++++++++++ src/protocols/vnc/sftp.h | 58 +++++++++++++++++++++++++++++++++++ 8 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 src/protocols/rdp/sftp.c create mode 100644 src/protocols/rdp/sftp.h create mode 100644 src/protocols/vnc/sftp.c create mode 100644 src/protocols/vnc/sftp.h diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 327cadea..1dffd4c2 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -97,6 +97,12 @@ noinst_HEADERS = \ resolution.h \ unicode.h +# Build SFTP support if enabled +if ENABLE_COMMON_SSH +noinst_HEADERS += sftp.h +libguac_client_rdp_la_SOURCES += sftp.c +endif + # Add compatibility layer for WinPR if not available if ! ENABLE_WINPR noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c index cf1b3295..03cef18a 100644 --- a/src/protocols/rdp/client.c +++ b/src/protocols/rdp/client.c @@ -35,6 +35,12 @@ #include "rdp_svc.h" #include "resolution.h" +#ifdef ENABLE_COMMON_SSH +#include "guac_sftp.h" +#include "guac_ssh.h" +#include "sftp.h" +#endif + #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT #include "rdp_disp.h" #endif @@ -298,6 +304,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { if (guac_client_data->settings.drive_enabled) { guac_client_data->filesystem = guac_rdp_fs_alloc(client, guac_client_data->settings.drive_path); + client->file_handler = guac_rdp_upload_file_handler; } /* If RDPDR required, load it */ @@ -469,7 +476,6 @@ BOOL rdp_freerdp_post_connect(freerdp* instance) { /* Stream handlers */ client->clipboard_handler = guac_rdp_clipboard_handler; - client->file_handler = guac_rdp_upload_file_handler; client->pipe_handler = guac_rdp_svc_pipe_handler; return TRUE; @@ -885,6 +891,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) { if (guac_client_data->sftp_filesystem == NULL) return 1; + /* Use SFTP for basic uploads, if drive not enabled */ + if (!settings->drive_enabled) + client->file_handler = guac_rdp_sftp_file_handler; + guac_client_log(client, GUAC_LOG_DEBUG, "SFTP connection succeeded."); diff --git a/src/protocols/rdp/sftp.c b/src/protocols/rdp/sftp.c new file mode 100644 index 00000000..a394ee63 --- /dev/null +++ b/src/protocols/rdp/sftp.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" + +#include "client.h" +#include "guac_sftp.h" +#include "sftp.h" + +#include +#include + +int guac_rdp_sftp_file_handler(guac_client* client, guac_stream* stream, + char* mimetype, char* filename) { + + rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data; + guac_object* filesystem = client_data->sftp_filesystem; + + /* Handle file upload */ + return guac_common_ssh_sftp_handle_file_stream(filesystem, stream, + mimetype, filename); + +} + diff --git a/src/protocols/rdp/sftp.h b/src/protocols/rdp/sftp.h new file mode 100644 index 00000000..40a85fee --- /dev/null +++ b/src/protocols/rdp/sftp.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef GUAC_RDP_SFTP_H +#define GUAC_RDP_SFTP_H + +#include "config.h" + +#include +#include + +/** + * Handles an incoming stream from a Guacamole "file" instruction, saving the + * contents of that stream to the file having the given name within the + * upload directory set by guac_sftp_set_upload_path(). + * + * @param client + * The client receiving the uploaded file. + * + * @param stream + * The stream through which the uploaded file data will be received. + * + * @param mimetype + * The mimetype of the data being received. + * + * @param filename + * The filename of the file to write to. This filename will always be taken + * relative to the upload path set by + * guac_common_ssh_sftp_set_upload_path(). + * + * @return + * Zero if the incoming stream has been handled successfully, non-zero on + * failure. + */ +int guac_rdp_sftp_file_handler(guac_client* client, guac_stream* stream, + char* mimetype, char* filename); + +#endif + diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index cd4a4366..db5a909d 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -37,6 +37,12 @@ noinst_HEADERS = \ guac_handlers.h \ vnc_handlers.h +# Optional SFTP support +if ENABLE_COMMON_SSH +libguac_client_vnc_la_SOURCES += sftp.c +noinst_HEADERS += sftp.h +endif + # Optional PulseAudio support if ENABLE_PULSE libguac_client_vnc_la_SOURCES += pulse.c diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c index ce0bd55e..49c53569 100644 --- a/src/protocols/vnc/client.c +++ b/src/protocols/vnc/client.c @@ -28,14 +28,18 @@ #include "guac_dot_cursor.h" #include "guac_handlers.h" #include "guac_pointer_cursor.h" -#include "guac_sftp.h" -#include "guac_ssh.h" #include "vnc_handlers.h" #ifdef ENABLE_PULSE #include "pulse.h" #endif +#ifdef ENABLE_COMMON_SSH +#include "guac_sftp.h" +#include "guac_ssh.h" +#include "sftp.h" +#endif + #include #include #include @@ -421,6 +425,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) { if (guac_client_data->sftp_filesystem == NULL) return 1; + /* Set file handler for basic uploads */ + client->file_handler = guac_vnc_sftp_file_handler; + guac_client_log(client, GUAC_LOG_DEBUG, "SFTP connection succeeded."); diff --git a/src/protocols/vnc/sftp.c b/src/protocols/vnc/sftp.c new file mode 100644 index 00000000..59866330 --- /dev/null +++ b/src/protocols/vnc/sftp.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" + +#include "client.h" +#include "guac_sftp.h" +#include "sftp.h" + +#include +#include + +int guac_vnc_sftp_file_handler(guac_client* client, guac_stream* stream, + char* mimetype, char* filename) { + + vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data; + guac_object* filesystem = client_data->sftp_filesystem; + + /* Handle file upload */ + return guac_common_ssh_sftp_handle_file_stream(filesystem, stream, + mimetype, filename); + +} + diff --git a/src/protocols/vnc/sftp.h b/src/protocols/vnc/sftp.h new file mode 100644 index 00000000..67651557 --- /dev/null +++ b/src/protocols/vnc/sftp.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef GUAC_VNC_SFTP_H +#define GUAC_VNC_SFTP_H + +#include "config.h" + +#include +#include + +/** + * Handles an incoming stream from a Guacamole "file" instruction, saving the + * contents of that stream to the file having the given name within the + * upload directory set by guac_sftp_set_upload_path(). + * + * @param client + * The client receiving the uploaded file. + * + * @param stream + * The stream through which the uploaded file data will be received. + * + * @param mimetype + * The mimetype of the data being received. + * + * @param filename + * The filename of the file to write to. This filename will always be taken + * relative to the upload path set by + * guac_common_ssh_sftp_set_upload_path(). + * + * @return + * Zero if the incoming stream has been handled successfully, non-zero on + * failure. + */ +int guac_vnc_sftp_file_handler(guac_client* client, guac_stream* stream, + char* mimetype, char* filename); + +#endif + From b770800edabe9736a114d9c2c436d7b99d3b4399 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 22:26:37 -0700 Subject: [PATCH 05/11] GUAC-1171: Only include common SSH library if enabled. --- src/protocols/rdp/Makefile.am | 17 ++++++++++------- src/protocols/vnc/Makefile.am | 14 +++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 1dffd4c2..acd23dc6 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -97,12 +97,6 @@ noinst_HEADERS = \ resolution.h \ unicode.h -# Build SFTP support if enabled -if ENABLE_COMMON_SSH -noinst_HEADERS += sftp.h -libguac_client_rdp_la_SOURCES += sftp.c -endif - # Add compatibility layer for WinPR if not available if ! ENABLE_WINPR noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h @@ -136,7 +130,6 @@ libguac_client_rdp_la_LDFLAGS = \ libguac_client_rdp_la_LIBADD = \ @COMMON_LTLIB@ \ - @COMMON_SSH_LTLIB@ \ @LIBGUAC_LTLIB@ # @@ -196,6 +189,16 @@ guacsvc_libadd = \ @COMMON_LTLIB@ \ @LIBGUAC_LTLIB@ +# +# Optional SFTP support +# + +if ENABLE_COMMON_SSH +libguac_client_rdp_la_SOURCES += sftp.c +noinst_HEADERS += sftp.h +libguac_client_rdp_la_LIBADD += @COMMON_SSH_LTLIB@ +endif + # # Autogenerate keymaps # diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am index db5a909d..86db0a75 100644 --- a/src/protocols/vnc/Makefile.am +++ b/src/protocols/vnc/Makefile.am @@ -37,12 +37,6 @@ noinst_HEADERS = \ guac_handlers.h \ vnc_handlers.h -# Optional SFTP support -if ENABLE_COMMON_SSH -libguac_client_vnc_la_SOURCES += sftp.c -noinst_HEADERS += sftp.h -endif - # Optional PulseAudio support if ENABLE_PULSE libguac_client_vnc_la_SOURCES += pulse.c @@ -63,6 +57,12 @@ libguac_client_vnc_la_LDFLAGS = \ libguac_client_vnc_la_LIBADD = \ @COMMON_LTLIB@ \ - @COMMON_SSH_LTLIB@ \ @LIBGUAC_LTLIB@ +# Optional SFTP support +if ENABLE_COMMON_SSH +libguac_client_vnc_la_SOURCES += sftp.c +noinst_HEADERS += sftp.h +libguac_client_vnc_la_LIBADD += @COMMON_SSH_LTLIB@ +endif + From cc717f411298df4d62115223e5c0f2ef49097cc4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 22:41:02 -0700 Subject: [PATCH 06/11] GUAC-1171: Fix SFTP file upload comments. --- src/protocols/rdp/sftp.h | 7 ++----- src/protocols/vnc/sftp.h | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/protocols/rdp/sftp.h b/src/protocols/rdp/sftp.h index 40a85fee..8172ec5f 100644 --- a/src/protocols/rdp/sftp.h +++ b/src/protocols/rdp/sftp.h @@ -30,8 +30,7 @@ /** * Handles an incoming stream from a Guacamole "file" instruction, saving the - * contents of that stream to the file having the given name within the - * upload directory set by guac_sftp_set_upload_path(). + * contents of that stream to the file having the given name. * * @param client * The client receiving the uploaded file. @@ -43,9 +42,7 @@ * The mimetype of the data being received. * * @param filename - * The filename of the file to write to. This filename will always be taken - * relative to the upload path set by - * guac_common_ssh_sftp_set_upload_path(). + * The filename of the file to write to. * * @return * Zero if the incoming stream has been handled successfully, non-zero on diff --git a/src/protocols/vnc/sftp.h b/src/protocols/vnc/sftp.h index 67651557..2477f334 100644 --- a/src/protocols/vnc/sftp.h +++ b/src/protocols/vnc/sftp.h @@ -30,8 +30,7 @@ /** * Handles an incoming stream from a Guacamole "file" instruction, saving the - * contents of that stream to the file having the given name within the - * upload directory set by guac_sftp_set_upload_path(). + * contents of that stream to the file having the given name. * * @param client * The client receiving the uploaded file. @@ -43,9 +42,7 @@ * The mimetype of the data being received. * * @param filename - * The filename of the file to write to. This filename will always be taken - * relative to the upload path set by - * guac_common_ssh_sftp_set_upload_path(). + * The filename of the file to write to. * * @return * Zero if the incoming stream has been handled successfully, non-zero on From c6191f45390766461f35f78a27eeb8a6a8827da4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jul 2015 22:19:36 -0700 Subject: [PATCH 07/11] GUAC-1171: Fix resource leaks. --- src/common-ssh/guac_sftp.c | 17 +++++++++-------- src/common-ssh/guac_ssh.c | 4 ++++ src/common-ssh/guac_ssh_user.c | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c index 54cfa804..d82b6a1a 100644 --- a/src/common-ssh/guac_sftp.c +++ b/src/common-ssh/guac_sftp.c @@ -665,24 +665,25 @@ guac_object* guac_common_ssh_create_sftp_filesystem( guac_client* client = session->client; + /* Request SFTP */ + LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session); + if (sftp_session == NULL) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, + "Unable to start SFTP session."); + return NULL; + } + /* Allocate data for SFTP session */ guac_common_ssh_sftp_data* sftp_data = malloc(sizeof(guac_common_ssh_sftp_data)); /* Associate SSH session with SFTP data */ sftp_data->ssh_session = session; + sftp_data->sftp_session = sftp_session; /* Initially upload files to current directory */ strcpy(sftp_data->upload_path, "."); - /* Request SFTP */ - sftp_data->sftp_session = libssh2_sftp_init(session->session); - if (sftp_data->sftp_session == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, - "Unable to start SFTP session."); - return NULL; - } - /* Init filesystem */ guac_object* filesystem = guac_client_alloc_object(client); filesystem->get_handler = guac_common_ssh_sftp_get_handler; diff --git a/src/common-ssh/guac_ssh.c b/src/common-ssh/guac_ssh.c index 7bbd789d..9fcbad50 100644 --- a/src/common-ssh/guac_ssh.c +++ b/src/common-ssh/guac_ssh.c @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef LIBSSH2_USES_GCRYPT GCRY_THREAD_OPTION_PTHREAD_IMPL; @@ -379,6 +380,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s", gai_strerror(retval)); + close(fd); return NULL; } @@ -422,6 +424,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, if (current_address == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to any addresses."); + close(fd); return NULL; } @@ -439,6 +442,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Session allocation failed."); free(common_session); + close(fd); return NULL; } diff --git a/src/common-ssh/guac_ssh_user.c b/src/common-ssh/guac_ssh_user.c index 84aad9c5..c9691b16 100644 --- a/src/common-ssh/guac_ssh_user.c +++ b/src/common-ssh/guac_ssh_user.c @@ -48,6 +48,7 @@ void guac_common_ssh_destroy_user(guac_common_ssh_user* user) { /* Free all other data */ free(user->password); free(user->username); + free(user); } From 3eac35c158bfcc7c8b136eb84bc3e3bcbc9bd186 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jul 2015 22:22:32 -0700 Subject: [PATCH 08/11] GUAC-1171: Handle socket() error return values. --- src/common-ssh/guac_ssh.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common-ssh/guac_ssh.c b/src/common-ssh/guac_ssh.c index 9fcbad50..9aa308d9 100644 --- a/src/common-ssh/guac_ssh.c +++ b/src/common-ssh/guac_ssh.c @@ -374,6 +374,11 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, /* Get socket */ fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Unable to create socket: %s", strerror(errno)); + return NULL; + } /* Get addresses connection */ if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) { From 1316743b022366bc354b6b127d1d1f29d6e334f6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jul 2015 22:24:26 -0700 Subject: [PATCH 09/11] GUAC-1171: Do not fall through to DSA if RSA signature fails. --- src/common-ssh/guac_ssh_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common-ssh/guac_ssh_key.c b/src/common-ssh/guac_ssh_key.c index 30594492..adbe0412 100644 --- a/src/common-ssh/guac_ssh_key.c +++ b/src/common-ssh/guac_ssh_key.c @@ -178,6 +178,7 @@ int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data, case SSH_KEY_RSA: if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1) return len; + break; case SSH_KEY_DSA: { From 4d941545cd8cf76d63ca7e7fc749b5172ac73a35 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jul 2015 22:27:02 -0700 Subject: [PATCH 10/11] GUAC-1171: Ensure directory names are null-terminated. --- src/common-ssh/guac_sftp.c | 2 +- src/protocols/rdp/rdp_stream.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c index d82b6a1a..a8e7e060 100644 --- a/src/common-ssh/guac_sftp.c +++ b/src/common-ssh/guac_sftp.c @@ -552,7 +552,7 @@ static int guac_common_ssh_sftp_get_handler(guac_client* client, list_state->directory = dir; list_state->sftp_data = sftp_data; strncpy(list_state->directory_name, name, - sizeof(list_state->directory_name)); + sizeof(list_state->directory_name) - 1); /* Allocate stream for body */ guac_stream* stream = guac_client_alloc_stream(client); diff --git a/src/protocols/rdp/rdp_stream.c b/src/protocols/rdp/rdp_stream.c index 299ba078..16aa560e 100644 --- a/src/protocols/rdp/rdp_stream.c +++ b/src/protocols/rdp/rdp_stream.c @@ -486,7 +486,7 @@ int guac_rdp_download_get_handler(guac_client* client, guac_object* object, rdp_stream->ls_status.fs = fs; rdp_stream->ls_status.file_id = file_id; strncpy(rdp_stream->ls_status.directory_name, name, - sizeof(rdp_stream->ls_status.directory_name)); + sizeof(rdp_stream->ls_status.directory_name) - 1); /* Allocate stream for body */ guac_stream* stream = guac_client_alloc_stream(client); From 46ce69a8a9463e5cedb4f2c3cf1525a829438ce3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jul 2015 22:36:08 -0700 Subject: [PATCH 11/11] GUAC-1171: Do not attempt to read error strings when there may be no such strings available. --- src/common-ssh/guac_sftp.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c index a8e7e060..47fb5c13 100644 --- a/src/common-ssh/guac_sftp.c +++ b/src/common-ssh/guac_sftp.c @@ -246,8 +246,8 @@ int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem, guac_socket_flush(client->socket); } else { - guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\": %s", - fullpath, libssh2_sftp_last_error(sftp_data->sftp_session)); + guac_client_log(client, GUAC_LOG_INFO, + "Unable to open file \"%s\"", fullpath); guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); guac_socket_flush(client->socket); @@ -350,8 +350,7 @@ guac_stream* guac_common_ssh_sftp_download_file(guac_object* filesystem, LIBSSH2_FXF_READ, 0); if (file == NULL) { guac_client_log(client, GUAC_LOG_INFO, - "Unable to read file \"%s\": %s", - filename, libssh2_sftp_last_error(sftp_data->sftp_session)); + "Unable to read file \"%s\"", filename); return NULL; } @@ -540,8 +539,7 @@ static int guac_common_ssh_sftp_get_handler(guac_client* client, LIBSSH2_SFTP_HANDLE* dir = libssh2_sftp_opendir(sftp, name); if (dir == NULL) { guac_client_log(client, GUAC_LOG_INFO, - "Unable to read directory \"%s\": %s", - name, libssh2_sftp_last_error(sftp)); + "Unable to read directory \"%s\"", name); return 0; } @@ -576,8 +574,7 @@ static int guac_common_ssh_sftp_get_handler(guac_client* client, LIBSSH2_FXF_READ, 0); if (file == NULL) { guac_client_log(client, GUAC_LOG_INFO, - "Unable to read file \"%s\": %s", - name, libssh2_sftp_last_error(sftp)); + "Unable to read file \"%s\"", name); return 0; } @@ -642,8 +639,8 @@ static int guac_common_ssh_sftp_put_handler(guac_client* client, /* Abort on failure */ else { - guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\": %s", - name, libssh2_sftp_last_error(sftp)); + guac_client_log(client, GUAC_LOG_INFO, + "Unable to open file \"%s\"", name); guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); }