FreeRDP
Loading...
Searching...
No Matches
assistance.c
1
20#include <freerdp/config.h>
21
22#include <errno.h>
23
24#include <winpr/wtypes.h>
25#include <winpr/collections.h>
26#include <winpr/string.h>
27#include <winpr/crt.h>
28#include <winpr/crypto.h>
29#include <winpr/print.h>
30#include <winpr/windows.h>
31#include <winpr/ssl.h>
32#include <winpr/file.h>
33
34#include <freerdp/log.h>
35#include <freerdp/client/file.h>
36#include <freerdp/client/cmdline.h>
37
38#include <freerdp/assistance.h>
39
40#include "../core/settings.h"
41
42#define TAG FREERDP_TAG("common")
43
44struct rdp_assistance_file
45{
46 UINT32 Type;
47
48 char* Username;
49 char* LHTicket;
50 char* RCTicket;
51 char* PassStub;
52 UINT32 DtStart;
53 UINT32 DtLength;
54 BOOL LowSpeed;
55 BOOL RCTicketEncrypted;
56
57 char* ConnectionString1;
58 char* ConnectionString2;
59
60 BYTE* EncryptedPassStub;
61 size_t EncryptedPassStubLength;
62
63 BYTE* EncryptedLHTicket;
64 size_t EncryptedLHTicketLength;
65
66 wArrayList* MachineAddresses;
67 wArrayList* MachinePorts;
68 wArrayList* MachineUris;
69
70 char* RASessionId;
71 char* RASpecificParams;
72 char* RASpecificParams2;
73
74 char* filename;
75 char* password;
76};
77
78static const char* strrstr(const char* haystack, size_t len, const char* needle)
79{
80 if (*needle == '\0')
81 return haystack;
82
83 char* result = nullptr;
84 for (;;)
85 {
86 char* p = strstr(haystack, needle);
87 if (p == nullptr)
88 break;
89 if (p > haystack + len)
90 return nullptr;
91
92 result = p;
93 haystack = p + 1;
94 }
95
96 return result;
97}
98
99static BOOL update_option(char** opt, const char* val, size_t len)
100{
101 WINPR_ASSERT(opt);
102 free(*opt);
103 *opt = nullptr;
104
105 if (!val && (len != 0))
106 return FALSE;
107 else if (!val && (len == 0))
108 return TRUE;
109 *opt = strndup(val, len);
110 return *opt != nullptr;
111}
112
113static BOOL update_name(rdpAssistanceFile* file, const char* name)
114{
115 WINPR_ASSERT(file);
116
117 if (!name)
118 {
119 WLog_ERR(TAG, "ASSISTANCE file %s invalid name", name);
120 return FALSE;
121 }
122
123 free(file->filename);
124 file->filename = _strdup(name);
125 return file->filename != nullptr;
126}
127
128static BOOL update_password(rdpAssistanceFile* file, const char* password)
129{
130 WINPR_ASSERT(file);
131 free(file->password);
132 file->password = nullptr;
133 if (!password)
134 return TRUE;
135 file->password = _strdup(password);
136 return file->password != nullptr;
137}
138
139static BOOL update_connectionstring2_nocopy(rdpAssistanceFile* file, char* str)
140{
141 WINPR_ASSERT(file);
142 free(file->ConnectionString2);
143 file->ConnectionString2 = nullptr;
144 if (!str)
145 return TRUE;
146 file->ConnectionString2 = str;
147 return file->ConnectionString2 != nullptr;
148}
149
150static BOOL update_connectionstring2(rdpAssistanceFile* file, const char* str, size_t len)
151{
152 char* strc = nullptr;
153 if (!str && (len != 0))
154 return FALSE;
155
156 if (str && (len > 0))
157 {
158 strc = strndup(str, len);
159 if (!strc)
160 return FALSE;
161 }
162 return update_connectionstring2_nocopy(file, strc);
163}
164
165static BOOL update_connectionstring2_wchar(rdpAssistanceFile* file, const WCHAR* str, size_t len)
166{
167 char* strc = nullptr;
168
169 if (!str && (len != 0))
170 return FALSE;
171
172 if (str && (len > 0))
173 {
174 strc = ConvertWCharNToUtf8Alloc(str, len, nullptr);
175 if (!strc)
176 return FALSE;
177 }
178 return update_connectionstring2_nocopy(file, strc);
179}
180
217static BOOL freerdp_assistance_crypt_derive_key_sha1(const BYTE* hash, size_t hashLength, BYTE* key,
218 size_t keyLength)
219{
220 BOOL rc = FALSE;
221 BYTE pad1[64] = WINPR_C_ARRAY_INIT;
222 BYTE pad2[64] = WINPR_C_ARRAY_INIT;
223
224 if (hashLength == 0)
225 return FALSE;
226
227 memset(pad1, 0x36, sizeof(pad1));
228 memset(pad2, 0x5C, sizeof(pad2));
229
230 for (size_t i = 0; i < hashLength; i++)
231 {
232 pad1[i] ^= hash[i];
233 pad2[i] ^= hash[i];
234 }
235
236 BYTE* buffer = (BYTE*)calloc(hashLength, 2);
237
238 if (!buffer)
239 goto fail;
240
241 if (!winpr_Digest(WINPR_MD_SHA1, pad1, 64, buffer, hashLength))
242 goto fail;
243
244 if (!winpr_Digest(WINPR_MD_SHA1, pad2, 64, &buffer[hashLength], hashLength))
245 goto fail;
246
247 CopyMemory(key, buffer, keyLength);
248 rc = TRUE;
249fail:
250 free(buffer);
251 return rc;
252}
253
254static BOOL append_address_to_list(wArrayList* MachineAddresses, const char* str, size_t len)
255{
256 char* copy = nullptr;
257 if (len > 0)
258 copy = strndup(str, len);
259 if (!copy)
260 return FALSE;
261
262 const BOOL rc = ArrayList_Append(MachineAddresses, copy);
263 if (!rc)
264 free(copy);
265 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of copy
266 return rc;
267}
268
269static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port)
270{
271 WINPR_ASSERT(file);
272
273 errno = 0;
274 unsigned long p = strtoul(port, nullptr, 0);
275
276 if ((errno != 0) || (p == 0) || (p > UINT16_MAX))
277 {
278 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid port value %s",
279 port);
280 return FALSE;
281 }
282
283 if (!append_address_to_list(file->MachineAddresses, host, host ? strlen(host) : 0))
284 return FALSE;
285 return ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p);
286}
287
288static BOOL freerdp_assistance_parse_address_list(rdpAssistanceFile* file, char* list)
289{
290 WINPR_ASSERT(file);
291
292 WLog_DBG(TAG, "freerdp_assistance_parse_address_list list=%s", list);
293
294 BOOL rc = FALSE;
295
296 if (!list)
297 return FALSE;
298
299 char* strp = list;
300 char* s = ";";
301
302 // get the first token
303 char* saveptr = nullptr;
304 char* token = strtok_s(strp, s, &saveptr);
305
306 // walk through other tokens
307 while (token != nullptr)
308 {
309 char* port = strchr(token, ':');
310 if (!port)
311 goto out;
312 *port = '\0';
313 port++;
314
315 if (!append_address(file, token, port))
316 goto out;
317
318 token = strtok_s(nullptr, s, &saveptr);
319 }
320 rc = TRUE;
321out:
322 return rc;
323}
324
325static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file)
326{
327 char* tokens[8] = WINPR_C_ARRAY_INIT;
328 BOOL rc = FALSE;
329
330 WINPR_ASSERT(file);
331
332 if (!file->RCTicket)
333 return FALSE;
334
339 char* str = _strdup(file->RCTicket);
340
341 if (!str)
342 goto error;
343
344 {
345 const size_t length = strlen(str);
346
347 {
348 int count = 1;
349 for (size_t i = 0; i < length; i++)
350 {
351 if (str[i] == ',')
352 count++;
353 }
354
355 if (count != 8)
356 goto error;
357 }
358
359 {
360 size_t count = 0;
361 tokens[count++] = str;
362
363 for (size_t i = 0; i < length; i++)
364 {
365 if (str[i] == ',')
366 {
367 str[i] = '\0';
368 tokens[count++] = &str[i + 1];
369 }
370 }
371 }
372 }
373
374 if (strcmp(tokens[0], "65538") != 0)
375 goto error;
376
377 if (strcmp(tokens[1], "1") != 0)
378 goto error;
379
380 if (strcmp(tokens[3], "*") != 0)
381 goto error;
382
383 if (strcmp(tokens[5], "*") != 0)
384 goto error;
385
386 if (strcmp(tokens[6], "*") != 0)
387 goto error;
388
389 file->RASessionId = _strdup(tokens[4]);
390
391 if (!file->RASessionId)
392 goto error;
393
394 file->RASpecificParams = _strdup(tokens[7]);
395
396 if (!file->RASpecificParams)
397 goto error;
398
399 if (!freerdp_assistance_parse_address_list(file, tokens[2]))
400 goto error;
401
402 rc = TRUE;
403error:
404 free(str);
405 return rc;
406}
407
421static BOOL freerdp_assistance_parse_attr(const char** opt, size_t* plength, const char* key,
422 const char* tag)
423{
424 WINPR_ASSERT(opt);
425 WINPR_ASSERT(plength);
426 WINPR_ASSERT(key);
427
428 *opt = nullptr;
429 *plength = 0;
430 if (!tag)
431 return FALSE;
432
433 char bkey[128] = WINPR_C_ARRAY_INIT;
434 const int rc = _snprintf(bkey, sizeof(bkey), "%s=\"", key);
435 WINPR_ASSERT(rc > 0);
436 WINPR_ASSERT((size_t)rc < sizeof(bkey));
437 if ((rc <= 0) || ((size_t)rc >= sizeof(bkey)))
438 return FALSE;
439
440 char* p = strstr(tag, bkey);
441 if (!p)
442 return TRUE;
443
444 p += strlen(bkey);
445 char* q = strchr(p, '"');
446
447 if (!q)
448 {
449 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field '%s=%s'",
450 key, p);
451 return FALSE;
452 }
453
454 if (p > q)
455 {
456 WLog_ERR(TAG,
457 "Failed to parse ASSISTANCE file: ConnectionString2 invalid field "
458 "order for '%s'",
459 key);
460 return FALSE;
461 }
462 const size_t length = WINPR_ASSERTING_INT_CAST(size_t, q - p);
463 *opt = p;
464 *plength = length;
465
466 return TRUE;
467}
468
469static BOOL freerdp_assistance_parse_attr_str(char** opt, const char* key, const char* tag)
470{
471 const char* copt = nullptr;
472 size_t size = 0;
473 if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
474 return FALSE;
475 return update_option(opt, copt, size);
476}
477
478static BOOL freerdp_assistance_parse_attr_bool(BOOL* opt, const char* key, const char* tag)
479{
480 const char* copt = nullptr;
481 size_t size = 0;
482
483 WINPR_ASSERT(opt);
484 *opt = FALSE;
485
486 if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
487 return FALSE;
488 if (size != 1)
489 return TRUE;
490
491 *opt = (copt[0] == '1');
492 return TRUE;
493}
494
495static BOOL freerdp_assistance_parse_attr_uint32(UINT32* opt, const char* key, const char* tag)
496{
497 const char* copt = nullptr;
498 size_t size = 0;
499
500 WINPR_ASSERT(opt);
501 *opt = 0;
502
503 if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
504 return FALSE;
505
506 char buffer[64] = WINPR_C_ARRAY_INIT;
507 if ((!copt && (size > 0)) || (size >= sizeof(buffer)))
508 {
509 WLog_WARN(TAG, "Invalid UINT32 string '%s' [%" PRIuz "]", copt, size);
510 return FALSE;
511 }
512
513 if (size > 0)
514 strncpy(buffer, copt, size);
515
516 errno = 0;
517 unsigned long val = strtoul(buffer, nullptr, 0);
518
519 if ((errno != 0) || (val > UINT32_MAX))
520 {
521 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid value %s", buffer);
522 return FALSE;
523 }
524
525 *opt = (UINT32)val;
526
527 return TRUE;
528}
529
530static char* freerdp_assistance_contains_element(char* input, size_t ilen, const char* key,
531 size_t* plen, char** pdata, size_t* pdlen)
532{
533 WINPR_ASSERT(input);
534 WINPR_ASSERT(key);
535 WINPR_ASSERT(plen);
536
537 char bkey[128] = WINPR_C_ARRAY_INIT;
538 const int rc = _snprintf(bkey, sizeof(bkey), "<%s", key);
539 WINPR_ASSERT(rc > 0);
540 WINPR_ASSERT((size_t)rc < sizeof(bkey));
541 if ((rc < 0) || ((size_t)rc >= sizeof(bkey)))
542 return nullptr;
543
544 char* tag = winpr_strnstr(input, bkey, ilen);
545 if (!tag)
546 return nullptr;
547
548 const intptr_t tdiff = (tag - input);
549 WINPR_ASSERT(tdiff >= 0);
550 const size_t utdiff = WINPR_ASSERTING_INT_CAST(size_t, tdiff);
551 WINPR_ASSERT(utdiff <= ilen);
552 const size_t tlen = ilen - utdiff;
553 const size_t texpect = strnlen(bkey, sizeof(bkey));
554 if (tlen <= texpect)
555 return nullptr;
556
557 char* data = tag + texpect;
558
559 /* Ensure there is a valid delimiter following our token */
560 switch (data[0])
561 {
562 case '>':
563 case '/':
564 case ' ':
565 case '\t':
566 break;
567 default:
568 WLog_ERR(TAG,
569 "Failed to parse ASSISTANCE file: ConnectionString2 missing delimiter after "
570 "field %s",
571 bkey);
572 return nullptr;
573 }
574
575 const char* start = winpr_strnstr(tag, ">", tlen);
576
577 if (!start || (start > input + ilen))
578 {
579 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field %s", bkey);
580 return nullptr;
581 }
582
583 const char* end = start;
584 const char* dend = start - 1;
585 if (*dend != '/')
586 {
587 char ekey[128] = WINPR_C_ARRAY_INIT;
588 const int erc = _snprintf(ekey, sizeof(ekey), "</%s>", key);
589 WINPR_ASSERT(erc > 0);
590 WINPR_ASSERT((size_t)erc < sizeof(ekey));
591 if ((erc <= 0) || ((size_t)erc >= sizeof(ekey)))
592 return nullptr;
593 const size_t offset = WINPR_ASSERTING_INT_CAST(size_t, start - tag);
594 dend = end = strrstr(start, ilen - offset, ekey);
595 if (end)
596 end += strnlen(ekey, sizeof(ekey));
597 }
598
599 if (!end)
600 {
601 WLog_ERR(TAG,
602 "Failed to parse ASSISTANCE file: ConnectionString2 missing end tag for field %s",
603 key);
604 return nullptr;
605 }
606 if (plen)
607 *plen = WINPR_ASSERTING_INT_CAST(size_t, end - tag);
608
609 if (pdata)
610 *pdata = data;
611 if (pdlen)
612 *pdlen = WINPR_ASSERTING_INT_CAST(size_t, dend - data);
613 return tag;
614}
615
621static BOOL freerdp_assistance_consume_input_and_get_element(char* input, size_t ilen,
622 const char* key, char** element,
623 size_t* elen)
624{
625 WINPR_ASSERT(input);
626 WINPR_ASSERT(key);
627 WINPR_ASSERT(element);
628 WINPR_ASSERT(elen);
629
630 size_t len = 0;
631 size_t dlen = 0;
632 char* data = nullptr;
633 char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen);
634 if (!tag)
635 return FALSE;
636
637 char* end = data + dlen;
638 *tag = '\0';
639 *end = '\0';
640 *element = data;
641 *elen = dlen + 1;
642 return TRUE;
643}
644
645static BOOL freerdp_assistance_get_element(char* input, size_t ilen, const char* key,
646 char** element, size_t* elen)
647{
648 WINPR_ASSERT(input);
649 WINPR_ASSERT(key);
650 WINPR_ASSERT(element);
651 WINPR_ASSERT(elen);
652
653 size_t len = 0;
654 size_t dlen = 0;
655 char* data = nullptr;
656 char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen);
657 if (!tag)
658 return FALSE;
659
660 if (tag + len > input + ilen)
661 return FALSE;
662
663 char* end = tag + len;
664 *element = data;
665 *elen = WINPR_ASSERTING_INT_CAST(size_t, end - data + 1);
666 return TRUE;
667}
668
669static BOOL freerdp_assistance_parse_all_elements_of(rdpAssistanceFile* file, char* data,
670 size_t len, const char* key,
671 BOOL (*fkt)(rdpAssistanceFile* file,
672 char* data, size_t len))
673{
674 char* val = nullptr;
675 size_t vlen = 0;
676
677 while (freerdp_assistance_get_element(data, len, key, &val, &vlen))
678 {
679 data = val + vlen;
680 len = strnlen(data, len);
681 if (vlen > 0)
682 {
683 val[vlen - 1] = '\0';
684
685 if (!fkt(file, val, vlen))
686 return FALSE;
687 }
688 }
689
690 return TRUE;
691}
692
693static BOOL freerdp_assistance_parse_all_elements_of_l(rdpAssistanceFile* file, char* data,
694 WINPR_ATTR_UNUSED size_t len)
695{
696 UINT32 p = 0;
697 const char* n = nullptr;
698 const char* u = nullptr;
699 size_t nlen = 0;
700 size_t ulen = 0;
701 if (!freerdp_assistance_parse_attr_uint32(&p, "P", data))
702 return FALSE;
703 if (!freerdp_assistance_parse_attr(&n, &nlen, "N", data))
704 return FALSE;
705 if (!freerdp_assistance_parse_attr(&u, &ulen, "U", data))
706 return FALSE;
707
708 if (n && (nlen > 0))
709 {
710 if (!append_address_to_list(file->MachineAddresses, n, nlen))
711 return FALSE;
712 if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
713 return FALSE;
714 }
715 if (u && (ulen > 0))
716 {
717 if (!append_address_to_list(file->MachineAddresses, u, ulen))
718 return FALSE;
719 if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
720 return FALSE;
721 }
722 return TRUE;
723}
724
725static BOOL freerdp_assistance_parse_all_elements_of_t(rdpAssistanceFile* file, char* data,
726 size_t len)
727{
728 UINT32 id = 0;
729 UINT32 sid = 0;
730 if (!freerdp_assistance_parse_attr_uint32(&id, "ID", data))
731 return FALSE;
732 if (!freerdp_assistance_parse_attr_uint32(&sid, "SID", data))
733 return FALSE;
734 WLog_DBG(TAG, "transport id=%" PRIu32 ", sid=%" PRIu32, id, sid);
735 return freerdp_assistance_parse_all_elements_of(file, data, len, "L",
736 freerdp_assistance_parse_all_elements_of_l);
737}
738
739static BOOL freerdp_assistance_parse_all_elements_of_c(rdpAssistanceFile* file, char* data,
740 size_t len)
741{
742 return freerdp_assistance_parse_all_elements_of(file, data, len, "T",
743 freerdp_assistance_parse_all_elements_of_t);
744}
745
746static BOOL freerdp_assistance_parse_find_elements_of_c(rdpAssistanceFile* file, char* data,
747 size_t len)
748{
749 return freerdp_assistance_parse_all_elements_of(file, data, len, "C",
750 freerdp_assistance_parse_all_elements_of_c);
751}
752
753static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
754{
755 BOOL rc = FALSE;
756
757 WINPR_ASSERT(file);
758
759 if (!file->ConnectionString2)
760 return FALSE;
761
762 char* str = _strdup(file->ConnectionString2);
763 if (!str)
764 goto out_fail;
765 const size_t slen = strlen(str);
766
767 {
768 char* e = nullptr;
769 size_t elen = 0;
770 if (!freerdp_assistance_consume_input_and_get_element(str, slen, "E", &e, &elen))
771 goto out_fail;
772
773 if (!e || (elen == 0))
774 goto out_fail;
775 {
776 char* a = nullptr;
777 size_t alen = 0;
778 if (!freerdp_assistance_get_element(e, elen, "A", &a, &alen))
779 goto out_fail;
780
781 if (!a || (alen == 0))
782 goto out_fail;
783
784 if (!freerdp_assistance_parse_find_elements_of_c(file, e, elen))
785 goto out_fail;
786
787 /* '\0' terminate the detected XML elements so
788 * the parser can continue with terminated strings
789 */
790 a[alen] = '\0';
791
792 if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams, "KH", a))
793 goto out_fail;
794
795 if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams2, "KH2", a))
796 goto out_fail;
797
798 if (!freerdp_assistance_parse_attr_str(&file->RASessionId, "ID", a))
799 goto out_fail;
800 }
801 }
802 rc = TRUE;
803out_fail:
804 free(str);
805 return rc;
806}
807
808char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
809{
810 if (!name || !pass)
811 return nullptr;
812
813 const size_t nameLength = strlen(name) + strlen("NAME=");
814 const size_t passLength = strlen(pass) + strlen("PASS=");
815 const size_t size = nameLength + passLength + 64;
816 char* ExpertBlob = (char*)calloc(1, size);
817
818 if (!ExpertBlob)
819 return nullptr;
820
821 (void)sprintf_s(ExpertBlob, size, "%" PRIuz ";NAME=%s%" PRIuz ";PASS=%s", nameLength, name,
822 passLength, pass);
823 return ExpertBlob;
824}
825
826char* freerdp_assistance_generate_pass_stub(WINPR_ATTR_UNUSED DWORD flags)
827{
828 UINT32 nums[14] = WINPR_C_ARRAY_INIT;
829 const char set1[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
830 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
831 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
832 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
833 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '_' };
834 const char set2[12] = { '!', '@', '#', '$', '&', '^', '*', '(', ')', '-', '+', '=' };
835 const char set3[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
836 const char set4[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
837 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
838 const char set5[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
839 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
840 char* passStub = calloc(15, sizeof(char));
841
842 if (!passStub)
843 return nullptr;
844
856 if (winpr_RAND(nums, sizeof(nums)) < 0)
857 {
858 free(passStub);
859 return nullptr;
860 }
861
862 passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */
863 passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */
864 passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */
865 passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */
866 passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */
867
868 for (size_t x = 5; x < ARRAYSIZE(nums); x++)
869 passStub[x] = set1[nums[x] % sizeof(set1)]; /* character 5 - 13 */
870 return passStub;
871}
872
873BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
874 size_t* pEncryptedSize)
875{
876 BOOL rc = 0;
877 size_t cbPasswordW = 0;
878 size_t cbPassStubW = 0;
879 BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
880 WINPR_RC4_CTX* rc4Ctx = nullptr;
881 BYTE* pbIn = nullptr;
882 BYTE* pbOut = nullptr;
883 BYTE* res = nullptr;
884 WCHAR* PasswordW = ConvertUtf8ToWCharAlloc(password, &cbPasswordW);
885 WCHAR* PassStubW = ConvertUtf8ToWCharAlloc(passStub, &cbPassStubW);
886
887 cbPasswordW = cbPasswordW * sizeof(WCHAR);
888 cbPassStubW = cbPassStubW * sizeof(WCHAR);
889 const size_t EncryptedSize = cbPassStubW + 4;
890
891 if (!PasswordW || !PassStubW)
892 goto fail;
893
894 if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*)PasswordHash,
895 sizeof(PasswordHash)))
896 goto fail;
897
898 pbIn = (BYTE*)calloc(1, EncryptedSize);
899 pbOut = (BYTE*)calloc(1, EncryptedSize);
900
901 if (!pbIn || !pbOut)
902 goto fail;
903
904 WINPR_ASSERT(cbPasswordW <= UINT32_MAX);
905 winpr_Data_Write_UINT32(pbIn, (UINT32)cbPassStubW);
906 CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
907 rc4Ctx = winpr_RC4_New(PasswordHash, sizeof(PasswordHash));
908
909 if (!rc4Ctx)
910 {
911 WLog_ERR(TAG, "winpr_Cipher_New failure");
912 goto fail;
913 }
914
915 rc = winpr_RC4_Update(rc4Ctx, EncryptedSize, pbIn, pbOut);
916
917 if (!rc)
918 {
919 WLog_ERR(TAG, "winpr_Cipher_Update failure");
920 goto fail;
921 }
922 res = pbOut;
923fail:
924 winpr_RC4_Free(rc4Ctx);
925 free(PasswordW);
926 free(PassStubW);
927 free(pbIn);
928 if (!res)
929 free(pbOut);
930 else
931 *pEncryptedSize = EncryptedSize;
932 return res;
933}
934
935static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file)
936{
937 BOOL rc = FALSE;
938 int status = 0;
939 size_t cbPasswordW = 0;
940 size_t cchOutW = 0;
941 WINPR_CIPHER_CTX* aesDec = nullptr;
942 WCHAR* PasswordW = nullptr;
943 BYTE* pbIn = nullptr;
944 BYTE* pbOut = nullptr;
945 size_t cbOut = 0;
946 size_t cbIn = 0;
947 size_t cbFinal = 0;
948 BYTE DerivedKey[WINPR_AES_BLOCK_SIZE] = WINPR_C_ARRAY_INIT;
949 BYTE InitializationVector[WINPR_AES_BLOCK_SIZE] = WINPR_C_ARRAY_INIT;
950 BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
951
952 WINPR_ASSERT(file);
953
954 if (!file->password)
955 return FALSE;
956
957 PasswordW = ConvertUtf8ToWCharAlloc(file->password, &cbPasswordW);
958 if (!PasswordW)
959 {
960 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
961 return FALSE;
962 }
963
964 cbPasswordW = cbPasswordW * sizeof(WCHAR);
965
966 if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash,
967 sizeof(PasswordHash)))
968 goto fail;
969
970 if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash), DerivedKey,
971 sizeof(DerivedKey)))
972 goto fail;
973
974 aesDec =
975 winpr_Cipher_NewEx(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, DerivedKey, sizeof(DerivedKey),
976 InitializationVector, sizeof(InitializationVector));
977
978 if (!aesDec)
979 goto fail;
980
981 cbOut = cbFinal = 0;
982 cbIn = file->EncryptedLHTicketLength;
983 pbIn = file->EncryptedLHTicket;
984 pbOut = (BYTE*)calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);
985
986 if (!pbOut)
987 goto fail;
988
989 if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
990 goto fail;
991
992 if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
993 {
994 WLog_ERR(TAG, "winpr_Cipher_Final failure");
995 goto fail;
996 }
997
998 cbOut += cbFinal;
999 cbFinal = 0;
1000
1001 union
1002 {
1003 const WCHAR* wc;
1004 const BYTE* b;
1005 } cnv;
1006
1007 cnv.b = pbOut;
1008 cchOutW = cbOut / sizeof(WCHAR);
1009
1010 if (!update_connectionstring2_wchar(file, cnv.wc, cchOutW))
1011 {
1012 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
1013 goto fail;
1014 }
1015
1016 if (!freerdp_assistance_parse_connection_string2(file))
1017 goto fail;
1018
1019 rc = TRUE;
1020fail:
1021 winpr_Cipher_Free(aesDec);
1022 free(PasswordW);
1023 free(pbOut);
1024 WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
1025 return rc;
1026}
1027
1028BYTE* freerdp_assistance_hex_string_to_bin(const void* str, size_t* size)
1029{
1030 BYTE* buffer = nullptr;
1031 if (!str || !size)
1032 return nullptr;
1033 *size = 0;
1034 const size_t length = strlen(str);
1035 buffer = calloc(length, sizeof(BYTE));
1036 if (!buffer)
1037 return nullptr;
1038 const size_t rc = winpr_HexStringToBinBuffer(str, length, buffer, length);
1039 if (rc == 0)
1040 {
1041 free(buffer);
1042 return nullptr;
1043 }
1044 *size = rc;
1045 return buffer;
1046}
1047
1048char* freerdp_assistance_bin_to_hex_string(const void* data, size_t size)
1049{
1050 return winpr_BinToHexString(data, size, FALSE);
1051}
1052
1053static int freerdp_assistance_parse_uploadinfo(rdpAssistanceFile* file, char* uploadinfo,
1054 size_t uploadinfosize)
1055{
1056 const char escalated[9] = { 'E', 's', 'c', 'a', 'l', 'a', 't', 'e', 'd' };
1057 const size_t esclen = sizeof(escalated);
1058 const char* typestr = nullptr;
1059 size_t typelen = 0;
1060
1061 if (!uploadinfo || (uploadinfosize == 0))
1062 return -1;
1063
1064 const size_t ulen = strnlen(uploadinfo, uploadinfosize);
1065 if (ulen == uploadinfosize)
1066 {
1067 WLog_WARN(TAG, "UPLOADINFOR string is not '\\0' terminated");
1068 return -1;
1069 }
1070
1071 if (!freerdp_assistance_parse_attr(&typestr, &typelen, "TYPE", uploadinfo))
1072 return -1;
1073
1074 if ((typelen != esclen) || (strncmp(typestr, escalated, esclen) != 0))
1075 {
1076 WLog_ERR(TAG,
1077 "Failed to parse ASSISTANCE file: Missing or invalid UPLOADINFO TYPE '%s' [%" PRIuz
1078 "]",
1079 typestr, typelen);
1080 return -1;
1081 }
1082
1083 char* uploaddata = nullptr;
1084 size_t uploaddatasize = 0;
1085 if (!freerdp_assistance_consume_input_and_get_element(uploadinfo, ulen, "UPLOADDATA",
1086 &uploaddata, &uploaddatasize))
1087 return -1;
1088
1089 /* Parse USERNAME */
1090 if (!freerdp_assistance_parse_attr_str(&file->Username, "USERNAME", uploaddata))
1091 return -1;
1092
1093 /* Parse LHTICKET */
1094 if (!freerdp_assistance_parse_attr_str(&file->LHTicket, "LHTICKET", uploaddata))
1095 return -1;
1096
1097 /* Parse RCTICKET */
1098 if (!freerdp_assistance_parse_attr_str(&file->RCTicket, "RCTICKET", uploaddata))
1099 return -1;
1100
1101 /* Parse RCTICKETENCRYPTED */
1102 if (!freerdp_assistance_parse_attr_bool(&file->RCTicketEncrypted, "RCTICKETENCRYPTED",
1103 uploaddata))
1104 return -1;
1105
1106 /* Parse PassStub */
1107 if (!freerdp_assistance_parse_attr_str(&file->PassStub, "PassStub", uploaddata))
1108 return -1;
1109
1110 if (file->PassStub)
1111 {
1112 const char* amp = "&amp;";
1113 char* passtub = strstr(file->PassStub, amp);
1114 while (passtub)
1115 {
1116 const char* end = passtub + 5;
1117 const size_t len = strlen(end);
1118 memmove(&passtub[1], end, len + 1);
1119 passtub = strstr(passtub, amp);
1120 }
1121 }
1122
1123 /* Parse DtStart */
1124 if (!freerdp_assistance_parse_attr_uint32(&file->DtStart, "DtStart", uploaddata))
1125 return -1;
1126
1127 /* Parse DtLength */
1128 if (!freerdp_assistance_parse_attr_uint32(&file->DtLength, "DtLength", uploaddata))
1129 return -1;
1130
1131 /* Parse L (LowSpeed) */
1132 if (!freerdp_assistance_parse_attr_bool(&file->LowSpeed, "L", uploaddata))
1133 return -1;
1134
1135 file->Type = (file->LHTicket) ? 2 : 1;
1136 int status = 0;
1137
1138 switch (file->Type)
1139 {
1140 case 2:
1141 {
1142 file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(
1143 file->LHTicket, &file->EncryptedLHTicketLength);
1144
1145 if (!freerdp_assistance_decrypt2(file))
1146 status = -1;
1147 }
1148 break;
1149
1150 case 1:
1151 {
1152 if (!freerdp_assistance_parse_connection_string1(file))
1153 status = -1;
1154 }
1155 break;
1156
1157 default:
1158 return -1;
1159 }
1160
1161 if (status < 0)
1162 {
1163 WLog_ERR(TAG, "freerdp_assistance_parse_connection_string1 failure: %d", status);
1164 return -1;
1165 }
1166
1167 file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(file->password, file->PassStub,
1168 &file->EncryptedPassStubLength);
1169
1170 if (!file->EncryptedPassStub)
1171 return -1;
1172
1173 return 1;
1174}
1175
1176static int freerdp_assistance_parse_file_buffer_int(rdpAssistanceFile* file, char* buffer,
1177 size_t size, const char* password)
1178{
1179 WINPR_ASSERT(file);
1180 WINPR_ASSERT(buffer);
1181 WINPR_ASSERT(size > 0);
1182
1183 if (!update_password(file, password))
1184 return -1;
1185
1186 char* uploadinfo = nullptr;
1187 size_t uploadinfosize = 0;
1188 if (freerdp_assistance_consume_input_and_get_element(buffer, size, "UPLOADINFO", &uploadinfo,
1189 &uploadinfosize))
1190 return freerdp_assistance_parse_uploadinfo(file, uploadinfo, uploadinfosize);
1191
1192 size_t elen = 0;
1193 const char* estr =
1194 freerdp_assistance_contains_element(buffer, size, "E", &elen, nullptr, nullptr);
1195 if (!estr || (elen == 0))
1196 {
1197 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor <E> found");
1198 return -1;
1199 }
1200 if (!update_connectionstring2(file, estr, elen))
1201 return -1;
1202
1203 if (!freerdp_assistance_parse_connection_string2(file))
1204 return -1;
1205
1206 return 1;
1207}
1208
1209int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* cbuffer, size_t size,
1210 const char* password)
1211{
1212 WINPR_ASSERT(file);
1213 if (!password)
1214 {
1215 WLog_WARN(TAG, "empty password supplied");
1216 }
1217
1218 if (!cbuffer || (size == 0))
1219 {
1220 WLog_WARN(TAG, "no data supplied [%p, %" PRIuz "]",
1221 WINPR_CXX_COMPAT_CAST(const void*, cbuffer), size);
1222 return -1;
1223 }
1224
1225 char* abuffer = strndup(cbuffer, size);
1226 const size_t len = strnlen(cbuffer, size);
1227 if (len == size)
1228 WLog_WARN(TAG, "Input data not '\\0' terminated");
1229
1230 if (!abuffer)
1231 return -1;
1232
1233 const int rc = freerdp_assistance_parse_file_buffer_int(file, abuffer, len + 1, password);
1234 free(abuffer);
1235 return rc;
1236}
1237
1238int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
1239{
1240 int status = -1;
1241 BYTE* buffer = nullptr;
1242 size_t readSize = 0;
1243 union
1244 {
1245 INT64 i64;
1246 size_t s;
1247 } fileSize;
1248
1249 if (!update_name(file, name))
1250 return -1;
1251
1252 FILE* fp = winpr_fopen(name, "r");
1253
1254 if (!fp)
1255 {
1256 WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name);
1257 return -1;
1258 }
1259
1260 if (_fseeki64(fp, 0, SEEK_END) < 0)
1261 goto fail;
1262 fileSize.i64 = _ftelli64(fp);
1263 if (_fseeki64(fp, 0, SEEK_SET) < 0)
1264 goto fail;
1265
1266 if (fileSize.i64 < 1)
1267 {
1268 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1269 goto fail;
1270 }
1271
1272 buffer = (BYTE*)malloc(fileSize.s + 2);
1273
1274 if (!buffer)
1275 goto fail;
1276
1277 readSize = fread(buffer, fileSize.s, 1, fp);
1278
1279 if (!readSize)
1280 {
1281 if (!ferror(fp))
1282 readSize = fileSize.s;
1283 }
1284
1285 if (readSize < 1)
1286 {
1287 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1288 goto fail;
1289 }
1290
1291 buffer[fileSize.s] = '\0';
1292 buffer[fileSize.s + 1] = '\0';
1293 status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize.s, password);
1294
1295fail:
1296 (void)fclose(fp);
1297 free(buffer);
1298 return status;
1299}
1300
1301BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file,
1302 rdpSettings* settings)
1303{
1304 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1305 return FALSE;
1306
1307 if (!file->RASessionId || !file->MachineAddresses)
1308 return FALSE;
1309
1310 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId,
1311 file->RASessionId))
1312 return FALSE;
1313
1314 if (file->RCTicket)
1315 {
1316 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1317 file->RCTicket))
1318 return FALSE;
1319 }
1320 else
1321 {
1322 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1323 file->ConnectionString2))
1324 return FALSE;
1325 }
1326
1327 if (file->PassStub)
1328 {
1329 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub,
1330 file->PassStub))
1331 return FALSE;
1332 }
1333
1334 if (ArrayList_Count(file->MachineAddresses) < 1)
1335 return FALSE;
1336
1337 const char* addr = ArrayList_GetItem(file->MachineAddresses, 0);
1338 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, addr))
1339 return FALSE;
1340
1341 if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename))
1342 return FALSE;
1343
1344 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password))
1345 return FALSE;
1346
1347 if (file->Username)
1348 {
1349 if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username))
1350 return FALSE;
1351 }
1352
1353 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1354 return FALSE;
1355
1356 const size_t ports = ArrayList_Count(file->MachinePorts);
1357 const size_t addresses = ArrayList_Count(file->MachineAddresses);
1358 if (ports < 1)
1359 return FALSE;
1360 if (ports != addresses)
1361 return FALSE;
1362
1363 union
1364 {
1365 uintptr_t port;
1366 void* data;
1367 } cnv;
1368 cnv.data = ArrayList_GetItem(file->MachinePorts, 0);
1369 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1370 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT32)cnv.port))
1371 return FALSE;
1372
1373 if (!freerdp_target_net_adresses_reset(settings, ports))
1374 return FALSE;
1375
1376 for (size_t x = 0; x < ports; x++)
1377 {
1378 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1379 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1380 const UINT32 port = (UINT32)cnv.port;
1381 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port))
1382 return FALSE;
1383 }
1384 for (size_t i = 0; i < addresses; i++)
1385 {
1386 const char* maddr = ArrayList_GetItem(file->MachineAddresses, i);
1387 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr))
1388 return FALSE;
1389 }
1390
1391 return TRUE;
1392}
1393
1394static BOOL setup_string(wArrayList* list)
1395{
1396 WINPR_ASSERT(list);
1397
1398 wObject* obj = ArrayList_Object(list);
1399 if (!obj)
1400 return FALSE;
1401 obj->fnObjectFree = free;
1402 // obj->fnObjectNew = wwinpr_ObjectStringClone;
1403 return TRUE;
1404}
1405
1406rdpAssistanceFile* freerdp_assistance_file_new(void)
1407{
1408 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
1409 return nullptr;
1410
1411 rdpAssistanceFile* file = calloc(1, sizeof(rdpAssistanceFile));
1412 if (!file)
1413 return nullptr;
1414
1415 file->MachineAddresses = ArrayList_New(FALSE);
1416 file->MachinePorts = ArrayList_New(FALSE);
1417 file->MachineUris = ArrayList_New(FALSE);
1418
1419 if (!file->MachineAddresses || !file->MachinePorts || !file->MachineUris)
1420 goto fail;
1421
1422 if (!setup_string(file->MachineAddresses) || !setup_string(file->MachineUris))
1423 goto fail;
1424
1425 return file;
1426
1427fail:
1428 WINPR_PRAGMA_DIAG_PUSH
1429 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1430 freerdp_assistance_file_free(file);
1431 WINPR_PRAGMA_DIAG_POP
1432 return nullptr;
1433}
1434
1435void freerdp_assistance_file_free(rdpAssistanceFile* file)
1436{
1437 if (!file)
1438 return;
1439
1440 update_password(file, nullptr);
1441 update_connectionstring2(file, nullptr, 0);
1442 free(file->filename);
1443 free(file->Username);
1444 free(file->LHTicket);
1445 free(file->RCTicket);
1446 free(file->PassStub);
1447 free(file->ConnectionString1);
1448 free(file->EncryptedLHTicket);
1449 free(file->RASessionId);
1450 free(file->RASpecificParams);
1451 free(file->RASpecificParams2);
1452 free(file->EncryptedPassStub);
1453
1454 ArrayList_Free(file->MachineAddresses);
1455 ArrayList_Free(file->MachinePorts);
1456 ArrayList_Free(file->MachineUris);
1457 free(file);
1458}
1459
1460void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level)
1461{
1462 WINPR_ASSERT(file);
1463
1464 WLog_Print(log, level, "Username: %s", file->Username);
1465 WLog_Print(log, level, "LHTicket: %s", file->LHTicket);
1466 WLog_Print(log, level, "RCTicket: %s", file->RCTicket);
1467 WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted);
1468 WLog_Print(log, level, "PassStub: %s", file->PassStub);
1469 WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart);
1470 WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength);
1471 WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed);
1472 WLog_Print(log, level, "RASessionId: %s", file->RASessionId);
1473 WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams);
1474 WLog_Print(log, level, "RASpecificParams2: %s", file->RASpecificParams2);
1475
1476 for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++)
1477 {
1478 UINT32 port = 0;
1479 const char* uri = nullptr;
1480 const char* addr = ArrayList_GetItem(file->MachineAddresses, x);
1481 if (x < ArrayList_Count(file->MachinePorts))
1482 {
1483 union
1484 {
1485 uintptr_t port;
1486 void* data;
1487 } cnv;
1488 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1489 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1490 port = (UINT32)cnv.port;
1491 }
1492 if (x < ArrayList_Count(file->MachineUris))
1493 uri = ArrayList_GetItem(file->MachineUris, x);
1494
1495 WLog_Print(log, level, "MachineAddress [%" PRIuz ": %s", x, addr);
1496 WLog_Print(log, level, "MachinePort [%" PRIuz ": %" PRIu32, x, port);
1497 WLog_Print(log, level, "MachineURI [%" PRIuz ": %s", x, uri);
1498 }
1499}
1500
1501BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd,
1502 size_t* size)
1503{
1504 if (!file || !pwd || !size)
1505 return FALSE;
1506
1507 *pwd = (const char*)file->EncryptedPassStub;
1508 *size = file->EncryptedPassStubLength;
1509 return TRUE;
1510}
1511
1512int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string,
1513 const char* password)
1514{
1515 if (!file || !string || !password)
1516 return -1;
1517
1518 char* str = _strdup(string);
1519 if (!str)
1520 return -1;
1521
1522 if (!update_connectionstring2_nocopy(file, str))
1523 return -1;
1524 if (!update_password(file, password))
1525 return -1;
1526 return freerdp_assistance_parse_connection_string2(file);
1527}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets 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.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59