22#include <winpr/wtypes.h>
23#include <freerdp/config.h>
28#include <winpr/assert.h>
30#include <freerdp/crypto/crypto.h>
31#include <freerdp/log.h>
32#include <freerdp/session.h>
39#define TAG FREERDP_TAG("core.info")
41#define logonInfoV2Size (2u + 4u + 4u + 4u + 4u)
42#define logonInfoV2ReservedSize 558u
43#define logonInfoV2TotalSize (logonInfoV2Size + logonInfoV2ReservedSize)
45const char* freerdp_session_logon_type_str(uint32_t type)
50 return "Logon Info V1";
51 case INFO_TYPE_LOGON_LONG:
52 return "Logon Info V2";
53 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
54 return "Logon Plain Notify";
55 case INFO_TYPE_LOGON_EXTENDED_INF:
56 return "Logon Extended Info";
58 return "INFO_TYPE_UNKNOWN";
62const char* freerdp_session_logon_type_data_str(uint32_t type,
const void* data,
char* buffer,
68 case INFO_TYPE_LOGON_LONG:
70 const logon_info* logonInfo = data;
72 (void)_snprintf(buffer, length,
"<INVALID DATA>");
74 (
void)_snprintf(buffer, length,
"%s\\%s [%" PRIu32
"]", logonInfo->domain,
75 logonInfo->username, logonInfo->sessionId);
78 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
79 memset(buffer, 0, length);
81 case INFO_TYPE_LOGON_EXTENDED_INF:
83 const logon_info_ex* logonInfo = data;
85 (void)_snprintf(buffer, length,
"<INVALID DATA>");
87 (
void)_snprintf(buffer, length,
88 "cookie: %s, LogonId: %" PRIu32
89 ", errorInfo: %s, notifyType: %" PRIu32
", notifyData: %" PRIu32,
90 logonInfo->haveCookie ?
"TRUE" :
"FALSE", logonInfo->LogonId,
91 logonInfo->haveErrorInfo ?
"TRUE" :
"FALSE",
92 logonInfo->ErrorNotificationType, logonInfo->ErrorNotificationData);
96 (void)_snprintf(buffer, length,
"<INVALID TYPE>");
103#define MAX_LABEL_LENGTH 40
110static const struct info_flags_t info_flags[] = {
111 { INFO_MOUSE,
"INFO_MOUSE" },
112 { INFO_DISABLECTRLALTDEL,
"INFO_DISABLECTRLALTDEL" },
113 { INFO_AUTOLOGON,
"INFO_AUTOLOGON" },
114 { INFO_UNICODE,
"INFO_UNICODE" },
115 { INFO_MAXIMIZESHELL,
"INFO_MAXIMIZESHELL" },
116 { INFO_LOGONNOTIFY,
"INFO_LOGONNOTIFY" },
117 { INFO_COMPRESSION,
"INFO_COMPRESSION" },
118 { INFO_ENABLEWINDOWSKEY,
"INFO_ENABLEWINDOWSKEY" },
119 { INFO_REMOTECONSOLEAUDIO,
"INFO_REMOTECONSOLEAUDIO" },
120 { INFO_FORCE_ENCRYPTED_CS_PDU,
"INFO_FORCE_ENCRYPTED_CS_PDU" },
121 { INFO_RAIL,
"INFO_RAIL" },
122 { INFO_LOGONERRORS,
"INFO_LOGONERRORS" },
123 { INFO_MOUSE_HAS_WHEEL,
"INFO_MOUSE_HAS_WHEEL" },
124 { INFO_PASSWORD_IS_SC_PIN,
"INFO_PASSWORD_IS_SC_PIN" },
125 { INFO_NOAUDIOPLAYBACK,
"INFO_NOAUDIOPLAYBACK" },
126 { INFO_USING_SAVED_CREDS,
"INFO_USING_SAVED_CREDS" },
127 { INFO_AUDIOCAPTURE,
"INFO_AUDIOCAPTURE" },
128 { INFO_VIDEO_DISABLE,
"INFO_VIDEO_DISABLE" },
129 { INFO_HIDEF_RAIL_SUPPORTED,
"INFO_HIDEF_RAIL_SUPPORTED" },
133static BOOL rdp_read_info_null_string(rdpSettings* settings, FreeRDP_Settings_Keys_String
id,
134 const char* what, UINT32 flags,
wStream* s,
size_t cbLen,
137 const BOOL unicode = (flags & INFO_UNICODE) != 0;
142 if (!Stream_CheckAndLogRequiredLength(TAG, s, (
size_t)(cbLen)))
147 if ((cbLen > max) || (unicode && ((cbLen % 2) != 0)))
149 WLog_ERR(TAG,
"protocol error: %s has invalid value: %" PRIuz
"", what, cbLen);
155 WCHAR wstr[513] = WINPR_C_ARRAY_INIT;
156 WINPR_ASSERT(ARRAYSIZE(wstr) > max);
157 memcpy(wstr, Stream_Pointer(s), cbLen);
159 const WCHAR* domain = wstr;
161 cbLen /
sizeof(WCHAR)))
163 WLog_ERR(TAG,
"protocol error: no data to read for %s [expected %" PRIuz
"]", what,
170 const char* domain = Stream_ConstPointer(s);
175 Stream_Seek(s, cbLen);
180static char* rdp_info_package_flags_description(UINT32 flags)
182 char* result =
nullptr;
183 size_t maximum_size = 1 + MAX_LABEL_LENGTH * ARRAYSIZE(info_flags);
185 result = calloc(maximum_size,
sizeof(
char));
190 for (
size_t i = 0; i < ARRAYSIZE(info_flags); i++)
192 const struct info_flags_t* cur = &info_flags[i];
193 if (cur->flag & flags)
195 winpr_str_append(cur->label, result, maximum_size,
"|");
202static BOOL rdp_compute_client_auto_reconnect_cookie(rdpRdp* rdp)
204 BYTE ClientRandom[CLIENT_RANDOM_LENGTH] = WINPR_C_ARRAY_INIT;
205 BYTE AutoReconnectRandom[32] = WINPR_C_ARRAY_INIT;
210 rdpSettings* settings = rdp->settings;
211 WINPR_ASSERT(settings);
213 serverCookie = settings->ServerAutoReconnectCookie;
214 clientCookie = settings->ClientAutoReconnectCookie;
215 clientCookie->cbLen = 28;
216 clientCookie->version = serverCookie->version;
217 clientCookie->logonId = serverCookie->logonId;
218 ZeroMemory(clientCookie->securityVerifier,
sizeof(clientCookie->securityVerifier));
219 CopyMemory(AutoReconnectRandom, serverCookie->arcRandomBits,
220 sizeof(serverCookie->arcRandomBits));
222 if (settings->SelectedProtocol == PROTOCOL_RDP)
223 CopyMemory(ClientRandom, settings->ClientRandom, settings->ClientRandomLength);
227 if (!winpr_HMAC(WINPR_MD_MD5, AutoReconnectRandom, 16, ClientRandom,
sizeof(ClientRandom),
228 clientCookie->securityVerifier,
sizeof(clientCookie->securityVerifier)))
239static BOOL rdp_read_server_auto_reconnect_cookie(rdpRdp* rdp,
wStream* s, logon_info_ex* info)
243 rdpSettings* settings = rdp->settings;
244 autoReconnectCookie = settings->ServerAutoReconnectCookie;
246 if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
249 Stream_Read_UINT32(s, autoReconnectCookie->cbLen);
251 if (autoReconnectCookie->cbLen != 28)
253 WLog_ERR(TAG,
"ServerAutoReconnectCookie.cbLen != 28");
257 Stream_Read_UINT32(s, autoReconnectCookie->version);
258 Stream_Read_UINT32(s, autoReconnectCookie->logonId);
259 Stream_Read(s, autoReconnectCookie->arcRandomBits, 16);
260 p = autoReconnectCookie->arcRandomBits;
262 "ServerAutoReconnectCookie: Version: %" PRIu32
" LogonId: %" PRIu32
263 " SecurityVerifier: "
264 "%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
266 "%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
268 autoReconnectCookie->version, autoReconnectCookie->logonId, p[0], p[1], p[2], p[3],
269 p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
270 info->LogonId = autoReconnectCookie->logonId;
271 CopyMemory(info->ArcRandomBits, p, 16);
273 if ((settings->PrintReconnectCookie))
275 char* base64 =
nullptr;
277 WLog_INFO(TAG,
"Reconnect-cookie: %s", base64);
289static BOOL rdp_read_client_auto_reconnect_cookie(rdpRdp* rdp,
wStream* s)
292 rdpSettings* settings = rdp->settings;
293 autoReconnectCookie = settings->ClientAutoReconnectCookie;
295 if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
298 Stream_Read_UINT32(s, autoReconnectCookie->cbLen);
299 Stream_Read_UINT32(s, autoReconnectCookie->version);
300 Stream_Read_UINT32(s, autoReconnectCookie->logonId);
301 Stream_Read(s, autoReconnectCookie->securityVerifier, 16);
310static BOOL rdp_write_client_auto_reconnect_cookie(rdpRdp* rdp,
wStream* s)
314 rdpSettings* settings =
nullptr;
318 settings = rdp->settings;
319 WINPR_ASSERT(settings);
321 autoReconnectCookie = settings->ClientAutoReconnectCookie;
322 WINPR_ASSERT(autoReconnectCookie);
324 p = autoReconnectCookie->securityVerifier;
328 "ClientAutoReconnectCookie: Version: %" PRIu32
" LogonId: %" PRIu32
" ArcRandomBits: "
329 "%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
331 "%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
"%02" PRIX8
333 autoReconnectCookie->version, autoReconnectCookie->logonId, p[0], p[1], p[2], p[3],
334 p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
335 if (!Stream_EnsureRemainingCapacity(s, 12ull + 16ull))
337 Stream_Write_UINT32(s, autoReconnectCookie->cbLen);
338 Stream_Write_UINT32(s, autoReconnectCookie->version);
339 Stream_Write_UINT32(s, autoReconnectCookie->logonId);
340 Stream_Write(s, autoReconnectCookie->securityVerifier, 16);
349static size_t rdp_get_client_address_max_size(
const rdpRdp* rdp)
352 rdpSettings* settings =
nullptr;
356 settings = rdp->settings;
357 WINPR_ASSERT(settings);
360 if (version < RDP_VERSION_10_0)
370static BOOL rdp_read_extended_info_packet(rdpRdp* rdp,
wStream* s)
372 UINT16 clientAddressFamily = 0;
373 UINT16 cbClientAddress = 0;
374 UINT16 cbClientDir = 0;
375 UINT16 cbAutoReconnectLen = 0;
379 rdpSettings* settings = rdp->settings;
380 WINPR_ASSERT(settings);
382 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
385 Stream_Read_UINT16(s, clientAddressFamily);
386 Stream_Read_UINT16(s, cbClientAddress);
388 settings->IPv6Enabled = ((clientAddressFamily == ADDRESS_FAMILY_INET6));
390 if (!rdp_read_info_null_string(settings, FreeRDP_ClientAddress,
"cbClientAddress", INFO_UNICODE,
391 s, cbClientAddress, rdp_get_client_address_max_size(rdp)))
394 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
397 Stream_Read_UINT16(s, cbClientDir);
407 if (!rdp_read_info_null_string(settings, FreeRDP_ClientDir,
"cbClientDir", INFO_UNICODE, s,
417 if (Stream_GetRemainingLength(s) == 0)
420 if (!rdp_read_client_time_zone(s, settings))
424 if (Stream_GetRemainingLength(s) == 0)
426 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
429 Stream_Read_UINT32(s, settings->ClientSessionId);
432 if (Stream_GetRemainingLength(s) == 0)
435 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
438 Stream_Read_UINT32(s, settings->PerformanceFlags);
439 freerdp_performance_flags_split(settings);
442 if (Stream_GetRemainingLength(s) == 0)
445 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
448 Stream_Read_UINT16(s, cbAutoReconnectLen);
452 if (cbAutoReconnectLen > 0)
454 if (!rdp_read_client_auto_reconnect_cookie(rdp, s))
459 if (Stream_GetRemainingLength(s) == 0)
462 if (!Stream_SafeSeek(s, 2))
465 if (Stream_GetRemainingLength(s) == 0)
468 if (!Stream_SafeSeek(s, 2))
471 if (Stream_GetRemainingLength(s) == 0)
474 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
479 UINT16 cbDynamicDSTTimeZoneKeyName = 0;
481 Stream_Read_UINT16(s, cbDynamicDSTTimeZoneKeyName);
483 if (!rdp_read_info_null_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName,
484 "cbDynamicDSTTimeZoneKeyName", INFO_UNICODE, s,
485 cbDynamicDSTTimeZoneKeyName, 254))
488 if (Stream_GetRemainingLength(s) == 0)
491 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
493 UINT16 DynamicDaylightTimeDisabled = 0;
494 Stream_Read_UINT16(s, DynamicDaylightTimeDisabled);
495 if (DynamicDaylightTimeDisabled > 1)
498 "[MS-RDPBCGR] 2.2.1.11.1.1.1 Extended Info Packet "
499 "(TS_EXTENDED_INFO_PACKET)::dynamicDaylightTimeDisabled value %d"
500 " not allowed in [0,1]",
501 settings->DynamicDaylightTimeDisabled);
505 DynamicDaylightTimeDisabled != 0))
507 DEBUG_TIMEZONE(
"DynamicTimeZone=%s [%s]",
523static BOOL rdp_write_extended_info_packet(rdpRdp* rdp,
wStream* s)
526 size_t cbClientAddress = 0;
527 const size_t cbClientAddressMax = rdp_get_client_address_max_size(rdp);
528 WCHAR* clientDir =
nullptr;
529 size_t cbClientDir = 0;
530 const size_t cbClientDirMax = 512;
531 UINT16 cbAutoReconnectCookie = 0;
535 rdpSettings* settings = rdp->settings;
536 WINPR_ASSERT(settings);
538 UINT16 clientAddressFamily = ADDRESS_FAMILY_INET;
539 if (settings->ConnectChildSession)
540 clientAddressFamily = 0x0000;
541 else if (settings->IPv6Enabled)
542 clientAddressFamily = ADDRESS_FAMILY_INET6;
544 WCHAR* clientAddress = ConvertUtf8ToWCharAlloc(settings->ClientAddress, &cbClientAddress);
546 if (cbClientAddress > (UINT16_MAX /
sizeof(WCHAR)))
548 WLog_ERR(TAG,
"cbClientAddress > UINT16_MAX");
552 if (cbClientAddress > 0)
554 cbClientAddress = (cbClientAddress + 1) *
sizeof(WCHAR);
555 if (cbClientAddress > cbClientAddressMax)
558 "the client address %s [%" PRIuz
"] exceeds the limit of %" PRIuz
560 settings->ClientAddress, cbClientAddress, cbClientAddressMax);
562 clientAddress[(cbClientAddressMax /
sizeof(WCHAR)) - 1] =
'\0';
563 cbClientAddress = cbClientAddressMax;
567 clientDir = ConvertUtf8ToWCharAlloc(settings->ClientDir, &cbClientDir);
568 if (cbClientDir > (UINT16_MAX /
sizeof(WCHAR)))
570 WLog_ERR(TAG,
"cbClientDir > UINT16_MAX");
576 cbClientDir = (cbClientDir + 1) *
sizeof(WCHAR);
577 if (cbClientDir > cbClientDirMax)
580 "the client dir %s [%" PRIuz
"] exceeds the limit of %" PRIuz
", truncating.",
581 settings->ClientDir, cbClientDir, cbClientDirMax);
583 clientDir[(cbClientDirMax /
sizeof(WCHAR)) - 1] =
'\0';
584 cbClientDir = cbClientDirMax;
588 if (settings->ServerAutoReconnectCookie->cbLen > UINT16_MAX)
590 WLog_ERR(TAG,
"ServerAutoreconnectCookie::cbLen > UINT16_MAX");
594 cbAutoReconnectCookie = (UINT16)settings->ServerAutoReconnectCookie->cbLen;
596 if (!Stream_EnsureRemainingCapacity(s, 4ull + cbClientAddress + 2ull + cbClientDir))
599 Stream_Write_UINT16(s, clientAddressFamily);
600 Stream_Write_UINT16(s, (UINT16)cbClientAddress);
602 Stream_Write(s, clientAddress, cbClientAddress);
604 Stream_Write_UINT16(s, (UINT16)cbClientDir);
606 Stream_Write(s, clientDir, cbClientDir);
608 if (!rdp_write_client_time_zone(s, settings))
611 if (!Stream_EnsureRemainingCapacity(s, 10ull))
615 Stream_Write_UINT32(s, settings->ClientSessionId);
616 freerdp_performance_flags_make(settings);
617 Stream_Write_UINT32(s, settings->PerformanceFlags);
618 Stream_Write_UINT16(s, cbAutoReconnectCookie);
620 if (cbAutoReconnectCookie > 0)
622 if (!rdp_compute_client_auto_reconnect_cookie(rdp))
624 if (!rdp_write_client_auto_reconnect_cookie(rdp, s))
630 if (!Stream_EnsureRemainingCapacity(s, 8 + 254 *
sizeof(WCHAR)))
633 Stream_Write_UINT16(s, 0);
634 Stream_Write_UINT16(s, 0);
641 rstrlen = strnlen(tz, 254);
642 const SSIZE_T wlen = ConvertUtf8NToWChar(tz, rstrlen,
nullptr, 0);
645 rlen = WINPR_ASSERTING_INT_CAST(
size_t, wlen);
647 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(UINT16, rlen) *
sizeof(WCHAR));
648 if (Stream_Write_UTF16_String_From_UTF8(s, rlen, tz, rstrlen, FALSE) < 0)
650 Stream_Write_UINT16(s, settings->DynamicDaylightTimeDisabled ? 0x01 : 0x00);
661static BOOL rdp_read_info_string(rdpSettings* settings, FreeRDP_Settings_Keys_String
id,
662 UINT32 flags,
wStream* s,
size_t cbLenNonNull,
size_t max)
671 const BOOL unicode = (flags & INFO_UNICODE) != 0;
672 const size_t nullSize = unicode ?
sizeof(WCHAR) : sizeof(CHAR);
677 if (!Stream_CheckAndLogRequiredLength(TAG, s, (
size_t)(cbLenNonNull + nullSize)))
680 if (cbLenNonNull > 0)
686 if ((cbLenNonNull % 2) || (cbLenNonNull > (max - nullSize)))
688 WLog_ERR(TAG,
"protocol error: invalid value: %" PRIuz
"", cbLenNonNull);
694 WCHAR wstr[513] = WINPR_C_ARRAY_INIT;
695 WINPR_ASSERT(ARRAYSIZE(wstr) > max);
697 memcpy(wstr, Stream_Pointer(s), cbLenNonNull);
698 const WCHAR* domain = wstr;
700 cbLenNonNull /
sizeof(WCHAR)))
705 const char* domain = Stream_PointerAs(s,
char);
711 Stream_Seek(s, cbLenNonNull);
713 terminator.w = L
'\0';
714 Stream_Read(s, terminator.b, nullSize);
716 if (terminator.w != L
'\0')
718 WLog_ERR(TAG,
"protocol error: Domain must be null terminated");
720 WLog_ERR(TAG,
"freerdp_settings_set_string(settings, id=%d, nullptr) failed",
id);
733static BOOL rdp_read_info_packet(rdpRdp* rdp,
wStream* s, UINT16 tpktlength)
735 BOOL smallsize = FALSE;
738 UINT16 cbUserName = 0;
739 UINT16 cbPassword = 0;
740 UINT16 cbAlternateShell = 0;
741 UINT16 cbWorkingDir = 0;
742 UINT32 CompressionLevel = 0;
743 rdpSettings* settings = rdp->settings;
745 if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, 18))
748 Stream_Read_UINT32(s, settings->KeyboardCodePage);
749 Stream_Read_UINT32(s, flags);
750 settings->AudioCapture = ((flags & INFO_AUDIOCAPTURE) != 0);
751 settings->AudioPlayback = (!(flags & INFO_NOAUDIOPLAYBACK));
752 settings->AutoLogonEnabled = ((flags & INFO_AUTOLOGON) != 0);
753 settings->RemoteApplicationMode = ((flags & INFO_RAIL) != 0);
754 settings->HiDefRemoteApp = ((flags & INFO_HIDEF_RAIL_SUPPORTED) != 0);
755 settings->RemoteConsoleAudio = ((flags & INFO_REMOTECONSOLEAUDIO) != 0);
756 settings->CompressionEnabled = ((flags & INFO_COMPRESSION) != 0);
757 settings->LogonNotify = ((flags & INFO_LOGONNOTIFY) != 0);
758 settings->MouseHasWheel = ((flags & INFO_MOUSE_HAS_WHEEL) != 0);
759 settings->DisableCtrlAltDel = ((flags & INFO_DISABLECTRLALTDEL) != 0);
760 settings->ForceEncryptedCsPdu = ((flags & INFO_FORCE_ENCRYPTED_CS_PDU) != 0);
761 settings->PasswordIsSmartcardPin = ((flags & INFO_PASSWORD_IS_SC_PIN) != 0);
763 if (flags & INFO_COMPRESSION)
765 CompressionLevel = ((flags & 0x00001E00) >> 9);
766 settings->CompressionLevel = CompressionLevel;
770 settings->CompressionLevel = 0;
774 if (settings->RdpVersion < RDP_VERSION_5_PLUS)
777 Stream_Read_UINT16(s, cbDomain);
778 Stream_Read_UINT16(s, cbUserName);
779 Stream_Read_UINT16(s, cbPassword);
780 Stream_Read_UINT16(s, cbAlternateShell);
781 Stream_Read_UINT16(s, cbWorkingDir);
783 if (!rdp_read_info_string(settings, FreeRDP_Domain, flags, s, cbDomain, smallsize ? 52 : 512))
786 if (!rdp_read_info_string(settings, FreeRDP_Username, flags, s, cbUserName,
787 smallsize ? 44 : 512))
790 if (!rdp_read_info_string(settings, FreeRDP_Password, flags, s, cbPassword,
791 smallsize ? 32 : 512))
794 if (!rdp_read_info_string(settings, FreeRDP_AlternateShell, flags, s, cbAlternateShell, 512))
797 if (!rdp_read_info_string(settings, FreeRDP_ShellWorkingDirectory, flags, s, cbWorkingDir, 512))
800 if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
802 if (!rdp_read_extended_info_packet(rdp, s))
806 const size_t xrem = Stream_GetRemainingLength(s);
807 if (!tpkt_ensure_stream_consumed(rdp->log, s, tpktlength))
808 Stream_Seek(s, xrem);
817static BOOL rdp_write_info_packet(rdpRdp* rdp,
wStream* s)
820 WCHAR* domainW =
nullptr;
822 WCHAR* userNameW =
nullptr;
823 size_t cbUserName = 0;
824 WCHAR* passwordW =
nullptr;
825 size_t cbPassword = 0;
826 WCHAR* alternateShellW =
nullptr;
827 size_t cbAlternateShell = 0;
828 WCHAR* workingDirW =
nullptr;
829 size_t cbWorkingDir = 0;
830 BOOL usedPasswordCookie = FALSE;
833 const rdpSettings* settings = rdp->settings;
834 WINPR_ASSERT(settings);
836 UINT32 flags = INFO_MOUSE | INFO_UNICODE | INFO_LOGONERRORS | INFO_MAXIMIZESHELL |
837 INFO_ENABLEWINDOWSKEY | INFO_DISABLECTRLALTDEL | INFO_MOUSE_HAS_WHEEL |
838 INFO_FORCE_ENCRYPTED_CS_PDU;
840 if (settings->SmartcardLogon)
842 flags |= INFO_AUTOLOGON;
843 flags |= INFO_PASSWORD_IS_SC_PIN;
846 if (settings->AudioCapture)
847 flags |= INFO_AUDIOCAPTURE;
849 if (!settings->AudioPlayback)
850 flags |= INFO_NOAUDIOPLAYBACK;
852 if (settings->VideoDisable)
853 flags |= INFO_VIDEO_DISABLE;
855 if (settings->AutoLogonEnabled)
856 flags |= INFO_AUTOLOGON;
858 if (settings->RemoteApplicationMode)
860 if (settings->HiDefRemoteApp)
862 if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
863 flags |= INFO_HIDEF_RAIL_SUPPORTED;
869 if (settings->RemoteConsoleAudio)
870 flags |= INFO_REMOTECONSOLEAUDIO;
872 if (settings->CompressionEnabled)
874 flags |= INFO_COMPRESSION;
875 flags |= ((settings->CompressionLevel << 9) & 0x00001E00);
878 if (settings->LogonNotify)
879 flags |= INFO_LOGONNOTIFY;
881 if (settings->PasswordIsSmartcardPin)
882 flags |= INFO_PASSWORD_IS_SC_PIN;
885 char* flags_description = rdp_info_package_flags_description(flags);
887 if (flags_description)
889 WLog_DBG(TAG,
"Client Info Packet Flags = %s", flags_description);
890 free(flags_description);
898 if (cbDomain > UINT16_MAX /
sizeof(WCHAR))
900 WLog_ERR(TAG,
"cbDomain > UINT16_MAX");
903 cbDomain *=
sizeof(WCHAR);
907 if (cbUserName > UINT16_MAX /
sizeof(WCHAR))
909 WLog_ERR(TAG,
"cbUserName > UINT16_MAX");
912 cbUserName *=
sizeof(WCHAR);
915 const char* pin =
"*";
916 if (!settings->RemoteAssistanceMode)
920 if (((flags & INFO_PASSWORD_IS_SC_PIN) == 0) && settings->RedirectionPassword &&
921 (settings->RedirectionPasswordLength > 0))
929 if (settings->RedirectionPasswordLength > UINT16_MAX)
931 WLog_ERR(TAG,
"RedirectionPasswordLength > UINT16_MAX");
934 usedPasswordCookie = TRUE;
936 ptrconv.bp = settings->RedirectionPassword;
937 passwordW = ptrconv.wp;
938 cbPassword = (UINT16)settings->RedirectionPasswordLength;
944 if (!usedPasswordCookie && pin)
946 passwordW = ConvertUtf8ToWCharAlloc(pin, &cbPassword);
947 if (cbPassword > UINT16_MAX /
sizeof(WCHAR))
949 WLog_ERR(TAG,
"cbPassword > UINT16_MAX");
952 cbPassword = (UINT16)cbPassword *
sizeof(WCHAR);
958 const char* altShell =
nullptr;
959 if (!settings->RemoteAssistanceMode)
961 else if (settings->RemoteAssistancePassStub)
966 if (altShell && strlen(altShell) > 0)
968 alternateShellW = ConvertUtf8ToWCharAlloc(altShell, &cbAlternateShell);
969 if (!alternateShellW)
971 WLog_ERR(TAG,
"alternateShellW == nullptr");
974 if (cbAlternateShell > (UINT16_MAX /
sizeof(WCHAR)))
976 WLog_ERR(TAG,
"cbAlternateShell > UINT16_MAX");
979 cbAlternateShell = (UINT16)cbAlternateShell *
sizeof(WCHAR);
984 FreeRDP_Settings_Keys_String inputId = FreeRDP_RemoteAssistanceSessionId;
986 inputId = FreeRDP_ShellWorkingDirectory;
990 if (cbWorkingDir > (UINT16_MAX /
sizeof(WCHAR)))
992 WLog_ERR(TAG,
"cbWorkingDir > UINT16_MAX");
995 cbWorkingDir = (UINT16)cbWorkingDir *
sizeof(WCHAR);
997 if (!Stream_EnsureRemainingCapacity(s, 18ull + cbDomain + cbUserName + cbPassword +
998 cbAlternateShell + cbWorkingDir + 5 *
sizeof(WCHAR)))
1001 Stream_Write_UINT32(s, settings->KeyboardCodePage);
1002 Stream_Write_UINT32(s, flags);
1003 Stream_Write_UINT16(s, (UINT16)cbDomain);
1004 Stream_Write_UINT16(s, (UINT16)cbUserName);
1005 Stream_Write_UINT16(s, (UINT16)cbPassword);
1006 Stream_Write_UINT16(s, (UINT16)cbAlternateShell);
1007 Stream_Write_UINT16(s, (UINT16)cbWorkingDir);
1009 Stream_Write(s, domainW, cbDomain);
1012 Stream_Write_UINT16(s, 0);
1014 Stream_Write(s, userNameW, cbUserName);
1017 Stream_Write_UINT16(s, 0);
1019 Stream_Write(s, passwordW, cbPassword);
1022 Stream_Write_UINT16(s, 0);
1024 Stream_Write(s, alternateShellW, cbAlternateShell);
1027 Stream_Write_UINT16(s, 0);
1029 Stream_Write(s, workingDirW, cbWorkingDir);
1032 Stream_Write_UINT16(s, 0);
1037 free(alternateShellW);
1040 if (!usedPasswordCookie)
1046 if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
1047 ret = rdp_write_extended_info_packet(rdp, s);
1059BOOL rdp_recv_client_info(rdpRdp* rdp,
wStream* s)
1062 UINT16 channelId = 0;
1063 UINT16 securityFlags = 0;
1065 WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE);
1067 if (!rdp_read_header(rdp, s, &length, &channelId))
1070 if (!rdp_read_security_header(rdp, s, &securityFlags, &length))
1073 if ((securityFlags & SEC_INFO_PKT) == 0)
1076 if (rdp->settings->UseRdpSecurityLayer)
1078 if (securityFlags & SEC_REDIRECTION_PKT)
1080 WLog_ERR(TAG,
"Error: SEC_REDIRECTION_PKT unsupported");
1084 if (securityFlags & SEC_ENCRYPT)
1086 if (!rdp_decrypt(rdp, s, &length, securityFlags))
1091 return rdp_read_info_packet(rdp, s, length);
1100BOOL rdp_send_client_info(rdpRdp* rdp)
1102 UINT16 sec_flags = SEC_INFO_PKT;
1105 s = rdp_send_stream_init(rdp, &sec_flags);
1109 WLog_ERR(TAG,
"Stream_New failed!");
1113 if (!rdp_write_info_packet(rdp, s))
1118 return rdp_send(rdp, s, MCS_GLOBAL_CHANNEL_ID, sec_flags);
1121static void rdp_free_logon_info(logon_info* info)
1126 free(info->username);
1128 const logon_info empty = WINPR_C_ARRAY_INIT;
1132static BOOL rdp_info_read_string(
const char* what,
wStream* s,
size_t size,
size_t max,
1133 BOOL skipMax,
char** dst)
1141 return Stream_SafeSeek(s, max);
1145 if (((size %
sizeof(WCHAR)) != 0) || (size > max))
1147 WLog_ERR(TAG,
"protocol error: invalid %s value: %" PRIuz
"", what, size);
1151 const WCHAR* str = Stream_ConstPointer(s);
1152 if (!Stream_SafeSeek(s, skipMax ? max : size))
1155 if (str[size /
sizeof(WCHAR) - 1])
1157 WLog_ERR(TAG,
"protocol error: %s must be null terminated", what);
1162 char* rc = ConvertWCharNToUtf8Alloc(str, size /
sizeof(WCHAR), &len);
1165 WLog_ERR(TAG,
"failed to convert the %s string", what);
1174static BOOL rdp_recv_logon_info_v1(rdpRdp* rdp,
wStream* s, logon_info* info)
1176 UINT32 cbDomain = 0;
1177 UINT32 cbUserName = 0;
1182 if (!Stream_CheckAndLogRequiredLength(TAG, s, 576))
1185 Stream_Read_UINT32(s, cbDomain);
1190 if (!rdp_info_read_string(
"Domain", s, cbDomain, 52, TRUE, &info->domain))
1193 Stream_Read_UINT32(s, cbUserName);
1198 if (!rdp_info_read_string(
"UserName", s, cbUserName, 512, TRUE, &info->username))
1201 Stream_Read_UINT32(s, info->sessionId);
1202 WLog_DBG(TAG,
"LogonInfoV1: SessionId: 0x%08" PRIX32
" UserName: [%s] Domain: [%s]",
1203 info->sessionId, info->username, info->domain);
1209static BOOL rdp_recv_logon_info_v2(rdpRdp* rdp,
wStream* s, logon_info* info)
1213 UINT32 cbDomain = 0;
1214 UINT32 cbUserName = 0;
1222 if (!Stream_CheckAndLogRequiredLength(TAG, s, logonInfoV2TotalSize))
1225 Stream_Read_UINT16(s, Version);
1226 if (Version != SAVE_SESSION_PDU_VERSION_ONE)
1228 WLog_WARN(TAG,
"LogonInfoV2::Version expected %d bytes, got %" PRIu16,
1229 SAVE_SESSION_PDU_VERSION_ONE, Version);
1233 Stream_Read_UINT32(s, Size);
1239 if (Size != logonInfoV2TotalSize)
1241 if (Size != logonInfoV2Size)
1243 WLog_WARN(TAG,
"LogonInfoV2::Size expected %" PRIu32
" bytes, got %" PRIu32,
1244 logonInfoV2TotalSize, Size);
1249 Stream_Read_UINT32(s, info->sessionId);
1250 Stream_Read_UINT32(s, cbDomain);
1251 Stream_Read_UINT32(s, cbUserName);
1252 Stream_Seek(s, logonInfoV2ReservedSize);
1260 if (!rdp_info_read_string(
"Domain", s, cbDomain, 52, FALSE, &info->domain))
1269 if (!rdp_info_read_string(
"UserName", s, cbUserName, 512, FALSE, &info->username))
1277 const size_t rem = Stream_GetRemainingLength(s);
1281 const char* str = Stream_ConstPointer(s);
1282 for (
size_t x = 0; x < rem; x++)
1289 WLog_WARN(TAG,
"unexpected padding of %" PRIuz
" bytes, data not '\\0'", rem);
1290 winpr_HexDump(TAG, WLOG_TRACE, str, rem);
1293 if (!Stream_SafeSeek(s, rem))
1298 WLog_DBG(TAG,
"LogonInfoV2: SessionId: 0x%08" PRIX32
" UserName: [%s] Domain: [%s]",
1299 info->sessionId, info->username, info->domain);
1305static BOOL rdp_recv_logon_plain_notify(rdpRdp* rdp,
wStream* s)
1308 if (!Stream_CheckAndLogRequiredLength(TAG, s, 576))
1311 Stream_Seek(s, 576);
1312 WLog_DBG(TAG,
"LogonPlainNotify");
1316static BOOL rdp_recv_logon_error_info(rdpRdp* rdp,
wStream* s, logon_info_ex* info)
1318 freerdp* instance =
nullptr;
1319 UINT32 errorNotificationType = 0;
1320 UINT32 errorNotificationData = 0;
1323 WINPR_ASSERT(rdp->context);
1327 instance = rdp->context->instance;
1328 WINPR_ASSERT(instance);
1330 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1333 Stream_Read_UINT32(s, errorNotificationType);
1334 Stream_Read_UINT32(s, errorNotificationData);
1335 WLog_DBG(TAG,
"LogonErrorInfo: Data: 0x%08" PRIX32
" Type: 0x%08" PRIX32
"",
1336 errorNotificationData, errorNotificationType);
1337 if (instance->LogonErrorInfo)
1340 instance->LogonErrorInfo(instance, errorNotificationData, errorNotificationType);
1344 info->ErrorNotificationType = errorNotificationType;
1345 info->ErrorNotificationData = errorNotificationData;
1349static BOOL rdp_recv_logon_info_extended(rdpRdp* rdp,
wStream* s, logon_info_ex* info)
1351 UINT32 cbFieldData = 0;
1352 UINT32 fieldsPresent = 0;
1359 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
1361 WLog_WARN(TAG,
"received short logon info extended, need 6 bytes, got %" PRIuz,
1362 Stream_GetRemainingLength(s));
1366 Stream_Read_UINT16(s, Length);
1367 Stream_Read_UINT32(s, fieldsPresent);
1369 if ((Length < 6) || (!Stream_CheckAndLogRequiredLength(TAG, s, (Length - 6U))))
1372 "received short logon info extended, need %" PRIu16
" - 6 bytes, got %" PRIuz,
1373 Length, Stream_GetRemainingLength(s));
1377 WLog_DBG(TAG,
"LogonInfoExtended: fieldsPresent: 0x%08" PRIX32
"", fieldsPresent);
1381 if (fieldsPresent & LOGON_EX_AUTORECONNECTCOOKIE)
1383 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1386 info->haveCookie = TRUE;
1387 Stream_Read_UINT32(s, cbFieldData);
1389 if (!Stream_CheckAndLogRequiredLength(TAG, s, cbFieldData))
1392 if (!rdp_read_server_auto_reconnect_cookie(rdp, s, info))
1396 if (fieldsPresent & LOGON_EX_LOGONERRORS)
1398 info->haveErrorInfo = TRUE;
1400 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1403 Stream_Read_UINT32(s, cbFieldData);
1405 if (!Stream_CheckAndLogRequiredLength(TAG, s, cbFieldData))
1408 if (!rdp_recv_logon_error_info(rdp, s, info))
1412 if (!Stream_CheckAndLogRequiredLength(TAG, s, 570))
1415 Stream_Seek(s, 570);
1419BOOL rdp_recv_save_session_info(rdpRdp* rdp,
wStream* s)
1421 UINT32 infoType = 0;
1422 BOOL status = FALSE;
1424 rdpContext* context = rdp->context;
1425 rdpUpdate* update = rdp->context->update;
1427 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1430 Stream_Read_UINT32(s, infoType);
1434 case INFO_TYPE_LOGON:
1436 logon_info logonInfo = WINPR_C_ARRAY_INIT;
1437 status = rdp_recv_logon_info_v1(rdp, s, &logonInfo);
1439 if (status && update->SaveSessionInfo)
1440 status = update->SaveSessionInfo(context, infoType, &logonInfo);
1442 rdp_free_logon_info(&logonInfo);
1446 case INFO_TYPE_LOGON_LONG:
1448 logon_info logonInfo = WINPR_C_ARRAY_INIT;
1449 status = rdp_recv_logon_info_v2(rdp, s, &logonInfo);
1451 if (status && update->SaveSessionInfo)
1452 status = update->SaveSessionInfo(context, infoType, &logonInfo);
1454 rdp_free_logon_info(&logonInfo);
1458 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
1459 status = rdp_recv_logon_plain_notify(rdp, s);
1461 if (status && update->SaveSessionInfo)
1462 status = update->SaveSessionInfo(context, infoType,
nullptr);
1466 case INFO_TYPE_LOGON_EXTENDED_INF:
1468 logon_info_ex logonInfoEx = WINPR_C_ARRAY_INIT;
1469 status = rdp_recv_logon_info_extended(rdp, s, &logonInfoEx);
1471 if (status && update->SaveSessionInfo)
1472 status = update->SaveSessionInfo(context, infoType, &logonInfoEx);
1477 WLog_WARN(TAG,
"Unhandled saveSessionInfo type 0x%" PRIx32
"", infoType);
1484 WLog_WARN(TAG,
"SaveSessionInfo error: infoType: %s (%" PRIu32
")",
1485 freerdp_session_logon_type_str(infoType), infoType);
1491static BOOL rdp_write_logon_info_v1(
wStream* s,
const logon_info* info)
1493 const size_t charLen = 52 /
sizeof(WCHAR);
1494 const size_t userCharLen = 512 /
sizeof(WCHAR);
1496 size_t sz = 4 + 52 + 4 + 512 + 4;
1498 if (!Stream_EnsureRemainingCapacity(s, sz))
1504 if (!info->domain || !info->username)
1506 const size_t len = strnlen(info->domain, charLen + 1);
1510 const SSIZE_T wclen = ConvertUtf8NToWChar(info->domain, len,
nullptr, 0);
1513 const size_t wzerolen = WINPR_ASSERTING_INT_CAST(
size_t, wclen) + 1ull;
1514 const size_t wlen = wzerolen *
sizeof(WCHAR);
1515 if (wlen > UINT32_MAX)
1518 Stream_Write_UINT32(s, (UINT32)wlen);
1519 if (Stream_Write_UTF16_String_From_UTF8(s, wzerolen, info->domain, len, TRUE) < 0)
1525 const size_t len = strnlen(info->username, userCharLen + 1);
1526 if (len > userCharLen)
1529 const SSIZE_T wclen = ConvertUtf8NToWChar(info->username, len,
nullptr, 0);
1532 const size_t wzerolen = WINPR_ASSERTING_INT_CAST(
size_t, wclen) + 1ull;
1533 const size_t wlen = wzerolen *
sizeof(WCHAR);
1534 if (wlen > UINT32_MAX)
1537 Stream_Write_UINT32(s, (UINT32)wlen);
1539 if (Stream_Write_UTF16_String_From_UTF8(s, wzerolen, info->username, len, TRUE) < 0)
1544 Stream_Write_UINT32(s, info->sessionId);
1548static BOOL rdp_write_logon_info_v2(
wStream* s,
const logon_info* info)
1550 size_t domainLen = 0;
1551 size_t usernameLen = 0;
1552 size_t domainStrLen = 0;
1553 size_t usernameStrLen = 0;
1555 if (!Stream_EnsureRemainingCapacity(s, logonInfoV2TotalSize))
1558 Stream_Write_UINT16(s, SAVE_SESSION_PDU_VERSION_ONE);
1563 Stream_Write_UINT32(s, logonInfoV2Size);
1564 Stream_Write_UINT32(s, info->sessionId);
1567 domainStrLen = strnlen(info->domain, 256);
1568 const SSIZE_T wlen = ConvertUtf8NToWChar(info->domain, domainStrLen,
nullptr, 0);
1571 domainLen = WINPR_ASSERTING_INT_CAST(
size_t, wlen);
1573 if (domainLen >= (UINT32_MAX - 1) /
sizeof(WCHAR))
1575 Stream_Write_UINT32(s, (UINT32)(domainLen + 1) *
sizeof(WCHAR));
1579 usernameStrLen = strnlen(info->username, 256);
1580 const SSIZE_T wlen = ConvertUtf8NToWChar(info->username, usernameStrLen,
nullptr, 0);
1583 usernameLen = WINPR_ASSERTING_INT_CAST(
size_t, wlen);
1585 if (usernameLen >= (UINT32_MAX - 1) /
sizeof(WCHAR))
1587 Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) *
sizeof(WCHAR));
1588 Stream_Seek(s, logonInfoV2ReservedSize);
1589 if (Stream_Write_UTF16_String_From_UTF8(s, domainLen + 1, info->domain, domainStrLen, TRUE) < 0)
1591 if (Stream_Write_UTF16_String_From_UTF8(s, usernameLen + 1, info->username, usernameStrLen,
1597static BOOL rdp_write_logon_info_plain(
wStream* s)
1599 if (!Stream_EnsureRemainingCapacity(s, 576))
1602 Stream_Zero(s, 576);
1606static BOOL rdp_write_logon_info_ex(
wStream* s,
const logon_info_ex* info)
1608 UINT32 FieldsPresent = 0;
1609 UINT16 Size = 2 + 4 + 570;
1611 if (info->haveCookie)
1613 FieldsPresent |= LOGON_EX_AUTORECONNECTCOOKIE;
1617 if (info->haveErrorInfo)
1619 FieldsPresent |= LOGON_EX_LOGONERRORS;
1623 if (!Stream_EnsureRemainingCapacity(s, Size))
1626 Stream_Write_UINT16(s, Size);
1627 Stream_Write_UINT32(s, FieldsPresent);
1629 if (info->haveCookie)
1631 Stream_Write_UINT32(s, 28);
1632 Stream_Write_UINT32(s, 28);
1633 Stream_Write_UINT32(s, AUTO_RECONNECT_VERSION_1);
1634 Stream_Write_UINT32(s, info->LogonId);
1635 Stream_Write(s, info->ArcRandomBits, 16);
1638 if (info->haveErrorInfo)
1640 Stream_Write_UINT32(s, 8);
1641 Stream_Write_UINT32(s, info->ErrorNotificationType);
1642 Stream_Write_UINT32(s, info->ErrorNotificationData);
1645 Stream_Seek(s, 570);
1649BOOL rdp_send_save_session_info(rdpContext* context, UINT32 type,
const void* data)
1651 UINT16 sec_flags = 0;
1654 WINPR_ASSERT(context);
1655 rdpRdp* rdp = context->rdp;
1656 wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
1661 Stream_Write_UINT32(s, type);
1665 case INFO_TYPE_LOGON:
1666 status = rdp_write_logon_info_v1(s, (
const logon_info*)data);
1669 case INFO_TYPE_LOGON_LONG:
1670 status = rdp_write_logon_info_v2(s, (
const logon_info*)data);
1673 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
1674 status = rdp_write_logon_info_plain(s);
1677 case INFO_TYPE_LOGON_EXTENDED_INF:
1678 status = rdp_write_logon_info_ex(s, (
const logon_info_ex*)data);
1682 WLog_ERR(TAG,
"saveSessionInfo type 0x%" PRIx32
" not handled", type);
1689 rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SAVE_SESSION_INFO, rdp->mcs->userId, sec_flags);
1696BOOL rdp_send_server_status_info(rdpContext* context, UINT32 status)
1698 UINT16 sec_flags = 0;
1700 rdpRdp* rdp = context->rdp;
1701 s = rdp_data_pdu_init(rdp, &sec_flags);
1706 Stream_Write_UINT32(s, status);
1707 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_STATUS_INFO, rdp->mcs->userId, sec_flags);
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string_from_utf16N(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const WCHAR *param, size_t length)
Sets a string settings value. The param is converted to UTF-8 and the copy stored.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string_len(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val, size_t len)
Sets a string settings value. The val is copied.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API WCHAR * freerdp_settings_get_string_as_utf16(const rdpSettings *settings, FreeRDP_Settings_Keys_String id, size_t *pCharLen)
Return an allocated UTF16 string.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.