FreeRDP
Loading...
Searching...
No Matches
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
39struct 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
62static 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((void*)*what);
72 }
73
74 *what = NULL;
75 *count = 0;
76}
77
78static void redirection_free_string(char** str)
79{
80 WINPR_ASSERT(str);
81 free(*str);
82 *str = NULL;
83}
84
85static 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
94static 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
104static 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
121static 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 = (char**)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
150static 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
168static 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
201static 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_EnsureRemainingCapacity(s, 4))
207 return FALSE;
208
209 Stream_Write_UINT32(s, (UINT32)length);
210
211 if (!Stream_EnsureRemainingCapacity(s, length))
212 return FALSE;
213
214 Stream_Write(s, data, length);
215 return TRUE;
216}
217
218static BOOL rdp_redirection_write_base64_wchar(WINPR_ATTR_UNUSED UINT32 flag, wStream* s,
219 size_t length, 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
238static 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;
291fail:
292 if (!rc)
293 WLog_ERR(TAG, "failed to read base64 data");
294 free(utf8);
295 return rc;
296}
297
298static 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
329static 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_EnsureRemainingCapacity(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_EnsureRemainingCapacity(s, length))
343 return FALSE;
344
345 Stream_Write(s, data, length);
346 return TRUE;
347}
348
349BOOL 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
401static 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
419fail:
420 free(der);
421 return rc;
422}
423
424static 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
437fail:
438 Stream_Free(serialized, TRUE);
439 return rc;
440}
441
442static 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
456BOOL rdp_set_target_certificate(rdpSettings* settings, const rdpCertificate* tcert)
457{
458 rdpCertificate* cert = freerdp_certificate_clone(tcert);
459 if (!freerdp_settings_set_pointer(settings, FreeRDP_RedirectionTargetCertificate, cert))
460 return FALSE;
461
462 BOOL pres = FALSE;
463 size_t length = 0;
464 char* pem = freerdp_certificate_get_pem(cert, &length);
465 if (pem && (length <= UINT32_MAX))
466 {
467 pres =
468 freerdp_settings_set_string_len(settings, FreeRDP_RedirectionAcceptedCert, pem, length);
469 if (pres)
470 pres = freerdp_settings_set_uint32(settings, FreeRDP_RedirectionAcceptedCertLength,
471 (UINT32)length);
472 }
473 free(pem);
474 return pres;
475}
476
477int rdp_redirection_apply_settings(rdpRdp* rdp)
478{
479 rdpSettings* settings = NULL;
480 rdpRedirection* redirection = NULL;
481
482 if (!rdp_reset_runtime_settings(rdp))
483 return -1;
484
485 settings = rdp->settings;
486 WINPR_ASSERT(settings);
487
488 redirection = rdp->redirection;
489 WINPR_ASSERT(redirection);
490
491 settings->RedirectionFlags = redirection->flags;
492 settings->RedirectedSessionId = redirection->sessionID;
493
494 if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESS)
495 {
496 if (!freerdp_settings_set_string(settings, FreeRDP_TargetNetAddress,
497 redirection->TargetNetAddress))
498 return -1;
499 }
500
501 if (settings->RedirectionFlags & LB_USERNAME)
502 {
503 if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionUsername,
504 redirection->Username))
505 return -1;
506 }
507
508 if (settings->RedirectionFlags & LB_DOMAIN)
509 {
510 if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionDomain, redirection->Domain))
511 return -1;
512 }
513
514 if (settings->RedirectionFlags & LB_PASSWORD)
515 {
516 /* Password may be a cookie without a null terminator */
517 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionPassword,
518 redirection->Password, redirection->PasswordLength))
519 return -1;
520 }
521
522 if (settings->RedirectionFlags & LB_DONTSTOREUSERNAME)
523 {
524 // TODO
525 }
526
527 if (settings->RedirectionFlags & LB_SMARTCARD_LOGON)
528 {
529 // TODO
530 }
531
532 if (settings->RedirectionFlags & LB_NOREDIRECT)
533 {
534 // TODO
535 }
536
537 if (settings->RedirectionFlags & LB_TARGET_FQDN)
538 {
539 if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetFQDN,
540 redirection->TargetFQDN))
541 return -1;
542 }
543
544 if (settings->RedirectionFlags & LB_TARGET_NETBIOS_NAME)
545 {
546 if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetNetBiosName,
547 redirection->TargetNetBiosName))
548 return -1;
549 }
550
551 if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESSES)
552 {
553 if (!freerdp_target_net_addresses_copy(settings, redirection->TargetNetAddresses,
554 redirection->TargetNetAddressesCount))
555 return -1;
556 }
557
558 if (settings->RedirectionFlags & LB_CLIENT_TSV_URL)
559 {
560 /* TsvUrl may not contain a null terminator */
561 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionTsvUrl,
562 redirection->TsvUrl, redirection->TsvUrlLength))
563 return -1;
564
565 const size_t lblen = freerdp_settings_get_uint32(settings, FreeRDP_LoadBalanceInfoLength);
566 const char* lb = freerdp_settings_get_pointer(settings, FreeRDP_LoadBalanceInfo);
567 if (lblen > 0)
568 {
569 BOOL valid = TRUE;
570 size_t tsvlen = 0;
571
572 char* tsv =
573 ConvertWCharNToUtf8Alloc((const WCHAR*)redirection->TsvUrl,
574 redirection->TsvUrlLength / sizeof(WCHAR), &tsvlen);
575 if (!tsv || !lb)
576 valid = FALSE;
577 else if (tsvlen != lblen)
578 valid = FALSE;
579 else if (memcmp(tsv, lb, lblen) != 0)
580 valid = FALSE;
581
582 if (!valid)
583 {
584 WLog_ERR(TAG,
585 "[redirection] Expected TsvUrl '%s' [%" PRIuz "], but got '%s' [%" PRIuz
586 "]",
587 lb, lblen, tsv, tsvlen);
588 }
589 free(tsv);
590
591 if (!valid)
592 return -2;
593 }
594 }
595
596 if (settings->RedirectionFlags & LB_SERVER_TSV_CAPABLE)
597 {
598 // TODO
599 }
600
601 if (settings->RedirectionFlags & LB_LOAD_BALANCE_INFO)
602 {
603 /* LoadBalanceInfo may not contain a null terminator */
604 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo,
605 redirection->LoadBalanceInfo,
606 redirection->LoadBalanceInfoLength))
607 return -1;
608 }
609 else
610 {
615 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, NULL, 0))
616 return -1;
617 }
618
619 if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
620 {
621 // TODO
622 }
623
624 if (settings->RedirectionFlags & LB_REDIRECTION_GUID)
625 {
626 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionGuid,
627 redirection->RedirectionGuid,
628 redirection->RedirectionGuidLength))
629 return -1;
630 }
631
632 if (settings->RedirectionFlags & LB_TARGET_CERTIFICATE)
633 {
634 if (!rdp_set_target_certificate(settings, redirection->TargetCertificate))
635 return -1;
636 }
637
638 return 0;
639}
640
641static BOOL rdp_redirection_read_data(UINT32 flag, wStream* s, UINT32* pLength, BYTE** pData)
642{
643 char buffer[64] = { 0 };
644 const BYTE* ptr = NULL;
645
646 if (!rdp_redirection_get_data(s, pLength, &ptr))
647 return FALSE;
648
649 redirection_free_data(pData, NULL);
650 *pData = (BYTE*)malloc(*pLength);
651
652 if (!*pData)
653 return FALSE;
654 memcpy(*pData, ptr, *pLength);
655
656 WLog_DBG(TAG, "%s:", rdp_redirection_flags_to_string(flag, buffer, sizeof(buffer)));
657
658 return TRUE;
659}
660
661static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
662{
663 char buffer[256] = { 0 };
664 UINT16 flags = 0;
665 UINT16 length = 0;
666 rdpRedirection* redirection = rdp->redirection;
667
668 if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
669 return STATE_RUN_FAILED;
670
671 Stream_Read_UINT16(s, flags); /* flags (2 bytes) */
672 if (flags != SEC_REDIRECTION_PKT)
673 {
674 char buffer1[1024] = { 0 };
675 char buffer2[1024] = { 0 };
676 WLog_ERR(TAG, "received invalid flags=%s, expected %s",
677 rdp_security_flag_string(flags, buffer1, sizeof(buffer1)),
678 rdp_security_flag_string(SEC_REDIRECTION_PKT, buffer2, sizeof(buffer2)));
679 return STATE_RUN_FAILED;
680 }
681 Stream_Read_UINT16(s, length); /* length (2 bytes) */
682 Stream_Read_UINT32(s, redirection->sessionID); /* sessionID (4 bytes) */
683 Stream_Read_UINT32(s, redirection->flags); /* redirFlags (4 bytes) */
684 WLog_INFO(TAG,
685 "flags: 0x%04" PRIX16 ", length: %" PRIu16 ", sessionID: 0x%08" PRIX32
686 ", redirFlags: %s [0x%08" PRIX32 "]",
687 flags, length, redirection->sessionID,
688 rdp_redirection_flags_to_string(redirection->flags, buffer, sizeof(buffer)),
689 redirection->flags);
690
691 /* Although MS-RDPBCGR does not mention any length constraints limits for the
692 * variable length null-terminated unicode strings in the RDP_SERVER_REDIRECTION_PACKET
693 * structure we will use the following limits in bytes including the null terminator:
694 *
695 * TargetNetAddress: 80 bytes
696 * UserName: 512 bytes
697 * Domain: 52 bytes
698 * Password(Cookie): 512 bytes
699 * TargetFQDN: 512 bytes
700 * TargetNetBiosName: 32 bytes
701 */
702
703 if (redirection->flags & LB_TARGET_NET_ADDRESS)
704 {
705 if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddress), 80))
706 return STATE_RUN_FAILED;
707 }
708
709 if (redirection->flags & LB_LOAD_BALANCE_INFO)
710 {
711 /* See [MSFT-SDLBTS] (a.k.a. TS_Session_Directory.doc)
712 * load balance info example data:
713 * 0000 43 6f 6f 6b 69 65 3a 20 6d 73 74 73 3d 32 31 33 Cookie: msts=213
714 * 0010 34 30 32 36 34 33 32 2e 31 35 36 32 39 2e 30 30 4026432.15629.00
715 * 0020 30 30 0d 0a 00..
716 */
717 if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
718 &redirection->LoadBalanceInfo))
719 return STATE_RUN_FAILED;
720 }
721
722 if (redirection->flags & LB_USERNAME)
723 {
724 if (!rdp_redirection_read_unicode_string(s, &(redirection->Username), 512))
725 return STATE_RUN_FAILED;
726
727 WLog_DBG(TAG, "Username: %s", redirection->Username);
728 }
729
730 if (redirection->flags & LB_DOMAIN)
731 {
732 if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52))
733 return STATE_RUN_FAILED;
734
735 WLog_DBG(TAG, "Domain: %s", redirection->Domain);
736 }
737
738 if (redirection->flags & LB_PASSWORD)
739 {
740 /* Note: Password is a variable-length array of bytes containing the
741 * password used by the user in Unicode format, including a null-terminator
742 * or (!) or a cookie value that MUST be passed to the target server on
743 * successful connection.
744 * Since the format of the password cookie (probably some salted hash) is
745 * currently unknown we'll treat it as opaque data. All cookies seen so far
746 * are 120 bytes including \0\0 termination.
747 * Here is an observed example of a redirection password cookie:
748 *
749 * 0000 02 00 00 80 44 53 48 4c 60 ab 69 2f 07 d6 9e 2d ....DSHL`.i/...-
750 * 0010 f0 3a 97 3b a9 c5 ec 7e 66 bd b3 84 6c b1 ef b9 .:.;...~f...l...
751 * 0020 b6 82 4e cc 3a df 64 b7 7b 25 04 54 c2 58 98 f8 ..N.:.d.{%.T.X..
752 * 0030 97 87 d4 93 c7 c1 e1 5b c2 85 f8 22 49 1f 81 88 .......[..."I...
753 * 0040 43 44 83 f6 9a 72 40 24 dc 4d 43 cb d9 92 3c 8f CD...r@$.MC...<.
754 * 0050 3a 37 5c 77 13 a0 72 3c 72 08 64 2a 29 fb dc eb :7\w..r<r.d*)...
755 * 0060 0d 2b 06 b4 c6 08 b4 73 34 16 93 62 6d 24 e9 93 .+.....s4..bm$..
756 * 0070 97 27 7b dd 9a 72 00 00 .'{..r..
757 *
758 * Notwithstanding the above, we'll allocated an additional zero WCHAR at the
759 * end of the buffer which won't get counted in PasswordLength.
760 */
761 if (!rdp_redirection_read_data(LB_PASSWORD, s, &redirection->PasswordLength,
762 &redirection->Password))
763 return STATE_RUN_FAILED;
764
765 /* [MS-RDPBCGR] specifies 512 bytes as the upper limit for the password length
766 * including the null terminatior(s). This should also be enough for the unknown
767 * password cookie format (see previous comment).
768 */
769 if ((redirection->flags & LB_PASSWORD_IS_PK_ENCRYPTED) == 0)
770 {
771 const size_t charLen = redirection->PasswordLength / sizeof(WCHAR);
772 if (redirection->PasswordLength > LB_PASSWORD_MAX_LENGTH)
773 {
774 WLog_ERR(TAG, "LB_PASSWORD: %" PRIuz " exceeds limit of %d", charLen,
775 LB_PASSWORD_MAX_LENGTH);
776 return STATE_RUN_FAILED;
777 }
778
779 /* Ensure the text password is '\0' terminated */
780 if (_wcsnlen((const WCHAR*)redirection->Password, charLen) == charLen)
781 {
782 WLog_ERR(TAG, "LB_PASSWORD: missing '\0' termination");
783 return STATE_RUN_FAILED;
784 }
785 }
786 }
787
788 if (redirection->flags & LB_TARGET_FQDN)
789 {
790 if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetFQDN), 512))
791 return STATE_RUN_FAILED;
792
793 WLog_DBG(TAG, "TargetFQDN: %s", redirection->TargetFQDN);
794 }
795
796 if (redirection->flags & LB_TARGET_NETBIOS_NAME)
797 {
798 if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetBiosName), 32))
799 return STATE_RUN_FAILED;
800
801 WLog_DBG(TAG, "TargetNetBiosName: %s", redirection->TargetNetBiosName);
802 }
803
804 if (redirection->flags & LB_CLIENT_TSV_URL)
805 {
806 if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
807 &redirection->TsvUrl))
808 return STATE_RUN_FAILED;
809 }
810
811 if (redirection->flags & LB_REDIRECTION_GUID)
812 {
813 if (!rdp_redirection_read_data(LB_REDIRECTION_GUID, s, &redirection->RedirectionGuidLength,
814 &redirection->RedirectionGuid))
815 return STATE_RUN_FAILED;
816 }
817
818 if (redirection->flags & LB_TARGET_CERTIFICATE)
819 {
820 if (!rdp_redirection_read_target_cert_stream(s, redirection))
821 return STATE_RUN_FAILED;
822 }
823
824 if (redirection->flags & LB_TARGET_NET_ADDRESSES)
825 {
826 UINT32 targetNetAddressesLength = 0;
827 UINT32 TargetNetAddressesCount = 0;
828
829 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
830 return STATE_RUN_FAILED;
831
832 Stream_Read_UINT32(s, targetNetAddressesLength);
833 Stream_Read_UINT32(s, TargetNetAddressesCount);
834
835 /* sanity check: the whole packet has a length limit of UINT16_MAX
836 * each TargetNetAddress is a WCHAR string, so minimum length 2 bytes
837 */
838 const size_t size = TargetNetAddressesCount * sizeof(WCHAR);
839 if ((size > Stream_GetRemainingLength(s)) || (size > targetNetAddressesLength))
840 {
841 WLog_ERR(TAG,
842 "Invalid RDP_SERVER_REDIRECTION_PACKET::TargetNetAddressLength %" PRIuz
843 ", sanity limit is %" PRIuz,
844 TargetNetAddressesCount * sizeof(WCHAR), Stream_GetRemainingLength(s));
845 return STATE_RUN_FAILED;
846 }
847
848 redirection_free_array(&redirection->TargetNetAddresses,
849 &redirection->TargetNetAddressesCount);
850 if (TargetNetAddressesCount > 0)
851 {
852 redirection->TargetNetAddresses =
853 (char**)calloc(TargetNetAddressesCount, sizeof(char*));
854
855 if (!redirection->TargetNetAddresses)
856 {
857 WLog_ERR(TAG, "TargetNetAddresses %" PRIu32 " failed to allocate",
858 TargetNetAddressesCount);
859 return STATE_RUN_FAILED;
860 }
861 }
862 redirection->TargetNetAddressesCount = TargetNetAddressesCount;
863
864 WLog_DBG(TAG, "TargetNetAddressesCount: %" PRIu32 "", TargetNetAddressesCount);
865
866 for (UINT32 i = 0; i < TargetNetAddressesCount; i++)
867 {
868 if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80))
869 return STATE_RUN_FAILED;
870
871 WLog_DBG(TAG, "TargetNetAddresses[%" PRIu32 "]: %s", i,
872 redirection->TargetNetAddresses[i]);
873 }
874 }
875
876 if (Stream_GetRemainingLength(s) >= 8)
877 {
878 /* some versions of windows don't included this padding before closing the connection */
879 Stream_Seek(s, 8); /* pad (8 bytes) */
880 }
881
882 if (redirection->flags & LB_NOREDIRECT)
883 return STATE_RUN_SUCCESS;
884
885 return STATE_RUN_REDIRECT;
886}
887
888state_run_t rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s)
889{
890 state_run_t status = STATE_RUN_SUCCESS;
891
892 if (!Stream_SafeSeek(s, 2)) /* pad2Octets (2 bytes) */
893 return STATE_RUN_FAILED;
894
895 status = rdp_recv_server_redirection_pdu(rdp, s);
896
897 if (state_run_failed(status))
898 return status;
899
900 if (Stream_GetRemainingLength(s) >= 1)
901 {
902 /* this field is optional, and its absence is not an error */
903 Stream_Seek(s, 1); /* pad2Octets (1 byte) */
904 }
905
906 return status;
907}
908
909rdpRedirection* redirection_new(void)
910{
911 rdpRedirection* redirection = (rdpRedirection*)calloc(1, sizeof(rdpRedirection));
912
913 if (redirection)
914 {
915 }
916
917 return redirection;
918}
919
920void redirection_free(rdpRedirection* redirection)
921{
922 if (redirection)
923 {
924 redirection_free_data(&redirection->TsvUrl, &redirection->TsvUrlLength);
925 redirection_free_string(&redirection->Username);
926 redirection_free_string(&redirection->Domain);
927 redirection_free_string(&redirection->TargetFQDN);
928 redirection_free_string(&redirection->TargetNetBiosName);
929 redirection_free_string(&redirection->TargetNetAddress);
930 redirection_free_data(&redirection->LoadBalanceInfo, &redirection->LoadBalanceInfoLength);
931 redirection_free_data(&redirection->Password, &redirection->PasswordLength);
932 redirection_free_data(&redirection->RedirectionGuid, &redirection->RedirectionGuidLength);
933 freerdp_certificate_free(redirection->TargetCertificate);
934 redirection_free_array(&redirection->TargetNetAddresses,
935 &redirection->TargetNetAddressesCount);
936
937 free(redirection);
938 }
939}
940
941static SSIZE_T redir_write_string(WINPR_ATTR_UNUSED UINT32 flag, wStream* s, const char* str)
942{
943 const size_t length = (strlen(str) + 1);
944 if (!Stream_EnsureRemainingCapacity(s, 4ull + length * sizeof(WCHAR)))
945 return -1;
946
947 const size_t pos = Stream_GetPosition(s);
948 Stream_Write_UINT32(s, (UINT32)length * sizeof(WCHAR));
949 if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
950 return -1;
951 return (SSIZE_T)(Stream_GetPosition(s) - pos);
952}
953
954static BOOL redir_write_data(WINPR_ATTR_UNUSED UINT32 flag, wStream* s, UINT32 length,
955 const BYTE* data)
956{
957 if (!Stream_EnsureRemainingCapacity(s, 4ull + length))
958 return FALSE;
959
960 Stream_Write_UINT32(s, length);
961 Stream_Write(s, data, length);
962 return TRUE;
963}
964
965BOOL rdp_write_enhanced_security_redirection_packet(wStream* s, const rdpRedirection* redirection)
966{
967 BOOL rc = FALSE;
968
969 WINPR_ASSERT(s);
970 WINPR_ASSERT(redirection);
971
972 if (!Stream_EnsureRemainingCapacity(s, 14))
973 goto fail;
974
975 Stream_Write_UINT16(s, 0);
976
977 const size_t start = Stream_GetPosition(s);
978 Stream_Write_UINT16(s, SEC_REDIRECTION_PKT);
979 const size_t lengthOffset = Stream_GetPosition(s);
980 Stream_Seek_UINT16(s); /* placeholder for length */
981
982 if (redirection->sessionID)
983 Stream_Write_UINT32(s, redirection->sessionID);
984 else
985 Stream_Write_UINT32(s, 0);
986
987 Stream_Write_UINT32(s, redirection->flags);
988
989 if (redirection->flags & LB_TARGET_NET_ADDRESS)
990 {
991 if (redir_write_string(LB_TARGET_NET_ADDRESS, s, redirection->TargetNetAddress) < 0)
992 goto fail;
993 }
994
995 if (redirection->flags & LB_LOAD_BALANCE_INFO)
996 {
997 const UINT32 length = 13 + redirection->LoadBalanceInfoLength + 2;
998 if (!Stream_EnsureRemainingCapacity(s, length))
999 goto fail;
1000 Stream_Write_UINT32(s, length);
1001 Stream_Write(s, "Cookie: msts=", 13);
1002 Stream_Write(s, redirection->LoadBalanceInfo, redirection->LoadBalanceInfoLength);
1003 Stream_Write_UINT8(s, 0x0d);
1004 Stream_Write_UINT8(s, 0x0a);
1005 }
1006
1007 if (redirection->flags & LB_USERNAME)
1008 {
1009 if (redir_write_string(LB_USERNAME, s, redirection->Username) < 0)
1010 goto fail;
1011 }
1012
1013 if (redirection->flags & LB_DOMAIN)
1014 {
1015 if (redir_write_string(LB_DOMAIN, s, redirection->Domain) < 0)
1016 goto fail;
1017 }
1018
1019 if (redirection->flags & LB_PASSWORD)
1020 {
1021 /* Password is either UNICODE or opaque data */
1022 if (!redir_write_data(LB_PASSWORD, s, redirection->PasswordLength, redirection->Password))
1023 goto fail;
1024 }
1025
1026 if (redirection->flags & LB_TARGET_FQDN)
1027 {
1028 if (redir_write_string(LB_TARGET_FQDN, s, redirection->TargetFQDN) < 0)
1029 goto fail;
1030 }
1031
1032 if (redirection->flags & LB_TARGET_NETBIOS_NAME)
1033 {
1034 if (redir_write_string(LB_TARGET_NETBIOS_NAME, s, redirection->TargetNetBiosName) < 0)
1035 goto fail;
1036 }
1037
1038 if (redirection->flags & LB_CLIENT_TSV_URL)
1039 {
1040 if (!redir_write_data(LB_CLIENT_TSV_URL, s, redirection->TsvUrlLength, redirection->TsvUrl))
1041 goto fail;
1042 }
1043
1044 if (redirection->flags & LB_REDIRECTION_GUID)
1045 {
1046 if (!redir_write_data(LB_REDIRECTION_GUID, s, redirection->RedirectionGuidLength,
1047 redirection->RedirectionGuid))
1048 goto fail;
1049 }
1050
1051 if (redirection->flags & LB_TARGET_CERTIFICATE)
1052 {
1053 if (!rdp_redireciton_write_target_cert_stream(s, redirection))
1054 goto fail;
1055 }
1056
1057 if (redirection->flags & LB_TARGET_NET_ADDRESSES)
1058 {
1059 UINT32 length = sizeof(UINT32);
1060
1061 if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT32)))
1062 goto fail;
1063
1064 const size_t lstart = Stream_GetPosition(s);
1065 Stream_Seek_UINT32(s); /* length of field */
1066 Stream_Write_UINT32(s, redirection->TargetNetAddressesCount);
1067 for (UINT32 i = 0; i < redirection->TargetNetAddressesCount; i++)
1068 {
1069 const SSIZE_T rcc =
1070 redir_write_string(LB_TARGET_NET_ADDRESSES, s, redirection->TargetNetAddresses[i]);
1071 if (rcc < 0)
1072 goto fail;
1073 length += (UINT32)rcc;
1074 }
1075
1076 /* Write length field */
1077 const size_t lend = Stream_GetPosition(s);
1078 Stream_SetPosition(s, lstart);
1079 Stream_Write_UINT32(s, length);
1080 Stream_SetPosition(s, lend);
1081 }
1082
1083 /* Padding 8 bytes */
1084 if (!Stream_EnsureRemainingCapacity(s, 8))
1085 goto fail;
1086 Stream_Zero(s, 8);
1087
1088 const size_t end = Stream_GetPosition(s);
1089 Stream_SetPosition(s, lengthOffset);
1090 Stream_Write_UINT16(s, (UINT16)(end - start));
1091 Stream_SetPosition(s, end);
1092
1093 rc = TRUE;
1094fail:
1095 return rc;
1096}
1097
1098BOOL redirection_settings_are_valid(rdpRedirection* redirection, UINT32* pFlags)
1099{
1100 UINT32 flags = 0;
1101
1102 WINPR_ASSERT(redirection);
1103
1104 if (redirection->flags & LB_CLIENT_TSV_URL)
1105 {
1106 if (!redirection->TsvUrl || (redirection->TsvUrlLength == 0))
1107 flags |= LB_CLIENT_TSV_URL;
1108 }
1109
1110 if (redirection->flags & LB_SERVER_TSV_CAPABLE)
1111 {
1112 if ((redirection->flags & LB_CLIENT_TSV_URL) == 0)
1113 flags |= LB_SERVER_TSV_CAPABLE;
1114 }
1115
1116 if (redirection->flags & LB_USERNAME)
1117 {
1118 if (utils_str_is_empty(redirection->Username))
1119 flags |= LB_USERNAME;
1120 }
1121
1122 if (redirection->flags & LB_DOMAIN)
1123 {
1124 if (utils_str_is_empty(redirection->Domain))
1125 flags |= LB_DOMAIN;
1126 }
1127
1128 if (redirection->flags & LB_PASSWORD)
1129 {
1130 if (!redirection->Password || (redirection->PasswordLength == 0))
1131 flags |= LB_PASSWORD;
1132 }
1133
1134 if (redirection->flags & LB_TARGET_FQDN)
1135 {
1136 if (utils_str_is_empty(redirection->TargetFQDN))
1137 flags |= LB_TARGET_FQDN;
1138 }
1139
1140 if (redirection->flags & LB_LOAD_BALANCE_INFO)
1141 {
1142 if (!redirection->LoadBalanceInfo || (redirection->LoadBalanceInfoLength == 0))
1143 flags |= LB_LOAD_BALANCE_INFO;
1144 }
1145
1146 if (redirection->flags & LB_TARGET_NETBIOS_NAME)
1147 {
1148 if (utils_str_is_empty(redirection->TargetNetBiosName))
1149 flags |= LB_TARGET_NETBIOS_NAME;
1150 }
1151
1152 if (redirection->flags & LB_TARGET_NET_ADDRESS)
1153 {
1154 if (utils_str_is_empty(redirection->TargetNetAddress))
1155 flags |= LB_TARGET_NET_ADDRESS;
1156 }
1157
1158 if (redirection->flags & LB_TARGET_NET_ADDRESSES)
1159 {
1160 if (!redirection->TargetNetAddresses || (redirection->TargetNetAddressesCount == 0))
1161 flags |= LB_TARGET_NET_ADDRESSES;
1162 else
1163 {
1164 for (UINT32 x = 0; x < redirection->TargetNetAddressesCount; x++)
1165 {
1166 if (!redirection->TargetNetAddresses[x])
1167 flags |= LB_TARGET_NET_ADDRESSES;
1168 }
1169 }
1170 }
1171
1172 if (redirection->flags & LB_REDIRECTION_GUID)
1173 {
1174 if (!redirection->RedirectionGuid || (redirection->RedirectionGuidLength == 0))
1175 flags |= LB_REDIRECTION_GUID;
1176 }
1177
1178 if (redirection->flags & LB_TARGET_CERTIFICATE)
1179 {
1180 if (!redirection->TargetCertificate)
1181 flags |= LB_TARGET_CERTIFICATE;
1182 }
1183
1184 if (pFlags)
1185 *pFlags = flags;
1186 return flags == 0;
1187}
1188
1189BOOL redirection_set_flags(rdpRedirection* redirection, UINT32 flags)
1190{
1191 WINPR_ASSERT(redirection);
1192 redirection->flags = flags;
1193 return TRUE;
1194}
1195
1196BOOL redirection_set_session_id(rdpRedirection* redirection, UINT32 session_id)
1197{
1198 WINPR_ASSERT(redirection);
1199 redirection->sessionID = session_id;
1200 return TRUE;
1201}
1202
1203static BOOL redirection_unsupported(const char* fkt, UINT32 flag, UINT32 mask)
1204{
1205 char buffer[1024] = { 0 };
1206 char buffer2[1024] = { 0 };
1207 WLog_WARN(TAG, "[%s] supported flags are {%s}, have {%s}", fkt,
1208 rdp_redirection_flags_to_string(mask, buffer, sizeof(buffer)),
1209 rdp_redirection_flags_to_string(flag, buffer2, sizeof(buffer2)));
1210 return FALSE;
1211}
1212
1213BOOL redirection_set_byte_option(rdpRedirection* redirection, UINT32 flag, const BYTE* data,
1214 size_t length)
1215{
1216 WINPR_ASSERT(redirection);
1217 switch (flag)
1218 {
1219 case LB_CLIENT_TSV_URL:
1220 return redirection_copy_data(&redirection->TsvUrl, &redirection->TsvUrlLength, data,
1221 length);
1222 case LB_PASSWORD:
1223 return redirection_copy_data(&redirection->Password, &redirection->PasswordLength, data,
1224 length);
1225 case LB_LOAD_BALANCE_INFO:
1226 return redirection_copy_data(&redirection->LoadBalanceInfo,
1227 &redirection->LoadBalanceInfoLength, data, length);
1228 case LB_REDIRECTION_GUID:
1229 return redirection_copy_data(&redirection->RedirectionGuid,
1230 &redirection->RedirectionGuidLength, data, length);
1231 case LB_TARGET_CERTIFICATE:
1232 return rdp_redirection_read_target_cert(&redirection->TargetCertificate, data, length);
1233 default:
1234 return redirection_unsupported(__func__, flag,
1235 LB_CLIENT_TSV_URL | LB_PASSWORD | LB_LOAD_BALANCE_INFO |
1236 LB_REDIRECTION_GUID | LB_TARGET_CERTIFICATE);
1237 }
1238}
1239
1240BOOL redirection_set_string_option(rdpRedirection* redirection, UINT32 flag, const char* str)
1241{
1242 WINPR_ASSERT(redirection);
1243 switch (flag)
1244 {
1245 case LB_USERNAME:
1246 return redirection_copy_string(&redirection->Username, str);
1247 case LB_DOMAIN:
1248 return redirection_copy_string(&redirection->Domain, str);
1249 case LB_TARGET_FQDN:
1250 return redirection_copy_string(&redirection->TargetFQDN, str);
1251 case LB_TARGET_NETBIOS_NAME:
1252 return redirection_copy_string(&redirection->TargetNetBiosName, str);
1253 case LB_TARGET_NET_ADDRESS:
1254 return redirection_copy_string(&redirection->TargetNetAddress, str);
1255 default:
1256 return redirection_unsupported(__func__, flag,
1257 LB_USERNAME | LB_DOMAIN | LB_TARGET_FQDN |
1258 LB_TARGET_NETBIOS_NAME | LB_TARGET_NET_ADDRESS);
1259 }
1260}
1261
1262BOOL redirection_set_array_option(rdpRedirection* redirection, UINT32 flag, const char** str,
1263 size_t count)
1264{
1265 WINPR_ASSERT(redirection);
1266 switch (flag)
1267 {
1268 case LB_TARGET_NET_ADDRESSES:
1269 return redirection_copy_array(&redirection->TargetNetAddresses,
1270 &redirection->TargetNetAddressesCount, str, count);
1271 default:
1272 return redirection_unsupported(__func__, flag, LB_TARGET_NET_ADDRESSES);
1273 }
1274}
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_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 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_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.