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 = NULL;
84 for (;;)
85 {
86 char* p = strstr(haystack, needle);
87 if (p == NULL)
88 break;
89 if (p > haystack + len)
90 return NULL;
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 = NULL;
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 != NULL;
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 != NULL;
126}
127
128static BOOL update_password(rdpAssistanceFile* file, const char* password)
129{
130 WINPR_ASSERT(file);
131 free(file->password);
132 file->password = NULL;
133 if (!password)
134 return TRUE;
135 file->password = _strdup(password);
136 return file->password != NULL;
137}
138
139static BOOL update_connectionstring2_nocopy(rdpAssistanceFile* file, char* str)
140{
141 WINPR_ASSERT(file);
142 free(file->ConnectionString2);
143 file->ConnectionString2 = NULL;
144 if (!str)
145 return TRUE;
146 file->ConnectionString2 = str;
147 return file->ConnectionString2 != NULL;
148}
149
150static BOOL update_connectionstring2(rdpAssistanceFile* file, const char* str, size_t len)
151{
152 char* strc = NULL;
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 = NULL;
168
169 if (!str && (len != 0))
170 return FALSE;
171
172 if (str && (len > 0))
173 {
174 strc = ConvertWCharNToUtf8Alloc(str, len, NULL);
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] = { 0 };
222 BYTE pad2[64] = { 0 };
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 = NULL;
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, NULL, 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 = NULL;
304 char* token = strtok_s(strp, s, &saveptr);
305
306 // walk through other tokens
307 while (token != NULL)
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(NULL, 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] = { 0 };
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 = NULL;
429 *plength = 0;
430 if (!tag)
431 return FALSE;
432
433 char bkey[128] = { 0 };
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 = NULL;
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 = NULL;
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 = NULL;
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] = { 0 };
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, NULL, 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] = { 0 };
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 NULL;
543
544 char* tag = strstr(input, bkey);
545 if (!tag || (tag > input + ilen))
546 return NULL;
547
548 char* data = tag + strnlen(bkey, sizeof(bkey));
549
550 /* Ensure there is a valid delimiter following our token */
551 switch (data[0])
552 {
553 case '>':
554 case '/':
555 case ' ':
556 case '\t':
557 break;
558 default:
559 WLog_ERR(TAG,
560 "Failed to parse ASSISTANCE file: ConnectionString2 missing delimiter after "
561 "field %s",
562 bkey);
563 return NULL;
564 }
565
566 char* start = strstr(tag, ">");
567
568 if (!start || (start > input + ilen))
569 {
570 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field %s", bkey);
571 return NULL;
572 }
573
574 const char* end = start;
575 const char* dend = start - 1;
576 if (*dend != '/')
577 {
578 char ekey[128] = { 0 };
579 const int erc = _snprintf(ekey, sizeof(ekey), "</%s>", key);
580 WINPR_ASSERT(erc > 0);
581 WINPR_ASSERT((size_t)erc < sizeof(ekey));
582 if ((erc <= 0) || ((size_t)erc >= sizeof(ekey)))
583 return NULL;
584 const size_t offset = WINPR_ASSERTING_INT_CAST(size_t, start - tag);
585 dend = end = strrstr(start, ilen - offset, ekey);
586 if (end)
587 end += strnlen(ekey, sizeof(ekey));
588 }
589
590 if (!end)
591 {
592 WLog_ERR(TAG,
593 "Failed to parse ASSISTANCE file: ConnectionString2 missing end tag for field %s",
594 key);
595 return NULL;
596 }
597 if (plen)
598 *plen = WINPR_ASSERTING_INT_CAST(size_t, end - tag);
599
600 if (pdata)
601 *pdata = data;
602 if (pdlen)
603 *pdlen = WINPR_ASSERTING_INT_CAST(size_t, dend - data);
604 return tag;
605}
606
612static BOOL freerdp_assistance_consume_input_and_get_element(char* input, const char* key,
613 char** element, size_t* elen)
614{
615 WINPR_ASSERT(input);
616 WINPR_ASSERT(key);
617 WINPR_ASSERT(element);
618 WINPR_ASSERT(elen);
619
620 size_t len = 0;
621 size_t dlen = 0;
622 char* data = NULL;
623 char* tag = freerdp_assistance_contains_element(input, strlen(input), key, &len, &data, &dlen);
624 if (!tag)
625 return FALSE;
626
627 char* end = data + dlen;
628 *tag = '\0';
629 *end = '\0';
630 *element = data;
631 *elen = dlen + 1;
632 return TRUE;
633}
634
635static BOOL freerdp_assistance_get_element(char* input, size_t ilen, const char* key,
636 char** element, size_t* elen)
637{
638 WINPR_ASSERT(input);
639 WINPR_ASSERT(key);
640 WINPR_ASSERT(element);
641 WINPR_ASSERT(elen);
642
643 size_t len = 0;
644 size_t dlen = 0;
645 char* data = NULL;
646 char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen);
647 if (!tag)
648 return FALSE;
649
650 if (tag + len > input + ilen)
651 return FALSE;
652
653 char* end = tag + len;
654 *element = data;
655 *elen = WINPR_ASSERTING_INT_CAST(size_t, end - data + 1);
656 return TRUE;
657}
658
659static BOOL freerdp_assistance_parse_all_elements_of(rdpAssistanceFile* file, char* data,
660 size_t len, const char* key,
661 BOOL (*fkt)(rdpAssistanceFile* file,
662 char* data, size_t len))
663{
664 char* val = NULL;
665 size_t vlen = 0;
666
667 while (freerdp_assistance_get_element(data, len, key, &val, &vlen))
668 {
669 data = val + vlen;
670 len = strnlen(data, len);
671 if (vlen > 0)
672 {
673 val[vlen - 1] = '\0';
674
675 if (!fkt(file, val, vlen))
676 return FALSE;
677 }
678 }
679
680 return TRUE;
681}
682
683static BOOL freerdp_assistance_parse_all_elements_of_l(rdpAssistanceFile* file, char* data,
684 WINPR_ATTR_UNUSED size_t len)
685{
686 UINT32 p = 0;
687 const char* n = NULL;
688 const char* u = NULL;
689 size_t nlen = 0;
690 size_t ulen = 0;
691 if (!freerdp_assistance_parse_attr_uint32(&p, "P", data))
692 return FALSE;
693 if (!freerdp_assistance_parse_attr(&n, &nlen, "N", data))
694 return FALSE;
695 if (!freerdp_assistance_parse_attr(&u, &ulen, "U", data))
696 return FALSE;
697
698 if (n && (nlen > 0))
699 {
700 if (!append_address_to_list(file->MachineAddresses, n, nlen))
701 return FALSE;
702 if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
703 return FALSE;
704 }
705 if (u && (ulen > 0))
706 {
707 if (!append_address_to_list(file->MachineAddresses, u, ulen))
708 return FALSE;
709 if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
710 return FALSE;
711 }
712 return TRUE;
713}
714
715static BOOL freerdp_assistance_parse_all_elements_of_t(rdpAssistanceFile* file, char* data,
716 size_t len)
717{
718 UINT32 id = 0;
719 UINT32 sid = 0;
720 if (!freerdp_assistance_parse_attr_uint32(&id, "ID", data))
721 return FALSE;
722 if (!freerdp_assistance_parse_attr_uint32(&sid, "SID", data))
723 return FALSE;
724 WLog_DBG(TAG, "transport id=%" PRIu32 ", sid=%" PRIu32, id, sid);
725 return freerdp_assistance_parse_all_elements_of(file, data, len, "L",
726 freerdp_assistance_parse_all_elements_of_l);
727}
728
729static BOOL freerdp_assistance_parse_all_elements_of_c(rdpAssistanceFile* file, char* data,
730 size_t len)
731{
732 return freerdp_assistance_parse_all_elements_of(file, data, len, "T",
733 freerdp_assistance_parse_all_elements_of_t);
734}
735
736static BOOL freerdp_assistance_parse_find_elements_of_c(rdpAssistanceFile* file, char* data,
737 size_t len)
738{
739 return freerdp_assistance_parse_all_elements_of(file, data, len, "C",
740 freerdp_assistance_parse_all_elements_of_c);
741}
742
743static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
744{
745 BOOL rc = FALSE;
746
747 WINPR_ASSERT(file);
748
749 if (!file->ConnectionString2)
750 return FALSE;
751
752 char* str = _strdup(file->ConnectionString2);
753 if (!str)
754 goto out_fail;
755
756 {
757 char* e = NULL;
758 size_t elen = 0;
759 if (!freerdp_assistance_consume_input_and_get_element(str, "E", &e, &elen))
760 goto out_fail;
761
762 if (!e || (elen == 0))
763 goto out_fail;
764 {
765 char* a = NULL;
766 size_t alen = 0;
767 if (!freerdp_assistance_get_element(e, elen, "A", &a, &alen))
768 goto out_fail;
769
770 if (!a || (alen == 0))
771 goto out_fail;
772
773 if (!freerdp_assistance_parse_find_elements_of_c(file, e, elen))
774 goto out_fail;
775
776 /* '\0' terminate the detected XML elements so
777 * the parser can continue with terminated strings
778 */
779 a[alen] = '\0';
780
781 if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams, "KH", a))
782 goto out_fail;
783
784 if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams2, "KH2", a))
785 goto out_fail;
786
787 if (!freerdp_assistance_parse_attr_str(&file->RASessionId, "ID", a))
788 goto out_fail;
789 }
790 }
791 rc = TRUE;
792out_fail:
793 free(str);
794 return rc;
795}
796
797char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
798{
799 if (!name || !pass)
800 return NULL;
801
802 const size_t nameLength = strlen(name) + strlen("NAME=");
803 const size_t passLength = strlen(pass) + strlen("PASS=");
804 const size_t size = nameLength + passLength + 64;
805 char* ExpertBlob = (char*)calloc(1, size);
806
807 if (!ExpertBlob)
808 return NULL;
809
810 (void)sprintf_s(ExpertBlob, size, "%" PRIuz ";NAME=%s%" PRIuz ";PASS=%s", nameLength, name,
811 passLength, pass);
812 return ExpertBlob;
813}
814
815char* freerdp_assistance_generate_pass_stub(WINPR_ATTR_UNUSED DWORD flags)
816{
817 UINT32 nums[14] = { 0 };
818 const char set1[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
819 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
820 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
821 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
822 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '_' };
823 const char set2[12] = { '!', '@', '#', '$', '&', '^', '*', '(', ')', '-', '+', '=' };
824 const char set3[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
825 const char set4[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
826 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
827 const char set5[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
828 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
829 char* passStub = calloc(15, sizeof(char));
830
831 if (!passStub)
832 return NULL;
833
845 winpr_RAND(nums, sizeof(nums));
846 passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */
847 passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */
848 passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */
849 passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */
850 passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */
851
852 for (size_t x = 5; x < ARRAYSIZE(nums); x++)
853 passStub[x] = set1[nums[x] % sizeof(set1)]; /* character 5 - 13 */
854 return passStub;
855}
856
857BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
858 size_t* pEncryptedSize)
859{
860 BOOL rc = 0;
861 size_t cbPasswordW = 0;
862 size_t cbPassStubW = 0;
863 BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH] = { 0 };
864 WINPR_RC4_CTX* rc4Ctx = NULL;
865 BYTE* pbIn = NULL;
866 BYTE* pbOut = NULL;
867 BYTE* res = NULL;
868 WCHAR* PasswordW = ConvertUtf8ToWCharAlloc(password, &cbPasswordW);
869 WCHAR* PassStubW = ConvertUtf8ToWCharAlloc(passStub, &cbPassStubW);
870
871 cbPasswordW = (cbPasswordW) * sizeof(WCHAR);
872 cbPassStubW = (cbPassStubW) * sizeof(WCHAR);
873 const size_t EncryptedSize = cbPassStubW + 4;
874
875 if (!PasswordW || !PassStubW)
876 goto fail;
877
878 if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*)PasswordHash,
879 sizeof(PasswordHash)))
880 goto fail;
881
882 pbIn = (BYTE*)calloc(1, EncryptedSize);
883 pbOut = (BYTE*)calloc(1, EncryptedSize);
884
885 if (!pbIn || !pbOut)
886 goto fail;
887
888 WINPR_ASSERT(cbPasswordW <= UINT32_MAX);
889 winpr_Data_Write_UINT32(pbIn, (UINT32)cbPassStubW);
890 CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
891 rc4Ctx = winpr_RC4_New(PasswordHash, sizeof(PasswordHash));
892
893 if (!rc4Ctx)
894 {
895 WLog_ERR(TAG, "winpr_Cipher_New failure");
896 goto fail;
897 }
898
899 rc = winpr_RC4_Update(rc4Ctx, EncryptedSize, pbIn, pbOut);
900
901 if (!rc)
902 {
903 WLog_ERR(TAG, "winpr_Cipher_Update failure");
904 goto fail;
905 }
906 res = pbOut;
907fail:
908 winpr_RC4_Free(rc4Ctx);
909 free(PasswordW);
910 free(PassStubW);
911 free(pbIn);
912 if (!res)
913 free(pbOut);
914 else
915 *pEncryptedSize = EncryptedSize;
916 return res;
917}
918
919static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file)
920{
921 BOOL rc = FALSE;
922 int status = 0;
923 size_t cbPasswordW = 0;
924 size_t cchOutW = 0;
925 WINPR_CIPHER_CTX* aesDec = NULL;
926 WCHAR* PasswordW = NULL;
927 BYTE* pbIn = NULL;
928 BYTE* pbOut = NULL;
929 size_t cbOut = 0;
930 size_t cbIn = 0;
931 size_t cbFinal = 0;
932 BYTE DerivedKey[WINPR_AES_BLOCK_SIZE] = { 0 };
933 BYTE InitializationVector[WINPR_AES_BLOCK_SIZE] = { 0 };
934 BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH] = { 0 };
935
936 WINPR_ASSERT(file);
937
938 if (!file->password)
939 return FALSE;
940
941 PasswordW = ConvertUtf8ToWCharAlloc(file->password, &cbPasswordW);
942 if (!PasswordW)
943 {
944 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
945 return FALSE;
946 }
947
948 cbPasswordW = (cbPasswordW) * sizeof(WCHAR);
949
950 if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash,
951 sizeof(PasswordHash)))
952 goto fail;
953
954 if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash), DerivedKey,
955 sizeof(DerivedKey)))
956 goto fail;
957
958 aesDec =
959 winpr_Cipher_NewEx(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, DerivedKey, sizeof(DerivedKey),
960 InitializationVector, sizeof(InitializationVector));
961
962 if (!aesDec)
963 goto fail;
964
965 cbOut = cbFinal = 0;
966 cbIn = file->EncryptedLHTicketLength;
967 pbIn = file->EncryptedLHTicket;
968 pbOut = (BYTE*)calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);
969
970 if (!pbOut)
971 goto fail;
972
973 if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
974 goto fail;
975
976 if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
977 {
978 WLog_ERR(TAG, "winpr_Cipher_Final failure");
979 goto fail;
980 }
981
982 cbOut += cbFinal;
983 cbFinal = 0;
984
985 union
986 {
987 const WCHAR* wc;
988 const BYTE* b;
989 } cnv;
990
991 cnv.b = pbOut;
992 cchOutW = cbOut / sizeof(WCHAR);
993
994 if (!update_connectionstring2_wchar(file, cnv.wc, cchOutW))
995 {
996 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
997 goto fail;
998 }
999
1000 if (!freerdp_assistance_parse_connection_string2(file))
1001 goto fail;
1002
1003 rc = TRUE;
1004fail:
1005 winpr_Cipher_Free(aesDec);
1006 free(PasswordW);
1007 free(pbOut);
1008 WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
1009 return rc;
1010}
1011
1012BYTE* freerdp_assistance_hex_string_to_bin(const void* raw, size_t* size)
1013{
1014 BYTE* buffer = NULL;
1015 if (!raw || !size)
1016 return NULL;
1017 *size = 0;
1018 const size_t length = strlen(raw);
1019 buffer = calloc(length, sizeof(BYTE));
1020 if (!buffer)
1021 return NULL;
1022 const size_t rc = winpr_HexStringToBinBuffer(raw, length, buffer, length);
1023 if (rc == 0)
1024 {
1025 free(buffer);
1026 return NULL;
1027 }
1028 *size = rc;
1029 return buffer;
1030}
1031
1032char* freerdp_assistance_bin_to_hex_string(const void* raw, size_t size)
1033{
1034 return winpr_BinToHexString(raw, size, FALSE);
1035}
1036
1037static int freerdp_assistance_parse_uploadinfo(rdpAssistanceFile* file, char* uploadinfo,
1038 size_t uploadinfosize)
1039{
1040 const char escalated[9] = { 'E', 's', 'c', 'a', 'l', 'a', 't', 'e', 'd' };
1041 const size_t esclen = sizeof(escalated);
1042 const char* typestr = NULL;
1043 size_t typelen = 0;
1044
1045 if (!uploadinfo || (uploadinfosize == 0))
1046 return -1;
1047
1048 if (strnlen(uploadinfo, uploadinfosize) == uploadinfosize)
1049 {
1050 WLog_WARN(TAG, "UPLOADINFOR string is not '\\0' terminated");
1051 return -1;
1052 }
1053
1054 if (!freerdp_assistance_parse_attr(&typestr, &typelen, "TYPE", uploadinfo))
1055 return -1;
1056
1057 if ((typelen != esclen) || (strncmp(typestr, escalated, esclen) != 0))
1058 {
1059 WLog_ERR(TAG,
1060 "Failed to parse ASSISTANCE file: Missing or invalid UPLOADINFO TYPE '%s' [%" PRIuz
1061 "]",
1062 typestr, typelen);
1063 return -1;
1064 }
1065
1066 char* uploaddata = NULL;
1067 size_t uploaddatasize = 0;
1068 if (!freerdp_assistance_consume_input_and_get_element(uploadinfo, "UPLOADDATA", &uploaddata,
1069 &uploaddatasize))
1070 return -1;
1071
1072 /* Parse USERNAME */
1073 if (!freerdp_assistance_parse_attr_str(&file->Username, "USERNAME", uploaddata))
1074 return -1;
1075
1076 /* Parse LHTICKET */
1077 if (!freerdp_assistance_parse_attr_str(&file->LHTicket, "LHTICKET", uploaddata))
1078 return -1;
1079
1080 /* Parse RCTICKET */
1081 if (!freerdp_assistance_parse_attr_str(&file->RCTicket, "RCTICKET", uploaddata))
1082 return -1;
1083
1084 /* Parse RCTICKETENCRYPTED */
1085 if (!freerdp_assistance_parse_attr_bool(&file->RCTicketEncrypted, "RCTICKETENCRYPTED",
1086 uploaddata))
1087 return -1;
1088
1089 /* Parse PassStub */
1090 if (!freerdp_assistance_parse_attr_str(&file->PassStub, "PassStub", uploaddata))
1091 return -1;
1092
1093 if (file->PassStub)
1094 {
1095 const char* amp = "&amp;";
1096 char* passtub = strstr(file->PassStub, amp);
1097 while (passtub)
1098 {
1099 const char* end = passtub + 5;
1100 const size_t len = strlen(end);
1101 memmove(&passtub[1], end, len + 1);
1102 passtub = strstr(passtub, amp);
1103 }
1104 }
1105
1106 /* Parse DtStart */
1107 if (!freerdp_assistance_parse_attr_uint32(&file->DtStart, "DtStart", uploaddata))
1108 return -1;
1109
1110 /* Parse DtLength */
1111 if (!freerdp_assistance_parse_attr_uint32(&file->DtLength, "DtLength", uploaddata))
1112 return -1;
1113
1114 /* Parse L (LowSpeed) */
1115 if (!freerdp_assistance_parse_attr_bool(&file->LowSpeed, "L", uploaddata))
1116 return -1;
1117
1118 file->Type = (file->LHTicket) ? 2 : 1;
1119 int status = 0;
1120
1121 switch (file->Type)
1122 {
1123 case 2:
1124 {
1125 file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(
1126 file->LHTicket, &file->EncryptedLHTicketLength);
1127
1128 if (!freerdp_assistance_decrypt2(file))
1129 status = -1;
1130 }
1131 break;
1132
1133 case 1:
1134 {
1135 if (!freerdp_assistance_parse_connection_string1(file))
1136 status = -1;
1137 }
1138 break;
1139
1140 default:
1141 return -1;
1142 }
1143
1144 if (status < 0)
1145 {
1146 WLog_ERR(TAG, "freerdp_assistance_parse_connection_string1 failure: %d", status);
1147 return -1;
1148 }
1149
1150 file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(file->password, file->PassStub,
1151 &file->EncryptedPassStubLength);
1152
1153 if (!file->EncryptedPassStub)
1154 return -1;
1155
1156 return 1;
1157}
1158
1159static int freerdp_assistance_parse_file_buffer_int(rdpAssistanceFile* file, char* buffer,
1160 size_t size, const char* password)
1161{
1162 WINPR_ASSERT(file);
1163 WINPR_ASSERT(buffer);
1164 WINPR_ASSERT(size > 0);
1165
1166 if (!update_password(file, password))
1167 return -1;
1168
1169 char* uploadinfo = NULL;
1170 size_t uploadinfosize = 0;
1171 if (freerdp_assistance_consume_input_and_get_element(buffer, "UPLOADINFO", &uploadinfo,
1172 &uploadinfosize))
1173 return freerdp_assistance_parse_uploadinfo(file, uploadinfo, uploadinfosize);
1174
1175 size_t elen = 0;
1176 const char* estr = freerdp_assistance_contains_element(buffer, size, "E", &elen, NULL, NULL);
1177 if (!estr || (elen == 0))
1178 {
1179 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor <E> found");
1180 return -1;
1181 }
1182 if (!update_connectionstring2(file, estr, elen))
1183 return -1;
1184
1185 if (!freerdp_assistance_parse_connection_string2(file))
1186 return -1;
1187
1188 return 1;
1189}
1190
1191int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* cbuffer, size_t size,
1192 const char* password)
1193{
1194 WINPR_ASSERT(file);
1195 if (!password)
1196 {
1197 WLog_WARN(TAG, "empty password supplied");
1198 }
1199
1200 if (!cbuffer || (size == 0))
1201 {
1202 WLog_WARN(TAG, "no data supplied [%p, %" PRIuz "]",
1203 WINPR_CXX_COMPAT_CAST(const void*, cbuffer), size);
1204 return -1;
1205 }
1206
1207 char* abuffer = strndup(cbuffer, size);
1208 const size_t len = strnlen(cbuffer, size);
1209 if (len == size)
1210 WLog_WARN(TAG, "Input data not '\\0' terminated");
1211
1212 if (!abuffer)
1213 return -1;
1214
1215 const int rc = freerdp_assistance_parse_file_buffer_int(file, abuffer, len + 1, password);
1216 free(abuffer);
1217 return rc;
1218}
1219
1220int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
1221{
1222 int status = 0;
1223 BYTE* buffer = NULL;
1224 FILE* fp = NULL;
1225 size_t readSize = 0;
1226 union
1227 {
1228 INT64 i64;
1229 size_t s;
1230 } fileSize;
1231
1232 if (!update_name(file, name))
1233 return -1;
1234
1235 fp = winpr_fopen(name, "r");
1236
1237 if (!fp)
1238 {
1239 WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name);
1240 return -1;
1241 }
1242
1243 (void)_fseeki64(fp, 0, SEEK_END);
1244 fileSize.i64 = _ftelli64(fp);
1245 (void)_fseeki64(fp, 0, SEEK_SET);
1246
1247 if (fileSize.i64 < 1)
1248 {
1249 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1250 (void)fclose(fp);
1251 return -1;
1252 }
1253
1254 buffer = (BYTE*)malloc(fileSize.s + 2);
1255
1256 if (!buffer)
1257 {
1258 (void)fclose(fp);
1259 return -1;
1260 }
1261
1262 readSize = fread(buffer, fileSize.s, 1, fp);
1263
1264 if (!readSize)
1265 {
1266 if (!ferror(fp))
1267 readSize = fileSize.s;
1268 }
1269
1270 (void)fclose(fp);
1271
1272 if (readSize < 1)
1273 {
1274 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1275 free(buffer);
1276 buffer = NULL;
1277 return -1;
1278 }
1279
1280 buffer[fileSize.s] = '\0';
1281 buffer[fileSize.s + 1] = '\0';
1282 status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize.s, password);
1283 free(buffer);
1284 return status;
1285}
1286
1287BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file,
1288 rdpSettings* settings)
1289{
1290 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1291 return FALSE;
1292
1293 if (!file->RASessionId || !file->MachineAddresses)
1294 return FALSE;
1295
1296 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId,
1297 file->RASessionId))
1298 return FALSE;
1299
1300 if (file->RCTicket)
1301 {
1302 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1303 file->RCTicket))
1304 return FALSE;
1305 }
1306 else
1307 {
1308 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1309 file->ConnectionString2))
1310 return FALSE;
1311 }
1312
1313 if (file->PassStub)
1314 {
1315 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub,
1316 file->PassStub))
1317 return FALSE;
1318 }
1319
1320 if (ArrayList_Count(file->MachineAddresses) < 1)
1321 return FALSE;
1322
1323 const char* addr = ArrayList_GetItem(file->MachineAddresses, 0);
1324 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, addr))
1325 return FALSE;
1326
1327 if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename))
1328 return FALSE;
1329
1330 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password))
1331 return FALSE;
1332
1333 if (file->Username)
1334 {
1335 if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username))
1336 return FALSE;
1337 }
1338
1339 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1340 return FALSE;
1341
1342 const size_t ports = ArrayList_Count(file->MachinePorts);
1343 const size_t addresses = ArrayList_Count(file->MachineAddresses);
1344 if (ports < 1)
1345 return FALSE;
1346 if (ports != addresses)
1347 return FALSE;
1348
1349 union
1350 {
1351 uintptr_t port;
1352 void* data;
1353 } cnv;
1354 cnv.data = ArrayList_GetItem(file->MachinePorts, 0);
1355 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1356 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT32)cnv.port))
1357 return FALSE;
1358
1359 if (!freerdp_target_net_adresses_reset(settings, ports))
1360 return FALSE;
1361
1362 for (size_t x = 0; x < ports; x++)
1363 {
1364 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1365 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1366 const UINT32 port = (UINT32)cnv.port;
1367 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port))
1368 return FALSE;
1369 }
1370 for (size_t i = 0; i < addresses; i++)
1371 {
1372 const char* maddr = ArrayList_GetItem(file->MachineAddresses, i);
1373 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr))
1374 return FALSE;
1375 }
1376
1377 return TRUE;
1378}
1379
1380static BOOL setup_string(wArrayList* list)
1381{
1382 WINPR_ASSERT(list);
1383
1384 wObject* obj = ArrayList_Object(list);
1385 if (!obj)
1386 return FALSE;
1387 obj->fnObjectFree = free;
1388 // obj->fnObjectNew = wwinpr_ObjectStringClone;
1389 return TRUE;
1390}
1391
1392rdpAssistanceFile* freerdp_assistance_file_new(void)
1393{
1394 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
1395 rdpAssistanceFile* file = calloc(1, sizeof(rdpAssistanceFile));
1396 if (!file)
1397 return NULL;
1398
1399 file->MachineAddresses = ArrayList_New(FALSE);
1400 file->MachinePorts = ArrayList_New(FALSE);
1401 file->MachineUris = ArrayList_New(FALSE);
1402
1403 if (!file->MachineAddresses || !file->MachinePorts || !file->MachineUris)
1404 goto fail;
1405
1406 if (!setup_string(file->MachineAddresses) || !setup_string(file->MachineUris))
1407 goto fail;
1408
1409 return file;
1410
1411fail:
1412 WINPR_PRAGMA_DIAG_PUSH
1413 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1414 freerdp_assistance_file_free(file);
1415 WINPR_PRAGMA_DIAG_POP
1416 return NULL;
1417}
1418
1419void freerdp_assistance_file_free(rdpAssistanceFile* file)
1420{
1421 if (!file)
1422 return;
1423
1424 update_password(file, NULL);
1425 update_connectionstring2(file, NULL, 0);
1426 free(file->filename);
1427 free(file->Username);
1428 free(file->LHTicket);
1429 free(file->RCTicket);
1430 free(file->PassStub);
1431 free(file->ConnectionString1);
1432 free(file->EncryptedLHTicket);
1433 free(file->RASessionId);
1434 free(file->RASpecificParams);
1435 free(file->RASpecificParams2);
1436 free(file->EncryptedPassStub);
1437
1438 ArrayList_Free(file->MachineAddresses);
1439 ArrayList_Free(file->MachinePorts);
1440 ArrayList_Free(file->MachineUris);
1441 free(file);
1442}
1443
1444void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level)
1445{
1446 WINPR_ASSERT(file);
1447
1448 WLog_Print(log, level, "Username: %s", file->Username);
1449 WLog_Print(log, level, "LHTicket: %s", file->LHTicket);
1450 WLog_Print(log, level, "RCTicket: %s", file->RCTicket);
1451 WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted);
1452 WLog_Print(log, level, "PassStub: %s", file->PassStub);
1453 WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart);
1454 WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength);
1455 WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed);
1456 WLog_Print(log, level, "RASessionId: %s", file->RASessionId);
1457 WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams);
1458 WLog_Print(log, level, "RASpecificParams2: %s", file->RASpecificParams2);
1459
1460 for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++)
1461 {
1462 UINT32 port = 0;
1463 const char* uri = NULL;
1464 const char* addr = ArrayList_GetItem(file->MachineAddresses, x);
1465 if (x < ArrayList_Count(file->MachinePorts))
1466 {
1467 union
1468 {
1469 uintptr_t port;
1470 void* data;
1471 } cnv;
1472 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1473 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1474 port = (UINT32)cnv.port;
1475 }
1476 if (x < ArrayList_Count(file->MachineUris))
1477 uri = ArrayList_GetItem(file->MachineUris, x);
1478
1479 WLog_Print(log, level, "MachineAddress [%" PRIuz ": %s", x, addr);
1480 WLog_Print(log, level, "MachinePort [%" PRIuz ": %" PRIu32, x, port);
1481 WLog_Print(log, level, "MachineURI [%" PRIuz ": %s", x, uri);
1482 }
1483}
1484
1485BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd,
1486 size_t* size)
1487{
1488 if (!file || !pwd || !size)
1489 return FALSE;
1490
1491 *pwd = (const char*)file->EncryptedPassStub;
1492 *size = file->EncryptedPassStubLength;
1493 return TRUE;
1494}
1495
1496int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string,
1497 const char* password)
1498{
1499 if (!file || !string || !password)
1500 return -1;
1501
1502 char* str = _strdup(string);
1503 if (!str)
1504 return -1;
1505
1506 if (!update_connectionstring2_nocopy(file, str))
1507 return -1;
1508 if (!update_password(file, password))
1509 return -1;
1510 return freerdp_assistance_parse_connection_string2(file);
1511}
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_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.
This struct contains function pointer to initialize/free objects.
Definition collections.h:57