GUACAMOLE-527: Move host key checking to a separate function.

Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/428243bb
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/428243bb
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/428243bb

Branch: refs/heads/staging/1.0.0
Commit: 428243bb783ac58ecf228a5704b5709c26d9239b
Parents: ac2b4f8
Author: Nick Couchman <vn...@apache.org>
Authored: Mon May 28 10:12:07 2018 -0400
Committer: Nick Couchman <nick_couch...@cotyinc.com>
Committed: Mon Jun 25 08:31:37 2018 -0400

----------------------------------------------------------------------
 src/common-ssh/common-ssh/key.h | 50 ++++++++++++++++++++++
 src/common-ssh/key.c            | 78 +++++++++++++++++++++++++++++++++
 src/common-ssh/ssh.c            | 83 +++++++++++-------------------------
 3 files changed, 154 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/428243bb/src/common-ssh/common-ssh/key.h
----------------------------------------------------------------------
diff --git a/src/common-ssh/common-ssh/key.h b/src/common-ssh/common-ssh/key.h
index 1754dea..576ba1b 100644
--- a/src/common-ssh/common-ssh/key.h
+++ b/src/common-ssh/common-ssh/key.h
@@ -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 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
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/428243bb/src/common-ssh/key.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/key.c b/src/common-ssh/key.c
index a05d696..570da8e 100644
--- a/src/common-ssh/key.c
+++ b/src/common-ssh/key.c
@@ -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;
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/428243bb/src/common-ssh/ssh.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c
index 4bd3a3d..efdef83 100644
--- a/src/common-ssh/ssh.c
+++ b/src/common-ssh/ssh.c
@@ -520,69 +520,38 @@ guac_common_ssh_session* 
guac_common_ssh_create_session(guac_client* client,
         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);
+    /* 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);
 
+    /* Failure to generate a fingerprint means we should abort */
+    if (!fingerprint) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+            "Failed to get fingerprint for host %s", hostname);
+        return NULL;
     }
 
-    /* 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);
-    }
+    /* 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);
 
-    /* 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 */
-        size_t fp_len;
-        int fp_type;
-        const char *fingerprint = libssh2_session_hostkey(session, &fp_len, 
&fp_type);
+    /* 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 (!fingerprint)
+        if (known_host_check < 0)
             guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
-                    "Failed to get fingerprint for host %s", hostname);
-
-        /* 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);
-        }
+                "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 */

Reply via email to