FreeRDP
Loading...
Searching...
No Matches
info.c
1
22#include <winpr/wtypes.h>
23#include <freerdp/config.h>
24
25#include "settings.h"
26
27#include <winpr/crt.h>
28#include <winpr/assert.h>
29
30#include <freerdp/crypto/crypto.h>
31#include <freerdp/log.h>
32#include <freerdp/session.h>
33#include <stdio.h>
34
35#include "timezone.h"
36
37#include "info.h"
38
39#define TAG FREERDP_TAG("core.info")
40
41#define logonInfoV2Size (2u + 4u + 4u + 4u + 4u)
42#define logonInfoV2ReservedSize 558u
43#define logonInfoV2TotalSize (logonInfoV2Size + logonInfoV2ReservedSize)
44
45const char* freerdp_session_logon_type_str(uint32_t type)
46{
47 switch (type)
48 {
49 case INFO_TYPE_LOGON:
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";
57 default:
58 return "INFO_TYPE_UNKNOWN";
59 }
60}
61
62const char* freerdp_session_logon_type_data_str(uint32_t type, const void* data, char* buffer,
63 size_t length)
64{
65 switch (type)
66 {
67 case INFO_TYPE_LOGON:
68 case INFO_TYPE_LOGON_LONG:
69 {
70 const logon_info* logonInfo = data;
71 if (!logonInfo)
72 (void)_snprintf(buffer, length, "<INVALID DATA>");
73 else
74 (void)_snprintf(buffer, length, "%s\\%s [%" PRIu32 "]", logonInfo->domain,
75 logonInfo->username, logonInfo->sessionId);
76 }
77 break;
78 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
79 (void)_snprintf(buffer, length, "");
80 break;
81 case INFO_TYPE_LOGON_EXTENDED_INF:
82 {
83 const logon_info_ex* logonInfo = data;
84 if (!logonInfo)
85 (void)_snprintf(buffer, length, "<INVALID DATA>");
86 else
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);
93 }
94 break;
95 default:
96 (void)_snprintf(buffer, length, "<INVALID TYPE>");
97 break;
98 }
99 return buffer;
100}
101
102/* This define limits the length of the strings in the label field. */
103#define MAX_LABEL_LENGTH 40
104struct info_flags_t
105{
106 UINT32 flag;
107 const char* label;
108};
109
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" },
130};
131
132static BOOL rdp_read_info_null_string(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
133 const char* what, UINT32 flags, wStream* s, size_t cbLen,
134 size_t max)
135{
136 const BOOL unicode = (flags & INFO_UNICODE) != 0;
137
138 if (!freerdp_settings_set_string(settings, id, nullptr))
139 return FALSE;
140
141 if (!Stream_CheckAndLogRequiredLength(TAG, s, (size_t)(cbLen)))
142 return FALSE;
143
144 if (cbLen > 0)
145 {
146 if ((cbLen > max) || (unicode && ((cbLen % 2) != 0)))
147 {
148 WLog_ERR(TAG, "protocol error: %s has invalid value: %" PRIuz "", what, cbLen);
149 return FALSE;
150 }
151
152 if (unicode)
153 {
154 const WCHAR* domain = Stream_PointerAs(s, WCHAR);
155 if (!freerdp_settings_set_string_from_utf16N(settings, id, domain,
156 cbLen / sizeof(WCHAR)))
157 {
158 WLog_ERR(TAG, "protocol error: no data to read for %s [expected %" PRIuz "]", what,
159 cbLen);
160 return FALSE;
161 }
162 }
163 else
164 {
165 const char* domain = Stream_ConstPointer(s);
166 if (!freerdp_settings_set_string_len(settings, id, domain, cbLen))
167 return FALSE;
168 }
169 }
170 Stream_Seek(s, cbLen);
171
172 return TRUE;
173}
174
175static char* rdp_info_package_flags_description(UINT32 flags)
176{
177 char* result = nullptr;
178 size_t maximum_size = 1 + MAX_LABEL_LENGTH * ARRAYSIZE(info_flags);
179
180 result = calloc(maximum_size, sizeof(char));
181
182 if (!result)
183 return nullptr;
184
185 for (size_t i = 0; i < ARRAYSIZE(info_flags); i++)
186 {
187 const struct info_flags_t* cur = &info_flags[i];
188 if (cur->flag & flags)
189 {
190 winpr_str_append(cur->label, result, maximum_size, "|");
191 }
192 }
193
194 return result;
195}
196
197static BOOL rdp_compute_client_auto_reconnect_cookie(rdpRdp* rdp)
198{
199 BYTE ClientRandom[CLIENT_RANDOM_LENGTH] = WINPR_C_ARRAY_INIT;
200 BYTE AutoReconnectRandom[32] = WINPR_C_ARRAY_INIT;
201 ARC_SC_PRIVATE_PACKET* serverCookie = nullptr;
202 ARC_CS_PRIVATE_PACKET* clientCookie = nullptr;
203
204 WINPR_ASSERT(rdp);
205 rdpSettings* settings = rdp->settings;
206 WINPR_ASSERT(settings);
207
208 serverCookie = settings->ServerAutoReconnectCookie;
209 clientCookie = settings->ClientAutoReconnectCookie;
210 clientCookie->cbLen = 28;
211 clientCookie->version = serverCookie->version;
212 clientCookie->logonId = serverCookie->logonId;
213 ZeroMemory(clientCookie->securityVerifier, sizeof(clientCookie->securityVerifier));
214 CopyMemory(AutoReconnectRandom, serverCookie->arcRandomBits,
215 sizeof(serverCookie->arcRandomBits));
216
217 if (settings->SelectedProtocol == PROTOCOL_RDP)
218 CopyMemory(ClientRandom, settings->ClientRandom, settings->ClientRandomLength);
219
220 /* SecurityVerifier = HMAC_MD5(AutoReconnectRandom, ClientRandom) */
221
222 if (!winpr_HMAC(WINPR_MD_MD5, AutoReconnectRandom, 16, ClientRandom, sizeof(ClientRandom),
223 clientCookie->securityVerifier, sizeof(clientCookie->securityVerifier)))
224 return FALSE;
225
226 return TRUE;
227}
228
234static BOOL rdp_read_server_auto_reconnect_cookie(rdpRdp* rdp, wStream* s, logon_info_ex* info)
235{
236 BYTE* p = nullptr;
237 ARC_SC_PRIVATE_PACKET* autoReconnectCookie = nullptr;
238 rdpSettings* settings = rdp->settings;
239 autoReconnectCookie = settings->ServerAutoReconnectCookie;
240
241 if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
242 return FALSE;
243
244 Stream_Read_UINT32(s, autoReconnectCookie->cbLen); /* cbLen (4 bytes) */
245
246 if (autoReconnectCookie->cbLen != 28)
247 {
248 WLog_ERR(TAG, "ServerAutoReconnectCookie.cbLen != 28");
249 return FALSE;
250 }
251
252 Stream_Read_UINT32(s, autoReconnectCookie->version); /* Version (4 bytes) */
253 Stream_Read_UINT32(s, autoReconnectCookie->logonId); /* LogonId (4 bytes) */
254 Stream_Read(s, autoReconnectCookie->arcRandomBits, 16); /* ArcRandomBits (16 bytes) */
255 p = autoReconnectCookie->arcRandomBits;
256 WLog_DBG(TAG,
257 "ServerAutoReconnectCookie: Version: %" PRIu32 " LogonId: %" PRIu32
258 " SecurityVerifier: "
259 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8
260 "%02" PRIX8 ""
261 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8
262 "%02" PRIX8 "",
263 autoReconnectCookie->version, autoReconnectCookie->logonId, p[0], p[1], p[2], p[3],
264 p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
265 info->LogonId = autoReconnectCookie->logonId;
266 CopyMemory(info->ArcRandomBits, p, 16);
267
268 if ((settings->PrintReconnectCookie))
269 {
270 char* base64 = nullptr;
271 base64 = crypto_base64_encode((BYTE*)autoReconnectCookie, sizeof(ARC_SC_PRIVATE_PACKET));
272 WLog_INFO(TAG, "Reconnect-cookie: %s", base64);
273 free(base64);
274 }
275
276 return TRUE;
277}
278
284static BOOL rdp_read_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s)
285{
286 ARC_CS_PRIVATE_PACKET* autoReconnectCookie = nullptr;
287 rdpSettings* settings = rdp->settings;
288 autoReconnectCookie = settings->ClientAutoReconnectCookie;
289
290 if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
291 return FALSE;
292
293 Stream_Read_UINT32(s, autoReconnectCookie->cbLen); /* cbLen (4 bytes) */
294 Stream_Read_UINT32(s, autoReconnectCookie->version); /* version (4 bytes) */
295 Stream_Read_UINT32(s, autoReconnectCookie->logonId); /* LogonId (4 bytes) */
296 Stream_Read(s, autoReconnectCookie->securityVerifier, 16); /* SecurityVerifier */
297 return TRUE;
298}
299
305static BOOL rdp_write_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s)
306{
307 BYTE* p = nullptr;
308 ARC_CS_PRIVATE_PACKET* autoReconnectCookie = nullptr;
309 rdpSettings* settings = nullptr;
310
311 WINPR_ASSERT(rdp);
312
313 settings = rdp->settings;
314 WINPR_ASSERT(settings);
315
316 autoReconnectCookie = settings->ClientAutoReconnectCookie;
317 WINPR_ASSERT(autoReconnectCookie);
318
319 p = autoReconnectCookie->securityVerifier;
320 WINPR_ASSERT(p);
321
322 WLog_DBG(TAG,
323 "ClientAutoReconnectCookie: Version: %" PRIu32 " LogonId: %" PRIu32 " ArcRandomBits: "
324 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8
325 "%02" PRIX8 ""
326 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8
327 "%02" PRIX8 "",
328 autoReconnectCookie->version, autoReconnectCookie->logonId, p[0], p[1], p[2], p[3],
329 p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
330 if (!Stream_EnsureRemainingCapacity(s, 12ull + 16ull))
331 return FALSE;
332 Stream_Write_UINT32(s, autoReconnectCookie->cbLen); /* cbLen (4 bytes) */
333 Stream_Write_UINT32(s, autoReconnectCookie->version); /* version (4 bytes) */
334 Stream_Write_UINT32(s, autoReconnectCookie->logonId); /* LogonId (4 bytes) */
335 Stream_Write(s, autoReconnectCookie->securityVerifier, 16); /* SecurityVerifier (16 bytes) */
336 return TRUE;
337}
338
339/*
340 * Get the cbClientAddress size limit
341 * see [MS-RDPBCGR] 2.2.1.11.1.1.1 Extended Info Packet (TS_EXTENDED_INFO_PACKET)
342 */
343
344static size_t rdp_get_client_address_max_size(const rdpRdp* rdp)
345{
346 UINT32 version = 0;
347 rdpSettings* settings = nullptr;
348
349 WINPR_ASSERT(rdp);
350
351 settings = rdp->settings;
352 WINPR_ASSERT(settings);
353
354 version = freerdp_settings_get_uint32(settings, FreeRDP_RdpVersion);
355 if (version < RDP_VERSION_10_0)
356 return 64;
357 return 80;
358}
359
365static BOOL rdp_read_extended_info_packet(rdpRdp* rdp, wStream* s)
366{
367 UINT16 clientAddressFamily = 0;
368 UINT16 cbClientAddress = 0;
369 UINT16 cbClientDir = 0;
370 UINT16 cbAutoReconnectLen = 0;
371
372 WINPR_ASSERT(rdp);
373
374 rdpSettings* settings = rdp->settings;
375 WINPR_ASSERT(settings);
376
377 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
378 return FALSE;
379
380 Stream_Read_UINT16(s, clientAddressFamily); /* clientAddressFamily (2 bytes) */
381 Stream_Read_UINT16(s, cbClientAddress); /* cbClientAddress (2 bytes) */
382
383 settings->IPv6Enabled = ((clientAddressFamily == ADDRESS_FAMILY_INET6));
384
385 if (!rdp_read_info_null_string(settings, FreeRDP_ClientAddress, "cbClientAddress", INFO_UNICODE,
386 s, cbClientAddress, rdp_get_client_address_max_size(rdp)))
387 return FALSE;
388
389 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
390 return FALSE;
391
392 Stream_Read_UINT16(s, cbClientDir); /* cbClientDir (2 bytes) */
393
394 /* cbClientDir is the size in bytes of the character data in the clientDir field.
395 * This size includes the length of the mandatory null terminator.
396 * The maximum allowed value is 512 bytes.
397 * Note: Although according to [MS-RDPBCGR 2.2.1.11.1.1.1] the null terminator
398 * is mandatory the Microsoft Android client (starting with version 8.1.31.44)
399 * sets cbClientDir to 0.
400 */
401
402 if (!rdp_read_info_null_string(settings, FreeRDP_ClientDir, "cbClientDir", INFO_UNICODE, s,
403 cbClientDir, 512))
404 return FALSE;
405
411 /* optional: clientTimeZone (172 bytes) */
412 if (Stream_GetRemainingLength(s) == 0)
413 goto end;
414
415 if (!rdp_read_client_time_zone(s, settings))
416 return FALSE;
417
418 /* optional: clientSessionId (4 bytes), should be set to 0 */
419 if (Stream_GetRemainingLength(s) == 0)
420 goto end;
421 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
422 return FALSE;
423
424 Stream_Read_UINT32(s, settings->ClientSessionId);
425
426 /* optional: performanceFlags (4 bytes) */
427 if (Stream_GetRemainingLength(s) == 0)
428 goto end;
429
430 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
431 return FALSE;
432
433 Stream_Read_UINT32(s, settings->PerformanceFlags);
434 freerdp_performance_flags_split(settings);
435
436 /* optional: cbAutoReconnectLen (2 bytes) */
437 if (Stream_GetRemainingLength(s) == 0)
438 goto end;
439
440 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
441 return FALSE;
442
443 Stream_Read_UINT16(s, cbAutoReconnectLen);
444
445 /* optional: autoReconnectCookie (28 bytes) */
446 /* must be present if cbAutoReconnectLen is > 0 */
447 if (cbAutoReconnectLen > 0)
448 {
449 if (!rdp_read_client_auto_reconnect_cookie(rdp, s))
450 return FALSE;
451 }
452
453 /* skip reserved1 and reserved2 fields */
454 if (Stream_GetRemainingLength(s) == 0)
455 goto end;
456
457 if (!Stream_SafeSeek(s, 2))
458 return FALSE;
459
460 if (Stream_GetRemainingLength(s) == 0)
461 goto end;
462
463 if (!Stream_SafeSeek(s, 2))
464 return FALSE;
465
466 if (Stream_GetRemainingLength(s) == 0)
467 goto end;
468
469 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
470 return FALSE;
471
472 if (freerdp_settings_get_bool(settings, FreeRDP_SupportDynamicTimeZone))
473 {
474 UINT16 cbDynamicDSTTimeZoneKeyName = 0;
475
476 Stream_Read_UINT16(s, cbDynamicDSTTimeZoneKeyName);
477
478 if (!rdp_read_info_null_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName,
479 "cbDynamicDSTTimeZoneKeyName", INFO_UNICODE, s,
480 cbDynamicDSTTimeZoneKeyName, 254))
481 return FALSE;
482
483 if (Stream_GetRemainingLength(s) == 0)
484 goto end;
485
486 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
487 return FALSE;
488 UINT16 DynamicDaylightTimeDisabled = 0;
489 Stream_Read_UINT16(s, DynamicDaylightTimeDisabled);
490 if (DynamicDaylightTimeDisabled > 1)
491 {
492 WLog_WARN(TAG,
493 "[MS-RDPBCGR] 2.2.1.11.1.1.1 Extended Info Packet "
494 "(TS_EXTENDED_INFO_PACKET)::dynamicDaylightTimeDisabled value %d"
495 " not allowed in [0,1]",
496 settings->DynamicDaylightTimeDisabled);
497 return FALSE;
498 }
499 if (!freerdp_settings_set_bool(settings, FreeRDP_DynamicDaylightTimeDisabled,
500 DynamicDaylightTimeDisabled != 0))
501 return FALSE;
502 DEBUG_TIMEZONE("DynamicTimeZone=%s [%s]",
503 freerdp_settings_get_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName),
504 freerdp_settings_get_bool(settings, FreeRDP_DynamicDaylightTimeDisabled)
505 ? "no-DST"
506 : "DST");
507 }
508
509end:
510 return TRUE;
511}
512
518static BOOL rdp_write_extended_info_packet(rdpRdp* rdp, wStream* s)
519{
520 BOOL ret = FALSE;
521 size_t cbClientAddress = 0;
522 const size_t cbClientAddressMax = rdp_get_client_address_max_size(rdp);
523 WCHAR* clientDir = nullptr;
524 size_t cbClientDir = 0;
525 const size_t cbClientDirMax = 512;
526 UINT16 cbAutoReconnectCookie = 0;
527
528 WINPR_ASSERT(rdp);
529
530 rdpSettings* settings = rdp->settings;
531 WINPR_ASSERT(settings);
532
533 UINT16 clientAddressFamily = ADDRESS_FAMILY_INET;
534 if (settings->ConnectChildSession)
535 clientAddressFamily = 0x0000;
536 else if (settings->IPv6Enabled)
537 clientAddressFamily = ADDRESS_FAMILY_INET6;
538
539 WCHAR* clientAddress = ConvertUtf8ToWCharAlloc(settings->ClientAddress, &cbClientAddress);
540
541 if (cbClientAddress > (UINT16_MAX / sizeof(WCHAR)))
542 {
543 WLog_ERR(TAG, "cbClientAddress > UINT16_MAX");
544 goto fail;
545 }
546
547 if (cbClientAddress > 0)
548 {
549 cbClientAddress = (cbClientAddress + 1) * sizeof(WCHAR);
550 if (cbClientAddress > cbClientAddressMax)
551 {
552 WLog_WARN(TAG,
553 "the client address %s [%" PRIuz "] exceeds the limit of %" PRIuz
554 ", truncating.",
555 settings->ClientAddress, cbClientAddress, cbClientAddressMax);
556
557 clientAddress[(cbClientAddressMax / sizeof(WCHAR)) - 1] = '\0';
558 cbClientAddress = cbClientAddressMax;
559 }
560 }
561
562 clientDir = ConvertUtf8ToWCharAlloc(settings->ClientDir, &cbClientDir);
563 if (cbClientDir > (UINT16_MAX / sizeof(WCHAR)))
564 {
565 WLog_ERR(TAG, "cbClientDir > UINT16_MAX");
566 goto fail;
567 }
568
569 if (cbClientDir > 0)
570 {
571 cbClientDir = (cbClientDir + 1) * sizeof(WCHAR);
572 if (cbClientDir > cbClientDirMax)
573 {
574 WLog_WARN(TAG,
575 "the client dir %s [%" PRIuz "] exceeds the limit of %" PRIuz ", truncating.",
576 settings->ClientDir, cbClientDir, cbClientDirMax);
577
578 clientDir[(cbClientDirMax / sizeof(WCHAR)) - 1] = '\0';
579 cbClientDir = cbClientDirMax;
580 }
581 }
582
583 if (settings->ServerAutoReconnectCookie->cbLen > UINT16_MAX)
584 {
585 WLog_ERR(TAG, "ServerAutoreconnectCookie::cbLen > UINT16_MAX");
586 goto fail;
587 }
588
589 cbAutoReconnectCookie = (UINT16)settings->ServerAutoReconnectCookie->cbLen;
590
591 if (!Stream_EnsureRemainingCapacity(s, 4ull + cbClientAddress + 2ull + cbClientDir))
592 goto fail;
593
594 Stream_Write_UINT16(s, clientAddressFamily); /* clientAddressFamily (2 bytes) */
595 Stream_Write_UINT16(s, (UINT16)cbClientAddress); /* cbClientAddress (2 bytes) */
596
597 Stream_Write(s, clientAddress, cbClientAddress); /* clientAddress */
598
599 Stream_Write_UINT16(s, (UINT16)cbClientDir); /* cbClientDir (2 bytes) */
600
601 Stream_Write(s, clientDir, cbClientDir); /* clientDir */
602
603 if (!rdp_write_client_time_zone(s, settings)) /* clientTimeZone (172 bytes) */
604 goto fail;
605
606 if (!Stream_EnsureRemainingCapacity(s, 10ull))
607 goto fail;
608
609 /* clientSessionId (4 bytes), should be set to 0 */
610 Stream_Write_UINT32(s, settings->ClientSessionId);
611 freerdp_performance_flags_make(settings);
612 Stream_Write_UINT32(s, settings->PerformanceFlags); /* performanceFlags (4 bytes) */
613 Stream_Write_UINT16(s, cbAutoReconnectCookie); /* cbAutoReconnectCookie (2 bytes) */
614
615 if (cbAutoReconnectCookie > 0)
616 {
617 if (!rdp_compute_client_auto_reconnect_cookie(rdp))
618 goto fail;
619 if (!rdp_write_client_auto_reconnect_cookie(rdp, s)) /* autoReconnectCookie */
620 goto fail;
621 }
622
623 if (freerdp_settings_get_bool(settings, FreeRDP_SupportDynamicTimeZone))
624 {
625 if (!Stream_EnsureRemainingCapacity(s, 8 + 254 * sizeof(WCHAR)))
626 goto fail;
627
628 Stream_Write_UINT16(s, 0); /* reserved1 (2 bytes) */
629 Stream_Write_UINT16(s, 0); /* reserved2 (2 bytes) */
630
631 size_t rstrlen = 0;
632 size_t rlen = 0;
633 const char* tz = freerdp_settings_get_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName);
634 if (tz)
635 {
636 rstrlen = strnlen(tz, 254);
637 const SSIZE_T wlen = ConvertUtf8NToWChar(tz, rstrlen, nullptr, 0);
638 if (wlen < 0)
639 goto fail;
640 rlen = WINPR_ASSERTING_INT_CAST(size_t, wlen);
641 }
642 Stream_Write_UINT16(s, (UINT16)rlen * sizeof(WCHAR));
643 if (Stream_Write_UTF16_String_From_UTF8(s, rlen, tz, rstrlen, FALSE) < 0)
644 goto fail;
645 Stream_Write_UINT16(s, settings->DynamicDaylightTimeDisabled ? 0x01 : 0x00);
646 }
647
648 ret = TRUE;
649fail:
650 free(clientAddress);
651 free(clientDir);
652 return ret;
653}
654
655static BOOL rdp_read_info_string(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
656 UINT32 flags, wStream* s, size_t cbLenNonNull, size_t max)
657{
658 union
659 {
660 char c;
661 WCHAR w;
662 BYTE b[2];
663 } terminator;
664
665 const BOOL unicode = (flags & INFO_UNICODE) != 0;
666 const size_t nullSize = unicode ? sizeof(WCHAR) : sizeof(CHAR);
667
668 if (!freerdp_settings_set_string(settings, id, nullptr))
669 return FALSE;
670
671 if (!Stream_CheckAndLogRequiredLength(TAG, s, (size_t)(cbLenNonNull + nullSize)))
672 return FALSE;
673
674 if (cbLenNonNull > 0)
675 {
676 /* cbDomain is the size in bytes of the character data in the Domain field.
677 * This size excludes (!) the length of the mandatory null terminator.
678 * Maximum value including the mandatory null terminator: 512
679 */
680 if ((cbLenNonNull % 2) || (cbLenNonNull > (max - nullSize)))
681 {
682 WLog_ERR(TAG, "protocol error: invalid value: %" PRIuz "", cbLenNonNull);
683 return FALSE;
684 }
685
686 if (unicode)
687 {
688 const WCHAR* domain = Stream_PointerAs(s, WCHAR);
689 if (!freerdp_settings_set_string_from_utf16N(settings, id, domain,
690 cbLenNonNull / sizeof(WCHAR)))
691 return FALSE;
692 }
693 else
694 {
695 const char* domain = Stream_PointerAs(s, char);
696 if (!freerdp_settings_set_string_len(settings, id, domain, cbLenNonNull))
697 return FALSE;
698 }
699 }
700
701 Stream_Seek(s, cbLenNonNull);
702
703 terminator.w = L'\0';
704 Stream_Read(s, terminator.b, nullSize);
705
706 if (terminator.w != L'\0')
707 {
708 WLog_ERR(TAG, "protocol error: Domain must be null terminated");
709 if (!freerdp_settings_set_string(settings, id, nullptr))
710 WLog_ERR(TAG, "freerdp_settings_set_string(settings, id=%d, nullptr) failed", id);
711
712 return FALSE;
713 }
714
715 return TRUE;
716}
717
723static BOOL rdp_read_info_packet(rdpRdp* rdp, wStream* s, UINT16 tpktlength)
724{
725 BOOL smallsize = FALSE;
726 UINT32 flags = 0;
727 UINT16 cbDomain = 0;
728 UINT16 cbUserName = 0;
729 UINT16 cbPassword = 0;
730 UINT16 cbAlternateShell = 0;
731 UINT16 cbWorkingDir = 0;
732 UINT32 CompressionLevel = 0;
733 rdpSettings* settings = rdp->settings;
734
735 if (!Stream_CheckAndLogRequiredLengthWLog(rdp->log, s, 18))
736 return FALSE;
737
738 Stream_Read_UINT32(s, settings->KeyboardCodePage); /* CodePage (4 bytes ) */
739 Stream_Read_UINT32(s, flags); /* flags (4 bytes) */
740 settings->AudioCapture = ((flags & INFO_AUDIOCAPTURE) != 0);
741 settings->AudioPlayback = (!(flags & INFO_NOAUDIOPLAYBACK));
742 settings->AutoLogonEnabled = ((flags & INFO_AUTOLOGON) != 0);
743 settings->RemoteApplicationMode = ((flags & INFO_RAIL) != 0);
744 settings->HiDefRemoteApp = ((flags & INFO_HIDEF_RAIL_SUPPORTED) != 0);
745 settings->RemoteConsoleAudio = ((flags & INFO_REMOTECONSOLEAUDIO) != 0);
746 settings->CompressionEnabled = ((flags & INFO_COMPRESSION) != 0);
747 settings->LogonNotify = ((flags & INFO_LOGONNOTIFY) != 0);
748 settings->MouseHasWheel = ((flags & INFO_MOUSE_HAS_WHEEL) != 0);
749 settings->DisableCtrlAltDel = ((flags & INFO_DISABLECTRLALTDEL) != 0);
750 settings->ForceEncryptedCsPdu = ((flags & INFO_FORCE_ENCRYPTED_CS_PDU) != 0);
751 settings->PasswordIsSmartcardPin = ((flags & INFO_PASSWORD_IS_SC_PIN) != 0);
752
753 if (flags & INFO_COMPRESSION)
754 {
755 CompressionLevel = ((flags & 0x00001E00) >> 9);
756 settings->CompressionLevel = CompressionLevel;
757 }
758 else
759 {
760 settings->CompressionLevel = 0;
761 }
762
763 /* RDP 4 and 5 have smaller credential limits */
764 if (settings->RdpVersion < RDP_VERSION_5_PLUS)
765 smallsize = TRUE;
766
767 Stream_Read_UINT16(s, cbDomain); /* cbDomain (2 bytes) */
768 Stream_Read_UINT16(s, cbUserName); /* cbUserName (2 bytes) */
769 Stream_Read_UINT16(s, cbPassword); /* cbPassword (2 bytes) */
770 Stream_Read_UINT16(s, cbAlternateShell); /* cbAlternateShell (2 bytes) */
771 Stream_Read_UINT16(s, cbWorkingDir); /* cbWorkingDir (2 bytes) */
772
773 if (!rdp_read_info_string(settings, FreeRDP_Domain, flags, s, cbDomain, smallsize ? 52 : 512))
774 return FALSE;
775
776 if (!rdp_read_info_string(settings, FreeRDP_Username, flags, s, cbUserName,
777 smallsize ? 44 : 512))
778 return FALSE;
779
780 if (!rdp_read_info_string(settings, FreeRDP_Password, flags, s, cbPassword,
781 smallsize ? 32 : 512))
782 return FALSE;
783
784 if (!rdp_read_info_string(settings, FreeRDP_AlternateShell, flags, s, cbAlternateShell, 512))
785 return FALSE;
786
787 if (!rdp_read_info_string(settings, FreeRDP_ShellWorkingDirectory, flags, s, cbWorkingDir, 512))
788 return FALSE;
789
790 if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
791 {
792 if (!rdp_read_extended_info_packet(rdp, s)) /* extraInfo */
793 return FALSE;
794 }
795
796 const size_t xrem = Stream_GetRemainingLength(s);
797 if (!tpkt_ensure_stream_consumed(rdp->log, s, tpktlength))
798 Stream_Seek(s, xrem);
799 return TRUE;
800}
801
807static BOOL rdp_write_info_packet(rdpRdp* rdp, wStream* s)
808{
809 BOOL ret = FALSE;
810 UINT32 flags = 0;
811 WCHAR* domainW = nullptr;
812 size_t cbDomain = 0;
813 WCHAR* userNameW = nullptr;
814 size_t cbUserName = 0;
815 WCHAR* passwordW = nullptr;
816 size_t cbPassword = 0;
817 WCHAR* alternateShellW = nullptr;
818 size_t cbAlternateShell = 0;
819 WCHAR* workingDirW = nullptr;
820 size_t cbWorkingDir = 0;
821 BOOL usedPasswordCookie = FALSE;
822 rdpSettings* settings = nullptr;
823
824 WINPR_ASSERT(rdp);
825 settings = rdp->settings;
826 WINPR_ASSERT(settings);
827
828 flags = INFO_MOUSE | INFO_UNICODE | INFO_LOGONERRORS | INFO_MAXIMIZESHELL |
829 INFO_ENABLEWINDOWSKEY | INFO_DISABLECTRLALTDEL | INFO_MOUSE_HAS_WHEEL |
830 INFO_FORCE_ENCRYPTED_CS_PDU;
831
832 if (settings->SmartcardLogon)
833 {
834 flags |= INFO_AUTOLOGON;
835 flags |= INFO_PASSWORD_IS_SC_PIN;
836 }
837
838 if (settings->AudioCapture)
839 flags |= INFO_AUDIOCAPTURE;
840
841 if (!settings->AudioPlayback)
842 flags |= INFO_NOAUDIOPLAYBACK;
843
844 if (settings->VideoDisable)
845 flags |= INFO_VIDEO_DISABLE;
846
847 if (settings->AutoLogonEnabled)
848 flags |= INFO_AUTOLOGON;
849
850 if (settings->RemoteApplicationMode)
851 {
852 if (settings->HiDefRemoteApp)
853 {
854 if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
855 flags |= INFO_HIDEF_RAIL_SUPPORTED;
856 }
857
858 flags |= INFO_RAIL;
859 }
860
861 if (settings->RemoteConsoleAudio)
862 flags |= INFO_REMOTECONSOLEAUDIO;
863
864 if (settings->CompressionEnabled)
865 {
866 flags |= INFO_COMPRESSION;
867 flags |= ((settings->CompressionLevel << 9) & 0x00001E00);
868 }
869
870 if (settings->LogonNotify)
871 flags |= INFO_LOGONNOTIFY;
872
873 if (settings->PasswordIsSmartcardPin)
874 flags |= INFO_PASSWORD_IS_SC_PIN;
875
876 {
877 char* flags_description = rdp_info_package_flags_description(flags);
878
879 if (flags_description)
880 {
881 WLog_DBG(TAG, "Client Info Packet Flags = %s", flags_description);
882 free(flags_description);
883 }
884 }
885
886 domainW = freerdp_settings_get_string_as_utf16(settings, FreeRDP_Domain, &cbDomain);
887 if (cbDomain > UINT16_MAX / sizeof(WCHAR))
888 {
889 WLog_ERR(TAG, "cbDomain > UINT16_MAX");
890 goto fail;
891 }
892 cbDomain *= sizeof(WCHAR);
893
894 /* user name provided by the expert for connecting to the novice computer */
895 userNameW = freerdp_settings_get_string_as_utf16(settings, FreeRDP_Username, &cbUserName);
896 if (cbUserName > UINT16_MAX / sizeof(WCHAR))
897 {
898 WLog_ERR(TAG, "cbUserName > UINT16_MAX");
899 goto fail;
900 }
901 cbUserName *= sizeof(WCHAR);
902
903 {
904 const char* pin = "*";
905 if (!settings->RemoteAssistanceMode)
906 {
907 /* Ignore redirection password if we´re using smartcard and have the pin as password */
908 if (((flags & INFO_PASSWORD_IS_SC_PIN) == 0) && settings->RedirectionPassword &&
909 (settings->RedirectionPasswordLength > 0))
910 {
911 union
912 {
913 BYTE* bp;
914 WCHAR* wp;
915 } ptrconv;
916
917 if (settings->RedirectionPasswordLength > UINT16_MAX)
918 {
919 WLog_ERR(TAG, "RedirectionPasswordLength > UINT16_MAX");
920 goto fail;
921 }
922 usedPasswordCookie = TRUE;
923
924 ptrconv.bp = settings->RedirectionPassword;
925 passwordW = ptrconv.wp;
926 cbPassword = (UINT16)settings->RedirectionPasswordLength;
927 }
928 else
929 pin = freerdp_settings_get_string(settings, FreeRDP_Password);
930 }
931
932 if (!usedPasswordCookie && pin)
933 {
934 passwordW = ConvertUtf8ToWCharAlloc(pin, &cbPassword);
935 if (cbPassword > UINT16_MAX / sizeof(WCHAR))
936 {
937 WLog_ERR(TAG, "cbPassword > UINT16_MAX");
938 goto fail;
939 }
940 cbPassword = (UINT16)cbPassword * sizeof(WCHAR);
941 }
942 }
943
944 {
945 const char* altShell = nullptr;
946 if (!settings->RemoteAssistanceMode)
947 altShell = freerdp_settings_get_string(settings, FreeRDP_AlternateShell);
948 else if (settings->RemoteAssistancePassStub)
949 altShell = "*"; /* This field MUST be filled with "*" */
950 else
951 altShell = freerdp_settings_get_string(settings, FreeRDP_RemoteAssistancePassword);
952
953 if (altShell && strlen(altShell) > 0)
954 {
955 alternateShellW = ConvertUtf8ToWCharAlloc(altShell, &cbAlternateShell);
956 if (!alternateShellW)
957 {
958 WLog_ERR(TAG, "alternateShellW == nullptr");
959 goto fail;
960 }
961 if (cbAlternateShell > (UINT16_MAX / sizeof(WCHAR)))
962 {
963 WLog_ERR(TAG, "cbAlternateShell > UINT16_MAX");
964 goto fail;
965 }
966 cbAlternateShell = (UINT16)cbAlternateShell * sizeof(WCHAR);
967 }
968 }
969
970 {
971 FreeRDP_Settings_Keys_String inputId = FreeRDP_RemoteAssistanceSessionId;
972 if (!freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceMode))
973 inputId = FreeRDP_ShellWorkingDirectory;
974
975 workingDirW = freerdp_settings_get_string_as_utf16(settings, inputId, &cbWorkingDir);
976 }
977 if (cbWorkingDir > (UINT16_MAX / sizeof(WCHAR)))
978 {
979 WLog_ERR(TAG, "cbWorkingDir > UINT16_MAX");
980 goto fail;
981 }
982 cbWorkingDir = (UINT16)cbWorkingDir * sizeof(WCHAR);
983
984 if (!Stream_EnsureRemainingCapacity(s, 18ull + cbDomain + cbUserName + cbPassword +
985 cbAlternateShell + cbWorkingDir + 5 * sizeof(WCHAR)))
986 goto fail;
987
988 Stream_Write_UINT32(s, settings->KeyboardCodePage); /* CodePage (4 bytes) */
989 Stream_Write_UINT32(s, flags); /* flags (4 bytes) */
990 Stream_Write_UINT16(s, (UINT16)cbDomain); /* cbDomain (2 bytes) */
991 Stream_Write_UINT16(s, (UINT16)cbUserName); /* cbUserName (2 bytes) */
992 Stream_Write_UINT16(s, (UINT16)cbPassword); /* cbPassword (2 bytes) */
993 Stream_Write_UINT16(s, (UINT16)cbAlternateShell); /* cbAlternateShell (2 bytes) */
994 Stream_Write_UINT16(s, (UINT16)cbWorkingDir); /* cbWorkingDir (2 bytes) */
995
996 Stream_Write(s, domainW, cbDomain);
997
998 /* the mandatory null terminator */
999 Stream_Write_UINT16(s, 0);
1000
1001 Stream_Write(s, userNameW, cbUserName);
1002
1003 /* the mandatory null terminator */
1004 Stream_Write_UINT16(s, 0);
1005
1006 Stream_Write(s, passwordW, cbPassword);
1007
1008 /* the mandatory null terminator */
1009 Stream_Write_UINT16(s, 0);
1010
1011 Stream_Write(s, alternateShellW, cbAlternateShell);
1012
1013 /* the mandatory null terminator */
1014 Stream_Write_UINT16(s, 0);
1015
1016 Stream_Write(s, workingDirW, cbWorkingDir);
1017
1018 /* the mandatory null terminator */
1019 Stream_Write_UINT16(s, 0);
1020 ret = TRUE;
1021fail:
1022 free(domainW);
1023 free(userNameW);
1024 free(alternateShellW);
1025 free(workingDirW);
1026
1027 if (!usedPasswordCookie)
1028 free(passwordW);
1029
1030 if (!ret)
1031 return FALSE;
1032
1033 if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
1034 ret = rdp_write_extended_info_packet(rdp, s); /* extraInfo */
1035
1036 return ret;
1037}
1038
1046BOOL rdp_recv_client_info(rdpRdp* rdp, wStream* s)
1047{
1048 UINT16 length = 0;
1049 UINT16 channelId = 0;
1050 UINT16 securityFlags = 0;
1051
1052 WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE);
1053
1054 if (!rdp_read_header(rdp, s, &length, &channelId))
1055 return FALSE;
1056
1057 if (!rdp_read_security_header(rdp, s, &securityFlags, &length))
1058 return FALSE;
1059
1060 if ((securityFlags & SEC_INFO_PKT) == 0)
1061 return FALSE;
1062
1063 if (rdp->settings->UseRdpSecurityLayer)
1064 {
1065 if (securityFlags & SEC_REDIRECTION_PKT)
1066 {
1067 WLog_ERR(TAG, "Error: SEC_REDIRECTION_PKT unsupported");
1068 return FALSE;
1069 }
1070
1071 if (securityFlags & SEC_ENCRYPT)
1072 {
1073 if (!rdp_decrypt(rdp, s, &length, securityFlags))
1074 return FALSE;
1075 }
1076 }
1077
1078 return rdp_read_info_packet(rdp, s, length);
1079}
1080
1087BOOL rdp_send_client_info(rdpRdp* rdp)
1088{
1089 UINT16 sec_flags = SEC_INFO_PKT;
1090 wStream* s = nullptr;
1091 WINPR_ASSERT(rdp);
1092 s = rdp_send_stream_init(rdp, &sec_flags);
1093
1094 if (!s)
1095 {
1096 WLog_ERR(TAG, "Stream_New failed!");
1097 return FALSE;
1098 }
1099
1100 if (!rdp_write_info_packet(rdp, s))
1101 {
1102 Stream_Release(s);
1103 return FALSE;
1104 }
1105 return rdp_send(rdp, s, MCS_GLOBAL_CHANNEL_ID, sec_flags);
1106}
1107
1108static void rdp_free_logon_info(logon_info* info)
1109{
1110 if (!info)
1111 return;
1112 free(info->domain);
1113 free(info->username);
1114
1115 const logon_info empty = WINPR_C_ARRAY_INIT;
1116 *info = empty;
1117}
1118
1119static BOOL rdp_info_read_string(const char* what, wStream* s, size_t size, size_t max,
1120 BOOL skipMax, char** dst)
1121{
1122 WINPR_ASSERT(dst);
1123 *dst = nullptr;
1124
1125 if (size == 0)
1126 {
1127 if (skipMax)
1128 return Stream_SafeSeek(s, max);
1129 return TRUE;
1130 }
1131
1132 if (((size % sizeof(WCHAR)) != 0) || (size > max))
1133 {
1134 WLog_ERR(TAG, "protocol error: invalid %s value: %" PRIuz "", what, size);
1135 return FALSE;
1136 }
1137
1138 const WCHAR* str = Stream_ConstPointer(s);
1139 if (!Stream_SafeSeek(s, skipMax ? max : size))
1140 return FALSE;
1141
1142 if (str[size / sizeof(WCHAR) - 1])
1143 {
1144 WLog_ERR(TAG, "protocol error: %s must be null terminated", what);
1145 return FALSE;
1146 }
1147
1148 size_t len = 0;
1149 char* rc = ConvertWCharNToUtf8Alloc(str, size / sizeof(WCHAR), &len);
1150 if (!rc)
1151 {
1152 WLog_ERR(TAG, "failed to convert the %s string", what);
1153 free(rc);
1154 return FALSE;
1155 }
1156
1157 *dst = rc;
1158 return TRUE;
1159}
1160
1161static BOOL rdp_recv_logon_info_v1(rdpRdp* rdp, wStream* s, logon_info* info)
1162{
1163 UINT32 cbDomain = 0;
1164 UINT32 cbUserName = 0;
1165
1166 WINPR_UNUSED(rdp);
1167 WINPR_ASSERT(info);
1168
1169 if (!Stream_CheckAndLogRequiredLength(TAG, s, 576))
1170 return FALSE;
1171
1172 Stream_Read_UINT32(s, cbDomain); /* cbDomain (4 bytes) */
1173
1174 /* cbDomain is the size of the Unicode character data (including the mandatory
1175 * null terminator) in bytes present in the fixed-length (52 bytes) Domain field
1176 */
1177 if (!rdp_info_read_string("Domain", s, cbDomain, 52, TRUE, &info->domain))
1178 goto fail;
1179
1180 Stream_Read_UINT32(s, cbUserName); /* cbUserName (4 bytes) */
1181
1182 /* cbUserName is the size of the Unicode character data (including the mandatory
1183 * null terminator) in bytes present in the fixed-length (512 bytes) UserName field.
1184 */
1185 if (!rdp_info_read_string("UserName", s, cbUserName, 512, TRUE, &info->username))
1186 goto fail;
1187
1188 Stream_Read_UINT32(s, info->sessionId); /* SessionId (4 bytes) */
1189 WLog_DBG(TAG, "LogonInfoV1: SessionId: 0x%08" PRIX32 " UserName: [%s] Domain: [%s]",
1190 info->sessionId, info->username, info->domain);
1191 return TRUE;
1192fail:
1193 return FALSE;
1194}
1195
1196static BOOL rdp_recv_logon_info_v2(rdpRdp* rdp, wStream* s, logon_info* info)
1197{
1198 UINT16 Version = 0;
1199 UINT32 Size = 0;
1200 UINT32 cbDomain = 0;
1201 UINT32 cbUserName = 0;
1202
1203 WINPR_ASSERT(rdp);
1204 WINPR_ASSERT(s);
1205 WINPR_ASSERT(info);
1206
1207 WINPR_UNUSED(rdp);
1208
1209 if (!Stream_CheckAndLogRequiredLength(TAG, s, logonInfoV2TotalSize))
1210 return FALSE;
1211
1212 Stream_Read_UINT16(s, Version); /* Version (2 bytes) */
1213 if (Version != SAVE_SESSION_PDU_VERSION_ONE)
1214 {
1215 WLog_WARN(TAG, "LogonInfoV2::Version expected %d bytes, got %" PRIu16,
1216 SAVE_SESSION_PDU_VERSION_ONE, Version);
1217 return FALSE;
1218 }
1219
1220 Stream_Read_UINT32(s, Size); /* Size (4 bytes) */
1221
1222 /* [MS-RDPBCGR] 2.2.10.1.1.2 Logon Info Version 2 (TS_LOGON_INFO_VERSION_2)
1223 * should be logonInfoV2TotalSize
1224 * but even MS server 2019 sends logonInfoV2Size
1225 */
1226 if (Size != logonInfoV2TotalSize)
1227 {
1228 if (Size != logonInfoV2Size)
1229 {
1230 WLog_WARN(TAG, "LogonInfoV2::Size expected %" PRIu32 " bytes, got %" PRIu32,
1231 logonInfoV2TotalSize, Size);
1232 return FALSE;
1233 }
1234 }
1235
1236 Stream_Read_UINT32(s, info->sessionId); /* SessionId (4 bytes) */
1237 Stream_Read_UINT32(s, cbDomain); /* cbDomain (4 bytes) */
1238 Stream_Read_UINT32(s, cbUserName); /* cbUserName (4 bytes) */
1239 Stream_Seek(s, logonInfoV2ReservedSize); /* pad (558 bytes) */
1240
1241 /* cbDomain is the size in bytes of the Unicode character data in the Domain field.
1242 * The size of the mandatory null terminator is include in this value.
1243 * Note: Since MS-RDPBCGR 2.2.10.1.1.2 does not mention any size limits we assume
1244 * that the maximum value is 52 bytes, according to the fixed size of the
1245 * Domain field in the Logon Info Version 1 (TS_LOGON_INFO) structure.
1246 */
1247 if (!rdp_info_read_string("Domain", s, cbDomain, 52, FALSE, &info->domain))
1248 goto fail;
1249
1250 /* cbUserName is the size in bytes of the Unicode character data in the UserName field.
1251 * The size of the mandatory null terminator is include in this value.
1252 * Note: Since MS-RDPBCGR 2.2.10.1.1.2 does not mention any size limits we assume
1253 * that the maximum value is 512 bytes, according to the fixed size of the
1254 * Username field in the Logon Info Version 1 (TS_LOGON_INFO) structure.
1255 */
1256 if (!rdp_info_read_string("UserName", s, cbUserName, 512, FALSE, &info->username))
1257 goto fail;
1258
1259 /* We´ve seen undocumented padding with windows 11 here.
1260 * unless it has actual data in it ignore it.
1261 * if there is unexpected data, print a warning and dump the contents
1262 */
1263 {
1264 const size_t rem = Stream_GetRemainingLength(s);
1265 if (rem > 0)
1266 {
1267 BOOL warn = FALSE;
1268 const char* str = Stream_ConstPointer(s);
1269 for (size_t x = 0; x < rem; x++)
1270 {
1271 if (str[x] != '\0')
1272 warn = TRUE;
1273 }
1274 if (warn)
1275 {
1276 WLog_WARN(TAG, "unexpected padding of %" PRIuz " bytes, data not '\\0'", rem);
1277 winpr_HexDump(TAG, WLOG_TRACE, str, rem);
1278 }
1279
1280 if (!Stream_SafeSeek(s, rem))
1281 goto fail;
1282 }
1283 }
1284
1285 WLog_DBG(TAG, "LogonInfoV2: SessionId: 0x%08" PRIX32 " UserName: [%s] Domain: [%s]",
1286 info->sessionId, info->username, info->domain);
1287 return TRUE;
1288fail:
1289 return FALSE;
1290}
1291
1292static BOOL rdp_recv_logon_plain_notify(rdpRdp* rdp, wStream* s)
1293{
1294 WINPR_UNUSED(rdp);
1295 if (!Stream_CheckAndLogRequiredLength(TAG, s, 576))
1296 return FALSE;
1297
1298 Stream_Seek(s, 576); /* pad (576 bytes) */
1299 WLog_DBG(TAG, "LogonPlainNotify");
1300 return TRUE;
1301}
1302
1303static BOOL rdp_recv_logon_error_info(rdpRdp* rdp, wStream* s, logon_info_ex* info)
1304{
1305 freerdp* instance = nullptr;
1306 UINT32 errorNotificationType = 0;
1307 UINT32 errorNotificationData = 0;
1308
1309 WINPR_ASSERT(rdp);
1310 WINPR_ASSERT(rdp->context);
1311 WINPR_ASSERT(s);
1312 WINPR_ASSERT(info);
1313
1314 instance = rdp->context->instance;
1315 WINPR_ASSERT(instance);
1316
1317 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1318 return FALSE;
1319
1320 Stream_Read_UINT32(s, errorNotificationType); /* errorNotificationType (4 bytes) */
1321 Stream_Read_UINT32(s, errorNotificationData); /* errorNotificationData (4 bytes) */
1322 WLog_DBG(TAG, "LogonErrorInfo: Data: 0x%08" PRIX32 " Type: 0x%08" PRIX32 "",
1323 errorNotificationData, errorNotificationType);
1324 if (instance->LogonErrorInfo)
1325 {
1326 const int rc =
1327 instance->LogonErrorInfo(instance, errorNotificationData, errorNotificationType);
1328 if (rc < 0)
1329 return FALSE;
1330 }
1331 info->ErrorNotificationType = errorNotificationType;
1332 info->ErrorNotificationData = errorNotificationData;
1333 return TRUE;
1334}
1335
1336static BOOL rdp_recv_logon_info_extended(rdpRdp* rdp, wStream* s, logon_info_ex* info)
1337{
1338 UINT32 cbFieldData = 0;
1339 UINT32 fieldsPresent = 0;
1340 UINT16 Length = 0;
1341
1342 WINPR_ASSERT(rdp);
1343 WINPR_ASSERT(s);
1344 WINPR_ASSERT(info);
1345
1346 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
1347 {
1348 WLog_WARN(TAG, "received short logon info extended, need 6 bytes, got %" PRIuz,
1349 Stream_GetRemainingLength(s));
1350 return FALSE;
1351 }
1352
1353 Stream_Read_UINT16(s, Length); /* Length (2 bytes) */
1354 Stream_Read_UINT32(s, fieldsPresent); /* fieldsPresent (4 bytes) */
1355
1356 if ((Length < 6) || (!Stream_CheckAndLogRequiredLength(TAG, s, (Length - 6U))))
1357 {
1358 WLog_WARN(TAG,
1359 "received short logon info extended, need %" PRIu16 " - 6 bytes, got %" PRIuz,
1360 Length, Stream_GetRemainingLength(s));
1361 return FALSE;
1362 }
1363
1364 WLog_DBG(TAG, "LogonInfoExtended: fieldsPresent: 0x%08" PRIX32 "", fieldsPresent);
1365
1366 /* logonFields */
1367
1368 if (fieldsPresent & LOGON_EX_AUTORECONNECTCOOKIE)
1369 {
1370 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1371 return FALSE;
1372
1373 info->haveCookie = TRUE;
1374 Stream_Read_UINT32(s, cbFieldData); /* cbFieldData (4 bytes) */
1375
1376 if (!Stream_CheckAndLogRequiredLength(TAG, s, cbFieldData))
1377 return FALSE;
1378
1379 if (!rdp_read_server_auto_reconnect_cookie(rdp, s, info))
1380 return FALSE;
1381 }
1382
1383 if (fieldsPresent & LOGON_EX_LOGONERRORS)
1384 {
1385 info->haveErrorInfo = TRUE;
1386
1387 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1388 return FALSE;
1389
1390 Stream_Read_UINT32(s, cbFieldData); /* cbFieldData (4 bytes) */
1391
1392 if (!Stream_CheckAndLogRequiredLength(TAG, s, cbFieldData))
1393 return FALSE;
1394
1395 if (!rdp_recv_logon_error_info(rdp, s, info))
1396 return FALSE;
1397 }
1398
1399 if (!Stream_CheckAndLogRequiredLength(TAG, s, 570))
1400 return FALSE;
1401
1402 Stream_Seek(s, 570); /* pad (570 bytes) */
1403 return TRUE;
1404}
1405
1406BOOL rdp_recv_save_session_info(rdpRdp* rdp, wStream* s)
1407{
1408 UINT32 infoType = 0;
1409 BOOL status = FALSE;
1410
1411 rdpContext* context = rdp->context;
1412 rdpUpdate* update = rdp->context->update;
1413
1414 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1415 return FALSE;
1416
1417 Stream_Read_UINT32(s, infoType); /* infoType (4 bytes) */
1418
1419 switch (infoType)
1420 {
1421 case INFO_TYPE_LOGON:
1422 {
1423 logon_info logonInfo = WINPR_C_ARRAY_INIT;
1424 status = rdp_recv_logon_info_v1(rdp, s, &logonInfo);
1425
1426 if (status && update->SaveSessionInfo)
1427 status = update->SaveSessionInfo(context, infoType, &logonInfo);
1428
1429 rdp_free_logon_info(&logonInfo);
1430 }
1431 break;
1432
1433 case INFO_TYPE_LOGON_LONG:
1434 {
1435 logon_info logonInfo = WINPR_C_ARRAY_INIT;
1436 status = rdp_recv_logon_info_v2(rdp, s, &logonInfo);
1437
1438 if (status && update->SaveSessionInfo)
1439 status = update->SaveSessionInfo(context, infoType, &logonInfo);
1440
1441 rdp_free_logon_info(&logonInfo);
1442 }
1443 break;
1444
1445 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
1446 status = rdp_recv_logon_plain_notify(rdp, s);
1447
1448 if (status && update->SaveSessionInfo)
1449 status = update->SaveSessionInfo(context, infoType, nullptr);
1450
1451 break;
1452
1453 case INFO_TYPE_LOGON_EXTENDED_INF:
1454 {
1455 logon_info_ex logonInfoEx = WINPR_C_ARRAY_INIT;
1456 status = rdp_recv_logon_info_extended(rdp, s, &logonInfoEx);
1457
1458 if (status && update->SaveSessionInfo)
1459 status = update->SaveSessionInfo(context, infoType, &logonInfoEx);
1460 }
1461 break;
1462
1463 default:
1464 WLog_WARN(TAG, "Unhandled saveSessionInfo type 0x%" PRIx32 "", infoType);
1465 status = TRUE;
1466 break;
1467 }
1468
1469 if (!status)
1470 {
1471 WLog_WARN(TAG, "SaveSessionInfo error: infoType: %s (%" PRIu32 ")",
1472 freerdp_session_logon_type_str(infoType), infoType);
1473 }
1474
1475 return status;
1476}
1477
1478static BOOL rdp_write_logon_info_v1(wStream* s, const logon_info* info)
1479{
1480 const size_t charLen = 52 / sizeof(WCHAR);
1481 const size_t userCharLen = 512 / sizeof(WCHAR);
1482
1483 size_t sz = 4 + 52 + 4 + 512 + 4;
1484
1485 if (!Stream_EnsureRemainingCapacity(s, sz))
1486 return FALSE;
1487
1488 /* domain */
1489 {
1490 WINPR_ASSERT(info);
1491 if (!info->domain || !info->username)
1492 return FALSE;
1493 const size_t len = strnlen(info->domain, charLen + 1);
1494 if (len > charLen)
1495 return FALSE;
1496
1497 const size_t wlen = len * sizeof(WCHAR);
1498 if (wlen > UINT32_MAX)
1499 return FALSE;
1500
1501 Stream_Write_UINT32(s, (UINT32)wlen);
1502 if (Stream_Write_UTF16_String_From_UTF8(s, charLen, info->domain, len, TRUE) < 0)
1503 return FALSE;
1504 }
1505
1506 /* username */
1507 {
1508 const size_t len = strnlen(info->username, userCharLen + 1);
1509 if (len > userCharLen)
1510 return FALSE;
1511
1512 const size_t wlen = len * sizeof(WCHAR);
1513 if (wlen > UINT32_MAX)
1514 return FALSE;
1515
1516 Stream_Write_UINT32(s, (UINT32)wlen);
1517
1518 if (Stream_Write_UTF16_String_From_UTF8(s, userCharLen, info->username, len, TRUE) < 0)
1519 return FALSE;
1520 }
1521
1522 /* sessionId */
1523 Stream_Write_UINT32(s, info->sessionId);
1524 return TRUE;
1525}
1526
1527static BOOL rdp_write_logon_info_v2(wStream* s, const logon_info* info)
1528{
1529 size_t domainLen = 0;
1530 size_t usernameLen = 0;
1531 size_t domainStrLen = 0;
1532 size_t usernameStrLen = 0;
1533
1534 if (!Stream_EnsureRemainingCapacity(s, logonInfoV2TotalSize))
1535 return FALSE;
1536
1537 Stream_Write_UINT16(s, SAVE_SESSION_PDU_VERSION_ONE);
1538 /* [MS-RDPBCGR] 2.2.10.1.1.2 Logon Info Version 2 (TS_LOGON_INFO_VERSION_2)
1539 * should be logonInfoV2TotalSize
1540 * but even MS server 2019 sends logonInfoV2Size
1541 */
1542 Stream_Write_UINT32(s, logonInfoV2Size);
1543 Stream_Write_UINT32(s, info->sessionId);
1544 if (info->domain)
1545 {
1546 domainStrLen = strnlen(info->domain, 256); /* lmcons.h UNLEN */
1547 const SSIZE_T wlen = ConvertUtf8NToWChar(info->domain, domainStrLen, nullptr, 0);
1548 if (wlen < 0)
1549 return FALSE;
1550 domainLen = WINPR_ASSERTING_INT_CAST(size_t, wlen);
1551 }
1552 if (domainLen >= UINT32_MAX / sizeof(WCHAR))
1553 return FALSE;
1554 Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * sizeof(WCHAR));
1555
1556 if (info->username)
1557 {
1558 usernameStrLen = strnlen(info->username, 256); /* lmcons.h UNLEN */
1559 const SSIZE_T wlen = ConvertUtf8NToWChar(info->username, usernameStrLen, nullptr, 0);
1560 if (wlen < 0)
1561 return FALSE;
1562 usernameLen = WINPR_ASSERTING_INT_CAST(size_t, wlen);
1563 }
1564 if (usernameLen >= UINT32_MAX / sizeof(WCHAR))
1565 return FALSE;
1566 Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * sizeof(WCHAR));
1567 Stream_Seek(s, logonInfoV2ReservedSize);
1568 if (Stream_Write_UTF16_String_From_UTF8(s, domainLen + 1, info->domain, domainStrLen, TRUE) < 0)
1569 return FALSE;
1570 if (Stream_Write_UTF16_String_From_UTF8(s, usernameLen + 1, info->username, usernameStrLen,
1571 TRUE) < 0)
1572 return FALSE;
1573 return TRUE;
1574}
1575
1576static BOOL rdp_write_logon_info_plain(wStream* s)
1577{
1578 if (!Stream_EnsureRemainingCapacity(s, 576))
1579 return FALSE;
1580
1581 Stream_Seek(s, 576);
1582 return TRUE;
1583}
1584
1585static BOOL rdp_write_logon_info_ex(wStream* s, const logon_info_ex* info)
1586{
1587 UINT32 FieldsPresent = 0;
1588 UINT16 Size = 2 + 4 + 570;
1589
1590 if (info->haveCookie)
1591 {
1592 FieldsPresent |= LOGON_EX_AUTORECONNECTCOOKIE;
1593 Size += 28;
1594 }
1595
1596 if (info->haveErrorInfo)
1597 {
1598 FieldsPresent |= LOGON_EX_LOGONERRORS;
1599 Size += 8;
1600 }
1601
1602 if (!Stream_EnsureRemainingCapacity(s, Size))
1603 return FALSE;
1604
1605 Stream_Write_UINT16(s, Size);
1606 Stream_Write_UINT32(s, FieldsPresent);
1607
1608 if (info->haveCookie)
1609 {
1610 Stream_Write_UINT32(s, 28); /* cbFieldData (4 bytes) */
1611 Stream_Write_UINT32(s, 28); /* cbLen (4 bytes) */
1612 Stream_Write_UINT32(s, AUTO_RECONNECT_VERSION_1); /* Version (4 bytes) */
1613 Stream_Write_UINT32(s, info->LogonId); /* LogonId (4 bytes) */
1614 Stream_Write(s, info->ArcRandomBits, 16); /* ArcRandomBits (16 bytes) */
1615 }
1616
1617 if (info->haveErrorInfo)
1618 {
1619 Stream_Write_UINT32(s, 8); /* cbFieldData (4 bytes) */
1620 Stream_Write_UINT32(s, info->ErrorNotificationType); /* ErrorNotificationType (4 bytes) */
1621 Stream_Write_UINT32(s, info->ErrorNotificationData); /* ErrorNotificationData (4 bytes) */
1622 }
1623
1624 Stream_Seek(s, 570);
1625 return TRUE;
1626}
1627
1628BOOL rdp_send_save_session_info(rdpContext* context, UINT32 type, const void* data)
1629{
1630 UINT16 sec_flags = 0;
1631 BOOL status = 0;
1632
1633 WINPR_ASSERT(context);
1634 rdpRdp* rdp = context->rdp;
1635 wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
1636
1637 if (!s)
1638 return FALSE;
1639
1640 Stream_Write_UINT32(s, type);
1641
1642 switch (type)
1643 {
1644 case INFO_TYPE_LOGON:
1645 status = rdp_write_logon_info_v1(s, (const logon_info*)data);
1646 break;
1647
1648 case INFO_TYPE_LOGON_LONG:
1649 status = rdp_write_logon_info_v2(s, (const logon_info*)data);
1650 break;
1651
1652 case INFO_TYPE_LOGON_PLAIN_NOTIFY:
1653 status = rdp_write_logon_info_plain(s);
1654 break;
1655
1656 case INFO_TYPE_LOGON_EXTENDED_INF:
1657 status = rdp_write_logon_info_ex(s, (const logon_info_ex*)data);
1658 break;
1659
1660 default:
1661 WLog_ERR(TAG, "saveSessionInfo type 0x%" PRIx32 " not handled", type);
1662 status = FALSE;
1663 break;
1664 }
1665
1666 if (status)
1667 status =
1668 rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SAVE_SESSION_INFO, rdp->mcs->userId, sec_flags);
1669 else
1670 Stream_Release(s);
1671
1672 return status;
1673}
1674
1675BOOL rdp_send_server_status_info(rdpContext* context, UINT32 status)
1676{
1677 UINT16 sec_flags = 0;
1678 wStream* s = nullptr;
1679 rdpRdp* rdp = context->rdp;
1680 s = rdp_data_pdu_init(rdp, &sec_flags);
1681
1682 if (!s)
1683 return FALSE;
1684
1685 Stream_Write_UINT32(s, status);
1686 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_STATUS_INFO, rdp->mcs->userId, sec_flags);
1687}
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.