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