GUACAMOLE-221: Implement guacd support for verifying that client can accept the required instruction.
This commit is contained in:
parent
b00b629b96
commit
bc8ed4e104
@ -340,6 +340,9 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {
|
||||
owner = 0;
|
||||
|
||||
}
|
||||
|
||||
/* Enable keep alive on the broadcast socket */
|
||||
guac_socket_require_keep_alive(client->socket);
|
||||
|
||||
cleanup_client:
|
||||
|
||||
|
@ -478,22 +478,39 @@ int guac_client_load_plugin(guac_client* client, const char* protocol) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which is invoked by guac_client_owner_send_required() to
|
||||
* send the required parameters to the specified user, who is the owner of the
|
||||
* client session.
|
||||
*
|
||||
* @param user
|
||||
* The guac_user that will receive the required parameters, who is the owner
|
||||
* of the client.
|
||||
*
|
||||
* @param data
|
||||
* Pointer to a NULL-terminated array of required parameters that will be
|
||||
* passed on to the owner to continue the connection.
|
||||
*
|
||||
* @return
|
||||
* A pointer to an integer containing the return status of the send
|
||||
* operation.
|
||||
*/
|
||||
static void* guac_client_owner_send_required_callback(guac_user* user, void* data) {
|
||||
|
||||
const char** required = (const char **) data;
|
||||
|
||||
/* Send required parameters to owner. */
|
||||
return (void*) ((intptr_t) guac_protocol_send_required(user->socket, required));
|
||||
|
||||
}
|
||||
|
||||
int guac_client_owner_send_required(guac_client* client, const char** required) {
|
||||
|
||||
int retval;
|
||||
|
||||
pthread_rwlock_rdlock(&(client->__users_lock));
|
||||
|
||||
/* Invoke callback with current owner */
|
||||
retval = guac_protocol_send_required(client->__owner->socket, required);
|
||||
/* Don't send required instruction if client does not support it. */
|
||||
if (!guac_client_owner_supports_required(client))
|
||||
return -1;
|
||||
|
||||
/* Flush the socket */
|
||||
guac_socket_flush(client->__owner->socket);
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
/* Return value from sending required */
|
||||
return retval;
|
||||
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_send_required_callback, required));
|
||||
|
||||
}
|
||||
|
||||
@ -652,6 +669,44 @@ static void* __webp_support_callback(guac_user* user, void* data) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback function which is invoked by guac_client_owner_supports_required()
|
||||
* to determine if the owner of a client supports the "required" instruction,
|
||||
* updating the flag to indicate support.
|
||||
*
|
||||
* @param user
|
||||
* The guac_user that will be checked for "required" instruction support.
|
||||
*
|
||||
* @param data
|
||||
* Pointer to an int containing the status for support of the "required"
|
||||
* instruction. This will be 0 if the owner does not support this
|
||||
* instruction, or 1 if the owner does support it.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static void* guac_owner_supports_required_callback(guac_user* user, void* data) {
|
||||
|
||||
int* required_supported = (int *) data;
|
||||
|
||||
/* Check if owner supports required */
|
||||
if (*required_supported)
|
||||
*required_supported = guac_user_supports_required(user);
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
int guac_client_owner_supports_required(guac_client* client) {
|
||||
|
||||
int required_supported = 1;
|
||||
|
||||
guac_client_for_owner(client, guac_owner_supports_required_callback, &required_supported);
|
||||
|
||||
return required_supported;
|
||||
|
||||
}
|
||||
|
||||
int guac_client_supports_webp(guac_client* client) {
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
|
@ -708,6 +708,21 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
cairo_surface_t* surface, int quality, int lossless);
|
||||
|
||||
/**
|
||||
* Returns whether the owner of the given client supports the "required"
|
||||
* instruction, returning non-zero if the client owner does support the
|
||||
* instruction, or zero if the owner does not.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole client whose owner should be checked for supporting
|
||||
* the "required" instruction.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the owner of the given client supports the "required"
|
||||
* instruction, zero otherwise.
|
||||
*/
|
||||
int guac_client_owner_supports_required(guac_client* client);
|
||||
|
||||
/**
|
||||
* Returns whether all users of the given client support WebP. If any user does
|
||||
* not support WebP, or the server cannot encode WebP images, zero is returned.
|
||||
|
@ -38,7 +38,7 @@
|
||||
* This version is passed by the __guac_protocol_send_args() function from the
|
||||
* server to the client during the client/server handshake.
|
||||
*/
|
||||
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_1_0"
|
||||
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_3_0"
|
||||
|
||||
/**
|
||||
* The maximum number of bytes that should be sent in any one blob instruction
|
||||
|
@ -20,6 +20,8 @@
|
||||
#ifndef _GUAC_PROTOCOL_TYPES_H
|
||||
#define _GUAC_PROTOCOL_TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Type definitions related to the Guacamole protocol.
|
||||
*
|
||||
@ -276,5 +278,38 @@ typedef enum guac_line_join_style {
|
||||
GUAC_LINE_JOIN_ROUND = 0x2
|
||||
} guac_line_join_style;
|
||||
|
||||
/**
|
||||
* Track the various protocol versions supported by guacd to help negotiate
|
||||
* features that may not be supported by various versions of the client.
|
||||
*/
|
||||
typedef enum guac_protocol_version {
|
||||
|
||||
/**
|
||||
* An unknown version of the Guacamole protocol.
|
||||
*/
|
||||
GUAC_PROTOCOL_VERSION_UNKNOWN = 0x000000,
|
||||
|
||||
/**
|
||||
* Original protocol version 1.0.0, which lacks support for negotiating
|
||||
* parameters and protocol version.
|
||||
*/
|
||||
GUAC_PROTOCOL_VERSION_1_0_0 = 0x010000,
|
||||
|
||||
/**
|
||||
* Protocol version 1.1.0, which includes support for parameter and version
|
||||
* negotiation and for sending timezone information from the client
|
||||
* to the server.
|
||||
*/
|
||||
GUAC_PROTOCOL_VERSION_1_1_0 = 0x010100,
|
||||
|
||||
/**
|
||||
* Protocol version 1.3.0, which supports the "required" instruction,
|
||||
* allowing connections in guacd to request information from the client and
|
||||
* await a response.
|
||||
*/
|
||||
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300
|
||||
|
||||
} guac_protocol_version;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1023,5 +1023,32 @@ int guac_protocol_send_name(guac_socket* socket, const char* name);
|
||||
*/
|
||||
int guac_protocol_decode_base64(char* base64);
|
||||
|
||||
/**
|
||||
* Given a string representation of a protocol version, search the mapping
|
||||
* to find the matching enum version, or GUAC_PROTOCOL_VERSION_UNKNOWN if no
|
||||
* match is found.
|
||||
*
|
||||
* @param version_string
|
||||
* The string representation of the protocol version.
|
||||
*
|
||||
* @return
|
||||
* The enum value of the protocol version, or GUAC_PROTOCOL_VERSION_UNKNOWN
|
||||
* if no match is found.
|
||||
*/
|
||||
guac_protocol_version guac_protocol_string_to_version(char* version_string);
|
||||
|
||||
/**
|
||||
* Given the enum value of the protocol version, return a pointer to the string
|
||||
* representation of the version, or NULL if the version is unknown.
|
||||
*
|
||||
* @param version
|
||||
* The enum value of the protocol version.
|
||||
*
|
||||
* @return
|
||||
* A pointer to the string representation of the protocol version, or NULL
|
||||
* if the version is unknown.
|
||||
*/
|
||||
const char* guac_protocol_version_to_string(guac_protocol_version version);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -136,10 +136,6 @@ void guac_socket_free(guac_socket* socket);
|
||||
* to ensure neither side of the socket times out while the socket is open.
|
||||
* This ping will take the form of a "nop" instruction.
|
||||
*
|
||||
* @deprecated
|
||||
* Manually starting the keep alive process on sockets is no longer
|
||||
* necessary, as the sockets enable the "nop" ping by default.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to declare as requiring an automatic keep-alive ping.
|
||||
*/
|
||||
|
@ -95,6 +95,12 @@ struct guac_user_info {
|
||||
* is the standard tzdata naming convention.
|
||||
*/
|
||||
const char* timezone;
|
||||
|
||||
/**
|
||||
* The Guacamole protocol version that the remote system supports, allowing
|
||||
* for feature support to be negotiated between client and server.
|
||||
*/
|
||||
guac_protocol_version protocol_version;
|
||||
|
||||
};
|
||||
|
||||
@ -823,6 +829,17 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
cairo_surface_t* surface, int quality, int lossless);
|
||||
|
||||
/**
|
||||
* Returns whether the given user supports the "required" instruction.
|
||||
*
|
||||
* @param user
|
||||
* The Guacamole user to check for support of the "required" instruction.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the user supports the "required" instruction, otherwise zero.
|
||||
*/
|
||||
int guac_user_supports_required(guac_user* user);
|
||||
|
||||
/**
|
||||
* Returns whether the given user supports WebP. If the user does not
|
||||
* support WebP, or the server cannot encode WebP images, zero is returned.
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "guacamole/layer.h"
|
||||
#include "guacamole/object.h"
|
||||
#include "guacamole/protocol.h"
|
||||
#include "guacamole/protocol-types.h"
|
||||
#include "guacamole/socket.h"
|
||||
#include "guacamole/stream.h"
|
||||
#include "guacamole/unicode.h"
|
||||
@ -39,6 +40,34 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* Structure mapping the enum value of a protocol version to the string
|
||||
* representation of the version.
|
||||
*/
|
||||
typedef struct guac_protocol_version_mapping {
|
||||
|
||||
/**
|
||||
* The enum value representing the selected protocol version.
|
||||
*/
|
||||
guac_protocol_version version;
|
||||
|
||||
/**
|
||||
* The string value representing the protocol version.
|
||||
*/
|
||||
char* version_string;
|
||||
|
||||
} guac_protocol_version_mapping;
|
||||
|
||||
/**
|
||||
* The map of known protocol version enum to the corresponding string value.
|
||||
*/
|
||||
guac_protocol_version_mapping guac_protocol_version_table[] = {
|
||||
{ GUAC_PROTOCOL_VERSION_1_0_0, "VERSION_1_0_0" },
|
||||
{ GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_0" },
|
||||
{ GUAC_PROTOCOL_VERSION_1_3_0, "VERSION_1_3_0" },
|
||||
{ GUAC_PROTOCOL_VERSION_UNKNOWN, NULL }
|
||||
};
|
||||
|
||||
/* Output formatting functions */
|
||||
|
||||
ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str) {
|
||||
@ -1275,3 +1304,34 @@ int guac_protocol_decode_base64(char* base64) {
|
||||
|
||||
}
|
||||
|
||||
guac_protocol_version guac_protocol_string_to_version(char* version_string) {
|
||||
|
||||
guac_protocol_version_mapping* current = guac_protocol_version_table;
|
||||
while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
|
||||
|
||||
if (strcmp(current->version_string, version_string) == 0)
|
||||
return current->version;
|
||||
|
||||
current++;
|
||||
|
||||
}
|
||||
|
||||
return GUAC_PROTOCOL_VERSION_UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
const char* guac_protocol_version_to_string(guac_protocol_version version) {
|
||||
|
||||
guac_protocol_version_mapping* current = guac_protocol_version_table;
|
||||
while (current->version) {
|
||||
|
||||
if (current->version == version) {
|
||||
return (const char*) current->version_string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
@ -150,10 +150,8 @@ guac_socket* guac_socket_alloc() {
|
||||
socket->state = GUAC_SOCKET_OPEN;
|
||||
socket->last_write_timestamp = guac_timestamp_current();
|
||||
|
||||
/* keep alive ping by default */
|
||||
socket->__keep_alive_enabled = 1;
|
||||
pthread_create(&(socket->__keep_alive_thread), NULL,
|
||||
__guac_socket_keep_alive_thread, (void*) socket);
|
||||
/* No keep alive ping by default */
|
||||
socket->__keep_alive_enabled = 0;
|
||||
|
||||
/* No handlers yet */
|
||||
socket->read_handler = NULL;
|
||||
|
@ -344,12 +344,16 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
|
||||
guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" joined connection "
|
||||
"\"%s\" (%i users now present)", user->user_id,
|
||||
client->connection_id, client->connected_users);
|
||||
if (strcmp(parser->argv[0],"") != 0)
|
||||
if (strcmp(parser->argv[0],"") != 0) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Client is using protocol "
|
||||
"version \"%s\"", parser->argv[0]);
|
||||
else
|
||||
user->info.protocol_version = guac_protocol_string_to_version(parser->argv[0]);
|
||||
}
|
||||
else {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Client has not defined "
|
||||
"its protocol version.");
|
||||
user->info.protocol_version = GUAC_PROTOCOL_VERSION_1_0_0;
|
||||
}
|
||||
|
||||
/* Handle user I/O, wait for connection to terminate */
|
||||
guac_user_start(parser, user, usec_timeout);
|
||||
|
@ -316,6 +316,15 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
|
||||
|
||||
}
|
||||
|
||||
int guac_user_supports_required(guac_user* user) {
|
||||
|
||||
if (user == NULL)
|
||||
return 0;
|
||||
|
||||
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_3_0);
|
||||
|
||||
}
|
||||
|
||||
int guac_user_supports_webp(guac_user* user) {
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
|
@ -202,7 +202,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
/**
|
||||
* Callback invoked by FreeRDP when authentication is required but a username
|
||||
* and password has not already been given. In the case of Guacamole, this
|
||||
* function always succeeds but does not populate the usename or password. The
|
||||
* function always succeeds but does not populate the username or password. The
|
||||
* username/password must be given within the connection parameters.
|
||||
*
|
||||
* @param instance
|
||||
@ -232,6 +232,11 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
|
||||
char* params[4] = {};
|
||||
int i = 0;
|
||||
|
||||
/* If the client does not support the "required" instruction, just
|
||||
exit. */
|
||||
if (!guac_client_owner_supports_required(client))
|
||||
return TRUE;
|
||||
|
||||
if (settings->username == NULL) {
|
||||
params[i] = GUAC_RDP_PARAMETER_NAME_USERNAME;
|
||||
rdp_client->rdp_credential_flags |= GUAC_RDP_CRED_FLAG_USERNAME;
|
||||
@ -257,7 +262,7 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
|
||||
/* Lock the client thread. */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_credential_lock));
|
||||
|
||||
/* Send require parameters to the owner. */
|
||||
/* Send required parameters to the owner. */
|
||||
guac_client_owner_send_required(client, (const char**) params);
|
||||
|
||||
/* Wait for condition. */
|
||||
|
@ -74,6 +74,10 @@ static void guac_ssh_get_credential(guac_client *client, char* cred_name) {
|
||||
|
||||
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||
|
||||
/* If the client does not support the "required" instruction, just return. */
|
||||
if (!guac_client_owner_supports_required(client))
|
||||
return;
|
||||
|
||||
/* Lock the terminal thread while prompting for the credential. */
|
||||
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
||||
|
||||
|
@ -36,6 +36,11 @@ 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 the client does not support the "required" instruction, just return
|
||||
the configuration data. */
|
||||
if (!guac_client_owner_supports_required(gc))
|
||||
return ((guac_vnc_client*) gc->data)->settings->password;
|
||||
|
||||
/* If password isn't around, prompt for it. */
|
||||
if (settings->password == NULL || strcmp(settings->password, "") == 0) {
|
||||
/* Lock the thread. */
|
||||
@ -64,10 +69,19 @@ rfbCredential* guac_vnc_get_credentials(rfbClient* client, int credentialType) {
|
||||
guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY);
|
||||
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));
|
||||
|
||||
/* If the client does not support the "required" instruction, just return
|
||||
the parameters already configured. */
|
||||
if (!guac_client_owner_supports_required(gc)) {
|
||||
creds->userCredential.username = settings->username;
|
||||
creds->userCredential.password = settings->password;
|
||||
return creds;
|
||||
}
|
||||
|
||||
char* params[2] = {NULL};
|
||||
int i = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user