From ad751f109a1680818821244d9f43bb5364b8e890 Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Sat, 18 Feb 2023 08:46:24 -0500 Subject: [PATCH] [WIP] Add SSH tunneling settings for Telnet. --- src/protocols/telnet/Makefile.am | 2 + src/protocols/telnet/client.c | 30 ++++ src/protocols/telnet/settings.c | 287 +++++++++++++++++++++++++++++++ src/protocols/telnet/settings.h | 144 ++++++++++++++++ src/protocols/telnet/telnet.c | 211 ++++++++++++++++++----- src/protocols/telnet/telnet.h | 30 ++++ 6 files changed, 657 insertions(+), 47 deletions(-) diff --git a/src/protocols/telnet/Makefile.am b/src/protocols/telnet/Makefile.am index d0264f67..1f3bbe3f 100644 --- a/src/protocols/telnet/Makefile.am +++ b/src/protocols/telnet/Makefile.am @@ -50,11 +50,13 @@ noinst_HEADERS = \ libguac_client_telnet_la_CFLAGS = \ -Werror -Wall -Iinclude \ + @COMMON_SSH_INCLUDE@ \ @LIBGUAC_INCLUDE@ \ @TERMINAL_INCLUDE@ libguac_client_telnet_la_LIBADD = \ @COMMON_LTLIB@ \ + @COMMON_SSH_LTLIB@ \ @LIBGUAC_LTLIB@ \ @TERMINAL_LTLIB@ diff --git a/src/protocols/telnet/client.c b/src/protocols/telnet/client.c index 27edfcda..9077308d 100644 --- a/src/protocols/telnet/client.c +++ b/src/protocols/telnet/client.c @@ -24,6 +24,13 @@ #include "telnet.h" #include "user.h" +#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 + #include #include #include @@ -54,6 +61,10 @@ int guac_client_init(guac_client* client) { client->free_handler = guac_telnet_client_free_handler; client->leave_handler = guac_telnet_user_leave_handler; +#ifdef ENABLE_COMMON_SSH + guac_common_ssh_init(client); +#endif + /* Register handlers for argument values that may be sent after the handshake */ guac_argv_register(GUAC_TELNET_ARGV_COLOR_SCHEME, guac_telnet_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO); guac_argv_register(GUAC_TELNET_ARGV_FONT_NAME, guac_telnet_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO); @@ -93,6 +104,25 @@ int guac_telnet_client_free_handler(guac_client* client) { telnet_free(telnet_client->telnet); } +#ifdef ENABLE_COMMON_SSH + /* Free SFTP filesystem, if loaded */ + if (telnet_client->sftp_filesystem) + guac_common_ssh_destroy_sftp_filesystem(telnet_client->sftp_filesystem); + + /* Free SFTP session */ + if (telnet_client->sftp_session) + guac_common_ssh_destroy_session(telnet_client->sftp_session); + + /* Free SFTP user */ + if (telnet_client->sftp_user) + guac_common_ssh_destroy_user(telnet_client->sftp_user); + + if (telnet_client->ssh_tunnel) + guac_common_ssh_tunnel_cleanup(telnet_client->ssh_tunnel); + + guac_common_ssh_uninit(); +#endif + /* Free settings */ if (telnet_client->settings != NULL) guac_telnet_settings_free(telnet_client->settings); diff --git a/src/protocols/telnet/settings.c b/src/protocols/telnet/settings.c index ddffc8e6..e1a9fe13 100644 --- a/src/protocols/telnet/settings.c +++ b/src/protocols/telnet/settings.c @@ -21,6 +21,7 @@ #include "argv.h" #include "common/defaults.h" +#include "common-ssh/ssh-constants.h" #include "settings.h" #include "terminal/terminal.h" @@ -66,6 +67,32 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = { "wol-broadcast-addr", "wol-udp-port", "wol-wait-time", + +#ifdef ENABLE_COMMON_SSH + "enable-sftp", + "sftp-hostname", + "sftp-host-key", + "sftp-port", + "sftp-username", + "sftp-password", + "sftp-private-key", + "sftp-passphrase", + "sftp-directory", + "sftp-root-directory", + "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 + NULL }; @@ -272,6 +299,137 @@ enum TELNET_ARGS_IDX { */ IDX_WOL_WAIT_TIME, +#ifdef ENABLE_COMMON_SSH + /** + * "true" if SFTP should be enabled for the connection, "false" or + * blank otherwise. + */ + IDX_ENABLE_SFTP, + + /** + * The hostname of the SSH server to connect to for SFTP. If blank, the + * hostname of the telnet server will be used. + */ + IDX_SFTP_HOSTNAME, + + /** + * The public SSH host key to identify the SFTP server. + */ + IDX_SFTP_HOST_KEY, + + /** + * The port of the SSH server to connect to for SFTP. If blank, the default + * SSH port of "22" will be used. + */ + IDX_SFTP_PORT, + + /** + * The username to provide when authenticating with the SSH server for + * SFTP. + */ + IDX_SFTP_USERNAME, + + /** + * The password to provide when authenticating with the SSH server for + * SFTP (if not using a private key). + */ + IDX_SFTP_PASSWORD, + + /** + * The base64-encoded private key to use when authenticating with the SSH + * server for SFTP (if not using a password). + */ + IDX_SFTP_PRIVATE_KEY, + + /** + * The passphrase to use to decrypt the provided base64-encoded private + * key. + */ + IDX_SFTP_PASSPHRASE, + + /** + * The default location for file uploads within the SSH server. This will + * apply only to uploads which do not use the filesystem guac_object (where + * the destination directory is otherwise ambiguous). + */ + IDX_SFTP_DIRECTORY, + + /** + * The path of the directory within the SSH server to expose as a + * filesystem guac_object. If omitted, "/" will be used by default. + */ + IDX_SFTP_ROOT_DIRECTORY, + + /** + * The interval at which SSH keepalive messages are sent to the server for + * SFTP connections. The default is 0 (disabling keepalives), and a value + * of 1 is automatically incremented to 2 by libssh2 to avoid busy loop corner + * 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, + + /** + * 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 + TELNET_ARGS_COUNT }; @@ -530,6 +688,118 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user, } +#ifdef ENABLE_COMMON_SSH + /* SFTP enable/disable */ + settings->enable_sftp = + guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_ENABLE_SFTP, false); + + /* If SFTP is not enabled, no reason to parse the rest. */ + if (settings->enable_sftp) { + /* Hostname for SFTP connection */ + settings->sftp_hostname = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_HOSTNAME, settings->hostname); + + /* The public SSH host key. */ + settings->sftp_host_key = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_HOST_KEY, NULL); + + /* Port for SFTP connection */ + settings->sftp_port = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_PORT, "22"); + + /* Username for SSH/SFTP authentication */ + settings->sftp_username = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_USERNAME, ""); + + /* Password for SFTP (if not using private key) */ + settings->sftp_password = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_PASSWORD, ""); + + /* Private key for SFTP (if not using password) */ + settings->sftp_private_key = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_PRIVATE_KEY, NULL); + + /* Passphrase for decrypting the SFTP private key (if applicable */ + settings->sftp_passphrase = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_PASSPHRASE, ""); + + /* Default upload directory */ + settings->sftp_directory = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_DIRECTORY, NULL); + + /* SFTP root directory */ + settings->sftp_root_directory = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + 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_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_SERVER_ALIVE_INTERVAL, + GUAC_COMMON_SSH_DEFAULT_ALIVE_INTERVAL); + + settings->sftp_disable_download = + guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_DOWNLOAD, false); + + settings->sftp_disable_upload = + guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SFTP_DISABLE_UPLOAD, false); + } + + /* Parse SSH tunneling. */ + settings->ssh_tunnel = + guac_user_parse_args_boolean(user, GUAC_TELNET_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_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_HOST, NULL); + + settings->ssh_tunnel_port = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_PORT, GUAC_COMMON_SSH_DEFAULT_PORT); + + settings->ssh_tunnel_host_key = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_HOST_KEY, NULL); + + settings->ssh_tunnel_username = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_USERNAME, NULL); + + settings->ssh_tunnel_password = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_PASSWORD, NULL); + + settings->ssh_tunnel_private_key = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_PRIVATE_KEY, NULL); + + settings->ssh_tunnel_passphrase = + guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_PASSPHRASE, NULL); + + settings->ssh_tunnel_alive_interval = + guac_user_parse_args_int(user, GUAC_TELNET_CLIENT_ARGS, argv, + IDX_SSH_TUNNEL_ALIVE_INTERVAL, + GUAC_COMMON_SSH_DEFAULT_ALIVE_INTERVAL); + + } +#endif + /* Parsing was successful */ return settings; @@ -570,6 +840,23 @@ void guac_telnet_settings_free(guac_telnet_settings* settings) { free(settings->wol_mac_addr); free(settings->wol_broadcast_addr); + /* Free SSH and SFTP settings. */ + free(settings->sftp_hostname); + free(settings->sftp_host_key); + free(settings->sftp_username); + free(settings->sftp_password); + free(settings->sftp_private_key); + free(settings->sftp_passphrase); + free(settings->sftp_directory); + free(settings->sftp_root_directory); + 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); + free(settings->ssh_tunnel_passphrase); + /* Free overall structure */ free(settings); diff --git a/src/protocols/telnet/settings.h b/src/protocols/telnet/settings.h index 3c5ba8b9..49eff80f 100644 --- a/src/protocols/telnet/settings.h +++ b/src/protocols/telnet/settings.h @@ -269,6 +269,150 @@ typedef struct guac_telnet_settings { */ int wol_wait_time; +#ifdef ENABLE_COMMON_SSH + /** + * Whether SFTP should be enabled for the VNC connection. + */ + bool enable_sftp; + + /** + * The hostname of the SSH server to connect to for SFTP. + */ + char* sftp_hostname; + + /** + * The public SSH host key. + */ + char* sftp_host_key; + + /** + * The port of the SSH server to connect to for SFTP. + */ + char* sftp_port; + + /** + * The username to provide when authenticating with the SSH server for + * SFTP. + */ + char* sftp_username; + + /** + * The password to provide when authenticating with the SSH server for + * SFTP (if not using a private key). + */ + char* sftp_password; + + /** + * The base64-encoded private key to use when authenticating with the SSH + * server for SFTP (if not using a password). + */ + char* sftp_private_key; + + /** + * The passphrase to use to decrypt the provided base64-encoded private + * key. + */ + char* sftp_passphrase; + + /** + * The default location for file uploads within the SSH server. This will + * apply only to uploads which do not use the filesystem guac_object (where + * the destination directory is otherwise ambiguous). + */ + char* sftp_directory; + + /** + * The path of the directory within the SSH server to expose as a + * filesystem guac_object. + */ + char* sftp_root_directory; + + /** + * The interval at which SSH keepalive messages are sent to the server for + * SFTP connections. The default is 0 (disabling keepalives), and a value + * of 1 is automatically increased to 2 by libssh2 to avoid busy loop corner + * 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; + + /** + * 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 + } guac_telnet_settings; /** diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c index 3a4c5049..7b18c423 100644 --- a/src/protocols/telnet/telnet.c +++ b/src/protocols/telnet/telnet.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include #include /** @@ -392,69 +394,184 @@ static telnet_t* __guac_telnet_create_session(guac_client* client) { guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; guac_telnet_settings* settings = telnet_client->settings; - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP - }; + if (settings->ssh_tunnel) { - /* Get socket */ - fd = socket(AF_INET, SOCK_STREAM, 0); + /* Allocate memory for the SSH tunnel data. */ + telnet_client->ssh_tunnel = malloc(sizeof(guac_ssh_tunnel)); - /* Get addresses connection */ - if ((retval = getaddrinfo(settings->hostname, settings->port, - &hints, &addresses))) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s", - gai_strerror(retval)); - return NULL; + guac_client_log(client, GUAC_LOG_DEBUG, + "SSH tunneling is enabled, connecting via SSH."); - } + /* Associate the guac_client object with the tunnel. */ + telnet_client->ssh_tunnel->client = client; - /* Attempt connection to each address until success */ - current_address = addresses; - while (current_address != NULL) { + /* 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; + } - int retval; + telnet_client->ssh_tunnel->user = + guac_common_ssh_create_user(settings->ssh_tunnel_username); - /* Resolve hostname */ - if ((retval = getnameinfo(current_address->ai_addr, - current_address->ai_addrlen, - connected_address, sizeof(connected_address), - connected_port, sizeof(connected_port), - NI_NUMERICHOST | NI_NUMERICSERV))) - guac_client_log(client, GUAC_LOG_DEBUG, "Unable to resolve host: %s", gai_strerror(retval)); + /* Import SSH tunnel private key, if given */ + if (settings->ssh_tunnel_private_key != NULL) { - /* Connect */ - if (connect(fd, current_address->ai_addr, - current_address->ai_addrlen) == 0) { + guac_client_log(client, GUAC_LOG_DEBUG, + "Authenticating SSH tunnel with private key."); - guac_client_log(client, GUAC_LOG_DEBUG, "Successfully connected to " - "host %s, port %s", connected_address, connected_port); - - /* Done if successful connect */ - break; + /* Abort if SSH tunnel private key cannot be read */ + if (guac_common_ssh_user_import_key(telnet_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 log information regarding bind failure */ - else - guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to " - "host %s, port %s: %s", - connected_address, connected_port, strerror(errno)); + /* Otherwise, use specified SSH tunnel password */ + else { - current_address = current_address->ai_next; + guac_client_log(client, GUAC_LOG_DEBUG, + "Authenticating SSH tunnel with password."); + + guac_common_ssh_user_set_password(telnet_client->ssh_tunnel->user, + settings->ssh_tunnel_password); + + } + + /* Attempt SSH tunnel connection */ + telnet_client->ssh_tunnel->session = + guac_common_ssh_create_session(client, settings->ssh_tunnel_host, + settings->ssh_tunnel_port, telnet_client->ssh_tunnel->user, + settings->ssh_tunnel_alive_interval, + settings->ssh_tunnel_host_key, NULL); + + /* Fail if SSH tunnel connection does not succeed */ + if (telnet_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(telnet_client->ssh_tunnel, + settings->hostname, strtol(settings->port, NULL, 10))) { + 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 (telnet_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(telnet_client->ssh_tunnel->socket_path); + settings->port = 0; + + guac_client_log(client, GUAC_LOG_DEBUG, + "SSH tunnel connection succeeded."); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (fd < 0) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Error opening UNIX socket for telnet connection: %s", + strerror(errno)); + return NULL; + } + + struct sockaddr_un socket_addr = { + .sun_family = AF_UNIX + }; + strncpy(socket_addr.sun_path, telnet_client->ssh_tunnel->socket_path, + sizeof(socket_addr.sun_path) - 1); + + if (connect(fd, (const struct sockaddr *) &socket_addr, sizeof(struct sockaddr_un))) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Error connecting to UNIX socket for SSH tunnel: %s", + telnet_client->ssh_tunnel->socket_path); + return NULL; + } } - /* If unable to connect to anything, fail */ - if (current_address == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND, - "Unable to connect to any addresses."); - return NULL; - } + /* SSH tunneling is not in use, so open the standard TCP socket. */ + else { + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP + }; - /* Free addrinfo */ - freeaddrinfo(addresses); + /* Get socket */ + fd = socket(AF_INET, SOCK_STREAM, 0); + + /* Get addresses connection */ + if ((retval = getaddrinfo(settings->hostname, settings->port, + &hints, &addresses))) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s", + gai_strerror(retval)); + return NULL; + + } + + /* Attempt connection to each address until success */ + current_address = addresses; + while (current_address != NULL) { + + int retval; + + /* Resolve hostname */ + if ((retval = getnameinfo(current_address->ai_addr, + current_address->ai_addrlen, + connected_address, sizeof(connected_address), + connected_port, sizeof(connected_port), + NI_NUMERICHOST | NI_NUMERICSERV))) + guac_client_log(client, GUAC_LOG_DEBUG, "Unable to resolve host: %s", gai_strerror(retval)); + + /* Connect */ + if (connect(fd, current_address->ai_addr, + current_address->ai_addrlen) == 0) { + + guac_client_log(client, GUAC_LOG_DEBUG, "Successfully connected to " + "host %s, port %s", connected_address, connected_port); + + /* Done if successful connect */ + break; + + } + + /* Otherwise log information regarding bind failure */ + else + guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to " + "host %s, port %s: %s", + connected_address, connected_port, strerror(errno)); + + current_address = current_address->ai_next; + + } + + /* If unable to connect to anything, fail */ + if (current_address == NULL) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND, + "Unable to connect to any addresses."); + return NULL; + } + + /* Free addrinfo */ + freeaddrinfo(addresses); + } /* Open telnet session */ telnet_t* telnet = telnet_init(__telnet_options, __guac_telnet_event_handler, 0, client); diff --git a/src/protocols/telnet/telnet.h b/src/protocols/telnet/telnet.h index 338f94c8..801458f1 100644 --- a/src/protocols/telnet/telnet.h +++ b/src/protocols/telnet/telnet.h @@ -24,6 +24,13 @@ #include "settings.h" #include "terminal/terminal.h" +#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 + #include #include @@ -77,6 +84,29 @@ typedef struct guac_telnet_client { */ guac_recording* recording; +#ifdef ENABLE_COMMON_SSH + /** + * The user and credentials used to authenticate for SFTP. + */ + guac_common_ssh_user* sftp_user; + + /** + * The SSH session used for SFTP. + */ + guac_common_ssh_session* sftp_session; + + /** + * An SFTP-based filesystem. + */ + guac_common_ssh_sftp_filesystem* sftp_filesystem; + + /** + * The SSH tunnel data. + */ + guac_ssh_tunnel* ssh_tunnel; +#endif + + } guac_telnet_client; /**