Michael Jumper 27e762d06f GUACAMOLE-1283: Add synchronization around absolutely all outbound RDP messages.
The FreeRDP library is intended to be threadsafe, but is not reliably so
with respect to legacy RDP encryption and outbound messages. When
outbound messages are sent by multiple threads, the encryption key used
for legacy RDP encryption may not be updated correctly, resulting in a
fatal connection error like:

"ERRINFO_DECRYPT_FAILED (0x00001192):(a) Decryption using Standard RDP
Security mechanisms (section 5.3.6) failed. (b) Session key creation
using Standard RDP Security mechanisms (section 5.3.5) failed."
2021-04-08 15:43:15 -07:00

206 lines
6.6 KiB
C

/*
* 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 "channels/disp.h"
#include "channels/rdpei.h"
#include "common/cursor.h"
#include "common/display.h"
#include "common/recording.h"
#include "input.h"
#include "keyboard.h"
#include "rdp.h"
#include "settings.h"
#include <freerdp/freerdp.h>
#include <freerdp/input.h>
#include <guacamole/client.h>
#include <guacamole/user.h>
#include <stdlib.h>
int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
pthread_rwlock_rdlock(&(rdp_client->lock));
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
if (rdp_inst == NULL)
goto complete;
/* Store current mouse location/state */
guac_common_cursor_update(rdp_client->display->cursor, user, x, y, mask);
/* Report mouse position within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_mouse(rdp_client->recording, x, y, mask);
/* If button mask unchanged, just send move event */
if (mask == rdp_client->mouse_button_mask) {
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
/* Otherwise, send events describing button change */
else {
/* Mouse buttons which have JUST become released */
int released_mask = rdp_client->mouse_button_mask & ~mask;
/* Mouse buttons which have JUST become pressed */
int pressed_mask = ~rdp_client->mouse_button_mask & mask;
/* Release event */
if (released_mask & 0x07) {
/* Calculate flags */
int flags = 0;
if (released_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
/* Press event */
if (pressed_mask & 0x07) {
/* Calculate flags */
int flags = PTR_FLAGS_DOWN;
if (pressed_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
if (pressed_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
if (pressed_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
if (pressed_mask & 0x08) flags |= PTR_FLAGS_WHEEL | 0x78;
if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88;
/* Send event */
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
/* Scroll event */
if (pressed_mask & 0x18) {
/* Down */
if (pressed_mask & 0x08) {
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_WHEEL | 0x78, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
/* Up */
if (pressed_mask & 0x10) {
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
}
rdp_client->mouse_button_mask = mask;
}
complete:
pthread_rwlock_unlock(&(rdp_client->lock));
return 0;
}
int guac_rdp_user_touch_handler(guac_user* user, int id, int x, int y,
int x_radius, int y_radius, double angle, double force) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
pthread_rwlock_rdlock(&(rdp_client->lock));
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
if (rdp_inst == NULL)
goto complete;
/* Report touch event within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_touch(rdp_client->recording, id, x, y,
x_radius, y_radius, angle, force);
/* Forward touch event along RDPEI channel */
guac_rdp_rdpei_touch_update(rdp_client->rdpei, id, x, y, force);
complete:
pthread_rwlock_unlock(&(rdp_client->lock));
return 0;
}
int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
int retval = 0;
pthread_rwlock_rdlock(&(rdp_client->lock));
/* Report key state within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_key(rdp_client->recording,
keysym, pressed);
/* Skip if keyboard not yet ready */
if (rdp_client->keyboard == NULL)
goto complete;
/* Update keysym state */
retval = guac_rdp_keyboard_update_keysym(rdp_client->keyboard,
keysym, pressed, GUAC_RDP_KEY_SOURCE_CLIENT);
complete:
pthread_rwlock_unlock(&(rdp_client->lock));
return retval;
}
int guac_rdp_user_size_handler(guac_user* user, int width, int height) {
guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_settings* settings = rdp_client->settings;
freerdp* rdp_inst = rdp_client->rdp_inst;
/* Convert client pixels to remote pixels */
width = width * settings->resolution / user->info.optimal_resolution;
height = height * settings->resolution / user->info.optimal_resolution;
/* Send display update */
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height);
return 0;
}