diff options
Diffstat (limited to 'libfreerdp/core')
31 files changed, 805 insertions, 232 deletions
diff --git a/libfreerdp/core/aad.c b/libfreerdp/core/aad.c index 72204d7..15eabed 100644 --- a/libfreerdp/core/aad.c +++ b/libfreerdp/core/aad.c @@ -210,8 +210,8 @@ cJSON* cJSON_ParseWithLength(const char* value, size_t buffer_length) static INLINE const char* aad_auth_result_to_string(DWORD code) { -#define ERROR_CASE(cd, x) \ - if (cd == (DWORD)(x)) \ +#define ERROR_CASE(cd, x) \ + if ((cd) == (DWORD)(x)) \ return #x; ERROR_CASE(code, S_OK) diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c index 427e434..e8df920 100644 --- a/libfreerdp/core/capabilities.c +++ b/libfreerdp/core/capabilities.c @@ -168,7 +168,7 @@ static BOOL rdp_apply_general_capability_set(rdpSettings* settings, const rdpSet settings->NoBitmapCompressionHeader = src->NoBitmapCompressionHeader; settings->LongCredentialsSupported = src->LongCredentialsSupported; - settings->AutoReconnectionEnabled = src->AutoReconnectionEnabled; + settings->AutoReconnectionPacketSupported = src->AutoReconnectionPacketSupported; if (!src->FastPathOutput) settings->FastPathOutput = FALSE; @@ -223,7 +223,8 @@ static BOOL rdp_read_general_capability_set(wStream* s, rdpSettings* settings) settings->NoBitmapCompressionHeader = (extraFlags & NO_BITMAP_COMPRESSION_HDR) ? TRUE : FALSE; settings->LongCredentialsSupported = (extraFlags & LONG_CREDENTIALS_SUPPORTED) ? TRUE : FALSE; - settings->AutoReconnectionEnabled = (extraFlags & AUTORECONNECT_SUPPORTED) ? TRUE : FALSE; + settings->AutoReconnectionPacketSupported = + (extraFlags & AUTORECONNECT_SUPPORTED) ? TRUE : FALSE; settings->FastPathOutput = (extraFlags & FASTPATH_OUTPUT_SUPPORTED) ? TRUE : FALSE; settings->SaltedChecksum = (extraFlags & ENC_SALTED_CHECKSUM) ? TRUE : FALSE; settings->RefreshRect = refreshRectSupport; @@ -252,7 +253,7 @@ static BOOL rdp_write_general_capability_set(wStream* s, const rdpSettings* sett if (settings->NoBitmapCompressionHeader) extraFlags |= NO_BITMAP_COMPRESSION_HDR; - if (settings->AutoReconnectionEnabled) + if (settings->AutoReconnectionPacketSupported) extraFlags |= AUTORECONNECT_SUPPORTED; if (settings->FastPathOutput) diff --git a/libfreerdp/core/childsession.c b/libfreerdp/core/childsession.c index 3bed262..f9d5b2c 100644 --- a/libfreerdp/core/childsession.c +++ b/libfreerdp/core/childsession.c @@ -2,7 +2,7 @@ * FreeRDP: A Remote Desktop Protocol Implementation * Named pipe transport * - * Copyright 2023 David Fort <contact@hardening-consulting.com> + * Copyright 2023-2024 David Fort <contact@hardening-consulting.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,29 @@ */ #include "tcp.h" + #include <winpr/library.h> #include <winpr/assert.h> +#include <winpr/print.h> +#include <winpr/sysinfo.h> + +#include <freerdp/utils/ringbuffer.h> + #include "childsession.h" #define TAG FREERDP_TAG("childsession") typedef struct { + OVERLAPPED readOverlapped; HANDLE hFile; + BOOL opInProgress; + BOOL lastOpClosed; + RingBuffer readBuffer; + BOOL blocking; + BYTE tmpReadBuffer[4096]; + + HANDLE readEvent; } WINPR_BIO_NAMED; static int transport_bio_named_uninit(BIO* bio); @@ -44,47 +58,192 @@ static int transport_bio_named_write(BIO* bio, const char* buf, int size) BIO_clear_flags(bio, BIO_FLAGS_WRITE); DWORD written = 0; + UINT64 start = GetTickCount64(); BOOL ret = WriteFile(ptr->hFile, buf, size, &written, NULL); - WLog_VRB(TAG, "transport_bio_named_write(%d)=%d written=%d", size, ret, written); + // winpr_HexDump(TAG, WLOG_DEBUG, buf, size); if (!ret) - return -1; + { + WLog_VRB(TAG, "error or deferred"); + return 0; + } + + WLog_VRB(TAG, "(%d)=%d written=%d duration=%d", size, ret, written, GetTickCount64() - start); if (written == 0) - return -1; + { + WLog_VRB(TAG, "closed on write"); + return 0; + } return written; } +static BOOL treatReadResult(WINPR_BIO_NAMED* ptr, DWORD readBytes) +{ + WLog_VRB(TAG, "treatReadResult(readBytes=%" PRIu32 ")", readBytes); + ptr->opInProgress = FALSE; + if (readBytes == 0) + { + WLog_VRB(TAG, "readBytes == 0"); + return TRUE; + } + + if (!ringbuffer_write(&ptr->readBuffer, ptr->tmpReadBuffer, readBytes)) + { + WLog_VRB(TAG, "ringbuffer_write()"); + return FALSE; + } + + return SetEvent(ptr->readEvent); +} + +static BOOL doReadOp(WINPR_BIO_NAMED* ptr) +{ + DWORD readBytes; + + if (!ResetEvent(ptr->readEvent)) + return FALSE; + + ptr->opInProgress = TRUE; + if (!ReadFile(ptr->hFile, ptr->tmpReadBuffer, sizeof(ptr->tmpReadBuffer), &readBytes, + &ptr->readOverlapped)) + { + DWORD error = GetLastError(); + switch (error) + { + case ERROR_NO_DATA: + WLog_VRB(TAG, "No Data, unexpected"); + return TRUE; + case ERROR_IO_PENDING: + WLog_VRB(TAG, "ERROR_IO_PENDING"); + return TRUE; + case ERROR_BROKEN_PIPE: + WLog_VRB(TAG, "broken pipe"); + ptr->lastOpClosed = TRUE; + return TRUE; + default: + return FALSE; + } + } + + return treatReadResult(ptr, readBytes); +} + static int transport_bio_named_read(BIO* bio, char* buf, int size) { WINPR_ASSERT(bio); WINPR_ASSERT(buf); WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); - if (!buf) return 0; - BIO_clear_flags(bio, BIO_FLAGS_READ); + BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ); - DWORD readBytes = 0; - BOOL ret = ReadFile(ptr->hFile, buf, size, &readBytes, NULL); - WLog_VRB(TAG, "transport_bio_named_read(%d)=%d read=%d", size, ret, readBytes); - if (!ret) + if (ptr->blocking) { - if (GetLastError() == ERROR_NO_DATA) - BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ)); - return -1; + while (!ringbuffer_used(&ptr->readBuffer)) + { + if (ptr->lastOpClosed) + return 0; + + if (ptr->opInProgress) + { + DWORD status = WaitForSingleObjectEx(ptr->readEvent, 500, TRUE); + switch (status) + { + case WAIT_TIMEOUT: + case WAIT_IO_COMPLETION: + continue; + case WAIT_OBJECT_0: + break; + default: + return -1; + } + + DWORD readBytes; + if (!GetOverlappedResult(ptr->hFile, &ptr->readOverlapped, &readBytes, FALSE)) + { + WLog_ERR(TAG, "GetOverlappedResult blocking(lastError=%" PRIu32 ")", + GetLastError()); + return -1; + } + + if (!treatReadResult(ptr, readBytes)) + { + WLog_ERR(TAG, "treatReadResult blocking"); + return -1; + } + } + } + } + else + { + if (ptr->opInProgress) + { + DWORD status = WaitForSingleObject(ptr->readEvent, 0); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ)); + return -1; + default: + WLog_ERR(TAG, "error WaitForSingleObject(readEvent)=0x%" PRIx32 "", status); + return -1; + } + + DWORD readBytes; + if (!GetOverlappedResult(ptr->hFile, &ptr->readOverlapped, &readBytes, FALSE)) + { + WLog_ERR(TAG, "GetOverlappedResult non blocking(lastError=%" PRIu32 ")", + GetLastError()); + return -1; + } + + if (!treatReadResult(ptr, readBytes)) + { + WLog_ERR(TAG, "error treatReadResult non blocking"); + return -1; + } + } } - if (readBytes == 0) + int ret = MIN(size, ringbuffer_used(&ptr->readBuffer)); + if (ret) { - BIO_set_flags(bio, (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY)); - return 0; + DataChunk chunks[2]; + int nchunks = ringbuffer_peek(&ptr->readBuffer, chunks, ret); + for (int i = 0; i < nchunks; i++) + { + memcpy(buf, chunks[i].data, chunks[i].size); + buf += chunks[i].size; + } + + ringbuffer_commit_read_bytes(&ptr->readBuffer, ret); + + WLog_VRB(TAG, "(%d)=%d nchunks=%d", size, ret, nchunks); + } + else + { + ret = -1; + } + + if (!ringbuffer_used(&ptr->readBuffer)) + { + if (!ptr->opInProgress && !doReadOp(ptr)) + { + WLog_ERR(TAG, "error rearming read"); + return -1; + } } - return readBytes; + if (ret <= 0) + BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ)); + + return ret; } static int transport_bio_named_puts(BIO* bio, const char* str) @@ -100,7 +259,7 @@ static int transport_bio_named_gets(BIO* bio, char* str, int size) WINPR_ASSERT(bio); WINPR_ASSERT(str); - return transport_bio_named_write(bio, str, size); + return transport_bio_named_read(bio, str, size); } static long transport_bio_named_ctrl(BIO* bio, int cmd, long arg1, void* arg2) @@ -119,7 +278,7 @@ static long transport_bio_named_ctrl(BIO* bio, int cmd, long arg1, void* arg2) if (!BIO_get_init(bio) || !arg2) return 0; - *((HANDLE*)arg2) = ptr->hFile; + *((HANDLE*)arg2) = ptr->readEvent; return 1; case BIO_C_SET_HANDLE: BIO_set_init(bio, 1); @@ -127,18 +286,25 @@ static long transport_bio_named_ctrl(BIO* bio, int cmd, long arg1, void* arg2) return 0; ptr->hFile = (HANDLE)arg2; + ptr->blocking = TRUE; + if (!doReadOp(ptr)) + return -1; return 1; case BIO_C_SET_NONBLOCK: { + WLog_DBG(TAG, "BIO_C_SET_NONBLOCK"); + ptr->blocking = FALSE; return 1; } case BIO_C_WAIT_READ: { + WLog_DBG(TAG, "BIO_C_WAIT_READ"); return 1; } case BIO_C_WAIT_WRITE: { + WLog_DBG(TAG, "BIO_C_WAIT_WRITE"); return 1; } @@ -173,17 +339,34 @@ static long transport_bio_named_ctrl(BIO* bio, int cmd, long arg1, void* arg2) return status; } -static int transport_bio_named_uninit(BIO* bio) +static void BIO_NAMED_free(WINPR_BIO_NAMED* ptr) { - WINPR_ASSERT(bio); - WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + if (!ptr) + return; - if (ptr && ptr->hFile) + if (ptr->hFile) { CloseHandle(ptr->hFile); ptr->hFile = NULL; } + if (ptr->readEvent) + { + CloseHandle(ptr->readEvent); + ptr->readEvent = NULL; + } + + ringbuffer_destroy(&ptr->readBuffer); + free(ptr); +} + +static int transport_bio_named_uninit(BIO* bio) +{ + WINPR_ASSERT(bio); + WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + + BIO_NAMED_free(ptr); + BIO_set_init(bio, 0); BIO_set_flags(bio, 0); return 1; @@ -192,15 +375,27 @@ static int transport_bio_named_uninit(BIO* bio) static int transport_bio_named_new(BIO* bio) { WINPR_ASSERT(bio); - BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)calloc(1, sizeof(WINPR_BIO_NAMED)); if (!ptr) return 0; + if (!ringbuffer_init(&ptr->readBuffer, 0xfffff)) + goto error; + + ptr->readEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + if (!ptr->readEvent || ptr->readEvent == INVALID_HANDLE_VALUE) + goto error; + + ptr->readOverlapped.hEvent = ptr->readEvent; + BIO_set_data(bio, ptr); BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); return 1; + +error: + BIO_NAMED_free(ptr); + return 0; } static int transport_bio_named_free(BIO* bio) @@ -211,13 +406,10 @@ static int transport_bio_named_free(BIO* bio) return 0; transport_bio_named_uninit(bio); - ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); if (ptr) - { BIO_set_data(bio, NULL); - free(ptr); - } return 1; } @@ -292,10 +484,28 @@ static BOOL createChildSessionTransport(HANDLE* pFile) goto out; } + const BYTE startOfPath[] = { '\\', 0, '\\', 0, '.', 0, '\\', 0 }; + if (_wcsncmp(pipePath, (WCHAR*)startOfPath, 4)) + { + /* when compiled under 32 bits, the path may miss "\\.\" at the beginning of the string + * so add it if it's not there + */ + size_t len = _wcslen(pipePath); + if (len > 0x80 - (4 + 1)) + { + WLog_ERR(TAG, "pipePath is too long to be adjusted"); + goto out; + } + + memmove(pipePath + 4, pipePath, (len + 1) * sizeof(WCHAR)); + memcpy(pipePath, startOfPath, 8); + } + ConvertWCharNToUtf8(pipePath, 0x80, pipePathA, sizeof(pipePathA)); WLog_DBG(TAG, "child session is at '%s'", pipePathA); - HANDLE f = CreateFileW(pipePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + HANDLE f = CreateFileW(pipePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); if (f == INVALID_HANDLE_VALUE) { WLog_ERR(TAG, "error when connecting to local named pipe"); diff --git a/libfreerdp/core/client.c b/libfreerdp/core/client.c index 1bfc617..7898a9d 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]; @@ -818,6 +822,7 @@ void freerdp_channels_close(rdpChannels* channels, freerdp* instance) channels->openDataCount = 0; channels->initDataCount = 0; + channels->clientDataCount = 0; WINPR_ASSERT(instance->context); WINPR_ASSERT(instance->context->settings); diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 3abfa93..240a29f 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -622,11 +622,10 @@ BOOL rdp_client_redirect(rdpRdp* rdp) if (!rdp_client_disconnect_and_clear(rdp)) return FALSE; + /* Only disconnect & close the channels here. + * they will be discarded and recreated after the new settings have been applied. */ freerdp_channels_disconnect(rdp->context->channels, rdp->context->instance); freerdp_channels_close(rdp->context->channels, rdp->context->instance); - freerdp_channels_free(rdp->context->channels); - rdp->context->channels = freerdp_channels_new(rdp->context->instance); - WINPR_ASSERT(rdp->context->channels); if (rdp_redirection_apply_settings(rdp) != 0) return FALSE; @@ -684,14 +683,10 @@ BOOL rdp_client_redirect(rdpRdp* rdp) if (!IFCALLRESULT(TRUE, rdp->context->instance->Redirect, rdp->context->instance)) return FALSE; - BOOL ok = IFCALLRESULT(TRUE, rdp->context->instance->LoadChannels, rdp->context->instance); + BOOL ok = utils_reload_channels(rdp->context); if (!ok) return FALSE; - if (CHANNEL_RC_OK != - freerdp_channels_pre_connect(rdp->context->channels, rdp->context->instance)) - return FALSE; - status = rdp_client_connect(rdp); if (status) @@ -782,7 +777,6 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) Stream_Zero(s, 8); Stream_SealLength(s); status = transport_write(rdp->mcs->transport, s); - Stream_Free(s, TRUE); if (status < 0) goto end; @@ -829,6 +823,7 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) ret = TRUE; end: + Stream_Free(s, TRUE); free(crypt_client_random); if (!ret) diff --git a/libfreerdp/core/credssp_auth.c b/libfreerdp/core/credssp_auth.c index c14dbe1..8b4c0cb 100644 --- a/libfreerdp/core/credssp_auth.c +++ b/libfreerdp/core/credssp_auth.c @@ -808,6 +808,7 @@ static SecurityFunctionTable* auth_resolve_sspi_table(const rdpSettings* setting if (!hSSPI) { WLog_ERR(TAG, "Failed to load SSPI module: %s", module_name); + free(sspi_module); return FALSE; } diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 29d907a..3660e24 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -84,7 +84,6 @@ static void sig_abort_connect(int signum, const char* signame, void* ctx) static int freerdp_connect_begin(freerdp* instance) { BOOL rc = 0; - UINT status2 = CHANNEL_RC_OK; rdpRdp* rdp = NULL; BOOL status = TRUE; rdpSettings* settings = NULL; @@ -121,16 +120,9 @@ static int freerdp_connect_begin(freerdp* instance) freerdp_settings_print_warnings(settings); if (status) - { - if (!rdp_set_backup_settings(rdp)) - return 0; - - WINPR_ASSERT(instance->LoadChannels); - if (!instance->LoadChannels(instance)) - return 0; - - status2 = freerdp_channels_pre_connect(instance->context->channels, instance); - } + status = rdp_set_backup_settings(rdp); + if (status) + status = utils_reload_channels(instance->context); KeyboardLayout = freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout); switch (KeyboardLayout) @@ -151,7 +143,7 @@ static int freerdp_connect_begin(freerdp* instance) break; } - if (!status || (status2 != CHANNEL_RC_OK)) + if (!status) { rdpContext* context = instance->context; WINPR_ASSERT(context); @@ -413,8 +405,8 @@ static BOOL freerdp_prevent_session_lock(rdpContext* context) if (now - in->lastInputTimestamp > FakeMouseMotionInterval) { WLog_Print(context->log, WLOG_DEBUG, - "fake mouse move: x=%d y=%d lastInputTimestamp=%d " - "FakeMouseMotionInterval=%d", + "fake mouse move: x=%d y=%d lastInputTimestamp=%" PRIu64 " " + "FakeMouseMotionInterval=%" PRIu32, in->lastX, in->lastY, in->lastInputTimestamp, FakeMouseMotionInterval); BOOL status = freerdp_input_send_mouse_event(context->input, PTR_FLAGS_MOVE, in->lastX, diff --git a/libfreerdp/core/gateway/arm.c b/libfreerdp/core/gateway/arm.c index 9848c48..d506c75 100644 --- a/libfreerdp/core/gateway/arm.c +++ b/libfreerdp/core/gateway/arm.c @@ -549,6 +549,7 @@ static BOOL arm_pick_base64Utf16Field(const cJSON* json, const char* name, BYTE* if (!output1 || !len1) { WLog_ERR(TAG, "error when first unbase64 for %s", name); + free(output1); return FALSE; } @@ -558,6 +559,7 @@ static BOOL arm_pick_base64Utf16Field(const cJSON* json, const char* name, BYTE* if (!output2 || !len2) { WLog_ERR(TAG, "error when decode('utf-16') for %s", name); + free(output2); return FALSE; } @@ -567,6 +569,7 @@ static BOOL arm_pick_base64Utf16Field(const cJSON* json, const char* name, BYTE* if (!output || !*plen) { WLog_ERR(TAG, "error when second unbase64 for %s", name); + free(output); return FALSE; } @@ -842,7 +845,11 @@ static BOOL arm_handle_bad_request(rdpArm* arm, const HttpResponse* response, BO 0) { *retry = TRUE; - WLog_DBG(TAG, "Starting your VM. It may take up to 5 minutes"); + const cJSON* message = cJSON_GetObjectItemCaseSensitive(json, "Message"); + if (!cJSON_IsString(message) || !message->valuestring) + WLog_WARN(TAG, "Starting your VM. It may take up to 5 minutes"); + else + WLog_WARN(TAG, "%s", message->valuestring); } else { diff --git a/libfreerdp/core/gateway/http.c b/libfreerdp/core/gateway/http.c index cf70b3b..a7cdaab 100644 --- a/libfreerdp/core/gateway/http.c +++ b/libfreerdp/core/gateway/http.c @@ -322,8 +322,9 @@ static BOOL list_append(HttpContext* context, WINPR_FORMAT_ARG const char* str, } else sstr = Pragma; - free(context->Pragma); + Pragma = NULL; + free(context->Pragma); context->Pragma = sstr; rc = TRUE; @@ -830,7 +831,10 @@ static BOOL http_response_parse_header_field(HttpResponse* response, const char* const char* value) { BOOL status = TRUE; - if (!response || !name) + + WINPR_ASSERT(response); + + if (!name) return FALSE; if (_stricmp(name, "Content-Length") == 0) @@ -924,6 +928,10 @@ static BOOL http_response_parse_header_field(HttpResponse* response, const char* *separator = '\0'; CookieName = value; CookieValue = separator + 1; + + if (!CookieName || !CookieValue) + return FALSE; + if (*CookieValue == '"') { char* p = CookieValue; @@ -944,9 +952,6 @@ static BOOL http_response_parse_header_field(HttpResponse* response, const char* } *p = '\0'; } - - if (!CookieName || !CookieValue) - return FALSE; } else { @@ -1053,12 +1058,13 @@ static void http_response_print(wLog* log, DWORD level, const HttpResponse* resp freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer))); for (size_t i = 0; i < response->count; i++) - WLog_Print(log, level, "[%" PRIuz "] %s", i, response->lines[i]); + WLog_Print(log, WLOG_DEBUG, "[%" PRIuz "] %s", i, response->lines[i]); if (response->ReasonPhrase) WLog_Print(log, level, "[reason] %s", response->ReasonPhrase); - WLog_Print(log, level, "[body][%" PRIuz "] %s", response->BodyLength, response->BodyContent); + WLog_Print(log, WLOG_TRACE, "[body][%" PRIuz "] %s", response->BodyLength, + response->BodyContent); } static BOOL http_use_content_length(const char* cur) diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c index c6d952b..484b599 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; } @@ -362,7 +357,13 @@ static int rdg_socket_read(BIO* bio, BYTE* pBuffer, size_t size, } } -static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size, +static BOOL rdg_shall_abort(rdpRdg* rdg) +{ + WINPR_ASSERT(rdg); + return freerdp_shall_disconnect_context(rdg->context); +} + +static BOOL rdg_read_all(rdpContext* context, rdpTls* tls, BYTE* buffer, size_t size, rdg_http_encoding_context* transferEncoding) { size_t readCount = 0; @@ -370,6 +371,9 @@ static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size, while (readCount < size) { + if (freerdp_shall_disconnect_context(context)) + return FALSE; + int status = rdg_socket_read(tls->bio, pBuffer, size - readCount, transferEncoding); if (status <= 0) { @@ -396,7 +400,7 @@ static wStream* rdg_receive_packet(rdpRdg* rdg) if (!s) return NULL; - if (!rdg_read_all(rdg->tlsOut, Stream_Buffer(s), header, &rdg->transferEncoding)) + if (!rdg_read_all(rdg->context, rdg->tlsOut, Stream_Buffer(s), header, &rdg->transferEncoding)) { Stream_Free(s, TRUE); return NULL; @@ -412,8 +416,8 @@ static wStream* rdg_receive_packet(rdpRdg* rdg) return NULL; } - if (!rdg_read_all(rdg->tlsOut, Stream_Buffer(s) + header, (int)packetLength - (int)header, - &rdg->transferEncoding)) + if (!rdg_read_all(rdg->context, rdg->tlsOut, Stream_Buffer(s) + header, + (int)packetLength - (int)header, &rdg->transferEncoding)) { Stream_Free(s, TRUE); return NULL; @@ -699,7 +703,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 +723,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; } @@ -738,6 +742,8 @@ static BOOL rdg_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response) authToken.cbBuffer = authTokenLength; credssp_auth_take_input_buffer(auth, &authToken); } + else + free(authTokenData); rc = credssp_auth_authenticate(auth); if (rc < 0) @@ -746,7 +752,7 @@ static BOOL rdg_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response) return TRUE; } -static BOOL rdg_skip_seed_payload(rdpTls* tls, SSIZE_T lastResponseLength, +static BOOL rdg_skip_seed_payload(rdpContext* context, rdpTls* tls, size_t lastResponseLength, rdg_http_encoding_context* transferEncoding) { BYTE seed_payload[10] = { 0 }; @@ -755,9 +761,9 @@ static BOOL rdg_skip_seed_payload(rdpTls* tls, SSIZE_T lastResponseLength, /* Per [MS-TSGU] 3.3.5.1 step 4, after final OK response RDG server sends * random "seed" payload of limited size. In practice it's 10 bytes. */ - if (lastResponseLength < (SSIZE_T)size) + if (lastResponseLength < size) { - if (!rdg_read_all(tls, seed_payload, size - lastResponseLength, transferEncoding)) + if (!rdg_read_all(context, tls, seed_payload, size - lastResponseLength, transferEncoding)) { return FALSE; } @@ -774,14 +780,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 +796,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 +821,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 +830,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 +842,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 +865,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 +884,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 +899,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 +920,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 +997,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; } @@ -972,6 +1014,8 @@ static BOOL rdg_process_extauth_sspi(rdpRdg* rdg, wStream* s) } authTokenData = malloc(authBlobLen); + if (authTokenData == NULL) + return FALSE; Stream_Read(s, authTokenData, authBlobLen); authToken.pvBuffer = authTokenData; @@ -993,27 +1037,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 +1073,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 +1082,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 +1106,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 +1114,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 +1370,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 +1381,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 +1390,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 +1412,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 +1433,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; } @@ -1398,9 +1443,9 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* const size_t bodyLength = http_response_get_body_length(response); 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) { @@ -1408,11 +1453,14 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* /* old rdg endpoint without websocket support, don't request websocket for RDG_IN_DATA */ http_context_enable_websocket_upgrade(rdg->http, FALSE); + http_response_free(response); break; case HTTP_STATUS_DENIED: freerdp_set_last_error_log(rdg->context, FREERDP_ERROR_CONNECT_ACCESS_DENIED); + http_response_free(response); return FALSE; case HTTP_STATUS_SWITCH_PROTOCOLS: + http_response_free(response); if (!isWebsocket) { /* @@ -1442,7 +1490,8 @@ 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); + http_response_free(response); return FALSE; } @@ -1455,7 +1504,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* rdg->transferEncoding.context.chunked.headerFooterPos = 0; rdg->transferEncoding.context.chunked.state = ChunkStateLenghHeader; } - if (!rdg_skip_seed_payload(tls, bodyLength, &rdg->transferEncoding)) + if (!rdg_skip_seed_payload(rdg->context, tls, bodyLength, &rdg->transferEncoding)) { return FALSE; } @@ -1522,7 +1571,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 +1766,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 +1818,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 +1832,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; } @@ -1808,6 +1857,11 @@ static BOOL rdg_process_control_packet(rdpRdg* rdg, int type, size_t packetLengt while (readCount < payloadSize) { + if (rdg_shall_abort(rdg)) + { + Stream_Free(s, TRUE); + return FALSE; + } status = rdg_socket_read(rdg->tlsOut->bio, Stream_Pointer(s), payloadSize - readCount, &rdg->transferEncoding); @@ -1852,7 +1906,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); @@ -1880,6 +1935,9 @@ static int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size) while (readCount < sizeof(RdgPacketHeader)) { + if (rdg_shall_abort(rdg)) + return -1; + status = rdg_socket_read(rdg->tlsOut->bio, (BYTE*)(&header) + readCount, (int)sizeof(RdgPacketHeader) - (int)readCount, &rdg->transferEncoding); @@ -1916,6 +1974,8 @@ static int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size) while (readCount < 2) { + if (rdg_shall_abort(rdg)) + return -1; status = rdg_socket_read(rdg->tlsOut->bio, (BYTE*)(&rdg->packetRemainingCount) + readCount, 2 - (int)readCount, &rdg->transferEncoding); @@ -2153,6 +2213,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 +2273,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 3ab833a..8101e53 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; @@ -1991,20 +2033,20 @@ fail: static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnelContext) { - size_t count = 0; - wStream* s = NULL; - rdpRpc* rpc = NULL; + WINPR_ASSERT(tsg); + WINPR_ASSERT(tunnelContext); + WLog_Print(tsg->log, WLOG_DEBUG, "TsProxyCreateChannelWriteRequest"); - if (!tsg || !tsg->rpc || !tunnelContext || !tsg->Hostname) + if (!tsg->rpc || !tsg->Hostname) return FALSE; - rpc = tsg->rpc; - count = _wcslen(tsg->Hostname) + 1; + rdpRpc* rpc = tsg->rpc; + const size_t count = _wcslen(tsg->Hostname) + 1; if (count > UINT32_MAX) return FALSE; - s = Stream_New(NULL, 60 + count * 2); + wStream* s = Stream_New(NULL, 60 + count * 2); if (!s) return FALSE; @@ -2036,14 +2078,16 @@ 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; - WLog_Print(log, WLOG_DEBUG, "TsProxyCreateChannelReadResponse"); - if (!pdu) - return FALSE; + WINPR_ASSERT(log); + WINPR_ASSERT(pdu); + WINPR_ASSERT(channelId); + + WLog_Print(log, WLOG_DEBUG, "TsProxyCreateChannelReadResponse"); if (!Stream_CheckAndLogRequiredLengthWLog(log, pdu->s, 28)) goto fail; @@ -2068,15 +2112,15 @@ fail: static BOOL TsProxyCloseChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context) { - wStream* s = NULL; - rdpRpc* rpc = NULL; + WINPR_ASSERT(tsg); + WINPR_ASSERT(context); + WLog_Print(tsg->log, WLOG_DEBUG, "TsProxyCloseChannelWriteRequest"); - if (!tsg || !tsg->rpc || !context) - return FALSE; + rdpRpc* rpc = tsg->rpc; + WINPR_ASSERT(rpc); - rpc = tsg->rpc; - s = Stream_New(NULL, 20); + wStream* s = Stream_New(NULL, 20); if (!s) return FALSE; @@ -2090,7 +2134,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"); @@ -2124,15 +2168,15 @@ fail: static BOOL TsProxyCloseTunnelWriteRequest(rdpTsg* tsg, const CONTEXT_HANDLE* context) { - wStream* s = NULL; - rdpRpc* rpc = NULL; + WINPR_ASSERT(tsg); + WINPR_ASSERT(context); + WLog_Print(tsg->log, WLOG_DEBUG, "TsProxyCloseTunnelWriteRequest"); - if (!tsg || !tsg->rpc || !context) - return FALSE; + rdpRpc* rpc = tsg->rpc; + WINPR_ASSERT(rpc); - rpc = tsg->rpc; - s = Stream_New(NULL, 20); + wStream* s = Stream_New(NULL, 20); if (!s) return FALSE; @@ -2146,13 +2190,15 @@ 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"); - if (!pdu || !context) - return FALSE; + WINPR_ASSERT(log); + WINPR_ASSERT(pdu); + WINPR_ASSERT(context); + + WLog_Print(log, WLOG_DEBUG, "TsProxyCloseTunnelReadResponse"); if (!Stream_CheckAndLogRequiredLengthWLog(log, pdu->s, 24)) goto fail; @@ -2294,7 +2340,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; @@ -2342,10 +2388,10 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) case TSG_STATE_CONNECTED: { - CONTEXT_HANDLE* TunnelContext = NULL; - TunnelContext = (tsg->reauthSequence) ? &tsg->NewTunnelContext : &tsg->TunnelContext; + CONTEXT_HANDLE* 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 626a7ac..81b50a7 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/gateway/wst.c b/libfreerdp/core/gateway/wst.c index 00581d3..87194da 100644 --- a/libfreerdp/core/gateway/wst.c +++ b/libfreerdp/core/gateway/wst.c @@ -199,6 +199,8 @@ static BOOL wst_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response) authToken.cbBuffer = authTokenLength; credssp_auth_take_input_buffer(auth, &authToken); } + else + free(authTokenData); rc = credssp_auth_authenticate(auth); if (rc < 0) diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c index d99ee86..733a763 100644 --- a/libfreerdp/core/gcc.c +++ b/libfreerdp/core/gcc.c @@ -1704,7 +1704,13 @@ BOOL gcc_read_server_security_data(wStream* s, rdpMcs* mcs) Stream_Read_UINT32(s, settings->ServerCertificateLength); /* serverCertLen */ if ((settings->ServerRandomLength == 0) || (settings->ServerCertificateLength == 0)) + { + WLog_ERR(TAG, + "Invalid ServerRandom (length=%" PRIu32 ") or ServerCertificate (length=%" PRIu32 + ")", + settings->ServerRandomLength, settings->ServerCertificateLength); return FALSE; + } if (!Stream_CheckAndLogRequiredLength(TAG, s, settings->ServerRandomLength)) return FALSE; diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c index 0d8e90e..7d6eec1 100644 --- a/libfreerdp/core/info.c +++ b/libfreerdp/core/info.c @@ -469,8 +469,12 @@ static BOOL rdp_write_extended_info_packet(rdpRdp* rdp, wStream* s) rdpSettings* settings = rdp->settings; WINPR_ASSERT(settings); - const UINT16 clientAddressFamily = - settings->IPv6Enabled ? ADDRESS_FAMILY_INET6 : ADDRESS_FAMILY_INET; + UINT16 clientAddressFamily = ADDRESS_FAMILY_INET; + if (settings->ConnectChildSession) + clientAddressFamily = 0x0000; + else if (settings->IPv6Enabled) + clientAddressFamily = ADDRESS_FAMILY_INET6; + WCHAR* clientAddress = ConvertUtf8ToWCharAlloc(settings->ClientAddress, &cbClientAddress); if (cbClientAddress > (UINT16_MAX / sizeof(WCHAR))) diff --git a/libfreerdp/core/input.h b/libfreerdp/core/input.h index c67153b..9a1585d 100644 --- a/libfreerdp/core/input.h +++ b/libfreerdp/core/input.h @@ -38,7 +38,7 @@ typedef struct rdpInputProxy* proxy; wMessageQueue* queue; - UINT32 lastInputTimestamp; + UINT64 lastInputTimestamp; UINT16 lastX; UINT16 lastY; } rdp_input_internal; diff --git a/libfreerdp/core/license.c b/libfreerdp/core/license.c index 99d4fa2..aeb7bb2 100644 --- a/libfreerdp/core/license.c +++ b/libfreerdp/core/license.c @@ -1114,10 +1114,9 @@ BOOL license_generate_hwid(rdpLicense* license) const char* hostname = license->rdp->settings->ClientHostname; wStream* s = Stream_StaticInit(&buffer, license->HardwareId, 4); Stream_Write_UINT32(s, license->PlatformId); - Stream_Free(s, TRUE); hashTarget = (const BYTE*)hostname; - targetLen = strlen(hostname); + targetLen = hostname ? strlen(hostname) : 0; } /* Allow FIPS override for use of MD5 here, really this does not have to be MD5 as we are just @@ -1723,14 +1722,15 @@ void license_free_scope_list(SCOPE_LIST* scopeList) BOOL license_send_license_info(rdpLicense* license, const LICENSE_BLOB* calBlob, const BYTE* signature, size_t signature_length) { - wStream* s = license_send_stream_init(license); - WINPR_ASSERT(calBlob); WINPR_ASSERT(signature); WINPR_ASSERT(license->certificate); const rdpCertInfo* info = freerdp_certificate_get_info(license->certificate); + if (!info) + return FALSE; + wStream* s = license_send_stream_init(license); if (!s) return FALSE; @@ -2802,18 +2802,25 @@ BOOL license_server_send_request(rdpLicense* license) return license_set_state(license, LICENSE_STATE_REQUEST); } -static BOOL license_set_string(const char* what, const char* value, WCHAR** dst, UINT32* dstLen) +static BOOL license_set_string(const char* what, const char* value, BYTE** bdst, UINT32* dstLen) { WINPR_ASSERT(what); WINPR_ASSERT(value); - WINPR_ASSERT(dst); + WINPR_ASSERT(bdst); WINPR_ASSERT(dstLen); + union + { + WCHAR** w; + BYTE** b; + } cnv; + cnv.b = bdst; + size_t len = 0; - *dst = (BYTE*)ConvertUtf8ToWCharAlloc(value, &len); - if (!*dst || (len > UINT32_MAX / sizeof(WCHAR))) + *cnv.w = ConvertUtf8ToWCharAlloc(value, &len); + if (!*cnv.w || (len > UINT32_MAX / sizeof(WCHAR))) { - WLog_ERR(TAG, "license->ProductInfo: %s == %p || %" PRIu32 " > UINT32_MAX", what, *dst, + WLog_ERR(TAG, "license->ProductInfo: %s == %p || %" PRIu32 " > UINT32_MAX", what, *cnv.w, len); return FALSE; } diff --git a/libfreerdp/core/listener.c b/libfreerdp/core/listener.c index 5a9c1e2..a7592da 100644 --- a/libfreerdp/core/listener.c +++ b/libfreerdp/core/listener.c @@ -188,10 +188,11 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&option_value, sizeof(option_value)) == -1) - WLog_ERR(TAG, "setsockopt"); + WLog_ERR(TAG, "setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR)"); #ifndef _WIN32 - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) != 0) + WLog_ERR(TAG, "fcntl(sockfd, F_SETFL, O_NONBLOCK)"); #else arg = 1; ioctlsocket(sockfd, FIONBIO, &arg); @@ -256,7 +257,14 @@ static BOOL freerdp_listener_open_local(freerdp_listener* instance, const char* return FALSE; } - fcntl(sockfd, F_SETFL, O_NONBLOCK); + int rc = fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (rc != 0) + { + WLog_ERR(TAG, "fcntl(sockfd, F_SETFL, O_NONBLOCK)"); + closesocket((SOCKET)sockfd); + return FALSE; + } + addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); unlink(path); diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index ddee306..577e0b4 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -1148,6 +1148,11 @@ static BOOL nla_read_TSCspDataDetail(WinPrAsn1Decoder* dec, rdpSettings* setting static BOOL nla_read_KERB_TICKET_LOGON(rdpNla* nla, wStream* s, KERB_TICKET_LOGON* ticket) { + WINPR_ASSERT(nla); + + if (!ticket) + return FALSE; + /* mysterious extra 16 bytes before TGS/TGT content */ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16 + 16)) return FALSE; @@ -1244,7 +1249,7 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data) WinPrAsn1_OctetString credentials = { 0 }; BOOL error = FALSE; WinPrAsn1_INTEGER credType = -1; - BOOL ret = true; + BOOL ret = TRUE; WINPR_ASSERT(nla); WINPR_ASSERT(data); diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index 9d00c66..42c4c21 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -1495,7 +1495,8 @@ void freerdp_peer_free(freerdp_peer* client) return; sspi_FreeAuthIdentity(&client->identity); - closesocket((SOCKET)client->sockfd); + if (client->sockfd >= 0) + closesocket((SOCKET)client->sockfd); free(client); } @@ -1511,6 +1512,7 @@ static BOOL freerdp_peer_transport_setup(freerdp_peer* client) if (!transport_attach(rdp->transport, client->sockfd)) return FALSE; + client->sockfd = -1; if (!transport_set_recv_callbacks(rdp->transport, peer_recv_callback, client)) return FALSE; diff --git a/libfreerdp/core/proxy.c b/libfreerdp/core/proxy.c index 9312c22..ecb9b34 100644 --- a/libfreerdp/core/proxy.c +++ b/libfreerdp/core/proxy.c @@ -284,14 +284,12 @@ static BOOL check_no_proxy(rdpSettings* settings, const char* no_proxy) void proxy_read_environment(rdpSettings* settings, char* envname) { - DWORD envlen = 0; - char* env = NULL; - envlen = GetEnvironmentVariableA(envname, NULL, 0); + const DWORD envlen = GetEnvironmentVariableA(envname, NULL, 0); - if (!envlen) + if (!envlen || (envlen <= 1)) return; - env = calloc(1, envlen); + char* env = calloc(1, envlen); if (!env) { diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index c1f6d3a..466a9a7 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -261,7 +261,7 @@ BOOL rdp_read_share_control_header(rdpRdp* rdp, wStream* s, UINT16* tpktLength, WLog_Print(rdp->log, WLOG_DEBUG, "[Flow control PDU] type=%s, tpktLength=%" PRIuz ", remainingLength=%" PRIuz, pdu_type_to_str(*type, buffer, sizeof(buffer)), tpktLength ? *tpktLength : 0, - *remainingLength); + remainingLength ? *remainingLength : 0); return TRUE; } @@ -2259,7 +2259,7 @@ rdpRdp* rdp_new(rdpContext* context) /* Keep a backup copy of settings for later comparisons */ if (!rdp_set_backup_settings(rdp)) - return FALSE; + goto fail; rdp->settings->instance = context->instance; diff --git a/libfreerdp/core/security.c b/libfreerdp/core/security.c index 653bf0a..96cf00b 100644 --- a/libfreerdp/core/security.c +++ b/libfreerdp/core/security.c @@ -592,6 +592,7 @@ static void fips_expand_key_bits(const BYTE* in, size_t in_len, BYTE* out, size_ } else { + WINPR_ASSERT(p + 1 < sizeof(buf)); /* c is accumulator */ BYTE c = (BYTE)(buf[p] << r) & 0xFF; c |= buf[p + 1] >> (8 - r); diff --git a/libfreerdp/core/smartcardlogon.c b/libfreerdp/core/smartcardlogon.c index d5907cf..f3f5581 100644 --- a/libfreerdp/core/smartcardlogon.c +++ b/libfreerdp/core/smartcardlogon.c @@ -222,11 +222,14 @@ static BOOL set_info_certificate(SmartcardCertInfo* cert, BYTE* certBytes, DWORD return FALSE; } - if (userFilter && cert->userHint && strcmp(cert->userHint, userFilter) != 0) + if (userFilter && (!cert->upn || (strcmp(cert->upn, userFilter) != 0))) { - WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->userHint, - cert->domainHint); - return FALSE; + if (cert->userHint && strcmp(cert->userHint, userFilter) != 0) + { + WLog_DBG(TAG, "discarding non matching cert by user %s@%s", cert->userHint, + cert->domainHint); + return FALSE; + } } if (domainFilter && cert->domainHint && strcmp(cert->domainHint, domainFilter) != 0) diff --git a/libfreerdp/core/test/CMakeLists.txt b/libfreerdp/core/test/CMakeLists.txt index 3e0a652..ebd8fef 100644 --- a/libfreerdp/core/test/CMakeLists.txt +++ b/libfreerdp/core/test/CMakeLists.txt @@ -9,8 +9,9 @@ set(${MODULE_PREFIX}_TESTS TestStreamDump.c TestSettings.c) -set(${MODULE_PREFIX}_FUZZERS - TestFuzzCryptoCertificateDataSetPEM.c) +set(FUZZERS + TestFuzzCryptoCertificateDataSetPEM.c +) if(WITH_SAMPLE AND WITH_SERVER AND NOT WIN32) add_definitions(-DCMAKE_EXECUTABLE_SUFFIX="${CMAKE_EXECUTABLE_SUFFIX}") @@ -32,16 +33,8 @@ add_definitions(-DTESTING_SRC_DIRECTORY="${PROJECT_SOURCE_DIR}") target_link_libraries(${MODULE_NAME} freerdp winpr freerdp-client) -if (BUILD_FUZZERS) - foreach(test ${${MODULE_PREFIX}_FUZZERS}) - get_filename_component(TestName ${test} NAME_WE) - add_executable(${TestName} ${test}) - target_link_libraries(${TestName} freerdp winpr freerdp-client fuzzer_config) - add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) - set_target_properties(${TestName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") - add_dependencies(fuzzers ${TestName}) - endforeach() -endif (BUILD_FUZZERS) +include (AddFuzzerTest) +add_fuzzer_test("${FUZZERS}" "freerdp winpr") set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index fb280e6..d966242 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -19,6 +19,7 @@ static const size_t bool_list_indices[] = { FreeRDP_AutoDenyCertificate, FreeRDP_AutoLogonEnabled, FreeRDP_AutoReconnectionEnabled, + FreeRDP_AutoReconnectionPacketSupported, FreeRDP_BitmapCacheEnabled, FreeRDP_BitmapCachePersistEnabled, FreeRDP_BitmapCacheV3Enabled, @@ -68,6 +69,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/transport.c b/libfreerdp/core/transport.c index e4cc570..a2a899b 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -197,8 +197,6 @@ static BOOL transport_default_attach(rdpTransport* transport, int sockfd) if (!socketBio) goto fail; - - BIO_set_fd(socketBio, sockfd, BIO_CLOSE); } bufferedBio = BIO_new(BIO_s_buffered_socket()); @@ -206,8 +204,18 @@ static BOOL transport_default_attach(rdpTransport* transport, int sockfd) goto fail; if (socketBio) + { bufferedBio = BIO_push(bufferedBio, socketBio); - WINPR_ASSERT(bufferedBio); + if (!bufferedBio) + goto fail; + + /* Attach the socket only when this function can no longer fail. + * This ensures solid ownership: + * - if this function fails, the caller is responsible to clean up + * - if this function is successful, the caller MUST NOT close the socket any more. + */ + BIO_set_fd(socketBio, sockfd, BIO_CLOSE); + } transport->frontBio = bufferedBio; return TRUE; fail: diff --git a/libfreerdp/core/transport.h b/libfreerdp/core/transport.h index 8b5c5c6..912fffd 100644 --- a/libfreerdp/core/transport.h +++ b/libfreerdp/core/transport.h @@ -58,6 +58,21 @@ FREERDP_LOCAL wStream* transport_send_stream_init(rdpTransport* transport, size_ FREERDP_LOCAL BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 port, DWORD timeout); FREERDP_LOCAL BOOL transport_connect_childsession(rdpTransport* transport); + +/**! \brief Attach a socket to the transport layer + * + * The ownership of the socket provided by \b sockfd is taken if and only if the function is + * successful. In such a case the caller must no longer close or otherwise use the socket. If the + * function fails it is up to the caller to close the socket. + * + * The implementation can be overridden by + * transport_set_io_callbacks(rdpTransportIo::TransportAttach) + * + * \param transport The transport instance to attach the socket to + * \param sockfd The socket to attach to the transport + * + * \return \b TRUE in case of success, \b FALSE otherwise. + */ FREERDP_LOCAL BOOL transport_attach(rdpTransport* transport, int sockfd); FREERDP_LOCAL BOOL transport_disconnect(rdpTransport* transport); FREERDP_LOCAL BOOL transport_connect_rdp(rdpTransport* transport); diff --git a/libfreerdp/core/update.c b/libfreerdp/core/update.c index db8ddef..cfc0abc 100644 --- a/libfreerdp/core/update.c +++ b/libfreerdp/core/update.c @@ -3322,6 +3322,10 @@ BOOL update_begin_paint(rdpUpdate* update) WINPR_ASSERT(update->context); + BOOL rc = IFCALLRESULT(TRUE, update->BeginPaint, update->context); + if (!rc) + WLog_WARN(TAG, "BeginPaint call failed"); + /* Reset the invalid regions, we start a new frame here. */ rdpGdi* gdi = update->context->gdi; WINPR_ASSERT(gdi); @@ -3335,10 +3339,6 @@ BOOL update_begin_paint(rdpUpdate* update) hwnd->ninvalid = 0; } - BOOL rc = IFCALLRESULT(TRUE, update->BeginPaint, update->context); - if (!rc) - WLog_WARN(TAG, "BeginPaint call failed"); - return rc; } diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c index 1bcb090..f414611 100644 --- a/libfreerdp/core/utils.c +++ b/libfreerdp/core/utils.c @@ -25,6 +25,8 @@ #include <winpr/assert.h> #include <freerdp/freerdp.h> +#include <freerdp/channels/cliprdr.h> +#include <freerdp/channels/rdpdr.h> #include <freerdp/log.h> #define TAG FREERDP_TAG("core.gateway.utils") @@ -299,3 +301,182 @@ 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); + return utils_reload_channels(context); +} + +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; +} + +BOOL utils_reload_channels(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; +} diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h index 87fa272..54a7856 100644 --- a/libfreerdp/core/utils.h +++ b/libfreerdp/core/utils.h @@ -24,6 +24,15 @@ #include <winpr/winpr.h> #include <freerdp/freerdp.h> +/* 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,9 @@ 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); + +BOOL utils_reload_channels(rdpContext* context); + #endif /* FREERDP_LIB_CORE_UTILS_H */ |