You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/09/22 00:27:30 UTC

[6/8] guacamole-server git commit: GUACAMOLE-622: Start terminal for telnet only after login status is known (if login success/failure detection enabled).

GUACAMOLE-622: Start terminal for telnet only after login status is known (if login success/failure detection enabled).


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

Branch: refs/heads/master
Commit: 442b1d5cc2a460a9f6d4797e9b1428c181aee2c4
Parents: 1178b47
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Sep 2 14:32:05 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Sep 21 14:29:01 2018 -0700

----------------------------------------------------------------------
 src/protocols/telnet/settings.c |  87 ++++++++++++++++++++++++------
 src/protocols/telnet/settings.h |  24 +++++++++
 src/protocols/telnet/telnet.c   | 102 ++++++++++++++++++++++++++++-------
 3 files changed, 178 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/442b1d5c/src/protocols/telnet/settings.c
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/settings.c b/src/protocols/telnet/settings.c
index bef5cfa..890d5fe 100644
--- a/src/protocols/telnet/settings.c
+++ b/src/protocols/telnet/settings.c
@@ -53,6 +53,8 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
     "backspace",
     "terminal-type",
     "scrollback",
+    "login-success-regex",
+    "login-failure-regex",
     NULL
 };
 
@@ -196,12 +198,31 @@ enum TELNET_ARGS_IDX {
      */
     IDX_SCROLLBACK,
 
+    /**
+     * The regular expression to use when searching for whether login was
+     * successful. This parameter is optional. If given, the
+     * "login-failure-regex" parameter must also be specified, and the first
+     * frame of the Guacamole connection will be withheld until login
+     * success/failure has been determined.
+     */
+    IDX_LOGIN_SUCCESS_REGEX,
+
+    /**
+     * The regular expression to use when searching for whether login was
+     * unsuccessful. This parameter is optional. If given, the
+     * "login-success-regex" parameter must also be specified, and the first
+     * frame of the Guacamole connection will be withheld until login
+     * success/failure has been determined.
+     */
+    IDX_LOGIN_FAILURE_REGEX,
+
     TELNET_ARGS_COUNT
 };
 
 /**
- * Compiles the given regular expression, returning NULL if compilation fails.
- * The returned regex_t must be freed with regfree() AND free().
+ * Compiles the given regular expression, returning NULL if compilation fails
+ * or of the given regular expression is NULL. The returned regex_t must be
+ * freed with regfree() AND free(), or with guac_telnet_regex_free().
  *
  * @param user
  *     The user who provided the setting associated with the given regex
@@ -211,10 +232,15 @@ enum TELNET_ARGS_IDX {
  *     The regular expression pattern to compile.
  *
  * @return
- *     The compiled regular expression, or NULL if compilation fails.
+ *     The compiled regular expression, or NULL if compilation fails or NULL
+ *     was originally provided for the pattern.
  */
 static regex_t* guac_telnet_compile_regex(guac_user* user, char* pattern) {
 
+    /* Nothing to compile if no pattern provided */
+    if (pattern == NULL)
+        return NULL;
+
     int compile_result;
     regex_t* regex = malloc(sizeof(regex_t));
 
@@ -233,6 +259,14 @@ static regex_t* guac_telnet_compile_regex(guac_user* user, char* pattern) {
     return regex;
 }
 
+void guac_telnet_regex_free(regex_t** regex) {
+    if (*regex != NULL) {
+        regfree(*regex);
+        free(*regex);
+        *regex = NULL;
+    }
+}
+
 guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
         int argc, const char** argv) {
 
@@ -256,7 +290,7 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
         guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
                 IDX_USERNAME, NULL);
 
-    /* Read username regex only if password is specified */
+    /* Read username regex only if username is specified */
     if (settings->username != NULL) {
         settings->username_regex = guac_telnet_compile_regex(user,
             guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
@@ -275,6 +309,35 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
                     IDX_PASSWORD_REGEX, GUAC_TELNET_DEFAULT_PASSWORD_REGEX));
     }
 
+    /* Read optional login success detection regex */
+    settings->login_success_regex = guac_telnet_compile_regex(user,
+            guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
+                    IDX_LOGIN_SUCCESS_REGEX, NULL));
+
+    /* Read optional login failure detection regex */
+    settings->login_failure_regex = guac_telnet_compile_regex(user,
+            guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
+                    IDX_LOGIN_FAILURE_REGEX, NULL));
+
+    /* Both login success and login failure regexes must be provided if either
+     * is present at all */
+    if (settings->login_success_regex != NULL
+            && settings->login_failure_regex == NULL) {
+        guac_telnet_regex_free(&settings->login_success_regex);
+        guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
+                "\"%s\" as \"%s\" must also be provided.",
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX],
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX]);
+    }
+    else if (settings->login_failure_regex != NULL
+            && settings->login_success_regex == NULL) {
+        guac_telnet_regex_free(&settings->login_failure_regex);
+        guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
+                "\"%s\" as \"%s\" must also be provided.",
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX],
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX]);
+    }
+
     /* Read-only mode */
     settings->read_only =
         guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
@@ -380,17 +443,11 @@ void guac_telnet_settings_free(guac_telnet_settings* settings) {
     free(settings->username);
     free(settings->password);
 
-    /* Free username regex (if allocated) */
-    if (settings->username_regex != NULL) {
-        regfree(settings->username_regex);
-        free(settings->username_regex);
-    }
-
-    /* Free password regex (if allocated) */
-    if (settings->password_regex != NULL) {
-        regfree(settings->password_regex);
-        free(settings->password_regex);
-    }
+    /* Free various regexes */
+    guac_telnet_regex_free(&settings->username_regex);
+    guac_telnet_regex_free(&settings->password_regex);
+    guac_telnet_regex_free(&settings->login_success_regex);
+    guac_telnet_regex_free(&settings->login_failure_regex);
 
     /* Free display preferences */
     free(settings->font_name);

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/442b1d5c/src/protocols/telnet/settings.h
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/settings.h b/src/protocols/telnet/settings.h
index 83ec431..86302b7 100644
--- a/src/protocols/telnet/settings.h
+++ b/src/protocols/telnet/settings.h
@@ -118,6 +118,20 @@ typedef struct guac_telnet_settings {
     regex_t* password_regex;
 
     /**
+     * The regular expression to use when searching for whether login was
+     * successful. If no such regex is specified, or if no login failure regex
+     * was specified, this will be NULL.
+     */
+    regex_t* login_success_regex;
+
+    /**
+     * The regular expression to use when searching for whether login failed.
+     * If no such regex is specified, or if no login success regex was
+     * specified, this will be NULL.
+     */
+    regex_t* login_failure_regex;
+
+    /**
      * Whether this connection is read-only, and user input should be dropped.
      */
     bool read_only;
@@ -254,6 +268,16 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
         int argc, const char** argv);
 
 /**
+ * Frees the regex pointed to by the given pointer, assigning the value NULL to
+ * that pointer once the regex is freed. If the pointer already contains NULL,
+ * this function has no effect.
+ *
+ * @param regex
+ *     The address of the pointer to the regex that should be freed.
+ */
+void guac_telnet_regex_free(regex_t** regex);
+
+/**
  * Frees the given guac_telnet_settings object, having been previously
  * allocated via guac_telnet_parse_args().
  *

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/442b1d5c/src/protocols/telnet/telnet.c
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c
index c9a636c..36e4cac 100644
--- a/src/protocols/telnet/telnet.c
+++ b/src/protocols/telnet/telnet.c
@@ -31,6 +31,7 @@
 #include <netinet/in.h>
 #include <poll.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -84,10 +85,32 @@ static int __guac_telnet_write_all(int fd, const char* buffer, int size) {
 /**
  * Searches for a line matching the stored password regex, appending the given
  * buffer to the internal pattern matching buffer. The internal pattern match
- * buffer is cleared whenever a newline is read. Returns TRUE if a match is found and the
- * value is sent.
+ * buffer is cleared whenever a newline is read. Returns true if a match is
+ * found and the value is sent. An enter keypress is automatically sent after
+ * the value is sent.
+ *
+ * @param client
+ *     The guac_client associated with the telnet session.
+ *
+ * @param regex
+ *     The regex to search for within the output of the telnet session
+ *     associated with the given client.
+ *
+ * @param value
+ *     The string value to send once a match is found, or NULL if no value
+ *     should be sent.
+ *
+ * @param buffer
+ *     The buffer of received data to search through.
+ *
+ * @param size
+ *     The size of the given buffer, in bytes.
+ *
+ * @return
+ *     true if a match is found, false otherwise.
  */
-static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex, char* value, const char* buffer, int size) {
+static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex,
+        char* value, const char* buffer, int size) {
 
     static char line_buffer[1024] = {0};
     static int length = 0;
@@ -123,16 +146,17 @@ static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex, char
     if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
 
         /* Send value */
-        guac_terminal_send_string(telnet_client->term, value);
-        guac_terminal_send_key(telnet_client->term, 0xFF0D, 1);
-        guac_terminal_send_key(telnet_client->term, 0xFF0D, 0);
+        if (value != NULL) {
+            guac_terminal_send_string(telnet_client->term, value);
+            guac_terminal_send_string(telnet_client->term, "\x0D");
+        }
 
         /* Stop searching for prompt */
-        return TRUE;
+        return true;
 
     }
 
-    return FALSE;
+    return false;
 }
 
 /**
@@ -158,9 +182,7 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event,
                             settings->username_regex, settings->username,
                             event->data.buffer, event->data.size)) {
                     guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
-                    regfree(settings->username_regex);
-                    free(settings->username_regex);
-                    settings->username_regex = NULL;
+                    guac_telnet_regex_free(&settings->username_regex);
                 }
             }
 
@@ -172,18 +194,52 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event,
 
                     guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
 
-                    /* Do not continue searching for username once password is sent */
-                    if (settings->username_regex != NULL) {
-                        regfree(settings->username_regex);
-                        free(settings->username_regex);
-                        settings->username_regex = NULL;
-                    }
+                    /* Do not continue searching for username/password once password is sent */
+                    guac_telnet_regex_free(&settings->username_regex);
+                    guac_telnet_regex_free(&settings->password_regex);
 
-                    regfree(settings->password_regex);
-                    free(settings->password_regex);
-                    settings->password_regex = NULL;
                 }
             }
+
+            /* Continue search for login success */
+            if (settings->login_success_regex != NULL) {
+                if (__guac_telnet_regex_search(client,
+                            settings->login_success_regex, NULL,
+                            event->data.buffer, event->data.size)) {
+
+                    /* Allow terminal to render now that login has been deemed successful */
+                    guac_client_log(client, GUAC_LOG_DEBUG, "Login successful");
+                    guac_terminal_start(telnet_client->term);
+
+                    /* Stop all searches */
+                    guac_telnet_regex_free(&settings->username_regex);
+                    guac_telnet_regex_free(&settings->password_regex);
+                    guac_telnet_regex_free(&settings->login_success_regex);
+                    guac_telnet_regex_free(&settings->login_failure_regex);
+
+                }
+            }
+
+            /* Continue search for login failure */
+            if (settings->login_failure_regex != NULL) {
+                if (__guac_telnet_regex_search(client,
+                            settings->login_failure_regex, NULL,
+                            event->data.buffer, event->data.size)) {
+
+                    /* Advise that login has failed and connection should be closed */
+                    guac_client_abort(client,
+                            GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                            "Login failed");
+
+                    /* Stop all searches */
+                    guac_telnet_regex_free(&settings->username_regex);
+                    guac_telnet_regex_free(&settings->password_regex);
+                    guac_telnet_regex_free(&settings->login_success_regex);
+                    guac_telnet_regex_free(&settings->login_failure_regex);
+
+                }
+            }
+
             break;
 
         /* Data destined for remote end */
@@ -508,6 +564,12 @@ void* guac_telnet_client_thread(void* data) {
     /* Logged in */
     guac_client_log(client, GUAC_LOG_INFO, "Telnet connection successful.");
 
+    /* Allow terminal to render if login success/failure detection is not
+     * enabled */
+    if (settings->login_success_regex == NULL
+            && settings->login_failure_regex == NULL)
+        guac_terminal_start(telnet_client->term);
+
     /* Start input thread */
     if (pthread_create(&(input_thread), NULL, __guac_telnet_input_thread, (void*) client)) {
         guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");