GUACAMOLE-622: Start terminal for telnet only after login status is known (if login success/failure detection enabled).
This commit is contained in:
parent
1178b475da
commit
442b1d5cc2
@ -53,6 +53,8 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
|
|||||||
"backspace",
|
"backspace",
|
||||||
"terminal-type",
|
"terminal-type",
|
||||||
"scrollback",
|
"scrollback",
|
||||||
|
"login-success-regex",
|
||||||
|
"login-failure-regex",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -196,12 +198,31 @@ enum TELNET_ARGS_IDX {
|
|||||||
*/
|
*/
|
||||||
IDX_SCROLLBACK,
|
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
|
TELNET_ARGS_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles the given regular expression, returning NULL if compilation fails.
|
* Compiles the given regular expression, returning NULL if compilation fails
|
||||||
* The returned regex_t must be freed with regfree() AND free().
|
* 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
|
* @param user
|
||||||
* The user who provided the setting associated with the given regex
|
* 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.
|
* The regular expression pattern to compile.
|
||||||
*
|
*
|
||||||
* @return
|
* @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) {
|
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;
|
int compile_result;
|
||||||
regex_t* regex = malloc(sizeof(regex_t));
|
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;
|
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,
|
guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
|
||||||
int argc, const char** argv) {
|
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,
|
guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
||||||
IDX_USERNAME, NULL);
|
IDX_USERNAME, NULL);
|
||||||
|
|
||||||
/* Read username regex only if password is specified */
|
/* Read username regex only if username is specified */
|
||||||
if (settings->username != NULL) {
|
if (settings->username != NULL) {
|
||||||
settings->username_regex = guac_telnet_compile_regex(user,
|
settings->username_regex = guac_telnet_compile_regex(user,
|
||||||
guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
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));
|
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 */
|
/* Read-only mode */
|
||||||
settings->read_only =
|
settings->read_only =
|
||||||
guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
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->username);
|
||||||
free(settings->password);
|
free(settings->password);
|
||||||
|
|
||||||
/* Free username regex (if allocated) */
|
/* Free various regexes */
|
||||||
if (settings->username_regex != NULL) {
|
guac_telnet_regex_free(&settings->username_regex);
|
||||||
regfree(settings->username_regex);
|
guac_telnet_regex_free(&settings->password_regex);
|
||||||
free(settings->username_regex);
|
guac_telnet_regex_free(&settings->login_success_regex);
|
||||||
}
|
guac_telnet_regex_free(&settings->login_failure_regex);
|
||||||
|
|
||||||
/* Free password regex (if allocated) */
|
|
||||||
if (settings->password_regex != NULL) {
|
|
||||||
regfree(settings->password_regex);
|
|
||||||
free(settings->password_regex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free display preferences */
|
/* Free display preferences */
|
||||||
free(settings->font_name);
|
free(settings->font_name);
|
||||||
|
@ -117,6 +117,20 @@ typedef struct guac_telnet_settings {
|
|||||||
*/
|
*/
|
||||||
regex_t* password_regex;
|
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.
|
* Whether this connection is read-only, and user input should be dropped.
|
||||||
*/
|
*/
|
||||||
@ -253,6 +267,16 @@ typedef struct guac_telnet_settings {
|
|||||||
guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
|
guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
|
||||||
int argc, const char** argv);
|
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
|
* Frees the given guac_telnet_settings object, having been previously
|
||||||
* allocated via guac_telnet_parse_args().
|
* allocated via guac_telnet_parse_args().
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.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
|
* Searches for a line matching the stored password regex, appending the given
|
||||||
* buffer to the internal pattern matching buffer. The internal pattern match
|
* 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
|
* buffer is cleared whenever a newline is read. Returns true if a match is
|
||||||
* value is sent.
|
* 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 char line_buffer[1024] = {0};
|
||||||
static int length = 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) {
|
if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
|
||||||
|
|
||||||
/* Send value */
|
/* Send value */
|
||||||
|
if (value != NULL) {
|
||||||
guac_terminal_send_string(telnet_client->term, value);
|
guac_terminal_send_string(telnet_client->term, value);
|
||||||
guac_terminal_send_key(telnet_client->term, 0xFF0D, 1);
|
guac_terminal_send_string(telnet_client->term, "\x0D");
|
||||||
guac_terminal_send_key(telnet_client->term, 0xFF0D, 0);
|
}
|
||||||
|
|
||||||
/* Stop searching for prompt */
|
/* 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,
|
settings->username_regex, settings->username,
|
||||||
event->data.buffer, event->data.size)) {
|
event->data.buffer, event->data.size)) {
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
|
guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
|
||||||
regfree(settings->username_regex);
|
guac_telnet_regex_free(&settings->username_regex);
|
||||||
free(settings->username_regex);
|
|
||||||
settings->username_regex = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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");
|
guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
|
||||||
|
|
||||||
/* Do not continue searching for username once password is sent */
|
/* Do not continue searching for username/password once password is sent */
|
||||||
if (settings->username_regex != NULL) {
|
guac_telnet_regex_free(&settings->username_regex);
|
||||||
regfree(settings->username_regex);
|
guac_telnet_regex_free(&settings->password_regex);
|
||||||
free(settings->username_regex);
|
|
||||||
settings->username_regex = NULL;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
regfree(settings->password_regex);
|
/* Continue search for login success */
|
||||||
free(settings->password_regex);
|
if (settings->login_success_regex != NULL) {
|
||||||
settings->password_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;
|
break;
|
||||||
|
|
||||||
/* Data destined for remote end */
|
/* Data destined for remote end */
|
||||||
@ -508,6 +564,12 @@ void* guac_telnet_client_thread(void* data) {
|
|||||||
/* Logged in */
|
/* Logged in */
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Telnet connection successful.");
|
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 */
|
/* Start input thread */
|
||||||
if (pthread_create(&(input_thread), NULL, __guac_telnet_input_thread, (void*) client)) {
|
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");
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");
|
||||||
|
Loading…
Reference in New Issue
Block a user