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