From 0d82cd1e6c78606a6b4d65b6d2441b02479fda88 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Thu, 5 Apr 2018 07:36:37 -0400 Subject: [PATCH] GUACAMOLE-527: Add host key and type settings. --- src/common-ssh/common-ssh/ssh.h | 3 +- src/common-ssh/ssh.c | 85 +++++++++++++++++++++------------ src/protocols/ssh/settings.c | 22 +++++++++ src/protocols/ssh/settings.h | 10 ++++ 4 files changed, 89 insertions(+), 31 deletions(-) diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 8026549d..e25b6263 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -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_type, const char* host_key); /** * Disconnects and destroys the given SSH session, freeing all associated diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index 5099de25..a1a62f60 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -415,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_type, const char* host_key) { int retval; @@ -511,44 +512,68 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, } /* Check known_hosts */ - size_t len; - int type; + /* Get known hosts file from user running guacd */ struct passwd *pw = getpwuid(getuid()); char *homedir = pw->pw_dir; char *known_hosts = strcat(homedir, "/.ssh/known_hosts"); LIBSSH2_KNOWNHOSTS *ssh_known_hosts = libssh2_knownhost_init(session); - libssh2_knownhost_readfile(ssh_known_hosts, known_hosts, LIBSSH2_KNOWNHOST_FILE_OPENSSH); - const char *fingerprint = libssh2_session_hostkey(session, &len, &type); - if (fingerprint) { - struct libssh2_knownhost *host; - int check = libssh2_knownhost_checkp(ssh_known_hosts, hostname, atoi(port), - fingerprint, len, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW, - &host); + /* Add host key provided from settings */ + if (strcmp(host_key, "") > 0) { - switch (check) { - case LIBSSH2_KNOWNHOST_CHECK_MATCH: - guac_client_log(client, GUAC_LOG_DEBUG, - "Host key match found."); - break; - case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Host key not found in known hosts file."); - break; - case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Host key does not match host entry."); - break; - case LIBSSH2_KNOWNHOST_CHECK_FAILURE: - default: - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Host could not be checked against known hosts."); - } + int kh_key_type = 0; + if (strcmp(host_key_type, "ssh-rsa") == 0) + kh_key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + else if(strcmp(host_key_type, "ssh-dss") == 0) + kh_key_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + else if(strcmp(host_key_type, "rsa1") == 0) + kh_key_type = LIBSSH2_KNOWNHOST_KEY_RSA1; + else + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Invalid SSH host key type %s", host_key_type); + if (libssh2_knownhost_addc(ssh_known_hosts, hostname, NULL, host_key, strlen(host_key), + NULL, 0, LIBSSH2_KNOWNHOST_TYPE_PLAIN|LIBSSH2_KNOWNHOST_KEYENC_BASE64| + kh_key_type, NULL)) + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Failed to add host key to known hosts store for %s", hostname); + } + /* Get fingerprint of host we're connecting to */ + size_t fp_len; + int fp_type; + const char *fingerprint = libssh2_session_hostkey(session, &fp_len, &fp_type); + + if (!fingerprint || strcmp(fingerprint, "") == 0) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Could not retrieve fingerprint of SSH server %s", hostname); + } + + struct libssh2_knownhost *host; + int kh_check = libssh2_knownhost_checkp(ssh_known_hosts, hostname, atoi(port), + fingerprint, fp_len, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW, + &host); + + switch (kh_check) { + case LIBSSH2_KNOWNHOST_CHECK_MATCH: + guac_client_log(client, GUAC_LOG_DEBUG, + "Host key match found."); + break; + case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Host key not found in known hosts file."); + break; + case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Host key does not match host entry."); + break; + case LIBSSH2_KNOWNHOST_CHECK_FAILURE: + default: + guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Host could not be checked against known hosts."); } /* Perform handshake */ diff --git a/src/protocols/ssh/settings.c b/src/protocols/ssh/settings.c index 2ce97903..bc650e99 100644 --- a/src/protocols/ssh/settings.c +++ b/src/protocols/ssh/settings.c @@ -31,6 +31,8 @@ /* Client plugin arguments */ const char* GUAC_SSH_CLIENT_ARGS[] = { "hostname", + "host_key_type", + "host_key", "port", "username", "password", @@ -68,6 +70,16 @@ enum SSH_ARGS_IDX { */ IDX_HOSTNAME, + /** + * The type of public SSH host key provided. Optional. + */ + IDX_HOST_KEY_TYPE, + + /** + * The Base64-encoded public SSH host key. Optional. + */ + IDX_HOST_KEY, + /** * The port to connect to. Optional. */ @@ -247,6 +259,14 @@ 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_type = + guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, + IDX_HOST_KEY_TYPE, "ssh-rsa"); + + 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 +404,8 @@ void guac_ssh_settings_free(guac_ssh_settings* settings) { /* Free network connection information */ free(settings->hostname); + free(settings->host_key_type); + free(settings->host_key); free(settings->port); /* Free credentials */ diff --git a/src/protocols/ssh/settings.h b/src/protocols/ssh/settings.h index 393cfc0c..ac8400ec 100644 --- a/src/protocols/ssh/settings.h +++ b/src/protocols/ssh/settings.h @@ -70,6 +70,16 @@ typedef struct guac_ssh_settings { */ char* hostname; + /** + * The type of public SSH host key. + */ + char* host_key_type; + + /** + * The public SSH host key. + */ + char* host_key; + /** * The port of the SSH server to connect to. */