[WIP] Add SSH tunneling for RDP.

This commit is contained in:
Virtually Nick 2023-02-18 08:45:58 -05:00
parent 3693601240
commit 4df9687832
5 changed files with 334 additions and 50 deletions

View File

@ -31,6 +31,7 @@
#ifdef ENABLE_COMMON_SSH #ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h" #include "common-ssh/sftp.h"
#include "common-ssh/ssh.h" #include "common-ssh/ssh.h"
#include "common-ssh/tunnel.h"
#include "common-ssh/user.h" #include "common-ssh/user.h"
#endif #endif
@ -220,6 +221,9 @@ int guac_rdp_client_free_handler(guac_client* client) {
if (rdp_client->sftp_user) if (rdp_client->sftp_user)
guac_common_ssh_destroy_user(rdp_client->sftp_user); guac_common_ssh_destroy_user(rdp_client->sftp_user);
if (rdp_client->ssh_tunnel)
guac_common_ssh_tunnel_cleanup(rdp_client->ssh_tunnel);
guac_common_ssh_uninit(); guac_common_ssh_uninit();
#endif #endif

View File

@ -49,6 +49,7 @@
#ifdef ENABLE_COMMON_SSH #ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h" #include "common-ssh/sftp.h"
#include "common-ssh/ssh.h" #include "common-ssh/ssh.h"
#include "common-ssh/tunnel.h"
#include "common-ssh/user.h" #include "common-ssh/user.h"
#endif #endif
@ -92,6 +93,13 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_settings* settings = rdp_client->settings; guac_rdp_settings* settings = rdp_client->settings;
/* Check for SSH tunneling and start it up, if requested */
if (settings->ssh_tunnel) {
}
/* Push desired settings to FreeRDP */ /* Push desired settings to FreeRDP */
guac_rdp_push_settings(client, settings, instance); guac_rdp_push_settings(client, settings, instance);
@ -814,6 +822,96 @@ void* guac_rdp_client_thread(void* data) {
"SFTP connection succeeded."); "SFTP connection succeeded.");
} }
/* If SSH tunneling is enabled, we set up the tunnel and redirect the connection. */
if (settings->ssh_tunnel) {
/* Allocate memory for the SSH tunnel data. */
rdp_client->ssh_tunnel = malloc(sizeof(guac_ssh_tunnel));
guac_client_log(client, GUAC_LOG_DEBUG,
"SSH tunneling is enabled, connecting via SSH.");
/* Associate the guac_client object with the tunnel. */
rdp_client->ssh_tunnel->client = client;
/* Abort if tunnel username is missing */
if (settings->ssh_tunnel_username == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"An SSH tunnel-specific username is required if "
"SSH tunneling is enabled.");
return NULL;
}
rdp_client->ssh_tunnel->user =
guac_common_ssh_create_user(settings->ssh_tunnel_username);
/* Import SSH tunnel private key, if given */
if (settings->ssh_tunnel_private_key != NULL) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Authenticating SSH tunnel with private key.");
/* Abort if SSH tunnel private key cannot be read */
if (guac_common_ssh_user_import_key(rdp_client->ssh_tunnel->user,
settings->ssh_tunnel_private_key,
settings->ssh_tunnel_passphrase)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"SSH tunnel private key unreadable.");
return NULL;
}
}
/* Otherwise, use specified SSH tunnel password */
else {
guac_client_log(client, GUAC_LOG_DEBUG,
"Authenticating SSH tunnel with password.");
guac_common_ssh_user_set_password(rdp_client->ssh_tunnel->user,
settings->ssh_tunnel_password);
}
/* Attempt SSH tunnel connection */
rdp_client->ssh_tunnel->session =
guac_common_ssh_create_session(client, settings->ssh_tunnel_host,
settings->ssh_tunnel_port, rdp_client->ssh_tunnel->user,
settings->ssh_tunnel_alive_interval,
settings->ssh_tunnel_host_key, NULL);
/* Fail if SSH tunnel connection does not succeed */
if (rdp_client->ssh_tunnel->session == NULL) {
/* Already aborted within guac_common_ssh_create_session() */
return NULL;
}
guac_client_log(client, GUAC_LOG_DEBUG,
"SSH session created for tunneling, initializing the tunnel.");
/* Initialize the tunnel or fail. */
if (guac_common_ssh_tunnel_init(rdp_client->ssh_tunnel,
settings->hostname, settings->port)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to initialize SSH tunnel, aborting connection.");
return NULL;
}
/* If tunnel socket is not returned, bail out. */
if (rdp_client->ssh_tunnel->socket_path == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to obtain socket for SSH tunnel, aborting.");
return NULL;
}
/* Overwrite the hostname with the path to the socket and zero out port. */
settings->hostname = guac_strdup(rdp_client->ssh_tunnel->socket_path);
settings->port = 0;
guac_client_log(client, GUAC_LOG_DEBUG,
"SSH tunnel connection succeeded.");
}
#endif #endif
/* Set up screen recording, if requested */ /* Set up screen recording, if requested */

View File

@ -37,6 +37,7 @@
#ifdef ENABLE_COMMON_SSH #ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h" #include "common-ssh/sftp.h"
#include "common-ssh/ssh.h" #include "common-ssh/ssh.h"
#include "common-ssh/tunnel.h"
#include "common-ssh/user.h" #include "common-ssh/user.h"
#endif #endif
@ -50,6 +51,8 @@
#include <pthread.h> #include <pthread.h>
#include <stdint.h> #include <stdint.h>
#define GUAC_RDP_DEFAULT_CONNECTION_TIMEOUT 30
/** /**
* RDP-specific client data. * RDP-specific client data.
*/ */
@ -154,6 +157,11 @@ typedef struct guac_rdp_client {
* An SFTP-based filesystem. * An SFTP-based filesystem.
*/ */
guac_common_ssh_sftp_filesystem* sftp_filesystem; guac_common_ssh_sftp_filesystem* sftp_filesystem;
/**
* The SSH session used for the tunnel.
*/
guac_ssh_tunnel* ssh_tunnel;
#endif #endif
/** /**

View File

@ -20,6 +20,7 @@
#include "argv.h" #include "argv.h"
#include "common/defaults.h" #include "common/defaults.h"
#include "common/string.h" #include "common/string.h"
#include "common-ssh/ssh-constants.h"
#include "config.h" #include "config.h"
#include "resolution.h" #include "resolution.h"
#include "settings.h" #include "settings.h"
@ -110,6 +111,15 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"sftp-server-alive-interval", "sftp-server-alive-interval",
"sftp-disable-download", "sftp-disable-download",
"sftp-disable-upload", "sftp-disable-upload",
"ssh-tunnel",
"ssh-tunnel-host",
"ssh-tunnel-port",
"ssh-tunnel-host-key",
"ssh-tunnel-username",
"ssh-tunnel-password",
"ssh-tunnel-private-key",
"ssh-tunnel-passphrase",
"ssh-tunnel-alive-interval",
#endif #endif
"recording-path", "recording-path",
@ -493,6 +503,55 @@ enum RDP_ARGS_IDX {
* blank otherwise. * blank otherwise.
*/ */
IDX_SFTP_DISABLE_UPLOAD, IDX_SFTP_DISABLE_UPLOAD,
/**
* True if SSH tunneling should be enabled. If false or not set, SSH
* tunneling will not be used.
*/
IDX_SSH_TUNNEL,
/**
* The hostname or IP address of the SSH server to use for tunneling.
*/
IDX_SSH_TUNNEL_HOST,
/**
* The TCP port of the SSH server to use for tunneling.
*/
IDX_SSH_TUNNEL_PORT,
/**
* If host key checking should be done, the public key of the SSH host
* to be used for tunneling.
*/
IDX_SSH_TUNNEL_HOST_KEY,
/**
* The username for authenticating to the SSH hsot for tunneling.
*/
IDX_SSH_TUNNEL_USERNAME,
/**
* The password to use to authenticate to the SSH host for tunneling.
*/
IDX_SSH_TUNNEL_PASSWORD,
/**
* The private key to use to authenticate to the SSH host for tunneling,
* as an alternative to password-based authentication.
*/
IDX_SSH_TUNNEL_PRIVATE_KEY,
/**
* The passphrase to use to decrypt the private key.
*/
IDX_SSH_TUNNEL_PASSPHRASE,
/**
* The interval at which keepalive packets should be sent to the SSH
* tunneling server, or zero if keepalive should be disabled.
*/
IDX_SSH_TUNNEL_ALIVE_INTERVAL,
#endif #endif
/** /**
@ -1036,66 +1095,112 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_ENABLE_SFTP, 0); IDX_ENABLE_SFTP, 0);
/* Hostname for SFTP connection */ /* Only parse remaining SFTP settings if it's enabled. */
settings->sftp_hostname = if (settings->enable_sftp) {
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, /* Hostname for SFTP connection */
IDX_SFTP_HOSTNAME, settings->hostname); settings->sftp_hostname =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_HOSTNAME, settings->hostname);
/* The public SSH host key. */ /* The public SSH host key. */
settings->sftp_host_key = settings->sftp_host_key =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_HOST_KEY, NULL); IDX_SFTP_HOST_KEY, NULL);
/* Port for SFTP connection */ /* Port for SFTP connection */
settings->sftp_port = settings->sftp_port =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_PORT, "22"); IDX_SFTP_PORT, GUAC_COMMON_SSH_DEFAULT_PORT);
/* Username for SSH/SFTP authentication */ /* Username for SSH/SFTP authentication */
settings->sftp_username = settings->sftp_username =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_USERNAME, IDX_SFTP_USERNAME,
settings->username != NULL ? settings->username : ""); settings->username != NULL ? settings->username : "");
/* Password for SFTP (if not using private key) */ /* Password for SFTP (if not using private key) */
settings->sftp_password = settings->sftp_password =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_PASSWORD, ""); IDX_SFTP_PASSWORD, "");
/* Private key for SFTP (if not using password) */ /* Private key for SFTP (if not using password) */
settings->sftp_private_key = settings->sftp_private_key =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_PRIVATE_KEY, NULL); IDX_SFTP_PRIVATE_KEY, NULL);
/* Passphrase for decrypting the SFTP private key (if applicable */ /* Passphrase for decrypting the SFTP private key (if applicable */
settings->sftp_passphrase = settings->sftp_passphrase =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_PASSPHRASE, ""); IDX_SFTP_PASSPHRASE, "");
/* Default upload directory */ /* Default upload directory */
settings->sftp_directory = settings->sftp_directory =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_DIRECTORY, NULL); IDX_SFTP_DIRECTORY, NULL);
/* SFTP root directory */ /* SFTP root directory */
settings->sftp_root_directory = settings->sftp_root_directory =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_ROOT_DIRECTORY, "/"); IDX_SFTP_ROOT_DIRECTORY, GUAC_COMMON_SSH_SFTP_DEFAULT_ROOT);
/* Default keepalive value */ /* Default keepalive value */
settings->sftp_server_alive_interval = settings->sftp_server_alive_interval =
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_SERVER_ALIVE_INTERVAL, 0); IDX_SFTP_SERVER_ALIVE_INTERVAL, 0);
/* Whether or not to disable file download over SFTP. */ /* Whether or not to disable file download over SFTP. */
settings->sftp_disable_download = 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);
}
/* Parse SSH tunneling. */
settings->ssh_tunnel =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_DISABLE_DOWNLOAD, 0); IDX_SSH_TUNNEL, false);
/* Whether or not to disable file upload over SFTP. */ /* Only parse remaining tunneling settings if it has been enabled. */
settings->sftp_disable_upload = if (settings->ssh_tunnel) {
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SFTP_DISABLE_UPLOAD, 0); settings->ssh_tunnel_host =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_HOST, NULL);
settings->ssh_tunnel_port =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PORT, GUAC_COMMON_SSH_DEFAULT_PORT);
settings->ssh_tunnel_host_key =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_HOST_KEY, NULL);
settings->ssh_tunnel_username =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_USERNAME, NULL);
settings->ssh_tunnel_password =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PASSWORD, NULL);
settings->ssh_tunnel_private_key =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PRIVATE_KEY, NULL);
settings->ssh_tunnel_passphrase =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PASSPHRASE, NULL);
settings->ssh_tunnel_alive_interval =
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_ALIVE_INTERVAL,
GUAC_COMMON_SSH_DEFAULT_ALIVE_INTERVAL);
}
#endif #endif
/* Read recording path */ /* Read recording path */
@ -1324,7 +1429,7 @@ void guac_rdp_settings_free(guac_rdp_settings* settings) {
} }
#ifdef ENABLE_COMMON_SSH #ifdef ENABLE_COMMON_SSH
/* Free SFTP settings */ /* Free SFTP and SSH tunnel settings */
free(settings->sftp_directory); free(settings->sftp_directory);
free(settings->sftp_root_directory); free(settings->sftp_root_directory);
free(settings->sftp_host_key); free(settings->sftp_host_key);
@ -1334,6 +1439,12 @@ void guac_rdp_settings_free(guac_rdp_settings* settings) {
free(settings->sftp_port); free(settings->sftp_port);
free(settings->sftp_private_key); free(settings->sftp_private_key);
free(settings->sftp_username); free(settings->sftp_username);
free(settings->ssh_tunnel_host);
free(settings->ssh_tunnel_host_key);
free(settings->ssh_tunnel_port);
free(settings->ssh_tunnel_username);
free(settings->ssh_tunnel_password);
free(settings->ssh_tunnel_private_key);
#endif #endif
/* Free RD gateway information */ /* Free RD gateway information */

View File

@ -494,6 +494,69 @@ typedef struct guac_rdp_settings {
* Whether or not to disable file upload over SFTP. * Whether or not to disable file upload over SFTP.
*/ */
int sftp_disable_upload; int sftp_disable_upload;
/**
* Whether to enable tunneling of this connection through the specified
* SSH server. If set to "true", guacd will attempt to connect to the SSH
* server and tunnel all of the traffic through the SSH connection. If
* set to "false" or not set, SSH tunneling will not be used.
*/
bool ssh_tunnel;
/**
* The hostname or address of the host through which traffic should be
* tunneled over SSH. If tunneling is enabled, this is required, or the
* connection will be aborted.
*/
char* ssh_tunnel_host;
/**
* The port on which to connect to the SSH server to tunnel traffic, if
* SSH tunneling is enabled. If not specified, this will default to 22, the
* normal SSH port.
*/
char* ssh_tunnel_port;
/**
* The public key of the SSH host through which this connection will be
* tunneled. If unset, no host key checking will be done and the connection
* will be attempted regardless of the identity of the remote host.
*/
char* ssh_tunnel_host_key;
/**
* The username to use when connecting to the SSH host to tunnel traffic.
* This is required if SSH tunneling is enabled.
*/
char* ssh_tunnel_username;
/**
* The password to use when connecting to the SSH host to tunnel traffic,
* if password authentication is used.
*/
char* ssh_tunnel_password;
/**
* The private key to use to authenticate to the SSH server to tunnel traffic,
* if key-based authentication is used.
*/
char* ssh_tunnel_private_key;
/**
* The passphrase of the private key to use to decrypt the private key when
* using key-based authentication, if the key is encrypted.
*/
char* ssh_tunnel_passphrase;
/**
* The interval at which keepalive messages will be sent to the SSH server
* over which the connection is being tunneled. The default is 0, meaning
* that keepalive messages will be disabled. The minimum value is 2 to avoid
* busy loop scenarios, and a value of 1 is automatically increased to 2 by
* the underlying libssh2 implementation.
*/
int ssh_tunnel_alive_interval;
#endif #endif
/** /**