GUACAMOLE-221: Add support for sending multiple params in required.

This commit is contained in:
Nick Couchman 2019-08-09 21:09:11 -04:00
parent 21a5d9ee62
commit 7369bed22c
11 changed files with 191 additions and 39 deletions

View File

@ -803,12 +803,12 @@ int guac_protocol_send_rect(guac_socket* socket, const guac_layer* layer,
* The guac_socket connection to use. * The guac_socket connection to use.
* *
* @param required * @param required
* The name of the parameter that is required. * A NULL-terminated array of required parameters.
* *
* @return * @return
* Zero on success, non-zero on error. * Zero on success, non-zero on error.
*/ */
int guac_protocol_send_required(guac_socket* socket, const char* required); int guac_protocol_send_required(guac_socket* socket, const char** required);
/** /**
* Sends a reset instruction over the given guac_socket connection. * Sends a reset instruction over the given guac_socket connection.

View File

@ -961,17 +961,33 @@ int guac_protocol_send_rect(guac_socket* socket,
} }
int guac_protocol_send_required(guac_socket* socket, const char* required) { static int __guac_protocol_send_required(guac_socket* socket,
const char** required) {
if (guac_socket_write_string(socket, "8.required")) return -1;
for (int i=0; required[i] != NULL; i++) {
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, required[i]))
return -1;
}
return guac_socket_write_string(socket, ";");
}
int guac_protocol_send_required(guac_socket* socket, const char** required) {
int ret_val; int ret_val;
guac_socket_instruction_begin(socket); guac_socket_instruction_begin(socket);
ret_val = ret_val = __guac_protocol_send_required(socket, required);
guac_socket_write_string(socket, "8.required,")
|| __guac_socket_write_length_string(socket, required)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket); guac_socket_instruction_end(socket);
return ret_val; return ret_val;
} }

View File

@ -48,7 +48,7 @@ typedef enum guac_rdp_argv_setting {
/** /**
* The domain to use for connection authentication. * The domain to use for connection authentication.
*/ */
GUAC_RDP_ARGV_SETTING_DOMAIN GUAC_RDP_ARGV_SETTING_DOMAIN,
} guac_rdp_argv_setting; } guac_rdp_argv_setting;
@ -125,25 +125,28 @@ static int guac_rdp_argv_end_handler(guac_user* user,
free(settings->username); free(settings->username);
settings->username = malloc(strlen(argv->buffer) * sizeof(char)); settings->username = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->username, argv->buffer); strcpy(settings->username, argv->buffer);
pthread_cond_broadcast(&(rdp_client->rdp_cond)); rdp_client->rdp_cond_flags ^= GUAC_RDP_COND_FLAG_USERNAME;
break; break;
case GUAC_RDP_ARGV_SETTING_PASSWORD: case GUAC_RDP_ARGV_SETTING_PASSWORD:
free(settings->password); free(settings->password);
settings->password = malloc(strlen(argv->buffer) * sizeof(char)); settings->password = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->password, argv->buffer); strcpy(settings->password, argv->buffer);
pthread_cond_broadcast(&(rdp_client->rdp_cond)); rdp_client->rdp_cond_flags ^= GUAC_RDP_COND_FLAG_PASSWORD;
break; break;
case GUAC_RDP_ARGV_SETTING_DOMAIN: case GUAC_RDP_ARGV_SETTING_DOMAIN:
free(settings->domain); free(settings->domain);
settings->domain = malloc(strlen(argv->buffer) * sizeof(char)); settings->domain = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->domain, argv->buffer); strcpy(settings->domain, argv->buffer);
pthread_cond_broadcast(&(rdp_client->rdp_cond)); rdp_client->rdp_cond_flags ^= GUAC_RDP_COND_FLAG_DOMAIN;
break; break;
} }
if (!rdp_client->rdp_cond_flags)
pthread_cond_signal(&(rdp_client->rdp_cond));
free(argv); free(argv);
return 0; return 0;

View File

@ -161,6 +161,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
/* Init RDP credential lock and condition */ /* Init RDP credential lock and condition */
pthread_mutex_init(&(rdp_client->rdp_credential_lock), &(rdp_client->attributes)); pthread_mutex_init(&(rdp_client->rdp_credential_lock), &(rdp_client->attributes));
pthread_cond_init(&(rdp_client->rdp_credential_cond), NULL);; pthread_cond_init(&(rdp_client->rdp_credential_cond), NULL);;
rdp_client->rdp_credential_flags = 0;
/* Set handlers */ /* Set handlers */
client->join_handler = guac_rdp_user_join_handler; client->join_handler = guac_rdp_user_join_handler;

View File

@ -230,32 +230,51 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
guac_client* client = ((rdp_freerdp_context*) context)->client; guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_settings* settings = rdp_client->settings; guac_rdp_settings* settings = rdp_client->settings;
char* params[4] = {};
int i = 0;
if (settings->username == NULL || strcmp(settings->username, "") == 0) {
params[i] = "username";
rdp_client->rdp_cond_flags |= GUAC_RDP_COND_FLAG_USERNAME;
i++;
}
if (settings->password == NULL || strcmp(settings->password, "") == 0) {
params[i] = "password";
rdp_client->rdp_cond_flags |= GUAC_RDP_COND_FLAG_PASSWORD;
i++;
}
if (settings->domain == NULL || strcmp(settings->domain, "") == 0) {
params[i] = "domain";
rdp_client->rdp_cond_flags |= GUAC_RDP_COND_FLAG_DOMAIN;
i++;
}
/* NULL-terminate the array. */
params[i] = NULL;
if (i > 0) {
/* Lock the client thread. */
pthread_mutex_lock(&(rdp_client->rdp_lock)); pthread_mutex_lock(&(rdp_client->rdp_lock));
while (settings->username == NULL || strcmp(settings->username, "") == 0) { /* Send require params and flush socket. */
guac_protocol_send_required(client->socket, "username"); guac_protocol_send_required(client->socket, (const char**) params);
guac_socket_flush(client->socket); guac_socket_flush(client->socket);
/* Wait for condition. */
pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock)); pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock));
/* Get new values from settings. */
*username = settings->username; *username = settings->username;
}
while (settings->password == NULL || strcmp(settings->password, "") == 0) {
guac_protocol_send_required(client->socket, "password");
guac_socket_flush(client->socket);
pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock));
*password = settings->password; *password = settings->password;
}
while (settings->domain == NULL || strcmp(settings->domain, "") == 0) {
guac_protocol_send_required(client->socket, "domain");
guac_socket_flush(client->socket);
pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock));
*domain = settings->domain; *domain = settings->domain;
/* Unlock the thread. */
pthread_mutex_unlock(&(rdp_client->rdp_lock));
} }
pthread_mutex_unlock(&(rdp_client->rdp_lock)); /* Always return TRUE allowing connection to retry. */
return TRUE; return TRUE;
} }

View File

@ -49,6 +49,21 @@
#include <pthread.h> #include <pthread.h>
#include <stdint.h> #include <stdint.h>
/**
* A flag for tracking if we are waiting conditionally on a username.
*/
#define GUAC_RDP_COND_FLAG_USERNAME 1
/**
* A flag for tracking if we are waiting conditionally on a password.
*/
#define GUAC_RDP_COND_FLAG_PASSWORD 2
/**
* A flag for tracking if we are waiting conditionally on a domain.
*/
#define GUAC_RDP_COND_FLAG_DOMAIN 3
/** /**
* RDP-specific client data. * RDP-specific client data.
*/ */
@ -165,6 +180,12 @@ typedef struct guac_rdp_client {
*/ */
pthread_cond_t rdp_credential_cond; pthread_cond_t rdp_credential_cond;
/**
* Flags for tracking events related to the rdp_credential_cond
* pthread condition.
*/
unsigned rdp_credential_flags;
/** /**
* Common attributes for locks. * Common attributes for locks.
*/ */

View File

@ -75,7 +75,7 @@ static void guac_ssh_get_credential(guac_client *client, char* cred_name) {
pthread_mutex_lock(&(ssh_client->term_channel_lock)); pthread_mutex_lock(&(ssh_client->term_channel_lock));
guac_protocol_send_required(client->socket, cred_name); guac_protocol_send_required(client->socket, (const char* []) {cred_name, NULL});
guac_socket_flush(client->socket); guac_socket_flush(client->socket);
pthread_cond_wait(&(ssh_client->ssh_cond), &(ssh_client->term_channel_lock)); pthread_cond_wait(&(ssh_client->ssh_cond), &(ssh_client->term_channel_lock));

View File

@ -35,6 +35,11 @@
*/ */
typedef enum guac_vnc_argv_setting { typedef enum guac_vnc_argv_setting {
/**
* The username for the connection.
*/
GUAC_VNC_ARGV_SETTING_USERNAME,
/** /**
* The password for the connection. * The password for the connection.
*/ */
@ -109,6 +114,19 @@ static int guac_vnc_argv_end_handler(guac_user* user, guac_stream* stream) {
/* Apply changes to chosen setting */ /* Apply changes to chosen setting */
switch (argv->setting) { switch (argv->setting) {
/* Update username */
case GUAC_VNC_ARGV_SETTING_USERNAME:
/* Update username in settings. */
if (settings->username != NULL)
free(settings->username);
settings->username = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->username, argv->buffer);
/* Remove the username conditional flag. */
vnc_client->argv_cond_flags ^= GUAC_VNC_COND_FLAG_USERNAME;
break;
/* Update password */ /* Update password */
case GUAC_VNC_ARGV_SETTING_PASSWORD: case GUAC_VNC_ARGV_SETTING_PASSWORD:
@ -118,11 +136,16 @@ static int guac_vnc_argv_end_handler(guac_user* user, guac_stream* stream) {
settings->password = malloc(strlen(argv->buffer) * sizeof(char)); settings->password = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->password, argv->buffer); strcpy(settings->password, argv->buffer);
pthread_cond_broadcast(&(vnc_client->argv_cond)); /* Remove the password conditional flag. */
vnc_client->argv_cond_flags ^= GUAC_VNC_COND_FLAG_PASSWORD;
break; break;
} }
/* If no flags are set, signal the conditional. */
if (!vnc_client->argv_cond_flags)
pthread_cond_broadcast(&(vnc_client->argv_cond));
free(argv); free(argv);
return 0; return 0;
@ -134,7 +157,9 @@ int guac_vnc_argv_handler(guac_user* user, guac_stream* stream, char* mimetype,
guac_vnc_argv_setting setting; guac_vnc_argv_setting setting;
/* Allow users to update authentication information */ /* Allow users to update authentication information */
if (strcmp(name, "password") == 0) if (strcmp(name, "username") == 0)
setting = GUAC_VNC_ARGV_SETTING_USERNAME;
else if (strcmp(name, "password") == 0)
setting = GUAC_VNC_ARGV_SETTING_PASSWORD; setting = GUAC_VNC_ARGV_SETTING_PASSWORD;
/* No other connection parameters may be updated */ /* No other connection parameters may be updated */

View File

@ -36,11 +36,23 @@ char* guac_vnc_get_password(rfbClient* client) {
guac_vnc_client* vnc_client = ((guac_vnc_client*) gc->data); guac_vnc_client* vnc_client = ((guac_vnc_client*) gc->data);
guac_vnc_settings* settings = vnc_client->settings; guac_vnc_settings* settings = vnc_client->settings;
/* If password isn't around, prompt for it. */
if (settings->password == NULL || strcmp(settings->password, "") == 0) { if (settings->password == NULL || strcmp(settings->password, "") == 0) {
/* Lock the thread. */
pthread_mutex_lock(&(vnc_client->argv_lock)); pthread_mutex_lock(&(vnc_client->argv_lock));
guac_protocol_send_required(gc->socket, "password");
/* Send the request for password and flush the socket. */
guac_protocol_send_required(gc->socket,
(const char* []) {"password", NULL});
guac_socket_flush(gc->socket); guac_socket_flush(gc->socket);
/* Set the conditional flag. */
vnc_client->argv_cond_flags |= GUAC_VNC_COND_FLAG_PASSWORD;
/* Wait for the condition. */
pthread_cond_wait(&(vnc_client->argv_cond), &(vnc_client->argv_lock)); pthread_cond_wait(&(vnc_client->argv_cond), &(vnc_client->argv_lock));
/* Unlock the thread. */
pthread_mutex_unlock(&(vnc_client->argv_lock)); pthread_mutex_unlock(&(vnc_client->argv_lock));
} }
@ -50,13 +62,52 @@ char* guac_vnc_get_password(rfbClient* client) {
rfbCredential* guac_vnc_get_credentials(rfbClient* client, int credentialType) { rfbCredential* guac_vnc_get_credentials(rfbClient* client, int credentialType) {
guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY);
guac_vnc_settings* settings = ((guac_vnc_client*) gc->data)->settings; guac_vnc_client* vnc_client = ((guac_vnc_client*) gc->data);
guac_vnc_settings* settings = vnc_client->settings;
/* Handle request for Username/Password credentials */
if (credentialType == rfbCredentialTypeUser) { if (credentialType == rfbCredentialTypeUser) {
rfbCredential *creds = malloc(sizeof(rfbCredential)); rfbCredential *creds = malloc(sizeof(rfbCredential));
char* params[2] = {NULL};
int i = 0;
/* Check if username is null or empty. */
if (settings->username == NULL || strcmp(settings->username, "") == 0) {
params[i] = "username";
i++;
vnc_client->argv_cond_flags |= GUAC_VNC_COND_FLAG_USERNAME;
}
/* Check if password is null or empty. */
if (settings->password == NULL || strcmp(settings->password, "") == 0) {
params[i] = "password";
i++;
vnc_client->argv_cond_flags |= GUAC_VNC_COND_FLAG_PASSWORD;
}
/* If we have empty parameters, request them. */
if (i > 0) {
/* Lock the thread. */
pthread_mutex_lock(&(vnc_client->argv_lock));
/* Send required parameters to client and flush the socket. */
guac_protocol_send_required(gc->socket, (const char**) params);
guac_socket_flush(gc->socket);
/* Wait for the parameters to be returned. */
pthread_cond_wait(&(vnc_client->argv_cond), &(vnc_client->argv_lock));
/* Pull the credentials from updated settings. */
creds->userCredential.username = settings->username; creds->userCredential.username = settings->username;
creds->userCredential.password = settings->password; creds->userCredential.password = settings->password;
/* Unlock the thread. */
pthread_mutex_unlock(&(vnc_client->argv_lock));
return creds; return creds;
}
} }
guac_client_abort(gc, GUAC_PROTOCOL_STATUS_SERVER_ERROR, guac_client_abort(gc, GUAC_PROTOCOL_STATUS_SERVER_ERROR,

View File

@ -57,6 +57,7 @@ int guac_client_init(guac_client* client) {
/* Initialize argv lock and condition */ /* Initialize argv lock and condition */
pthread_mutex_init(&(vnc_client->argv_lock), NULL); pthread_mutex_init(&(vnc_client->argv_lock), NULL);
pthread_cond_init(&(vnc_client->argv_cond), NULL); pthread_cond_init(&(vnc_client->argv_cond), NULL);
vnc_client->argv_cond_flags = 0;
/* Init clipboard */ /* Init clipboard */
vnc_client->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH); vnc_client->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH);

View File

@ -45,6 +45,16 @@
#include <pthread.h> #include <pthread.h>
/**
* A flag for tracking status of requesting username from client.
*/
#define GUAC_VNC_COND_FLAG_USERNAME 1
/**
* A flag for tracking status of requesting password from client.
*/
#define GUAC_VNC_COND_FLAG_PASSWORD 2
/** /**
* VNC-specific client data. * VNC-specific client data.
*/ */
@ -72,6 +82,11 @@ typedef struct guac_vnc_client {
*/ */
pthread_cond_t argv_cond; pthread_cond_t argv_cond;
/**
* Flags for conditional signaling for argv updates;
*/
unsigned argv_cond_flags;
/** /**
* The underlying VNC client. * The underlying VNC client.
*/ */