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 = WINPR_ASSERTING_INT_CAST(size_t, 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 ((!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 
524 static 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 
606 static 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 
629 static 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 
653 static 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 
677 static BOOL freerdp_assistance_parse_all_elements_of_l(rdpAssistanceFile* file, char* data,
678  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 
709 static 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 
723 static 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 
730 static 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 
737 static 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;
783 out_fail:
784  free(str);
785  return rc;
786 }
787 
788 char* 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 
806 char* freerdp_assistance_generate_pass_stub(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 
850 BYTE* 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;
900 fail:
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 
912 static 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;
997 fail:
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 
1005 BYTE* 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 
1025 char* freerdp_assistance_bin_to_hex_string(const void* raw, size_t size)
1026 {
1027  return winpr_BinToHexString(raw, size, FALSE);
1028 }
1029 
1030 static 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 
1152 static 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 
1184 int 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 
1212 int 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 
1279 BOOL 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 
1372 static 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 
1384 rdpAssistanceFile* 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 
1403 fail:
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 
1411 void 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 
1436 void 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 
1477 BOOL 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 
1488 int 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