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