FreeRDP
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 (2 + 4 + 4 + 4 + 4)
41 #define logonInfoV2ReservedSize 558
42 #define logonInfoV2TotalSize (logonInfoV2Size + logonInfoV2ReservedSize)
43 
44 static 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
49 struct info_flags_t
50 {
51  UINT32 flag;
52  const char* label;
53 };
54 
55 static 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 
77 static 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) ? TRUE : FALSE;
82 
83  if (!freerdp_settings_set_string(settings, id, NULL))
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 
120 static char* rdp_info_package_flags_description(UINT32 flags)
121 {
122  char* result = NULL;
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 0;
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 
142 static BOOL rdp_compute_client_auto_reconnect_cookie(rdpRdp* rdp)
143 {
144  BYTE ClientRandom[CLIENT_RANDOM_LENGTH] = { 0 };
145  BYTE AutoReconnectRandom[32] = { 0 };
146  ARC_SC_PRIVATE_PACKET* serverCookie = NULL;
147  ARC_CS_PRIVATE_PACKET* clientCookie = NULL;
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 
179 static BOOL rdp_read_server_auto_reconnect_cookie(rdpRdp* rdp, wStream* s, logon_info_ex* info)
180 {
181  BYTE* p = NULL;
182  ARC_SC_PRIVATE_PACKET* autoReconnectCookie = NULL;
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 = NULL;
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 
229 static BOOL rdp_read_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s)
230 {
231  ARC_CS_PRIVATE_PACKET* autoReconnectCookie = NULL;
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 
250 static BOOL rdp_write_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s)
251 {
252  BYTE* p = NULL;
253  ARC_CS_PRIVATE_PACKET* autoReconnectCookie = NULL;
254  rdpSettings* settings = NULL;
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 
289 static size_t rdp_get_client_address_max_size(const rdpRdp* rdp)
290 {
291  UINT32 version = 0;
292  rdpSettings* settings = NULL;
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 
310 static 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 ? TRUE : FALSE);
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 %" PRIu16
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 
454 end:
455  return TRUE;
456 }
457 
463 static 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 = NULL;
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 = (UINT16)(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 = (UINT16)(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 rlen = 0;
577  const char* tz = freerdp_settings_get_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName);
578  if (tz)
579  rlen = strnlen(tz, 254);
580  Stream_Write_UINT16(s, (UINT16)rlen * sizeof(WCHAR));
581  if (Stream_Write_UTF16_String_From_UTF8(s, rlen, tz, rlen, FALSE) < 0)
582  goto fail;
583  Stream_Write_UINT16(s, settings->DynamicDaylightTimeDisabled ? 0x01 : 0x00);
584  }
585 
586  ret = TRUE;
587 fail:
588  free(clientAddress);
589  free(clientDir);
590  return ret;
591 }
592 
593 static BOOL rdp_read_info_string(rdpSettings* settings, FreeRDP_Settings_Keys_String id,
594  UINT32 flags, wStream* s, size_t cbLenNonNull, size_t max)
595 {
596  union
597  {
598  char c;
599  WCHAR w;
600  BYTE b[2];
601  } terminator;
602 
603  const BOOL unicode = (flags & INFO_UNICODE) ? TRUE : FALSE;
604  const size_t nullSize = unicode ? sizeof(WCHAR) : sizeof(CHAR);
605 
606  if (!freerdp_settings_set_string(settings, id, NULL))
607  return FALSE;
608 
609  if (!Stream_CheckAndLogRequiredLength(TAG, s, (size_t)(cbLenNonNull + nullSize)))
610  return FALSE;
611 
612  if (cbLenNonNull > 0)
613  {
614  /* cbDomain is the size in bytes of the character data in the Domain field.
615  * This size excludes (!) the length of the mandatory null terminator.
616  * Maximum value including the mandatory null terminator: 512
617  */
618  if ((cbLenNonNull % 2) || (cbLenNonNull > (max - nullSize)))
619  {
620  WLog_ERR(TAG, "protocol error: invalid value: %" PRIuz "", cbLenNonNull);
621  return FALSE;
622  }
623 
624  if (unicode)
625  {
626  const WCHAR* domain = Stream_PointerAs(s, WCHAR);
627  if (!freerdp_settings_set_string_from_utf16N(settings, id, domain,
628  cbLenNonNull / sizeof(WCHAR)))
629  return FALSE;
630  }
631  else
632  {
633  const char* domain = Stream_PointerAs(s, char);
634  if (!freerdp_settings_set_string_len(settings, id, domain, cbLenNonNull))
635  return FALSE;
636  }
637  }
638 
639  Stream_Seek(s, cbLenNonNull);
640 
641  terminator.w = L'\0';
642  Stream_Read(s, terminator.b, nullSize);
643 
644  if (terminator.w != L'\0')
645  {
646  WLog_ERR(TAG, "protocol error: Domain must be null terminated");
647  (void)freerdp_settings_set_string(settings, id, NULL);
648  return FALSE;
649  }
650 
651  return TRUE;
652 }
653 
659 static BOOL rdp_read_info_packet(rdpRdp* rdp, wStream* s, UINT16 tpktlength)
660 {
661  BOOL smallsize = FALSE;
662  UINT32 flags = 0;
663  UINT16 cbDomain = 0;
664  UINT16 cbUserName = 0;
665  UINT16 cbPassword = 0;
666  UINT16 cbAlternateShell = 0;
667  UINT16 cbWorkingDir = 0;
668  UINT32 CompressionLevel = 0;
669  rdpSettings* settings = rdp->settings;
670 
671  if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
672  return FALSE;
673 
674  Stream_Read_UINT32(s, settings->KeyboardCodePage); /* CodePage (4 bytes ) */
675  Stream_Read_UINT32(s, flags); /* flags (4 bytes) */
676  settings->AudioCapture = ((flags & INFO_AUDIOCAPTURE) ? TRUE : FALSE);
677  settings->AudioPlayback = ((flags & INFO_NOAUDIOPLAYBACK) ? FALSE : TRUE);
678  settings->AutoLogonEnabled = ((flags & INFO_AUTOLOGON) ? TRUE : FALSE);
679  settings->RemoteApplicationMode = ((flags & INFO_RAIL) ? TRUE : FALSE);
680  settings->HiDefRemoteApp = ((flags & INFO_HIDEF_RAIL_SUPPORTED) ? TRUE : FALSE);
681  settings->RemoteConsoleAudio = ((flags & INFO_REMOTECONSOLEAUDIO) ? TRUE : FALSE);
682  settings->CompressionEnabled = ((flags & INFO_COMPRESSION) ? TRUE : FALSE);
683  settings->LogonNotify = ((flags & INFO_LOGONNOTIFY) ? TRUE : FALSE);
684  settings->MouseHasWheel = ((flags & INFO_MOUSE_HAS_WHEEL) ? TRUE : FALSE);
685  settings->DisableCtrlAltDel = ((flags & INFO_DISABLECTRLALTDEL) ? TRUE : FALSE);
686  settings->ForceEncryptedCsPdu = ((flags & INFO_FORCE_ENCRYPTED_CS_PDU) ? TRUE : FALSE);
687  settings->PasswordIsSmartcardPin = ((flags & INFO_PASSWORD_IS_SC_PIN) ? TRUE : FALSE);
688 
689  if (flags & INFO_COMPRESSION)
690  {
691  CompressionLevel = ((flags & 0x00001E00) >> 9);
692  settings->CompressionLevel = CompressionLevel;
693  }
694  else
695  {
696  settings->CompressionLevel = 0;
697  }
698 
699  /* RDP 4 and 5 have smaller credential limits */
700  if (settings->RdpVersion < RDP_VERSION_5_PLUS)
701  smallsize = TRUE;
702 
703  Stream_Read_UINT16(s, cbDomain); /* cbDomain (2 bytes) */
704  Stream_Read_UINT16(s, cbUserName); /* cbUserName (2 bytes) */
705  Stream_Read_UINT16(s, cbPassword); /* cbPassword (2 bytes) */
706  Stream_Read_UINT16(s, cbAlternateShell); /* cbAlternateShell (2 bytes) */
707  Stream_Read_UINT16(s, cbWorkingDir); /* cbWorkingDir (2 bytes) */
708 
709  if (!rdp_read_info_string(settings, FreeRDP_Domain, flags, s, cbDomain, smallsize ? 52 : 512))
710  return FALSE;
711 
712  if (!rdp_read_info_string(settings, FreeRDP_Username, flags, s, cbUserName,
713  smallsize ? 44 : 512))
714  return FALSE;
715 
716  if (!rdp_read_info_string(settings, FreeRDP_Password, flags, s, cbPassword,
717  smallsize ? 32 : 512))
718  return FALSE;
719 
720  if (!rdp_read_info_string(settings, FreeRDP_AlternateShell, flags, s, cbAlternateShell, 512))
721  return FALSE;
722 
723  if (!rdp_read_info_string(settings, FreeRDP_ShellWorkingDirectory, flags, s, cbWorkingDir, 512))
724  return FALSE;
725 
726  if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
727  {
728  if (!rdp_read_extended_info_packet(rdp, s)) /* extraInfo */
729  return FALSE;
730  }
731 
732  const size_t xrem = Stream_GetRemainingLength(s);
733  if (!tpkt_ensure_stream_consumed(s, tpktlength))
734  Stream_Seek(s, xrem);
735  return TRUE;
736 }
737 
743 static BOOL rdp_write_info_packet(rdpRdp* rdp, wStream* s)
744 {
745  BOOL ret = FALSE;
746  UINT32 flags = 0;
747  WCHAR* domainW = NULL;
748  size_t cbDomain = 0;
749  WCHAR* userNameW = NULL;
750  size_t cbUserName = 0;
751  WCHAR* passwordW = NULL;
752  size_t cbPassword = 0;
753  WCHAR* alternateShellW = NULL;
754  size_t cbAlternateShell = 0;
755  WCHAR* workingDirW = NULL;
756  size_t cbWorkingDir = 0;
757  BOOL usedPasswordCookie = FALSE;
758  rdpSettings* settings = NULL;
759 
760  WINPR_ASSERT(rdp);
761  settings = rdp->settings;
762  WINPR_ASSERT(settings);
763 
764  flags = INFO_MOUSE | INFO_UNICODE | INFO_LOGONERRORS | INFO_MAXIMIZESHELL |
765  INFO_ENABLEWINDOWSKEY | INFO_DISABLECTRLALTDEL | INFO_MOUSE_HAS_WHEEL |
766  INFO_FORCE_ENCRYPTED_CS_PDU;
767 
768  if (settings->SmartcardLogon)
769  {
770  flags |= INFO_AUTOLOGON;
771  flags |= INFO_PASSWORD_IS_SC_PIN;
772  }
773 
774  if (settings->AudioCapture)
775  flags |= INFO_AUDIOCAPTURE;
776 
777  if (!settings->AudioPlayback)
778  flags |= INFO_NOAUDIOPLAYBACK;
779 
780  if (settings->VideoDisable)
781  flags |= INFO_VIDEO_DISABLE;
782 
783  if (settings->AutoLogonEnabled)
784  flags |= INFO_AUTOLOGON;
785 
786  if (settings->RemoteApplicationMode)
787  {
788  if (settings->HiDefRemoteApp)
789  flags |= INFO_HIDEF_RAIL_SUPPORTED;
790 
791  flags |= INFO_RAIL;
792  }
793 
794  if (settings->RemoteConsoleAudio)
795  flags |= INFO_REMOTECONSOLEAUDIO;
796 
797  if (settings->CompressionEnabled)
798  {
799  flags |= INFO_COMPRESSION;
800  flags |= ((settings->CompressionLevel << 9) & 0x00001E00);
801  }
802 
803  if (settings->LogonNotify)
804  flags |= INFO_LOGONNOTIFY;
805 
806  if (settings->PasswordIsSmartcardPin)
807  flags |= INFO_PASSWORD_IS_SC_PIN;
808 
809  {
810  char* flags_description = rdp_info_package_flags_description(flags);
811 
812  if (flags_description)
813  {
814  WLog_DBG(TAG, "Client Info Packet Flags = %s", flags_description);
815  free(flags_description);
816  }
817  }
818 
819  domainW = freerdp_settings_get_string_as_utf16(settings, FreeRDP_Domain, &cbDomain);
820  if (cbDomain > UINT16_MAX / sizeof(WCHAR))
821  {
822  WLog_ERR(TAG, "cbDomain > UINT16_MAX");
823  goto fail;
824  }
825  cbDomain *= sizeof(WCHAR);
826 
827  /* user name provided by the expert for connecting to the novice computer */
828  userNameW = freerdp_settings_get_string_as_utf16(settings, FreeRDP_Username, &cbUserName);
829  if (cbUserName > UINT16_MAX / sizeof(WCHAR))
830  {
831  WLog_ERR(TAG, "cbUserName > UINT16_MAX");
832  goto fail;
833  }
834  cbUserName *= sizeof(WCHAR);
835 
836  const char* pin = "*";
837  if (!settings->RemoteAssistanceMode)
838  {
839  /* Ignore redirection password if we´re using smartcard and have the pin as password */
840  if (((flags & INFO_PASSWORD_IS_SC_PIN) == 0) && settings->RedirectionPassword &&
841  (settings->RedirectionPasswordLength > 0))
842  {
843  union
844  {
845  BYTE* bp;
846  WCHAR* wp;
847  } ptrconv;
848 
849  if (settings->RedirectionPasswordLength > UINT16_MAX)
850  {
851  WLog_ERR(TAG, "RedirectionPasswordLength > UINT16_MAX");
852  goto fail;
853  }
854  usedPasswordCookie = TRUE;
855 
856  ptrconv.bp = settings->RedirectionPassword;
857  passwordW = ptrconv.wp;
858  cbPassword = (UINT16)settings->RedirectionPasswordLength;
859  }
860  else
861  pin = freerdp_settings_get_string(settings, FreeRDP_Password);
862  }
863 
864  if (!usedPasswordCookie && pin)
865  {
866  passwordW = ConvertUtf8ToWCharAlloc(pin, &cbPassword);
867  if (cbPassword > UINT16_MAX / sizeof(WCHAR))
868  {
869  WLog_ERR(TAG, "cbPassword > UINT16_MAX");
870  goto fail;
871  }
872  cbPassword = (UINT16)cbPassword * sizeof(WCHAR);
873  }
874 
875  const char* altShell = NULL;
876  if (!settings->RemoteAssistanceMode)
877  altShell = freerdp_settings_get_string(settings, FreeRDP_AlternateShell);
878  else if (settings->RemoteAssistancePassStub)
879  altShell = "*"; /* This field MUST be filled with "*" */
880  else
881  altShell = freerdp_settings_get_string(settings, FreeRDP_RemoteAssistancePassword);
882 
883  if (altShell && strlen(altShell) > 0)
884  {
885  alternateShellW = ConvertUtf8ToWCharAlloc(altShell, &cbAlternateShell);
886  if (!alternateShellW)
887  {
888  WLog_ERR(TAG, "alternateShellW == NULL");
889  goto fail;
890  }
891  if (cbAlternateShell > (UINT16_MAX / sizeof(WCHAR)))
892  {
893  WLog_ERR(TAG, "cbAlternateShell > UINT16_MAX");
894  goto fail;
895  }
896  cbAlternateShell = (UINT16)cbAlternateShell * sizeof(WCHAR);
897  }
898 
899  FreeRDP_Settings_Keys_String inputId = FreeRDP_RemoteAssistanceSessionId;
900  if (!freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceMode))
901  inputId = FreeRDP_ShellWorkingDirectory;
902 
903  workingDirW = freerdp_settings_get_string_as_utf16(settings, inputId, &cbWorkingDir);
904  if (cbWorkingDir > (UINT16_MAX / sizeof(WCHAR)))
905  {
906  WLog_ERR(TAG, "cbWorkingDir > UINT16_MAX");
907  goto fail;
908  }
909  cbWorkingDir = (UINT16)cbWorkingDir * sizeof(WCHAR);
910 
911  if (!Stream_EnsureRemainingCapacity(s, 18ull + cbDomain + cbUserName + cbPassword +
912  cbAlternateShell + cbWorkingDir + 5 * sizeof(WCHAR)))
913  goto fail;
914 
915  Stream_Write_UINT32(s, settings->KeyboardCodePage); /* CodePage (4 bytes) */
916  Stream_Write_UINT32(s, flags); /* flags (4 bytes) */
917  Stream_Write_UINT16(s, (UINT16)cbDomain); /* cbDomain (2 bytes) */
918  Stream_Write_UINT16(s, (UINT16)cbUserName); /* cbUserName (2 bytes) */
919  Stream_Write_UINT16(s, (UINT16)cbPassword); /* cbPassword (2 bytes) */
920  Stream_Write_UINT16(s, (UINT16)cbAlternateShell); /* cbAlternateShell (2 bytes) */
921  Stream_Write_UINT16(s, (UINT16)cbWorkingDir); /* cbWorkingDir (2 bytes) */
922 
923  Stream_Write(s, domainW, cbDomain);
924 
925  /* the mandatory null terminator */
926  Stream_Write_UINT16(s, 0);
927 
928  Stream_Write(s, userNameW, cbUserName);
929 
930  /* the mandatory null terminator */
931  Stream_Write_UINT16(s, 0);
932 
933  Stream_Write(s, passwordW, cbPassword);
934 
935  /* the mandatory null terminator */
936  Stream_Write_UINT16(s, 0);
937 
938  Stream_Write(s, alternateShellW, cbAlternateShell);
939 
940  /* the mandatory null terminator */
941  Stream_Write_UINT16(s, 0);
942 
943  Stream_Write(s, workingDirW, cbWorkingDir);
944 
945  /* the mandatory null terminator */
946  Stream_Write_UINT16(s, 0);
947  ret = TRUE;
948 fail:
949  free(domainW);
950  free(userNameW);
951  free(alternateShellW);
952  free(workingDirW);
953 
954  if (!usedPasswordCookie)
955  free(passwordW);
956 
957  if (!ret)
958  return FALSE;
959 
960  if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
961  ret = rdp_write_extended_info_packet(rdp, s); /* extraInfo */
962 
963  return ret;
964 }
965 
973 BOOL rdp_recv_client_info(rdpRdp* rdp, wStream* s)
974 {
975  UINT16 length = 0;
976  UINT16 channelId = 0;
977  UINT16 securityFlags = 0;
978 
979  WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE);
980 
981  if (!rdp_read_header(rdp, s, &length, &channelId))
982  return FALSE;
983 
984  if (!rdp_read_security_header(rdp, s, &securityFlags, &length))
985  return FALSE;
986 
987  if ((securityFlags & SEC_INFO_PKT) == 0)
988  return FALSE;
989 
990  if (rdp->settings->UseRdpSecurityLayer)
991  {
992  if (securityFlags & SEC_REDIRECTION_PKT)
993  {
994  WLog_ERR(TAG, "Error: SEC_REDIRECTION_PKT unsupported");
995  return FALSE;
996  }
997 
998  if (securityFlags & SEC_ENCRYPT)
999  {
1000  if (!rdp_decrypt(rdp, s, &length, securityFlags))
1001  return FALSE;
1002  }
1003  }
1004 
1005  return rdp_read_info_packet(rdp, s, length);
1006 }
1007 
1014 BOOL rdp_send_client_info(rdpRdp* rdp)
1015 {
1016  wStream* s = NULL;
1017  WINPR_ASSERT(rdp);
1018  rdp->sec_flags |= SEC_INFO_PKT;
1019  s = rdp_send_stream_init(rdp);
1020 
1021  if (!s)
1022  {
1023  WLog_ERR(TAG, "Stream_New failed!");
1024  return FALSE;
1025  }
1026 
1027  if (!rdp_write_info_packet(rdp, s))
1028  {
1029  Stream_Release(s);
1030  return FALSE;
1031  }
1032  return rdp_send(rdp, s, MCS_GLOBAL_CHANNEL_ID);
1033 }
1034 
1035 static void rdp_free_logon_info(logon_info* info)
1036 {
1037  if (!info)
1038  return;
1039  free(info->domain);
1040  free(info->username);
1041 
1042  const logon_info empty = { 0 };
1043  *info = empty;
1044 }
1045 
1046 static BOOL rdp_info_read_string(const char* what, wStream* s, size_t size, size_t max,
1047  BOOL skipMax, char** dst)
1048 {
1049  WINPR_ASSERT(dst);
1050  *dst = NULL;
1051 
1052  if (size == 0)
1053  {
1054  if (skipMax)
1055  return Stream_SafeSeek(s, max);
1056  return TRUE;
1057  }
1058 
1059  if (((size % sizeof(WCHAR)) != 0) || (size > max))
1060  {
1061  WLog_ERR(TAG, "protocol error: invalid %s value: %" PRIu32 "", what, size);
1062  return FALSE;
1063  }
1064 
1065  const WCHAR* str = Stream_ConstPointer(s);
1066  if (!Stream_SafeSeek(s, skipMax ? max : size))
1067  return FALSE;
1068 
1069  if (str[size / sizeof(WCHAR) - 1])
1070  {
1071  WLog_ERR(TAG, "protocol error: %s must be null terminated", what);
1072  return FALSE;
1073  }
1074 
1075  size_t len = 0;
1076  char* rc = ConvertWCharNToUtf8Alloc(str, size / sizeof(WCHAR), &len);
1077  if (!rc)
1078  {
1079  WLog_ERR(TAG, "failed to convert the %s string", what);
1080  free(rc);
1081  return FALSE;
1082  }
1083 
1084  *dst = rc;
1085  return TRUE;
1086 }
1087 
1088 static BOOL rdp_recv_logon_info_v1(rdpRdp* rdp, wStream* s, logon_info* info)
1089 {
1090  UINT32 cbDomain = 0;
1091  UINT32 cbUserName = 0;
1092 
1093  WINPR_UNUSED(rdp);
1094  WINPR_ASSERT(info);
1095 
1096  if (!Stream_CheckAndLogRequiredLength(TAG, s, 576))
1097  return FALSE;
1098 
1099  Stream_Read_UINT32(s, cbDomain); /* cbDomain (4 bytes) */
1100 
1101  /* cbDomain is the size of the Unicode character data (including the mandatory
1102  * null terminator) in bytes present in the fixed-length (52 bytes) Domain field
1103  */
1104  if (!rdp_info_read_string("Domain", s, cbDomain, 52, TRUE, &info->domain))
1105  goto fail;
1106 
1107  Stream_Read_UINT32(s, cbUserName); /* cbUserName (4 bytes) */
1108 
1109  /* cbUserName is the size of the Unicode character data (including the mandatory
1110  * null terminator) in bytes present in the fixed-length (512 bytes) UserName field.
1111  */
1112  if (!rdp_info_read_string("UserName", s, cbUserName, 512, TRUE, &info->username))
1113  goto fail;
1114 
1115  Stream_Read_UINT32(s, info->sessionId); /* SessionId (4 bytes) */
1116  WLog_DBG(TAG, "LogonInfoV1: SessionId: 0x%08" PRIX32 " UserName: [%s] Domain: [%s]",
1117  info->sessionId, info->username, info->domain);
1118  return TRUE;
1119 fail:
1120  return FALSE;
1121 }
1122 
1123 static BOOL rdp_recv_logon_info_v2(rdpRdp* rdp, wStream* s, logon_info* info)
1124 {
1125  UINT16 Version = 0;
1126  UINT32 Size = 0;
1127  UINT32 cbDomain = 0;
1128  UINT32 cbUserName = 0;
1129 
1130  WINPR_ASSERT(rdp);
1131  WINPR_ASSERT(s);
1132  WINPR_ASSERT(info);
1133 
1134  WINPR_UNUSED(rdp);
1135 
1136  if (!Stream_CheckAndLogRequiredLength(TAG, s, logonInfoV2TotalSize))
1137  return FALSE;
1138 
1139  Stream_Read_UINT16(s, Version); /* Version (2 bytes) */
1140  if (Version != SAVE_SESSION_PDU_VERSION_ONE)
1141  {
1142  WLog_WARN(TAG, "LogonInfoV2::Version expected %" PRIu16 " bytes, got %" PRIu16,
1143  SAVE_SESSION_PDU_VERSION_ONE, Version);
1144  return FALSE;
1145  }
1146 
1147  Stream_Read_UINT32(s, Size); /* Size (4 bytes) */
1148 
1149  /* [MS-RDPBCGR] 2.2.10.1.1.2 Logon Info Version 2 (TS_LOGON_INFO_VERSION_2)
1150  * should be logonInfoV2TotalSize
1151  * but even MS server 2019 sends logonInfoV2Size
1152  */
1153  if (Size != logonInfoV2TotalSize)
1154  {
1155  if (Size != logonInfoV2Size)
1156  {
1157  WLog_WARN(TAG, "LogonInfoV2::Size expected %" PRIu32 " bytes, got %" PRIu32,
1158  logonInfoV2TotalSize, Size);
1159  return FALSE;
1160  }
1161  }
1162 
1163  Stream_Read_UINT32(s, info->sessionId); /* SessionId (4 bytes) */
1164  Stream_Read_UINT32(s, cbDomain); /* cbDomain (4 bytes) */
1165  Stream_Read_UINT32(s, cbUserName); /* cbUserName (4 bytes) */
1166  Stream_Seek(s, logonInfoV2ReservedSize); /* pad (558 bytes) */
1167 
1168  /* cbDomain is the size in bytes of the Unicode character data in the Domain field.
1169  * The size of the mandatory null terminator is include in this value.
1170  * Note: Since MS-RDPBCGR 2.2.10.1.1.2 does not mention any size limits we assume
1171  * that the maximum value is 52 bytes, according to the fixed size of the
1172  * Domain field in the Logon Info Version 1 (TS_LOGON_INFO) structure.
1173  */
1174  if (!rdp_info_read_string("Domain", s, cbDomain, 52, FALSE, &info->domain))
1175  goto fail;
1176 
1177  /* cbUserName is the size in bytes of the Unicode character data in the UserName field.
1178  * The size of the mandatory null terminator is include in this value.
1179  * Note: Since MS-RDPBCGR 2.2.10.1.1.2 does not mention any size limits we assume
1180  * that the maximum value is 512 bytes, according to the fixed size of the
1181  * Username field in the Logon Info Version 1 (TS_LOGON_INFO) structure.
1182  */
1183  if (!rdp_info_read_string("UserName", s, cbUserName, 512, FALSE, &info->username))
1184  goto fail;
1185 
1186  /* We´ve seen undocumented padding with windows 11 here.
1187  * unless it has actual data in it ignore it.
1188  * if there is unexpected data, print a warning and dump the contents
1189  */
1190  const size_t rem = Stream_GetRemainingLength(s);
1191  if (rem > 0)
1192  {
1193  BOOL warn = FALSE;
1194  const char* str = Stream_ConstPointer(s);
1195  for (size_t x = 0; x < rem; x++)
1196  {
1197  if (str[x] != '\0')
1198  warn = TRUE;
1199  }
1200  if (warn)
1201  {
1202  WLog_WARN(TAG, "unexpected padding of %" PRIuz " bytes, data not '\0'", rem);
1203  winpr_HexDump(TAG, WLOG_TRACE, str, rem);
1204  }
1205 
1206  if (!Stream_SafeSeek(s, rem))
1207  goto fail;
1208  }
1209 
1210  WLog_DBG(TAG, "LogonInfoV2: SessionId: 0x%08" PRIX32 " UserName: [%s] Domain: [%s]",
1211  info->sessionId, info->username, info->domain);
1212  return TRUE;
1213 fail:
1214  return FALSE;
1215 }
1216 
1217 static BOOL rdp_recv_logon_plain_notify(rdpRdp* rdp, wStream* s)
1218 {
1219  WINPR_UNUSED(rdp);
1220  if (!Stream_CheckAndLogRequiredLength(TAG, s, 576))
1221  return FALSE;
1222 
1223  Stream_Seek(s, 576); /* pad (576 bytes) */
1224  WLog_DBG(TAG, "LogonPlainNotify");
1225  return TRUE;
1226 }
1227 
1228 static BOOL rdp_recv_logon_error_info(rdpRdp* rdp, wStream* s, logon_info_ex* info)
1229 {
1230  freerdp* instance = NULL;
1231  UINT32 errorNotificationType = 0;
1232  UINT32 errorNotificationData = 0;
1233 
1234  WINPR_ASSERT(rdp);
1235  WINPR_ASSERT(rdp->context);
1236  WINPR_ASSERT(s);
1237  WINPR_ASSERT(info);
1238 
1239  instance = rdp->context->instance;
1240  WINPR_ASSERT(instance);
1241 
1242  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1243  return FALSE;
1244 
1245  Stream_Read_UINT32(s, errorNotificationType); /* errorNotificationType (4 bytes) */
1246  Stream_Read_UINT32(s, errorNotificationData); /* errorNotificationData (4 bytes) */
1247  WLog_DBG(TAG, "LogonErrorInfo: Data: 0x%08" PRIX32 " Type: 0x%08" PRIX32 "",
1248  errorNotificationData, errorNotificationType);
1249  IFCALL(instance->LogonErrorInfo, instance, errorNotificationData, errorNotificationType);
1250  info->ErrorNotificationType = errorNotificationType;
1251  info->ErrorNotificationData = errorNotificationData;
1252  return TRUE;
1253 }
1254 
1255 static BOOL rdp_recv_logon_info_extended(rdpRdp* rdp, wStream* s, logon_info_ex* info)
1256 {
1257  UINT32 cbFieldData = 0;
1258  UINT32 fieldsPresent = 0;
1259  UINT16 Length = 0;
1260 
1261  WINPR_ASSERT(rdp);
1262  WINPR_ASSERT(s);
1263  WINPR_ASSERT(info);
1264 
1265  if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
1266  {
1267  WLog_WARN(TAG, "received short logon info extended, need 6 bytes, got %" PRIuz,
1268  Stream_GetRemainingLength(s));
1269  return FALSE;
1270  }
1271 
1272  Stream_Read_UINT16(s, Length); /* Length (2 bytes) */
1273  Stream_Read_UINT32(s, fieldsPresent); /* fieldsPresent (4 bytes) */
1274 
1275  if ((Length < 6) || (!Stream_CheckAndLogRequiredLength(TAG, s, (Length - 6U))))
1276  {
1277  WLog_WARN(TAG,
1278  "received short logon info extended, need %" PRIu16 " - 6 bytes, got %" PRIuz,
1279  Length, Stream_GetRemainingLength(s));
1280  return FALSE;
1281  }
1282 
1283  WLog_DBG(TAG, "LogonInfoExtended: fieldsPresent: 0x%08" PRIX32 "", fieldsPresent);
1284 
1285  /* logonFields */
1286 
1287  if (fieldsPresent & LOGON_EX_AUTORECONNECTCOOKIE)
1288  {
1289  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1290  return FALSE;
1291 
1292  info->haveCookie = TRUE;
1293  Stream_Read_UINT32(s, cbFieldData); /* cbFieldData (4 bytes) */
1294 
1295  if (!Stream_CheckAndLogRequiredLength(TAG, s, cbFieldData))
1296  return FALSE;
1297 
1298  if (!rdp_read_server_auto_reconnect_cookie(rdp, s, info))
1299  return FALSE;
1300  }
1301 
1302  if (fieldsPresent & LOGON_EX_LOGONERRORS)
1303  {
1304  info->haveErrorInfo = TRUE;
1305 
1306  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1307  return FALSE;
1308 
1309  Stream_Read_UINT32(s, cbFieldData); /* cbFieldData (4 bytes) */
1310 
1311  if (!Stream_CheckAndLogRequiredLength(TAG, s, cbFieldData))
1312  return FALSE;
1313 
1314  if (!rdp_recv_logon_error_info(rdp, s, info))
1315  return FALSE;
1316  }
1317 
1318  if (!Stream_CheckAndLogRequiredLength(TAG, s, 570))
1319  return FALSE;
1320 
1321  Stream_Seek(s, 570); /* pad (570 bytes) */
1322  return TRUE;
1323 }
1324 
1325 BOOL rdp_recv_save_session_info(rdpRdp* rdp, wStream* s)
1326 {
1327  UINT32 infoType = 0;
1328  BOOL status = 0;
1329  logon_info logonInfo = { 0 };
1330  logon_info_ex logonInfoEx = { 0 };
1331  rdpContext* context = rdp->context;
1332  rdpUpdate* update = rdp->context->update;
1333 
1334  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1335  return FALSE;
1336 
1337  Stream_Read_UINT32(s, infoType); /* infoType (4 bytes) */
1338 
1339  switch (infoType)
1340  {
1341  case INFO_TYPE_LOGON:
1342  status = rdp_recv_logon_info_v1(rdp, s, &logonInfo);
1343 
1344  if (status && update->SaveSessionInfo)
1345  status = update->SaveSessionInfo(context, infoType, &logonInfo);
1346 
1347  rdp_free_logon_info(&logonInfo);
1348  break;
1349 
1350  case INFO_TYPE_LOGON_LONG:
1351  status = rdp_recv_logon_info_v2(rdp, s, &logonInfo);
1352 
1353  if (status && update->SaveSessionInfo)
1354  status = update->SaveSessionInfo(context, infoType, &logonInfo);
1355 
1356  rdp_free_logon_info(&logonInfo);
1357  break;
1358 
1359  case INFO_TYPE_LOGON_PLAIN_NOTIFY:
1360  status = rdp_recv_logon_plain_notify(rdp, s);
1361 
1362  if (status && update->SaveSessionInfo)
1363  status = update->SaveSessionInfo(context, infoType, NULL);
1364 
1365  break;
1366 
1367  case INFO_TYPE_LOGON_EXTENDED_INF:
1368  status = rdp_recv_logon_info_extended(rdp, s, &logonInfoEx);
1369 
1370  if (status && update->SaveSessionInfo)
1371  status = update->SaveSessionInfo(context, infoType, &logonInfoEx);
1372 
1373  break;
1374 
1375  default:
1376  WLog_ERR(TAG, "Unhandled saveSessionInfo type 0x%" PRIx32 "", infoType);
1377  status = TRUE;
1378  break;
1379  }
1380 
1381  if (!status)
1382  {
1383  WLog_DBG(TAG, "SaveSessionInfo error: infoType: %s (%" PRIu32 ")",
1384  infoType < 4 ? INFO_TYPE_LOGON_STRINGS[infoType % 4] : "Unknown", infoType);
1385  }
1386 
1387  return status;
1388 }
1389 
1390 static BOOL rdp_write_logon_info_v1(wStream* s, logon_info* info)
1391 {
1392  const size_t charLen = 52 / sizeof(WCHAR);
1393  const size_t userCharLen = 512 / sizeof(WCHAR);
1394 
1395  size_t sz = 4 + 52 + 4 + 512 + 4;
1396 
1397  if (!Stream_EnsureRemainingCapacity(s, sz))
1398  return FALSE;
1399 
1400  /* domain */
1401  {
1402  WINPR_ASSERT(info);
1403  if (!info->domain || !info->username)
1404  return FALSE;
1405  const size_t len = strnlen(info->domain, charLen + 1);
1406  if (len > charLen)
1407  return FALSE;
1408 
1409  const size_t wlen = len * sizeof(WCHAR);
1410  if (wlen > UINT32_MAX)
1411  return FALSE;
1412 
1413  Stream_Write_UINT32(s, (UINT32)wlen);
1414  if (Stream_Write_UTF16_String_From_UTF8(s, charLen, info->domain, len, TRUE) < 0)
1415  return FALSE;
1416  }
1417 
1418  /* username */
1419  {
1420  const size_t len = strnlen(info->username, userCharLen + 1);
1421  if (len > userCharLen)
1422  return FALSE;
1423 
1424  const size_t wlen = len * sizeof(WCHAR);
1425  if (wlen > UINT32_MAX)
1426  return FALSE;
1427 
1428  Stream_Write_UINT32(s, (UINT32)wlen);
1429 
1430  if (Stream_Write_UTF16_String_From_UTF8(s, userCharLen, info->username, len, TRUE) < 0)
1431  return FALSE;
1432  }
1433 
1434  /* sessionId */
1435  Stream_Write_UINT32(s, info->sessionId);
1436  return TRUE;
1437 }
1438 
1439 static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info)
1440 {
1441  size_t domainLen = 0;
1442  size_t usernameLen = 0;
1443 
1444  if (!Stream_EnsureRemainingCapacity(s, logonInfoV2TotalSize))
1445  return FALSE;
1446 
1447  Stream_Write_UINT16(s, SAVE_SESSION_PDU_VERSION_ONE);
1448  /* [MS-RDPBCGR] 2.2.10.1.1.2 Logon Info Version 2 (TS_LOGON_INFO_VERSION_2)
1449  * should be logonInfoV2TotalSize
1450  * but even MS server 2019 sends logonInfoV2Size
1451  */
1452  Stream_Write_UINT32(s, logonInfoV2Size);
1453  Stream_Write_UINT32(s, info->sessionId);
1454  domainLen = strnlen(info->domain, UINT32_MAX);
1455  if (domainLen >= UINT32_MAX / sizeof(WCHAR))
1456  return FALSE;
1457  Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * sizeof(WCHAR));
1458  usernameLen = strnlen(info->username, UINT32_MAX);
1459  if (usernameLen >= UINT32_MAX / sizeof(WCHAR))
1460  return FALSE;
1461  Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * sizeof(WCHAR));
1462  Stream_Seek(s, logonInfoV2ReservedSize);
1463  if (Stream_Write_UTF16_String_From_UTF8(s, domainLen + 1, info->domain, domainLen, TRUE) < 0)
1464  return FALSE;
1465  if (Stream_Write_UTF16_String_From_UTF8(s, usernameLen + 1, info->username, usernameLen, TRUE) <
1466  0)
1467  return FALSE;
1468  return TRUE;
1469 }
1470 
1471 static BOOL rdp_write_logon_info_plain(wStream* s)
1472 {
1473  if (!Stream_EnsureRemainingCapacity(s, 576))
1474  return FALSE;
1475 
1476  Stream_Seek(s, 576);
1477  return TRUE;
1478 }
1479 
1480 static BOOL rdp_write_logon_info_ex(wStream* s, logon_info_ex* info)
1481 {
1482  UINT32 FieldsPresent = 0;
1483  UINT16 Size = 2 + 4 + 570;
1484 
1485  if (info->haveCookie)
1486  {
1487  FieldsPresent |= LOGON_EX_AUTORECONNECTCOOKIE;
1488  Size += 28;
1489  }
1490 
1491  if (info->haveErrorInfo)
1492  {
1493  FieldsPresent |= LOGON_EX_LOGONERRORS;
1494  Size += 8;
1495  }
1496 
1497  if (!Stream_EnsureRemainingCapacity(s, Size))
1498  return FALSE;
1499 
1500  Stream_Write_UINT16(s, Size);
1501  Stream_Write_UINT32(s, FieldsPresent);
1502 
1503  if (info->haveCookie)
1504  {
1505  Stream_Write_UINT32(s, 28); /* cbFieldData (4 bytes) */
1506  Stream_Write_UINT32(s, 28); /* cbLen (4 bytes) */
1507  Stream_Write_UINT32(s, AUTO_RECONNECT_VERSION_1); /* Version (4 bytes) */
1508  Stream_Write_UINT32(s, info->LogonId); /* LogonId (4 bytes) */
1509  Stream_Write(s, info->ArcRandomBits, 16); /* ArcRandomBits (16 bytes) */
1510  }
1511 
1512  if (info->haveErrorInfo)
1513  {
1514  Stream_Write_UINT32(s, 8); /* cbFieldData (4 bytes) */
1515  Stream_Write_UINT32(s, info->ErrorNotificationType); /* ErrorNotificationType (4 bytes) */
1516  Stream_Write_UINT32(s, info->ErrorNotificationData); /* ErrorNotificationData (4 bytes) */
1517  }
1518 
1519  Stream_Seek(s, 570);
1520  return TRUE;
1521 }
1522 
1523 BOOL rdp_send_save_session_info(rdpContext* context, UINT32 type, void* data)
1524 {
1525  wStream* s = NULL;
1526  BOOL status = 0;
1527  rdpRdp* rdp = context->rdp;
1528  s = rdp_data_pdu_init(rdp);
1529 
1530  if (!s)
1531  return FALSE;
1532 
1533  Stream_Write_UINT32(s, type);
1534 
1535  switch (type)
1536  {
1537  case INFO_TYPE_LOGON:
1538  status = rdp_write_logon_info_v1(s, (logon_info*)data);
1539  break;
1540 
1541  case INFO_TYPE_LOGON_LONG:
1542  status = rdp_write_logon_info_v2(s, (logon_info*)data);
1543  break;
1544 
1545  case INFO_TYPE_LOGON_PLAIN_NOTIFY:
1546  status = rdp_write_logon_info_plain(s);
1547  break;
1548 
1549  case INFO_TYPE_LOGON_EXTENDED_INF:
1550  status = rdp_write_logon_info_ex(s, (logon_info_ex*)data);
1551  break;
1552 
1553  default:
1554  WLog_ERR(TAG, "saveSessionInfo type 0x%" PRIx32 " not handled", type);
1555  status = FALSE;
1556  break;
1557  }
1558 
1559  if (status)
1560  status = rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SAVE_SESSION_INFO, rdp->mcs->userId);
1561  else
1562  Stream_Release(s);
1563 
1564  return status;
1565 }
1566 
1567 BOOL rdp_send_server_status_info(rdpContext* context, UINT32 status)
1568 {
1569  wStream* s = NULL;
1570  rdpRdp* rdp = context->rdp;
1571  s = rdp_data_pdu_init(rdp);
1572 
1573  if (!s)
1574  return FALSE;
1575 
1576  Stream_Write_UINT32(s, status);
1577  return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_STATUS_INFO, rdp->mcs->userId);
1578 }
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
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.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string 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.
FREERDP_API BOOL freerdp_settings_set_string_len(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param, size_t len)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.