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