Merge pull request #54 from glyptodon/vnc-rdp-sftp

GUAC-1171: Add support for SFTP file transfer to VNC and RDP.
This commit is contained in:
James Muehlner 2015-07-13 17:36:07 -07:00
commit 6f93872e0e
18 changed files with 508 additions and 21 deletions

View File

@ -840,6 +840,9 @@ fi
AM_CONDITIONAL([ENABLE_COMMON_SSH], [test "x${have_libssh2}" = "xyes" \ AM_CONDITIONAL([ENABLE_COMMON_SSH], [test "x${have_libssh2}" = "xyes" \
-a "x${have_ssl}" = "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" \ AM_CONDITIONAL([ENABLE_SSH], [test "x${have_libssh2}" = "xyes" \
-a "x${have_terminal}" = "xyes" \ -a "x${have_terminal}" = "xyes" \

View File

@ -246,8 +246,8 @@ int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem,
guac_socket_flush(client->socket); guac_socket_flush(client->socket);
} }
else { else {
guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\": %s", guac_client_log(client, GUAC_LOG_INFO,
fullpath, libssh2_sftp_last_error(sftp_data->sftp_session)); "Unable to open file \"%s\"", fullpath);
guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed",
GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND);
guac_socket_flush(client->socket); guac_socket_flush(client->socket);
@ -350,8 +350,7 @@ guac_stream* guac_common_ssh_sftp_download_file(guac_object* filesystem,
LIBSSH2_FXF_READ, 0); LIBSSH2_FXF_READ, 0);
if (file == NULL) { if (file == NULL) {
guac_client_log(client, GUAC_LOG_INFO, guac_client_log(client, GUAC_LOG_INFO,
"Unable to read file \"%s\": %s", "Unable to read file \"%s\"", filename);
filename, libssh2_sftp_last_error(sftp_data->sftp_session));
return NULL; 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); LIBSSH2_SFTP_HANDLE* dir = libssh2_sftp_opendir(sftp, name);
if (dir == NULL) { if (dir == NULL) {
guac_client_log(client, GUAC_LOG_INFO, guac_client_log(client, GUAC_LOG_INFO,
"Unable to read directory \"%s\": %s", "Unable to read directory \"%s\"", name);
name, libssh2_sftp_last_error(sftp));
return 0; return 0;
} }
@ -552,7 +550,7 @@ static int guac_common_ssh_sftp_get_handler(guac_client* client,
list_state->directory = dir; list_state->directory = dir;
list_state->sftp_data = sftp_data; list_state->sftp_data = sftp_data;
strncpy(list_state->directory_name, name, strncpy(list_state->directory_name, name,
sizeof(list_state->directory_name)); sizeof(list_state->directory_name) - 1);
/* Allocate stream for body */ /* Allocate stream for body */
guac_stream* stream = guac_client_alloc_stream(client); guac_stream* stream = guac_client_alloc_stream(client);
@ -576,8 +574,7 @@ static int guac_common_ssh_sftp_get_handler(guac_client* client,
LIBSSH2_FXF_READ, 0); LIBSSH2_FXF_READ, 0);
if (file == NULL) { if (file == NULL) {
guac_client_log(client, GUAC_LOG_INFO, guac_client_log(client, GUAC_LOG_INFO,
"Unable to read file \"%s\": %s", "Unable to read file \"%s\"", name);
name, libssh2_sftp_last_error(sftp));
return 0; return 0;
} }
@ -642,8 +639,8 @@ static int guac_common_ssh_sftp_put_handler(guac_client* client,
/* Abort on failure */ /* Abort on failure */
else { else {
guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\": %s", guac_client_log(client, GUAC_LOG_INFO,
name, libssh2_sftp_last_error(sftp)); "Unable to open file \"%s\"", name);
guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed",
GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND);
} }
@ -665,24 +662,25 @@ guac_object* guac_common_ssh_create_sftp_filesystem(
guac_client* client = session->client; 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 */ /* Allocate data for SFTP session */
guac_common_ssh_sftp_data* sftp_data = guac_common_ssh_sftp_data* sftp_data =
malloc(sizeof(guac_common_ssh_sftp_data)); malloc(sizeof(guac_common_ssh_sftp_data));
/* Associate SSH session with SFTP data */ /* Associate SSH session with SFTP data */
sftp_data->ssh_session = session; sftp_data->ssh_session = session;
sftp_data->sftp_session = sftp_session;
/* Initially upload files to current directory */ /* Initially upload files to current directory */
strcpy(sftp_data->upload_path, "."); 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 */ /* Init filesystem */
guac_object* filesystem = guac_client_alloc_object(client); guac_object* filesystem = guac_client_alloc_object(client);
filesystem->get_handler = guac_common_ssh_sftp_get_handler; filesystem->get_handler = guac_common_ssh_sftp_get_handler;

View File

@ -41,6 +41,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h>
#ifdef LIBSSH2_USES_GCRYPT #ifdef LIBSSH2_USES_GCRYPT
GCRY_THREAD_OPTION_PTHREAD_IMPL; GCRY_THREAD_OPTION_PTHREAD_IMPL;
@ -373,12 +374,18 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
/* Get socket */ /* Get socket */
fd = socket(AF_INET, SOCK_STREAM, 0); 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 */ /* Get addresses connection */
if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) { if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Error parsing given address or port: %s", "Error parsing given address or port: %s",
gai_strerror(retval)); gai_strerror(retval));
close(fd);
return NULL; return NULL;
} }
@ -422,6 +429,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
if (current_address == NULL) { if (current_address == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
"Unable to connect to any addresses."); "Unable to connect to any addresses.");
close(fd);
return NULL; return NULL;
} }
@ -439,6 +447,7 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Session allocation failed."); "Session allocation failed.");
free(common_session); free(common_session);
close(fd);
return NULL; return NULL;
} }

View File

@ -178,6 +178,7 @@ int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
case SSH_KEY_RSA: case SSH_KEY_RSA:
if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1) if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1)
return len; return len;
break;
case SSH_KEY_DSA: { case SSH_KEY_DSA: {

View File

@ -48,6 +48,7 @@ void guac_common_ssh_destroy_user(guac_common_ssh_user* user) {
/* Free all other data */ /* Free all other data */
free(user->password); free(user->password);
free(user->username); free(user->username);
free(user);
} }

View File

@ -119,6 +119,7 @@ endif
libguac_client_rdp_la_CFLAGS = \ libguac_client_rdp_la_CFLAGS = \
-Werror -Wall -Iinclude \ -Werror -Wall -Iinclude \
@COMMON_INCLUDE@ \ @COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@ @LIBGUAC_INCLUDE@
libguac_client_rdp_la_LDFLAGS = \ libguac_client_rdp_la_LDFLAGS = \
@ -138,6 +139,7 @@ libguac_client_rdp_la_LIBADD = \
guacdr_cflags = \ guacdr_cflags = \
-Werror -Wall -Iinclude \ -Werror -Wall -Iinclude \
@COMMON_INCLUDE@ \ @COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@ @LIBGUAC_INCLUDE@
guacdr_ldflags = \ guacdr_ldflags = \
@ -156,6 +158,7 @@ guacdr_libadd = \
guacsnd_cflags = \ guacsnd_cflags = \
-Werror -Wall -Iinclude \ -Werror -Wall -Iinclude \
@COMMON_INCLUDE@ \ @COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@ @LIBGUAC_INCLUDE@
guacsnd_ldflags = \ guacsnd_ldflags = \
@ -174,6 +177,7 @@ guacsnd_libadd = \
guacsvc_cflags = \ guacsvc_cflags = \
-Werror -Wall -Iinclude \ -Werror -Wall -Iinclude \
@COMMON_INCLUDE@ \ @COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@ @LIBGUAC_INCLUDE@
guacsvc_ldflags = \ guacsvc_ldflags = \
@ -185,6 +189,16 @@ guacsvc_libadd = \
@COMMON_LTLIB@ \ @COMMON_LTLIB@ \
@LIBGUAC_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 # Autogenerate keymaps
# #

View File

@ -35,6 +35,12 @@
#include "rdp_svc.h" #include "rdp_svc.h"
#include "resolution.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 #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
#include "rdp_disp.h" #include "rdp_disp.h"
#endif #endif
@ -122,6 +128,17 @@ const char* GUAC_CLIENT_ARGS[] = {
"enable-full-window-drag", "enable-full-window-drag",
"enable-desktop-composition", "enable-desktop-composition",
"enable-menu-animations", "enable-menu-animations",
#ifdef ENABLE_COMMON_SSH
"enable-sftp",
"sftp-hostname",
"sftp-port",
"sftp-username",
"sftp-password",
"sftp-private-key",
"sftp-passphrase",
#endif
NULL NULL
}; };
@ -158,6 +175,17 @@ enum RDP_ARGS_IDX {
IDX_ENABLE_FULL_WINDOW_DRAG, IDX_ENABLE_FULL_WINDOW_DRAG,
IDX_ENABLE_DESKTOP_COMPOSITION, IDX_ENABLE_DESKTOP_COMPOSITION,
IDX_ENABLE_MENU_ANIMATIONS, 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 RDP_ARGS_COUNT
}; };
@ -276,6 +304,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
if (guac_client_data->settings.drive_enabled) { if (guac_client_data->settings.drive_enabled) {
guac_client_data->filesystem = guac_client_data->filesystem =
guac_rdp_fs_alloc(client, guac_client_data->settings.drive_path); guac_rdp_fs_alloc(client, guac_client_data->settings.drive_path);
client->file_handler = guac_rdp_upload_file_handler;
} }
/* If RDPDR required, load it */ /* If RDPDR required, load it */
@ -447,7 +476,6 @@ BOOL rdp_freerdp_post_connect(freerdp* instance) {
/* Stream handlers */ /* Stream handlers */
client->clipboard_handler = guac_rdp_clipboard_handler; client->clipboard_handler = guac_rdp_clipboard_handler;
client->file_handler = guac_rdp_upload_file_handler;
client->pipe_handler = guac_rdp_svc_pipe_handler; client->pipe_handler = guac_rdp_svc_pipe_handler;
return TRUE; return TRUE;
@ -789,6 +817,90 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
/* Load keymap into client */ /* Load keymap into client */
__guac_rdp_client_load_keymap(client, settings->server_layout); __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;
/* 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.");
}
#endif
/* Create default surface */ /* Create default surface */
guac_client_data->default_surface = guac_common_surface_alloc(client->socket, GUAC_DEFAULT_LAYER, guac_client_data->default_surface = guac_common_surface_alloc(client->socket, GUAC_DEFAULT_LAYER,
settings->width, settings->height); settings->width, settings->height);

View File

@ -33,6 +33,10 @@
#include "rdp_keymap.h" #include "rdp_keymap.h"
#include "rdp_settings.h" #include "rdp_settings.h"
#ifdef ENABLE_COMMON_SSH
#include "guac_sftp.h"
#endif
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
#include "rdp_disp.h" #include "rdp_disp.h"
#endif #endif
@ -157,6 +161,13 @@ typedef struct rdp_guac_client_data {
*/ */
guac_rdp_fs* filesystem; 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 #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/** /**
* Display size update module. * Display size update module.

View File

@ -33,6 +33,11 @@
#include "rdp_rail.h" #include "rdp_rail.h"
#include "rdp_stream.h" #include "rdp_stream.h"
#ifdef ENABLE_COMMON_SSH
#include <guac_sftp.h>
#include <guac_ssh.h>
#endif
#include <freerdp/cache/cache.h> #include <freerdp/cache/cache.h>
#include <freerdp/channels/channels.h> #include <freerdp/channels/channels.h>
#include <freerdp/codec/color.h> #include <freerdp/codec/color.h>
@ -89,6 +94,14 @@ int rdp_guac_client_free_handler(guac_client* client) {
if (guac_client_data->filesystem != NULL) if (guac_client_data->filesystem != NULL)
guac_rdp_fs_free(guac_client_data->filesystem); 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 #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/* Free display update module */ /* Free display update module */
guac_rdp_disp_free(guac_client_data->disp); guac_rdp_disp_free(guac_client_data->disp);

View File

@ -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.fs = fs;
rdp_stream->ls_status.file_id = file_id; rdp_stream->ls_status.file_id = file_id;
strncpy(rdp_stream->ls_status.directory_name, name, 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 */ /* Allocate stream for body */
guac_stream* stream = guac_client_alloc_stream(client); guac_stream* stream = guac_client_alloc_stream(client);

43
src/protocols/rdp/sftp.c Normal file
View File

@ -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 <guacamole/client.h>
#include <guacamole/stream.h>
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);
}

55
src/protocols/rdp/sftp.h Normal file
View File

@ -0,0 +1,55 @@
/*
* 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 <guacamole/client.h>
#include <guacamole/stream.h>
/**
* Handles an incoming stream from a Guacamole "file" instruction, saving the
* contents of that stream to the file having the given name.
*
* @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.
*
* @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

View File

@ -46,6 +46,7 @@ endif
libguac_client_vnc_la_CFLAGS = \ libguac_client_vnc_la_CFLAGS = \
-Werror -Wall -pedantic -Iinclude \ -Werror -Wall -pedantic -Iinclude \
@COMMON_INCLUDE@ \ @COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@ @LIBGUAC_INCLUDE@
libguac_client_vnc_la_LDFLAGS = \ libguac_client_vnc_la_LDFLAGS = \
@ -58,3 +59,10 @@ libguac_client_vnc_la_LIBADD = \
@COMMON_LTLIB@ \ @COMMON_LTLIB@ \
@LIBGUAC_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

View File

@ -34,6 +34,12 @@
#include "pulse.h" #include "pulse.h"
#endif #endif
#ifdef ENABLE_COMMON_SSH
#include "guac_sftp.h"
#include "guac_ssh.h"
#include "sftp.h"
#endif
#include <rfb/rfbclient.h> #include <rfb/rfbclient.h>
#include <rfb/rfbproto.h> #include <rfb/rfbproto.h>
#include <guacamole/client.h> #include <guacamole/client.h>
@ -71,6 +77,16 @@ const char* GUAC_CLIENT_ARGS[] = {
"listen-timeout", "listen-timeout",
#endif #endif
#ifdef ENABLE_COMMON_SSH
"enable-sftp",
"sftp-hostname",
"sftp-port",
"sftp-username",
"sftp-password",
"sftp-private-key",
"sftp-passphrase",
#endif
NULL NULL
}; };
@ -101,6 +117,16 @@ enum VNC_ARGS_IDX {
IDX_LISTEN_TIMEOUT, IDX_LISTEN_TIMEOUT,
#endif #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 VNC_ARGS_COUNT
}; };
@ -337,6 +363,77 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
} /* end if audio enabled */ } /* end if audio enabled */
#endif #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;
/* Set file handler for basic uploads */
client->file_handler = guac_vnc_sftp_file_handler;
guac_client_log(client, GUAC_LOG_DEBUG,
"SFTP connection succeeded.");
}
#endif
/* Set remaining client data */ /* Set remaining client data */
guac_client_data->rfb_client = rfb_client; guac_client_data->rfb_client = rfb_client;
guac_client_data->copy_rect_used = 0; guac_client_data->copy_rect_used = 0;

View File

@ -36,6 +36,10 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#endif #endif
#ifdef ENABLE_COMMON_SSH
#include "guac_sftp.h"
#endif
/** /**
* The maximum duration of a frame in milliseconds. * The maximum duration of a frame in milliseconds.
*/ */
@ -186,6 +190,13 @@ typedef struct vnc_guac_client_data {
*/ */
guac_common_surface* default_surface; 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; } vnc_guac_client_data;
#endif #endif

View File

@ -31,6 +31,11 @@
#include <guacamole/timestamp.h> #include <guacamole/timestamp.h>
#include <rfb/rfbclient.h> #include <rfb/rfbclient.h>
#ifdef ENABLE_COMMON_SSH
#include <guac_sftp.h>
#include <guac_ssh.h>
#endif
#ifdef ENABLE_PULSE #ifdef ENABLE_PULSE
#include "pulse.h" #include "pulse.h"
#endif #endif
@ -135,6 +140,14 @@ int vnc_guac_client_free_handler(guac_client* client) {
guac_pa_stop_stream(client); guac_pa_stop_stream(client);
#endif #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 */ /* Free encodings string, if used */
if (guac_client_data->encodings != NULL) if (guac_client_data->encodings != NULL)
free(guac_client_data->encodings); free(guac_client_data->encodings);

43
src/protocols/vnc/sftp.c Normal file
View File

@ -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 <guacamole/client.h>
#include <guacamole/stream.h>
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);
}

55
src/protocols/vnc/sftp.h Normal file
View File

@ -0,0 +1,55 @@
/*
* 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 <guacamole/client.h>
#include <guacamole/stream.h>
/**
* Handles an incoming stream from a Guacamole "file" instruction, saving the
* contents of that stream to the file having the given name.
*
* @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.
*
* @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