GUACAMOLE-221: Add protocol functions for sending prompt to client.
This commit is contained in:
parent
382d72a26a
commit
21a5d9ee62
@ -26,21 +26,16 @@
|
||||
#include <libssh2.h>
|
||||
|
||||
/**
|
||||
* Handler for retrieving additional credentials.
|
||||
* A handler function for retrieving additional credentials for the client.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole Client associated with this need for additional
|
||||
* credentials.
|
||||
*
|
||||
* @param cred_name
|
||||
* The name of the credential being requested, which will be shared
|
||||
* with the client in order to generate a meaningful prompt.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated string containing the credentials provided by
|
||||
* the user, which must be freed by a call to free().
|
||||
* The connection parameter that is being requested from the client.
|
||||
*/
|
||||
typedef char* guac_ssh_credential_handler(guac_client* client, char* cred_name);
|
||||
typedef void guac_ssh_credential_handler(guac_client* client, char* cred_name);
|
||||
|
||||
/**
|
||||
* An SSH session, backed by libssh2 and associated with a particular
|
||||
|
@ -360,7 +360,7 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
||||
|
||||
/* Attempt authentication with username + password. */
|
||||
if (user->password == NULL && common_session->credential_handler)
|
||||
user->password = common_session->credential_handler(client, "Password: ");
|
||||
common_session->credential_handler(client, "password");
|
||||
|
||||
/* Authenticate with password, if provided */
|
||||
if (user->password != NULL) {
|
||||
|
@ -794,6 +794,22 @@ int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer);
|
||||
int guac_protocol_send_rect(guac_socket* socket, const guac_layer* layer,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* Sends a "required" instruction over the given guac_socket connection. This
|
||||
* instruction indicates to the client that one or more additional parameters
|
||||
* is needed to continue the connection.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket connection to use.
|
||||
*
|
||||
* @param required
|
||||
* The name of the parameter that is required.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_required(guac_socket* socket, const char* required);
|
||||
|
||||
/**
|
||||
* Sends a reset instruction over the given guac_socket connection.
|
||||
*
|
||||
|
@ -961,6 +961,21 @@ int guac_protocol_send_rect(guac_socket* 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, ";");
|
||||
|
||||
guac_socket_instruction_end(socket);
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) {
|
||||
|
||||
int ret_val;
|
||||
|
@ -38,6 +38,7 @@ nodist_libguac_client_rdp_la_SOURCES = \
|
||||
_generated_keymaps.c
|
||||
|
||||
libguac_client_rdp_la_SOURCES = \
|
||||
argv.c \
|
||||
beep.c \
|
||||
bitmap.c \
|
||||
channels/audio-input/audio-buffer.c \
|
||||
@ -82,6 +83,7 @@ libguac_client_rdp_la_SOURCES = \
|
||||
user.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
argv.h \
|
||||
beep.h \
|
||||
bitmap.h \
|
||||
channels/audio-input/audio-buffer.h \
|
||||
|
189
src/protocols/rdp/argv.c
Normal file
189
src/protocols/rdp/argv.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "argv.h"
|
||||
#include "rdp.h"
|
||||
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* All RDP connection settings which may be updated by unprivileged users
|
||||
* through "argv" streams.
|
||||
*/
|
||||
typedef enum guac_rdp_argv_setting {
|
||||
|
||||
/**
|
||||
* The username of the connection.
|
||||
*/
|
||||
GUAC_RDP_ARGV_SETTING_USERNAME,
|
||||
|
||||
/**
|
||||
* The password to authenticate the connection.
|
||||
*/
|
||||
GUAC_RDP_ARGV_SETTING_PASSWORD,
|
||||
|
||||
/**
|
||||
* The domain to use for connection authentication.
|
||||
*/
|
||||
GUAC_RDP_ARGV_SETTING_DOMAIN
|
||||
|
||||
} guac_rdp_argv_setting;
|
||||
|
||||
/**
|
||||
* The value or current status of a connection parameter received over an
|
||||
* "argv" stream.
|
||||
*/
|
||||
typedef struct guac_rdp_argv {
|
||||
|
||||
/**
|
||||
* The specific setting being updated.
|
||||
*/
|
||||
guac_rdp_argv_setting setting;
|
||||
|
||||
/**
|
||||
* Buffer space for containing the received argument value.
|
||||
*/
|
||||
char buffer[GUAC_RDP_ARGV_MAX_LENGTH];
|
||||
|
||||
/**
|
||||
* The number of bytes received so far.
|
||||
*/
|
||||
int length;
|
||||
|
||||
} guac_rdp_argv;
|
||||
|
||||
/**
|
||||
* Handler for "blob" instructions which appends the data from received blobs
|
||||
* to the end of the in-progress argument value buffer.
|
||||
*
|
||||
* @see guac_user_blob_handler
|
||||
*/
|
||||
static int guac_rdp_argv_blob_handler(guac_user* user,
|
||||
guac_stream* stream, void* data, int length) {
|
||||
|
||||
guac_rdp_argv* argv = (guac_rdp_argv*) stream->data;
|
||||
|
||||
/* Calculate buffer size remaining, including space for null terminator,
|
||||
* adjusting received length accordingly */
|
||||
int remaining = sizeof(argv->buffer) - argv->length - 1;
|
||||
if (length > remaining)
|
||||
length = remaining;
|
||||
|
||||
/* Append received data to end of buffer */
|
||||
memcpy(argv->buffer + argv->length, data, length);
|
||||
argv->length += length;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for "end" instructions which applies the changes specified by the
|
||||
* argument value buffer associated with the stream.
|
||||
*
|
||||
* @see guac_user_end_handler
|
||||
*/
|
||||
static int guac_rdp_argv_end_handler(guac_user* user,
|
||||
guac_stream* stream) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_settings* settings = rdp_client->settings;
|
||||
|
||||
/* Append null terminator to value */
|
||||
guac_rdp_argv* argv = (guac_rdp_argv*) stream->data;
|
||||
argv->buffer[argv->length] = '\0';
|
||||
|
||||
/* Apply changes to chosen setting */
|
||||
switch (argv->setting) {
|
||||
|
||||
/* Update RDP username. */
|
||||
case GUAC_RDP_ARGV_SETTING_USERNAME:
|
||||
free(settings->username);
|
||||
settings->username = malloc(strlen(argv->buffer) * sizeof(char));
|
||||
strcpy(settings->username, argv->buffer);
|
||||
pthread_cond_broadcast(&(rdp_client->rdp_cond));
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
free(argv);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_argv_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* name) {
|
||||
|
||||
guac_rdp_argv_setting setting;
|
||||
|
||||
/* Allow users to update authentication details */
|
||||
if (strcmp(name, "username") == 0)
|
||||
setting = GUAC_RDP_ARGV_SETTING_USERNAME;
|
||||
else if (strcmp(name, "password") == 0)
|
||||
setting = GUAC_RDP_ARGV_SETTING_PASSWORD;
|
||||
else if (strcmp(name, "domain") == 0)
|
||||
setting = GUAC_RDP_ARGV_SETTING_DOMAIN;
|
||||
|
||||
/* No other connection parameters may be updated */
|
||||
else {
|
||||
guac_protocol_send_ack(user->socket, stream, "Not allowed.",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guac_rdp_argv* argv = malloc(sizeof(guac_rdp_argv));
|
||||
argv->setting = setting;
|
||||
argv->length = 0;
|
||||
|
||||
/* Prepare stream to receive argument value */
|
||||
stream->blob_handler = guac_rdp_argv_blob_handler;
|
||||
stream->end_handler = guac_rdp_argv_end_handler;
|
||||
stream->data = argv;
|
||||
|
||||
/* Signal stream is ready */
|
||||
guac_protocol_send_ack(user->socket, stream, "Ready for updated "
|
||||
"parameter.", GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
41
src/protocols/rdp/argv.h
Normal file
41
src/protocols/rdp/argv.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef GUAC_RDP_ARGV_H
|
||||
#define GUAC_RDP_ARGV_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/user.h>
|
||||
|
||||
/**
|
||||
* The maximum number of bytes to allow for any argument value received via an
|
||||
* argv stream, including null terminator.
|
||||
*/
|
||||
#define GUAC_RDP_ARGV_MAX_LENGTH 16384
|
||||
|
||||
/**
|
||||
* Handles an incoming stream from a Guacamole "argv" instruction, updating the
|
||||
* given connection parameter if that parameter is allowed to be updated.
|
||||
*/
|
||||
guac_user_argv_handler guac_rdp_argv_handler;
|
||||
|
||||
#endif
|
||||
|
@ -158,6 +158,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
/* Initalize the lock */
|
||||
pthread_rwlock_init(&(rdp_client->lock), NULL);
|
||||
|
||||
/* 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);;
|
||||
|
||||
/* Set handlers */
|
||||
client->join_handler = guac_rdp_user_join_handler;
|
||||
client->free_handler = guac_rdp_client_free_handler;
|
||||
@ -218,6 +222,9 @@ int guac_rdp_client_free_handler(guac_client* client) {
|
||||
/* Clean up audio input buffer, if allocated */
|
||||
if (rdp_client->audio_input != NULL)
|
||||
guac_rdp_audio_buffer_free(rdp_client->audio_input);
|
||||
|
||||
/* Destroy the pthread conditional handler */
|
||||
pthread_cond_destroy(&(rdp_client->rdp_cond));
|
||||
|
||||
pthread_rwlock_destroy(&(rdp_client->lock));
|
||||
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include <freerdp/update.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/parser.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
@ -227,10 +228,34 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
|
||||
/* Warn if connection is likely to fail due to lack of credentials */
|
||||
guac_client_log(client, GUAC_LOG_INFO,
|
||||
"Authentication requested but username or password not given");
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_settings* settings = rdp_client->settings;
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
while (settings->username == NULL || strcmp(settings->username, "") == 0) {
|
||||
guac_protocol_send_required(client->socket, "username");
|
||||
guac_socket_flush(client->socket);
|
||||
pthread_cond_wait(&(rdp_client->rdp_cond), &(rdp_client->rdp_lock));
|
||||
*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;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
@ -153,6 +153,18 @@ typedef struct guac_rdp_client {
|
||||
*/
|
||||
guac_common_list* available_svc;
|
||||
|
||||
/**
|
||||
* Lock which is locked and unlocked for each credential required
|
||||
* during the connection process.
|
||||
*/
|
||||
pthread_mutex_t rdp_credential_lock;
|
||||
|
||||
/**
|
||||
* Condition which is used when the pthread needs to wait for a
|
||||
* particular credential to be provided.
|
||||
*/
|
||||
pthread_cond_t rdp_credential_cond;
|
||||
|
||||
/**
|
||||
* Common attributes for locks.
|
||||
*/
|
||||
|
@ -116,6 +116,9 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
|
||||
|
||||
/* Inbound arbitrary named pipes */
|
||||
user->pipe_handler = guac_rdp_pipe_svc_pipe_handler;
|
||||
|
||||
/* Handler for updating parameters during connection. */
|
||||
user->argv_handler = guac_rdp_argv_handler;
|
||||
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ int guac_ssh_argv_callback(guac_user* user, const char* mimetype,
|
||||
guac_client* client = user->client;
|
||||
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||
guac_terminal* terminal = ssh_client->term;
|
||||
guac_ssh_settings* settings = ssh_client->settings;
|
||||
|
||||
/* Update color scheme */
|
||||
if (strcmp(name, GUAC_SSH_ARGV_COLOR_SCHEME) == 0)
|
||||
|
@ -57,6 +57,32 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
/**
|
||||
* A function used to generate a prompt to gather additional credentials from
|
||||
* the guac_client during a connection, and using the specified parameter to
|
||||
* generate the prompt to the client.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client object associated with the current connection
|
||||
* where additional credentials are required.
|
||||
*
|
||||
* @param cred_name
|
||||
* The name of the parameter to prompt for in the client.
|
||||
*/
|
||||
static void guac_ssh_get_credential(guac_client *client, char* cred_name) {
|
||||
|
||||
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||
|
||||
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
||||
|
||||
guac_protocol_send_required(client->socket, cred_name);
|
||||
guac_socket_flush(client->socket);
|
||||
|
||||
pthread_cond_wait(&(ssh_client->ssh_cond), &(ssh_client->term_channel_lock));
|
||||
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a new user object containing a username and password or private
|
||||
* key, prompting the user as necessary to obtain that information.
|
||||
@ -77,9 +103,8 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
||||
guac_common_ssh_user* user;
|
||||
|
||||
/* Get username */
|
||||
if (settings->username == NULL)
|
||||
settings->username = guac_terminal_prompt(ssh_client->term,
|
||||
"Login as: ", true);
|
||||
while (settings->username == NULL)
|
||||
guac_ssh_get_credential(client, "username");
|
||||
|
||||
/* Create user object from username */
|
||||
user = guac_common_ssh_create_user(settings->username);
|
||||
@ -103,10 +128,8 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
||||
"Re-attempting private key import (WITH passphrase)");
|
||||
|
||||
/* Prompt for passphrase if missing */
|
||||
if (settings->key_passphrase == NULL)
|
||||
settings->key_passphrase =
|
||||
guac_terminal_prompt(ssh_client->term,
|
||||
"Key passphrase: ", false);
|
||||
while (settings->key_passphrase == NULL)
|
||||
guac_ssh_get_credential(client, "passphrase");
|
||||
|
||||
/* Reattempt import with passphrase */
|
||||
if (guac_common_ssh_user_import_key(user,
|
||||
@ -144,29 +167,6 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A function used to generate a terminal prompt to gather additional
|
||||
* credentials from the guac_client during a connection, and using
|
||||
* the specified string to generate the prompt for the user.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client object associated with the current connection
|
||||
* where additional credentials are required.
|
||||
*
|
||||
* @param cred_name
|
||||
* The prompt text to display to the screen when prompting for the
|
||||
* additional credentials.
|
||||
*
|
||||
* @return
|
||||
* The string of credentials gathered from the user.
|
||||
*/
|
||||
static char* guac_ssh_get_credential(guac_client *client, char* cred_name) {
|
||||
|
||||
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||
return guac_terminal_prompt(ssh_client->term, cred_name, false);
|
||||
|
||||
}
|
||||
|
||||
void* ssh_input_thread(void* data) {
|
||||
|
||||
guac_client* client = (guac_client*) data;
|
||||
@ -281,6 +281,7 @@ void* ssh_client_thread(void* data) {
|
||||
}
|
||||
|
||||
pthread_mutex_init(&ssh_client->term_channel_lock, NULL);
|
||||
pthread_cond_init(&(ssh_client->ssh_cond), NULL);
|
||||
|
||||
/* Open channel for terminal */
|
||||
ssh_client->term_channel =
|
||||
@ -495,6 +496,7 @@ void* ssh_client_thread(void* data) {
|
||||
guac_client_stop(client);
|
||||
pthread_join(input_thread, NULL);
|
||||
|
||||
pthread_cond_destroy(&(ssh_client->ssh_cond));
|
||||
pthread_mutex_destroy(&ssh_client->term_channel_lock);
|
||||
|
||||
guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
|
||||
|
@ -89,6 +89,12 @@ typedef struct guac_ssh_client {
|
||||
* Lock dictating access to the SSH terminal channel.
|
||||
*/
|
||||
pthread_mutex_t term_channel_lock;
|
||||
|
||||
/**
|
||||
* Condition used when SSH client thread needs to wait for Guacamole
|
||||
* client response.
|
||||
*/
|
||||
pthread_cond_t ssh_cond;
|
||||
|
||||
/**
|
||||
* The current clipboard contents.
|
||||
|
@ -29,6 +29,7 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
lib_LTLIBRARIES = libguac-client-vnc.la
|
||||
|
||||
libguac_client_vnc_la_SOURCES = \
|
||||
argv.c \
|
||||
auth.c \
|
||||
client.c \
|
||||
clipboard.c \
|
||||
@ -41,6 +42,7 @@ libguac_client_vnc_la_SOURCES = \
|
||||
vnc.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
argv.h \
|
||||
auth.h \
|
||||
client.h \
|
||||
clipboard.h \
|
||||
|
163
src/protocols/vnc/argv.c
Normal file
163
src/protocols/vnc/argv.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "argv.h"
|
||||
#include "vnc.h"
|
||||
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* All VNC connection settings which may be updated by unprivileged users
|
||||
* through "argv" streams.
|
||||
*/
|
||||
typedef enum guac_vnc_argv_setting {
|
||||
|
||||
/**
|
||||
* The password for the connection.
|
||||
*/
|
||||
GUAC_VNC_ARGV_SETTING_PASSWORD
|
||||
|
||||
} guac_vnc_argv_setting;
|
||||
|
||||
/**
|
||||
* The value or current status of a connection parameter received over an
|
||||
* "argv" stream.
|
||||
*/
|
||||
typedef struct guac_vnc_argv {
|
||||
|
||||
/**
|
||||
* The specific setting being updated.
|
||||
*/
|
||||
guac_vnc_argv_setting setting;
|
||||
|
||||
/**
|
||||
* Buffer space for containing the received argument value.
|
||||
*/
|
||||
char buffer[GUAC_VNC_ARGV_MAX_LENGTH];
|
||||
|
||||
/**
|
||||
* The number of bytes received so far.
|
||||
*/
|
||||
int length;
|
||||
|
||||
} guac_vnc_argv;
|
||||
|
||||
/**
|
||||
* Handler for "blob" instructions which appends the data from received blobs
|
||||
* to the end of the in-progress argument value buffer.
|
||||
*
|
||||
* @see guac_user_blob_handler
|
||||
*/
|
||||
static int guac_vnc_argv_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
guac_vnc_argv* argv = (guac_vnc_argv*) stream->data;
|
||||
|
||||
/* Calculate buffer size remaining, including space for null terminator,
|
||||
* adjusting received length accordingly */
|
||||
int remaining = sizeof(argv->buffer) - argv->length - 1;
|
||||
if (length > remaining)
|
||||
length = remaining;
|
||||
|
||||
/* Append received data to end of buffer */
|
||||
memcpy(argv->buffer + argv->length, data, length);
|
||||
argv->length += length;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for "end" instructions which applies the changes specified by the
|
||||
* argument value buffer associated with the stream.
|
||||
*
|
||||
* @see guac_user_end_handler
|
||||
*/
|
||||
static int guac_vnc_argv_end_handler(guac_user* user, guac_stream* stream) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_vnc_client* vnc_client = (guac_vnc_client*) client->data;
|
||||
guac_vnc_settings* settings = vnc_client->settings;
|
||||
|
||||
/* Append null terminator to value */
|
||||
guac_vnc_argv* argv = (guac_vnc_argv*) stream->data;
|
||||
argv->buffer[argv->length] = '\0';
|
||||
|
||||
/* Apply changes to chosen setting */
|
||||
switch (argv->setting) {
|
||||
|
||||
/* Update password */
|
||||
case GUAC_VNC_ARGV_SETTING_PASSWORD:
|
||||
|
||||
/* Update password in settings */
|
||||
if (settings->password != NULL)
|
||||
free(settings->password);
|
||||
settings->password = malloc(strlen(argv->buffer) * sizeof(char));
|
||||
strcpy(settings->password, argv->buffer);
|
||||
|
||||
pthread_cond_broadcast(&(vnc_client->argv_cond));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
free(argv);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_vnc_argv_handler(guac_user* user, guac_stream* stream, char* mimetype,
|
||||
char* name) {
|
||||
|
||||
guac_vnc_argv_setting setting;
|
||||
|
||||
/* Allow users to update authentication information */
|
||||
if (strcmp(name, "password") == 0)
|
||||
setting = GUAC_VNC_ARGV_SETTING_PASSWORD;
|
||||
|
||||
/* No other connection parameters may be updated */
|
||||
else {
|
||||
guac_protocol_send_ack(user->socket, stream, "Not allowed.",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guac_vnc_argv* argv = malloc(sizeof(guac_vnc_argv));
|
||||
argv->setting = setting;
|
||||
argv->length = 0;
|
||||
|
||||
/* Prepare stream to receive argument value */
|
||||
stream->blob_handler = guac_vnc_argv_blob_handler;
|
||||
stream->end_handler = guac_vnc_argv_end_handler;
|
||||
stream->data = argv;
|
||||
|
||||
/* Signal stream is ready */
|
||||
guac_protocol_send_ack(user->socket, stream, "Ready for updated "
|
||||
"parameter.", GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
41
src/protocols/vnc/argv.h
Normal file
41
src/protocols/vnc/argv.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef ARGV_H
|
||||
#define ARGV_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/user.h>
|
||||
|
||||
|
||||
/**
|
||||
* The maximum number of bytes to allow for any argument value received via an
|
||||
* argv stream, including null terminator.
|
||||
*/
|
||||
#define GUAC_VNC_ARGV_MAX_LENGTH 16384
|
||||
|
||||
/**
|
||||
* Handles an incoming stream from a Guacamole "argv" instruction, updating the
|
||||
* given connection parameter if that parameter is allowed to be updated.
|
||||
*/
|
||||
guac_user_argv_handler guac_vnc_argv_handler;
|
||||
|
||||
#endif /* ARGV_H */
|
||||
|
@ -23,12 +23,29 @@
|
||||
#include "vnc.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include <rfb/rfbproto.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
char* guac_vnc_get_password(rfbClient* client) {
|
||||
guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY);
|
||||
return ((guac_vnc_client*) gc->data)->settings->password;
|
||||
guac_vnc_client* vnc_client = ((guac_vnc_client*) gc->data);
|
||||
guac_vnc_settings* settings = vnc_client->settings;
|
||||
|
||||
if (settings->password == NULL || strcmp(settings->password, "") == 0) {
|
||||
pthread_mutex_lock(&(vnc_client->argv_lock));
|
||||
guac_protocol_send_required(gc->socket, "password");
|
||||
guac_socket_flush(gc->socket);
|
||||
pthread_cond_wait(&(vnc_client->argv_cond), &(vnc_client->argv_lock));
|
||||
pthread_mutex_unlock(&(vnc_client->argv_lock));
|
||||
}
|
||||
|
||||
return settings->password;
|
||||
|
||||
}
|
||||
|
||||
rfbCredential* guac_vnc_get_credentials(rfbClient* client, int credentialType) {
|
||||
|
@ -50,9 +50,13 @@ int guac_client_init(guac_client* client) {
|
||||
client->data = vnc_client;
|
||||
|
||||
#ifdef ENABLE_VNC_TLS_LOCKING
|
||||
/* Initialize the write lock */
|
||||
/* Initialize the TLS write lock */
|
||||
pthread_mutex_init(&(vnc_client->tls_lock), NULL);
|
||||
#endif
|
||||
|
||||
/* Initialize argv lock and condition */
|
||||
pthread_mutex_init(&(vnc_client->argv_lock), NULL);
|
||||
pthread_cond_init(&(vnc_client->argv_cond), NULL);
|
||||
|
||||
/* Init clipboard */
|
||||
vnc_client->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH);
|
||||
@ -135,6 +139,10 @@ int guac_vnc_client_free_handler(guac_client* client) {
|
||||
/* Clean up TLS lock mutex. */
|
||||
pthread_mutex_destroy(&(vnc_client->tls_lock));
|
||||
#endif
|
||||
|
||||
/* Clean up argv mutex */
|
||||
pthread_cond_destroy(&(vnc_client->argv_cond));
|
||||
pthread_mutex_destroy(&(vnc_client->argv_lock));
|
||||
|
||||
/* Free generic data struct */
|
||||
free(client->data);
|
||||
|
@ -61,6 +61,16 @@ typedef struct guac_vnc_client {
|
||||
*/
|
||||
pthread_mutex_t tls_lock;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The lock for tracking changes via argv.
|
||||
*/
|
||||
pthread_mutex_t argv_lock;
|
||||
|
||||
/**
|
||||
* The condition for signaling argv updates.
|
||||
*/
|
||||
pthread_cond_t argv_cond;
|
||||
|
||||
/**
|
||||
* The underlying VNC client.
|
||||
|
Loading…
Reference in New Issue
Block a user