Merge 1.0.0 changes back to master.
This commit is contained in:
commit
67680bd2d5
@ -22,6 +22,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <libssh2.h>
|
||||
|
||||
#include <openssl/ossl_typ.h>
|
||||
|
||||
/**
|
||||
@ -166,5 +169,52 @@ void guac_common_ssh_key_free(guac_common_ssh_key* key);
|
||||
int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
|
||||
int length, unsigned char* sig);
|
||||
|
||||
/**
|
||||
* Verifies the host key for the given hostname/port combination against
|
||||
* one or more known_hosts entries. The known_host entries can either be a
|
||||
* single host_key, provided by the client, or a set of known_hosts entries
|
||||
* provided in the /etc/guacamole/ssh_known_hosts file. Failure to correctly
|
||||
* load the known_hosts entries will result in a connection abort and a returned
|
||||
* error code. A return code of zero indiciates that either no known_hosts entries
|
||||
* were provided, or that the verification succeeded (match). Negative values
|
||||
* indicate internal libssh2 error codes; positive values indicate a failure
|
||||
* during verification of the host key against the known hosts.
|
||||
*
|
||||
* @param session
|
||||
* A pointer to the LIBSSH2_SESSION structure of the SSH connection already
|
||||
* in progress.
|
||||
*
|
||||
* @param client
|
||||
* The current guac_client instance for which the known_hosts checking is
|
||||
* being performed.
|
||||
*
|
||||
* @param host_key
|
||||
* The known host entry provided by the client. If this is non-null and not
|
||||
* empty, it will be the only host key loaded and used for verification. If
|
||||
* this is null or empty an attempt will be made to read the
|
||||
* /etc/guacamole/ssh_known_hosts file and load entries from it.
|
||||
*
|
||||
* @param hostname
|
||||
* The hostname or IP of the server that is being verified.
|
||||
*
|
||||
* @param port
|
||||
* The port number of the server being verified.
|
||||
*
|
||||
* @param remote_hostkey
|
||||
* The host key of the remote system being verified.
|
||||
*
|
||||
* @param remote_hostkey_len
|
||||
* The length of the remote host key being verified
|
||||
*
|
||||
* @return
|
||||
* The status of the known_hosts check. This will be zero if no entries
|
||||
* are provided or if the match succeeds, negative to indicate internal
|
||||
* libssh2 errors, or positive to indicate failures during host key
|
||||
* checking.
|
||||
*/
|
||||
int guac_common_ssh_verify_host_key(LIBSSH2_SESSION* session, guac_client* client,
|
||||
const char* host_key, const char* hostname, int port, const char* remote_hostkey,
|
||||
const size_t remote_hostkey_len);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -98,7 +98,8 @@ void guac_common_ssh_uninit();
|
||||
* if the connection or authentication were not successful.
|
||||
*/
|
||||
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
||||
const char* hostname, const char* port, guac_common_ssh_user* user, int keepalive);
|
||||
const char* hostname, const char* port, guac_common_ssh_user* user, int keepalive,
|
||||
const char* host_key);
|
||||
|
||||
/**
|
||||
* Disconnects and destroys the given SSH session, freeing all associated
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
|
||||
char* passphrase) {
|
||||
@ -245,3 +246,86 @@ int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
|
||||
|
||||
}
|
||||
|
||||
int guac_common_ssh_verify_host_key(LIBSSH2_SESSION* session, guac_client* client,
|
||||
const char* host_key, const char* hostname, int port, const char* remote_hostkey,
|
||||
const size_t remote_hostkey_len) {
|
||||
|
||||
LIBSSH2_KNOWNHOSTS* ssh_known_hosts = libssh2_knownhost_init(session);
|
||||
int known_hosts = 0;
|
||||
|
||||
/* Add host key provided from settings */
|
||||
if (host_key && strcmp(host_key, "") != 0) {
|
||||
|
||||
known_hosts = libssh2_knownhost_readline(ssh_known_hosts, host_key, strlen(host_key),
|
||||
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||
|
||||
/* readline function returns 0 on success, so we increment to indicate a valid entry */
|
||||
if (known_hosts == 0)
|
||||
known_hosts++;
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise, we look for a ssh_known_hosts file within GUACAMOLE_HOME and read that in. */
|
||||
else {
|
||||
|
||||
const char *guac_known_hosts = "/etc/guacamole/ssh_known_hosts";
|
||||
if (access(guac_known_hosts, F_OK) != -1)
|
||||
known_hosts = libssh2_knownhost_readfile(ssh_known_hosts, guac_known_hosts, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||
|
||||
}
|
||||
|
||||
/* If there's an error provided, abort connection and return that. */
|
||||
if (known_hosts < 0) {
|
||||
|
||||
char* errmsg;
|
||||
int errval = libssh2_session_last_error(session, &errmsg, NULL, 0);
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
"Error %d trying to load SSH host keys: %s", errval, errmsg);
|
||||
|
||||
libssh2_knownhost_free(ssh_known_hosts);
|
||||
return known_hosts;
|
||||
|
||||
}
|
||||
|
||||
/* No host keys were loaded, so we bail out checking and continue the connection. */
|
||||
else if (known_hosts == 0) {
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"No known host keys provided, host identity will not be verified.");
|
||||
libssh2_knownhost_free(ssh_known_hosts);
|
||||
return known_hosts;
|
||||
}
|
||||
|
||||
|
||||
/* Check remote host key against known hosts */
|
||||
int kh_check = libssh2_knownhost_checkp(ssh_known_hosts, hostname, port,
|
||||
remote_hostkey, remote_hostkey_len,
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
|
||||
LIBSSH2_KNOWNHOST_KEYENC_RAW,
|
||||
NULL);
|
||||
|
||||
/* Deal with the return of the host key check */
|
||||
switch (kh_check) {
|
||||
case LIBSSH2_KNOWNHOST_CHECK_MATCH:
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Host key match found for %s", hostname);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
"Host key not found for %s.", hostname);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
"Host key does not match known hosts entry for %s", hostname);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
||||
default:
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
"Host %s could not be checked against known hosts.",
|
||||
hostname);
|
||||
}
|
||||
|
||||
/* Return the check value */
|
||||
libssh2_knownhost_free(ssh_known_hosts);
|
||||
return kh_check;
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -414,7 +415,8 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
||||
}
|
||||
|
||||
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
||||
const char* hostname, const char* port, guac_common_ssh_user* user, int keepalive) {
|
||||
const char* hostname, const char* port, guac_common_ssh_user* user, int keepalive,
|
||||
const char* host_key) {
|
||||
|
||||
int retval;
|
||||
|
||||
@ -518,6 +520,42 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get host key of remote system we're connecting to */
|
||||
size_t remote_hostkey_len;
|
||||
const char *remote_hostkey = libssh2_session_hostkey(session, &remote_hostkey_len, NULL);
|
||||
|
||||
/* Failure to retrieve a host key means we should abort */
|
||||
if (!remote_hostkey) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||
"Failed to get host key for %s", hostname);
|
||||
free(common_session);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* SSH known host key checking. */
|
||||
int known_host_check = guac_common_ssh_verify_host_key(session, client, host_key,
|
||||
hostname, atoi(port), remote_hostkey,
|
||||
remote_hostkey_len);
|
||||
|
||||
/* Abort on any error codes */
|
||||
if (known_host_check != 0) {
|
||||
char* err_msg;
|
||||
libssh2_session_last_error(session, &err_msg, NULL, 0);
|
||||
|
||||
if (known_host_check < 0)
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||
"Error occurred attempting to check host key: %s", err_msg);
|
||||
|
||||
if (known_host_check > 0)
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||
"Host key did not match any provided known host keys. %s", err_msg);
|
||||
|
||||
free(common_session);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Store basic session data */
|
||||
common_session->client = client;
|
||||
common_session->user = user;
|
||||
@ -560,4 +598,3 @@ void guac_common_ssh_destroy_session(guac_common_ssh_session* session) {
|
||||
free(session);
|
||||
|
||||
}
|
||||
|
||||
|
@ -974,7 +974,8 @@ void* guac_rdp_client_thread(void* data) {
|
||||
/* Attempt SSH connection */
|
||||
rdp_client->sftp_session =
|
||||
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
||||
settings->sftp_port, rdp_client->sftp_user, settings->sftp_server_alive_interval);
|
||||
settings->sftp_port, rdp_client->sftp_user, settings->sftp_server_alive_interval,
|
||||
settings->sftp_host_key);
|
||||
|
||||
/* Fail if SSH connection does not succeed */
|
||||
if (rdp_client->sftp_session == NULL) {
|
||||
|
@ -81,6 +81,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
"enable-sftp",
|
||||
"sftp-hostname",
|
||||
"sftp-host-key",
|
||||
"sftp-port",
|
||||
"sftp-username",
|
||||
"sftp-password",
|
||||
@ -355,6 +356,11 @@ enum RDP_ARGS_IDX {
|
||||
*/
|
||||
IDX_SFTP_HOSTNAME,
|
||||
|
||||
/**
|
||||
* The public SSH host key of the SFTP server. Optional.
|
||||
*/
|
||||
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.
|
||||
@ -822,6 +828,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_HOSTNAME, settings->hostname);
|
||||
|
||||
/* The public SSH host key. */
|
||||
settings->sftp_host_key =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_HOST_KEY, NULL);
|
||||
|
||||
/* Port for SFTP connection */
|
||||
settings->sftp_port =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
@ -999,6 +1010,7 @@ void guac_rdp_settings_free(guac_rdp_settings* settings) {
|
||||
/* Free SFTP settings */
|
||||
free(settings->sftp_directory);
|
||||
free(settings->sftp_root_directory);
|
||||
free(settings->sftp_host_key);
|
||||
free(settings->sftp_hostname);
|
||||
free(settings->sftp_passphrase);
|
||||
free(settings->sftp_password);
|
||||
|
@ -342,6 +342,11 @@ typedef struct guac_rdp_settings {
|
||||
*/
|
||||
char* sftp_hostname;
|
||||
|
||||
/**
|
||||
* The public SSH host key.
|
||||
*/
|
||||
char* sftp_host_key;
|
||||
|
||||
/**
|
||||
* The port of the SSH server to connect to for SFTP.
|
||||
*/
|
||||
|
@ -31,6 +31,7 @@
|
||||
/* Client plugin arguments */
|
||||
const char* GUAC_SSH_CLIENT_ARGS[] = {
|
||||
"hostname",
|
||||
"host-key",
|
||||
"port",
|
||||
"username",
|
||||
"password",
|
||||
@ -68,6 +69,11 @@ enum SSH_ARGS_IDX {
|
||||
*/
|
||||
IDX_HOSTNAME,
|
||||
|
||||
/**
|
||||
* The Base64-encoded public SSH host key. Optional.
|
||||
*/
|
||||
IDX_HOST_KEY,
|
||||
|
||||
/**
|
||||
* The port to connect to. Optional.
|
||||
*/
|
||||
@ -247,6 +253,10 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
|
||||
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_HOSTNAME, "");
|
||||
|
||||
settings->host_key =
|
||||
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_HOST_KEY, NULL);
|
||||
|
||||
settings->username =
|
||||
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_USERNAME, NULL);
|
||||
@ -384,6 +394,7 @@ void guac_ssh_settings_free(guac_ssh_settings* settings) {
|
||||
|
||||
/* Free network connection information */
|
||||
free(settings->hostname);
|
||||
free(settings->host_key);
|
||||
free(settings->port);
|
||||
|
||||
/* Free credentials */
|
||||
@ -417,4 +428,3 @@ void guac_ssh_settings_free(guac_ssh_settings* settings) {
|
||||
free(settings);
|
||||
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,11 @@ typedef struct guac_ssh_settings {
|
||||
*/
|
||||
char* hostname;
|
||||
|
||||
/**
|
||||
* The public SSH host key.
|
||||
*/
|
||||
char* host_key;
|
||||
|
||||
/**
|
||||
* The port of the SSH server to connect to.
|
||||
*/
|
||||
|
@ -235,7 +235,8 @@ void* ssh_client_thread(void* data) {
|
||||
|
||||
/* Open SSH session */
|
||||
ssh_client->session = guac_common_ssh_create_session(client,
|
||||
settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval);
|
||||
settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval,
|
||||
settings->host_key);
|
||||
if (ssh_client->session == NULL) {
|
||||
/* Already aborted within guac_common_ssh_create_session() */
|
||||
return NULL;
|
||||
@ -275,7 +276,8 @@ void* ssh_client_thread(void* data) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
|
||||
ssh_client->sftp_session =
|
||||
guac_common_ssh_create_session(client, settings->hostname,
|
||||
settings->port, ssh_client->user, settings->server_alive_interval);
|
||||
settings->port, ssh_client->user, settings->server_alive_interval,
|
||||
settings->host_key);
|
||||
if (ssh_client->sftp_session == NULL) {
|
||||
/* Already aborted within guac_common_ssh_create_session() */
|
||||
return NULL;
|
||||
|
@ -60,6 +60,7 @@ const char* GUAC_VNC_CLIENT_ARGS[] = {
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
"enable-sftp",
|
||||
"sftp-hostname",
|
||||
"sftp-host-key",
|
||||
"sftp-port",
|
||||
"sftp-username",
|
||||
"sftp-password",
|
||||
@ -208,6 +209,11 @@ enum VNC_ARGS_IDX {
|
||||
*/
|
||||
IDX_SFTP_USERNAME,
|
||||
|
||||
/**
|
||||
* The public SSH host key to identify the SFTP server.
|
||||
*/
|
||||
IDX_SFTP_HOST_KEY,
|
||||
|
||||
/**
|
||||
* The password to provide when authenticating with the SSH server for
|
||||
* SFTP (if not using a private key).
|
||||
@ -411,6 +417,11 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user,
|
||||
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_HOSTNAME, settings->hostname);
|
||||
|
||||
/* The public SSH host key. */
|
||||
settings->sftp_host_key =
|
||||
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_HOST_KEY, NULL);
|
||||
|
||||
/* Port for SFTP connection */
|
||||
settings->sftp_port =
|
||||
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
|
||||
@ -504,6 +515,7 @@ void guac_vnc_settings_free(guac_vnc_settings* settings) {
|
||||
/* Free SFTP settings */
|
||||
free(settings->sftp_directory);
|
||||
free(settings->sftp_root_directory);
|
||||
free(settings->sftp_host_key);
|
||||
free(settings->sftp_hostname);
|
||||
free(settings->sftp_passphrase);
|
||||
free(settings->sftp_password);
|
||||
|
@ -138,6 +138,11 @@ typedef struct guac_vnc_settings {
|
||||
*/
|
||||
char* sftp_hostname;
|
||||
|
||||
/**
|
||||
* The public SSH host key.
|
||||
*/
|
||||
char* sftp_host_key;
|
||||
|
||||
/**
|
||||
* The port of the SSH server to connect to for SFTP.
|
||||
*/
|
||||
|
@ -261,7 +261,8 @@ void* guac_vnc_client_thread(void* data) {
|
||||
/* Attempt SSH connection */
|
||||
vnc_client->sftp_session =
|
||||
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
||||
settings->sftp_port, vnc_client->sftp_user, settings->sftp_server_alive_interval);
|
||||
settings->sftp_port, vnc_client->sftp_user, settings->sftp_server_alive_interval,
|
||||
settings->sftp_host_key);
|
||||
|
||||
/* Fail if SSH connection does not succeed */
|
||||
if (vnc_client->sftp_session == NULL) {
|
||||
|
Loading…
Reference in New Issue
Block a user