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 "]", cbuffer, size);
1203 return -1;
1204 }
1205
1206 char* abuffer = strndup(cbuffer, size);
1207 const size_t len = strnlen(cbuffer, size);
1208 if (len == size)
1209 WLog_WARN(TAG, "Input data not '\0' terminated");
1210
1211 if (!abuffer)
1212 return -1;
1213
1214 const int rc = freerdp_assistance_parse_file_buffer_int(file, abuffer, len + 1, password);
1215 free(abuffer);
1216 return rc;
1217}
1218
1219int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
1220{
1221 int status = 0;
1222 BYTE* buffer = NULL;
1223 FILE* fp = NULL;
1224 size_t readSize = 0;
1225 union
1226 {
1227 INT64 i64;
1228 size_t s;
1229 } fileSize;
1230
1231 if (!update_name(file, name))
1232 return -1;
1233
1234 fp = winpr_fopen(name, "r");
1235
1236 if (!fp)
1237 {
1238 WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name);
1239 return -1;
1240 }
1241
1242 (void)_fseeki64(fp, 0, SEEK_END);
1243 fileSize.i64 = _ftelli64(fp);
1244 (void)_fseeki64(fp, 0, SEEK_SET);
1245
1246 if (fileSize.i64 < 1)
1247 {
1248 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1249 (void)fclose(fp);
1250 return -1;
1251 }
1252
1253 buffer = (BYTE*)malloc(fileSize.s + 2);
1254
1255 if (!buffer)
1256 {
1257 (void)fclose(fp);
1258 return -1;
1259 }
1260
1261 readSize = fread(buffer, fileSize.s, 1, fp);
1262
1263 if (!readSize)
1264 {
1265 if (!ferror(fp))
1266 readSize = fileSize.s;
1267 }
1268
1269 (void)fclose(fp);
1270
1271 if (readSize < 1)
1272 {
1273 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1274 free(buffer);
1275 buffer = NULL;
1276 return -1;
1277 }
1278
1279 buffer[fileSize.s] = '\0';
1280 buffer[fileSize.s + 1] = '\0';
1281 status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize.s, password);
1282 free(buffer);
1283 return status;
1284}
1285
1286BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file,
1287 rdpSettings* settings)
1288{
1289 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1290 return FALSE;
1291
1292 if (!file->RASessionId || !file->MachineAddresses)
1293 return FALSE;
1294
1295 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId,
1296 file->RASessionId))
1297 return FALSE;
1298
1299 if (file->RCTicket)
1300 {
1301 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1302 file->RCTicket))
1303 return FALSE;
1304 }
1305 else
1306 {
1307 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1308 file->ConnectionString2))
1309 return FALSE;
1310 }
1311
1312 if (file->PassStub)
1313 {
1314 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub,
1315 file->PassStub))
1316 return FALSE;
1317 }
1318
1319 if (ArrayList_Count(file->MachineAddresses) < 1)
1320 return FALSE;
1321
1322 const char* addr = ArrayList_GetItem(file->MachineAddresses, 0);
1323 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, addr))
1324 return FALSE;
1325
1326 if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename))
1327 return FALSE;
1328
1329 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password))
1330 return FALSE;
1331
1332 if (file->Username)
1333 {
1334 if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username))
1335 return FALSE;
1336 }
1337
1338 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1339 return FALSE;
1340
1341 const size_t ports = ArrayList_Count(file->MachinePorts);
1342 const size_t addresses = ArrayList_Count(file->MachineAddresses);
1343 if (ports < 1)
1344 return FALSE;
1345 if (ports != addresses)
1346 return FALSE;
1347
1348 union
1349 {
1350 uintptr_t port;
1351 void* data;
1352 } cnv;
1353 cnv.data = ArrayList_GetItem(file->MachinePorts, 0);
1354 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1355 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT32)cnv.port))
1356 return FALSE;
1357
1358 if (!freerdp_target_net_adresses_reset(settings, ports))
1359 return FALSE;
1360
1361 for (size_t x = 0; x < ports; x++)
1362 {
1363 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1364 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1365 const UINT32 port = (UINT32)cnv.port;
1366 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port))
1367 return FALSE;
1368 }
1369 for (size_t i = 0; i < addresses; i++)
1370 {
1371 const char* maddr = ArrayList_GetItem(file->MachineAddresses, i);
1372 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr))
1373 return FALSE;
1374 }
1375
1376 return TRUE;
1377}
1378
1379static BOOL setup_string(wArrayList* list)
1380{
1381 WINPR_ASSERT(list);
1382
1383 wObject* obj = ArrayList_Object(list);
1384 if (!obj)
1385 return FALSE;
1386 obj->fnObjectFree = free;
1387 // obj->fnObjectNew = wwinpr_ObjectStringClone;
1388 return TRUE;
1389}
1390
1391rdpAssistanceFile* freerdp_assistance_file_new(void)
1392{
1393 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
1394 rdpAssistanceFile* file = calloc(1, sizeof(rdpAssistanceFile));
1395 if (!file)
1396 return NULL;
1397
1398 file->MachineAddresses = ArrayList_New(FALSE);
1399 file->MachinePorts = ArrayList_New(FALSE);
1400 file->MachineUris = ArrayList_New(FALSE);
1401
1402 if (!file->MachineAddresses || !file->MachinePorts || !file->MachineUris)
1403 goto fail;
1404
1405 if (!setup_string(file->MachineAddresses) || !setup_string(file->MachineUris))
1406 goto fail;
1407
1408 return file;
1409
1410fail:
1411 WINPR_PRAGMA_DIAG_PUSH
1412 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1413 freerdp_assistance_file_free(file);
1414 WINPR_PRAGMA_DIAG_POP
1415 return NULL;
1416}
1417
1418void freerdp_assistance_file_free(rdpAssistanceFile* file)
1419{
1420 if (!file)
1421 return;
1422
1423 update_password(file, NULL);
1424 update_connectionstring2(file, NULL, 0);
1425 free(file->filename);
1426 free(file->Username);
1427 free(file->LHTicket);
1428 free(file->RCTicket);
1429 free(file->PassStub);
1430 free(file->ConnectionString1);
1431 free(file->EncryptedLHTicket);
1432 free(file->RASessionId);
1433 free(file->RASpecificParams);
1434 free(file->RASpecificParams2);
1435 free(file->EncryptedPassStub);
1436
1437 ArrayList_Free(file->MachineAddresses);
1438 ArrayList_Free(file->MachinePorts);
1439 ArrayList_Free(file->MachineUris);
1440 free(file);
1441}
1442
1443void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level)
1444{
1445 WINPR_ASSERT(file);
1446
1447 WLog_Print(log, level, "Username: %s", file->Username);
1448 WLog_Print(log, level, "LHTicket: %s", file->LHTicket);
1449 WLog_Print(log, level, "RCTicket: %s", file->RCTicket);
1450 WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted);
1451 WLog_Print(log, level, "PassStub: %s", file->PassStub);
1452 WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart);
1453 WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength);
1454 WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed);
1455 WLog_Print(log, level, "RASessionId: %s", file->RASessionId);
1456 WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams);
1457 WLog_Print(log, level, "RASpecificParams2: %s", file->RASpecificParams2);
1458
1459 for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++)
1460 {
1461 UINT32 port = 0;
1462 const char* uri = NULL;
1463 const char* addr = ArrayList_GetItem(file->MachineAddresses, x);
1464 if (x < ArrayList_Count(file->MachinePorts))
1465 {
1466 union
1467 {
1468 uintptr_t port;
1469 void* data;
1470 } cnv;
1471 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1472 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1473 port = (UINT32)cnv.port;
1474 }
1475 if (x < ArrayList_Count(file->MachineUris))
1476 uri = ArrayList_GetItem(file->MachineUris, x);
1477
1478 WLog_Print(log, level, "MachineAddress [%" PRIuz ": %s", x, addr);
1479 WLog_Print(log, level, "MachinePort [%" PRIuz ": %" PRIu32, x, port);
1480 WLog_Print(log, level, "MachineURI [%" PRIuz ": %s", x, uri);
1481 }
1482}
1483
1484BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd,
1485 size_t* size)
1486{
1487 if (!file || !pwd || !size)
1488 return FALSE;
1489
1490 *pwd = (const char*)file->EncryptedPassStub;
1491 *size = file->EncryptedPassStubLength;
1492 return TRUE;
1493}
1494
1495int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string,
1496 const char* password)
1497{
1498 if (!file || !string || !password)
1499 return -1;
1500
1501 char* str = _strdup(string);
1502 if (!str)
1503 return -1;
1504
1505 if (!update_connectionstring2_nocopy(file, str))
1506 return -1;
1507 if (!update_password(file, password))
1508 return -1;
1509 return freerdp_assistance_parse_connection_string2(file);
1510}
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