diff --git a/src/protocols/telnet/client.c b/src/protocols/telnet/client.c index b3fa9f63..4a9d9f3a 100644 --- a/src/protocols/telnet/client.c +++ b/src/protocols/telnet/client.c @@ -48,6 +48,7 @@ const char* GUAC_CLIENT_ARGS[] = { "hostname", "port", "username", + "username-regex", "password", "password-regex", "font-name", @@ -72,6 +73,12 @@ enum __TELNET_ARGS_IDX { */ IDX_USERNAME, + /** + * The regular expression to use when searching for the username/login prompt. + * Optional + */ + IDX_USERNAME_REGEX, + /** * The password to use when logging in. Optional. */ @@ -96,6 +103,26 @@ enum __TELNET_ARGS_IDX { TELNET_ARGS_COUNT }; +/** + * Compile a regular expression and checks it return value. Returns NULL if compilation fails + */ +static regex_t* __guac_telnet_compile_regext(guac_client* client, char* pattern) { + int compile_result; + regex_t* regex = malloc(sizeof(regex_t)); + + /* Compile regular expression */ + compile_result = regcomp(regex, pattern,REG_EXTENDED | REG_NOSUB | REG_ICASE | REG_NEWLINE); + + /* Notify of failure to parse/compile */ + if (compile_result != 0) { + guac_client_log_info(client, "Regular expression '%s' could not be compiled.", pattern); + free(regex); + return NULL; + } + + return regex; +} + int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; @@ -124,29 +151,25 @@ int guac_client_init(guac_client* client, int argc, char** argv) { strcpy(client_data->username, argv[IDX_USERNAME]); strcpy(client_data->password, argv[IDX_PASSWORD]); - /* Set password regex, if needed */ - if (client_data->password[0] != 0) { - - int compile_result; - - client_data->password_regex = malloc(sizeof(regex_t)); + /* Set username regex, if needed */ + if (client_data->username[0] != 0) { /* Compile regular expression */ - if (argv[IDX_PASSWORD_REGEX][0] != 0) - compile_result = regcomp(client_data->password_regex, argv[IDX_PASSWORD_REGEX], - REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + if (argv[IDX_USERNAME_REGEX][0] != 0) + client_data->username_regex = __guac_telnet_compile_regext(client, argv[IDX_USERNAME_REGEX]); else - compile_result = regcomp(client_data->password_regex, GUAC_TELNET_DEFAULT_REGEX, - REG_EXTENDED | REG_NOSUB | REG_NEWLINE); - - /* Notify of failure to parse/compile */ - if (compile_result != 0) { - guac_client_log_info(client, "Password regular expression could not be compiled. " - "Password must be entered manually."); - free(client_data->password_regex); - client_data->password_regex = NULL; - } + client_data->username_regex = __guac_telnet_compile_regext(client, GUAC_TELNET_DEFAULT_USERNAME_REGEX); + } + else + client_data->username_regex = NULL; + /* Set password regex, if needed */ + if (client_data->password[0] != 0) { + /* Compile regular expression */ + if (argv[IDX_PASSWORD_REGEX][0] != 0) + client_data->password_regex = __guac_telnet_compile_regext(client, argv[IDX_PASSWORD_REGEX]); + else + client_data->password_regex = __guac_telnet_compile_regext(client, GUAC_TELNET_DEFAULT_PASSWORD_REGEX); } else client_data->password_regex = NULL; diff --git a/src/protocols/telnet/client.h b/src/protocols/telnet/client.h index 489d70f4..d2364904 100644 --- a/src/protocols/telnet/client.h +++ b/src/protocols/telnet/client.h @@ -32,7 +32,8 @@ #include -#define GUAC_TELNET_DEFAULT_REGEX "^Password:" +#define GUAC_TELNET_DEFAULT_USERNAME_REGEX "login:" +#define GUAC_TELNET_DEFAULT_PASSWORD_REGEX "password:" /** * Telnet-specific client data. @@ -54,6 +55,13 @@ typedef struct guac_telnet_client_data { */ char username[1024]; + /** + * The regular expression to use when searching for the username + * prompt. This will be NULL unless the telnet client is currently + * searching for the username prompt. + */ + regex_t* username_regex; + /** * The password to give when authenticating. */ diff --git a/src/protocols/telnet/guac_handlers.c b/src/protocols/telnet/guac_handlers.c index 230a618f..59b91ab3 100644 --- a/src/protocols/telnet/guac_handlers.c +++ b/src/protocols/telnet/guac_handlers.c @@ -71,6 +71,17 @@ int guac_telnet_client_key_handler(guac_client* client, int keysym, int pressed) } + /* Stop searching for username */ + if (client_data->username_regex != NULL) { + + guac_client_log_info(client, "Stopping username prompt search due to user input."); + + regfree(client_data->username_regex); + free(client_data->username_regex); + client_data->username_regex = NULL; + + } + /* Send key */ guac_terminal_send_key(term, keysym, pressed); diff --git a/src/protocols/telnet/telnet_client.c b/src/protocols/telnet/telnet_client.c index b5bd1850..f07ae6b7 100644 --- a/src/protocols/telnet/telnet_client.c +++ b/src/protocols/telnet/telnet_client.c @@ -87,9 +87,10 @@ 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. + * buffer is cleared whenever a newline is read. Returns TRUE if a match is found and the + * value is sent. */ -static void __guac_telnet_password_search(guac_client* client, 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; @@ -110,7 +111,6 @@ static void __guac_telnet_password_search(guac_client* client, const char* buffe size -= i; i = 0; } - } /* Truncate if necessary */ @@ -122,21 +122,26 @@ static void __guac_telnet_password_search(guac_client* client, const char* buffe length += size; line_buffer[length] = '\0'; + /* Remove non-printable characters as they interfere with regex matching */ + for (int i = 0; i < length; i++) { + if (line_buffer[i] <= 31 || (line_buffer[i] >= 128 && line_buffer[i] <= 255)) { + line_buffer[i] = ' '; + } + } + /* Send password upon match */ - if (regexec(client_data->password_regex, line_buffer, 0, NULL, 0) == 0) { + if (regexec(regex, line_buffer, 0, NULL, 0) == 0) { /* Send password */ - guac_terminal_send_string(client_data->term, client_data->password); + guac_terminal_send_string(client_data->term, value); guac_terminal_send_key(client_data->term, 0xFF0D, 1); guac_terminal_send_key(client_data->term, 0xFF0D, 0); /* Stop searching for password */ - regfree(client_data->password_regex); - free(client_data->password_regex); - client_data->password_regex = NULL; - + return TRUE; } + return FALSE; } /** @@ -155,10 +160,25 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event, case TELNET_EV_DATA: guac_terminal_write_stdout(client_data->term, event->data.buffer, event->data.size); - /* Continue search for password prompt */ - if (client_data->password_regex != NULL) - __guac_telnet_password_search(client, event->data.buffer, event->data.size); + /* Continue search for username prompt */ + if (client_data->username_regex != NULL) { + if (__guac_telnet_regex_search(client, client_data->username_regex, client_data->username, + event->data.buffer, event->data.size)) { + regfree(client_data->username_regex); + free(client_data->username_regex); + client_data->username_regex = NULL; + } + } + /* Continue search for password prompt */ + if (client_data->password_regex != NULL) { + if (__guac_telnet_regex_search(client, client_data->password_regex, client_data->password, + event->data.buffer, event->data.size)) { + regfree(client_data->password_regex); + free(client_data->password_regex); + client_data->password_regex = NULL; + } + } break; /* Data destined for remote end */ @@ -196,7 +216,6 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event, /* Environment request */ case TELNET_EV_ENVIRON: - /* Only send USER if entire environment was requested */ if (event->environ.size == 0) guac_telnet_send_user(telnet, client_data->username);