diff --git a/src/common-ssh/common-ssh/sftp.h b/src/common-ssh/common-ssh/sftp.h index eafdf21f..a3784ea2 100644 --- a/src/common-ssh/common-ssh/sftp.h +++ b/src/common-ssh/common-ssh/sftp.h @@ -69,6 +69,16 @@ typedef struct guac_common_ssh_sftp_filesystem { * instruction. */ char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + + /** + * If downloads from SFTP to the local browser should be disabled. + */ + int disable_download; + + /** + * If uploads from the local browser to SFTP should be disabled. + */ + int disable_upload; } guac_common_ssh_sftp_filesystem; @@ -122,13 +132,20 @@ typedef struct guac_common_ssh_sftp_ls_state { * The name to send as the name of the filesystem whenever it is exposed * to a user, or NULL to automatically generate a name from the provided * root_path. + * + * @param disable_download + * Whether downloads from the SFTP share to the local browser should be + * disabled. + * + * @param disable_upload + * Whether uploads from the local browser to SFTP should be disabled. * * @return * A new SFTP filesystem object, not yet exposed to users. */ guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( guac_common_ssh_session* session, const char* root_path, - const char* name); + const char* name, int disable_download, int disable_upload); /** * Destroys the given filesystem object, disconnecting from SFTP and freeing diff --git a/src/common-ssh/sftp.c b/src/common-ssh/sftp.c index d57a1826..e12fbf55 100644 --- a/src/common-ssh/sftp.c +++ b/src/common-ssh/sftp.c @@ -429,7 +429,7 @@ int guac_common_ssh_sftp_handle_file_stream( /** * Handler for ack messages which continue an outbound SFTP data transfer - * (download), signalling the current status and requesting additional data. + * (download), signaling the current status and requesting additional data. * The data associated with the given stream is expected to be a pointer to an * open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read. * @@ -786,6 +786,14 @@ static int guac_common_ssh_sftp_get_handler(guac_user* user, /* Otherwise, send file contents */ else { + /* If downloads are disabled, log and return. */ + if (filesystem->disable_download) { + guac_user_log(user, GUAC_LOG_INFO, + "Unable to download file \"%s\", " + "file downloads have been disabled.", fullpath); + return 0; + } + /* Open as normal file */ LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, fullpath, LIBSSH2_FXF_READ, 0); @@ -902,7 +910,11 @@ guac_object* guac_common_ssh_alloc_sftp_filesystem_object( /* Init filesystem */ guac_object* fs_object = guac_user_alloc_object(user); fs_object->get_handler = guac_common_ssh_sftp_get_handler; - fs_object->put_handler = guac_common_ssh_sftp_put_handler; + + /* Only handle uploads if not disabled. */ + if (!filesystem->disable_upload) + fs_object->put_handler = guac_common_ssh_sftp_put_handler; + fs_object->data = filesystem; /* Send filesystem to user */ @@ -915,7 +927,7 @@ guac_object* guac_common_ssh_alloc_sftp_filesystem_object( guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( guac_common_ssh_session* session, const char* root_path, - const char* name) { + const char* name, int disable_download, int disable_upload) { /* Request SFTP */ LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session); @@ -929,6 +941,10 @@ guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( /* Associate SSH session with SFTP data and user */ filesystem->ssh_session = session; filesystem->sftp_session = sftp_session; + + /* Copy over disable flags */ + filesystem->disable_download = disable_download; + filesystem->disable_upload = disable_upload; /* Normalize and store the provided root path */ if (!guac_common_ssh_sftp_normalize_path(filesystem->root_path, diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c index facba6f3..6e0638f9 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c @@ -114,9 +114,13 @@ void guac_rdpdr_fs_process_create(guac_rdp_common_svc* svc, /* Create \Download if it doesn't exist */ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id); if (file != NULL && strcmp(file->absolute_path, "\\") == 0) { - int download_id = - guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download", - GENERIC_READ, 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE); + + /* Only create Download folder if downloads are enabled. */ + if (!((guac_rdp_fs*) devices->data)->disable_download) { + int download_id = + guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download", + GENERIC_READ, 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE); + } if (download_id >= 0) guac_rdp_fs_close((guac_rdp_fs*) device->data, download_id); @@ -261,8 +265,9 @@ void guac_rdpdr_fs_process_close(guac_rdp_common_svc* svc, return; /* If file was written to, and it's in the \Download folder, start stream */ - if (file->bytes_written > 0 && - strncmp(file->absolute_path, "\\Download\\", 10) == 0) { + if (file->bytes_written > 0 + && strncmp(file->absolute_path, "\\Download\\", 10) == 0 + && !((guac_rdp_fs*) device->data)->disable_download) { guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path); guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id); } diff --git a/src/protocols/rdp/fs.c b/src/protocols/rdp/fs.c index 5223b5a6..36bb28d3 100644 --- a/src/protocols/rdp/fs.c +++ b/src/protocols/rdp/fs.c @@ -43,7 +43,7 @@ #include guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path, - int create_drive_path) { + int create_drive_path, int disable_download, int disable_upload) { /* Create drive path if it does not exist */ if (create_drive_path) { @@ -65,6 +65,8 @@ guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path, fs->drive_path = strdup(drive_path); fs->file_id_pool = guac_pool_alloc(0); fs->open_files = 0; + fs->disable_download = disable_download; + fs->disable_upload = disable_upload; return fs; @@ -77,11 +79,15 @@ void guac_rdp_fs_free(guac_rdp_fs* fs) { } guac_object* guac_rdp_fs_alloc_object(guac_rdp_fs* fs, guac_user* user) { - + /* Init filesystem */ guac_object* fs_object = guac_user_alloc_object(user); fs_object->get_handler = guac_rdp_download_get_handler; - fs_object->put_handler = guac_rdp_upload_put_handler; + + /* Assign handler only if uploads are not disabled. */ + if (!fs->disable_upload) + fs_object->put_handler = guac_rdp_upload_put_handler; + fs_object->data = fs; /* Send filesystem to user */ diff --git a/src/protocols/rdp/fs.h b/src/protocols/rdp/fs.h index c8cde409..298ef41b 100644 --- a/src/protocols/rdp/fs.h +++ b/src/protocols/rdp/fs.h @@ -211,6 +211,16 @@ typedef struct guac_rdp_fs { * All available file structures. */ guac_rdp_fs_file files[GUAC_RDP_FS_MAX_FILES]; + + /** + * If downloads from the remote server to the browser should be disabled. + */ + int disable_download; + + /** + * If uploads from the browser to the remote server should be disabled. + */ + int disable_upload; } guac_rdp_fs; @@ -250,12 +260,20 @@ typedef struct guac_rdp_fs_info { * @param create_drive_path * Non-zero if the drive path specified should be automatically created if * it does not yet exist, zero otherwise. + * + * @param disable_download + * Non-zero if downloads from the remote server to the local browser should + * be disabled. + * + * @param disable_upload + * Non-zero if uploads from the browser to the remote server should be + * disabled. * * @return * The newly-allocated filesystem. */ guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path, - int create_drive_path); + int create_drive_path, int disable_download, int disable_upload); /** * Frees the given filesystem. diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 9388d0cd..0988632b 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -550,7 +550,8 @@ void* guac_rdp_client_thread(void* data) { /* Allocate actual emulated filesystem */ rdp_client->filesystem = guac_rdp_fs_alloc(client, settings->drive_path, - settings->create_drive_path); + settings->create_drive_path, settings->disable_download, + settings->disable_upload); /* Expose filesystem to owner */ guac_client_for_owner(client, guac_rdp_fs_expose, @@ -619,7 +620,9 @@ void* guac_rdp_client_thread(void* data) { /* Load and expose filesystem */ rdp_client->sftp_filesystem = guac_common_ssh_create_sftp_filesystem(rdp_client->sftp_session, - settings->sftp_root_directory, NULL); + settings->sftp_root_directory, NULL, + settings->sftp_disable_download, + settings->sftp_disable_upload); /* Expose filesystem to connection owner */ guac_client_for_owner(client, diff --git a/src/protocols/rdp/settings.c b/src/protocols/rdp/settings.c index c6db3c8f..ef7221b8 100644 --- a/src/protocols/rdp/settings.c +++ b/src/protocols/rdp/settings.c @@ -55,6 +55,8 @@ const char* GUAC_RDP_CLIENT_ARGS[] = { "drive-name", "drive-path", "create-drive-path", + "disable-download", + "disable-upload", "console", "console-audio", "server-layout", @@ -91,6 +93,8 @@ const char* GUAC_RDP_CLIENT_ARGS[] = { "sftp-directory", "sftp-root-directory", "sftp-server-alive-interval", + "sftp-disable-download", + "sftp-disable-upload", #endif "recording-path", @@ -214,6 +218,18 @@ enum RDP_ARGS_IDX { * drive if it does not yet exist, "false" or blank otherwise. */ IDX_CREATE_DRIVE_PATH, + + /** + * "true" to disable the ability to download files from a remote server to + * the local client over RDP, "false" or blank otherwise. + */ + IDX_DISABLE_DOWNLOAD, + + /** + * "true" to disable the ability to upload files from the local client to + * the remote server over RDP, "false" or blank otherwise. + */ + IDX_DISABLE_UPLOAD, /** * "true" if this session is a console session, "false" or blank otherwise. @@ -430,6 +446,20 @@ enum RDP_ARGS_IDX { * cases. */ IDX_SFTP_SERVER_ALIVE_INTERVAL, + + /** + * "true" to disable file download from the SFTP server to the local client + * over the SFTP connection, if SFTP is configured and enabled. "false" or + * blank otherwise. + */ + IDX_SFTP_DISABLE_DOWNLOAD, + + /** + * "true" to disable file upload from the SFTP server to the local client + * over the SFTP connection, if SFTP is configured and enabled. "false" or + * blank otherwise. + */ + IDX_SFTP_DISABLE_UPLOAD, #endif /** @@ -839,13 +869,25 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_DRIVE_NAME, "Guacamole Filesystem"); + /* The path on the server to connect the drive. */ settings->drive_path = guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_DRIVE_PATH, ""); + /* If the server path should be created if it doesn't already exist. */ settings->create_drive_path = guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_CREATE_DRIVE_PATH, 0); + + /* If file downloads over RDP should be disabled. */ + settings->disable_download = + guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_DISABLE_DOWNLOAD, 0); + + /* If file uploads over RDP should be disabled. */ + settings->disable_upload = + guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_DISABLE_UPLOAD, 0); /* Pick keymap based on argument */ settings->server_layout = NULL; @@ -918,6 +960,16 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user, settings->sftp_server_alive_interval = guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_SFTP_SERVER_ALIVE_INTERVAL, 0); + + /* Whether or not to disable file download over SFTP. */ + settings->sftp_disable_download = + guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_DOWNLOAD, 0); + + /* Whether or not to disable file upload over SFTP. */ + settings->sftp_disable_upload = + guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_UPLOAD, 0); #endif /* Read recording path */ diff --git a/src/protocols/rdp/settings.h b/src/protocols/rdp/settings.h index 70199b8f..c39c7345 100644 --- a/src/protocols/rdp/settings.h +++ b/src/protocols/rdp/settings.h @@ -228,6 +228,16 @@ typedef struct guac_rdp_settings { * exist. */ int create_drive_path; + + /** + * Whether or not to disable file download over RDP. + */ + int disable_download; + + /** + * Wether or not to disable file upload over RDP. + */ + int disable_upload; /** * Whether this session is a console session. @@ -450,6 +460,16 @@ typedef struct guac_rdp_settings { * cases. */ int sftp_server_alive_interval; + + /** + * Whether or not to disable file download over SFTP. + */ + int sftp_disable_download; + + /** + * Whether or not to disable file upload over SFTP. + */ + int sftp_disable_upload; #endif /** diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index 62df34f7..4e32645a 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -131,16 +131,17 @@ int guac_rdp_user_file_handler(guac_user* user, guac_stream* stream, #ifdef ENABLE_COMMON_SSH guac_rdp_settings* settings = rdp_client->settings; - /* If SFTP is enabled, it should be used for default uploads only if RDPDR - * is not enabled or its upload directory has been set */ - if (rdp_client->sftp_filesystem != NULL) { + /* If SFTP is enabled and SFTP uploads have not been disabled, it should be + * used for default uploads only if RDPDR is not enabled or its upload + * directory has been set */ + if (rdp_client->sftp_filesystem != NULL && !settings->sftp_disable_upload) { if (!settings->drive_enabled || settings->sftp_directory != NULL) return guac_rdp_sftp_file_handler(user, stream, mimetype, filename); } #endif /* Default to using RDPDR uploads (if enabled) */ - if (rdp_client->filesystem != NULL) + if (rdp_client->filesystem != NULL && !settings->disable_upload) return guac_rdp_upload_file_handler(user, stream, mimetype, filename); /* File transfer not enabled */ diff --git a/src/protocols/ssh/settings.c b/src/protocols/ssh/settings.c index 7dab3215..35f3b067 100644 --- a/src/protocols/ssh/settings.c +++ b/src/protocols/ssh/settings.c @@ -39,6 +39,8 @@ const char* GUAC_SSH_CLIENT_ARGS[] = { "font-size", "enable-sftp", "sftp-root-directory", + "sftp-disable-download", + "sftp-disable-upload", "private-key", "passphrase", #ifdef ENABLE_SSH_AGENT @@ -114,6 +116,18 @@ enum SSH_ARGS_IDX { * filesystem guac_object. If omitted, "/" will be used by default. */ IDX_SFTP_ROOT_DIRECTORY, + + /** + * "true" if file downloads over SFTP should be blocked. "false" or blank + * if file downloads should be allowed. + */ + IDX_SFTP_DISABLE_DOWNLOAD, + + /** + * "true" if file uploads over SFTP should be blocked. "false" or blank if + * file uploads should be allowed. + */ + IDX_SFTP_DISABLE_UPLOAD, /** * The private key to use for authentication, if any. @@ -350,6 +364,16 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user, settings->sftp_root_directory = guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, IDX_SFTP_ROOT_DIRECTORY, "/"); + + /* Disable file downloads. */ + settings->sftp_disable_download = + guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_DOWNLOAD, false); + + /* Disable file uploads. */ + settings->sftp_disable_upload = + guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_UPLOAD, false); #ifdef ENABLE_SSH_AGENT settings->enable_agent = diff --git a/src/protocols/ssh/settings.h b/src/protocols/ssh/settings.h index bab21bdf..5e79d39a 100644 --- a/src/protocols/ssh/settings.h +++ b/src/protocols/ssh/settings.h @@ -179,6 +179,20 @@ typedef struct guac_ssh_settings { * filesystem guac_object. */ char* sftp_root_directory; + + /** + * Whether file download over SFTP should be disabled. If set to true, file + * downloads will not be allowed over SFTP. If not set or set to false, file + * downloads will be allowed. + */ + bool sftp_disable_download; + + /** + * Whether file uploads over SFTP should be disabled. If set to true, file + * uploads will not be allowed over SFTP. If not set or set to false, file + * uploads will be allowed. + */ + bool sftp_disable_upload; #ifdef ENABLE_SSH_AGENT /** diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 89572050..9ec9bbe4 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -320,7 +320,8 @@ void* ssh_client_thread(void* data) { /* Request SFTP */ ssh_client->sftp_filesystem = guac_common_ssh_create_sftp_filesystem( ssh_client->sftp_session, settings->sftp_root_directory, - NULL); + NULL, settings->sftp_disable_download, + settings->sftp_disable_upload); /* Expose filesystem to connection owner */ guac_client_for_owner(client, @@ -328,8 +329,11 @@ void* ssh_client_thread(void* data) { ssh_client->sftp_filesystem); /* Init handlers for Guacamole-specific console codes */ - ssh_client->term->upload_path_handler = guac_sftp_set_upload_path; - ssh_client->term->file_download_handler = guac_sftp_download_file; + if (!settings->sftp_disable_upload) + ssh_client->term->upload_path_handler = guac_sftp_set_upload_path; + + if (!settings->sftp_disable_download) + ssh_client->term->file_download_handler = guac_sftp_download_file; guac_client_log(client, GUAC_LOG_DEBUG, "SFTP session initialized"); diff --git a/src/protocols/ssh/user.c b/src/protocols/ssh/user.c index 8ea30a4c..853ae308 100644 --- a/src/protocols/ssh/user.c +++ b/src/protocols/ssh/user.c @@ -99,7 +99,7 @@ int guac_ssh_user_join_handler(guac_user* user, int argc, char** argv) { user->size_handler = guac_ssh_user_size_handler; /* Set generic (non-filesystem) file upload handler */ - if (settings->enable_sftp) + if (settings->enable_sftp && !settings->sftp_disable_upload) user->file_handler = guac_sftp_file_handler; } diff --git a/src/protocols/vnc/settings.c b/src/protocols/vnc/settings.c index 21f64057..76b03650 100644 --- a/src/protocols/vnc/settings.c +++ b/src/protocols/vnc/settings.c @@ -70,6 +70,8 @@ const char* GUAC_VNC_CLIENT_ARGS[] = { "sftp-directory", "sftp-root-directory", "sftp-server-alive-interval", + "sftp-disable-download", + "sftp-disable-upload", #endif "recording-path", @@ -259,6 +261,18 @@ enum VNC_ARGS_IDX { * cases. */ IDX_SFTP_SERVER_ALIVE_INTERVAL, + + /** + * If set to "true", file downloads over SFTP will be blocked. If set to + * "false" or not set, file downloads will be allowed. + */ + IDX_SFTP_DISABLE_DOWNLOAD, + + /** + * If set to "true", file uploads over SFTP will be blocked. If set to + * "false" or not set, file uploads will be allowed. + */ + IDX_SFTP_DISABLE_UPLOAD, #endif /** @@ -486,6 +500,14 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user, settings->sftp_server_alive_interval = guac_user_parse_args_int(user, GUAC_VNC_CLIENT_ARGS, argv, IDX_SFTP_SERVER_ALIVE_INTERVAL, 0); + + settings->sftp_disable_download = + guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_DOWNLOAD, false); + + settings->sftp_disable_upload = + guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_UPLOAD, false); #endif /* Read recording path */ diff --git a/src/protocols/vnc/settings.h b/src/protocols/vnc/settings.h index 34c08ec9..f2205fb6 100644 --- a/src/protocols/vnc/settings.h +++ b/src/protocols/vnc/settings.h @@ -211,6 +211,20 @@ typedef struct guac_vnc_settings { * cases. */ int sftp_server_alive_interval; + + /** + * Whether file downloads over SFTP should be blocked. If set to "true", + * the local client will not be able to download files from the SFTP server. + * If set to "false" or not set, file downloads will be allowed. + */ + bool sftp_disable_download; + + /** + * Whether file uploads over SFTP should be blocked. If set to "true", the + * local client will not be able to upload files to the SFTP server. If set + * to "false" or not set, file uploads will be allowed. + */ + bool sftp_disable_upload; #endif /** diff --git a/src/protocols/vnc/user.c b/src/protocols/vnc/user.c index 0dee5043..a3d38d79 100644 --- a/src/protocols/vnc/user.c +++ b/src/protocols/vnc/user.c @@ -101,7 +101,7 @@ int guac_vnc_user_join_handler(guac_user* user, int argc, char** argv) { #ifdef ENABLE_COMMON_SSH /* Set generic (non-filesystem) file upload handler */ - if (settings->enable_sftp) + if (settings->enable_sftp && !settings->sftp_disable_upload) user->file_handler = guac_vnc_sftp_file_handler; #endif diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index f33b267a..b88f7945 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -342,7 +342,9 @@ void* guac_vnc_client_thread(void* data) { /* Load filesystem */ vnc_client->sftp_filesystem = guac_common_ssh_create_sftp_filesystem(vnc_client->sftp_session, - settings->sftp_root_directory, NULL); + settings->sftp_root_directory, NULL, + settings->sftp_disable_download, + settings->sftp_disable_upload); /* Expose filesystem to connection owner */ guac_client_for_owner(client,