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 const size_t length = strlen(str);
345
346 int count = 1;
347 for (size_t i = 0; i < length; i++)
348 {
349 if (str[i] == ',')
350 count++;
351 }
352
353 if (count != 8)
354 goto error;
355
356 count = 0;
357 tokens[count++] = str;
358
359 for (size_t i = 0; i < length; i++)
360 {
361 if (str[i] == ',')
362 {
363 str[i] = '\0';
364 tokens[count++] = &str[i + 1];
365 }
366 }
367
368 if (strcmp(tokens[0], "65538") != 0)
369 goto error;
370
371 if (strcmp(tokens[1], "1") != 0)
372 goto error;
373
374 if (strcmp(tokens[3], "*") != 0)
375 goto error;
376
377 if (strcmp(tokens[5], "*") != 0)
378 goto error;
379
380 if (strcmp(tokens[6], "*") != 0)
381 goto error;
382
383 file->RASessionId = _strdup(tokens[4]);
384
385 if (!file->RASessionId)
386 goto error;
387
388 file->RASpecificParams = _strdup(tokens[7]);
389
390 if (!file->RASpecificParams)
391 goto error;
392
393 if (!freerdp_assistance_parse_address_list(file, tokens[2]))
394 goto error;
395
396 rc = TRUE;
397error:
398 free(str);
399 return rc;
400}
401
415static BOOL freerdp_assistance_parse_attr(const char** opt, size_t* plength, const char* key,
416 const char* tag)
417{
418 WINPR_ASSERT(opt);
419 WINPR_ASSERT(plength);
420 WINPR_ASSERT(key);
421
422 *opt = NULL;
423 *plength = 0;
424 if (!tag)
425 return FALSE;
426
427 char bkey[128] = { 0 };
428 const int rc = _snprintf(bkey, sizeof(bkey), "%s=\"", key);
429 WINPR_ASSERT(rc > 0);
430 WINPR_ASSERT((size_t)rc < sizeof(bkey));
431 if ((rc <= 0) || ((size_t)rc >= sizeof(bkey)))
432 return FALSE;
433
434 char* p = strstr(tag, bkey);
435 if (!p)
436 return TRUE;
437
438 p += strlen(bkey);
439 char* q = strchr(p, '"');
440
441 if (!q)
442 {
443 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field '%s=%s'",
444 key, p);
445 return FALSE;
446 }
447
448 if (p > q)
449 {
450 WLog_ERR(TAG,
451 "Failed to parse ASSISTANCE file: ConnectionString2 invalid field "
452 "order for '%s'",
453 key);
454 return FALSE;
455 }
456 const size_t length = WINPR_ASSERTING_INT_CAST(size_t, q - p);
457 *opt = p;
458 *plength = length;
459
460 return TRUE;
461}
462
463static BOOL freerdp_assistance_parse_attr_str(char** opt, const char* key, const char* tag)
464{
465 const char* copt = NULL;
466 size_t size = 0;
467 if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
468 return FALSE;
469 return update_option(opt, copt, size);
470}
471
472static BOOL freerdp_assistance_parse_attr_bool(BOOL* opt, const char* key, const char* tag)
473{
474 const char* copt = NULL;
475 size_t size = 0;
476
477 WINPR_ASSERT(opt);
478 *opt = FALSE;
479
480 if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
481 return FALSE;
482 if (size != 1)
483 return TRUE;
484
485 *opt = (copt[0] == '1');
486 return TRUE;
487}
488
489static BOOL freerdp_assistance_parse_attr_uint32(UINT32* opt, const char* key, const char* tag)
490{
491 const char* copt = NULL;
492 size_t size = 0;
493
494 WINPR_ASSERT(opt);
495 *opt = 0;
496
497 if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
498 return FALSE;
499
500 char buffer[64] = { 0 };
501 if ((!copt && (size > 0)) || (size >= sizeof(buffer)))
502 {
503 WLog_WARN(TAG, "Invalid UINT32 string '%s' [%" PRIuz "]", copt, size);
504 return FALSE;
505 }
506
507 if (size > 0)
508 strncpy(buffer, copt, size);
509
510 errno = 0;
511 unsigned long val = strtoul(buffer, NULL, 0);
512
513 if ((errno != 0) || (val > UINT32_MAX))
514 {
515 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid value %s", buffer);
516 return FALSE;
517 }
518
519 *opt = (UINT32)val;
520
521 return TRUE;
522}
523
524static char* freerdp_assistance_contains_element(char* input, size_t ilen, const char* key,
525 size_t* plen, char** pdata, size_t* pdlen)
526{
527 WINPR_ASSERT(input);
528 WINPR_ASSERT(key);
529 WINPR_ASSERT(plen);
530
531 char bkey[128] = { 0 };
532 const int rc = _snprintf(bkey, sizeof(bkey), "<%s", key);
533 WINPR_ASSERT(rc > 0);
534 WINPR_ASSERT((size_t)rc < sizeof(bkey));
535 if ((rc < 0) || ((size_t)rc >= sizeof(bkey)))
536 return NULL;
537
538 char* tag = strstr(input, bkey);
539 if (!tag || (tag > input + ilen))
540 return NULL;
541
542 char* data = tag + strnlen(bkey, sizeof(bkey));
543
544 /* Ensure there is a valid delimiter following our token */
545 switch (data[0])
546 {
547 case '>':
548 case '/':
549 case ' ':
550 case '\t':
551 break;
552 default:
553 WLog_ERR(TAG,
554 "Failed to parse ASSISTANCE file: ConnectionString2 missing delimiter after "
555 "field %s",
556 bkey);
557 return NULL;
558 }
559
560 char* start = strstr(tag, ">");
561
562 if (!start || (start > input + ilen))
563 {
564 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field %s", bkey);
565 return NULL;
566 }
567
568 const char* end = start;
569 const char* dend = start - 1;
570 if (*dend != '/')
571 {
572 char ekey[128] = { 0 };
573 const int erc = _snprintf(ekey, sizeof(ekey), "</%s>", key);
574 WINPR_ASSERT(erc > 0);
575 WINPR_ASSERT((size_t)erc < sizeof(ekey));
576 if ((erc <= 0) || ((size_t)erc >= sizeof(ekey)))
577 return NULL;
578 const size_t offset = WINPR_ASSERTING_INT_CAST(size_t, start - tag);
579 dend = end = strrstr(start, ilen - offset, ekey);
580 if (end)
581 end += strnlen(ekey, sizeof(ekey));
582 }
583
584 if (!end)
585 {
586 WLog_ERR(TAG,
587 "Failed to parse ASSISTANCE file: ConnectionString2 missing end tag for field %s",
588 key);
589 return NULL;
590 }
591 if (plen)
592 *plen = WINPR_ASSERTING_INT_CAST(size_t, end - tag);
593
594 if (pdata)
595 *pdata = data;
596 if (pdlen)
597 *pdlen = WINPR_ASSERTING_INT_CAST(size_t, dend - data);
598 return tag;
599}
600
606static BOOL freerdp_assistance_consume_input_and_get_element(char* input, const char* key,
607 char** element, size_t* elen)
608{
609 WINPR_ASSERT(input);
610 WINPR_ASSERT(key);
611 WINPR_ASSERT(element);
612 WINPR_ASSERT(elen);
613
614 size_t len = 0;
615 size_t dlen = 0;
616 char* data = NULL;
617 char* tag = freerdp_assistance_contains_element(input, strlen(input), key, &len, &data, &dlen);
618 if (!tag)
619 return FALSE;
620
621 char* end = data + dlen;
622 *tag = '\0';
623 *end = '\0';
624 *element = data;
625 *elen = dlen + 1;
626 return TRUE;
627}
628
629static BOOL freerdp_assistance_get_element(char* input, size_t ilen, const char* key,
630 char** element, size_t* elen)
631{
632 WINPR_ASSERT(input);
633 WINPR_ASSERT(key);
634 WINPR_ASSERT(element);
635 WINPR_ASSERT(elen);
636
637 size_t len = 0;
638 size_t dlen = 0;
639 char* data = NULL;
640 char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen);
641 if (!tag)
642 return FALSE;
643
644 if (tag + len > input + ilen)
645 return FALSE;
646
647 char* end = tag + len;
648 *element = data;
649 *elen = WINPR_ASSERTING_INT_CAST(size_t, end - data + 1);
650 return TRUE;
651}
652
653static BOOL freerdp_assistance_parse_all_elements_of(rdpAssistanceFile* file, char* data,
654 size_t len, const char* key,
655 BOOL (*fkt)(rdpAssistanceFile* file,
656 char* data, size_t len))
657{
658 char* val = NULL;
659 size_t vlen = 0;
660
661 while (freerdp_assistance_get_element(data, len, key, &val, &vlen))
662 {
663 data = val + vlen;
664 len = strnlen(data, len);
665 if (vlen > 0)
666 {
667 val[vlen - 1] = '\0';
668
669 if (!fkt(file, val, vlen))
670 return FALSE;
671 }
672 }
673
674 return TRUE;
675}
676
677static BOOL freerdp_assistance_parse_all_elements_of_l(rdpAssistanceFile* file, char* data,
678 WINPR_ATTR_UNUSED size_t len)
679{
680 UINT32 p = 0;
681 const char* n = NULL;
682 const char* u = NULL;
683 size_t nlen = 0;
684 size_t ulen = 0;
685 if (!freerdp_assistance_parse_attr_uint32(&p, "P", data))
686 return FALSE;
687 if (!freerdp_assistance_parse_attr(&n, &nlen, "N", data))
688 return FALSE;
689 if (!freerdp_assistance_parse_attr(&u, &ulen, "U", data))
690 return FALSE;
691
692 if (n && (nlen > 0))
693 {
694 if (!append_address_to_list(file->MachineAddresses, n, nlen))
695 return FALSE;
696 if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
697 return FALSE;
698 }
699 if (u && (ulen > 0))
700 {
701 if (!append_address_to_list(file->MachineAddresses, u, ulen))
702 return FALSE;
703 if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
704 return FALSE;
705 }
706 return TRUE;
707}
708
709static BOOL freerdp_assistance_parse_all_elements_of_t(rdpAssistanceFile* file, char* data,
710 size_t len)
711{
712 UINT32 id = 0;
713 UINT32 sid = 0;
714 if (!freerdp_assistance_parse_attr_uint32(&id, "ID", data))
715 return FALSE;
716 if (!freerdp_assistance_parse_attr_uint32(&sid, "SID", data))
717 return FALSE;
718 WLog_DBG(TAG, "transport id=%" PRIu32 ", sid=%" PRIu32, id, sid);
719 return freerdp_assistance_parse_all_elements_of(file, data, len, "L",
720 freerdp_assistance_parse_all_elements_of_l);
721}
722
723static BOOL freerdp_assistance_parse_all_elements_of_c(rdpAssistanceFile* file, char* data,
724 size_t len)
725{
726 return freerdp_assistance_parse_all_elements_of(file, data, len, "T",
727 freerdp_assistance_parse_all_elements_of_t);
728}
729
730static BOOL freerdp_assistance_parse_find_elements_of_c(rdpAssistanceFile* file, char* data,
731 size_t len)
732{
733 return freerdp_assistance_parse_all_elements_of(file, data, len, "C",
734 freerdp_assistance_parse_all_elements_of_c);
735}
736
737static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
738{
739 BOOL rc = FALSE;
740
741 WINPR_ASSERT(file);
742
743 if (!file->ConnectionString2)
744 return FALSE;
745
746 char* str = _strdup(file->ConnectionString2);
747 if (!str)
748 goto out_fail;
749
750 char* e = NULL;
751 size_t elen = 0;
752 if (!freerdp_assistance_consume_input_and_get_element(str, "E", &e, &elen))
753 goto out_fail;
754
755 if (!e || (elen == 0))
756 goto out_fail;
757
758 char* a = NULL;
759 size_t alen = 0;
760 if (!freerdp_assistance_get_element(e, elen, "A", &a, &alen))
761 goto out_fail;
762
763 if (!a || (alen == 0))
764 goto out_fail;
765
766 if (!freerdp_assistance_parse_find_elements_of_c(file, e, elen))
767 goto out_fail;
768
769 /* '\0' terminate the detected XML elements so
770 * the parser can continue with terminated strings
771 */
772 a[alen] = '\0';
773 if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams, "KH", a))
774 goto out_fail;
775
776 if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams2, "KH2", a))
777 goto out_fail;
778
779 if (!freerdp_assistance_parse_attr_str(&file->RASessionId, "ID", a))
780 goto out_fail;
781
782 rc = TRUE;
783out_fail:
784 free(str);
785 return rc;
786}
787
788char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
789{
790 if (!name || !pass)
791 return NULL;
792
793 const size_t nameLength = strlen(name) + strlen("NAME=");
794 const size_t passLength = strlen(pass) + strlen("PASS=");
795 const size_t size = nameLength + passLength + 64;
796 char* ExpertBlob = (char*)calloc(1, size);
797
798 if (!ExpertBlob)
799 return NULL;
800
801 (void)sprintf_s(ExpertBlob, size, "%" PRIuz ";NAME=%s%" PRIuz ";PASS=%s", nameLength, name,
802 passLength, pass);
803 return ExpertBlob;
804}
805
806char* freerdp_assistance_generate_pass_stub(WINPR_ATTR_UNUSED DWORD flags)
807{
808 UINT32 nums[14];
809 char* passStub = NULL;
810 char set1[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*_";
811 char set2[12] = "!@#$&^*()-+=";
812 char set3[10] = "0123456789";
813 char set4[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
814 char set5[26] = "abcdefghijklmnopqrstuvwxyz";
815 passStub = (char*)malloc(15);
816
817 if (!passStub)
818 return NULL;
819
831 winpr_RAND(nums, sizeof(nums));
832 passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */
833 passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */
834 passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */
835 passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */
836 passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */
837 passStub[5] = set1[nums[5] % sizeof(set1)]; /* character 5 */
838 passStub[6] = set1[nums[6] % sizeof(set1)]; /* character 6 */
839 passStub[7] = set1[nums[7] % sizeof(set1)]; /* character 7 */
840 passStub[8] = set1[nums[8] % sizeof(set1)]; /* character 8 */
841 passStub[9] = set1[nums[9] % sizeof(set1)]; /* character 9 */
842 passStub[10] = set1[nums[10] % sizeof(set1)]; /* character 10 */
843 passStub[11] = set1[nums[11] % sizeof(set1)]; /* character 11 */
844 passStub[12] = set1[nums[12] % sizeof(set1)]; /* character 12 */
845 passStub[13] = set1[nums[13] % sizeof(set1)]; /* character 13 */
846 passStub[14] = '\0';
847 return passStub;
848}
849
850BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
851 size_t* pEncryptedSize)
852{
853 BOOL rc = 0;
854 size_t cbPasswordW = 0;
855 size_t cbPassStubW = 0;
856 BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH] = { 0 };
857 WINPR_RC4_CTX* rc4Ctx = NULL;
858 BYTE* pbIn = NULL;
859 BYTE* pbOut = NULL;
860 BYTE* res = NULL;
861 WCHAR* PasswordW = ConvertUtf8ToWCharAlloc(password, &cbPasswordW);
862 WCHAR* PassStubW = ConvertUtf8ToWCharAlloc(passStub, &cbPassStubW);
863
864 cbPasswordW = (cbPasswordW) * sizeof(WCHAR);
865 cbPassStubW = (cbPassStubW) * sizeof(WCHAR);
866 const size_t EncryptedSize = cbPassStubW + 4;
867
868 if (!PasswordW || !PassStubW)
869 goto fail;
870
871 if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*)PasswordHash,
872 sizeof(PasswordHash)))
873 goto fail;
874
875 pbIn = (BYTE*)calloc(1, EncryptedSize);
876 pbOut = (BYTE*)calloc(1, EncryptedSize);
877
878 if (!pbIn || !pbOut)
879 goto fail;
880
881 WINPR_ASSERT(cbPasswordW <= UINT32_MAX);
882 winpr_Data_Write_UINT32(pbIn, (UINT32)cbPassStubW);
883 CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
884 rc4Ctx = winpr_RC4_New(PasswordHash, sizeof(PasswordHash));
885
886 if (!rc4Ctx)
887 {
888 WLog_ERR(TAG, "winpr_Cipher_New failure");
889 goto fail;
890 }
891
892 rc = winpr_RC4_Update(rc4Ctx, EncryptedSize, pbIn, pbOut);
893
894 if (!rc)
895 {
896 WLog_ERR(TAG, "winpr_Cipher_Update failure");
897 goto fail;
898 }
899 res = pbOut;
900fail:
901 winpr_RC4_Free(rc4Ctx);
902 free(PasswordW);
903 free(PassStubW);
904 free(pbIn);
905 if (!res)
906 free(pbOut);
907 else
908 *pEncryptedSize = EncryptedSize;
909 return res;
910}
911
912static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file)
913{
914 BOOL rc = FALSE;
915 int status = 0;
916 size_t cbPasswordW = 0;
917 size_t cchOutW = 0;
918 WINPR_CIPHER_CTX* aesDec = NULL;
919 WCHAR* PasswordW = NULL;
920 BYTE* pbIn = NULL;
921 BYTE* pbOut = NULL;
922 size_t cbOut = 0;
923 size_t cbIn = 0;
924 size_t cbFinal = 0;
925 BYTE DerivedKey[WINPR_AES_BLOCK_SIZE] = { 0 };
926 BYTE InitializationVector[WINPR_AES_BLOCK_SIZE] = { 0 };
927 BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH] = { 0 };
928
929 WINPR_ASSERT(file);
930
931 if (!file->password)
932 return FALSE;
933
934 PasswordW = ConvertUtf8ToWCharAlloc(file->password, &cbPasswordW);
935 if (!PasswordW)
936 {
937 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
938 return FALSE;
939 }
940
941 cbPasswordW = (cbPasswordW) * sizeof(WCHAR);
942
943 if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash,
944 sizeof(PasswordHash)))
945 goto fail;
946
947 if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash), DerivedKey,
948 sizeof(DerivedKey)))
949 goto fail;
950
951 aesDec =
952 winpr_Cipher_NewEx(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, DerivedKey, sizeof(DerivedKey),
953 InitializationVector, sizeof(InitializationVector));
954
955 if (!aesDec)
956 goto fail;
957
958 cbOut = cbFinal = 0;
959 cbIn = file->EncryptedLHTicketLength;
960 pbIn = file->EncryptedLHTicket;
961 pbOut = (BYTE*)calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);
962
963 if (!pbOut)
964 goto fail;
965
966 if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
967 goto fail;
968
969 if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
970 {
971 WLog_ERR(TAG, "winpr_Cipher_Final failure");
972 goto fail;
973 }
974
975 cbOut += cbFinal;
976 cbFinal = 0;
977
978 union
979 {
980 const WCHAR* wc;
981 const BYTE* b;
982 } cnv;
983
984 cnv.b = pbOut;
985 cchOutW = cbOut / sizeof(WCHAR);
986
987 if (!update_connectionstring2_wchar(file, cnv.wc, cchOutW))
988 {
989 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
990 goto fail;
991 }
992
993 if (!freerdp_assistance_parse_connection_string2(file))
994 goto fail;
995
996 rc = TRUE;
997fail:
998 winpr_Cipher_Free(aesDec);
999 free(PasswordW);
1000 free(pbOut);
1001 WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
1002 return rc;
1003}
1004
1005BYTE* freerdp_assistance_hex_string_to_bin(const void* raw, size_t* size)
1006{
1007 BYTE* buffer = NULL;
1008 if (!raw || !size)
1009 return NULL;
1010 *size = 0;
1011 const size_t length = strlen(raw);
1012 buffer = calloc(length, sizeof(BYTE));
1013 if (!buffer)
1014 return NULL;
1015 const size_t rc = winpr_HexStringToBinBuffer(raw, length, buffer, length);
1016 if (rc == 0)
1017 {
1018 free(buffer);
1019 return NULL;
1020 }
1021 *size = rc;
1022 return buffer;
1023}
1024
1025char* freerdp_assistance_bin_to_hex_string(const void* raw, size_t size)
1026{
1027 return winpr_BinToHexString(raw, size, FALSE);
1028}
1029
1030static int freerdp_assistance_parse_uploadinfo(rdpAssistanceFile* file, char* uploadinfo,
1031 size_t uploadinfosize)
1032{
1033 const char escalated[9] = "Escalated";
1034 const size_t esclen = sizeof(escalated);
1035 const char* typestr = NULL;
1036 size_t typelen = 0;
1037
1038 if (!uploadinfo || (uploadinfosize == 0))
1039 return -1;
1040
1041 if (strnlen(uploadinfo, uploadinfosize) == uploadinfosize)
1042 {
1043 WLog_WARN(TAG, "UPLOADINFOR string is not '\0' terminated");
1044 return -1;
1045 }
1046
1047 if (!freerdp_assistance_parse_attr(&typestr, &typelen, "TYPE", uploadinfo))
1048 return -1;
1049
1050 if ((typelen != esclen) || (strncmp(typestr, escalated, esclen) != 0))
1051 {
1052 WLog_ERR(TAG,
1053 "Failed to parse ASSISTANCE file: Missing or invalid UPLOADINFO TYPE '%s' [%" PRIuz
1054 "]",
1055 typestr, typelen);
1056 return -1;
1057 }
1058
1059 char* uploaddata = NULL;
1060 size_t uploaddatasize = 0;
1061 if (!freerdp_assistance_consume_input_and_get_element(uploadinfo, "UPLOADDATA", &uploaddata,
1062 &uploaddatasize))
1063 return -1;
1064
1065 /* Parse USERNAME */
1066 if (!freerdp_assistance_parse_attr_str(&file->Username, "USERNAME", uploaddata))
1067 return -1;
1068
1069 /* Parse LHTICKET */
1070 if (!freerdp_assistance_parse_attr_str(&file->LHTicket, "LHTICKET", uploaddata))
1071 return -1;
1072
1073 /* Parse RCTICKET */
1074 if (!freerdp_assistance_parse_attr_str(&file->RCTicket, "RCTICKET", uploaddata))
1075 return -1;
1076
1077 /* Parse RCTICKETENCRYPTED */
1078 if (!freerdp_assistance_parse_attr_bool(&file->RCTicketEncrypted, "RCTICKETENCRYPTED",
1079 uploaddata))
1080 return -1;
1081
1082 /* Parse PassStub */
1083 if (!freerdp_assistance_parse_attr_str(&file->PassStub, "PassStub", uploaddata))
1084 return -1;
1085
1086 if (file->PassStub)
1087 {
1088 const char* amp = "&amp;";
1089 char* passtub = strstr(file->PassStub, amp);
1090 while (passtub)
1091 {
1092 const char* end = passtub + 5;
1093 const size_t len = strlen(end);
1094 memmove(&passtub[1], end, len + 1);
1095 passtub = strstr(passtub, amp);
1096 }
1097 }
1098
1099 /* Parse DtStart */
1100 if (!freerdp_assistance_parse_attr_uint32(&file->DtStart, "DtStart", uploaddata))
1101 return -1;
1102
1103 /* Parse DtLength */
1104 if (!freerdp_assistance_parse_attr_uint32(&file->DtLength, "DtLength", uploaddata))
1105 return -1;
1106
1107 /* Parse L (LowSpeed) */
1108 if (!freerdp_assistance_parse_attr_bool(&file->LowSpeed, "L", uploaddata))
1109 return -1;
1110
1111 file->Type = (file->LHTicket) ? 2 : 1;
1112 int status = 0;
1113
1114 switch (file->Type)
1115 {
1116 case 2:
1117 {
1118 file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(
1119 file->LHTicket, &file->EncryptedLHTicketLength);
1120
1121 if (!freerdp_assistance_decrypt2(file))
1122 status = -1;
1123 }
1124 break;
1125
1126 case 1:
1127 {
1128 if (!freerdp_assistance_parse_connection_string1(file))
1129 status = -1;
1130 }
1131 break;
1132
1133 default:
1134 return -1;
1135 }
1136
1137 if (status < 0)
1138 {
1139 WLog_ERR(TAG, "freerdp_assistance_parse_connection_string1 failure: %d", status);
1140 return -1;
1141 }
1142
1143 file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(file->password, file->PassStub,
1144 &file->EncryptedPassStubLength);
1145
1146 if (!file->EncryptedPassStub)
1147 return -1;
1148
1149 return 1;
1150}
1151
1152static int freerdp_assistance_parse_file_buffer_int(rdpAssistanceFile* file, char* buffer,
1153 size_t size, const char* password)
1154{
1155 WINPR_ASSERT(file);
1156 WINPR_ASSERT(buffer);
1157 WINPR_ASSERT(size > 0);
1158
1159 if (!update_password(file, password))
1160 return -1;
1161
1162 char* uploadinfo = NULL;
1163 size_t uploadinfosize = 0;
1164 if (freerdp_assistance_consume_input_and_get_element(buffer, "UPLOADINFO", &uploadinfo,
1165 &uploadinfosize))
1166 return freerdp_assistance_parse_uploadinfo(file, uploadinfo, uploadinfosize);
1167
1168 size_t elen = 0;
1169 const char* estr = freerdp_assistance_contains_element(buffer, size, "E", &elen, NULL, NULL);
1170 if (!estr || (elen == 0))
1171 {
1172 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor <E> found");
1173 return -1;
1174 }
1175 if (!update_connectionstring2(file, estr, elen))
1176 return -1;
1177
1178 if (!freerdp_assistance_parse_connection_string2(file))
1179 return -1;
1180
1181 return 1;
1182}
1183
1184int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* cbuffer, size_t size,
1185 const char* password)
1186{
1187 WINPR_ASSERT(file);
1188 if (!password)
1189 {
1190 WLog_WARN(TAG, "empty password supplied");
1191 }
1192
1193 if (!cbuffer || (size == 0))
1194 {
1195 WLog_WARN(TAG, "no data supplied [%p, %" PRIuz "]", cbuffer, size);
1196 return -1;
1197 }
1198
1199 char* abuffer = strndup(cbuffer, size);
1200 const size_t len = strnlen(cbuffer, size);
1201 if (len == size)
1202 WLog_WARN(TAG, "Input data not '\0' terminated");
1203
1204 if (!abuffer)
1205 return -1;
1206
1207 const int rc = freerdp_assistance_parse_file_buffer_int(file, abuffer, len + 1, password);
1208 free(abuffer);
1209 return rc;
1210}
1211
1212int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
1213{
1214 int status = 0;
1215 BYTE* buffer = NULL;
1216 FILE* fp = NULL;
1217 size_t readSize = 0;
1218 union
1219 {
1220 INT64 i64;
1221 size_t s;
1222 } fileSize;
1223
1224 if (!update_name(file, name))
1225 return -1;
1226
1227 fp = winpr_fopen(name, "r");
1228
1229 if (!fp)
1230 {
1231 WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name);
1232 return -1;
1233 }
1234
1235 (void)_fseeki64(fp, 0, SEEK_END);
1236 fileSize.i64 = _ftelli64(fp);
1237 (void)_fseeki64(fp, 0, SEEK_SET);
1238
1239 if (fileSize.i64 < 1)
1240 {
1241 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1242 (void)fclose(fp);
1243 return -1;
1244 }
1245
1246 buffer = (BYTE*)malloc(fileSize.s + 2);
1247
1248 if (!buffer)
1249 {
1250 (void)fclose(fp);
1251 return -1;
1252 }
1253
1254 readSize = fread(buffer, fileSize.s, 1, fp);
1255
1256 if (!readSize)
1257 {
1258 if (!ferror(fp))
1259 readSize = fileSize.s;
1260 }
1261
1262 (void)fclose(fp);
1263
1264 if (readSize < 1)
1265 {
1266 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1267 free(buffer);
1268 buffer = NULL;
1269 return -1;
1270 }
1271
1272 buffer[fileSize.s] = '\0';
1273 buffer[fileSize.s + 1] = '\0';
1274 status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize.s, password);
1275 free(buffer);
1276 return status;
1277}
1278
1279BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file,
1280 rdpSettings* settings)
1281{
1282 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1283 return FALSE;
1284
1285 if (!file->RASessionId || !file->MachineAddresses)
1286 return FALSE;
1287
1288 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId,
1289 file->RASessionId))
1290 return FALSE;
1291
1292 if (file->RCTicket)
1293 {
1294 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1295 file->RCTicket))
1296 return FALSE;
1297 }
1298 else
1299 {
1300 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1301 file->ConnectionString2))
1302 return FALSE;
1303 }
1304
1305 if (file->PassStub)
1306 {
1307 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub,
1308 file->PassStub))
1309 return FALSE;
1310 }
1311
1312 if (ArrayList_Count(file->MachineAddresses) < 1)
1313 return FALSE;
1314
1315 const char* addr = ArrayList_GetItem(file->MachineAddresses, 0);
1316 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, addr))
1317 return FALSE;
1318
1319 if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename))
1320 return FALSE;
1321
1322 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password))
1323 return FALSE;
1324
1325 if (file->Username)
1326 {
1327 if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username))
1328 return FALSE;
1329 }
1330
1331 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1332 return FALSE;
1333
1334 const size_t ports = ArrayList_Count(file->MachinePorts);
1335 const size_t addresses = ArrayList_Count(file->MachineAddresses);
1336 if (ports < 1)
1337 return FALSE;
1338 if (ports != addresses)
1339 return FALSE;
1340
1341 union
1342 {
1343 uintptr_t port;
1344 void* data;
1345 } cnv;
1346 cnv.data = ArrayList_GetItem(file->MachinePorts, 0);
1347 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1348 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT32)cnv.port))
1349 return FALSE;
1350
1351 if (!freerdp_target_net_adresses_reset(settings, ports))
1352 return FALSE;
1353
1354 for (size_t x = 0; x < ports; x++)
1355 {
1356 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1357 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1358 const UINT32 port = (UINT32)cnv.port;
1359 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port))
1360 return FALSE;
1361 }
1362 for (size_t i = 0; i < addresses; i++)
1363 {
1364 const char* maddr = ArrayList_GetItem(file->MachineAddresses, i);
1365 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr))
1366 return FALSE;
1367 }
1368
1369 return TRUE;
1370}
1371
1372static BOOL setup_string(wArrayList* list)
1373{
1374 WINPR_ASSERT(list);
1375
1376 wObject* obj = ArrayList_Object(list);
1377 if (!obj)
1378 return FALSE;
1379 obj->fnObjectFree = free;
1380 // obj->fnObjectNew = wwinpr_ObjectStringClone;
1381 return TRUE;
1382}
1383
1384rdpAssistanceFile* freerdp_assistance_file_new(void)
1385{
1386 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
1387 rdpAssistanceFile* file = calloc(1, sizeof(rdpAssistanceFile));
1388 if (!file)
1389 return NULL;
1390
1391 file->MachineAddresses = ArrayList_New(FALSE);
1392 file->MachinePorts = ArrayList_New(FALSE);
1393 file->MachineUris = ArrayList_New(FALSE);
1394
1395 if (!file->MachineAddresses || !file->MachinePorts || !file->MachineUris)
1396 goto fail;
1397
1398 if (!setup_string(file->MachineAddresses) || !setup_string(file->MachineUris))
1399 goto fail;
1400
1401 return file;
1402
1403fail:
1404 WINPR_PRAGMA_DIAG_PUSH
1405 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1406 freerdp_assistance_file_free(file);
1407 WINPR_PRAGMA_DIAG_POP
1408 return NULL;
1409}
1410
1411void freerdp_assistance_file_free(rdpAssistanceFile* file)
1412{
1413 if (!file)
1414 return;
1415
1416 update_password(file, NULL);
1417 update_connectionstring2(file, NULL, 0);
1418 free(file->filename);
1419 free(file->Username);
1420 free(file->LHTicket);
1421 free(file->RCTicket);
1422 free(file->PassStub);
1423 free(file->ConnectionString1);
1424 free(file->EncryptedLHTicket);
1425 free(file->RASessionId);
1426 free(file->RASpecificParams);
1427 free(file->RASpecificParams2);
1428 free(file->EncryptedPassStub);
1429
1430 ArrayList_Free(file->MachineAddresses);
1431 ArrayList_Free(file->MachinePorts);
1432 ArrayList_Free(file->MachineUris);
1433 free(file);
1434}
1435
1436void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level)
1437{
1438 WINPR_ASSERT(file);
1439
1440 WLog_Print(log, level, "Username: %s", file->Username);
1441 WLog_Print(log, level, "LHTicket: %s", file->LHTicket);
1442 WLog_Print(log, level, "RCTicket: %s", file->RCTicket);
1443 WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted);
1444 WLog_Print(log, level, "PassStub: %s", file->PassStub);
1445 WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart);
1446 WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength);
1447 WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed);
1448 WLog_Print(log, level, "RASessionId: %s", file->RASessionId);
1449 WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams);
1450 WLog_Print(log, level, "RASpecificParams2: %s", file->RASpecificParams2);
1451
1452 for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++)
1453 {
1454 UINT32 port = 0;
1455 const char* uri = NULL;
1456 const char* addr = ArrayList_GetItem(file->MachineAddresses, x);
1457 if (x < ArrayList_Count(file->MachinePorts))
1458 {
1459 union
1460 {
1461 uintptr_t port;
1462 void* data;
1463 } cnv;
1464 cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1465 WINPR_ASSERT(cnv.port <= UINT32_MAX);
1466 port = (UINT32)cnv.port;
1467 }
1468 if (x < ArrayList_Count(file->MachineUris))
1469 uri = ArrayList_GetItem(file->MachineUris, x);
1470
1471 WLog_Print(log, level, "MachineAddress [%" PRIdz ": %s", x, addr);
1472 WLog_Print(log, level, "MachinePort [%" PRIdz ": %" PRIu32, x, port);
1473 WLog_Print(log, level, "MachineURI [%" PRIdz ": %s", x, uri);
1474 }
1475}
1476
1477BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd,
1478 size_t* size)
1479{
1480 if (!file || !pwd || !size)
1481 return FALSE;
1482
1483 *pwd = (const char*)file->EncryptedPassStub;
1484 *size = file->EncryptedPassStubLength;
1485 return TRUE;
1486}
1487
1488int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string,
1489 const char* password)
1490{
1491 if (!file || !string || !password)
1492 return -1;
1493
1494 char* str = _strdup(string);
1495 if (!str)
1496 return -1;
1497
1498 if (!update_connectionstring2_nocopy(file, str))
1499 return -1;
1500 if (!update_password(file, password))
1501 return -1;
1502 return freerdp_assistance_parse_connection_string2(file);
1503}
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