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");