/** * FreeRDP: A Remote Desktop Protocol Implementation * RDP Settings * * Copyright 2009-2011 Jay Sorg * Copyright 2023 Armin Novak * Copyright 2023 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../core/utils.h" #include "../crypto/certificate.h" #include "../crypto/privatekey.h" #include "capabilities.h" #define TAG FREERDP_TAG("settings") #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4244) #endif static const char client_dll[] = "C:\\Windows\\System32\\mstscax.dll"; #define SERVER_KEY "Software\\%s\\Server" #define CLIENT_KEY "Software\\%s\\Client" #define BITMAP_CACHE_KEY CLIENT_KEY "\\BitmapCacheV2" #define GLYPH_CACHE_KEY CLIENT_KEY "\\GlyphCache" #define POINTER_CACHE_KEY CLIENT_KEY "\\PointerCache" struct bounds_t { INT32 x; INT32 y; INT32 width; INT32 height; }; static const char* bounds2str(const struct bounds_t* bounds, char* buffer, size_t len) { WINPR_ASSERT(bounds); (void)_snprintf(buffer, len, "{%dx%d-%dx%d}", bounds->x, bounds->y, bounds->x + bounds->width, bounds->y + bounds->height); return buffer; } static struct bounds_t union_rect(const struct bounds_t* a, const struct bounds_t* b) { WINPR_ASSERT(a); WINPR_ASSERT(b); struct bounds_t u = *a; if (b->x < u.x) u.x = b->x; if (b->y < u.y) u.y = b->y; const INT32 rightA = a->x + a->width; const INT32 rightB = b->x + b->width; const INT32 right = MAX(rightA, rightB); u.width = right - u.x; const INT32 bottomA = a->y + a->height; const INT32 bottomB = b->y + b->height; const INT32 bottom = MAX(bottomA, bottomB); u.height = bottom - u.y; return u; } static BOOL intersect_rects(const struct bounds_t* r1, const struct bounds_t* r2) { WINPR_ASSERT(r1); WINPR_ASSERT(r2); const INT32 left = MAX(r1->x, r2->x); const INT32 top = MAX(r1->y, r2->y); const INT32 right = MIN(r1->x + r1->width, r2->x + r2->width); const INT32 bottom = MIN(r1->y + r1->height, r2->y + r2->height); return (left < right) && (top < bottom); } static BOOL align_rects(const struct bounds_t* r1, const struct bounds_t* r2) { WINPR_ASSERT(r1); WINPR_ASSERT(r2); const INT32 left = MAX(r1->x, r2->x); const INT32 top = MAX(r1->y, r2->y); const INT32 right = MIN(r1->x + r1->width, r2->x + r2->width); const INT32 bottom = MIN(r1->y + r1->height, r2->y + r2->height); return (left == right) || (top == bottom); } static BOOL settings_reg_query_dword_val(HKEY hKey, const TCHAR* sub, DWORD* value) { DWORD dwType = 0; DWORD dwSize = 0; dwSize = sizeof(DWORD); if (RegQueryValueEx(hKey, sub, nullptr, &dwType, (BYTE*)value, &dwSize) != ERROR_SUCCESS) return FALSE; if (dwType != REG_DWORD) return FALSE; return TRUE; } static BOOL settings_reg_query_word_val(HKEY hKey, const TCHAR* sub, UINT16* value) { DWORD dwValue = 0; if (!settings_reg_query_dword_val(hKey, sub, &dwValue)) return FALSE; if (dwValue > UINT16_MAX) return FALSE; *value = (UINT16)dwValue; return TRUE; } static BOOL settings_reg_query_bool_val(HKEY hKey, const TCHAR* sub, BOOL* value) { DWORD dwValue = 0; if (!settings_reg_query_dword_val(hKey, sub, &dwValue)) return FALSE; *value = dwValue != 0; return TRUE; } static BOOL settings_reg_query_dword(rdpSettings* settings, FreeRDP_Settings_Keys_UInt32 id, HKEY hKey, const TCHAR* sub) { DWORD dwValue = 0; if (!settings_reg_query_dword_val(hKey, sub, &dwValue)) return FALSE; return freerdp_settings_set_uint32(settings, id, dwValue); } static BOOL settings_reg_query_bool(rdpSettings* settings, FreeRDP_Settings_Keys_Bool id, HKEY hKey, const TCHAR* sub) { DWORD dwValue = 0; if (!settings_reg_query_dword_val(hKey, sub, &dwValue)) return FALSE; return freerdp_settings_set_bool(settings, id, (dwValue != 0)); } static void settings_client_load_hkey_local_machine(rdpSettings* settings) { { char* key = freerdp_getApplicatonDetailsRegKey(CLIENT_KEY); if (key) { HKEY hKey = nullptr; const LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); free(key); if (status == ERROR_SUCCESS) { settings_reg_query_dword(settings, FreeRDP_DesktopWidth, hKey, _T("DesktopWidth")); settings_reg_query_dword(settings, FreeRDP_DesktopHeight, hKey, _T("DesktopHeight")); settings_reg_query_bool(settings, FreeRDP_Fullscreen, hKey, _T("Fullscreen")); settings_reg_query_dword(settings, FreeRDP_ColorDepth, hKey, _T("ColorDepth")); settings_reg_query_dword(settings, FreeRDP_KeyboardType, hKey, _T("KeyboardType")); settings_reg_query_dword(settings, FreeRDP_KeyboardSubType, hKey, _T("KeyboardSubType")); settings_reg_query_dword(settings, FreeRDP_KeyboardFunctionKey, hKey, _T("KeyboardFunctionKeys")); settings_reg_query_dword(settings, FreeRDP_KeyboardLayout, hKey, _T("KeyboardLayout")); settings_reg_query_bool(settings, FreeRDP_ExtSecurity, hKey, _T("ExtSecurity")); settings_reg_query_bool(settings, FreeRDP_NlaSecurity, hKey, _T("NlaSecurity")); settings_reg_query_bool(settings, FreeRDP_TlsSecurity, hKey, _T("TlsSecurity")); settings_reg_query_bool(settings, FreeRDP_RdpSecurity, hKey, _T("RdpSecurity")); settings_reg_query_bool(settings, FreeRDP_MstscCookieMode, hKey, _T("MstscCookieMode")); settings_reg_query_dword(settings, FreeRDP_CookieMaxLength, hKey, _T("CookieMaxLength")); settings_reg_query_bool(settings, FreeRDP_BitmapCacheEnabled, hKey, _T("BitmapCache")); settings_reg_query_dword(settings, FreeRDP_OffscreenSupportLevel, hKey, _T("OffscreenBitmapCache")); settings_reg_query_dword(settings, FreeRDP_OffscreenCacheSize, hKey, _T("OffscreenBitmapCacheSize")); settings_reg_query_dword(settings, FreeRDP_OffscreenCacheEntries, hKey, _T("OffscreenBitmapCacheEntries")); RegCloseKey(hKey); } } } { char* key = freerdp_getApplicatonDetailsRegKey(BITMAP_CACHE_KEY); if (key) { HKEY hKey = nullptr; const LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); free(key); if (status == ERROR_SUCCESS) { settings_reg_query_dword(settings, FreeRDP_BitmapCacheV2NumCells, hKey, _T("NumCells")); for (unsigned x = 0; x < 5; x++) { DWORD val = 0; TCHAR numentries[64] = WINPR_C_ARRAY_INIT; TCHAR persist[64] = WINPR_C_ARRAY_INIT; BITMAP_CACHE_V2_CELL_INFO cache = WINPR_C_ARRAY_INIT; (void)_sntprintf(numentries, ARRAYSIZE(numentries), _T("Cell%uNumEntries"), x); (void)_sntprintf(persist, ARRAYSIZE(persist), _T("Cell%uPersistent"), x); if (!settings_reg_query_dword_val(hKey, numentries, &val) || !settings_reg_query_bool_val(hKey, persist, &cache.persistent) || !freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, x, &cache)) WLog_WARN(TAG, "Failed to load registry keys to settings!"); cache.numEntries = val; } settings_reg_query_bool(settings, FreeRDP_AllowCacheWaitingList, hKey, _T("AllowCacheWaitingList")); RegCloseKey(hKey); } } } { char* key = freerdp_getApplicatonDetailsRegKey(GLYPH_CACHE_KEY); if (key) { HKEY hKey = nullptr; const LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); free(key); if (status == ERROR_SUCCESS) { unsigned x = 0; UINT32 GlyphSupportLevel = 0; settings_reg_query_dword(settings, FreeRDP_GlyphSupportLevel, hKey, _T("SupportLevel")); for (; x < 10; x++) { GLYPH_CACHE_DEFINITION cache = WINPR_C_ARRAY_INIT; TCHAR numentries[64] = WINPR_C_ARRAY_INIT; TCHAR maxsize[64] = WINPR_C_ARRAY_INIT; (void)_sntprintf(numentries, ARRAYSIZE(numentries), _T("Cache%uNumEntries"), x); (void)_sntprintf(maxsize, ARRAYSIZE(maxsize), _T("Cache%uMaxCellSize"), x); settings_reg_query_word_val(hKey, numentries, &cache.cacheEntries); settings_reg_query_word_val(hKey, maxsize, &cache.cacheMaximumCellSize); if (!freerdp_settings_set_pointer_array(settings, FreeRDP_GlyphCache, x, &cache)) WLog_WARN(TAG, "Failed to store GlyphCache %u", x); } { GLYPH_CACHE_DEFINITION cache = WINPR_C_ARRAY_INIT; settings_reg_query_word_val(hKey, _T("FragCacheNumEntries"), &cache.cacheEntries); settings_reg_query_word_val(hKey, _T("FragCacheMaxCellSize"), &cache.cacheMaximumCellSize); if (!freerdp_settings_set_pointer_array(settings, FreeRDP_FragCache, x, &cache)) WLog_WARN(TAG, "Failed to store FragCache"); } RegCloseKey(hKey); if (!freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel, GlyphSupportLevel)) WLog_WARN(TAG, "Failed to load registry keys to settings!"); } } } { char* key = freerdp_getApplicatonDetailsRegKey(POINTER_CACHE_KEY); if (key) { HKEY hKey = nullptr; const LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); free(key); if (status == ERROR_SUCCESS) { settings_reg_query_dword(settings, FreeRDP_LargePointerFlag, hKey, _T("LargePointer")); settings_reg_query_dword(settings, FreeRDP_PointerCacheSize, hKey, _T("PointerCacheSize")); settings_reg_query_dword(settings, FreeRDP_ColorPointerCacheSize, hKey, _T("ColorPointerCacheSize")); RegCloseKey(hKey); } } } } static void settings_server_load_hkey_local_machine(rdpSettings* settings) { HKEY hKey = nullptr; char* key = freerdp_getApplicatonDetailsRegKey(SERVER_KEY); if (!key) return; const LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); free(key); if (status != ERROR_SUCCESS) return; settings_reg_query_bool(settings, FreeRDP_ExtSecurity, hKey, _T("ExtSecurity")); settings_reg_query_bool(settings, FreeRDP_NlaSecurity, hKey, _T("NlaSecurity")); settings_reg_query_bool(settings, FreeRDP_TlsSecurity, hKey, _T("TlsSecurity")); settings_reg_query_dword(settings, FreeRDP_TlsSecLevel, hKey, _T("TlsSecLevel")); settings_reg_query_bool(settings, FreeRDP_RdpSecurity, hKey, _T("RdpSecurity")); RegCloseKey(hKey); } static void settings_load_hkey_local_machine(rdpSettings* settings) { if (freerdp_settings_get_bool(settings, FreeRDP_ServerMode)) settings_server_load_hkey_local_machine(settings); else settings_client_load_hkey_local_machine(settings); } static BOOL settings_init_computer_name(rdpSettings* settings) { CHAR computerName[MAX_COMPUTERNAME_LENGTH + 1] = WINPR_C_ARRAY_INIT; DWORD nSize = ARRAYSIZE(computerName); if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize)) return FALSE; if (!freerdp_settings_set_string(settings, FreeRDP_ComputerName, computerName)) return FALSE; return freerdp_settings_set_string(settings, FreeRDP_ClientHostname, computerName); } void freerdp_settings_print_warnings(const rdpSettings* settings) { const UINT32 level = freerdp_settings_get_uint32(settings, FreeRDP_GlyphSupportLevel); if (level != GLYPH_SUPPORT_NONE) { char buffer[32] = WINPR_C_ARRAY_INIT; WLog_WARN(TAG, "[experimental] enabled GlyphSupportLevel %s, expect visual artefacts!", freerdp_settings_glyph_level_string(level, buffer, sizeof(buffer))); } } static BOOL monitor_operlaps(const rdpSettings* settings, UINT32 orig, UINT32 start, UINT32 count, const rdpMonitor* compare) { const struct bounds_t rect1 = { .x = compare->x, .y = compare->y, .width = compare->width, .height = compare->height }; for (UINT32 x = start; x < count; x++) { const rdpMonitor* monitor = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x); const struct bounds_t rect2 = { .x = monitor->x, .y = monitor->y, .width = monitor->width, .height = monitor->height }; if (intersect_rects(&rect1, &rect2)) { char buffer1[32] = WINPR_C_ARRAY_INIT; char buffer2[32] = WINPR_C_ARRAY_INIT; WLog_ERR(TAG, "Monitor %" PRIu32 " and %" PRIu32 " are overlapping:", orig, x); WLog_ERR(TAG, "%s overlapps with %s", bounds2str(&rect1, buffer1, sizeof(buffer1)), bounds2str(&rect2, buffer2, sizeof(buffer2))); WLog_ERR( TAG, "Mulitimonitor mode requested, but local layout has gaps or overlapping areas!"); WLog_ERR(TAG, "Please reconfigure your local monitor setup so that there are no gaps or " "overlapping areas!"); return TRUE; } } return FALSE; } static BOOL monitor_has_gaps(const rdpSettings* settings, UINT32 start, UINT32 count, const rdpMonitor* compare, UINT32** graph) { const struct bounds_t rect1 = { .x = compare->x, .y = compare->y, .width = compare->width, .height = compare->height }; BOOL hasNeighbor = FALSE; for (UINT32 i = 0; i < count; i++) { if (i == start) continue; const rdpMonitor* monitor = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, i); const struct bounds_t rect2 = { .x = monitor->x, .y = monitor->y, .width = monitor->width, .height = monitor->height }; if (align_rects(&rect1, &rect2)) { hasNeighbor = TRUE; graph[start][i] = 1; graph[i][start] = 1; } } if (!hasNeighbor) { WLog_ERR(TAG, "Monitor configuration has gaps! Monitor %" PRIu32 " does not have any neighbor", start); } return !hasNeighbor; } static void alloc_free(UINT32** ptr) { free((void*)ptr); } WINPR_ATTR_MALLOC(alloc_free, 1) WINPR_ATTR_NODISCARD static UINT32** alloc_array(size_t count) { // NOLINTNEXTLINE(clang-analyzer-unix.MallocSizeof) BYTE* array = calloc(count * sizeof(uintptr_t), count * sizeof(UINT32)); UINT32** dst = (UINT32**)array; UINT32* val = (UINT32*)(array + count * sizeof(uintptr_t)); for (size_t x = 0; x < count; x++) dst[x] = &val[x]; return dst; } /* Monitors in the array need to: * * 1. be connected to another monitor (edges touch but don't overlap or have gaps) * 2. all monitors need to be connected so there are no separate groups. * * So, what we do here is we use dijkstra algorithm to find a path from any start node * (lazy as we are we always use the first in the array) to each other node. */ static BOOL find_path_exists_with_dijkstra(UINT32** graph, size_t count, UINT32 start) { if (count < 1) return FALSE; WINPR_ASSERT(start < count); UINT32** cost = alloc_array(count); WINPR_ASSERT(cost); UINT32* distance = calloc(count, sizeof(UINT32)); WINPR_ASSERT(distance); UINT32* visited = calloc(count, sizeof(UINT32)); WINPR_ASSERT(visited); UINT32* parent = calloc(count, sizeof(UINT32)); WINPR_ASSERT(parent); for (size_t x = 0; x < count; x++) { for (size_t y = 0; y < count; y++) { if (graph[x][y] == 0) cost[x][y] = UINT32_MAX; else cost[x][y] = graph[x][y]; } } for (UINT32 x = 0; x < count; x++) { distance[x] = cost[start][x]; parent[x] = start; visited[x] = 0; } distance[start] = 0; visited[start] = 1; size_t pos = 1; UINT32 nextnode = UINT32_MAX; while (pos < count - 1) { UINT32 mindistance = UINT32_MAX; for (UINT32 x = 0; x < count; x++) { if ((distance[x] < mindistance) && !visited[x]) { mindistance = distance[x]; nextnode = x; } } visited[nextnode] = 1; for (size_t y = 0; y < count; y++) { if (!visited[y]) { UINT32 dist = mindistance + cost[nextnode][y]; if (dist < distance[y]) { distance[y] = dist; parent[y] = nextnode; } } } pos++; } BOOL rc = TRUE; for (size_t x = 0; x < count; x++) { if (x != start) { if (distance[x] == UINT32_MAX) { WLog_ERR(TAG, "monitor %" PRIu32 " not connected with monitor %" PRIuz, start, x); rc = FALSE; break; } } } alloc_free(cost); free(distance); free(visited); free(parent); return rc; } static BOOL freerdp_settings_client_monitors_have_gaps(const rdpSettings* settings) { BOOL rc = TRUE; const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); if (count <= 1) return FALSE; UINT32** graph = alloc_array(count); WINPR_ASSERT(graph); for (UINT32 x = 0; x < count; x++) { const rdpMonitor* monitor = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x); if (monitor_has_gaps(settings, x, count, monitor, graph)) goto fail; } rc = !find_path_exists_with_dijkstra(graph, count, 0); fail: alloc_free(graph); return rc; } static void log_monitor(UINT32 idx, const rdpMonitor* monitor, wLog* log, DWORD level) { WINPR_ASSERT(monitor); WLog_Print(log, level, "[%" PRIu32 "] [%s] {%dx%d-%dx%d} [%" PRIu32 "] {%" PRIu32 "x%" PRIu32 ", orientation: %s [%" PRIu32 "], desktopScale: %" PRIu32 ", deviceScale: %" PRIu32 "}", idx, monitor->is_primary ? "primary" : " ", monitor->x, monitor->y, monitor->width, monitor->height, monitor->orig_screen, monitor->attributes.physicalWidth, monitor->attributes.physicalHeight, freerdp_desktop_rotation_flags_to_string(monitor->attributes.orientation), monitor->attributes.orientation, monitor->attributes.desktopScaleFactor, monitor->attributes.deviceScaleFactor); } static void log_monitor_configuration(const rdpSettings* settings, wLog* log, DWORD level) { const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); WLog_Print(log, level, "[BEGIN] MonitorDefArray[%" PRIu32 "]", count); for (UINT32 x = 0; x < count; x++) { const rdpMonitor* monitor = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x); log_monitor(x, monitor, log, level); } WLog_Print(log, level, "[END] MonitorDefArray[%" PRIu32 "]", count); } static BOOL freerdp_settings_client_monitors_overlap(const rdpSettings* settings) { const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); for (UINT32 x = 0; x < count; x++) { const rdpMonitor* monitor = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x); if (monitor_operlaps(settings, x, x + 1, count, monitor)) return TRUE; } return FALSE; } /* See [MS-RDPBCGR] 2.2.1.3.6.1 for details on limits * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/c3964b39-3d54-4ae1-a84a-ceaed311e0f6 */ static BOOL freerdp_settings_client_monitors_check_primary_and_origin(const rdpSettings* settings) { const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); BOOL havePrimary = FALSE; BOOL foundOrigin = FALSE; BOOL primaryIsOrigin = FALSE; BOOL rc = TRUE; struct bounds_t bounds = WINPR_C_ARRAY_INIT; if (count == 0) { WLog_WARN(TAG, "Monitor configuration empty."); return TRUE; } for (UINT32 x = 0; x < count; x++) { const rdpMonitor* monitor = freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x); struct bounds_t cur = { .x = monitor->x, .y = monitor->y, .width = monitor->width, .height = monitor->height }; bounds = union_rect(&bounds, &cur); if (monitor->is_primary) { if (havePrimary) { WLog_ERR(TAG, "Monitor configuration contains multiple primary monitors!"); rc = FALSE; } havePrimary = TRUE; } if ((monitor->x == 0) && (monitor->y == 0)) { if (foundOrigin) { WLog_ERR(TAG, "Monitor configuration does have multiple origin 0/0"); rc = FALSE; } foundOrigin = TRUE; primaryIsOrigin = monitor->is_primary != 0; } } if ((bounds.width > 32766) || (bounds.width < 200)) { WLog_ERR(TAG, "Monitor configuration virtual desktop width must be 200 <= %" PRId32 " <= 32766", bounds.width); rc = FALSE; } if ((bounds.height > 32766) || (bounds.height < 200)) { WLog_ERR(TAG, "Monitor configuration virtual desktop height must be 200 <= %" PRId32 " <= 32766", bounds.height); rc = FALSE; } if (!havePrimary) { WLog_ERR(TAG, "Monitor configuration does not contain a primary monitor!"); rc = FALSE; } if (!foundOrigin) { WLog_ERR(TAG, "Monitor configuration must start at 0/0 for first monitor!"); rc = FALSE; } if (!primaryIsOrigin) { WLog_ERR(TAG, "Monitor configuration must start at 0/0 for primary monitor!"); rc = FALSE; } return rc; } BOOL freerdp_settings_check_client_after_preconnect(const rdpSettings* settings) { wLog* log = WLog_Get(TAG); BOOL rc = TRUE; log_monitor_configuration(settings, log, WLOG_DEBUG); if (freerdp_settings_client_monitors_overlap(settings)) rc = FALSE; if (freerdp_settings_client_monitors_have_gaps(settings)) rc = FALSE; if (!freerdp_settings_client_monitors_check_primary_and_origin(settings)) rc = FALSE; if (!rc) { DWORD level = WLOG_ERROR; WLog_Print(log, level, "Invalid or unsupported monitor configuration detected"); WLog_Print(log, level, "Check if the configuration is valid."); WLog_Print(log, level, "If you suspect a bug create a new issue at " "https://github.com/FreeRDP/FreeRDP/issues/new"); WLog_Print( log, level, "Provide at least the following log lines detailing your monitor configuration:"); log_monitor_configuration(settings, log, level); } return rc; } BOOL freerdp_settings_set_default_order_support(rdpSettings* settings) { BYTE* OrderSupport = freerdp_settings_get_pointer_writable(settings, FreeRDP_OrderSupport); if (!OrderSupport) return FALSE; ZeroMemory(OrderSupport, 32); OrderSupport[NEG_DSTBLT_INDEX] = TRUE; OrderSupport[NEG_PATBLT_INDEX] = TRUE; OrderSupport[NEG_SCRBLT_INDEX] = TRUE; OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; OrderSupport[NEG_LINETO_INDEX] = TRUE; OrderSupport[NEG_POLYLINE_INDEX] = TRUE; OrderSupport[NEG_MEMBLT_INDEX] = freerdp_settings_get_bool(settings, FreeRDP_BitmapCacheEnabled) ? 1 : 0; OrderSupport[NEG_MEM3BLT_INDEX] = freerdp_settings_get_bool(settings, FreeRDP_BitmapCacheEnabled) ? 1 : 0; OrderSupport[NEG_MEMBLT_V2_INDEX] = freerdp_settings_get_bool(settings, FreeRDP_BitmapCacheEnabled) ? 1 : 0; OrderSupport[NEG_MEM3BLT_V2_INDEX] = freerdp_settings_get_bool(settings, FreeRDP_BitmapCacheEnabled) ? 1 : 0; OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; OrderSupport[NEG_GLYPH_INDEX_INDEX] = freerdp_settings_get_uint32(settings, FreeRDP_GlyphSupportLevel) != GLYPH_SUPPORT_NONE; OrderSupport[NEG_FAST_INDEX_INDEX] = freerdp_settings_get_uint32(settings, FreeRDP_GlyphSupportLevel) != GLYPH_SUPPORT_NONE; OrderSupport[NEG_FAST_GLYPH_INDEX] = freerdp_settings_get_uint32(settings, FreeRDP_GlyphSupportLevel) != GLYPH_SUPPORT_NONE; OrderSupport[NEG_POLYGON_SC_INDEX] = FALSE; OrderSupport[NEG_POLYGON_CB_INDEX] = FALSE; OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; return TRUE; } char* freerdp_settings_get_config_path(void) { return freerdp_GetConfigFilePath(FALSE, ""); } rdpSettings* freerdp_settings_new(DWORD flags) { const BOOL server = ((flags & FREERDP_SETTINGS_SERVER_MODE) != 0); const BOOL remote = ((flags & FREERDP_SETTINGS_REMOTE_MODE) != 0); rdpSettings* settings = (rdpSettings*)calloc(1, sizeof(rdpSettings)); if (!settings) return nullptr; { const char* vendor = freerdp_getApplicationDetailsVendor(); const char* product = freerdp_getApplicationDetailsProduct(); const char* details = freerdp_getApplicationDetailsString(); char* issuer1 = nullptr; char* issuer2 = nullptr; char* issuer3 = nullptr; size_t len = 0; (void)winpr_asprintf(&issuer1, &len, "%s-licenser", freerdp_getApplicationDetailsString()); (void)winpr_asprintf(&issuer2, &len, "%s-licenser", freerdp_getApplicationDetailsVendor()); (void)winpr_asprintf(&issuer3, &len, "%s-licenser", freerdp_getApplicationDetailsProduct()); char* issuers[] = { WINPR_CAST_CONST_PTR_AWAY(vendor, char*), WINPR_CAST_CONST_PTR_AWAY(product, char*), WINPR_CAST_CONST_PTR_AWAY(details, char*), issuer1, issuer2, issuer3 }; const BOOL res = freerdp_server_license_issuers_copy(settings, issuers, ARRAYSIZE(issuers)); free(issuer1); free(issuer2); free(issuer3); if (!res) goto out_fail; } if (!server && !remote) { if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpUserAgent, FREERDP_USER_AGENT)) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpMsUserAgent, FREERDP_USER_AGENT)) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpReferer, "")) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdAccessTokenFormat, "ms-appx-web%%3a%%2f%%2fMicrosoft.AAD.BrokerPlugin%%2f%s")) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdAccessAadFormat, "https%%3A%%2F%%2F%s%%2F%s%%2Foauth2%%2Fnativeclient")) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdScope, "https%3A%2F%2Fwww.wvd.microsoft.com%2F.default%20openid%" "20profile%20offline_access")) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdClientID, "a85cf173-4192-42f8-81fa-777a763e6e2c")) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAzureActiveDirectory, "login.microsoftonline.com")) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdAadtenantid, "common")) goto out_fail; if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayAvdUseTenantid, FALSE)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopPhysicalWidth, 1000)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopPhysicalHeight, 1000)) goto out_fail; if (!freerdp_settings_set_uint16(settings, FreeRDP_DesktopOrientation, ORIENTATION_LANDSCAPE)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_DeviceScaleFactor, 100)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopScaleFactor, 100)) goto out_fail; } if (!freerdp_settings_set_uint32(settings, FreeRDP_SurfaceCommandsSupported, SURFCMDS_SET_SURFACE_BITS | SURFCMDS_STREAM_SURFACE_BITS | SURFCMDS_FRAME_MARKER)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_RemoteFxRlgrMode, RLGR3)) goto out_fail; if (!freerdp_settings_set_uint16(settings, FreeRDP_CapsProtocolVersion, TS_CAPS_PROTOCOLVERSION)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_ClipboardFeatureMask, CLIPRDR_FLAG_DEFAULT_MASK)) goto out_fail; if (!freerdp_settings_set_string(settings, FreeRDP_ServerLicenseCompanyName, freerdp_getApplicationDetailsVendor())) goto out_fail; { size_t len = 0; char* val = nullptr; (void)winpr_asprintf(&val, &len, "%s-licensing-server", freerdp_getApplicationDetailsProduct()); if (!val) goto out_fail; const BOOL rc = freerdp_settings_set_string(settings, FreeRDP_ServerLicenseProductName, val); free(val); if (!rc) goto out_fail; } if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerLicenseProductVersion, 1)) goto out_fail; if (!freerdp_settings_set_uint16(settings, FreeRDP_SupportedColorDepths, RNS_UD_32BPP_SUPPORT | RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_15BPP_SUPPORT)) goto out_fail; if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_HasHorizontalWheel, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_HasExtendedMouseEvent, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_HasQoeEvent, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_HiDefRemoteApp, TRUE) || !freerdp_settings_set_uint32( settings, FreeRDP_RemoteApplicationSupportMask, RAIL_LEVEL_SUPPORTED | RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED | RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED | RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED | RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED | RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED | RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED | RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED) || !freerdp_settings_set_uint16(settings, FreeRDP_TextANSICodePage, CP_UTF8) || !freerdp_settings_set_uint16(settings, FreeRDP_OrderSupportFlags, NEGOTIATE_ORDER_SUPPORT | ZERO_BOUNDS_DELTA_SUPPORT | COLOR_INDEX_SUPPORT) || !freerdp_settings_set_bool(settings, FreeRDP_SupportHeartbeatPdu, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_ServerMode, server) || !freerdp_settings_set_bool(settings, FreeRDP_WaitForOutputBufferFlush, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_ClusterInfoFlags, REDIRECTION_SUPPORTED) || !freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, 1024) || !freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, 768) || !freerdp_settings_set_bool(settings, FreeRDP_Workarea, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_Fullscreen, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GrabKeyboard, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_Decorations, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_RdpVersion, RDP_VERSION_10_12) || !freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32) || !freerdp_settings_set_bool(settings, FreeRDP_AadSecurity, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_NegotiateSecurityLayer, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeRequired, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeSupported, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_MstscCookieMode, FALSE) || !freerdp_settings_set_uint32(settings, FreeRDP_CookieMaxLength, DEFAULT_COOKIE_MAX_LENGTH) || !freerdp_settings_set_uint32(settings, FreeRDP_ClientBuild, 18363) || /* Windows 10, Version 1909 */ !freerdp_settings_set_uint32(settings, FreeRDP_KeyboardType, WINPR_KBD_TYPE_IBM_ENHANCED) || !freerdp_settings_set_uint32(settings, FreeRDP_KeyboardSubType, 0) || !freerdp_settings_set_uint32(settings, FreeRDP_KeyboardFunctionKey, 12) || !freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, 0) || !freerdp_settings_set_uint32(settings, FreeRDP_KeyboardHook, KEYBOARD_HOOK_FULLSCREEN_ONLY) || !freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_SaltedChecksum, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, 3389) || !freerdp_settings_set_uint32(settings, FreeRDP_GatewayPort, 443) || !freerdp_settings_set_bool(settings, FreeRDP_DesktopResize, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_ToggleFullscreen, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_DesktopPosX, UINT32_MAX) || !freerdp_settings_set_uint32(settings, FreeRDP_DesktopPosY, UINT32_MAX) || !freerdp_settings_set_bool(settings, FreeRDP_SoftwareGdi, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_UnmapButtons, FALSE) || !freerdp_settings_set_uint32(settings, FreeRDP_PerformanceFlags, PERF_FLAG_NONE) || !freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE) || !freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, CONNECTION_TYPE_AUTODETECT) || !freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_EncryptionMethods, ENCRYPTION_METHOD_NONE) || !freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel, ENCRYPTION_LEVEL_NONE) || !freerdp_settings_set_bool(settings, FreeRDP_FIPSMode, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_CompressionEnabled, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_LogonNotify, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_BrushSupportLevel, BRUSH_COLOR_FULL) || !freerdp_settings_set_uint32(settings, FreeRDP_CompressionLevel, PACKET_COMPR_TYPE_RDP61) || !freerdp_settings_set_bool(settings, FreeRDP_Authentication, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_AuthenticationOnly, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_CredentialsFromStdin, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_DisableCredentialsDelegation, FALSE) || !freerdp_settings_set_uint32(settings, FreeRDP_AuthenticationLevel, 2) || !freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, 0) || !freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, FALSE) || !freerdp_settings_set_uint32(settings, FreeRDP_KeySpec, AT_KEYEXCHANGE)) goto out_fail; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ChannelDefArray, nullptr, CHANNEL_MAX_COUNT)) goto out_fail; if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMonitorLayoutPdu, FALSE)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_MonitorCount, 0)) goto out_fail; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorDefArray, nullptr, 32)) goto out_fail; if (!freerdp_settings_set_int32(settings, FreeRDP_MonitorLocalShiftX, 0)) goto out_fail; if (!freerdp_settings_set_int32(settings, FreeRDP_MonitorLocalShiftY, 0)) goto out_fail; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorIds, nullptr, 0)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_MultitransportFlags, TRANSPORT_TYPE_UDP_FECR)) goto out_fail; if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMultitransport, TRUE)) goto out_fail; if (!settings_init_computer_name(settings)) goto out_fail; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, nullptr, 1)) goto out_fail; if (!freerdp_capability_buffer_resize(settings, 32, FALSE)) goto out_fail; { char tmp[32] = WINPR_C_ARRAY_INIT; if (!freerdp_settings_set_string_len(settings, FreeRDP_ClientProductId, tmp, sizeof(tmp))) goto out_fail; } /* [MS-RDPBCGR] 2.2.7.1.5 Pointer Capability Set (TS_POINTER_CAPABILITYSET) * * if we are in server mode send a reasonable large cache size, * if we are in client mode just set the value to the maximum we want to * support and during capability exchange that size will be limited to the * sizes the server supports * * We have chosen 128 cursors in cache which is at worst 128 * 576kB (384x384 pixel cursor with * 32bit color depth) * */ if (freerdp_settings_get_bool(settings, FreeRDP_ServerMode)) { if (!freerdp_settings_set_uint32(settings, FreeRDP_PointerCacheSize, 25) || !freerdp_settings_set_uint32(settings, FreeRDP_ColorPointerCacheSize, 25)) goto out_fail; } else { if (!freerdp_settings_set_uint32(settings, FreeRDP_PointerCacheSize, 128) || !freerdp_settings_set_uint32(settings, FreeRDP_ColorPointerCacheSize, 128)) goto out_fail; } if (!freerdp_settings_set_uint32(settings, FreeRDP_LargePointerFlag, (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384)) || !freerdp_settings_set_bool(settings, FreeRDP_SoundBeepsEnabled, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_DrawGdiPlusEnabled, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_DrawAllowSkipAlpha, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_DrawAllowColorSubsampling, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_DrawAllowDynamicColorFidelity, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_FrameMarkerCommandEnabled, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_AllowCacheWaitingList, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_BitmapCacheV2NumCells, 5)) goto out_fail; settings->BitmapCacheV2CellInfo = (BITMAP_CACHE_V2_CELL_INFO*)calloc(6, sizeof(BITMAP_CACHE_V2_CELL_INFO)); if (!settings->BitmapCacheV2CellInfo) goto out_fail; { BITMAP_CACHE_V2_CELL_INFO cache = WINPR_C_ARRAY_INIT; cache.numEntries = 600; if (!freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, 0, &cache) || !freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, 1, &cache)) goto out_fail; cache.numEntries = 2048; if (!freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, 2, &cache) || !freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, 4, &cache)) goto out_fail; cache.numEntries = 4096; if (!freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, 3, &cache)) goto out_fail; } if (!freerdp_settings_set_bool(settings, FreeRDP_NoBitmapCompressionHeader, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_RefreshRect, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_SuppressOutput, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel, GLYPH_SUPPORT_NONE)) goto out_fail; settings->GlyphCache = calloc(10, sizeof(GLYPH_CACHE_DEFINITION)); if (!settings->GlyphCache) goto out_fail; settings->FragCache = calloc(1, sizeof(GLYPH_CACHE_DEFINITION)); if (!settings->FragCache) goto out_fail; for (size_t x = 0; x < 10; x++) { GLYPH_CACHE_DEFINITION cache = WINPR_C_ARRAY_INIT; cache.cacheEntries = 254; switch (x) { case 0: case 1: cache.cacheMaximumCellSize = 4; break; case 2: case 3: cache.cacheMaximumCellSize = 8; break; case 4: cache.cacheMaximumCellSize = 16; break; case 5: cache.cacheMaximumCellSize = 32; break; case 6: cache.cacheMaximumCellSize = 64; break; case 7: cache.cacheMaximumCellSize = 128; break; case 8: cache.cacheMaximumCellSize = 256; break; case 9: cache.cacheMaximumCellSize = 256; break; default: goto out_fail; } if (!freerdp_settings_set_pointer_array(settings, FreeRDP_GlyphCache, x, &cache)) goto out_fail; } { GLYPH_CACHE_DEFINITION cache = WINPR_C_ARRAY_INIT; cache.cacheEntries = 256; cache.cacheMaximumCellSize = 256; if (!freerdp_settings_set_pointer_array(settings, FreeRDP_FragCache, 0, &cache)) goto out_fail; } if (!freerdp_settings_set_uint32(settings, FreeRDP_OffscreenSupportLevel, 0) || !freerdp_settings_set_uint32(settings, FreeRDP_OffscreenCacheSize, 7680) || !freerdp_settings_set_uint32(settings, FreeRDP_OffscreenCacheEntries, 2000) || !freerdp_settings_set_uint32(settings, FreeRDP_DrawNineGridCacheSize, 2560) || !freerdp_settings_set_uint32(settings, FreeRDP_DrawNineGridCacheEntries, 256) || !freerdp_settings_set_string(settings, FreeRDP_ClientDir, client_dll) || !freerdp_settings_get_string(settings, FreeRDP_ClientDir) || !freerdp_settings_set_uint32(settings, FreeRDP_RemoteWndSupportLevel, WINDOW_LEVEL_SUPPORTED | WINDOW_LEVEL_SUPPORTED_EX) || !freerdp_settings_set_uint32(settings, FreeRDP_RemoteAppNumIconCaches, 3) || !freerdp_settings_set_uint32(settings, FreeRDP_RemoteAppNumIconCacheEntries, 12) || !freerdp_settings_set_uint32(settings, FreeRDP_VCChunkSize, (server && !remote) ? CHANNEL_CHUNK_MAX_LENGTH : CHANNEL_CHUNK_LENGTH) || /* [MS-RDPBCGR] 2.2.7.2.7 Large Pointer Capability Set (TS_LARGE_POINTER_CAPABILITYSET) requires at least this size */ !freerdp_settings_set_uint32(settings, FreeRDP_MultifragMaxRequestSize, server ? 0 : 608299) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayUseSameCredentials, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayBypassLocal, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayUdpTransport, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpExtAuthSspiNtlm, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GatewayArmTransport, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_FastPathInput, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_FastPathOutput, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_LongCredentialsSupported, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_FrameAcknowledge, 2) || !freerdp_settings_set_bool(settings, FreeRDP_MouseMotion, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_NSCodecColorLossLevel, 3) || !freerdp_settings_set_bool(settings, FreeRDP_NSCodecAllowSubsampling, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_NSCodecAllowDynamicColorFidelity, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_AutoReconnectionEnabled, FALSE) || !freerdp_settings_set_uint32(settings, FreeRDP_AutoReconnectMaxRetries, 20) || !freerdp_settings_set_bool(settings, FreeRDP_GfxThinClient, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxSmallCache, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressiveV2, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxH264, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxSendQoeAck, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl, TRUE)) goto out_fail; { ARC_CS_PRIVATE_PACKET cookie = WINPR_C_ARRAY_INIT; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ClientAutoReconnectCookie, &cookie, 1)) goto out_fail; } { ARC_SC_PRIVATE_PACKET cookie = WINPR_C_ARRAY_INIT; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ServerAutoReconnectCookie, &cookie, 1)) goto out_fail; } settings->ClientTimeZone = (LPTIME_ZONE_INFORMATION)calloc(1, sizeof(TIME_ZONE_INFORMATION)); if (!settings->ClientTimeZone) goto out_fail; if (!settings->ServerMode) { DYNAMIC_TIME_ZONE_INFORMATION dynamic = WINPR_C_ARRAY_INIT; TIME_ZONE_INFORMATION* tz = freerdp_settings_get_pointer_writable(settings, FreeRDP_ClientTimeZone); WINPR_ASSERT(tz); GetTimeZoneInformation(tz); GetDynamicTimeZoneInformation(&dynamic); if (!freerdp_settings_set_string_from_utf16N(settings, FreeRDP_DynamicDSTTimeZoneKeyName, dynamic.TimeZoneKeyName, ARRAYSIZE(dynamic.TimeZoneKeyName))) goto out_fail; if (!freerdp_settings_set_bool(settings, FreeRDP_DynamicDaylightTimeDisabled, dynamic.DynamicDaylightTimeDisabled)) goto out_fail; } if (!freerdp_settings_set_bool(settings, FreeRDP_TcpKeepAlive, TRUE) || !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveRetries, 3) || !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveDelay, 5) || !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveInterval, 2) || !freerdp_settings_set_uint32(settings, FreeRDP_TcpAckTimeout, 9000) || !freerdp_settings_set_uint32(settings, FreeRDP_TcpConnectTimeout, 15000)) goto out_fail; if (!freerdp_settings_get_bool(settings, FreeRDP_ServerMode)) { BOOL rc = FALSE; char* path = nullptr; if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, TRUE)) goto out_fail; /* these values are used only by the client part */ path = GetKnownPath(KNOWN_PATH_HOME); rc = freerdp_settings_set_string(settings, FreeRDP_HomePath, path); free(path); if (!rc || !freerdp_settings_get_string(settings, FreeRDP_HomePath)) goto out_fail; char* config = freerdp_settings_get_config_path(); rc = freerdp_settings_set_string(settings, FreeRDP_ConfigPath, config); if (rc) { char* action = GetCombinedPath(config, "action.sh"); rc = freerdp_settings_set_string(settings, FreeRDP_ActionScript, action); free(action); } free(config); if (!rc) goto out_fail; } settings_load_hkey_local_machine(settings); if (!freerdp_settings_set_bool(settings, FreeRDP_SmartcardLogon, FALSE)) goto out_fail; if (!freerdp_settings_set_uint32(settings, FreeRDP_TlsSecLevel, 1)) goto out_fail; settings->OrderSupport = calloc(1, 32); if (!freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_VERSION)) goto out_fail; if (!freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, 0)) goto out_fail; if (!settings->OrderSupport) goto out_fail; if (!freerdp_settings_set_default_order_support(settings)) goto out_fail; { const BOOL enable = freerdp_settings_get_bool(settings, FreeRDP_ServerMode); { const FreeRDP_Settings_Keys_Bool keys[] = { FreeRDP_SupportGraphicsPipeline, FreeRDP_SupportStatusInfoPdu, FreeRDP_SupportErrorInfoPdu, FreeRDP_SupportAsymetricKeys }; for (size_t x = 0; x < ARRAYSIZE(keys); x++) { if (!freerdp_settings_set_bool(settings, keys[x], enable)) goto out_fail; } } } if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDynamicTimeZone, TRUE)) goto out_fail; if (!freerdp_settings_set_bool(settings, FreeRDP_SupportSkipChannelJoin, TRUE)) goto out_fail; return settings; out_fail: WINPR_PRAGMA_DIAG_PUSH WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC freerdp_settings_free(settings); WINPR_PRAGMA_DIAG_POP return nullptr; } static void freerdp_settings_free_internal(rdpSettings* settings) { freerdp_server_license_issuers_free(settings); freerdp_target_net_addresses_free(settings); freerdp_device_collection_free(settings); freerdp_static_channel_collection_free(settings); freerdp_dynamic_channel_collection_free(settings); freerdp_capability_buffer_free(settings); /* Free all strings, set other pointers nullptr */ freerdp_settings_free_keys(settings, TRUE); } static void freerdp_settings_free_internal_ensure_reset(rdpSettings* settings) { settings->ServerLicenseProductIssuersCount = 0; settings->ServerLicenseProductIssuers = nullptr; settings->ReceivedCapabilitiesSize = 0; } void freerdp_settings_free(rdpSettings* settings) { if (!settings) return; freerdp_settings_free_internal(settings); free(settings); } static BOOL freerdp_settings_int_buffer_copy(rdpSettings* dst, const rdpSettings* settings) { BOOL rc = FALSE; if (!dst || !settings) return FALSE; typedef struct { FreeRDP_Settings_Keys_UInt32 lenKey; FreeRDP_Settings_Keys_Pointer pointerKey; } PointerDefs; PointerDefs pointerDefs[] = { /* */ { FreeRDP_LoadBalanceInfoLength, FreeRDP_LoadBalanceInfo }, { FreeRDP_ServerRandomLength, FreeRDP_ServerRandom }, { FreeRDP_ClientRandomLength, FreeRDP_ClientRandom }, { FreeRDP_ServerCertificateLength, FreeRDP_ServerCertificate }, { FreeRDP_ChannelDefArraySize, FreeRDP_ChannelDefArray }, { FreeRDP_NumMonitorIds, FreeRDP_MonitorIds }, { FreeRDP_BitmapCacheV2NumCells, FreeRDP_BitmapCacheV2CellInfo }, { FreeRDP_RedirectionPasswordLength, FreeRDP_RedirectionPassword }, { FreeRDP_RedirectionTsvUrlLength, FreeRDP_RedirectionTsvUrl }, { FreeRDP_RedirectionGuidLength, FreeRDP_RedirectionGuid }, { FreeRDP_MonitorDefArraySize, FreeRDP_MonitorDefArray } }; for (size_t i = 0; i < ARRAYSIZE(pointerDefs); i++) { const PointerDefs* keys = &pointerDefs[i]; UINT32 n = freerdp_settings_get_uint32(settings, keys->lenKey); const void* ptr = freerdp_settings_get_pointer(settings, keys->pointerKey); if (!freerdp_settings_set_pointer_len(dst, keys->pointerKey, ptr, n)) goto out_fail; } if (!freerdp_server_license_issuers_copy(dst, settings->ServerLicenseProductIssuers, settings->ServerLicenseProductIssuersCount)) goto out_fail; if (settings->RdpServerCertificate) { rdpCertificate* cert = freerdp_certificate_clone(settings->RdpServerCertificate); if (!cert) goto out_fail; if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerCertificate, cert, 1)) goto out_fail; } else { if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerCertificate, nullptr, 0)) goto out_fail; } if (settings->RdpServerRsaKey) { rdpPrivateKey* key = freerdp_key_clone(settings->RdpServerRsaKey); if (!key) goto out_fail; if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerRsaKey, key, 1)) goto out_fail; } else { if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerRsaKey, nullptr, 0)) goto out_fail; } if (!freerdp_settings_set_uint32(dst, FreeRDP_ChannelCount, freerdp_settings_get_uint32(settings, FreeRDP_ChannelCount))) goto out_fail; if (settings->OrderSupport) { dst->OrderSupport = calloc(32, sizeof(BYTE)); if (!dst->OrderSupport) goto out_fail; CopyMemory(dst->OrderSupport, settings->OrderSupport, 32); } if (!freerdp_capability_buffer_copy(dst, settings)) goto out_fail; { const UINT32 glyphCacheCount = 10; const GLYPH_CACHE_DEFINITION* glyphCache = freerdp_settings_get_pointer(settings, FreeRDP_GlyphCache); if (!freerdp_settings_set_pointer_len(dst, FreeRDP_GlyphCache, glyphCache, glyphCacheCount)) goto out_fail; } { const UINT32 fragCacheCount = 1; const GLYPH_CACHE_DEFINITION* fragCache = freerdp_settings_get_pointer(settings, FreeRDP_FragCache); if (!freerdp_settings_set_pointer_len(dst, FreeRDP_FragCache, fragCache, fragCacheCount)) goto out_fail; } if (!freerdp_settings_set_pointer_len( dst, FreeRDP_ClientAutoReconnectCookie, freerdp_settings_get_pointer(settings, FreeRDP_ClientAutoReconnectCookie), 1)) goto out_fail; if (!freerdp_settings_set_pointer_len( dst, FreeRDP_ServerAutoReconnectCookie, freerdp_settings_get_pointer(settings, FreeRDP_ServerAutoReconnectCookie), 1)) goto out_fail; { const TIME_ZONE_INFORMATION* tz = freerdp_settings_get_pointer(settings, FreeRDP_ClientTimeZone); if (!freerdp_settings_set_pointer_len(dst, FreeRDP_ClientTimeZone, tz, 1)) goto out_fail; } { const UINT32 nrports = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); if (!freerdp_target_net_adresses_reset(dst, nrports)) goto out_fail; for (UINT32 i = 0; i < nrports; i++) { const char* address = freerdp_settings_get_pointer_array(settings, FreeRDP_TargetNetAddresses, i); const UINT32* port = freerdp_settings_get_pointer_array(settings, FreeRDP_TargetNetPorts, i); if (!freerdp_settings_set_pointer_array(dst, FreeRDP_TargetNetAddresses, i, address)) goto out_fail; if (!freerdp_settings_set_pointer_array(dst, FreeRDP_TargetNetPorts, i, port)) goto out_fail; } } { const UINT32 len = freerdp_settings_get_uint32(settings, FreeRDP_DeviceArraySize); const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_DeviceCount); if (len < count) goto out_fail; if (!freerdp_settings_set_pointer_len(dst, FreeRDP_DeviceArray, nullptr, len)) goto out_fail; if (!freerdp_settings_set_uint32(dst, FreeRDP_DeviceCount, count)) goto out_fail; for (size_t index = 0; index < count; index++) { const RDPDR_DEVICE* argv = freerdp_settings_get_pointer_array(settings, FreeRDP_DeviceArray, index); WINPR_ASSERT(argv); if (!freerdp_settings_set_pointer_array(dst, FreeRDP_DeviceArray, index, argv)) goto out_fail; } WINPR_ASSERT(dst->DeviceArray || (len == 0)); WINPR_ASSERT(len >= count); } { const UINT32 len = freerdp_settings_get_uint32(dst, FreeRDP_StaticChannelArraySize); const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_StaticChannelCount); if (len < count) goto out_fail; if (!freerdp_settings_set_pointer_len(dst, FreeRDP_StaticChannelArray, nullptr, len)) goto out_fail; if (!freerdp_settings_set_uint32(dst, FreeRDP_StaticChannelCount, count)) goto out_fail; for (size_t index = 0; index < count; index++) { const ADDIN_ARGV* argv = freerdp_settings_get_pointer_array(settings, FreeRDP_StaticChannelArray, index); WINPR_ASSERT(argv); if (!freerdp_settings_set_pointer_array(dst, FreeRDP_StaticChannelArray, index, argv)) goto out_fail; } } { const UINT32 len = freerdp_settings_get_uint32(dst, FreeRDP_DynamicChannelArraySize); const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelCount); if (len < count) goto out_fail; if (!freerdp_settings_set_pointer_len(dst, FreeRDP_DynamicChannelArray, nullptr, len)) goto out_fail; if (!freerdp_settings_set_uint32(dst, FreeRDP_DynamicChannelCount, count)) goto out_fail; for (size_t index = 0; index < count; index++) { const ADDIN_ARGV* argv = freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index); WINPR_ASSERT(argv); if (!freerdp_settings_set_pointer_array(dst, FreeRDP_DynamicChannelArray, index, argv)) goto out_fail; } } rc = freerdp_settings_set_string(dst, FreeRDP_ActionScript, freerdp_settings_get_string(settings, FreeRDP_ActionScript)); out_fail: return rc; } BOOL freerdp_settings_copy(rdpSettings* dst, const rdpSettings* src) { BOOL rc = 0; if (!dst || !src) return FALSE; /* This is required to free all non string buffers */ freerdp_settings_free_internal(dst); /* This copies everything except allocated non string buffers. reset all allocated buffers to * nullptr to fix issues during cleanup */ rc = freerdp_settings_clone_keys(dst, src); if (!rc) goto out_fail; freerdp_settings_free_internal_ensure_reset(dst); /* Begin copying */ if (!freerdp_settings_int_buffer_copy(dst, src)) goto out_fail; return TRUE; out_fail: freerdp_settings_free_internal(dst); return FALSE; } rdpSettings* freerdp_settings_clone(const rdpSettings* settings) { rdpSettings* dst = (rdpSettings*)calloc(1, sizeof(rdpSettings)); if (!freerdp_settings_copy(dst, settings)) goto out_fail; return dst; out_fail: WINPR_PRAGMA_DIAG_PUSH WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC freerdp_settings_free(dst); WINPR_PRAGMA_DIAG_POP return nullptr; } #ifdef _MSC_VER #pragma warning(pop) #endif static void zfree(WCHAR* str, size_t len) { if (str) memset(str, 0, len * sizeof(WCHAR)); free(str); } BOOL identity_set_from_settings_with_pwd(SEC_WINNT_AUTH_IDENTITY* identity, const rdpSettings* settings, FreeRDP_Settings_Keys_String UserId, FreeRDP_Settings_Keys_String DomainId, const WCHAR* Password, size_t pwdLen) { WINPR_ASSERT(identity); WINPR_ASSERT(settings); size_t UserLen = 0; size_t DomainLen = 0; WCHAR* Username = freerdp_settings_get_string_as_utf16(settings, UserId, &UserLen); WCHAR* Domain = freerdp_settings_get_string_as_utf16(settings, DomainId, &DomainLen); const int rc = sspi_SetAuthIdentityWithLengthW(identity, Username, UserLen, Domain, DomainLen, Password, pwdLen); zfree(Username, UserLen); zfree(Domain, DomainLen); return (rc >= 0); } BOOL identity_set_from_settings(SEC_WINNT_AUTH_IDENTITY_W* identity, const rdpSettings* settings, FreeRDP_Settings_Keys_String UserId, FreeRDP_Settings_Keys_String DomainId, FreeRDP_Settings_Keys_String PwdId) { WINPR_ASSERT(identity); WINPR_ASSERT(settings); size_t PwdLen = 0; WCHAR* Password = freerdp_settings_get_string_as_utf16(settings, PwdId, &PwdLen); const BOOL rc = identity_set_from_settings_with_pwd(identity, settings, UserId, DomainId, Password, PwdLen); zfree(Password, PwdLen); return rc; } BOOL identity_set_from_smartcard_hash(SEC_WINNT_AUTH_IDENTITY_W* identity, const rdpSettings* settings, FreeRDP_Settings_Keys_String userId, FreeRDP_Settings_Keys_String domainId, FreeRDP_Settings_Keys_String pwdId, WINPR_ATTR_UNUSED const BYTE* certSha1, WINPR_ATTR_UNUSED size_t sha1len) { #ifdef _WIN32 CERT_CREDENTIAL_INFO certInfo = { sizeof(CERT_CREDENTIAL_INFO), { 0 } }; LPWSTR marshalledCredentials = nullptr; memcpy(certInfo.rgbHashOfCert, certSha1, MIN(sha1len, sizeof(certInfo.rgbHashOfCert))); if (!CredMarshalCredentialW(CertCredential, &certInfo, &marshalledCredentials)) { WLog_ERR(TAG, "error marshalling cert credentials"); return FALSE; } size_t pwdLen = 0; WCHAR* Password = freerdp_settings_get_string_as_utf16(settings, pwdId, &pwdLen); const int rc = sspi_SetAuthIdentityWithLengthW(identity, marshalledCredentials, _wcslen(marshalledCredentials), nullptr, 0, Password, pwdLen); zfree(Password, pwdLen); CredFree(marshalledCredentials); return (rc >= 0); #else return identity_set_from_settings(identity, settings, userId, domainId, pwdId); #endif /* _WIN32 */ } const char* freerdp_settings_glyph_level_string(UINT32 level, char* buffer, size_t size) { const char* str = "GLYPH_SUPPORT_UNKNOWN"; switch (level) { case GLYPH_SUPPORT_NONE: str = "GLYPH_SUPPORT_NONE"; break; case GLYPH_SUPPORT_PARTIAL: str = "GLYPH_SUPPORT_PARTIAL"; break; case GLYPH_SUPPORT_FULL: str = "GLYPH_SUPPORT_FULL"; break; case GLYPH_SUPPORT_ENCODE: str = "GLYPH_SUPPORT_ENCODE"; break; default: break; } (void)_snprintf(buffer, size, "%s[0x%08" PRIx32 "]", str, level); return buffer; } BOOL freerdp_target_net_adresses_reset(rdpSettings* settings, size_t size) { freerdp_target_net_addresses_free(settings); return freerdp_target_net_addresses_resize(settings, size); } BOOL freerdp_settings_enforce_monitor_exists(rdpSettings* settings) { const UINT32 count = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); const BOOL fullscreen = freerdp_settings_get_bool(settings, FreeRDP_Fullscreen); const BOOL multimon = freerdp_settings_get_bool(settings, FreeRDP_UseMultimon); const BOOL useMonitors = fullscreen || multimon; const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_MonitorDefArraySize); if (size == 0) { if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorDefArray, nullptr, 16)) return FALSE; } if (!useMonitors || (count == 0)) { const UINT32 width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); const UINT32 height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); const UINT32 pwidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopPhysicalWidth); const UINT32 pheight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopPhysicalHeight); const UINT16 orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation); const UINT32 desktopScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor); const UINT32 deviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor); if (!freerdp_settings_set_uint32(settings, FreeRDP_MonitorCount, 1)) return FALSE; rdpMonitor monitor = WINPR_C_ARRAY_INIT; monitor.x = 0; monitor.y = 0; monitor.width = WINPR_ASSERTING_INT_CAST(int32_t, width); monitor.height = WINPR_ASSERTING_INT_CAST(int32_t, height); monitor.is_primary = TRUE; monitor.orig_screen = 0; monitor.attributes.physicalWidth = pwidth; monitor.attributes.physicalHeight = pheight; monitor.attributes.orientation = orientation; monitor.attributes.desktopScaleFactor = desktopScaleFactor; monitor.attributes.deviceScaleFactor = deviceScaleFactor; if (!freerdp_settings_set_monitor_def_array_sorted(settings, &monitor, 1)) return FALSE; } return TRUE; } BOOL freerdp_settings_enforce_consistency(rdpSettings* settings) { if (freerdp_settings_get_bool(settings, FreeRDP_BitmapCacheV3Enabled)) { settings->OrderSupportFlagsEx |= CACHE_BITMAP_V3_SUPPORT; settings->OrderSupportFlags |= ORDER_FLAGS_EXTRA_SUPPORT; } if (settings->FrameMarkerCommandEnabled) { settings->OrderSupportFlagsEx |= ALTSEC_FRAME_MARKER_SUPPORT; settings->OrderSupportFlags |= ORDER_FLAGS_EXTRA_SUPPORT; } return TRUE; }