Skip to content

Commit

Permalink
GUACAMOLE-312: Implement VNC protocol support for SSH tunneling.
Browse files Browse the repository at this point in the history
  • Loading branch information
necouchman committed Aug 3, 2024
1 parent f169d9c commit 7a60ef2
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 3 deletions.
8 changes: 8 additions & 0 deletions src/protocols/vnc/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "common-ssh/tunnel.h"
#include "common-ssh/user.h"
#endif

Expand Down Expand Up @@ -120,6 +121,10 @@ int guac_client_init(guac_client* client) {
client->leave_handler = guac_vnc_user_leave_handler;
client->free_handler = guac_vnc_client_free_handler;

#ifdef ENABLE_COMMON_SSH
guac_common_ssh_init(client);
#endif

return 0;
}

Expand Down Expand Up @@ -179,6 +184,9 @@ int guac_vnc_client_free_handler(guac_client* client) {
if (vnc_client->sftp_user)
guac_common_ssh_destroy_user(vnc_client->sftp_user);

if (vnc_client->ssh_tunnel)
guac_common_ssh_tunnel_cleanup(vnc_client->ssh_tunnel);

guac_common_ssh_uninit();
#endif

Expand Down
114 changes: 112 additions & 2 deletions src/protocols/vnc/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "argv.h"
#include "client.h"
#include "common/defaults.h"
#include "common-ssh/ssh-constants.h"
#include "settings.h"

#include <guacamole/mem.h>
Expand Down Expand Up @@ -76,6 +77,15 @@ const char* GUAC_VNC_CLIENT_ARGS[] = {
"sftp-server-alive-interval",
"sftp-disable-download",
"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

"recording-path",
Expand Down Expand Up @@ -289,6 +299,55 @@ enum VNC_ARGS_IDX {
* "false" or not set, file uploads will be allowed.
*/
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

/**
Expand Down Expand Up @@ -591,12 +650,13 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user,
/* SFTP root directory */
settings->sftp_root_directory =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SFTP_ROOT_DIRECTORY, "/");
IDX_SFTP_ROOT_DIRECTORY, GUAC_COMMON_SSH_SFTP_DEFAULT_ROOT);

/* Default keepalive value */
settings->sftp_server_alive_interval =
guac_user_parse_args_int(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SFTP_SERVER_ALIVE_INTERVAL, 0);
IDX_SFTP_SERVER_ALIVE_INTERVAL,
GUAC_COMMON_SSH_DEFAULT_ALIVE_INTERVAL);

settings->sftp_disable_download =
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
Expand All @@ -605,6 +665,49 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user,
settings->sftp_disable_upload =
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SFTP_DISABLE_UPLOAD, false);

/* Parse SSH tunneling settings. */
settings->ssh_tunnel =
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL, false);

/* Only parse remaining tunneling settings if it has been enabled. */
if (settings->ssh_tunnel) {

settings->ssh_tunnel_host =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_HOST, NULL);

settings->ssh_tunnel_port =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PORT, GUAC_COMMON_SSH_DEFAULT_PORT);

settings->ssh_tunnel_host_key =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_HOST_KEY, NULL);

settings->ssh_tunnel_username =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_USERNAME, NULL);

settings->ssh_tunnel_password =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PASSWORD, NULL);

settings->ssh_tunnel_private_key =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PRIVATE_KEY, NULL);

settings->ssh_tunnel_passphrase =
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_PASSPHRASE, NULL);

settings->ssh_tunnel_alive_interval =
guac_user_parse_args_int(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_SSH_TUNNEL_ALIVE_INTERVAL,
GUAC_COMMON_SSH_DEFAULT_ALIVE_INTERVAL);

}
#endif

/* Read recording path */
Expand Down Expand Up @@ -719,6 +822,13 @@ void guac_vnc_settings_free(guac_vnc_settings* settings) {
guac_mem_free(settings->sftp_port);
guac_mem_free(settings->sftp_private_key);
guac_mem_free(settings->sftp_username);
guac_mem_free(settings->ssh_tunnel_host);
guac_mem_free(settings->ssh_tunnel_host_key);
guac_mem_free(settings->ssh_tunnel_port);
guac_mem_free(settings->ssh_tunnel_username);
guac_mem_free(settings->ssh_tunnel_password);
guac_mem_free(settings->ssh_tunnel_private_key);
guac_mem_free(settings->ssh_tunnel_passphrase);
#endif

#ifdef ENABLE_PULSE
Expand Down
63 changes: 63 additions & 0 deletions src/protocols/vnc/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,69 @@ typedef struct guac_vnc_settings {
* to "false" or not set, file uploads will be allowed.
*/
bool 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

/**
Expand Down
94 changes: 93 additions & 1 deletion src/protocols/vnc/vnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "common-ssh/tunnel.h"
#include "sftp.h"
#endif

Expand Down Expand Up @@ -330,6 +331,98 @@ void* guac_vnc_client_thread(void* data) {
rfbClientLog = guac_vnc_client_log_info;
rfbClientErr = guac_vnc_client_log_error;

#ifdef ENABLE_COMMON_SSH
/* 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. */
vnc_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. */
vnc_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;
}

vnc_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(vnc_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(vnc_client->ssh_tunnel->user,
settings->ssh_tunnel_password);

}

/* Attempt SSH tunnel connection */
vnc_client->ssh_tunnel->session =
guac_common_ssh_create_session(client, settings->ssh_tunnel_host,
settings->ssh_tunnel_port, vnc_client->ssh_tunnel->user,
settings->ssh_tunnel_alive_interval,
settings->ssh_tunnel_host_key, NULL);

/* Fail if SSH tunnel connection does not succeed */
if (vnc_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(vnc_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 (vnc_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(vnc_client->ssh_tunnel->socket_path);
settings->port = 0;

guac_client_log(client, GUAC_LOG_DEBUG,
"SSH tunnel connection succeeded.");
}
#endif

/* Attempt connection */
rfbClient* rfb_client = guac_vnc_get_client(client);
int retries_remaining = settings->retries;
Expand Down Expand Up @@ -363,7 +456,6 @@ void* guac_vnc_client_thread(void* data) {
#endif

#ifdef ENABLE_COMMON_SSH
guac_common_ssh_init(client);

/* Connect via SSH if SFTP is enabled */
if (settings->enable_sftp) {
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/vnc/vnc.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "common-ssh/tunnel.h"
#include "common-ssh/user.h"
#endif

Expand Down Expand Up @@ -117,6 +118,11 @@ typedef struct guac_vnc_client {
* An SFTP-based filesystem.
*/
guac_common_ssh_sftp_filesystem* sftp_filesystem;

/**
* The data structure containing SSH tunnel-related information.
*/
guac_ssh_tunnel* ssh_tunnel;
#endif

/**
Expand Down

0 comments on commit 7a60ef2

Please sign in to comment.