/* * Copyright (C) 2020-2023 Pascal Nowack * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "grd-session-rdp.h" #include #include #include #include #include #include #include #include #include #include #include "grd-clipboard-rdp.h" #include "grd-context.h" #include "grd-rdp-cursor-renderer.h" #include "grd-rdp-dvc-audio-input.h" #include "grd-rdp-dvc-audio-playback.h" #include "grd-rdp-dvc-camera-enumerator.h" #include "grd-rdp-dvc-display-control.h" #include "grd-rdp-dvc-graphics-pipeline.h" #include "grd-rdp-dvc-handler.h" #include "grd-rdp-dvc-input.h" #include "grd-rdp-dvc-telemetry.h" #include "grd-rdp-event-queue.h" #include "grd-rdp-layout-manager.h" #include "grd-rdp-network-autodetection.h" #include "grd-rdp-private.h" #include "grd-rdp-renderer.h" #include "grd-rdp-sam.h" #include "grd-rdp-server.h" #include "grd-rdp-session-metrics.h" #include "grd-settings.h" #include "grd-utils.h" #define MAX_MONITOR_COUNT_HEADLESS 16 #define MAX_MONITOR_COUNT_SCREEN_SHARE 1 #define DISCRETE_SCROLL_STEP 10.0 #define ELEMENT_TYPE_CERTIFICATE 32 enum { POST_CONNECTED, N_SIGNALS }; static guint signals[N_SIGNALS]; typedef enum _RdpPeerFlag { RDP_PEER_ACTIVATED = 1 << 0, } RdpPeerFlag; typedef enum _PauseKeyState { PAUSE_KEY_STATE_NONE, PAUSE_KEY_STATE_CTRL_DOWN, PAUSE_KEY_STATE_NUMLOCK_DOWN, PAUSE_KEY_STATE_CTRL_UP, } PauseKeyState; struct _GrdSessionRdp { GrdSession parent; GrdRdpServer *server; GSocketConnection *connection; freerdp_peer *peer; GrdRdpSAMFile *sam_file; uint32_t rdp_error_info; GrdRdpScreenShareMode screen_share_mode; gboolean is_view_only; gboolean session_should_stop; GrdRdpSessionMetrics *session_metrics; GMutex rdp_flags_mutex; RdpPeerFlag rdp_flags; GThread *socket_thread; HANDLE stop_event; GrdRdpRenderer *renderer; GrdRdpCursorRenderer *cursor_renderer; GHashTable *pressed_keys; GHashTable *pressed_unicode_keys; PauseKeyState pause_key_state; GrdRdpEventQueue *rdp_event_queue; GrdRdpLayoutManager *layout_manager; GHashTable *stream_table; GMutex close_session_mutex; unsigned int close_session_idle_id; GMutex notify_post_connected_mutex; unsigned int notify_post_connected_source_id; uint32_t next_stream_id; }; G_DEFINE_TYPE (GrdSessionRdp, grd_session_rdp, GRD_TYPE_SESSION) G_DEFINE_AUTOPTR_CLEANUP_FUNC (rdpCertificate, freerdp_certificate_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (rdpRedirection, redirection_free) static gboolean close_session_idle (gpointer user_data); GrdRdpServer * grd_session_rdp_get_server (GrdSessionRdp *session_rdp) { return session_rdp->server; } GrdRdpRenderer * grd_session_rdp_get_renderer (GrdSessionRdp *session_rdp) { return session_rdp->renderer; } GrdRdpCursorRenderer * grd_session_rdp_get_cursor_renderer (GrdSessionRdp *session_rdp) { return session_rdp->cursor_renderer; } rdpContext * grd_session_rdp_get_rdp_context (GrdSessionRdp *session_rdp) { return session_rdp->peer->context; } GrdRdpDvcGraphicsPipeline * grd_session_rdp_get_graphics_pipeline (GrdSessionRdp *session_rdp) { rdpContext *rdp_context = grd_session_rdp_get_rdp_context (session_rdp); RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context; return rdp_peer_context->graphics_pipeline; } GrdRdpScreenShareMode grd_session_rdp_get_screen_share_mode (GrdSessionRdp *session_rdp) { return session_rdp->screen_share_mode; } static gboolean is_rdp_peer_flag_set (GrdSessionRdp *session_rdp, RdpPeerFlag flag) { gboolean state; g_mutex_lock (&session_rdp->rdp_flags_mutex); state = !!(session_rdp->rdp_flags & flag); g_mutex_unlock (&session_rdp->rdp_flags_mutex); return state; } static void set_rdp_peer_flag (GrdSessionRdp *session_rdp, RdpPeerFlag flag) { g_mutex_lock (&session_rdp->rdp_flags_mutex); session_rdp->rdp_flags |= flag; g_mutex_unlock (&session_rdp->rdp_flags_mutex); } static void unset_rdp_peer_flag (GrdSessionRdp *session_rdp, RdpPeerFlag flag) { g_mutex_lock (&session_rdp->rdp_flags_mutex); session_rdp->rdp_flags &= ~flag; g_mutex_unlock (&session_rdp->rdp_flags_mutex); } GrdRdpSessionMetrics * grd_session_rdp_get_session_metrics (GrdSessionRdp *session_rdp) { return session_rdp->session_metrics; } static uint32_t get_next_free_stream_id (GrdSessionRdp *session_rdp) { uint32_t stream_id = session_rdp->next_stream_id; while (g_hash_table_contains (session_rdp->stream_table, GUINT_TO_POINTER (stream_id))) ++stream_id; session_rdp->next_stream_id = stream_id + 1; return stream_id; } uint32_t grd_session_rdp_acquire_stream_id (GrdSessionRdp *session_rdp, GrdRdpStreamOwner *stream_owner) { uint32_t stream_id; stream_id = get_next_free_stream_id (session_rdp); g_hash_table_insert (session_rdp->stream_table, GUINT_TO_POINTER (stream_id), stream_owner); return stream_id; } void grd_session_rdp_release_stream_id (GrdSessionRdp *session_rdp, uint32_t stream_id) { g_hash_table_remove (session_rdp->stream_table, GUINT_TO_POINTER (stream_id)); } gboolean grd_session_rdp_is_client_mstsc (GrdSessionRdp *session_rdp) { rdpContext *rdp_context = session_rdp->peer->context; rdpSettings *rdp_settings = rdp_context->settings; return freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType) == OSMAJORTYPE_WINDOWS && freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMinorType) == OSMINORTYPE_WINDOWS_NT; } static WCHAR * get_utf16_string (const char *str, size_t *size) { WCHAR *utf16_string; *size = 0; utf16_string = ConvertUtf8ToWCharAlloc (str, size); if (!utf16_string) return NULL; *size = (*size + 1) * sizeof (WCHAR); return utf16_string; } static WCHAR * generate_encoded_redirection_guid (size_t *size) { BYTE redirection_guid[16] = {}; g_autofree char *redirection_guid_base_64 = NULL; WCHAR *encoded_redirection_guid; *size = 0; if (winpr_RAND (redirection_guid, 16) == -1) return NULL; redirection_guid_base_64 = crypto_base64_encode (redirection_guid, 16); if (!redirection_guid_base_64) return NULL; encoded_redirection_guid = get_utf16_string (redirection_guid_base_64, size); if (!encoded_redirection_guid) return NULL; return encoded_redirection_guid; } static BYTE * get_certificate_container (const char *certificate, size_t *size) { g_autofree BYTE *der_certificate = NULL; g_autoptr (rdpCertificate) rdp_certificate = NULL; BYTE *certificate_container = NULL; size_t der_certificate_len = 0; wStream *s; *size = 0; rdp_certificate = freerdp_certificate_new_from_pem (certificate); if (!rdp_certificate) return NULL; der_certificate = freerdp_certificate_get_der (rdp_certificate, &der_certificate_len); if (!der_certificate) return NULL; s = Stream_New (NULL, 2048); g_assert (s); if (!Stream_EnsureRemainingCapacity (s, 12)) g_assert_not_reached (); Stream_Write_UINT32 (s, ELEMENT_TYPE_CERTIFICATE); Stream_Write_UINT32 (s, ENCODING_TYPE_ASN1_DER); Stream_Write_UINT32 (s, der_certificate_len); if (!Stream_EnsureRemainingCapacity (s, der_certificate_len)) g_assert_not_reached (); Stream_Write (s, der_certificate, der_certificate_len); *size = Stream_GetPosition (s); certificate_container = Stream_Buffer (s); Stream_Free (s, FALSE); return certificate_container; } gboolean grd_session_rdp_send_server_redirection (GrdSessionRdp *session_rdp, const char *routing_token, const char *username, const char *password, const char *certificate) { freerdp_peer *peer = session_rdp->peer; rdpSettings *rdp_settings = peer->context->settings; uint32_t os_major_type = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType); g_autoptr (rdpRedirection) redirection = NULL; g_autofree BYTE *certificate_container = NULL; g_autofree WCHAR *utf16_password = NULL; g_autofree WCHAR *utf16_encoded_redirection_guid = NULL; size_t size = 0; uint32_t redirection_flags = 0; uint32_t redirection_incorrect_flags = 0; g_assert (routing_token); g_assert (username); g_assert (password); g_assert (certificate); redirection = redirection_new (); g_assert (redirection); /* Load Balance Info */ redirection_flags |= LB_LOAD_BALANCE_INFO; redirection_set_byte_option (redirection, LB_LOAD_BALANCE_INFO, (BYTE *) routing_token, strlen (routing_token)); /* Username */ redirection_flags |= LB_USERNAME; redirection_set_string_option (redirection, LB_USERNAME, username); /* Password */ redirection_flags |= LB_PASSWORD; if (os_major_type != OSMAJORTYPE_IOS && os_major_type != OSMAJORTYPE_ANDROID) redirection_flags |= LB_PASSWORD_IS_PK_ENCRYPTED; utf16_password = get_utf16_string (password, &size); g_assert (utf16_password); redirection_set_byte_option (redirection, LB_PASSWORD, (BYTE *) utf16_password, size); /* Redirection GUID */ redirection_flags |= LB_REDIRECTION_GUID; utf16_encoded_redirection_guid = generate_encoded_redirection_guid (&size); g_assert (utf16_encoded_redirection_guid); redirection_set_byte_option (redirection, LB_REDIRECTION_GUID, (BYTE *) utf16_encoded_redirection_guid, size); /* Target Certificate */ redirection_flags |= LB_TARGET_CERTIFICATE; certificate_container = get_certificate_container (certificate, &size); g_assert (certificate_container); redirection_set_byte_option (redirection, LB_TARGET_CERTIFICATE, certificate_container, size); redirection_set_flags (redirection, redirection_flags); if (!redirection_settings_are_valid (redirection, &redirection_incorrect_flags)) { g_warning ("[RDP] Something went wrong sending Server Redirection PDU. " "Incorrect flag/s: 0x%08x", redirection_incorrect_flags); return FALSE; } g_message ("[RDP] Sending server redirection"); if (!peer->SendServerRedirection (peer, redirection)) { g_warning ("[RDP] Error sending server Redirection"); return FALSE; } return TRUE; } static void maybe_queue_close_session_idle (GrdSessionRdp *session_rdp) { g_mutex_lock (&session_rdp->close_session_mutex); if (session_rdp->close_session_idle_id) { g_mutex_unlock (&session_rdp->close_session_mutex); return; } session_rdp->close_session_idle_id = g_idle_add (close_session_idle, session_rdp); g_mutex_unlock (&session_rdp->close_session_mutex); session_rdp->session_should_stop = TRUE; SetEvent (session_rdp->stop_event); } void grd_session_rdp_notify_error (GrdSessionRdp *session_rdp, GrdSessionRdpError error_info) { switch (error_info) { case GRD_SESSION_RDP_ERROR_NONE: g_assert_not_reached (); break; case GRD_SESSION_RDP_ERROR_BAD_CAPS: session_rdp->rdp_error_info = ERRINFO_BAD_CAPABILITIES; break; case GRD_SESSION_RDP_ERROR_BAD_MONITOR_DATA: session_rdp->rdp_error_info = ERRINFO_BAD_MONITOR_DATA; break; case GRD_SESSION_RDP_ERROR_CLOSE_STACK_ON_DRIVER_FAILURE: session_rdp->rdp_error_info = ERRINFO_CLOSE_STACK_ON_DRIVER_FAILURE; break; case GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED: session_rdp->rdp_error_info = ERRINFO_GRAPHICS_SUBSYSTEM_FAILED; break; case GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION: session_rdp->rdp_error_info = ERRINFO_CB_CONNECTION_CANCELLED; break; } unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED); maybe_queue_close_session_idle (session_rdp); } void grd_session_rdp_tear_down_channel (GrdSessionRdp *session_rdp, GrdRdpChannel channel) { freerdp_peer *peer = session_rdp->peer; RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context; g_mutex_lock (&rdp_peer_context->channel_mutex); switch (channel) { case GRD_RDP_CHANNEL_NONE: g_assert_not_reached (); break; case GRD_RDP_CHANNEL_AUDIO_INPUT: g_clear_object (&rdp_peer_context->audio_input); break; case GRD_RDP_CHANNEL_AUDIO_PLAYBACK: g_clear_object (&rdp_peer_context->audio_playback); break; case GRD_RDP_CHANNEL_CAMERA: g_assert_not_reached (); break; case GRD_RDP_CHANNEL_CAMERA_ENUMERATOR: g_clear_object (&rdp_peer_context->camera_enumerator); break; case GRD_RDP_CHANNEL_DISPLAY_CONTROL: g_clear_object (&rdp_peer_context->display_control); break; case GRD_RDP_CHANNEL_GRAPHICS_PIPELINE: g_assert_not_reached (); break; case GRD_RDP_CHANNEL_INPUT: g_clear_object (&rdp_peer_context->input); break; case GRD_RDP_CHANNEL_TELEMETRY: g_clear_object (&rdp_peer_context->telemetry); break; } g_mutex_unlock (&rdp_peer_context->channel_mutex); } static void handle_client_gone (GrdSessionRdp *session_rdp) { g_debug ("RDP client gone"); unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED); maybe_queue_close_session_idle (session_rdp); } static gboolean notify_keycode_released (gpointer key, gpointer value, gpointer user_data) { GrdSessionRdp *session_rdp = user_data; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; uint32_t keycode = GPOINTER_TO_UINT (key); grd_rdp_event_queue_add_input_event_keyboard_keycode (rdp_event_queue, keycode, GRD_KEY_STATE_RELEASED); return TRUE; } static gboolean notify_keysym_released (gpointer key, gpointer value, gpointer user_data) { GrdSessionRdp *session_rdp = user_data; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; xkb_keysym_t keysym = GPOINTER_TO_UINT (key); grd_rdp_event_queue_add_input_event_keyboard_keysym (rdp_event_queue, keysym, GRD_KEY_STATE_RELEASED); return TRUE; } static BOOL rdp_input_synchronize_event (rdpInput *rdp_input, uint32_t flags) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; g_debug ("[RDP] Received Synchronize event with flags 0x%08X", flags); if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) || session_rdp->is_view_only) return TRUE; g_hash_table_foreach_remove (session_rdp->pressed_keys, notify_keycode_released, session_rdp); g_hash_table_foreach_remove (session_rdp->pressed_unicode_keys, notify_keysym_released, session_rdp); grd_rdp_event_queue_add_synchronization_event (rdp_event_queue, !!(flags & KBD_SYNC_CAPS_LOCK), !!(flags & KBD_SYNC_NUM_LOCK)); return TRUE; } static BOOL rdp_input_mouse_event (rdpInput *rdp_input, uint16_t flags, uint16_t x, uint16_t y) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; GrdEventMotionAbs motion_abs = {}; GrdStream *stream = NULL; GrdButtonState button_state; int32_t button = 0; uint16_t axis_value; double axis_step; if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) || session_rdp->is_view_only) return TRUE; if (!(flags & PTR_FLAGS_WHEEL) && !(flags & PTR_FLAGS_HWHEEL) && grd_rdp_layout_manager_transform_position (session_rdp->layout_manager, x, y, &stream, &motion_abs)) { grd_rdp_event_queue_add_input_event_pointer_motion_abs (rdp_event_queue, stream, &motion_abs); } button_state = flags & PTR_FLAGS_DOWN ? GRD_BUTTON_STATE_PRESSED : GRD_BUTTON_STATE_RELEASED; if (flags & PTR_FLAGS_BUTTON1) button = BTN_LEFT; else if (flags & PTR_FLAGS_BUTTON2) button = BTN_RIGHT; else if (flags & PTR_FLAGS_BUTTON3) button = BTN_MIDDLE; if (button) { grd_rdp_event_queue_add_input_event_pointer_button (rdp_event_queue, button, button_state); } if (!(flags & PTR_FLAGS_WHEEL) && !(flags & PTR_FLAGS_HWHEEL)) return TRUE; axis_value = flags & WheelRotationMask; if (axis_value & PTR_FLAGS_WHEEL_NEGATIVE) { axis_value = ~axis_value & WheelRotationMask; ++axis_value; } axis_step = -axis_value / 120.0; if (flags & PTR_FLAGS_WHEEL_NEGATIVE) axis_step = -axis_step; if (flags & PTR_FLAGS_WHEEL) { grd_rdp_event_queue_add_input_event_pointer_axis ( rdp_event_queue, 0, axis_step * DISCRETE_SCROLL_STEP, GRD_POINTER_AXIS_FLAGS_SOURCE_WHEEL); } if (flags & PTR_FLAGS_HWHEEL) { grd_rdp_event_queue_add_input_event_pointer_axis ( rdp_event_queue, -axis_step * DISCRETE_SCROLL_STEP, 0, GRD_POINTER_AXIS_FLAGS_SOURCE_WHEEL); } return TRUE; } static BOOL rdp_input_extended_mouse_event (rdpInput *rdp_input, uint16_t flags, uint16_t x, uint16_t y) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; GrdButtonState button_state; int32_t button = 0; if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) || session_rdp->is_view_only) return TRUE; rdp_input_mouse_event (rdp_input, PTR_FLAGS_MOVE, x, y); button_state = flags & PTR_XFLAGS_DOWN ? GRD_BUTTON_STATE_PRESSED : GRD_BUTTON_STATE_RELEASED; if (flags & PTR_XFLAGS_BUTTON1) button = BTN_SIDE; else if (flags & PTR_XFLAGS_BUTTON2) button = BTN_EXTRA; if (button) { grd_rdp_event_queue_add_input_event_pointer_button (rdp_event_queue, button, button_state); } return TRUE; } static BOOL rdp_input_rel_mouse_event (rdpInput *rdp_input, uint16_t flags, int16_t dx, int16_t dy) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; GrdButtonState button_state; int32_t button = 0; grd_rdp_event_queue_add_input_event_pointer_motion (rdp_event_queue, dx, dy); button_state = flags & PTR_FLAGS_DOWN ? GRD_BUTTON_STATE_PRESSED : GRD_BUTTON_STATE_RELEASED; if (flags & PTR_FLAGS_BUTTON1) button = BTN_LEFT; else if (flags & PTR_FLAGS_BUTTON2) button = BTN_RIGHT; else if (flags & PTR_FLAGS_BUTTON3) button = BTN_MIDDLE; else if (flags & PTR_XFLAGS_BUTTON1) button = BTN_SIDE; else if (flags & PTR_XFLAGS_BUTTON2) button = BTN_EXTRA; if (button) { grd_rdp_event_queue_add_input_event_pointer_button (rdp_event_queue, button, button_state); } return TRUE; } static gboolean is_pause_key_sequence (GrdSessionRdp *session_rdp, uint16_t vkcode, uint16_t flags) { GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; switch (session_rdp->pause_key_state) { case PAUSE_KEY_STATE_NONE: if (vkcode == VK_LCONTROL && !(flags & KBD_FLAGS_RELEASE) && flags & KBD_FLAGS_EXTENDED1) { session_rdp->pause_key_state = PAUSE_KEY_STATE_CTRL_DOWN; return TRUE; } return FALSE; case PAUSE_KEY_STATE_CTRL_DOWN: if (vkcode == VK_NUMLOCK && !(flags & KBD_FLAGS_RELEASE)) { session_rdp->pause_key_state = PAUSE_KEY_STATE_NUMLOCK_DOWN; return TRUE; } break; case PAUSE_KEY_STATE_NUMLOCK_DOWN: if (vkcode == VK_LCONTROL && flags & KBD_FLAGS_RELEASE && flags & KBD_FLAGS_EXTENDED1) { session_rdp->pause_key_state = PAUSE_KEY_STATE_CTRL_UP; return TRUE; } break; case PAUSE_KEY_STATE_CTRL_UP: if (vkcode == VK_NUMLOCK && flags & KBD_FLAGS_RELEASE) { session_rdp->pause_key_state = PAUSE_KEY_STATE_NONE; grd_rdp_event_queue_add_input_event_keyboard_keysym ( rdp_event_queue, XKB_KEY_Pause, GRD_KEY_STATE_PRESSED); grd_rdp_event_queue_add_input_event_keyboard_keysym ( rdp_event_queue, XKB_KEY_Pause, GRD_KEY_STATE_RELEASED); return TRUE; } break; } g_warning ("Received invalid pause key sequence"); session_rdp->pause_key_state = PAUSE_KEY_STATE_NONE; return FALSE; } static BOOL rdp_input_keyboard_event (rdpInput *rdp_input, uint16_t flags, uint8_t code) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; rdpSettings *rdp_settings = rdp_input->context->settings; uint32_t keyboard_type = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_KeyboardType); GrdKeyState key_state; uint16_t scancode; uint16_t fullcode; uint16_t vkcode; uint16_t keycode; if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) || session_rdp->is_view_only) return TRUE; scancode = code; fullcode = flags & KBD_FLAGS_EXTENDED ? scancode | KBDEXT : scancode; vkcode = GetVirtualKeyCodeFromVirtualScanCode (fullcode, keyboard_type); vkcode = flags & KBD_FLAGS_EXTENDED ? vkcode | KBDEXT : vkcode; keycode = GetKeycodeFromVirtualKeyCode (vkcode, WINPR_KEYCODE_TYPE_EVDEV); key_state = flags & KBD_FLAGS_RELEASE ? GRD_KEY_STATE_RELEASED : GRD_KEY_STATE_PRESSED; if (is_pause_key_sequence (session_rdp, vkcode, flags)) return TRUE; if (key_state == GRD_KEY_STATE_PRESSED) { if (!g_hash_table_add (session_rdp->pressed_keys, GUINT_TO_POINTER (keycode))) return TRUE; } else { if (!g_hash_table_remove (session_rdp->pressed_keys, GUINT_TO_POINTER (keycode))) return TRUE; } grd_rdp_event_queue_add_input_event_keyboard_keycode (rdp_event_queue, keycode, key_state); return TRUE; } static BOOL rdp_input_unicode_keyboard_event (rdpInput *rdp_input, uint16_t flags, uint16_t code_utf16) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; uint32_t *code_utf32; xkb_keysym_t keysym; GrdKeyState key_state; if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) || session_rdp->is_view_only) return TRUE; code_utf32 = g_utf16_to_ucs4 (&code_utf16, 1, NULL, NULL, NULL); if (!code_utf32) return TRUE; keysym = xkb_utf32_to_keysym (*code_utf32); g_free (code_utf32); key_state = flags & KBD_FLAGS_RELEASE ? GRD_KEY_STATE_RELEASED : GRD_KEY_STATE_PRESSED; if (key_state == GRD_KEY_STATE_PRESSED) { if (!g_hash_table_add (session_rdp->pressed_unicode_keys, GUINT_TO_POINTER (keysym))) return TRUE; } else { if (!g_hash_table_remove (session_rdp->pressed_unicode_keys, GUINT_TO_POINTER (keysym))) return TRUE; } grd_rdp_event_queue_add_input_event_keyboard_keysym (rdp_event_queue, keysym, key_state); return TRUE; } static BOOL rdp_suppress_output (rdpContext *rdp_context, uint8_t allow, const RECTANGLE_16 *area) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; rdpSettings *rdp_settings = rdp_context->settings; if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED)) return TRUE; grd_rdp_renderer_update_output_suppression_state (session_rdp->renderer, !allow); if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) && rdp_peer_context->network_autodetection) { if (allow) { grd_rdp_network_autodetection_ensure_rtt_consumer ( rdp_peer_context->network_autodetection, GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX); } else { grd_rdp_network_autodetection_remove_rtt_consumer ( rdp_peer_context->network_autodetection, GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX); } } return TRUE; } static FREERDP_AUTODETECT_STATE rdp_autodetect_on_connect_time_autodetect_begin (rdpAutoDetect *rdp_autodetect) { rdpContext *rdp_context = rdp_autodetect->context; RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context; rdpSettings *rdp_settings = rdp_context->settings; GrdRdpNetworkAutodetection *network_autodetection; g_assert (freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect)); g_assert (!rdp_peer_context->network_autodetection); network_autodetection = grd_rdp_network_autodetection_new (rdp_context); rdp_peer_context->network_autodetection = network_autodetection; grd_rdp_network_autodetection_start_connect_time_autodetection (network_autodetection); return FREERDP_AUTODETECT_STATE_REQUEST; } static gboolean is_using_remote_login (GrdSessionRdp *session_rdp) { GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp)); switch (grd_context_get_runtime_mode (context)) { case GRD_RUNTIME_MODE_SCREEN_SHARE: case GRD_RUNTIME_MODE_HEADLESS: return FALSE; case GRD_RUNTIME_MODE_SYSTEM: case GRD_RUNTIME_MODE_HANDOVER: return TRUE; } g_assert_not_reached (); } static GrdRdpAuthMethods get_effective_auth_method (rdpContext *rdp_context) { GrdRdpAuthMethods auth_method = -1; SECURITY_STATUS status; SecPkgContext_PackageInfo package_info = {}; status = freerdp_nla_QueryContextAttributes (rdp_context, SECPKG_ATTR_PACKAGE_INFO, &package_info); if (status != SEC_E_OK) { g_warning ("Failed to query NLA context package info: %s", GetSecurityStatusString (status)); return FALSE; } if (g_strcmp0 (package_info.PackageInfo->Name, KERBEROS_SSP_NAME) == 0) auth_method = GRD_RDP_AUTH_METHOD_KERBEROS; else if (g_strcmp0 (package_info.PackageInfo->Name, NTLM_SSP_NAME) == 0) auth_method = GRD_RDP_AUTH_METHOD_CREDENTIALS; else g_warning ("Unknown NLA context package '%s'", package_info.PackageInfo->Name); freerdp_nla_FreeContextBuffer (rdp_context, package_info.PackageInfo); return auth_method; } static gboolean is_auth_identity_current_user (const SecPkgContext_AuthIdentity *auth_identity) { krb5_error_code kret; g_autofree char *principal_string = NULL; krb5_context krb5_context = NULL; krb5_principal principal = NULL; char local_name[256 + 1]; g_autofree struct passwd *pwd = NULL; g_autoptr (GError) error = NULL; uid_t current_uid; kret = krb5_init_context (&krb5_context); if (kret) { g_critical ("Failed to initialize krb5 context"); return FALSE; } principal_string = g_strdup_printf ("%s@%s", auth_identity->User, auth_identity->Domain); kret = krb5_parse_name (krb5_context, principal_string, &principal); if (kret) { g_critical ("Failed to parse principal string %s", principal_string); goto err; } kret = krb5_aname_to_localname (krb5_context, principal, sizeof (local_name), local_name); if (kret) { g_critical ("Failed to map principal '%s' name to local name", principal_string); return FALSE; goto err; } pwd = g_unix_get_passwd_entry (local_name, &error); if (error) { g_critical ("Failed to get passwd field for %s: %s", local_name, error->message); goto err; } if (!pwd) { g_warning ("Tried to authenticate with invalid user %s", local_name); goto err; } krb5_free_principal (krb5_context, principal); krb5_free_context (krb5_context); current_uid = getuid (); if (pwd->pw_uid == current_uid) { g_debug ("[RDP] Kerberos principal %s match current user %s (%u), " "accepting.", principal_string, local_name, current_uid); return TRUE; } else { return FALSE; } err: if (principal) krb5_free_principal (krb5_context, principal); if (krb5_context) krb5_free_context (krb5_context); return FALSE; } static BOOL rdp_peer_logon (freerdp_peer *peer, const SEC_WINNT_AUTH_IDENTITY *identity, BOOL automatic) { rdpContext *rdp_context = peer->context; RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp)); GrdRdpAuthMethods auth_method; SECURITY_STATUS status; SecPkgContext_AuthIdentity auth_identity = {}; if (is_using_remote_login (session_rdp)) return TRUE; auth_method = get_effective_auth_method (rdp_context); if (auth_method == -1) return FALSE; switch (auth_method) { case GRD_RDP_AUTH_METHOD_CREDENTIALS: g_debug ("[RDP] Authenticated using NTLM, " "not applying any additional policy."); return TRUE; case GRD_RDP_AUTH_METHOD_KERBEROS: break; } status = freerdp_nla_QueryContextAttributes (rdp_context, SECPKG_ATTR_AUTH_IDENTITY, &auth_identity); if (status != SEC_E_OK) { g_warning ("Failed to authenticate Kerberos principal: %s", GetSecurityStatusString (status)); return FALSE; } switch (grd_context_get_runtime_mode (context)) { case GRD_RUNTIME_MODE_HEADLESS: case GRD_RUNTIME_MODE_SCREEN_SHARE: if (is_auth_identity_current_user (&auth_identity)) { return TRUE; } else { g_debug ("[RDP] Kerberos principal doesn't match current user, " "disconnecting"); return FALSE; } case GRD_RUNTIME_MODE_SYSTEM: case GRD_RUNTIME_MODE_HANDOVER: g_assert_not_reached (); } g_assert_not_reached (); } static uint32_t get_max_monitor_count (GrdSessionRdp *session_rdp) { GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp)); switch (grd_context_get_runtime_mode (context)) { case GRD_RUNTIME_MODE_HEADLESS: case GRD_RUNTIME_MODE_SYSTEM: case GRD_RUNTIME_MODE_HANDOVER: return MAX_MONITOR_COUNT_HEADLESS; case GRD_RUNTIME_MODE_SCREEN_SHARE: return MAX_MONITOR_COUNT_SCREEN_SHARE; } g_assert_not_reached (); } static BOOL rdp_peer_capabilities (freerdp_peer *peer) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; rdpSettings *rdp_settings = peer->context->settings; HANDLE vcm = rdp_peer_context->vcm; GrdRdpMonitorConfig *monitor_config; g_autoptr (GError) error = NULL; if (!freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline)) { g_warning ("[RDP] Client did not advertise support for the Graphics " "Pipeline, closing connection"); return FALSE; } if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) || freerdp_settings_get_bool (rdp_settings, FreeRDP_RemoteFxCodec) || freerdp_settings_get_bool (rdp_settings, FreeRDP_RemoteFxImageCodec) || freerdp_settings_get_bool (rdp_settings, FreeRDP_NSCodec)) { uint16_t supported_color_depths = freerdp_settings_get_uint16 (rdp_settings, FreeRDP_SupportedColorDepths); if (!(supported_color_depths & RNS_UD_32BPP_SUPPORT)) { g_warning ("[RDP] Protocol violation: Client advertised support for " "codecs or the Graphics Pipeline, but does not support " "32-bit colour depth"); return FALSE; } if (freerdp_settings_get_uint32 (rdp_settings, FreeRDP_ColorDepth) != 32) { g_debug ("[RDP] Client prefers colour depth %u, invalidated by support " "of codecs or graphics pipeline", freerdp_settings_get_uint32 (rdp_settings, FreeRDP_ColorDepth)); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_ColorDepth, 32); } } if (!freerdp_settings_get_bool (rdp_settings, FreeRDP_DesktopResize)) { g_warning ("Client doesn't support desktop resizing, closing connection"); return FALSE; } if (!WTSVirtualChannelManagerIsChannelJoined (vcm, DRDYNVC_SVC_CHANNEL_NAME)) { g_warning ("[RDP] Client doesn't support the DRDYNVC SVC, " "closing connection"); return FALSE; } if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND) { uint32_t max_monitor_count; max_monitor_count = get_max_monitor_count (session_rdp); monitor_config = grd_rdp_monitor_config_new_from_client_data (rdp_settings, max_monitor_count, &error); } else { g_autoptr (GStrvBuilder) connector_builder = NULL; char **connectors; monitor_config = g_new0 (GrdRdpMonitorConfig, 1); connector_builder = g_strv_builder_new (); g_strv_builder_add (connector_builder, ""); connectors = g_strv_builder_end (connector_builder); monitor_config->connectors = connectors; monitor_config->monitor_count = 1; } if (!monitor_config) { g_warning ("[RDP] Received invalid monitor layout from client: %s, " "closing connection", error->message); return FALSE; } if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND) { freerdp_settings_set_uint32 (rdp_settings, FreeRDP_DesktopWidth, monitor_config->desktop_width); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_DesktopHeight, monitor_config->desktop_height); } if (monitor_config->is_virtual) g_debug ("[RDP] Remote Desktop session will use virtual monitors"); else g_debug ("[RDP] Remote Desktop session will mirror the primary monitor"); grd_rdp_layout_manager_submit_new_monitor_config (session_rdp->layout_manager, monitor_config); return TRUE; } static void notify_post_connected (gpointer user_data) { GrdSessionRdp *session_rdp = user_data; GrdContext *grd_context = grd_session_get_context (GRD_SESSION (session_rdp)); g_mutex_lock (&session_rdp->notify_post_connected_mutex); session_rdp->notify_post_connected_source_id = 0; g_mutex_unlock (&session_rdp->notify_post_connected_mutex); grd_rdp_session_metrics_notify_phase_completion (session_rdp->session_metrics, GRD_RDP_PHASE_POST_CONNECT); if (grd_context_get_runtime_mode (grd_context) != GRD_RUNTIME_MODE_SYSTEM) grd_session_start (GRD_SESSION (session_rdp)); g_signal_emit (session_rdp, signals[POST_CONNECTED], 0); } static BOOL rdp_peer_post_connect (freerdp_peer *peer) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; rdpSettings *rdp_settings = peer->context->settings; uint32_t rdp_version = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_RdpVersion); uint32_t keyboard_type = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_KeyboardType); uint32_t os_major_type = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType); if (freerdp_settings_get_uint32 (rdp_settings, FreeRDP_PointerCacheSize) <= 0) { g_warning ("Client doesn't have a pointer cache, closing connection"); return FALSE; } if (!freerdp_settings_get_bool (rdp_settings, FreeRDP_FastPathOutput)) { g_warning ("Client does not support fastpath output, closing connection"); return FALSE; } g_debug ("New RDP client: [OS major type, OS minor type]: [%s, %s]", freerdp_peer_os_major_type_string (peer), freerdp_peer_os_minor_type_string (peer)); g_debug ("[RDP] Maximum common RDP version 0x%08X", rdp_version); g_debug ("[RDP] Client uses keyboard type %u", keyboard_type); g_debug ("[RDP] Virtual Channels: compression flags: %u, " "compression level: %u, chunk size: %u", freerdp_settings_get_uint32 (rdp_settings, FreeRDP_VCFlags), freerdp_settings_get_uint32 (rdp_settings, FreeRDP_CompressionLevel), freerdp_settings_get_uint32 (rdp_settings, FreeRDP_VCChunkSize)); if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) && !freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect)) { g_warning ("Client does not support autodetecting network characteristics " "(RTT detection, Bandwidth measurement). " "High latency connections will suffer!"); } if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioPlayback) && !freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect)) { g_warning ("[RDP] Client does not support autodetecting network " "characteristics. Disabling audio output redirection"); freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioPlayback, FALSE); } if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioPlayback) && (os_major_type == OSMAJORTYPE_IOS || os_major_type == OSMAJORTYPE_ANDROID)) { g_warning ("[RDP] Client cannot handle graphics and audio " "simultaneously. Disabling audio output redirection"); freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioPlayback, FALSE); } if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline)) grd_rdp_renderer_notify_graphics_pipeline_reset (session_rdp->renderer); g_clear_pointer (&session_rdp->sam_file, grd_rdp_sam_free_sam_file); if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) && rdp_peer_context->network_autodetection) { grd_rdp_network_autodetection_ensure_rtt_consumer ( rdp_peer_context->network_autodetection, GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX); } set_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED); g_mutex_lock (&session_rdp->notify_post_connected_mutex); session_rdp->notify_post_connected_source_id = g_idle_add_once (notify_post_connected, session_rdp); g_mutex_unlock (&session_rdp->notify_post_connected_mutex); return TRUE; } static BOOL rdp_peer_activate (freerdp_peer *peer) { g_debug ("Activating client"); return TRUE; } static void rdp_peer_context_free (freerdp_peer *peer, RdpPeerContext *rdp_peer_context) { if (!rdp_peer_context) return; g_clear_object (&rdp_peer_context->dvc_handler); if (rdp_peer_context->vcm != INVALID_HANDLE_VALUE) g_clear_pointer (&rdp_peer_context->vcm, WTSCloseServer); if (rdp_peer_context->encode_stream) { Stream_Free (rdp_peer_context->encode_stream, TRUE); rdp_peer_context->encode_stream = NULL; } g_clear_pointer (&rdp_peer_context->rfx_context, rfx_context_free); g_mutex_clear (&rdp_peer_context->channel_mutex); } static BOOL rdp_peer_context_new (freerdp_peer *peer, RdpPeerContext *rdp_peer_context) { g_mutex_init (&rdp_peer_context->channel_mutex); rdp_peer_context->rfx_context = rfx_context_new (TRUE); if (!rdp_peer_context->rfx_context) { g_warning ("[RDP] Failed to create RFX context"); return FALSE; } #if G_BYTE_ORDER == G_LITTLE_ENDIAN rfx_context_set_pixel_format (rdp_peer_context->rfx_context, PIXEL_FORMAT_BGRX32); #else rfx_context_set_pixel_format (rdp_peer_context->rfx_context, PIXEL_FORMAT_XRGB32); #endif rdp_peer_context->encode_stream = Stream_New (NULL, 64 * 64 * 4); if (!rdp_peer_context->encode_stream) { g_warning ("[RDP] Failed to create encode stream"); return FALSE; } rdp_peer_context->vcm = WTSOpenServerA ((LPSTR) peer->context); if (!rdp_peer_context->vcm || rdp_peer_context->vcm == INVALID_HANDLE_VALUE) { g_warning ("[RDP] Failed to create virtual channel manager"); return FALSE; } rdp_peer_context->dvc_handler = grd_rdp_dvc_handler_new (rdp_peer_context->vcm); return TRUE; } static gboolean init_rdp_session (GrdSessionRdp *session_rdp, const char *username, const char *password, GError **error) { GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp)); GrdSettings *settings = grd_context_get_settings (context); GSocket *socket = g_socket_connection_get_socket (session_rdp->connection); GrdRdpAuthMethods auth_methods = 0; g_autofree char *server_cert = NULL; g_autofree char *server_key = NULL; gboolean use_client_configs; freerdp_peer *peer; rdpInput *rdp_input; rdpAutoDetect *rdp_autodetect; RdpPeerContext *rdp_peer_context; rdpSettings *rdp_settings; rdpCertificate *rdp_certificate = NULL; rdpPrivateKey *rdp_private_key = NULL; use_client_configs = session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND; g_debug ("Initialize RDP session"); peer = freerdp_peer_new (g_socket_get_fd (socket)); if (!peer) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create peer"); return FALSE; } peer->ContextSize = sizeof (RdpPeerContext); peer->ContextFree = (psPeerContextFree) rdp_peer_context_free; peer->ContextNew = (psPeerContextNew) rdp_peer_context_new; if (!freerdp_peer_context_new (peer)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create peer context"); freerdp_peer_free (peer); return FALSE; } session_rdp->peer = peer; rdp_peer_context = (RdpPeerContext *) peer->context; rdp_peer_context->session_rdp = session_rdp; rdp_settings = peer->context->settings; g_object_get (G_OBJECT (settings), "rdp-auth-methods", &auth_methods, NULL); g_assert (auth_methods); if (auth_methods & GRD_RDP_AUTH_METHOD_CREDENTIALS) { session_rdp->sam_file = grd_rdp_sam_create_sam_file (username, password); if (!session_rdp->sam_file) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create SAM database"); return FALSE; } freerdp_settings_set_string (rdp_settings, FreeRDP_NtlmSamFile, session_rdp->sam_file->filename); } if (auth_methods & GRD_RDP_AUTH_METHOD_KERBEROS && !is_using_remote_login (session_rdp)) { g_autofree char *kerberos_keytab = NULL; g_object_get (G_OBJECT (settings), "rdp-kerberos-keytab", &kerberos_keytab, NULL); g_debug ("[RDP] Enabling Kerberos authentication using %s", kerberos_keytab); freerdp_settings_set_string (rdp_settings, FreeRDP_KerberosKeytab, kerberos_keytab); } g_object_get (G_OBJECT (settings), "rdp-server-cert", &server_cert, "rdp-server-key", &server_key, NULL); if (server_cert) rdp_certificate = freerdp_certificate_new_from_pem (server_cert); if (!rdp_certificate) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create certificate from file"); return FALSE; } freerdp_settings_set_pointer_len (rdp_settings, FreeRDP_RdpServerCertificate, rdp_certificate, 1); if (server_key) rdp_private_key = freerdp_key_new_from_pem (server_key); if (!rdp_private_key) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create private key from file"); return FALSE; } freerdp_settings_set_pointer_len (rdp_settings, FreeRDP_RdpServerRsaKey, rdp_private_key, 1); freerdp_settings_set_bool (rdp_settings, FreeRDP_RdpSecurity, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_TlsSecurity, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_NlaSecurity, TRUE); if (grd_context_get_runtime_mode (context) == GRD_RUNTIME_MODE_HANDOVER) { freerdp_settings_set_string (rdp_settings, FreeRDP_Username, username); freerdp_settings_set_string (rdp_settings, FreeRDP_Password, password); freerdp_settings_set_bool (rdp_settings, FreeRDP_RdstlsSecurity, TRUE); } freerdp_settings_set_uint32 (rdp_settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_OsMinorType, OSMINORTYPE_PSEUDO_XSERVER); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_ColorDepth, 32); freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxAVC444v2, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxAVC444, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxH264, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxSmallCache, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxThinClient, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_RemoteFxCodec, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_RemoteFxImageCodec, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_NSCodec, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_FrameMarkerCommandEnabled, TRUE); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_PointerCacheSize, 100); freerdp_settings_set_bool (rdp_settings, FreeRDP_FastPathOutput, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_NetworkAutoDetect, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_RefreshRect, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_SupportMonitorLayoutPdu, use_client_configs); freerdp_settings_set_bool (rdp_settings, FreeRDP_SupportMultitransport, FALSE); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_VCFlags, VCCAPS_COMPR_SC); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_VCChunkSize, 16256); freerdp_settings_set_bool (rdp_settings, FreeRDP_HasExtendedMouseEvent, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_HasHorizontalWheel, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_HasRelativeMouseEvent, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_HasQoeEvent, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_UnicodeInput, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioCapture, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioPlayback, TRUE); freerdp_settings_set_bool (rdp_settings, FreeRDP_RemoteConsoleAudio, TRUE); peer->Logon = rdp_peer_logon; peer->Capabilities = rdp_peer_capabilities; peer->PostConnect = rdp_peer_post_connect; peer->Activate = rdp_peer_activate; peer->context->update->SuppressOutput = rdp_suppress_output; rdp_input = peer->context->input; rdp_input->SynchronizeEvent = rdp_input_synchronize_event; rdp_input->MouseEvent = rdp_input_mouse_event; rdp_input->ExtendedMouseEvent = rdp_input_extended_mouse_event; rdp_input->RelMouseEvent = rdp_input_rel_mouse_event; rdp_input->KeyboardEvent = rdp_input_keyboard_event; rdp_input->UnicodeKeyboardEvent = rdp_input_unicode_keyboard_event; rdp_autodetect = peer->context->autodetect; rdp_autodetect->OnConnectTimeAutoDetectBegin = rdp_autodetect_on_connect_time_autodetect_begin; if (!peer->Initialize (peer)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to initialize peer"); return FALSE; } return TRUE; } gpointer socket_thread_func (gpointer data) { GrdSessionRdp *session_rdp = data; freerdp_peer *peer; RdpPeerContext *rdp_peer_context; HANDLE vcm; HANDLE channel_event; HANDLE events[32] = {}; uint32_t n_events; uint32_t n_freerdp_handles; peer = session_rdp->peer; rdp_peer_context = (RdpPeerContext *) peer->context; vcm = rdp_peer_context->vcm; channel_event = WTSVirtualChannelManagerGetEventHandle (vcm); while (TRUE) { GrdRdpNetworkAutodetection *network_autodetection = rdp_peer_context->network_autodetection; gboolean pending_bw_measure_stop = FALSE; HANDLE bw_measure_stop_event = NULL; n_events = 0; events[n_events++] = session_rdp->stop_event; if (network_autodetection) { bw_measure_stop_event = grd_rdp_network_autodetection_get_bw_measure_stop_event_handle (network_autodetection); events[n_events++] = bw_measure_stop_event; } events[n_events++] = channel_event; n_freerdp_handles = peer->GetEventHandles (peer, &events[n_events], 32 - n_events); if (!n_freerdp_handles) { g_warning ("Failed to get FreeRDP transport event handles"); handle_client_gone (session_rdp); break; } n_events += n_freerdp_handles; WaitForMultipleObjects (n_events, events, FALSE, INFINITE); if (session_rdp->session_should_stop) break; if (!peer->CheckFileDescriptor (peer)) { g_message ("[RDP] Network or intentional disconnect, stopping session"); handle_client_gone (session_rdp); break; } if (peer->connected && WTSVirtualChannelManagerIsChannelJoined (vcm, DRDYNVC_SVC_CHANNEL_NAME)) { GrdRdpDvcTelemetry *telemetry; GrdRdpDvcGraphicsPipeline *graphics_pipeline; GrdRdpDvcInput *input; GrdRdpDvcAudioPlayback *audio_playback; GrdRdpDvcDisplayControl *display_control; GrdRdpDvcAudioInput *audio_input; GrdRdpDvcCameraEnumerator *camera_enumerator; switch (WTSVirtualChannelManagerGetDrdynvcState (vcm)) { case DRDYNVC_STATE_NONE: /* * This ensures that WTSVirtualChannelManagerCheckFileDescriptor() * will be called, which initializes the drdynvc channel */ SetEvent (channel_event); break; case DRDYNVC_STATE_READY: g_mutex_lock (&rdp_peer_context->channel_mutex); telemetry = rdp_peer_context->telemetry; graphics_pipeline = rdp_peer_context->graphics_pipeline; input = rdp_peer_context->input; audio_playback = rdp_peer_context->audio_playback; display_control = rdp_peer_context->display_control; audio_input = rdp_peer_context->audio_input; camera_enumerator = rdp_peer_context->camera_enumerator; if (telemetry && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (telemetry)); if (graphics_pipeline && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (graphics_pipeline)); if (input && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (input)); if (audio_playback && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (audio_playback)); if (display_control && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (display_control)); if (audio_input && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (audio_input)); if (camera_enumerator && !session_rdp->session_should_stop) grd_rdp_dvc_maybe_init (GRD_RDP_DVC (camera_enumerator)); g_mutex_unlock (&rdp_peer_context->channel_mutex); break; } if (session_rdp->session_should_stop) break; } if (bw_measure_stop_event) { pending_bw_measure_stop = WaitForSingleObject (bw_measure_stop_event, 0) == WAIT_OBJECT_0; } if (WaitForSingleObject (channel_event, 0) == WAIT_OBJECT_0 && !WTSVirtualChannelManagerCheckFileDescriptor (vcm)) { g_message ("Unable to check VCM file descriptor, closing connection"); handle_client_gone (session_rdp); break; } if (pending_bw_measure_stop) grd_rdp_network_autodetection_bw_measure_stop (network_autodetection); } return NULL; } static void on_view_only_changed (GrdSettings *settings, GParamSpec *pspec, GrdSessionRdp *session_rdp) { g_object_get (G_OBJECT (settings), "rdp-view-only", &session_rdp->is_view_only, NULL); } GrdSessionRdp * grd_session_rdp_new (GrdRdpServer *rdp_server, GSocketConnection *connection) { g_autoptr (GrdSessionRdp) session_rdp = NULL; GrdContext *context; GrdSettings *settings; char *username; char *password; g_autoptr (GError) error = NULL; context = grd_rdp_server_get_context (rdp_server); settings = grd_context_get_settings (context); if (!grd_settings_get_rdp_credentials (settings, &username, &password, &error)) { if (error) g_warning ("[RDP] Couldn't retrieve RDP credentials: %s", error->message); else g_message ("[RDP] Credentials are not set, denying client"); return NULL; } session_rdp = g_object_new (GRD_TYPE_SESSION_RDP, "context", context, NULL); session_rdp->server = rdp_server; session_rdp->connection = g_object_ref (connection); g_object_get (G_OBJECT (settings), "rdp-screen-share-mode", &session_rdp->screen_share_mode, "rdp-view-only", &session_rdp->is_view_only, NULL); g_signal_connect (settings, "notify::rdp-view-only", G_CALLBACK (on_view_only_changed), session_rdp); session_rdp->renderer = grd_rdp_renderer_new (session_rdp); session_rdp->layout_manager = grd_rdp_layout_manager_new (session_rdp); if (!init_rdp_session (session_rdp, username, password, &error)) { g_warning ("[RDP] Couldn't initialize session: %s", error->message); g_free (password); g_free (username); return NULL; } session_rdp->socket_thread = g_thread_new ("RDP socket thread", socket_thread_func, session_rdp); g_free (password); g_free (username); return g_steal_pointer (&session_rdp); } static gboolean has_session_close_queued (GrdSessionRdp *session_rdp) { gboolean pending_session_closing; g_mutex_lock (&session_rdp->close_session_mutex); pending_session_closing = session_rdp->close_session_idle_id != 0; g_mutex_unlock (&session_rdp->close_session_mutex); return pending_session_closing; } static void clear_rdp_peer (GrdSessionRdp *session_rdp) { g_clear_pointer (&session_rdp->sam_file, grd_rdp_sam_free_sam_file); if (session_rdp->peer) { freerdp_peer_context_free (session_rdp->peer); g_clear_pointer (&session_rdp->peer, freerdp_peer_free); } } static void grd_session_rdp_stop (GrdSession *session) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session); freerdp_peer *peer = session_rdp->peer; RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context; g_debug ("Stopping RDP session"); unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED); session_rdp->session_should_stop = TRUE; SetEvent (session_rdp->stop_event); if (!has_session_close_queued (session_rdp)) { freerdp_set_error_info (peer->context->rdp, ERRINFO_RPC_INITIATED_DISCONNECT); } else if (session_rdp->rdp_error_info) { freerdp_set_error_info (peer->context->rdp, session_rdp->rdp_error_info); } if (rdp_peer_context->network_autodetection) { grd_rdp_network_autodetection_invoke_shutdown ( rdp_peer_context->network_autodetection); } grd_rdp_renderer_invoke_shutdown (session_rdp->renderer); g_mutex_lock (&rdp_peer_context->channel_mutex); g_clear_object (&rdp_peer_context->camera_enumerator); g_clear_object (&rdp_peer_context->audio_input); g_clear_object (&rdp_peer_context->clipboard_rdp); g_clear_object (&rdp_peer_context->audio_playback); g_clear_object (&rdp_peer_context->display_control); g_clear_object (&rdp_peer_context->input); g_clear_object (&rdp_peer_context->graphics_pipeline); g_clear_object (&rdp_peer_context->telemetry); g_mutex_unlock (&rdp_peer_context->channel_mutex); g_clear_pointer (&session_rdp->socket_thread, g_thread_join); g_clear_handle_id (&session_rdp->notify_post_connected_source_id, g_source_remove); g_hash_table_foreach_remove (session_rdp->pressed_keys, notify_keycode_released, session_rdp); g_hash_table_foreach_remove (session_rdp->pressed_unicode_keys, notify_keysym_released, session_rdp); grd_rdp_event_queue_flush (session_rdp->rdp_event_queue); g_clear_object (&session_rdp->layout_manager); g_clear_object (&session_rdp->cursor_renderer); g_clear_object (&session_rdp->renderer); peer->Close (peer); grd_close_connection_and_notify (session_rdp->connection); g_clear_object (&session_rdp->connection); g_clear_object (&rdp_peer_context->network_autodetection); peer->Disconnect (peer); clear_rdp_peer (session_rdp); g_clear_handle_id (&session_rdp->close_session_idle_id, g_source_remove); } static gboolean close_session_idle (gpointer user_data) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (user_data); grd_session_stop (GRD_SESSION (session_rdp)); session_rdp->close_session_idle_id = 0; return G_SOURCE_REMOVE; } static void initialize_graphics_pipeline (GrdSessionRdp *session_rdp) { rdpContext *rdp_context = session_rdp->peer->context; RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context; GrdHwAccelNvidia *hwaccel_nvidia = grd_rdp_server_get_hwaccel_nvidia (session_rdp->server); GrdRdpDvcGraphicsPipeline *graphics_pipeline; GrdRdpDvcTelemetry *telemetry; g_assert (!rdp_peer_context->telemetry); g_assert (!rdp_peer_context->graphics_pipeline); telemetry = grd_rdp_dvc_telemetry_new (session_rdp, rdp_peer_context->dvc_handler, rdp_peer_context->vcm, rdp_context); rdp_peer_context->telemetry = telemetry; graphics_pipeline = grd_rdp_dvc_graphics_pipeline_new (session_rdp, session_rdp->renderer, rdp_peer_context->dvc_handler, rdp_peer_context->vcm, rdp_context, rdp_peer_context->network_autodetection, rdp_peer_context->encode_stream, rdp_peer_context->rfx_context); grd_rdp_dvc_graphics_pipeline_set_hwaccel_nvidia (graphics_pipeline, hwaccel_nvidia); rdp_peer_context->graphics_pipeline = graphics_pipeline; } static void initialize_remaining_virtual_channels (GrdSessionRdp *session_rdp) { rdpContext *rdp_context = session_rdp->peer->context; RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context; rdpSettings *rdp_settings = rdp_context->settings; GrdRdpDvcHandler *dvc_handler = rdp_peer_context->dvc_handler; HANDLE vcm = rdp_peer_context->vcm; rdp_peer_context->input = grd_rdp_dvc_input_new (session_rdp->layout_manager, session_rdp, dvc_handler, vcm); if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND) { rdp_peer_context->display_control = grd_rdp_dvc_display_control_new (session_rdp->layout_manager, session_rdp, dvc_handler, rdp_peer_context->vcm, get_max_monitor_count (session_rdp)); } if (WTSVirtualChannelManagerIsChannelJoined (vcm, CLIPRDR_SVC_CHANNEL_NAME)) { uint32_t os_major_type = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType); gboolean peer_is_on_ms_windows; peer_is_on_ms_windows = os_major_type == OSMAJORTYPE_WINDOWS; rdp_peer_context->clipboard_rdp = grd_clipboard_rdp_new (session_rdp, vcm, !peer_is_on_ms_windows); } if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioPlayback) && !freerdp_settings_get_bool (rdp_settings, FreeRDP_RemoteConsoleAudio)) { rdp_peer_context->audio_playback = grd_rdp_dvc_audio_playback_new (session_rdp, dvc_handler, vcm, rdp_context); } if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioCapture)) { rdp_peer_context->audio_input = grd_rdp_dvc_audio_input_new (session_rdp, dvc_handler, vcm, rdp_context); } rdp_peer_context->camera_enumerator = grd_rdp_dvc_camera_enumerator_new (session_rdp, dvc_handler, vcm, rdp_context); } static void on_remote_desktop_session_ready (GrdSession *session) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session); grd_rdp_session_metrics_notify_phase_completion (session_rdp->session_metrics, GRD_RDP_PHASE_SESSION_READY); session_rdp->cursor_renderer = grd_rdp_cursor_renderer_new (session_rdp->renderer, session_rdp->peer->context); grd_rdp_cursor_renderer_notify_session_ready (session_rdp->cursor_renderer); initialize_graphics_pipeline (session_rdp); initialize_remaining_virtual_channels (session_rdp); } static void on_remote_desktop_session_started (GrdSession *session) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session); if (!grd_rdp_renderer_start (session_rdp->renderer)) { grd_session_rdp_notify_error (session_rdp, GRD_SESSION_RDP_ERROR_CLOSE_STACK_ON_DRIVER_FAILURE); return; } grd_rdp_session_metrics_notify_phase_completion (session_rdp->session_metrics, GRD_RDP_PHASE_SESSION_STARTED); grd_rdp_layout_manager_notify_session_started (session_rdp->layout_manager); grd_rdp_event_queue_flush_synchronization (session_rdp->rdp_event_queue); } static void grd_session_rdp_on_stream_created (GrdSession *session, uint32_t stream_id, GrdStream *stream) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session); GrdRdpStreamOwner *stream_owner = NULL; if (!g_hash_table_lookup_extended (session_rdp->stream_table, GUINT_TO_POINTER (stream_id), NULL, (gpointer) &stream_owner)) g_assert_not_reached (); grd_rdp_stream_owner_notify_stream_created (stream_owner, stream_id, stream); } static void grd_session_rdp_on_caps_lock_state_changed (GrdSession *session, gboolean state) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session); GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; grd_rdp_event_queue_update_caps_lock_state (rdp_event_queue, state); } static void grd_session_rdp_on_num_lock_state_changed (GrdSession *session, gboolean state) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session); GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue; grd_rdp_event_queue_update_num_lock_state (rdp_event_queue, state); } static void grd_session_rdp_dispose (GObject *object) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (object); g_assert (!session_rdp->notify_post_connected_source_id); g_assert (!session_rdp->cursor_renderer); g_clear_object (&session_rdp->layout_manager); clear_rdp_peer (session_rdp); if (session_rdp->connection) grd_close_connection_and_notify (session_rdp->connection); g_clear_object (&session_rdp->connection); g_clear_object (&session_rdp->renderer); g_clear_object (&session_rdp->rdp_event_queue); g_clear_object (&session_rdp->session_metrics); g_clear_pointer (&session_rdp->stream_table, g_hash_table_unref); g_clear_pointer (&session_rdp->pressed_unicode_keys, g_hash_table_unref); g_clear_pointer (&session_rdp->pressed_keys, g_hash_table_unref); g_clear_pointer (&session_rdp->stop_event, CloseHandle); G_OBJECT_CLASS (grd_session_rdp_parent_class)->dispose (object); } static void grd_session_rdp_finalize (GObject *object) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (object); g_mutex_clear (&session_rdp->notify_post_connected_mutex); g_mutex_clear (&session_rdp->close_session_mutex); g_mutex_clear (&session_rdp->rdp_flags_mutex); G_OBJECT_CLASS (grd_session_rdp_parent_class)->finalize (object); } static void grd_session_rdp_init (GrdSessionRdp *session_rdp) { session_rdp->stop_event = CreateEvent (NULL, TRUE, FALSE, NULL); session_rdp->pressed_keys = g_hash_table_new (NULL, NULL); session_rdp->pressed_unicode_keys = g_hash_table_new (NULL, NULL); session_rdp->stream_table = g_hash_table_new (NULL, NULL); g_mutex_init (&session_rdp->rdp_flags_mutex); g_mutex_init (&session_rdp->close_session_mutex); g_mutex_init (&session_rdp->notify_post_connected_mutex); session_rdp->session_metrics = grd_rdp_session_metrics_new (); session_rdp->rdp_event_queue = grd_rdp_event_queue_new (session_rdp); g_signal_connect (session_rdp, "ready", G_CALLBACK (on_remote_desktop_session_ready), NULL); g_signal_connect (session_rdp, "started", G_CALLBACK (on_remote_desktop_session_started), NULL); } static void grd_session_rdp_class_init (GrdSessionRdpClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GrdSessionClass *session_class = GRD_SESSION_CLASS (klass); object_class->dispose = grd_session_rdp_dispose; object_class->finalize = grd_session_rdp_finalize; session_class->stop = grd_session_rdp_stop; session_class->on_stream_created = grd_session_rdp_on_stream_created; session_class->on_caps_lock_state_changed = grd_session_rdp_on_caps_lock_state_changed; session_class->on_num_lock_state_changed = grd_session_rdp_on_num_lock_state_changed; signals[POST_CONNECTED] = g_signal_new ("post-connected", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); }