diff --git a/src/protocols/telnet/client.c b/src/protocols/telnet/client.c index 6d4b5e3c..b3fa9f63 100644 --- a/src/protocols/telnet/client.c +++ b/src/protocols/telnet/client.c @@ -30,8 +30,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -46,6 +48,8 @@ const char* GUAC_CLIENT_ARGS[] = { "hostname", "port", "username", + "password", + "password-regex", "font-name", "font-size", NULL @@ -68,6 +72,17 @@ enum __TELNET_ARGS_IDX { */ IDX_USERNAME, + /** + * The password to use when logging in. Optional. + */ + IDX_PASSWORD, + + /** + * The regular expression to use when searching for the password prompt. + * Optional. + */ + IDX_PASSWORD_REGEX, + /** * The name of the font to use within the terminal. */ @@ -107,6 +122,34 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Read parameters */ strcpy(client_data->hostname, argv[IDX_HOSTNAME]); 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)); + + /* 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); + 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; + } + + } + else + client_data->password_regex = NULL; /* Read port */ if (argv[IDX_PORT][0] != 0) diff --git a/src/protocols/telnet/client.h b/src/protocols/telnet/client.h index e850dce2..489d70f4 100644 --- a/src/protocols/telnet/client.h +++ b/src/protocols/telnet/client.h @@ -27,8 +27,13 @@ #include "terminal.h" #include +#include +#include + #include +#define GUAC_TELNET_DEFAULT_REGEX "^Password:" + /** * Telnet-specific client data. */ @@ -49,6 +54,18 @@ typedef struct guac_telnet_client_data { */ char username[1024]; + /** + * The password to give when authenticating. + */ + char password[1024]; + + /** + * The regular expression to use when searching for the password + * prompt. This will be NULL unless the telnet client is currently + * searching for the password prompt. + */ + regex_t* password_regex; + /** * The name of the font to use for display rendering. */ diff --git a/src/protocols/telnet/guac_handlers.c b/src/protocols/telnet/guac_handlers.c index 45f4b3f8..f68cb653 100644 --- a/src/protocols/telnet/guac_handlers.c +++ b/src/protocols/telnet/guac_handlers.c @@ -30,7 +30,9 @@ #include #include +#include #include +#include #include int guac_telnet_client_handle_messages(guac_client* client) { @@ -45,8 +47,10 @@ int guac_telnet_client_mouse_handler(guac_client* client, int x, int y, int mask guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data; guac_terminal* term = client_data->term; - /* Send mouse event */ - guac_terminal_send_mouse(term, x, y, mask); + /* Send mouse if not searching for password */ + if (client_data->password_regex == NULL) + guac_terminal_send_mouse(term, x, y, mask); + return 0; } @@ -56,8 +60,10 @@ int guac_telnet_client_key_handler(guac_client* client, int keysym, int pressed) guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data; guac_terminal* term = client_data->term; - /* Send key */ - guac_terminal_send_key(term, keysym, pressed); + /* Send key if not searching for password */ + if (client_data->password_regex == NULL) + guac_terminal_send_key(term, keysym, pressed); + return 0; } @@ -95,6 +101,12 @@ int guac_telnet_client_free_handler(guac_client* client) { telnet_free(guac_client_data->telnet); } + /* Free password regex */ + if (guac_client_data->password_regex != NULL) { + regfree(guac_client_data->password_regex); + free(guac_client_data->password_regex); + } + free(client->data); return 0; diff --git a/src/protocols/telnet/telnet_client.c b/src/protocols/telnet/telnet_client.c index a636d731..b5bd1850 100644 --- a/src/protocols/telnet/telnet_client.c +++ b/src/protocols/telnet/telnet_client.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,61 @@ 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. + */ +static void __guac_telnet_password_search(guac_client* client, const char* buffer, int size) { + + static char line_buffer[1024] = {0}; + static int length = 0; + + guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data; + + int i; + const char* current; + + /* Ensure line buffer contains only the most recent line */ + current = buffer; + for (i = 0; i < size; i++) { + + /* Reset line buffer and shift input buffer for each newline */ + if (*(current++) == '\n') { + length = 0; + buffer += i; + size -= i; + i = 0; + } + + } + + /* Truncate if necessary */ + if (size + length + 1 > sizeof(line_buffer)) + size = sizeof(line_buffer) - length - 1; + + /* Append to line */ + memcpy(&(line_buffer[length]), buffer, size); + length += size; + line_buffer[length] = '\0'; + + /* Send password upon match */ + if (regexec(client_data->password_regex, line_buffer, 0, NULL, 0) == 0) { + + /* Send password */ + guac_terminal_send_string(client_data->term, client_data->password); + 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; + + } + +} + /** * Event handler, as defined by libtelnet. This function is passed to * telnet_init() and will be called for every event fired by libtelnet, @@ -95,9 +151,14 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event, switch (event->type) { - /* User input received */ + /* Terminal output received */ 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); + break; /* Data destined for remote end */