From 93eb4df5248680170a416cafcb0bb46eacf8703c Mon Sep 17 00:00:00 2001 From: akallabeth Date: Wed, 6 Mar 2024 15:08:25 +0100 Subject: [PATCH] [core,gateway] implement RDG and TSG policy * use dynamic logger in RDG * honor [MS-TSGU] 2.2.9.2.1.5.2 TSG_REDIRECTION_FLAGS * honor [MS-TSGU] 2.2.10.16 HTTP_TUNNEL_AUTH_RESPONSE HTTP_TUNNEL_AUTH_RESPONSE_FIELD_REDIR_FLAGS flag * add setting GatewayIgnoreRedirectionPolicy to ignore the gateway policy if desired --- include/freerdp/settings.h | 11 ++ include/freerdp/settings_types_private.h | 3 +- libfreerdp/common/settings.c | 27 +++ libfreerdp/common/settings_getters.c | 7 + libfreerdp/common/settings_str.h | 2 + libfreerdp/core/client.c | 8 +- libfreerdp/core/gateway/rdg.c | 176 +++++++++++------- libfreerdp/core/gateway/tsg.c | 62 +++++- libfreerdp/core/gateway/tsg.h | 2 +- .../core/test/settings_property_lists.h | 1 + libfreerdp/core/utils.c | 175 +++++++++++++++++ libfreerdp/core/utils.h | 12 ++ 12 files changed, 401 insertions(+), 85 deletions(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index cd89b329f..705ba45d3 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -164,6 +164,17 @@ typedef struct rdp_settings rdpSettings; const char* option, const char* value); FREERDP_API BOOL freerdp_device_collection_add(rdpSettings* settings, RDPDR_DEVICE* device); + + /** \brief Removed a device from the settings, returns ownership of the allocated device to + * caller. + * + * \param settings the settings to remove the device from + * \param device the device to remove + * + * \return \b TRUE if the device was removed, \b FALSE if device was not found or is NULL + */ + FREERDP_API BOOL freerdp_device_collection_del(rdpSettings* settings, + const RDPDR_DEVICE* device); FREERDP_API RDPDR_DEVICE* freerdp_device_collection_find(rdpSettings* settings, const char* name); FREERDP_API RDPDR_DEVICE* freerdp_device_collection_find_type(rdpSettings* settings, diff --git a/include/freerdp/settings_types_private.h b/include/freerdp/settings_types_private.h index 6d23110b6..5b542b646 100644 --- a/include/freerdp/settings_types_private.h +++ b/include/freerdp/settings_types_private.h @@ -467,7 +467,8 @@ struct rdp_settings SETTINGS_DEPRECATED(ALIGN64 char* GatewayAvdDiagnosticserviceurl); /* 2009 */ SETTINGS_DEPRECATED(ALIGN64 char* GatewayAvdHubdiscoverygeourl); /* 2010 */ SETTINGS_DEPRECATED(ALIGN64 char* GatewayAvdActivityhint); /* 2011 */ - UINT64 padding2015[2015 - 2012]; /* 2012 */ + SETTINGS_DEPRECATED(ALIGN64 BOOL GatewayIgnoreRedirectionPolicy); /* 2012 */ + UINT64 padding2015[2015 - 2013]; /* 2013 */ /* Proxy */ SETTINGS_DEPRECATED(ALIGN64 UINT32 ProxyType); /* 2015 */ diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index 34712c81a..8a9007029 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -237,6 +237,33 @@ BOOL freerdp_device_collection_add(rdpSettings* settings, RDPDR_DEVICE* device) return TRUE; } +BOOL freerdp_device_collection_del(rdpSettings* settings, const RDPDR_DEVICE* device) +{ + WINPR_ASSERT(settings); + + if (!device) + return FALSE; + + const UINT32 count = settings->DeviceCount; + for (size_t x = 0; x < count; x++) + { + const RDPDR_DEVICE* cur = settings->DeviceArray[x]; + if (cur == device) + { + for (size_t y = x + 1; y < count; y++) + { + RDPDR_DEVICE* next = settings->DeviceArray[y]; + settings->DeviceArray[y - 1] = next; + } + settings->DeviceArray[count - 1] = NULL; + settings->DeviceCount--; + return TRUE; + } + } + + return FALSE; +} + RDPDR_DEVICE* freerdp_device_collection_find(rdpSettings* settings, const char* name) { RDPDR_DEVICE* device = NULL; diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index ecec04477..8ab4f9b3a 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -252,6 +252,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, FreeRDP_Settings_Key case FreeRDP_GatewayHttpUseWebsockets: return settings->GatewayHttpUseWebsockets; + case FreeRDP_GatewayIgnoreRedirectionPolicy: + return settings->GatewayIgnoreRedirectionPolicy; + case FreeRDP_GatewayRpcTransport: return settings->GatewayRpcTransport; @@ -908,6 +911,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, FreeRDP_Settings_Keys_Bool settings->GatewayHttpUseWebsockets = cnv.c; break; + case FreeRDP_GatewayIgnoreRedirectionPolicy: + settings->GatewayIgnoreRedirectionPolicy = cnv.c; + break; + case FreeRDP_GatewayRpcTransport: settings->GatewayRpcTransport = cnv.c; break; diff --git a/libfreerdp/common/settings_str.h b/libfreerdp/common/settings_str.h index a3c71fb09..a308fba0f 100644 --- a/libfreerdp/common/settings_str.h +++ b/libfreerdp/common/settings_str.h @@ -114,6 +114,8 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_GatewayHttpTransport, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_GatewayHttpTransport" }, { FreeRDP_GatewayHttpUseWebsockets, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_GatewayHttpUseWebsockets" }, + { FreeRDP_GatewayIgnoreRedirectionPolicy, FREERDP_SETTINGS_TYPE_BOOL, + "FreeRDP_GatewayIgnoreRedirectionPolicy" }, { FreeRDP_GatewayRpcTransport, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_GatewayRpcTransport" }, { FreeRDP_GatewayUdpTransport, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_GatewayUdpTransport" }, { FreeRDP_GatewayUseSameCredentials, FREERDP_SETTINGS_TYPE_BOOL, diff --git a/libfreerdp/core/client.c b/libfreerdp/core/client.c index 1bfc61704..4965dafd9 100644 --- a/libfreerdp/core/client.c +++ b/libfreerdp/core/client.c @@ -661,6 +661,8 @@ static int freerdp_channels_process_sync(rdpChannels* channels, freerdp* instanc int status = TRUE; wMessage message = { 0 }; + WINPR_ASSERT(channels); + while (MessageQueue_Peek(channels->queue, &message, TRUE)) { freerdp_channels_process_message(instance, &message); @@ -728,6 +730,8 @@ int freerdp_channels_process_pending_messages(freerdp* instance) */ BOOL freerdp_channels_check_fds(rdpChannels* channels, freerdp* instance) { + WINPR_ASSERT(channels); + if (WaitForSingleObject(MessageQueue_Event(channels->queue), 0) == WAIT_OBJECT_0) { freerdp_channels_process_sync(channels, instance); @@ -742,6 +746,8 @@ UINT freerdp_channels_disconnect(rdpChannels* channels, freerdp* instance) CHANNEL_OPEN_DATA* pChannelOpenData = NULL; CHANNEL_CLIENT_DATA* pChannelClientData = NULL; + WINPR_ASSERT(channels); + if (!channels->connected) return 0; @@ -808,8 +814,6 @@ void freerdp_channels_close(rdpChannels* channels, freerdp* instance) } } - channels->clientDataCount = 0; - for (int index = 0; index < channels->openDataCount; index++) { pChannelOpenData = &channels->openDataList[index]; diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c index c6d952b83..41d4b55e5 100644 --- a/libfreerdp/core/gateway/rdg.c +++ b/libfreerdp/core/gateway/rdg.c @@ -89,15 +89,6 @@ #define HTTP_TUNNEL_PACKET_FIELD_PAA_COOKIE 0x1 #define HTTP_TUNNEL_PACKET_FIELD_REAUTH 0x2 -/* HTTP tunnel redir flags. */ -#define HTTP_TUNNEL_REDIR_ENABLE_ALL 0x80000000 -#define HTTP_TUNNEL_REDIR_DISABLE_ALL 0x40000000 -#define HTTP_TUNNEL_REDIR_DISABLE_DRIVE 0x1 -#define HTTP_TUNNEL_REDIR_DISABLE_PRINTER 0x2 -#define HTTP_TUNNEL_REDIR_DISABLE_PORT 0x4 -#define HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD 0x8 -#define HTTP_TUNNEL_REDIR_DISABLE_PNP 0x10 - /* HTTP tunnel response fields present flags. */ #define HTTP_TUNNEL_RESPONSE_FIELD_TUNNEL_ID 0x1 #define HTTP_TUNNEL_RESPONSE_FIELD_CAPS 0x2 @@ -146,6 +137,7 @@ struct rdp_rdg rdg_http_encoding_context transferEncoding; SmartcardCertInfo* smartcard; + wLog* log; }; enum @@ -261,15 +253,17 @@ static const char* capabilities_enum_to_string(UINT32 capabilities) return flags_to_string(capabilities, capabilities_enum, ARRAYSIZE(capabilities_enum)); } -static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT16* lengthInBytes) +static BOOL rdg_read_http_unicode_string(wLog* log, wStream* s, const WCHAR** string, + UINT16* lengthInBytes) { UINT16 strLenBytes = 0; size_t rem = Stream_GetRemainingLength(s); /* Read length of the string */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) { - WLog_ERR(TAG, "Could not read stream length, only have %" PRIuz " bytes", rem); + WLog_Print(log, WLOG_ERROR, "Could not read stream length, only have %" PRIuz " bytes", + rem); return FALSE; } Stream_Read_UINT16(s, strLenBytes); @@ -280,8 +274,9 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1 /* seek past the string - if this fails something is wrong */ if (!Stream_SafeSeek(s, strLenBytes)) { - WLog_ERR(TAG, "Could not read stream data, only have %" PRIuz " bytes, expected %" PRIu16, - rem - 4, strLenBytes); + WLog_Print(log, WLOG_ERROR, + "Could not read stream data, only have %" PRIuz " bytes, expected %" PRIu16, + rem - 4, strLenBytes); return FALSE; } @@ -699,7 +694,7 @@ out: return s; } -static BOOL rdg_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response) +static BOOL rdg_recv_auth_token(wLog* log, rdpCredsspAuth* auth, HttpResponse* response) { size_t len = 0; const char* token64 = NULL; @@ -719,7 +714,7 @@ static BOOL rdg_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response) case HTTP_STATUS_OK: break; default: - http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response); + http_response_log_error_status(log, WLOG_WARN, response); return FALSE; } @@ -774,14 +769,14 @@ static BOOL rdg_process_handshake_response(rdpRdg* rdg, wStream* s) BYTE verMajor = 0; BYTE verMinor = 0; const char* error = NULL; - WLog_DBG(TAG, "Handshake response received"); + WLog_Print(rdg->log, WLOG_DEBUG, "Handshake response received"); if (rdg->state != RDG_CLIENT_STATE_HANDSHAKE) { return FALSE; } - if (!Stream_CheckAndLogRequiredLength(TAG, s, 10)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 10)) return FALSE; Stream_Read_UINT32(s, errorCode); @@ -790,14 +785,14 @@ static BOOL rdg_process_handshake_response(rdpRdg* rdg, wStream* s) Stream_Read_UINT16(s, serverVersion); Stream_Read_UINT16(s, extendedAuth); error = rpc_error_to_string(errorCode); - WLog_DBG(TAG, - "errorCode=%s, verMajor=%" PRId8 ", verMinor=%" PRId8 ", serverVersion=%" PRId16 - ", extendedAuth=%s", - error, verMajor, verMinor, serverVersion, extended_auth_to_string(extendedAuth)); + WLog_Print(rdg->log, WLOG_DEBUG, + "errorCode=%s, verMajor=%" PRId8 ", verMinor=%" PRId8 ", serverVersion=%" PRId16 + ", extendedAuth=%s", + error, verMajor, verMinor, serverVersion, extended_auth_to_string(extendedAuth)); if (FAILED((HRESULT)errorCode)) { - WLog_ERR(TAG, "Handshake error %s", error); + WLog_Print(rdg->log, WLOG_ERROR, "Handshake error %s", error); freerdp_set_last_error_log(rdg->context, errorCode); return FALSE; } @@ -815,8 +810,8 @@ static BOOL rdg_process_tunnel_response_optional(rdpRdg* rdg, wStream* s, UINT16 /* Seek over tunnelId (4 bytes) */ if (!Stream_SafeSeek(s, 4)) { - WLog_ERR(TAG, "Short tunnelId, got %" PRIuz ", expected 4", - Stream_GetRemainingLength(s)); + WLog_Print(rdg->log, WLOG_ERROR, "Short tunnelId, got %" PRIuz ", expected 4", + Stream_GetRemainingLength(s)); return FALSE; } } @@ -824,11 +819,11 @@ static BOOL rdg_process_tunnel_response_optional(rdpRdg* rdg, wStream* s, UINT16 if (fieldsPresent & HTTP_TUNNEL_RESPONSE_FIELD_CAPS) { UINT32 caps = 0; - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 4)) return FALSE; Stream_Read_UINT32(s, caps); - WLog_DBG(TAG, "capabilities=%s", capabilities_enum_to_string(caps)); + WLog_Print(rdg->log, WLOG_DEBUG, "capabilities=%s", capabilities_enum_to_string(caps)); } if (fieldsPresent & HTTP_TUNNEL_RESPONSE_FIELD_SOH_REQ) @@ -836,14 +831,15 @@ static BOOL rdg_process_tunnel_response_optional(rdpRdg* rdg, wStream* s, UINT16 /* Seek over nonce (20 bytes) */ if (!Stream_SafeSeek(s, 20)) { - WLog_ERR(TAG, "Short nonce, got %" PRIuz ", expected 20", Stream_GetRemainingLength(s)); + WLog_Print(rdg->log, WLOG_ERROR, "Short nonce, got %" PRIuz ", expected 20", + Stream_GetRemainingLength(s)); return FALSE; } /* Read serverCert */ - if (!rdg_read_http_unicode_string(s, NULL, NULL)) + if (!rdg_read_http_unicode_string(rdg->log, s, NULL, NULL)) { - WLog_ERR(TAG, "Failed to read server certificate"); + WLog_Print(rdg->log, WLOG_ERROR, "Failed to read server certificate"); return FALSE; } } @@ -858,9 +854,9 @@ static BOOL rdg_process_tunnel_response_optional(rdpRdg* rdg, wStream* s, UINT16 WINPR_ASSERT(context->instance); /* Read message string and invoke callback */ - if (!rdg_read_http_unicode_string(s, &msg, &msgLenBytes)) + if (!rdg_read_http_unicode_string(rdg->log, s, &msg, &msgLenBytes)) { - WLog_ERR(TAG, "Failed to read consent message"); + WLog_Print(rdg->log, WLOG_ERROR, "Failed to read consent message"); return FALSE; } @@ -877,14 +873,14 @@ static BOOL rdg_process_tunnel_response(rdpRdg* rdg, wStream* s) UINT16 fieldsPresent = 0; UINT32 errorCode = 0; const char* error = NULL; - WLog_DBG(TAG, "Tunnel response received"); + WLog_Print(rdg->log, WLOG_DEBUG, "Tunnel response received"); if (rdg->state != RDG_CLIENT_STATE_TUNNEL_CREATE) { return FALSE; } - if (!Stream_CheckAndLogRequiredLength(TAG, s, 10)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 10)) return FALSE; Stream_Read_UINT16(s, serverVersion); @@ -892,12 +888,12 @@ static BOOL rdg_process_tunnel_response(rdpRdg* rdg, wStream* s) Stream_Read_UINT16(s, fieldsPresent); Stream_Seek_UINT16(s); /* reserved */ error = rpc_error_to_string(errorCode); - WLog_DBG(TAG, "serverVersion=%" PRId16 ", errorCode=%s, fieldsPresent=%s", serverVersion, error, - tunnel_response_fields_present_to_string(fieldsPresent)); + WLog_Print(rdg->log, WLOG_DEBUG, "serverVersion=%" PRId16 ", errorCode=%s, fieldsPresent=%s", + serverVersion, error, tunnel_response_fields_present_to_string(fieldsPresent)); if (FAILED((HRESULT)errorCode)) { - WLog_ERR(TAG, "Tunnel creation error %s", error); + WLog_Print(rdg->log, WLOG_ERROR, "Tunnel creation error %s", error); freerdp_set_last_error_log(rdg->context, errorCode); return FALSE; } @@ -913,31 +909,66 @@ static BOOL rdg_process_tunnel_authorization_response(rdpRdg* rdg, wStream* s) UINT32 errorCode = 0; UINT16 fieldsPresent = 0; const char* error = NULL; - WLog_DBG(TAG, "Tunnel authorization received"); + WLog_Print(rdg->log, WLOG_DEBUG, "Tunnel authorization received"); if (rdg->state != RDG_CLIENT_STATE_TUNNEL_AUTHORIZE) { return FALSE; } - if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 8)) return FALSE; Stream_Read_UINT32(s, errorCode); Stream_Read_UINT16(s, fieldsPresent); Stream_Seek_UINT16(s); /* reserved */ error = rpc_error_to_string(errorCode); - WLog_DBG(TAG, "errorCode=%s, fieldsPresent=%s", error, - tunnel_authorization_response_fields_present_to_string(fieldsPresent)); + WLog_Print(rdg->log, WLOG_DEBUG, "errorCode=%s, fieldsPresent=%s", error, + tunnel_authorization_response_fields_present_to_string(fieldsPresent)); /* [MS-TSGU] 3.7.5.2.7 */ if (errorCode != S_OK && errorCode != E_PROXY_QUARANTINE_ACCESSDENIED) { - WLog_ERR(TAG, "Tunnel authorization error %s", error); + WLog_Print(rdg->log, WLOG_ERROR, "Tunnel authorization error %s", error); freerdp_set_last_error_log(rdg->context, errorCode); return FALSE; } + if (fieldsPresent & HTTP_TUNNEL_AUTH_RESPONSE_FIELD_REDIR_FLAGS) + { + UINT32 redirFlags = 0; + if (!Stream_CheckAndLogRequiredCapacityWLog(rdg->log, s, 4)) + return FALSE; + Stream_Read_UINT32(s, redirFlags); + + rdpContext* context = rdg->context; + if (!utils_apply_gateway_policy(rdg->log, context, redirFlags, "RDG")) + return FALSE; + } + + if (fieldsPresent & HTTP_TUNNEL_AUTH_RESPONSE_FIELD_IDLE_TIMEOUT) + { + UINT32 idleTimeout = 0; + if (!Stream_CheckAndLogRequiredCapacityWLog(rdg->log, s, 4)) + return FALSE; + Stream_Read_UINT32(s, idleTimeout); + WLog_Print(rdg->log, WLOG_DEBUG, "[IDLE_TIMEOUT] idleTimeout=%" PRIu32 ": TODO: unused", + idleTimeout); + } + + if (fieldsPresent & HTTP_TUNNEL_AUTH_RESPONSE_FIELD_SOH_RESPONSE) + { + UINT16 cbLen = 0; + if (!Stream_CheckAndLogRequiredCapacityWLog(rdg->log, s, 2)) + return FALSE; + Stream_Read_UINT16(s, cbLen); + + WLog_Print(rdg->log, WLOG_DEBUG, "[SOH_RESPONSE] cbLen=%" PRIu16 ": TODO: unused", cbLen); + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, cbLen)) + return FALSE; + Stream_Seek(s, cbLen); + } + return rdg_send_channel_create(rdg); } @@ -955,8 +986,8 @@ static BOOL rdg_process_extauth_sspi(rdpRdg* rdg, wStream* s) if (errorCode != ERROR_SUCCESS) { - WLog_ERR(TAG, "EXTAUTH_SSPI_NTLM failed with error %s [0x%08X]", - GetSecurityStatusString(errorCode), errorCode); + WLog_Print(rdg->log, WLOG_ERROR, "EXTAUTH_SSPI_NTLM failed with error %s [0x%08X]", + GetSecurityStatusString(errorCode), errorCode); return FALSE; } @@ -993,27 +1024,27 @@ static BOOL rdg_process_channel_response(rdpRdg* rdg, wStream* s) UINT16 fieldsPresent = 0; UINT32 errorCode = 0; const char* error = NULL; - WLog_DBG(TAG, "Channel response received"); + WLog_Print(rdg->log, WLOG_DEBUG, "Channel response received"); if (rdg->state != RDG_CLIENT_STATE_CHANNEL_CREATE) { return FALSE; } - if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 8)) return FALSE; Stream_Read_UINT32(s, errorCode); Stream_Read_UINT16(s, fieldsPresent); Stream_Seek_UINT16(s); /* reserved */ error = rpc_error_to_string(errorCode); - WLog_DBG(TAG, "channel response errorCode=%s, fieldsPresent=%s", error, - channel_response_fields_present_to_string(fieldsPresent)); + WLog_Print(rdg->log, WLOG_DEBUG, "channel response errorCode=%s, fieldsPresent=%s", error, + channel_response_fields_present_to_string(fieldsPresent)); if (FAILED((HRESULT)errorCode)) { - WLog_ERR(TAG, "channel response errorCode=%s, fieldsPresent=%s", error, - channel_response_fields_present_to_string(fieldsPresent)); + WLog_Print(rdg->log, WLOG_ERROR, "channel response errorCode=%s, fieldsPresent=%s", error, + channel_response_fields_present_to_string(fieldsPresent)); freerdp_set_last_error_log(rdg->context, errorCode); return FALSE; } @@ -1029,7 +1060,7 @@ static BOOL rdg_process_packet(rdpRdg* rdg, wStream* s) UINT32 packetLength = 0; Stream_SetPosition(s, 0); - if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 8)) return FALSE; Stream_Read_UINT16(s, type); @@ -1038,7 +1069,8 @@ static BOOL rdg_process_packet(rdpRdg* rdg, wStream* s) if (Stream_Length(s) < packetLength) { - WLog_ERR(TAG, "Short packet %" PRIuz ", expected %" PRIuz, Stream_Length(s), packetLength); + WLog_Print(rdg->log, WLOG_ERROR, "Short packet %" PRIuz ", expected %" PRIuz, + Stream_Length(s), packetLength); return FALSE; } @@ -1061,7 +1093,7 @@ static BOOL rdg_process_packet(rdpRdg* rdg, wStream* s) break; case PKT_TYPE_DATA: - WLog_ERR(TAG, "Unexpected packet type DATA"); + WLog_Print(rdg->log, WLOG_ERROR, "Unexpected packet type DATA"); return FALSE; case PKT_TYPE_EXTENDED_AUTH_MSG: @@ -1069,7 +1101,7 @@ static BOOL rdg_process_packet(rdpRdg* rdg, wStream* s) break; default: - WLog_ERR(TAG, "PKG TYPE 0x%x not implemented", type); + WLog_Print(rdg->log, WLOG_ERROR, "PKG TYPE 0x%x not implemented", type); return FALSE; } @@ -1325,7 +1357,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* * sending an answer if it is not happy with the http request */ if (!response) { - WLog_INFO(TAG, "RD Gateway HTTP transport broken."); + WLog_Print(rdg->log, WLOG_INFO, "RD Gateway HTTP transport broken."); *rpcFallback = TRUE; return FALSE; } @@ -1336,7 +1368,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* { case HTTP_STATUS_NOT_FOUND: { - WLog_INFO(TAG, "RD Gateway does not support HTTP transport."); + WLog_Print(rdg->log, WLOG_INFO, "RD Gateway does not support HTTP transport."); *rpcFallback = TRUE; http_response_free(response); @@ -1345,13 +1377,13 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* case HTTP_STATUS_OK: break; default: - http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response); + http_response_log_error_status(rdg->log, WLOG_WARN, response); break; } while (!credssp_auth_is_complete(rdg->auth)) { - if (!rdg_recv_auth_token(rdg->auth, response)) + if (!rdg_recv_auth_token(rdg->log, rdg->auth, response)) { http_response_free(response); return FALSE; @@ -1367,7 +1399,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* response = http_response_recv(tls, TRUE); if (!response) { - WLog_INFO(TAG, "RD Gateway HTTP transport broken."); + WLog_Print(rdg->log, WLOG_INFO, "RD Gateway HTTP transport broken."); *rpcFallback = TRUE; return FALSE; } @@ -1388,7 +1420,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* if (!response) { - WLog_INFO(TAG, "RD Gateway HTTP transport broken."); + WLog_Print(rdg->log, WLOG_INFO, "RD Gateway HTTP transport broken."); *rpcFallback = TRUE; return FALSE; } @@ -1399,8 +1431,8 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* const TRANSFER_ENCODING encoding = http_response_get_transfer_encoding(response); const BOOL isWebsocket = http_response_is_websocket(rdg->http, response); http_response_free(response); - WLog_DBG(TAG, "%s authorization result: %s", method, - freerdp_http_status_string_format(statusCode, buffer, ARRAYSIZE(buffer))); + WLog_Print(rdg->log, WLOG_DEBUG, "%s authorization result: %s", method, + freerdp_http_status_string_format(statusCode, buffer, ARRAYSIZE(buffer))); switch (statusCode) { @@ -1442,7 +1474,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* } return TRUE; default: - http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response); + http_response_log_error_status(rdg->log, WLOG_WARN, response); return FALSE; } @@ -1522,7 +1554,7 @@ BOOL rdg_connect(rdpRdg* rdg, DWORD timeout, BOOL* rpcFallback) { if (rdg->transferEncoding.isWebsocketTransport) { - WLog_DBG(TAG, "Upgraded to websocket. RDG_IN_DATA not required"); + WLog_Print(rdg->log, WLOG_DEBUG, "Upgraded to websocket. RDG_IN_DATA not required"); } else { @@ -1717,7 +1749,7 @@ static BOOL rdg_process_close_packet(rdpRdg* rdg, wStream* s) UINT32 packetSize = 12; /* Read error code */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + if (!Stream_CheckAndLogRequiredLengthWLog(rdg->log, s, 4)) return FALSE; Stream_Read_UINT32(s, errorCode); @@ -1769,9 +1801,9 @@ static BOOL rdg_process_service_message(rdpRdg* rdg, wStream* s) WINPR_ASSERT(context->instance); /* Read message string */ - if (!rdg_read_http_unicode_string(s, &msg, &msgLenBytes)) + if (!rdg_read_http_unicode_string(rdg->log, s, &msg, &msgLenBytes)) { - WLog_ERR(TAG, "Failed to read string"); + WLog_Print(rdg->log, WLOG_ERROR, "Failed to read string"); return FALSE; } @@ -1783,7 +1815,7 @@ static BOOL rdg_process_unknown_packet(rdpRdg* rdg, int type) { WINPR_UNUSED(rdg); WINPR_UNUSED(type); - WLog_WARN(TAG, "Unknown Control Packet received: %X", type); + WLog_Print(rdg->log, WLOG_WARN, "Unknown Control Packet received: %X", type); return TRUE; } @@ -1852,7 +1884,8 @@ static BOOL rdg_process_control_packet(rdpRdg* rdg, int type, size_t packetLengt case PKT_TYPE_SERVICE_MESSAGE: if (!s) { - WLog_ERR(TAG, "PKT_TYPE_SERVICE_MESSAGE requires payload but none was sent"); + WLog_Print(rdg->log, WLOG_ERROR, + "PKT_TYPE_SERVICE_MESSAGE requires payload but none was sent"); return FALSE; } status = rdg_process_service_message(rdg, s); @@ -2153,6 +2186,7 @@ rdpRdg* rdg_new(rdpContext* context) if (rdg) { + rdg->log = WLog_Get(TAG); rdg->state = RDG_CLIENT_STATE_INITIAL; rdg->context = context; rdg->settings = rdg->context->settings; @@ -2212,8 +2246,8 @@ rdpRdg* rdg_new(rdpContext* context) break; default: - WLog_DBG(TAG, "RDG extended authentication method %d not supported", - rdg->extAuth); + WLog_Print(rdg->log, WLOG_DEBUG, + "RDG extended authentication method %d not supported", rdg->extAuth); } } diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c index 3ab833a0b..49654c79f 100644 --- a/libfreerdp/core/gateway/tsg.c +++ b/libfreerdp/core/gateway/tsg.c @@ -35,6 +35,7 @@ #include "rpc_bind.h" #include "rpc_client.h" #include "tsg.h" +#include "../utils.h" #include "../../crypto/opensslcompat.h" #define TAG FREERDP_TAG("core.gateway.tsg") @@ -1590,7 +1591,7 @@ fail: return FALSE; } -static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, +static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, const RPC_PDU* pdu, CONTEXT_HANDLE* tunnelContext, UINT32* tunnelId) { BOOL rc = FALSE; @@ -1728,7 +1729,41 @@ fail: return FALSE; } -static BOOL TsProxyAuthorizeTunnelReadResponse(wLog* log, RPC_PDU* pdu) +static UINT32 tsg_redir_to_flags(const TSG_REDIRECTION_FLAGS* redirect) +{ + UINT32 flags = 0; + if (redirect->enableAllRedirections) + flags |= HTTP_TUNNEL_REDIR_ENABLE_ALL; + if (redirect->disableAllRedirections) + flags |= HTTP_TUNNEL_REDIR_DISABLE_ALL; + + if (redirect->driveRedirectionDisabled) + flags |= HTTP_TUNNEL_REDIR_DISABLE_DRIVE; + if (redirect->printerRedirectionDisabled) + flags |= HTTP_TUNNEL_REDIR_DISABLE_PRINTER; + if (redirect->portRedirectionDisabled) + flags |= HTTP_TUNNEL_REDIR_DISABLE_PORT; + if (redirect->clipboardRedirectionDisabled) + flags |= HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD; + if (redirect->pnpRedirectionDisabled) + flags |= HTTP_TUNNEL_REDIR_DISABLE_PNP; + return flags; +} + +static BOOL tsg_redirect_apply(rdpTsg* tsg, const TSG_REDIRECTION_FLAGS* redirect) +{ + WINPR_ASSERT(tsg); + WINPR_ASSERT(redirect); + + rdpTransport* transport = tsg->transport; + WINPR_ASSERT(transport); + + rdpContext* context = transport_get_context(transport); + UINT32 redirFlags = tsg_redir_to_flags(redirect); + return utils_apply_gateway_policy(tsg->log, context, redirFlags, "TSG"); +} + +static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, const RPC_PDU* pdu) { BOOL rc = FALSE; UINT32 SwitchValue = 0; @@ -1736,8 +1771,12 @@ static BOOL TsProxyAuthorizeTunnelReadResponse(wLog* log, RPC_PDU* pdu) TSG_PACKET packet = { 0 }; UINT32 PacketPtr = 0; UINT32 PacketResponsePtr = 0; - if (!pdu) - return FALSE; + + WINPR_ASSERT(tsg); + WINPR_ASSERT(pdu); + + wLog* log = tsg->log; + WINPR_ASSERT(log); if (!tsg_ndr_pointer_read(log, pdu->s, &index, &PacketPtr, TRUE)) goto fail; @@ -1773,6 +1812,9 @@ static BOOL TsProxyAuthorizeTunnelReadResponse(wLog* log, RPC_PDU* pdu) goto fail; rc = TRUE; + + if (packet.tsgPacket.packetResponse.flags & TSG_PACKET_TYPE_QUARREQUEST) + rc = tsg_redirect_apply(tsg, &packet.tsgPacket.packetResponse.redirectionFlags); fail: return rc; } @@ -1846,7 +1888,7 @@ static BOOL TsProxyReadPacketSTringMessage(rdpTsg* tsg, wStream* s, TSG_PACKET_S return tsg_ndr_read_string(tsg->log, s, &msg->msgBuffer, msg->msgBytes); } -static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) +static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, const RPC_PDU* pdu) { BOOL rc = FALSE; UINT32 index = 0; @@ -2036,7 +2078,7 @@ fail: return FALSE; } -static BOOL TsProxyCreateChannelReadResponse(wLog* log, RPC_PDU* pdu, +static BOOL TsProxyCreateChannelReadResponse(wLog* log, const RPC_PDU* pdu, CONTEXT_HANDLE* channelContext, UINT32* channelId) { BOOL rc = FALSE; @@ -2090,7 +2132,7 @@ fail: return FALSE; } -static BOOL TsProxyCloseChannelReadResponse(wLog* log, RPC_PDU* pdu, CONTEXT_HANDLE* context) +static BOOL TsProxyCloseChannelReadResponse(wLog* log, const RPC_PDU* pdu, CONTEXT_HANDLE* context) { BOOL rc = FALSE; WLog_Print(log, WLOG_DEBUG, "TsProxyCloseChannelReadResponse"); @@ -2146,7 +2188,7 @@ fail: return FALSE; } -static BOOL TsProxyCloseTunnelReadResponse(wLog* log, RPC_PDU* pdu, CONTEXT_HANDLE* context) +static BOOL TsProxyCloseTunnelReadResponse(wLog* log, const RPC_PDU* pdu, CONTEXT_HANDLE* context) { BOOL rc = FALSE; WLog_Print(log, WLOG_DEBUG, "TsProxyCloseTunnelReadResponse"); @@ -2294,7 +2336,7 @@ static BOOL tsg_proxy_reauth(rdpTsg* tsg) return tsg_transition_to_state(tsg, TSG_STATE_INITIAL); } -BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) +BOOL tsg_recv_pdu(rdpTsg* tsg, const RPC_PDU* pdu) { BOOL rc = FALSE; RpcClientCall* call = NULL; @@ -2345,7 +2387,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) CONTEXT_HANDLE* TunnelContext = NULL; TunnelContext = (tsg->reauthSequence) ? &tsg->NewTunnelContext : &tsg->TunnelContext; - if (!TsProxyAuthorizeTunnelReadResponse(tsg->log, pdu)) + if (!TsProxyAuthorizeTunnelReadResponse(tsg, pdu)) { WLog_Print(tsg->log, WLOG_ERROR, "TsProxyAuthorizeTunnelReadResponse failure"); return FALSE; diff --git a/libfreerdp/core/gateway/tsg.h b/libfreerdp/core/gateway/tsg.h index 626a7aca5..81b50a7e5 100644 --- a/libfreerdp/core/gateway/tsg.h +++ b/libfreerdp/core/gateway/tsg.h @@ -109,7 +109,7 @@ FREERDP_LOCAL BOOL tsg_proxy_begin(rdpTsg* tsg); FREERDP_LOCAL BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout); FREERDP_LOCAL BOOL tsg_disconnect(rdpTsg* tsg); -FREERDP_LOCAL BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu); +FREERDP_LOCAL BOOL tsg_recv_pdu(rdpTsg* tsg, const RPC_PDU* pdu); FREERDP_LOCAL BOOL tsg_check_event_handles(rdpTsg* tsg); FREERDP_LOCAL DWORD tsg_get_event_handles(rdpTsg* tsg, HANDLE* events, DWORD count); diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index fb280e65e..7779b0e5e 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -68,6 +68,7 @@ static const size_t bool_list_indices[] = { FreeRDP_GatewayHttpExtAuthSspiNtlm, FreeRDP_GatewayHttpTransport, FreeRDP_GatewayHttpUseWebsockets, + FreeRDP_GatewayIgnoreRedirectionPolicy, FreeRDP_GatewayRpcTransport, FreeRDP_GatewayUdpTransport, FreeRDP_GatewayUseSameCredentials, diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c index 1bcb09075..09a5b261f 100644 --- a/libfreerdp/core/utils.c +++ b/libfreerdp/core/utils.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include #define TAG FREERDP_TAG("core.gateway.utils") @@ -299,3 +301,176 @@ const char* utils_is_vsock(const char* hostname) return &hostname[sizeof(vsock)]; return NULL; } + +static BOOL remove_rdpdr_type(rdpSettings* settings, UINT32 type) +{ + RDPDR_DEVICE* printer = NULL; + do + { + printer = freerdp_device_collection_find_type(settings, type); + freerdp_device_collection_del(settings, printer); + freerdp_device_free(printer); + } while (printer); + return TRUE; +} + +static BOOL disable_clipboard(rdpSettings* settings) +{ + if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, FALSE)) + return FALSE; + freerdp_static_channel_collection_del(settings, CLIPRDR_SVC_CHANNEL_NAME); + return TRUE; +} + +static BOOL disable_drive(rdpSettings* settings) +{ + if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectDrives, FALSE)) + return FALSE; + if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectHomeDrive, FALSE)) + return FALSE; + + return remove_rdpdr_type(settings, RDPDR_DTYP_FILESYSTEM); +} + +static BOOL disable_printers(rdpSettings* settings) +{ + if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectPrinters, FALSE)) + return FALSE; + + return remove_rdpdr_type(settings, RDPDR_DTYP_PRINT); +} + +static BOOL disable_port(rdpSettings* settings) +{ + if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectParallelPorts, FALSE)) + return FALSE; + if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSerialPorts, FALSE)) + return FALSE; + if (!remove_rdpdr_type(settings, RDPDR_DTYP_SERIAL)) + return FALSE; + return remove_rdpdr_type(settings, RDPDR_DTYP_PARALLEL); +} + +static BOOL disable_pnp(rdpSettings* settings) +{ + // TODO(akallabeth): [MS-RDPEPNP] related stuff is disabled. + return TRUE; +} + +static BOOL apply_gw_policy(rdpContext* context) +{ + WINPR_ASSERT(context); + + freerdp_channels_disconnect(context->channels, context->instance); + freerdp_channels_close(context->channels, context->instance); + freerdp_channels_free(context->channels); + context->channels = freerdp_channels_new(context->instance); + WINPR_ASSERT(context->channels); + + BOOL rc = TRUE; + IFCALLRET(context->instance->LoadChannels, rc, context->instance); + if (rc) + return freerdp_channels_pre_connect(context->channels, context->instance) == CHANNEL_RC_OK; + return rc; +} + +BOOL utils_apply_gateway_policy(wLog* log, rdpContext* context, UINT32 flags, const char* module) +{ + WINPR_ASSERT(log); + WINPR_ASSERT(context); + + rdpSettings* settings = context->settings; + WINPR_ASSERT(settings); + + if (flags & HTTP_TUNNEL_REDIR_ENABLE_ALL) + { + WLog_Print(log, WLOG_DEBUG, "[%s] policy allows all redirections", module); + } + else if (freerdp_settings_get_bool(settings, FreeRDP_GatewayIgnoreRedirectionPolicy)) + { + char buffer[128] = { 0 }; + WLog_Print(log, WLOG_INFO, "[%s] policy ignored on user request %s", module, + utils_redir_flags_to_string(flags, buffer, sizeof(buffer))); + } + else if (flags & HTTP_TUNNEL_REDIR_DISABLE_ALL) + { + WLog_Print(log, WLOG_INFO, "[%s] policy denies all redirections", module); + if (!disable_drive(settings)) + return FALSE; + if (!disable_printers(settings)) + return FALSE; + if (!disable_clipboard(settings)) + return FALSE; + if (!disable_port(settings)) + return FALSE; + if (!disable_pnp(settings)) + return FALSE; + if (!apply_gw_policy(context)) + return FALSE; + } + else + { + if (flags & HTTP_TUNNEL_REDIR_DISABLE_DRIVE) + { + WLog_Print(log, WLOG_INFO, "[%s] policy denies drive redirections", module); + if (!disable_drive(settings)) + return FALSE; + } + if (flags & HTTP_TUNNEL_REDIR_DISABLE_PRINTER) + { + WLog_Print(log, WLOG_INFO, "[%s] policy denies printer redirections", module); + if (!disable_printers(settings)) + return FALSE; + } + if (flags & HTTP_TUNNEL_REDIR_DISABLE_PORT) + { + WLog_Print(log, WLOG_INFO, "[%s] policy denies port redirections", module); + if (!disable_port(settings)) + return FALSE; + } + if (flags & HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD) + { + WLog_Print(log, WLOG_INFO, "[%s] policy denies clipboard redirections", module); + if (!disable_clipboard(settings)) + return FALSE; + } + if (flags & HTTP_TUNNEL_REDIR_DISABLE_PNP) + { + WLog_Print(log, WLOG_INFO, "[%s] policy denies PNP redirections", module); + if (!disable_pnp(settings)) + return FALSE; + } + if (flags != 0) + { + if (!apply_gw_policy(context)) + return FALSE; + } + } + return TRUE; +} + +char* utils_redir_flags_to_string(UINT32 flags, char* buffer, size_t size) +{ + winpr_str_append("{", buffer, size, ""); + if (flags & HTTP_TUNNEL_REDIR_ENABLE_ALL) + winpr_str_append("ENABLE_ALL", buffer, size, "|"); + if (flags & HTTP_TUNNEL_REDIR_DISABLE_ALL) + winpr_str_append("DISABLE_ALL", buffer, size, "|"); + if (flags & HTTP_TUNNEL_REDIR_DISABLE_DRIVE) + winpr_str_append("DISABLE_DRIVE", buffer, size, "|"); + if (flags & HTTP_TUNNEL_REDIR_DISABLE_PRINTER) + winpr_str_append("DISABLE_PRINTER", buffer, size, "|"); + if (flags & HTTP_TUNNEL_REDIR_DISABLE_PORT) + winpr_str_append("DISABLE_PORT", buffer, size, "|"); + if (flags & HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD) + winpr_str_append("DISABLE_CLIPBOARD", buffer, size, "|"); + if (flags & HTTP_TUNNEL_REDIR_DISABLE_PNP) + winpr_str_append("DISABLE_PNP", buffer, size, "|"); + + char fbuffer[16] = { 0 }; + _snprintf(fbuffer, sizeof(fbuffer), "[0x%08" PRIx32 "]", flags); + + winpr_str_append(fbuffer, buffer, size, " "); + winpr_str_append("{", buffer, size, "}"); + return buffer; +} diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h index 87fa2727b..55f21f676 100644 --- a/libfreerdp/core/utils.h +++ b/libfreerdp/core/utils.h @@ -24,6 +24,15 @@ #include #include +/* HTTP tunnel redir flags. */ +#define HTTP_TUNNEL_REDIR_ENABLE_ALL 0x80000000 +#define HTTP_TUNNEL_REDIR_DISABLE_ALL 0x40000000 +#define HTTP_TUNNEL_REDIR_DISABLE_DRIVE 0x1 +#define HTTP_TUNNEL_REDIR_DISABLE_PRINTER 0x2 +#define HTTP_TUNNEL_REDIR_DISABLE_PORT 0x4 +#define HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD 0x8 +#define HTTP_TUNNEL_REDIR_DISABLE_PNP 0x10 + typedef enum { AUTH_SUCCESS, @@ -47,4 +56,7 @@ BOOL utils_str_copy(const char* value, char** dst); const char* utils_is_vsock(const char* hostname); +BOOL utils_apply_gateway_policy(wLog* log, rdpContext* context, UINT32 flags, const char* module); +char* utils_redir_flags_to_string(UINT32 flags, char* buffer, size_t size); + #endif /* FREERDP_LIB_CORE_UTILS_H */