FreeRDP
redirection.c
1 
22 #include <freerdp/config.h>
23 
24 #include "settings.h"
25 
26 #include <winpr/crt.h>
27 #include <winpr/string.h>
28 #include <freerdp/log.h>
29 #include <freerdp/crypto/certificate.h>
30 #include <freerdp/redirection.h>
31 #include <freerdp/utils/string.h>
32 
33 #include "../crypto/certificate.h"
34 #include "redirection.h"
35 #include "utils.h"
36 
37 #define TAG FREERDP_TAG("core.redirection")
38 
39 struct rdp_redirection
40 {
41  UINT32 flags;
42  UINT32 sessionID;
43  BYTE* TsvUrl;
44  UINT32 TsvUrlLength;
45  char* Username;
46  char* Domain;
47  BYTE* Password;
48  UINT32 PasswordLength;
49  char* TargetFQDN;
50  BYTE* LoadBalanceInfo;
51  UINT32 LoadBalanceInfoLength;
52  char* TargetNetBiosName;
53  char* TargetNetAddress;
54  UINT32 TargetNetAddressesCount;
55  char** TargetNetAddresses;
56  UINT32 RedirectionGuidLength;
57  BYTE* RedirectionGuid;
58 
59  rdpCertificate* TargetCertificate;
60 };
61 
62 static void redirection_free_array(char*** what, UINT32* count)
63 {
64  WINPR_ASSERT(what);
65  WINPR_ASSERT(count);
66 
67  if (*what)
68  {
69  for (UINT32 x = 0; x < *count; x++)
70  free((*what)[x]);
71  free(*what);
72  }
73 
74  *what = NULL;
75  *count = 0;
76 }
77 
78 static void redirection_free_string(char** str)
79 {
80  WINPR_ASSERT(str);
81  free(*str);
82  *str = NULL;
83 }
84 
85 static void redirection_free_data(BYTE** str, UINT32* length)
86 {
87  WINPR_ASSERT(str);
88  free(*str);
89  if (length)
90  *length = 0;
91  *str = NULL;
92 }
93 
94 static BOOL redirection_copy_string(char** dst, const char* str)
95 {
96  redirection_free_string(dst);
97  if (!str)
98  return TRUE;
99 
100  *dst = _strdup(str);
101  return *dst != NULL;
102 }
103 
104 static BOOL redirection_copy_data(BYTE** dst, UINT32* plen, const BYTE* str, size_t len)
105 {
106  redirection_free_data(dst, plen);
107 
108  if (!str || (len == 0))
109  return TRUE;
110  if (len > UINT32_MAX)
111  return FALSE;
112 
113  *dst = malloc(len);
114  if (!*dst)
115  return FALSE;
116  memcpy(*dst, str, len);
117  *plen = (UINT32)len;
118  return *dst != NULL;
119 }
120 
121 static BOOL redirection_copy_array(char*** dst, UINT32* plen, const char** str, size_t len)
122 {
123  redirection_free_array(dst, plen);
124 
125  if (len > UINT32_MAX)
126  return FALSE;
127  if (!str || (len == 0))
128  return TRUE;
129 
130  *dst = calloc(len, sizeof(char*));
131  if (!*dst)
132  return FALSE;
133  *plen = (UINT32)len;
134 
135  for (size_t x = 0; x < len; x++)
136  {
137  if (str[x])
138  (*dst)[x] = _strdup(str[x]);
139 
140  if (!((*dst)[x]))
141  {
142  redirection_free_array(dst, plen);
143  return FALSE;
144  }
145  }
146 
147  return *dst != NULL;
148 }
149 
150 static BOOL rdp_redirection_get_data(wStream* s, UINT32* pLength, const BYTE** pData)
151 {
152  WINPR_ASSERT(pLength);
153  WINPR_ASSERT(pData);
154 
155  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
156  return FALSE;
157 
158  Stream_Read_UINT32(s, *pLength);
159 
160  if (!Stream_CheckAndLogRequiredLength(TAG, s, *pLength))
161  return FALSE;
162 
163  *pData = Stream_ConstPointer(s);
164  Stream_Seek(s, *pLength);
165  return TRUE;
166 }
167 
168 static BOOL rdp_redirection_read_unicode_string(wStream* s, char** str, size_t maxLength)
169 {
170  UINT32 length = 0;
171  const BYTE* data = NULL;
172 
173  if (!rdp_redirection_get_data(s, &length, &data))
174  return FALSE;
175 
176  const WCHAR* wstr = (const WCHAR*)data;
177 
178  if ((length % 2) || length < 2 || length > maxLength)
179  {
180  WLog_ERR(TAG, "failure: invalid unicode string length: %" PRIu32 "", length);
181  return FALSE;
182  }
183 
184  if (wstr[length / 2 - 1])
185  {
186  WLog_ERR(TAG, "failure: unterminated unicode string");
187  return FALSE;
188  }
189 
190  redirection_free_string(str);
191  *str = ConvertWCharNToUtf8Alloc(wstr, length / sizeof(WCHAR), NULL);
192  if (!*str)
193  {
194  WLog_ERR(TAG, "failure: string conversion failed");
195  return FALSE;
196  }
197 
198  return TRUE;
199 }
200 
201 static BOOL rdp_redirection_write_data(wStream* s, size_t length, const void* data)
202 {
203  WINPR_ASSERT(data || (length == 0));
204  WINPR_ASSERT(length <= UINT32_MAX);
205 
206  if (!Stream_CheckAndLogRequiredCapacity(TAG, s, 4))
207  return FALSE;
208 
209  Stream_Write_UINT32(s, (UINT32)length);
210 
211  if (!Stream_CheckAndLogRequiredCapacity(TAG, s, length))
212  return FALSE;
213 
214  Stream_Write(s, data, length);
215  return TRUE;
216 }
217 
218 static BOOL rdp_redirection_write_base64_wchar(UINT32 flag, wStream* s, size_t length,
219  const void* data)
220 {
221  BOOL rc = FALSE;
222 
223  char* base64 = crypto_base64_encode(data, length);
224  if (!base64)
225  return FALSE;
226 
227  size_t wbase64len = 0;
228  WCHAR* wbase64 = ConvertUtf8ToWCharAlloc(base64, &wbase64len);
229  free(base64);
230  if (!wbase64)
231  return FALSE;
232 
233  rc = rdp_redirection_write_data(s, wbase64len * sizeof(WCHAR), wbase64);
234  free(wbase64);
235  return rc;
236 }
237 
238 static BOOL rdp_redirection_read_base64_wchar(UINT32 flag, wStream* s, UINT32* pLength,
239  BYTE** pData)
240 {
241  BOOL rc = FALSE;
242  char buffer[64] = { 0 };
243  const BYTE* ptr = NULL;
244 
245  if (!rdp_redirection_get_data(s, pLength, &ptr))
246  return FALSE;
247  const WCHAR* wchar = (const WCHAR*)ptr;
248 
249  size_t utf8_len = 0;
250  char* utf8 = ConvertWCharNToUtf8Alloc(wchar, *pLength / sizeof(WCHAR), &utf8_len);
251  if (!utf8)
252  goto fail;
253 
254  redirection_free_data(pData, NULL);
255 
256  utf8_len = strnlen(utf8, utf8_len);
257  *pData = NULL;
258  if (utf8_len > 0)
259  *pData = calloc(utf8_len, sizeof(BYTE));
260  if (!*pData)
261  goto fail;
262 
263  size_t rlen = utf8_len;
264  size_t wpos = 0;
265  char* saveptr = NULL;
266  char* tok = strtok_s(utf8, "\r\n", &saveptr);
267  while (tok)
268  {
269  const size_t len = strnlen(tok, rlen);
270  rlen -= len;
271 
272  size_t bplen = 0;
273  BYTE* bptr = NULL;
274  crypto_base64_decode(tok, len, &bptr, &bplen);
275  if (!bptr)
276  goto fail;
277  memcpy(&(*pData)[wpos], bptr, bplen);
278  wpos += bplen;
279  free(bptr);
280 
281  tok = strtok_s(NULL, "\r\n", &saveptr);
282  }
283  if (wpos > UINT32_MAX)
284  goto fail;
285 
286  *pLength = (UINT32)wpos;
287 
288  WLog_DBG(TAG, "%s:", rdp_redirection_flags_to_string(flag, buffer, sizeof(buffer)));
289 
290  rc = TRUE;
291 fail:
292  if (!rc)
293  WLog_ERR(TAG, "failed to read base64 data");
294  free(utf8);
295  return rc;
296 }
297 
298 static BOOL rdp_target_cert_get_element(wStream* s, UINT32* pType, UINT32* pEncoding,
299  const BYTE** ptr, size_t* pLength)
300 {
301  WINPR_ASSERT(pType);
302  WINPR_ASSERT(pEncoding);
303  WINPR_ASSERT(ptr);
304  WINPR_ASSERT(pLength);
305 
306  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
307  return FALSE;
308 
309  UINT32 type = 0;
310  UINT32 encoding = 0;
311  UINT32 elementSize = 0;
312 
313  Stream_Read_UINT32(s, type);
314  Stream_Read_UINT32(s, encoding);
315  Stream_Read_UINT32(s, elementSize);
316 
317  if (!Stream_CheckAndLogRequiredLength(TAG, s, elementSize))
318  return FALSE;
319 
320  *ptr = Stream_ConstPointer(s);
321  *pLength = elementSize;
322  Stream_Seek(s, elementSize);
323 
324  *pType = type;
325  *pEncoding = encoding;
326  return TRUE;
327 }
328 
329 static BOOL rdp_target_cert_write_element(wStream* s, UINT32 Type, UINT32 Encoding,
330  const BYTE* data, size_t length)
331 {
332  WINPR_ASSERT(data || (length == 0));
333  WINPR_ASSERT(length <= UINT32_MAX);
334 
335  if (!Stream_CheckAndLogRequiredCapacity(TAG, s, 12))
336  return FALSE;
337 
338  Stream_Write_UINT32(s, Type);
339  Stream_Write_UINT32(s, Encoding);
340  Stream_Write_UINT32(s, (UINT32)length);
341 
342  if (!Stream_CheckAndLogRequiredCapacity(TAG, s, length))
343  return FALSE;
344 
345  Stream_Write(s, data, length);
346  return TRUE;
347 }
348 
349 BOOL rdp_redirection_read_target_cert(rdpCertificate** ptargetCertificate, const BYTE* data,
350  size_t length)
351 {
352  WINPR_ASSERT(ptargetCertificate);
353 
354  wStream sbuffer = { 0 };
355  wStream* s = Stream_StaticConstInit(&sbuffer, data, length);
356 
357  freerdp_certificate_free(*ptargetCertificate);
358  *ptargetCertificate = NULL;
359 
360  size_t plength = 0;
361  const BYTE* ptr = NULL;
362  while (Stream_GetRemainingLength(s) > 0)
363  {
364  UINT32 type = 0;
365  UINT32 encoding = 0;
366  if (!rdp_target_cert_get_element(s, &type, &encoding, &ptr, &plength))
367  return FALSE;
368 
369  switch (type)
370  {
371  case CERT_cert_file_element:
372  if (encoding == ENCODING_TYPE_ASN1_DER)
373  {
374  if (*ptargetCertificate)
375  WLog_WARN(TAG, "Duplicate TargetCertificate in data detected!");
376  else
377  {
378  *ptargetCertificate = freerdp_certificate_new_from_der(ptr, plength);
379  if (!*ptargetCertificate)
380  WLog_ERR(TAG, "TargetCertificate parsing DER data failed");
381  }
382  }
383  else
384  {
385  WLog_ERR(TAG,
386  "TargetCertificate data in unknown encoding %" PRIu32 " detected!");
387  }
388  break;
389  default: /* ignore unknown fields */
390  WLog_VRB(TAG,
391  "Unknown TargetCertificate field type %" PRIu32 ", encoding %" PRIu32
392  " of length %" PRIu32,
393  type, encoding, plength);
394  break;
395  }
396  }
397 
398  return *ptargetCertificate != NULL;
399 }
400 
401 static BOOL rdp_redirection_write_target_cert(wStream* s, const rdpRedirection* redirection)
402 {
403  BOOL rc = FALSE;
404  WINPR_ASSERT(redirection);
405 
406  const rdpCertificate* cert = redirection->TargetCertificate;
407  if (!cert)
408  return FALSE;
409 
410  size_t derlen = 0;
411 
412  BYTE* der = freerdp_certificate_get_der(cert, &derlen);
413  if (!rdp_target_cert_write_element(s, CERT_cert_file_element, ENCODING_TYPE_ASN1_DER, der,
414  derlen))
415  goto fail;
416 
417  rc = TRUE;
418 
419 fail:
420  free(der);
421  return rc;
422 }
423 
424 static BOOL rdp_redireciton_write_target_cert_stream(wStream* s, const rdpRedirection* redirection)
425 {
426  BOOL rc = FALSE;
427  wStream* serialized = Stream_New(NULL, 2048);
428  if (!serialized)
429  goto fail;
430 
431  if (!rdp_redirection_write_target_cert(serialized, redirection))
432  goto fail;
433 
434  rc = rdp_redirection_write_base64_wchar(
435  LB_TARGET_CERTIFICATE, s, Stream_GetPosition(serialized), Stream_Buffer(serialized));
436 
437 fail:
438  Stream_Free(serialized, TRUE);
439  return rc;
440 }
441 
442 static BOOL rdp_redirection_read_target_cert_stream(wStream* s, rdpRedirection* redirection)
443 {
444  UINT32 length = 0;
445  BYTE* ptr = NULL;
446 
447  WINPR_ASSERT(redirection);
448 
449  BOOL rc = FALSE;
450  if (rdp_redirection_read_base64_wchar(LB_TARGET_CERTIFICATE, s, &length, &ptr))
451  rc = rdp_redirection_read_target_cert(&redirection->TargetCertificate, ptr, length);
452  free(ptr);
453  return rc;
454 }
455 
456 int rdp_redirection_apply_settings(rdpRdp* rdp)
457 {
458  rdpSettings* settings = NULL;
459  rdpRedirection* redirection = NULL;
460 
461  if (!rdp_reset_runtime_settings(rdp))
462  return -1;
463 
464  settings = rdp->settings;
465  WINPR_ASSERT(settings);
466 
467  redirection = rdp->redirection;
468  WINPR_ASSERT(redirection);
469 
470  settings->RedirectionFlags = redirection->flags;
471  settings->RedirectedSessionId = redirection->sessionID;
472 
473  if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESS)
474  {
475  if (!freerdp_settings_set_string(settings, FreeRDP_TargetNetAddress,
476  redirection->TargetNetAddress))
477  return -1;
478  }
479 
480  if (settings->RedirectionFlags & LB_USERNAME)
481  {
482  if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionUsername,
483  redirection->Username))
484  return -1;
485  }
486 
487  if (settings->RedirectionFlags & LB_DOMAIN)
488  {
489  if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionDomain, redirection->Domain))
490  return -1;
491  }
492 
493  if (settings->RedirectionFlags & LB_PASSWORD)
494  {
495  /* Password may be a cookie without a null terminator */
496  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionPassword,
497  redirection->Password, redirection->PasswordLength))
498  return -1;
499  }
500 
501  if (settings->RedirectionFlags & LB_DONTSTOREUSERNAME)
502  {
503  // TODO
504  }
505 
506  if (settings->RedirectionFlags & LB_SMARTCARD_LOGON)
507  {
508  // TODO
509  }
510 
511  if (settings->RedirectionFlags & LB_NOREDIRECT)
512  {
513  // TODO
514  }
515 
516  if (settings->RedirectionFlags & LB_TARGET_FQDN)
517  {
518  if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetFQDN,
519  redirection->TargetFQDN))
520  return -1;
521  }
522 
523  if (settings->RedirectionFlags & LB_TARGET_NETBIOS_NAME)
524  {
525  if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetNetBiosName,
526  redirection->TargetNetBiosName))
527  return -1;
528  }
529 
530  if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESSES)
531  {
532  if (!freerdp_target_net_addresses_copy(settings, redirection->TargetNetAddresses,
533  redirection->TargetNetAddressesCount))
534  return -1;
535  }
536 
537  if (settings->RedirectionFlags & LB_CLIENT_TSV_URL)
538  {
539  /* TsvUrl may not contain a null terminator */
540  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionTsvUrl,
541  redirection->TsvUrl, redirection->TsvUrlLength))
542  return -1;
543 
544  const size_t lblen = freerdp_settings_get_uint32(settings, FreeRDP_LoadBalanceInfoLength);
545  const char* lb = freerdp_settings_get_pointer(settings, FreeRDP_LoadBalanceInfo);
546  if (lblen > 0)
547  {
548  BOOL valid = TRUE;
549  size_t tsvlen = 0;
550 
551  char* tsv =
552  ConvertWCharNToUtf8Alloc((const WCHAR*)redirection->TsvUrl,
553  redirection->TsvUrlLength / sizeof(WCHAR), &tsvlen);
554  if (!tsv || !lb)
555  valid = FALSE;
556  else if (tsvlen != lblen)
557  valid = FALSE;
558  else if (memcmp(tsv, lb, lblen) != 0)
559  valid = FALSE;
560 
561  if (!valid)
562  {
563  WLog_ERR(TAG,
564  "[redirection] Expected TsvUrl '%s' [%" PRIuz "], but got '%s' [%" PRIuz
565  "]",
566  lb, lblen, tsv, tsvlen);
567  }
568  free(tsv);
569 
570  if (!valid)
571  return -2;
572  }
573  }
574 
575  if (settings->RedirectionFlags & LB_SERVER_TSV_CAPABLE)
576  {
577  // TODO
578  }
579 
580  if (settings->RedirectionFlags & LB_LOAD_BALANCE_INFO)
581  {
582  /* LoadBalanceInfo may not contain a null terminator */
583  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo,
584  redirection->LoadBalanceInfo,
585  redirection->LoadBalanceInfoLength))
586  return -1;
587  }
588  else
589  {
594  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, NULL, 0))
595  return -1;
596  }
597 
598  if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
599  {
600  // TODO
601  }
602 
603  if (settings->RedirectionFlags & LB_REDIRECTION_GUID)
604  {
605  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionGuid,
606  redirection->RedirectionGuid,
607  redirection->RedirectionGuidLength))
608  return -1;
609  }
610 
611  if (settings->RedirectionFlags & LB_TARGET_CERTIFICATE)
612  {
613  rdpCertificate* cert = freerdp_certificate_clone(redirection->TargetCertificate);
614  if (!freerdp_settings_set_pointer(settings, FreeRDP_RedirectionTargetCertificate, cert))
615  return -1;
616 
617  BOOL pres = FALSE;
618  size_t length = 0;
619  char* pem = freerdp_certificate_get_pem(cert, &length);
620  if (pem && (length <= UINT32_MAX))
621  {
622  pres = freerdp_settings_set_string_len(settings, FreeRDP_RedirectionAcceptedCert, pem,
623  length);
624  if (pres)
625  pres = freerdp_settings_set_uint32(settings, FreeRDP_RedirectionAcceptedCertLength,
626  (UINT32)length);
627  }
628  free(pem);
629  if (!pres)
630  return -1;
631  }
632 
633  return 0;
634 }
635 
636 static BOOL rdp_redirection_read_data(UINT32 flag, wStream* s, UINT32* pLength, BYTE** pData)
637 {
638  char buffer[64] = { 0 };
639  const BYTE* ptr = NULL;
640 
641  if (!rdp_redirection_get_data(s, pLength, &ptr))
642  return FALSE;
643 
644  redirection_free_data(pData, NULL);
645  *pData = (BYTE*)malloc(*pLength);
646 
647  if (!*pData)
648  return FALSE;
649  memcpy(*pData, ptr, *pLength);
650 
651  WLog_DBG(TAG, "%s:", rdp_redirection_flags_to_string(flag, buffer, sizeof(buffer)));
652 
653  return TRUE;
654 }
655 
656 static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
657 {
658  char buffer[256] = { 0 };
659  UINT16 flags = 0;
660  UINT16 length = 0;
661  rdpRedirection* redirection = rdp->redirection;
662 
663  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
664  return STATE_RUN_FAILED;
665 
666  Stream_Read_UINT16(s, flags); /* flags (2 bytes) */
667  if (flags != SEC_REDIRECTION_PKT)
668  {
669  char buffer1[1024] = { 0 };
670  char buffer2[1024] = { 0 };
671  WLog_ERR(TAG, "received invalid flags=%s, expected %s",
672  rdp_security_flag_string(flags, buffer1, sizeof(buffer1)),
673  rdp_security_flag_string(SEC_REDIRECTION_PKT, buffer2, sizeof(buffer2)));
674  return STATE_RUN_FAILED;
675  }
676  Stream_Read_UINT16(s, length); /* length (2 bytes) */
677  Stream_Read_UINT32(s, redirection->sessionID); /* sessionID (4 bytes) */
678  Stream_Read_UINT32(s, redirection->flags); /* redirFlags (4 bytes) */
679  WLog_INFO(TAG,
680  "flags: 0x%04" PRIX16 ", length: %" PRIu16 ", sessionID: 0x%08" PRIX32
681  ", redirFlags: %s [0x%08" PRIX32 "]",
682  flags, length, redirection->sessionID,
683  rdp_redirection_flags_to_string(redirection->flags, buffer, sizeof(buffer)),
684  redirection->flags);
685 
686  /* Although MS-RDPBCGR does not mention any length constraints limits for the
687  * variable length null-terminated unicode strings in the RDP_SERVER_REDIRECTION_PACKET
688  * structure we will use the following limits in bytes including the null terminator:
689  *
690  * TargetNetAddress: 80 bytes
691  * UserName: 512 bytes
692  * Domain: 52 bytes
693  * Password(Cookie): 512 bytes
694  * TargetFQDN: 512 bytes
695  * TargetNetBiosName: 32 bytes
696  */
697 
698  if (redirection->flags & LB_TARGET_NET_ADDRESS)
699  {
700  if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddress), 80))
701  return STATE_RUN_FAILED;
702  }
703 
704  if (redirection->flags & LB_LOAD_BALANCE_INFO)
705  {
706  /* See [MSFT-SDLBTS] (a.k.a. TS_Session_Directory.doc)
707  * load balance info example data:
708  * 0000 43 6f 6f 6b 69 65 3a 20 6d 73 74 73 3d 32 31 33 Cookie: msts=213
709  * 0010 34 30 32 36 34 33 32 2e 31 35 36 32 39 2e 30 30 4026432.15629.00
710  * 0020 30 30 0d 0a 00..
711  */
712  if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
713  &redirection->LoadBalanceInfo))
714  return STATE_RUN_FAILED;
715  }
716 
717  if (redirection->flags & LB_USERNAME)
718  {
719  if (!rdp_redirection_read_unicode_string(s, &(redirection->Username), 512))
720  return STATE_RUN_FAILED;
721 
722  WLog_DBG(TAG, "Username: %s", redirection->Username);
723  }
724 
725  if (redirection->flags & LB_DOMAIN)
726  {
727  if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52))
728  return STATE_RUN_FAILED;
729 
730  WLog_DBG(TAG, "Domain: %s", redirection->Domain);
731  }
732 
733  if (redirection->flags & LB_PASSWORD)
734  {
735  /* Note: Password is a variable-length array of bytes containing the
736  * password used by the user in Unicode format, including a null-terminator
737  * or (!) or a cookie value that MUST be passed to the target server on
738  * successful connection.
739  * Since the format of the password cookie (probably some salted hash) is
740  * currently unknown we'll treat it as opaque data. All cookies seen so far
741  * are 120 bytes including \0\0 termination.
742  * Here is an observed example of a redirection password cookie:
743  *
744  * 0000 02 00 00 80 44 53 48 4c 60 ab 69 2f 07 d6 9e 2d ....DSHL`.i/...-
745  * 0010 f0 3a 97 3b a9 c5 ec 7e 66 bd b3 84 6c b1 ef b9 .:.;...~f...l...
746  * 0020 b6 82 4e cc 3a df 64 b7 7b 25 04 54 c2 58 98 f8 ..N.:.d.{%.T.X..
747  * 0030 97 87 d4 93 c7 c1 e1 5b c2 85 f8 22 49 1f 81 88 .......[..."I...
748  * 0040 43 44 83 f6 9a 72 40 24 dc 4d 43 cb d9 92 3c 8f CD...r@$.MC...<.
749  * 0050 3a 37 5c 77 13 a0 72 3c 72 08 64 2a 29 fb dc eb :7\w..r<r.d*)...
750  * 0060 0d 2b 06 b4 c6 08 b4 73 34 16 93 62 6d 24 e9 93 .+.....s4..bm$..
751  * 0070 97 27 7b dd 9a 72 00 00 .'{..r..
752  *
753  * Notwithstanding the above, we'll allocated an additional zero WCHAR at the
754  * end of the buffer which won't get counted in PasswordLength.
755  */
756  if (!rdp_redirection_read_data(LB_PASSWORD, s, &redirection->PasswordLength,
757  &redirection->Password))
758  return STATE_RUN_FAILED;
759 
760  /* [MS-RDPBCGR] specifies 512 bytes as the upper limit for the password length
761  * including the null terminatior(s). This should also be enough for the unknown
762  * password cookie format (see previous comment).
763  */
764  if ((redirection->flags & LB_PASSWORD_IS_PK_ENCRYPTED) == 0)
765  {
766  const size_t charLen = redirection->PasswordLength / sizeof(WCHAR);
767  if (redirection->PasswordLength > LB_PASSWORD_MAX_LENGTH)
768  {
769  WLog_ERR(TAG, "LB_PASSWORD: %" PRIuz " exceeds limit of %d", charLen,
770  LB_PASSWORD_MAX_LENGTH);
771  return STATE_RUN_FAILED;
772  }
773 
774  /* Ensure the text password is '\0' terminated */
775  if (_wcsnlen((const WCHAR*)redirection->Password, charLen) == charLen)
776  {
777  WLog_ERR(TAG, "LB_PASSWORD: missing '\0' termination");
778  return STATE_RUN_FAILED;
779  }
780  }
781  }
782 
783  if (redirection->flags & LB_TARGET_FQDN)
784  {
785  if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetFQDN), 512))
786  return STATE_RUN_FAILED;
787 
788  WLog_DBG(TAG, "TargetFQDN: %s", redirection->TargetFQDN);
789  }
790 
791  if (redirection->flags & LB_TARGET_NETBIOS_NAME)
792  {
793  if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetBiosName), 32))
794  return STATE_RUN_FAILED;
795 
796  WLog_DBG(TAG, "TargetNetBiosName: %s", redirection->TargetNetBiosName);
797  }
798 
799  if (redirection->flags & LB_CLIENT_TSV_URL)
800  {
801  if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
802  &redirection->TsvUrl))
803  return STATE_RUN_FAILED;
804  }
805 
806  if (redirection->flags & LB_REDIRECTION_GUID)
807  {
808  if (!rdp_redirection_read_data(LB_REDIRECTION_GUID, s, &redirection->RedirectionGuidLength,
809  &redirection->RedirectionGuid))
810  return STATE_RUN_FAILED;
811  }
812 
813  if (redirection->flags & LB_TARGET_CERTIFICATE)
814  {
815  if (!rdp_redirection_read_target_cert_stream(s, redirection))
816  return STATE_RUN_FAILED;
817  }
818 
819  if (redirection->flags & LB_TARGET_NET_ADDRESSES)
820  {
821  UINT32 targetNetAddressesLength = 0;
822  UINT32 TargetNetAddressesCount = 0;
823 
824  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
825  return STATE_RUN_FAILED;
826 
827  Stream_Read_UINT32(s, targetNetAddressesLength);
828  Stream_Read_UINT32(s, TargetNetAddressesCount);
829 
830  /* sanity check: the whole packet has a length limit of UINT16_MAX
831  * each TargetNetAddress is a WCHAR string, so minimum length 2 bytes
832  */
833  const size_t size = TargetNetAddressesCount * sizeof(WCHAR);
834  if ((size > Stream_GetRemainingLength(s)) || (size > targetNetAddressesLength))
835  {
836  WLog_ERR(TAG,
837  "Invalid RDP_SERVER_REDIRECTION_PACKET::TargetNetAddressLength %" PRIuz
838  ", sanity limit is %" PRIuz,
839  TargetNetAddressesCount * sizeof(WCHAR), Stream_GetRemainingLength(s));
840  return STATE_RUN_FAILED;
841  }
842 
843  redirection_free_array(&redirection->TargetNetAddresses,
844  &redirection->TargetNetAddressesCount);
845  if (TargetNetAddressesCount > 0)
846  {
847  redirection->TargetNetAddresses =
848  (char**)calloc(TargetNetAddressesCount, sizeof(char*));
849 
850  if (!redirection->TargetNetAddresses)
851  {
852  WLog_ERR(TAG, "TargetNetAddresses %" PRIu32 " failed to allocate",
853  TargetNetAddressesCount);
854  return STATE_RUN_FAILED;
855  }
856  }
857  redirection->TargetNetAddressesCount = TargetNetAddressesCount;
858 
859  WLog_DBG(TAG, "TargetNetAddressesCount: %" PRIu32 "", TargetNetAddressesCount);
860 
861  for (UINT32 i = 0; i < TargetNetAddressesCount; i++)
862  {
863  if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80))
864  return STATE_RUN_FAILED;
865 
866  WLog_DBG(TAG, "TargetNetAddresses[%" PRIu32 "]: %s", i,
867  redirection->TargetNetAddresses[i]);
868  }
869  }
870 
871  if (Stream_GetRemainingLength(s) >= 8)
872  {
873  /* some versions of windows don't included this padding before closing the connection */
874  Stream_Seek(s, 8); /* pad (8 bytes) */
875  }
876 
877  if (redirection->flags & LB_NOREDIRECT)
878  return STATE_RUN_SUCCESS;
879 
880  return STATE_RUN_REDIRECT;
881 }
882 
883 state_run_t rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s)
884 {
885  state_run_t status = STATE_RUN_SUCCESS;
886 
887  if (!Stream_SafeSeek(s, 2)) /* pad2Octets (2 bytes) */
888  return STATE_RUN_FAILED;
889 
890  status = rdp_recv_server_redirection_pdu(rdp, s);
891 
892  if (state_run_failed(status))
893  return status;
894 
895  if (Stream_GetRemainingLength(s) >= 1)
896  {
897  /* this field is optional, and its absence is not an error */
898  Stream_Seek(s, 1); /* pad2Octets (1 byte) */
899  }
900 
901  return status;
902 }
903 
904 rdpRedirection* redirection_new(void)
905 {
906  rdpRedirection* redirection = (rdpRedirection*)calloc(1, sizeof(rdpRedirection));
907 
908  if (redirection)
909  {
910  }
911 
912  return redirection;
913 }
914 
915 void redirection_free(rdpRedirection* redirection)
916 {
917  if (redirection)
918  {
919  redirection_free_data(&redirection->TsvUrl, &redirection->TsvUrlLength);
920  redirection_free_string(&redirection->Username);
921  redirection_free_string(&redirection->Domain);
922  redirection_free_string(&redirection->TargetFQDN);
923  redirection_free_string(&redirection->TargetNetBiosName);
924  redirection_free_string(&redirection->TargetNetAddress);
925  redirection_free_data(&redirection->LoadBalanceInfo, &redirection->LoadBalanceInfoLength);
926  redirection_free_data(&redirection->Password, &redirection->PasswordLength);
927  redirection_free_data(&redirection->RedirectionGuid, &redirection->RedirectionGuidLength);
928  freerdp_certificate_free(redirection->TargetCertificate);
929  redirection_free_array(&redirection->TargetNetAddresses,
930  &redirection->TargetNetAddressesCount);
931 
932  free(redirection);
933  }
934 }
935 
936 static SSIZE_T redir_write_string(UINT32 flag, wStream* s, const char* str)
937 {
938  const size_t length = (strlen(str) + 1);
939  if (!Stream_EnsureRemainingCapacity(s, 4ull + length * sizeof(WCHAR)))
940  return -1;
941 
942  const size_t pos = Stream_GetPosition(s);
943  Stream_Write_UINT32(s, (UINT32)length * sizeof(WCHAR));
944  if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
945  return -1;
946  return (SSIZE_T)(Stream_GetPosition(s) - pos);
947 }
948 
949 static BOOL redir_write_data(UINT32 flag, wStream* s, UINT32 length, const BYTE* data)
950 {
951  if (!Stream_EnsureRemainingCapacity(s, 4ull + length))
952  return FALSE;
953 
954  Stream_Write_UINT32(s, length);
955  Stream_Write(s, data, length);
956  return TRUE;
957 }
958 
959 BOOL rdp_write_enhanced_security_redirection_packet(wStream* s, const rdpRedirection* redirection)
960 {
961  BOOL rc = FALSE;
962 
963  WINPR_ASSERT(s);
964  WINPR_ASSERT(redirection);
965 
966  if (!Stream_EnsureRemainingCapacity(s, 14))
967  goto fail;
968 
969  Stream_Write_UINT16(s, 0);
970 
971  const size_t start = Stream_GetPosition(s);
972  Stream_Write_UINT16(s, SEC_REDIRECTION_PKT);
973  const size_t lengthOffset = Stream_GetPosition(s);
974  Stream_Seek_UINT16(s); /* placeholder for length */
975 
976  if (redirection->sessionID)
977  Stream_Write_UINT32(s, redirection->sessionID);
978  else
979  Stream_Write_UINT32(s, 0);
980 
981  Stream_Write_UINT32(s, redirection->flags);
982 
983  if (redirection->flags & LB_TARGET_NET_ADDRESS)
984  {
985  if (redir_write_string(LB_TARGET_NET_ADDRESS, s, redirection->TargetNetAddress) < 0)
986  goto fail;
987  }
988 
989  if (redirection->flags & LB_LOAD_BALANCE_INFO)
990  {
991  const UINT32 length = 13 + redirection->LoadBalanceInfoLength + 2;
992  if (!Stream_EnsureRemainingCapacity(s, length))
993  goto fail;
994  Stream_Write_UINT32(s, length);
995  Stream_Write(s, "Cookie: msts=", 13);
996  Stream_Write(s, redirection->LoadBalanceInfo, redirection->LoadBalanceInfoLength);
997  Stream_Write_UINT8(s, 0x0d);
998  Stream_Write_UINT8(s, 0x0a);
999  }
1000 
1001  if (redirection->flags & LB_USERNAME)
1002  {
1003  if (redir_write_string(LB_USERNAME, s, redirection->Username) < 0)
1004  goto fail;
1005  }
1006 
1007  if (redirection->flags & LB_DOMAIN)
1008  {
1009  if (redir_write_string(LB_DOMAIN, s, redirection->Domain) < 0)
1010  goto fail;
1011  }
1012 
1013  if (redirection->flags & LB_PASSWORD)
1014  {
1015  /* Password is eighter UNICODE or opaque data */
1016  if (!redir_write_data(LB_PASSWORD, s, redirection->PasswordLength, redirection->Password))
1017  goto fail;
1018  }
1019 
1020  if (redirection->flags & LB_TARGET_FQDN)
1021  {
1022  if (redir_write_string(LB_TARGET_FQDN, s, redirection->TargetFQDN) < 0)
1023  goto fail;
1024  }
1025 
1026  if (redirection->flags & LB_TARGET_NETBIOS_NAME)
1027  {
1028  if (redir_write_string(LB_TARGET_NETBIOS_NAME, s, redirection->TargetNetBiosName) < 0)
1029  goto fail;
1030  }
1031 
1032  if (redirection->flags & LB_CLIENT_TSV_URL)
1033  {
1034  if (!redir_write_data(LB_CLIENT_TSV_URL, s, redirection->TsvUrlLength, redirection->TsvUrl))
1035  goto fail;
1036  }
1037 
1038  if (redirection->flags & LB_REDIRECTION_GUID)
1039  {
1040  if (!redir_write_data(LB_REDIRECTION_GUID, s, redirection->RedirectionGuidLength,
1041  redirection->RedirectionGuid))
1042  goto fail;
1043  }
1044 
1045  if (redirection->flags & LB_TARGET_CERTIFICATE)
1046  {
1047  if (!rdp_redireciton_write_target_cert_stream(s, redirection))
1048  goto fail;
1049  }
1050 
1051  if (redirection->flags & LB_TARGET_NET_ADDRESSES)
1052  {
1053  UINT32 length = sizeof(UINT32);
1054 
1055  if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT32)))
1056  goto fail;
1057 
1058  const size_t lstart = Stream_GetPosition(s);
1059  Stream_Seek_UINT32(s); /* length of field */
1060  Stream_Write_UINT32(s, redirection->TargetNetAddressesCount);
1061  for (UINT32 i = 0; i < redirection->TargetNetAddressesCount; i++)
1062  {
1063  const SSIZE_T rcc =
1064  redir_write_string(LB_TARGET_NET_ADDRESSES, s, redirection->TargetNetAddresses[i]);
1065  if (rcc < 0)
1066  goto fail;
1067  length += (UINT32)rcc;
1068  }
1069 
1070  /* Write length field */
1071  const size_t lend = Stream_GetPosition(s);
1072  Stream_SetPosition(s, lstart);
1073  Stream_Write_UINT32(s, length);
1074  Stream_SetPosition(s, lend);
1075  }
1076 
1077  /* Padding 8 bytes */
1078  if (!Stream_EnsureRemainingCapacity(s, 8))
1079  goto fail;
1080  Stream_Zero(s, 8);
1081 
1082  const size_t end = Stream_GetPosition(s);
1083  Stream_SetPosition(s, lengthOffset);
1084  Stream_Write_UINT16(s, (UINT16)(end - start));
1085  Stream_SetPosition(s, end);
1086 
1087  rc = TRUE;
1088 fail:
1089  return rc;
1090 }
1091 
1092 BOOL redirection_settings_are_valid(rdpRedirection* redirection, UINT32* pFlags)
1093 {
1094  UINT32 flags = 0;
1095 
1096  WINPR_ASSERT(redirection);
1097 
1098  if (redirection->flags & LB_CLIENT_TSV_URL)
1099  {
1100  if (!redirection->TsvUrl || (redirection->TsvUrlLength == 0))
1101  flags |= LB_CLIENT_TSV_URL;
1102  }
1103 
1104  if (redirection->flags & LB_SERVER_TSV_CAPABLE)
1105  {
1106  if ((redirection->flags & LB_CLIENT_TSV_URL) == 0)
1107  flags |= LB_SERVER_TSV_CAPABLE;
1108  }
1109 
1110  if (redirection->flags & LB_USERNAME)
1111  {
1112  if (utils_str_is_empty(redirection->Username))
1113  flags |= LB_USERNAME;
1114  }
1115 
1116  if (redirection->flags & LB_DOMAIN)
1117  {
1118  if (utils_str_is_empty(redirection->Domain))
1119  flags |= LB_DOMAIN;
1120  }
1121 
1122  if (redirection->flags & LB_PASSWORD)
1123  {
1124  if (!redirection->Password || (redirection->PasswordLength == 0))
1125  flags |= LB_PASSWORD;
1126  }
1127 
1128  if (redirection->flags & LB_TARGET_FQDN)
1129  {
1130  if (utils_str_is_empty(redirection->TargetFQDN))
1131  flags |= LB_TARGET_FQDN;
1132  }
1133 
1134  if (redirection->flags & LB_LOAD_BALANCE_INFO)
1135  {
1136  if (!redirection->LoadBalanceInfo || (redirection->LoadBalanceInfoLength == 0))
1137  flags |= LB_LOAD_BALANCE_INFO;
1138  }
1139 
1140  if (redirection->flags & LB_TARGET_NETBIOS_NAME)
1141  {
1142  if (utils_str_is_empty(redirection->TargetNetBiosName))
1143  flags |= LB_TARGET_NETBIOS_NAME;
1144  }
1145 
1146  if (redirection->flags & LB_TARGET_NET_ADDRESS)
1147  {
1148  if (utils_str_is_empty(redirection->TargetNetAddress))
1149  flags |= LB_TARGET_NET_ADDRESS;
1150  }
1151 
1152  if (redirection->flags & LB_TARGET_NET_ADDRESSES)
1153  {
1154  if (!redirection->TargetNetAddresses || (redirection->TargetNetAddressesCount == 0))
1155  flags |= LB_TARGET_NET_ADDRESSES;
1156  else
1157  {
1158  for (UINT32 x = 0; x < redirection->TargetNetAddressesCount; x++)
1159  {
1160  if (!redirection->TargetNetAddresses[x])
1161  flags |= LB_TARGET_NET_ADDRESSES;
1162  }
1163  }
1164  }
1165 
1166  if (redirection->flags & LB_REDIRECTION_GUID)
1167  {
1168  if (!redirection->RedirectionGuid || (redirection->RedirectionGuidLength == 0))
1169  flags |= LB_REDIRECTION_GUID;
1170  }
1171 
1172  if (redirection->flags & LB_TARGET_CERTIFICATE)
1173  {
1174  if (!redirection->TargetCertificate)
1175  flags |= LB_TARGET_CERTIFICATE;
1176  }
1177 
1178  if (pFlags)
1179  *pFlags = flags;
1180  return flags == 0;
1181 }
1182 
1183 BOOL redirection_set_flags(rdpRedirection* redirection, UINT32 flags)
1184 {
1185  WINPR_ASSERT(redirection);
1186  redirection->flags = flags;
1187  return TRUE;
1188 }
1189 
1190 BOOL redirection_set_session_id(rdpRedirection* redirection, UINT32 session_id)
1191 {
1192  WINPR_ASSERT(redirection);
1193  redirection->sessionID = session_id;
1194  return TRUE;
1195 }
1196 
1197 static BOOL redirection_unsupported(const char* fkt, UINT32 flag, UINT32 mask)
1198 {
1199  char buffer[1024] = { 0 };
1200  char buffer2[1024] = { 0 };
1201  WLog_WARN(TAG, "[%s] supported flags are {%s}, have {%s}", fkt,
1202  rdp_redirection_flags_to_string(mask, buffer, sizeof(buffer)),
1203  rdp_redirection_flags_to_string(flag, buffer2, sizeof(buffer2)));
1204  return FALSE;
1205 }
1206 
1207 BOOL redirection_set_byte_option(rdpRedirection* redirection, UINT32 flag, const BYTE* data,
1208  size_t length)
1209 {
1210  WINPR_ASSERT(redirection);
1211  switch (flag)
1212  {
1213  case LB_CLIENT_TSV_URL:
1214  return redirection_copy_data(&redirection->TsvUrl, &redirection->TsvUrlLength, data,
1215  length);
1216  case LB_PASSWORD:
1217  return redirection_copy_data(&redirection->Password, &redirection->PasswordLength, data,
1218  length);
1219  case LB_LOAD_BALANCE_INFO:
1220  return redirection_copy_data(&redirection->LoadBalanceInfo,
1221  &redirection->LoadBalanceInfoLength, data, length);
1222  case LB_REDIRECTION_GUID:
1223  return redirection_copy_data(&redirection->RedirectionGuid,
1224  &redirection->RedirectionGuidLength, data, length);
1225  case LB_TARGET_CERTIFICATE:
1226  return rdp_redirection_read_target_cert(&redirection->TargetCertificate, data, length);
1227  default:
1228  return redirection_unsupported(__func__, flag,
1229  LB_CLIENT_TSV_URL | LB_PASSWORD | LB_LOAD_BALANCE_INFO |
1230  LB_REDIRECTION_GUID | LB_TARGET_CERTIFICATE);
1231  }
1232 }
1233 
1234 BOOL redirection_set_string_option(rdpRedirection* redirection, UINT32 flag, const char* str)
1235 {
1236  WINPR_ASSERT(redirection);
1237  switch (flag)
1238  {
1239  case LB_USERNAME:
1240  return redirection_copy_string(&redirection->Username, str);
1241  case LB_DOMAIN:
1242  return redirection_copy_string(&redirection->Domain, str);
1243  case LB_TARGET_FQDN:
1244  return redirection_copy_string(&redirection->TargetFQDN, str);
1245  case LB_TARGET_NETBIOS_NAME:
1246  return redirection_copy_string(&redirection->TargetNetBiosName, str);
1247  case LB_TARGET_NET_ADDRESS:
1248  return redirection_copy_string(&redirection->TargetNetAddress, str);
1249  default:
1250  return redirection_unsupported(__func__, flag,
1251  LB_USERNAME | LB_DOMAIN | LB_TARGET_FQDN |
1252  LB_TARGET_NETBIOS_NAME | LB_TARGET_NET_ADDRESS);
1253  }
1254 }
1255 
1256 BOOL redirection_set_array_option(rdpRedirection* redirection, UINT32 flag, const char** str,
1257  size_t count)
1258 {
1259  WINPR_ASSERT(redirection);
1260  switch (flag)
1261  {
1262  case LB_TARGET_NET_ADDRESSES:
1263  return redirection_copy_array(&redirection->TargetNetAddresses,
1264  &redirection->TargetNetAddressesCount, str, count);
1265  default:
1266  return redirection_unsupported(__func__, flag, LB_TARGET_NET_ADDRESSES);
1267  }
1268 }
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 const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
FREERDP_API BOOL freerdp_settings_set_pointer(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data)
Set a pointer to value data.
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_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.