GUACAMOLE-527: Move host key checking to a separate function.
This commit is contained in:
parent
ac2b4f8d12
commit
428243bb78
@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <libssh2.h>
|
||||||
|
|
||||||
#include <openssl/ossl_typ.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 guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
|
||||||
int length, unsigned char* sig);
|
int length, unsigned char* sig);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the fingerprint 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 fingerprint 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 fingerprint
|
||||||
|
* The fingering of the server being verified.
|
||||||
|
*
|
||||||
|
* @param fp_len
|
||||||
|
* The length of the fingerprint 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* fingerprint,
|
||||||
|
const size_t fp_len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -245,3 +245,81 @@ 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* fingerprint,
|
||||||
|
const size_t fp_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";
|
||||||
|
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) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_ERROR,
|
||||||
|
"Failure trying to load SSH host keys.");
|
||||||
|
|
||||||
|
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) {
|
||||||
|
libssh2_knownhost_free(ssh_known_hosts);
|
||||||
|
return known_hosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check fingerprint against known hosts */
|
||||||
|
int kh_check = libssh2_knownhost_checkp(ssh_known_hosts, hostname, port,
|
||||||
|
fingerprint, fp_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;
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -520,69 +520,38 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SSH known host key checking. */
|
|
||||||
LIBSSH2_KNOWNHOSTS *ssh_known_hosts = libssh2_knownhost_init(session);
|
|
||||||
int num_known_hosts = 0;
|
|
||||||
|
|
||||||
/* Add host key provided from settings */
|
|
||||||
if (host_key && strcmp(host_key, "") != 0) {
|
|
||||||
|
|
||||||
int kh_add = libssh2_knownhost_readline(ssh_known_hosts, host_key, strlen(host_key),
|
|
||||||
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
|
||||||
num_known_hosts++;
|
|
||||||
|
|
||||||
if (kh_add)
|
|
||||||
guac_client_log(client, GUAC_LOG_WARNING, "Failed to add provided host key"
|
|
||||||
" to known hosts store for %s. Error was %d", hostname, kh_add);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, we look for a ssh_known_hosts file within GUACAMOLE_HOME and read that in. */
|
|
||||||
else {
|
|
||||||
const char *known_hosts = "/etc/guacamole/ssh_known_hosts";
|
|
||||||
num_known_hosts = libssh2_knownhost_readfile(ssh_known_hosts, known_hosts, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we've found a provided set of host keys, check against them. */
|
|
||||||
if (num_known_hosts > 0) {
|
|
||||||
/* Get fingerprint of host we're connecting to */
|
/* Get fingerprint of host we're connecting to */
|
||||||
size_t fp_len;
|
size_t fp_len;
|
||||||
int fp_type;
|
int fp_type;
|
||||||
const char *fingerprint = libssh2_session_hostkey(session, &fp_len, &fp_type);
|
const char *fingerprint = libssh2_session_hostkey(session, &fp_len, &fp_type);
|
||||||
|
|
||||||
if (!fingerprint)
|
/* Failure to generate a fingerprint means we should abort */
|
||||||
|
if (!fingerprint) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||||
"Failed to get fingerprint for host %s", hostname);
|
"Failed to get fingerprint for host %s", hostname);
|
||||||
|
return NULL;
|
||||||
/* Check fingerprint against known hosts */
|
|
||||||
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);
|
|
||||||
|
|
||||||
libssh2_knownhost_free(ssh_known_hosts);
|
|
||||||
|
|
||||||
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_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
|
||||||
"Host key not found for %s.", hostname);
|
|
||||||
break;
|
|
||||||
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
|
||||||
"Host key does not match known hosts entry for %s", hostname);
|
|
||||||
break;
|
|
||||||
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
|
||||||
default:
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
|
||||||
"Host %s could not be checked against known hosts.",
|
|
||||||
hostname);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SSH known host key checking. */
|
||||||
|
int known_host_check = guac_common_ssh_verify_host_key(session, client, host_key,
|
||||||
|
hostname, atoi(port), fingerprint,
|
||||||
|
fp_len);
|
||||||
|
|
||||||
|
/* Abort on any error codes */
|
||||||
|
if (known_host_check != 0) {
|
||||||
|
char* err_msg;
|
||||||
|
int err_len;
|
||||||
|
libssh2_session_last_error(session, &err_msg, &err_len, 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 fingerprint did not match any provided known host keys: %s", err_msg);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store basic session data */
|
/* Store basic session data */
|
||||||
|
Loading…
Reference in New Issue
Block a user