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.
*
* @param required
* The name of the parameter that is required.
* A NULL-terminated array of required parameters.
*
* @return
* 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.

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;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "8.required,")
|| __guac_socket_write_length_string(socket, required)
|| guac_socket_write_string(socket, ";");
ret_val = __guac_protocol_send_required(socket, required);
guac_socket_instruction_end(socket);
return ret_val;
}

View File

@ -48,8 +48,8 @@ typedef enum guac_rdp_argv_setting {
/**
* The domain to use for connection authentication.
*/
GUAC_RDP_ARGV_SETTING_DOMAIN
GUAC_RDP_ARGV_SETTING_DOMAIN,
} guac_rdp_argv_setting;
/**
@ -125,24 +125,27 @@ static int guac_rdp_argv_end_handler(guac_user* user,
free(settings->username);
settings->username = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->username, argv->buffer);
pthread_cond_broadcast(&(rdp_client->rdp_cond));
rdp_client->rdp_cond_flags ^= GUAC_RDP_COND_FLAG_USERNAME;
break;
case GUAC_RDP_ARGV_SETTING_PASSWORD:
free(settings->password);
settings->password = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->password, argv->buffer);
pthread_cond_broadcast(&(rdp_client->rdp_cond));
rdp_client->rdp_cond_flags ^= GUAC_RDP_COND_FLAG_PASSWORD;
break;
case GUAC_RDP_ARGV_SETTING_DOMAIN:
free(settings->domain);
settings->domain = malloc(strlen(argv->buffer) * sizeof(char));
strcpy(settings->domain, argv->buffer);
pthread_cond_broadcast(&(rdp_client->rdp_cond));
rdp_client->rdp_cond_flags ^= GUAC_RDP_COND_FLAG_DOMAIN;
break;
}
if (!rdp_client->rdp_cond_flags)
pthread_cond_signal(&(rdp_client->rdp_cond));
free(argv);
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 */
pthread_mutex_init(&(rdp_client->rdp_credential_lock), &(rdp_client->attributes));
pthread_cond_init(&(rdp_client->rdp_credential_cond), NULL);;
rdp_client->rdp_credential_flags = 0;
/* Set handlers */
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_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_settings* settings = rdp_client->settings;
char* params[4] = {};
int i = 0;
pthread_mutex_lock(&(rdp_client->rdp_lock));
if (settings->username == NULL || strcmp(settings->username, "") == 0) {
params[i] = "username";
rdp_client->rdp_cond_flags |= GUAC_RDP_COND_FLAG_USERNAME;
i++;
}
while (settings->username == NULL || strcmp(settings->username, "") == 0) {
guac_protocol_send_required(client->socket, "username");
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));
/* Send require params and flush socket. */
guac_protocol_send_required(client->socket, (const char**) params);
guac_socket_flush(client->socket);
/* Wait for condition. */
pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock));
/* Get new values from settings. */
*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;
}
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;
/* 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;
}

View File

@ -49,6 +49,21 @@
#include <pthread.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.
*/
@ -164,6 +179,12 @@ typedef struct guac_rdp_client {
* particular credential to be provided.
*/
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.

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));
guac_protocol_send_required(client->socket, cred_name);
guac_protocol_send_required(client->socket, (const char* []) {cred_name, NULL});
guac_socket_flush(client->socket);
pthread_cond_wait(&(ssh_client->ssh_cond), &(ssh_client->term_channel_lock));

View File

@ -35,6 +35,11 @@
*/
typedef enum guac_vnc_argv_setting {
/**
* The username for the connection.
*/
GUAC_VNC_ARGV_SETTING_USERNAME,
/**
* 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 */
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 */
case GUAC_VNC_ARGV_SETTING_PASSWORD:
@ -118,10 +136,15 @@ static int guac_vnc_argv_end_handler(guac_user* user, guac_stream* stream) {
settings->password = malloc(strlen(argv->buffer) * sizeof(char));
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;
}
/* If no flags are set, signal the conditional. */
if (!vnc_client->argv_cond_flags)
pthread_cond_broadcast(&(vnc_client->argv_cond));
free(argv);
return 0;
@ -134,7 +157,9 @@ int guac_vnc_argv_handler(guac_user* user, guac_stream* stream, char* mimetype,
guac_vnc_argv_setting setting;
/* 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;
/* 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_settings* settings = vnc_client->settings;
/* If password isn't around, prompt for it. */
if (settings->password == NULL || strcmp(settings->password, "") == 0) {
/* Lock the thread. */
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);
/* 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));
/* Unlock the thread. */
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) {
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) {
rfbCredential *creds = malloc(sizeof(rfbCredential));
creds->userCredential.username = settings->username;
creds->userCredential.password = settings->password;
return creds;
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.password = settings->password;
/* Unlock the thread. */
pthread_mutex_unlock(&(vnc_client->argv_lock));
return creds;
}
}
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 */
pthread_mutex_init(&(vnc_client->argv_lock), NULL);
pthread_cond_init(&(vnc_client->argv_cond), NULL);
vnc_client->argv_cond_flags = 0;
/* Init clipboard */
vnc_client->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH);

View File

@ -45,6 +45,16 @@
#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.
*/
@ -71,6 +81,11 @@ typedef struct guac_vnc_client {
* The condition for signaling argv updates.
*/
pthread_cond_t argv_cond;
/**
* Flags for conditional signaling for argv updates;
*/
unsigned argv_cond_flags;
/**
* The underlying VNC client.