Coverage Report

Created: 2026-10-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/common/assistance.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Remote Assistance
4
 *
5
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
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
WINPR_ATTR_NODISCARD
79
static const char* strrstr(const char* haystack, size_t len, const char* needle)
80
3.88k
{
81
3.88k
  if (*needle == '\0')
82
0
    return haystack;
83
84
3.88k
  const char* result = nullptr;
85
3.88k
  for (;;)
86
11.0k
  {
87
11.0k
    const char* p = strstr(haystack, needle);
88
11.0k
    if (p == nullptr)
89
3.88k
      break;
90
7.12k
    if (p > haystack + len)
91
0
      return nullptr;
92
93
7.12k
    result = p;
94
7.12k
    haystack = p + 1;
95
7.12k
  }
96
97
3.88k
  return result;
98
3.88k
}
99
100
WINPR_ATTR_NODISCARD
101
static BOOL update_option(char** opt, const char* val, size_t len)
102
6.00k
{
103
6.00k
  WINPR_ASSERT(opt);
104
6.00k
  free(*opt);
105
6.00k
  *opt = nullptr;
106
107
6.00k
  if (!val && (len != 0))
108
0
    return FALSE;
109
6.00k
  else if (!val && (len == 0))
110
4.58k
    return TRUE;
111
1.41k
  *opt = strndup(val, len);
112
1.41k
  return *opt != nullptr;
113
6.00k
}
114
115
WINPR_ATTR_NODISCARD
116
static BOOL update_name(rdpAssistanceFile* file, const char* name)
117
0
{
118
0
  WINPR_ASSERT(file);
119
120
0
  if (!name)
121
0
  {
122
0
    WLog_ERR(TAG, "ASSISTANCE file %s invalid name", name);
123
0
    return FALSE;
124
0
  }
125
126
0
  free(file->filename);
127
0
  file->filename = _strdup(name);
128
0
  return file->filename != nullptr;
129
0
}
130
131
WINPR_ATTR_NODISCARD
132
static BOOL update_password(rdpAssistanceFile* file, const char* password)
133
2.20k
{
134
2.20k
  WINPR_ASSERT(file);
135
2.20k
  free(file->password);
136
2.20k
  file->password = nullptr;
137
2.20k
  if (!password)
138
0
    return TRUE;
139
2.20k
  file->password = _strdup(password);
140
2.20k
  return file->password != nullptr;
141
2.20k
}
142
143
WINPR_ATTR_NODISCARD
144
static BOOL update_connectionstring2_nocopy(rdpAssistanceFile* file, char* str)
145
922
{
146
922
  WINPR_ASSERT(file);
147
922
  free(file->ConnectionString2);
148
922
  file->ConnectionString2 = nullptr;
149
922
  if (!str)
150
1
    return TRUE;
151
921
  file->ConnectionString2 = str;
152
921
  return file->ConnectionString2 != nullptr;
153
922
}
154
155
WINPR_ATTR_NODISCARD
156
static BOOL update_connectionstring2(rdpAssistanceFile* file, const char* str, size_t len)
157
753
{
158
753
  char* strc = nullptr;
159
753
  if (!str && (len != 0))
160
0
    return FALSE;
161
162
753
  if (str && (len > 0))
163
753
  {
164
753
    strc = strndup(str, len);
165
753
    if (!strc)
166
0
      return FALSE;
167
753
  }
168
753
  return update_connectionstring2_nocopy(file, strc);
169
753
}
170
171
WINPR_ATTR_NODISCARD
172
static BOOL update_connectionstring2_wchar(rdpAssistanceFile* file, const WCHAR* str, size_t len)
173
252
{
174
252
  char* strc = nullptr;
175
176
252
  if (!str && (len != 0))
177
0
    return FALSE;
178
179
252
  if (str && (len > 0))
180
251
  {
181
251
    strc = ConvertWCharNToUtf8Alloc(str, len, nullptr);
182
251
    if (!strc)
183
83
      return FALSE;
184
251
  }
185
169
  return update_connectionstring2_nocopy(file, strc);
186
252
}
187
188
/**
189
 * Password encryption in establishing a remote assistance session of type 1:
190
 * http://blogs.msdn.com/b/openspecification/archive/2011/10/31/password-encryption-in-establishing-a-remote-assistance-session-of-type-1.aspx
191
 *
192
 * Creation of PassStub for the Remote Assistance Ticket:
193
 * http://social.msdn.microsoft.com/Forums/en-US/6316c3f4-ea09-4343-a4a1-9cca46d70d28/creation-of-passstub-for-the-remote-assistance-ticket?forum=os_windowsprotocols
194
 */
195
196
/**
197
 * CryptDeriveKey Function:
198
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916/
199
 *
200
 * Let n be the required derived key length, in bytes.
201
 * The derived key is the first n bytes of the hash value after the hash computation
202
 * has been completed by CryptDeriveKey. If the hash is not a member of the SHA-2
203
 * family and the required key is for either 3DES or AES, the key is derived as follows:
204
 *
205
 * Form a 64-byte buffer by repeating the constant 0x36 64 times.
206
 * Let k be the length of the hash value that is represented by the input parameter hBaseData.
207
 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
208
 * of the buffer with the hash value that is represented by the input parameter hBaseData.
209
 *
210
 * Form a 64-byte buffer by repeating the constant 0x5C 64 times.
211
 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
212
 * of the buffer with the hash value that is represented by the input parameter hBaseData.
213
 *
214
 * Hash the result of step 1 by using the same hash algorithm as that used to compute the hash
215
 * value that is represented by the hBaseData parameter.
216
 *
217
 * Hash the result of step 2 by using the same hash algorithm as that used to compute the hash
218
 * value that is represented by the hBaseData parameter.
219
 *
220
 * Concatenate the result of step 3 with the result of step 4.
221
 * Use the first n bytes of the result of step 5 as the derived key.
222
 */
223
WINPR_ATTR_NODISCARD
224
static BOOL freerdp_assistance_crypt_derive_key_sha1(const BYTE* hash, size_t hashLength, BYTE* key,
225
                                                     size_t keyLength)
226
453
{
227
453
  BOOL rc = FALSE;
228
453
  BYTE pad1[64] = WINPR_C_ARRAY_INIT;
229
453
  BYTE pad2[64] = WINPR_C_ARRAY_INIT;
230
231
453
  if (hashLength == 0)
232
0
    return FALSE;
233
234
453
  memset(pad1, 0x36, sizeof(pad1));
235
453
  memset(pad2, 0x5C, sizeof(pad2));
236
237
9.51k
  for (size_t i = 0; i < hashLength; i++)
238
9.06k
  {
239
9.06k
    pad1[i] ^= hash[i];
240
9.06k
    pad2[i] ^= hash[i];
241
9.06k
  }
242
243
453
  BYTE* buffer = (BYTE*)calloc(hashLength, 2);
244
245
453
  if (!buffer)
246
0
    goto fail;
247
248
453
  if (!winpr_Digest(WINPR_MD_SHA1, pad1, 64, buffer, hashLength))
249
0
    goto fail;
250
251
453
  if (!winpr_Digest(WINPR_MD_SHA1, pad2, 64, &buffer[hashLength], hashLength))
252
0
    goto fail;
253
254
453
  CopyMemory(key, buffer, keyLength);
255
453
  rc = TRUE;
256
453
fail:
257
453
  free(buffer);
258
453
  return rc;
259
453
}
260
261
WINPR_ATTR_NODISCARD
262
static BOOL append_address_to_list(wArrayList* MachineAddresses, const char* str, size_t len)
263
595k
{
264
595k
  char* copy = nullptr;
265
595k
  if (len > 0)
266
595k
    copy = strndup(str, len);
267
595k
  if (!copy)
268
30
    return FALSE;
269
270
595k
  const BOOL rc = ArrayList_Append(MachineAddresses, copy);
271
595k
  if (!rc)
272
0
    free(copy);
273
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of copy
274
595k
  return rc;
275
595k
}
276
277
WINPR_ATTR_NODISCARD
278
static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port)
279
593k
{
280
593k
  WINPR_ASSERT(file);
281
282
593k
  errno = 0;
283
593k
  unsigned long p = strtoul(port, nullptr, 0);
284
285
593k
  if ((errno != 0) || (p == 0) || (p > UINT16_MAX))
286
135
  {
287
135
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid port value %s",
288
135
             port);
289
135
    return FALSE;
290
135
  }
291
292
593k
  if (!append_address_to_list(file->MachineAddresses, host, host ? strlen(host) : 0))
293
30
    return FALSE;
294
593k
  return ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p);
295
593k
}
296
297
WINPR_ATTR_NODISCARD
298
static BOOL freerdp_assistance_parse_address_list(rdpAssistanceFile* file, char* list)
299
499
{
300
499
  WINPR_ASSERT(file);
301
302
499
  WLog_DBG(TAG, "freerdp_assistance_parse_address_list list=%s", list);
303
304
499
  BOOL rc = FALSE;
305
306
499
  if (!list)
307
0
    return FALSE;
308
309
499
  char* strp = list;
310
499
  char* s = ";";
311
312
  // get the first token
313
499
  char* saveptr = nullptr;
314
499
  char* token = strtok_s(strp, s, &saveptr);
315
316
  // walk through other tokens
317
593k
  while (token != nullptr)
318
593k
  {
319
593k
    char* port = strchr(token, ':');
320
593k
    if (!port)
321
12
      goto out;
322
593k
    *port = '\0';
323
593k
    port++;
324
325
593k
    if (!append_address(file, token, port))
326
165
      goto out;
327
328
593k
    token = strtok_s(nullptr, s, &saveptr);
329
593k
  }
330
322
  rc = TRUE;
331
499
out:
332
499
  return rc;
333
322
}
334
335
WINPR_ATTR_NODISCARD
336
static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file)
337
643
{
338
643
  char* tokens[8] = WINPR_C_ARRAY_INIT;
339
643
  BOOL rc = FALSE;
340
341
643
  WINPR_ASSERT(file);
342
343
643
  if (!file->RCTicket)
344
36
    return FALSE;
345
346
  /**
347
   * <ProtocolVersion>,<protocolType>,<machineAddressList>,<assistantAccountPwd>,
348
   * <RASessionID>,<RASessionName>,<RASessionPwd>,<protocolSpecificParms>
349
   */
350
607
  char* str = _strdup(file->RCTicket);
351
352
607
  if (!str)
353
0
    goto error;
354
355
607
  {
356
607
    const size_t length = strlen(str);
357
358
607
    {
359
607
      int count = 1;
360
4.77M
      for (size_t i = 0; i < length; i++)
361
4.77M
      {
362
4.77M
        if (str[i] == ',')
363
91.9k
          count++;
364
4.77M
      }
365
366
607
      if (count != 8)
367
37
        goto error;
368
607
    }
369
370
570
    {
371
570
      size_t count = 0;
372
570
      tokens[count++] = str;
373
374
4.63M
      for (size_t i = 0; i < length; i++)
375
4.63M
      {
376
4.63M
        if (str[i] == ',')
377
3.99k
        {
378
3.99k
          str[i] = '\0';
379
3.99k
          tokens[count++] = &str[i + 1];
380
3.99k
        }
381
4.63M
      }
382
570
    }
383
570
  }
384
385
570
  if (strcmp(tokens[0], "65538") != 0)
386
60
    goto error;
387
388
510
  if (strcmp(tokens[1], "1") != 0)
389
4
    goto error;
390
391
506
  if (strcmp(tokens[3], "*") != 0)
392
2
    goto error;
393
394
504
  if (strcmp(tokens[5], "*") != 0)
395
3
    goto error;
396
397
501
  if (strcmp(tokens[6], "*") != 0)
398
2
    goto error;
399
400
499
  file->RASessionId = _strdup(tokens[4]);
401
402
499
  if (!file->RASessionId)
403
0
    goto error;
404
405
499
  file->RASpecificParams = _strdup(tokens[7]);
406
407
499
  if (!file->RASpecificParams)
408
0
    goto error;
409
410
499
  if (!freerdp_assistance_parse_address_list(file, tokens[2]))
411
177
    goto error;
412
413
322
  rc = TRUE;
414
607
error:
415
607
  free(str);
416
607
  return rc;
417
322
}
418
419
/**
420
 * Decrypted Connection String 2:
421
 *
422
 * <E>
423
 * <A KH="BNRjdu97DyczQSRuMRrDWoue+HA="
424
 * ID="+ULZ6ifjoCa6cGPMLQiGHRPwkg6VyJqGwxMnO6GcelwUh9a6/FBq3It5ADSndmLL"/> <C> <T ID="1" SID="0"> <L
425
 * P="49228" N="fe80::1032:53d9:5a01:909b%3"/> <L P="49229" N="fe80::3d8f:9b2d:6b4e:6aa%6"/> <L
426
 * P="49230" N="192.168.1.200"/> <L P="49231" N="169.254.6.170"/>
427
 * </T>
428
 * </C>
429
 * </E>
430
 */
431
WINPR_ATTR_NODISCARD
432
static BOOL freerdp_assistance_parse_attr(const char** opt, size_t* plength, const char* key,
433
                                          const char* tag)
434
35.2k
{
435
35.2k
  WINPR_ASSERT(opt);
436
35.2k
  WINPR_ASSERT(plength);
437
35.2k
  WINPR_ASSERT(key);
438
439
35.2k
  *opt = nullptr;
440
35.2k
  *plength = 0;
441
35.2k
  if (!tag)
442
0
    return FALSE;
443
444
35.2k
  char bkey[128] = WINPR_C_ARRAY_INIT;
445
35.2k
  const int rc = _snprintf(bkey, sizeof(bkey), "%s=\"", key);
446
35.2k
  WINPR_ASSERT(rc > 0);
447
35.2k
  WINPR_ASSERT((size_t)rc < sizeof(bkey));
448
35.2k
  if ((rc <= 0) || ((size_t)rc >= sizeof(bkey)))
449
0
    return FALSE;
450
451
35.2k
  const char* p = strstr(tag, bkey);
452
35.2k
  if (!p)
453
25.3k
    return TRUE;
454
455
9.94k
  p += strlen(bkey);
456
9.94k
  const char* q = strchr(p, '"');
457
458
9.94k
  if (!q)
459
101
  {
460
101
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field '%s=%s'",
461
101
             key, p);
462
101
    return FALSE;
463
101
  }
464
465
9.84k
  if (p > q)
466
0
  {
467
0
    WLog_ERR(TAG,
468
0
             "Failed to parse ASSISTANCE file: ConnectionString2 invalid field "
469
0
             "order for '%s'",
470
0
             key);
471
0
    return FALSE;
472
0
  }
473
9.84k
  const size_t length = WINPR_ASSERTING_INT_CAST(size_t, q - p);
474
9.84k
  *opt = p;
475
9.84k
  *plength = length;
476
477
9.84k
  return TRUE;
478
9.84k
}
479
480
WINPR_ATTR_NODISCARD
481
static BOOL freerdp_assistance_parse_attr_str(char** opt, const char* key, const char* tag)
482
6.06k
{
483
6.06k
  const char* copt = nullptr;
484
6.06k
  size_t size = 0;
485
6.06k
  if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
486
58
    return FALSE;
487
6.00k
  return update_option(opt, copt, size);
488
6.06k
}
489
490
WINPR_ATTR_NODISCARD
491
static BOOL freerdp_assistance_parse_attr_bool(BOOL* opt, const char* key, const char* tag)
492
2.20k
{
493
2.20k
  const char* copt = nullptr;
494
2.20k
  size_t size = 0;
495
496
2.20k
  WINPR_ASSERT(opt);
497
2.20k
  *opt = FALSE;
498
499
2.20k
  if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
500
2
    return FALSE;
501
2.20k
  if (size != 1)
502
2.18k
    return TRUE;
503
504
13
  *opt = (copt[0] == '1');
505
13
  return TRUE;
506
2.20k
}
507
508
WINPR_ATTR_NODISCARD
509
static BOOL freerdp_assistance_parse_attr_uint32(UINT32* opt, const char* key, const char* tag)
510
13.1k
{
511
13.1k
  const char* copt = nullptr;
512
13.1k
  size_t size = 0;
513
514
13.1k
  WINPR_ASSERT(opt);
515
13.1k
  *opt = 0;
516
517
13.1k
  if (!freerdp_assistance_parse_attr(&copt, &size, key, tag))
518
17
    return FALSE;
519
520
13.1k
  char buffer[64] = WINPR_C_ARRAY_INIT;
521
13.1k
  if ((!copt && (size > 0)) || (size >= sizeof(buffer)))
522
33
  {
523
33
    WLog_WARN(TAG, "Invalid UINT32 string '%s' [%" PRIuz "]", copt, size);
524
33
    return FALSE;
525
33
  }
526
527
13.0k
  if (size > 0)
528
1.12k
    strncpy(buffer, copt, size);
529
530
13.0k
  errno = 0;
531
13.0k
  unsigned long val = strtoul(buffer, nullptr, 0);
532
533
13.0k
  if ((errno != 0) || (val > UINT32_MAX))
534
80
  {
535
80
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid value %s", buffer);
536
80
    return FALSE;
537
80
  }
538
539
12.9k
  *opt = (UINT32)val;
540
541
12.9k
  return TRUE;
542
13.0k
}
543
544
WINPR_ATTR_NODISCARD
545
static char* freerdp_assistance_contains_element(char* input, size_t ilen, const char* key,
546
                                                 size_t* plen, char** pdata, size_t* pdlen)
547
224k
{
548
224k
  WINPR_ASSERT(input);
549
224k
  WINPR_ASSERT(key);
550
224k
  WINPR_ASSERT(plen);
551
552
224k
  char bkey[128] = WINPR_C_ARRAY_INIT;
553
224k
  const int rc = _snprintf(bkey, sizeof(bkey), "<%s", key);
554
224k
  WINPR_ASSERT(rc > 0);
555
224k
  WINPR_ASSERT((size_t)rc < sizeof(bkey));
556
224k
  if ((rc < 0) || ((size_t)rc >= sizeof(bkey)))
557
0
    return nullptr;
558
559
224k
  char* tag = winpr_strnstr(input, bkey, ilen);
560
224k
  if (!tag)
561
11.3k
    return nullptr;
562
563
212k
  const intptr_t tdiff = (tag - input);
564
212k
  WINPR_ASSERT(tdiff >= 0);
565
212k
  const size_t utdiff = WINPR_ASSERTING_INT_CAST(size_t, tdiff);
566
212k
  WINPR_ASSERT(utdiff <= ilen);
567
212k
  const size_t tlen = ilen - utdiff;
568
212k
  const size_t texpect = strnlen(bkey, sizeof(bkey));
569
212k
  if (tlen <= texpect)
570
2
    return nullptr;
571
572
212k
  char* data = tag + texpect;
573
574
  /* Ensure there is a valid delimiter following our token */
575
212k
  switch (data[0])
576
212k
  {
577
3.28k
    case '>':
578
43.8k
    case '/':
579
123k
    case ' ':
580
125k
    case '\t':
581
125k
      break;
582
87.7k
    default:
583
87.7k
      WLog_ERR(TAG,
584
87.7k
               "Failed to parse ASSISTANCE file: ConnectionString2 missing delimiter after "
585
87.7k
               "field %s",
586
87.7k
               bkey);
587
87.7k
      return nullptr;
588
212k
  }
589
590
125k
  const char* start = winpr_strnstr(tag, ">", tlen);
591
592
125k
  if (!start || (start > input + ilen))
593
8.14k
  {
594
8.14k
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field %s", bkey);
595
8.14k
    return nullptr;
596
8.14k
  }
597
598
117k
  const char* end = start;
599
117k
  const char* dend = start - 1;
600
117k
  if (*dend != '/')
601
3.88k
  {
602
3.88k
    char ekey[128] = WINPR_C_ARRAY_INIT;
603
3.88k
    const int erc = _snprintf(ekey, sizeof(ekey), "</%s>", key);
604
3.88k
    WINPR_ASSERT(erc > 0);
605
3.88k
    WINPR_ASSERT((size_t)erc < sizeof(ekey));
606
3.88k
    if ((erc <= 0) || ((size_t)erc >= sizeof(ekey)))
607
0
      return nullptr;
608
3.88k
    const size_t offset = WINPR_ASSERTING_INT_CAST(size_t, start - tag);
609
3.88k
    dend = end = strrstr(start, ilen - offset, ekey);
610
3.88k
    if (end)
611
3.65k
      end += strnlen(ekey, sizeof(ekey));
612
3.88k
  }
613
614
117k
  if (!end)
615
226
  {
616
226
    WLog_ERR(TAG,
617
226
             "Failed to parse ASSISTANCE file: ConnectionString2 missing end tag for field %s",
618
226
             key);
619
226
    return nullptr;
620
226
  }
621
116k
  if (plen)
622
116k
    *plen = WINPR_ASSERTING_INT_CAST(size_t, end - tag);
623
624
116k
  if (pdata)
625
116k
    *pdata = data;
626
116k
  if (pdlen)
627
116k
    *pdlen = WINPR_ASSERTING_INT_CAST(size_t, dend - data);
628
116k
  return tag;
629
117k
}
630
631
/**! \brief this function returns a XML element identified by \b key
632
 * The input string will be manipulated, so that the element found is '\0' terminated.
633
 *
634
 * This function can not find multiple elements on the same level as the input string is changed!
635
 */
636
WINPR_ATTR_NODISCARD
637
static BOOL freerdp_assistance_consume_input_and_get_element(char* input, size_t ilen,
638
                                                             const char* key, char** element,
639
                                                             size_t* elen)
640
4.24k
{
641
4.24k
  WINPR_ASSERT(input);
642
4.24k
  WINPR_ASSERT(key);
643
4.24k
  WINPR_ASSERT(element);
644
4.24k
  WINPR_ASSERT(elen);
645
646
4.24k
  size_t len = 0;
647
4.24k
  size_t dlen = 0;
648
4.24k
  char* data = nullptr;
649
4.24k
  char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen);
650
4.24k
  if (!tag)
651
1.12k
    return FALSE;
652
653
3.11k
  char* end = data + dlen;
654
3.11k
  *tag = '\0';
655
3.11k
  *end = '\0';
656
3.11k
  *element = data;
657
3.11k
  *elen = dlen + 1;
658
3.11k
  return TRUE;
659
4.24k
}
660
661
WINPR_ATTR_NODISCARD
662
static BOOL freerdp_assistance_get_element(char* input, size_t ilen, const char* key,
663
                                           char** element, size_t* elen)
664
219k
{
665
219k
  WINPR_ASSERT(input);
666
219k
  WINPR_ASSERT(key);
667
219k
  WINPR_ASSERT(element);
668
219k
  WINPR_ASSERT(elen);
669
670
219k
  size_t len = 0;
671
219k
  size_t dlen = 0;
672
219k
  char* data = nullptr;
673
219k
  char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen);
674
219k
  if (!tag)
675
106k
    return FALSE;
676
677
112k
  if (tag + len > input + ilen)
678
0
    return FALSE;
679
680
112k
  char* end = tag + len;
681
112k
  *element = data;
682
112k
  *elen = WINPR_ASSERTING_INT_CAST(size_t, end - data + 1);
683
112k
  return TRUE;
684
112k
}
685
686
WINPR_ATTR_NODISCARD
687
static BOOL freerdp_assistance_parse_all_elements_of(rdpAssistanceFile* file, char* data,
688
                                                     size_t len, const char* key,
689
                                                     BOOL (*fkt)(rdpAssistanceFile* file,
690
                                                                 char* data, size_t len))
691
106k
{
692
106k
  char* val = nullptr;
693
106k
  size_t vlen = 0;
694
695
218k
  while (freerdp_assistance_get_element(data, len, key, &val, &vlen))
696
112k
  {
697
112k
    if (vlen > len)
698
0
      return FALSE;
699
112k
    data = val + vlen;
700
112k
    len = strnlen(data, len - vlen);
701
112k
    if (vlen > 0)
702
112k
    {
703
112k
      val[vlen - 1] = '\0';
704
705
112k
      if (!fkt(file, val, vlen))
706
329
        return FALSE;
707
112k
    }
708
112k
  }
709
710
106k
  return TRUE;
711
106k
}
712
713
WINPR_ATTR_NODISCARD
714
static BOOL freerdp_assistance_parse_all_elements_of_l(rdpAssistanceFile* file, char* data,
715
                                                       WINPR_ATTR_UNUSED size_t len)
716
6.32k
{
717
6.32k
  UINT32 p = 0;
718
6.32k
  const char* n = nullptr;
719
6.32k
  const char* u = nullptr;
720
6.32k
  size_t nlen = 0;
721
6.32k
  size_t ulen = 0;
722
6.32k
  if (!freerdp_assistance_parse_attr_uint32(&p, "P", data))
723
14
    return FALSE;
724
6.30k
  if (!freerdp_assistance_parse_attr(&n, &nlen, "N", data))
725
13
    return FALSE;
726
6.29k
  if (!freerdp_assistance_parse_attr(&u, &ulen, "U", data))
727
10
    return FALSE;
728
729
6.28k
  if (n && (nlen > 0))
730
863
  {
731
863
    if (!append_address_to_list(file->MachineAddresses, n, nlen))
732
0
      return FALSE;
733
863
    if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
734
0
      return FALSE;
735
863
  }
736
6.28k
  if (u && (ulen > 0))
737
1.67k
  {
738
1.67k
    if (!append_address_to_list(file->MachineAddresses, u, ulen))
739
0
      return FALSE;
740
1.67k
    if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p))
741
0
      return FALSE;
742
1.67k
  }
743
6.28k
  return TRUE;
744
6.28k
}
745
746
WINPR_ATTR_NODISCARD
747
static BOOL freerdp_assistance_parse_all_elements_of_t(rdpAssistanceFile* file, char* data,
748
                                                       size_t len)
749
2.35k
{
750
2.35k
  UINT32 id = 0;
751
2.35k
  UINT32 sid = 0;
752
2.35k
  if (!freerdp_assistance_parse_attr_uint32(&id, "ID", data))
753
99
    return FALSE;
754
2.25k
  if (!freerdp_assistance_parse_attr_uint32(&sid, "SID", data))
755
10
    return FALSE;
756
2.24k
  WLog_DBG(TAG, "transport id=%" PRIu32 ", sid=%" PRIu32, id, sid);
757
2.24k
  return freerdp_assistance_parse_all_elements_of(file, data, len, "L",
758
2.24k
                                                  freerdp_assistance_parse_all_elements_of_l);
759
2.25k
}
760
761
WINPR_ATTR_NODISCARD
762
static BOOL freerdp_assistance_parse_all_elements_of_c(rdpAssistanceFile* file, char* data,
763
                                                       size_t len)
764
103k
{
765
103k
  return freerdp_assistance_parse_all_elements_of(file, data, len, "T",
766
103k
                                                  freerdp_assistance_parse_all_elements_of_t);
767
103k
}
768
769
WINPR_ATTR_NODISCARD
770
static BOOL freerdp_assistance_parse_find_elements_of_c(rdpAssistanceFile* file, char* data,
771
                                                        size_t len)
772
719
{
773
719
  return freerdp_assistance_parse_all_elements_of(file, data, len, "C",
774
719
                                                  freerdp_assistance_parse_all_elements_of_c);
775
719
}
776
777
WINPR_ATTR_NODISCARD
778
static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
779
922
{
780
922
  BOOL rc = FALSE;
781
782
922
  WINPR_ASSERT(file);
783
784
922
  if (!file->ConnectionString2)
785
1
    return FALSE;
786
787
921
  char* str = _strdup(file->ConnectionString2);
788
921
  if (!str)
789
0
    goto out_fail;
790
921
  const size_t slen = strlen(str);
791
792
921
  {
793
921
    char* e = nullptr;
794
921
    size_t elen = 0;
795
921
    if (!freerdp_assistance_consume_input_and_get_element(str, slen, "E", &e, &elen))
796
174
      goto out_fail;
797
798
747
    if (!e || (elen == 0))
799
0
      goto out_fail;
800
747
    {
801
747
      char* a = nullptr;
802
747
      size_t alen = 0;
803
747
      if (!freerdp_assistance_get_element(e, elen, "A", &a, &alen))
804
28
        goto out_fail;
805
806
719
      if (!a || (alen == 0))
807
0
        goto out_fail;
808
809
719
      if (!freerdp_assistance_parse_find_elements_of_c(file, e, elen))
810
146
        goto out_fail;
811
812
      /* '\0' terminate the detected XML elements so
813
       * the parser can continue with terminated strings
814
       */
815
573
      a[alen] = '\0';
816
817
573
      if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams, "KH", a))
818
43
        goto out_fail;
819
820
530
      if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams2, "KH2", a))
821
5
        goto out_fail;
822
823
525
      if (!freerdp_assistance_parse_attr_str(&file->RASessionId, "ID", a))
824
4
        goto out_fail;
825
525
    }
826
525
  }
827
521
  rc = TRUE;
828
921
out_fail:
829
921
  free(str);
830
921
  return rc;
831
521
}
832
833
char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
834
0
{
835
0
  if (!name || !pass)
836
0
    return nullptr;
837
838
0
  const size_t nameLength = strlen(name) + strlen("NAME=");
839
0
  const size_t passLength = strlen(pass) + strlen("PASS=");
840
0
  const size_t size = nameLength + passLength + 64;
841
0
  char* ExpertBlob = (char*)calloc(1, size);
842
843
0
  if (!ExpertBlob)
844
0
    return nullptr;
845
846
0
  (void)sprintf_s(ExpertBlob, size, "%" PRIuz ";NAME=%s%" PRIuz ";PASS=%s", nameLength, name,
847
0
                  passLength, pass);
848
0
  return ExpertBlob;
849
0
}
850
851
char* freerdp_assistance_generate_pass_stub(WINPR_ATTR_UNUSED DWORD flags)
852
0
{
853
0
  UINT32 nums[14] = WINPR_C_ARRAY_INIT;
854
0
  const char set1[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
855
0
                        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
856
0
                        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
857
0
                        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
858
0
                        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '_' };
859
0
  const char set2[12] = { '!', '@', '#', '$', '&', '^', '*', '(', ')', '-', '+', '=' };
860
0
  const char set3[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
861
0
  const char set4[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
862
0
                        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
863
0
  const char set5[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
864
0
                        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
865
0
  char* passStub = calloc(15, sizeof(char));
866
867
0
  if (!passStub)
868
0
    return nullptr;
869
870
  /**
871
   * PassStub generation:
872
   *
873
   * Characters 0 and 5-13 are from the set A-Z a-z 0-9 * _
874
   * Character 1 is from the set !@#$&^*()-+=
875
   * Character 2 is from the set 0-9
876
   * Character 3 is from the set A-Z
877
   * Character 4 is from the set a-z
878
   *
879
   * Example: WB^6HsrIaFmEpi
880
   */
881
0
  if (winpr_RAND(nums, sizeof(nums)) < 0)
882
0
  {
883
0
    free(passStub);
884
0
    return nullptr;
885
0
  }
886
887
0
  passStub[0] = set1[nums[0] % sizeof(set1)];   /* character 0 */
888
0
  passStub[1] = set2[nums[1] % sizeof(set2)];   /* character 1 */
889
0
  passStub[2] = set3[nums[2] % sizeof(set3)];   /* character 2 */
890
0
  passStub[3] = set4[nums[3] % sizeof(set4)];   /* character 3 */
891
0
  passStub[4] = set5[nums[4] % sizeof(set5)];   /* character 4 */
892
893
0
  for (size_t x = 5; x < ARRAYSIZE(nums); x++)
894
0
    passStub[x] = set1[nums[x] % sizeof(set1)]; /* character 5 - 13 */
895
0
  return passStub;
896
0
}
897
898
BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
899
                                           size_t* pEncryptedSize)
900
322
{
901
322
  BOOL rc = 0;
902
322
  size_t cbPasswordW = 0;
903
322
  size_t cbPassStubW = 0;
904
322
  BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
905
322
  WINPR_RC4_CTX* rc4Ctx = nullptr;
906
322
  BYTE* pbIn = nullptr;
907
322
  BYTE* pbOut = nullptr;
908
322
  BYTE* res = nullptr;
909
322
  WCHAR* PasswordW = ConvertUtf8ToWCharAlloc(password, &cbPasswordW);
910
322
  WCHAR* PassStubW = ConvertUtf8ToWCharAlloc(passStub, &cbPassStubW);
911
912
322
  cbPasswordW = cbPasswordW * sizeof(WCHAR);
913
322
  cbPassStubW = cbPassStubW * sizeof(WCHAR);
914
322
  const size_t EncryptedSize = cbPassStubW + 4;
915
916
322
  if (!PasswordW || !PassStubW)
917
78
    goto fail;
918
919
244
  if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*)PasswordHash,
920
244
                    sizeof(PasswordHash)))
921
0
    goto fail;
922
923
244
  pbIn = (BYTE*)calloc(1, EncryptedSize);
924
244
  pbOut = (BYTE*)calloc(1, EncryptedSize);
925
926
244
  if (!pbIn || !pbOut)
927
0
    goto fail;
928
929
244
  WINPR_ASSERT(cbPasswordW <= UINT32_MAX);
930
244
  winpr_Data_Write_UINT32(pbIn, (UINT32)cbPassStubW);
931
244
  CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
932
244
  rc4Ctx = winpr_RC4_New(PasswordHash, sizeof(PasswordHash));
933
934
244
  if (!rc4Ctx)
935
0
  {
936
0
    WLog_ERR(TAG, "winpr_Cipher_New failure");
937
0
    goto fail;
938
0
  }
939
940
244
  rc = winpr_RC4_Update(rc4Ctx, EncryptedSize, pbIn, pbOut);
941
942
244
  if (!rc)
943
0
  {
944
0
    WLog_ERR(TAG, "winpr_Cipher_Update failure");
945
0
    goto fail;
946
0
  }
947
244
  res = pbOut;
948
322
fail:
949
322
  winpr_RC4_Free(rc4Ctx);
950
322
  free(PasswordW);
951
322
  free(PassStubW);
952
322
  free(pbIn);
953
322
  if (!res)
954
78
    free(pbOut);
955
244
  else
956
244
    *pEncryptedSize = EncryptedSize;
957
322
  return res;
958
244
}
959
960
WINPR_ATTR_NODISCARD
961
static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file)
962
453
{
963
453
  BOOL rc = FALSE;
964
453
  int status = 0;
965
453
  size_t cbPasswordW = 0;
966
453
  size_t cchOutW = 0;
967
453
  WINPR_CIPHER_CTX* aesDec = nullptr;
968
453
  WCHAR* PasswordW = nullptr;
969
453
  BYTE* pbIn = nullptr;
970
453
  BYTE* pbOut = nullptr;
971
453
  size_t cbOut = 0;
972
453
  size_t cbIn = 0;
973
453
  size_t cbFinal = 0;
974
453
  BYTE DerivedKey[WINPR_AES_BLOCK_SIZE] = WINPR_C_ARRAY_INIT;
975
453
  BYTE InitializationVector[WINPR_AES_BLOCK_SIZE] = WINPR_C_ARRAY_INIT;
976
453
  BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
977
978
453
  WINPR_ASSERT(file);
979
980
453
  if (!file->password)
981
0
    return FALSE;
982
983
453
  PasswordW = ConvertUtf8ToWCharAlloc(file->password, &cbPasswordW);
984
453
  if (!PasswordW)
985
0
  {
986
0
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
987
0
    return FALSE;
988
0
  }
989
990
453
  cbPasswordW = cbPasswordW * sizeof(WCHAR);
991
992
453
  if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash,
993
453
                    sizeof(PasswordHash)))
994
0
    goto fail;
995
996
453
  if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash), DerivedKey,
997
453
                                                sizeof(DerivedKey)))
998
0
    goto fail;
999
1000
453
  aesDec =
1001
453
      winpr_Cipher_NewEx(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, DerivedKey, sizeof(DerivedKey),
1002
453
                         InitializationVector, sizeof(InitializationVector));
1003
1004
453
  if (!aesDec)
1005
0
    goto fail;
1006
1007
453
  cbOut = cbFinal = 0;
1008
453
  cbIn = file->EncryptedLHTicketLength;
1009
453
  pbIn = file->EncryptedLHTicket;
1010
453
  pbOut = (BYTE*)calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);
1011
1012
453
  if (!pbOut)
1013
0
    goto fail;
1014
1015
453
  if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
1016
0
    goto fail;
1017
1018
453
  if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
1019
201
  {
1020
201
    WLog_ERR(TAG, "winpr_Cipher_Final failure");
1021
201
    goto fail;
1022
201
  }
1023
1024
252
  cbOut += cbFinal;
1025
252
  cbFinal = 0;
1026
1027
252
  union
1028
252
  {
1029
252
    const WCHAR* wc;
1030
252
    const BYTE* b;
1031
252
  } cnv;
1032
1033
252
  cnv.b = pbOut;
1034
252
  cchOutW = cbOut / sizeof(WCHAR);
1035
1036
252
  if (!update_connectionstring2_wchar(file, cnv.wc, cchOutW))
1037
83
  {
1038
83
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
1039
83
    goto fail;
1040
83
  }
1041
1042
169
  if (!freerdp_assistance_parse_connection_string2(file))
1043
169
    goto fail;
1044
1045
0
  rc = TRUE;
1046
453
fail:
1047
453
  winpr_Cipher_Free(aesDec);
1048
453
  free(PasswordW);
1049
453
  free(pbOut);
1050
453
  WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
1051
453
  return rc;
1052
0
}
1053
1054
BYTE* freerdp_assistance_hex_string_to_bin(const void* str, size_t* size)
1055
453
{
1056
453
  BYTE* buffer = nullptr;
1057
453
  if (!str || !size)
1058
0
    return nullptr;
1059
453
  *size = 0;
1060
453
  const size_t length = strlen(str);
1061
453
  buffer = calloc(length, sizeof(BYTE));
1062
453
  if (!buffer)
1063
0
    return nullptr;
1064
453
  const size_t rc = winpr_HexStringToBinBuffer(str, length, buffer, length);
1065
453
  if (rc == 0)
1066
1
  {
1067
1
    free(buffer);
1068
1
    return nullptr;
1069
1
  }
1070
452
  *size = rc;
1071
452
  return buffer;
1072
453
}
1073
1074
char* freerdp_assistance_bin_to_hex_string(const void* data, size_t size)
1075
0
{
1076
0
  return winpr_BinToHexString(data, size, FALSE);
1077
0
}
1078
1079
WINPR_ATTR_NODISCARD
1080
static int freerdp_assistance_parse_uploadinfo(rdpAssistanceFile* file, char* uploadinfo,
1081
                                               size_t uploadinfosize)
1082
1.26k
{
1083
1.26k
  const char escalated[9] = { 'E', 's', 'c', 'a', 'l', 'a', 't', 'e', 'd' };
1084
1.26k
  const size_t esclen = sizeof(escalated);
1085
1.26k
  const char* typestr = nullptr;
1086
1.26k
  size_t typelen = 0;
1087
1088
1.26k
  if (!uploadinfo || (uploadinfosize == 0))
1089
0
    return -1;
1090
1091
1.26k
  const size_t ulen = strnlen(uploadinfo, uploadinfosize);
1092
1.26k
  if (ulen == uploadinfosize)
1093
0
  {
1094
0
    WLog_WARN(TAG, "UPLOADINFOR string is not '\\0' terminated");
1095
0
    return -1;
1096
0
  }
1097
1098
1.26k
  if (!freerdp_assistance_parse_attr(&typestr, &typelen, "TYPE", uploadinfo))
1099
1
    return -1;
1100
1101
1.25k
  if ((typelen != esclen) || (strncmp(typestr, escalated, esclen) != 0))
1102
138
  {
1103
138
    WLog_ERR(TAG,
1104
138
             "Failed to parse ASSISTANCE file: Missing or invalid UPLOADINFO TYPE '%s' [%" PRIuz
1105
138
             "]",
1106
138
             typestr, typelen);
1107
138
    return -1;
1108
138
  }
1109
1110
1.12k
  char* uploaddata = nullptr;
1111
1.12k
  size_t uploaddatasize = 0;
1112
1.12k
  if (!freerdp_assistance_consume_input_and_get_element(uploadinfo, ulen, "UPLOADDATA",
1113
1.12k
                                                        &uploaddata, &uploaddatasize))
1114
10
    return -1;
1115
1116
  /* Parse USERNAME */
1117
1.11k
  if (!freerdp_assistance_parse_attr_str(&file->Username, "USERNAME", uploaddata))
1118
1
    return -1;
1119
1120
  /* Parse LHTICKET */
1121
1.11k
  if (!freerdp_assistance_parse_attr_str(&file->LHTicket, "LHTICKET", uploaddata))
1122
3
    return -1;
1123
1124
  /* Parse RCTICKET */
1125
1.10k
  if (!freerdp_assistance_parse_attr_str(&file->RCTicket, "RCTICKET", uploaddata))
1126
1
    return -1;
1127
1128
  /* Parse RCTICKETENCRYPTED */
1129
1.10k
  if (!freerdp_assistance_parse_attr_bool(&file->RCTicketEncrypted, "RCTICKETENCRYPTED",
1130
1.10k
                                          uploaddata))
1131
1
    return -1;
1132
1133
  /* Parse PassStub */
1134
1.10k
  if (!freerdp_assistance_parse_attr_str(&file->PassStub, "PassStub", uploaddata))
1135
1
    return -1;
1136
1137
1.10k
  if (file->PassStub)
1138
281
  {
1139
281
    const char* amp = "&amp;";
1140
281
    char* passtub = strstr(file->PassStub, amp);
1141
553
    while (passtub)
1142
272
    {
1143
272
      const char* end = passtub + 5;
1144
272
      const size_t len = strlen(end);
1145
272
      memmove(&passtub[1], end, len + 1);
1146
272
      passtub = strstr(passtub, amp);
1147
272
    }
1148
281
  }
1149
1150
  /* Parse DtStart */
1151
1.10k
  if (!freerdp_assistance_parse_attr_uint32(&file->DtStart, "DtStart", uploaddata))
1152
6
    return -1;
1153
1154
  /* Parse DtLength */
1155
1.09k
  if (!freerdp_assistance_parse_attr_uint32(&file->DtLength, "DtLength", uploaddata))
1156
1
    return -1;
1157
1158
  /* Parse L (LowSpeed) */
1159
1.09k
  if (!freerdp_assistance_parse_attr_bool(&file->LowSpeed, "L", uploaddata))
1160
1
    return -1;
1161
1162
1.09k
  file->Type = (file->LHTicket) ? 2 : 1;
1163
1.09k
  int status = 0;
1164
1165
1.09k
  switch (file->Type)
1166
1.09k
  {
1167
453
    case 2:
1168
453
    {
1169
453
      file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(
1170
453
          file->LHTicket, &file->EncryptedLHTicketLength);
1171
1172
453
      if (!freerdp_assistance_decrypt2(file))
1173
453
        status = -1;
1174
453
    }
1175
453
    break;
1176
1177
643
    case 1:
1178
643
    {
1179
643
      if (!freerdp_assistance_parse_connection_string1(file))
1180
321
        status = -1;
1181
643
    }
1182
643
    break;
1183
1184
0
    default:
1185
0
      return -1;
1186
1.09k
  }
1187
1188
1.09k
  if (status < 0)
1189
774
  {
1190
774
    WLog_ERR(TAG, "freerdp_assistance_parse_connection_string1 failure: %d", status);
1191
774
    return -1;
1192
774
  }
1193
1194
322
  file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(file->password, file->PassStub,
1195
322
                                                                 &file->EncryptedPassStubLength);
1196
1197
322
  if (!file->EncryptedPassStub)
1198
78
    return -1;
1199
1200
244
  return 1;
1201
322
}
1202
1203
WINPR_ATTR_NODISCARD
1204
static int freerdp_assistance_parse_file_buffer_int(rdpAssistanceFile* file, char* buffer,
1205
                                                    size_t size, const char* password)
1206
2.20k
{
1207
2.20k
  WINPR_ASSERT(file);
1208
2.20k
  WINPR_ASSERT(buffer);
1209
2.20k
  WINPR_ASSERT(size > 0);
1210
1211
2.20k
  if (!update_password(file, password))
1212
0
    return -1;
1213
1214
2.20k
  char* uploadinfo = nullptr;
1215
2.20k
  size_t uploadinfosize = 0;
1216
2.20k
  if (freerdp_assistance_consume_input_and_get_element(buffer, size, "UPLOADINFO", &uploadinfo,
1217
2.20k
                                                       &uploadinfosize))
1218
1.26k
    return freerdp_assistance_parse_uploadinfo(file, uploadinfo, uploadinfosize);
1219
1220
941
  size_t elen = 0;
1221
941
  const char* estr =
1222
941
      freerdp_assistance_contains_element(buffer, size, "E", &elen, nullptr, nullptr);
1223
941
  if (!estr || (elen == 0))
1224
188
  {
1225
188
    WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor <E> found");
1226
188
    return -1;
1227
188
  }
1228
753
  if (!update_connectionstring2(file, estr, elen))
1229
0
    return -1;
1230
1231
753
  if (!freerdp_assistance_parse_connection_string2(file))
1232
232
    return -1;
1233
1234
521
  return 1;
1235
753
}
1236
1237
int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* cbuffer, size_t size,
1238
                                         const char* password)
1239
2.20k
{
1240
2.20k
  WINPR_ASSERT(file);
1241
2.20k
  if (!password)
1242
0
  {
1243
0
    WLog_WARN(TAG, "empty password supplied");
1244
0
  }
1245
1246
2.20k
  if (!cbuffer || (size == 0))
1247
0
  {
1248
0
    WLog_WARN(TAG, "no data supplied [%p, %" PRIuz "]",
1249
0
              WINPR_CXX_COMPAT_CAST(const void*, cbuffer), size);
1250
0
    return -1;
1251
0
  }
1252
1253
2.20k
  char* abuffer = strndup(cbuffer, size);
1254
2.20k
  const size_t len = strnlen(cbuffer, size);
1255
2.20k
  if (len == size)
1256
0
    WLog_WARN(TAG, "Input data not '\\0' terminated");
1257
1258
2.20k
  if (!abuffer)
1259
0
    return -1;
1260
1261
2.20k
  const int rc = freerdp_assistance_parse_file_buffer_int(file, abuffer, len + 1, password);
1262
2.20k
  free(abuffer);
1263
2.20k
  return rc;
1264
2.20k
}
1265
1266
int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
1267
0
{
1268
0
  int status = -1;
1269
0
  BYTE* buffer = nullptr;
1270
0
  size_t readSize = 0;
1271
0
  union
1272
0
  {
1273
0
    INT64 i64;
1274
0
    size_t s;
1275
0
  } fileSize;
1276
1277
0
  if (!update_name(file, name))
1278
0
    return -1;
1279
1280
0
  FILE* fp = winpr_fopen(name, "r");
1281
1282
0
  if (!fp)
1283
0
  {
1284
0
    WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name);
1285
0
    return -1;
1286
0
  }
1287
1288
0
  if (_fseeki64(fp, 0, SEEK_END) < 0)
1289
0
    goto fail;
1290
0
  fileSize.i64 = _ftelli64(fp);
1291
0
  if (_fseeki64(fp, 0, SEEK_SET) < 0)
1292
0
    goto fail;
1293
1294
0
  if (fileSize.i64 < 1)
1295
0
  {
1296
0
    WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1297
0
    goto fail;
1298
0
  }
1299
1300
0
  buffer = (BYTE*)malloc(fileSize.s + 2);
1301
1302
0
  if (!buffer)
1303
0
    goto fail;
1304
1305
0
  readSize = fread(buffer, fileSize.s, 1, fp);
1306
1307
0
  if (!readSize)
1308
0
  {
1309
0
    if (!ferror(fp))
1310
0
      readSize = fileSize.s;
1311
0
  }
1312
1313
0
  if (readSize < 1)
1314
0
  {
1315
0
    WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1316
0
    goto fail;
1317
0
  }
1318
1319
0
  buffer[fileSize.s] = '\0';
1320
0
  buffer[fileSize.s + 1] = '\0';
1321
0
  status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize.s, password);
1322
1323
0
fail:
1324
0
  (void)fclose(fp);
1325
0
  free(buffer);
1326
0
  return status;
1327
0
}
1328
1329
BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file,
1330
                                                               rdpSettings* settings)
1331
0
{
1332
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1333
0
    return FALSE;
1334
1335
0
  if (!file->RASessionId || !file->MachineAddresses)
1336
0
    return FALSE;
1337
1338
0
  if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId,
1339
0
                                   file->RASessionId))
1340
0
    return FALSE;
1341
1342
0
  if (file->RCTicket)
1343
0
  {
1344
0
    if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1345
0
                                     file->RCTicket))
1346
0
      return FALSE;
1347
0
  }
1348
0
  else
1349
0
  {
1350
0
    if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1351
0
                                     file->ConnectionString2))
1352
0
      return FALSE;
1353
0
  }
1354
1355
0
  if (file->PassStub)
1356
0
  {
1357
0
    if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub,
1358
0
                                     file->PassStub))
1359
0
      return FALSE;
1360
0
  }
1361
1362
0
  if (ArrayList_Count(file->MachineAddresses) < 1)
1363
0
    return FALSE;
1364
1365
0
  const char* addr = ArrayList_GetItem(file->MachineAddresses, 0);
1366
0
  if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, addr))
1367
0
    return FALSE;
1368
1369
0
  if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename))
1370
0
    return FALSE;
1371
1372
0
  if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password))
1373
0
    return FALSE;
1374
1375
0
  if (file->Username)
1376
0
  {
1377
0
    if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username))
1378
0
      return FALSE;
1379
0
  }
1380
1381
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1382
0
    return FALSE;
1383
1384
0
  const size_t ports = ArrayList_Count(file->MachinePorts);
1385
0
  const size_t addresses = ArrayList_Count(file->MachineAddresses);
1386
0
  if (ports < 1)
1387
0
    return FALSE;
1388
0
  if (ports != addresses)
1389
0
    return FALSE;
1390
1391
0
  union
1392
0
  {
1393
0
    uintptr_t port;
1394
0
    void* data;
1395
0
  } cnv;
1396
0
  cnv.data = ArrayList_GetItem(file->MachinePorts, 0);
1397
0
  WINPR_ASSERT(cnv.port <= UINT32_MAX);
1398
0
  if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT32)cnv.port))
1399
0
    return FALSE;
1400
1401
0
  if (!freerdp_target_net_adresses_reset(settings, ports))
1402
0
    return FALSE;
1403
1404
0
  for (size_t x = 0; x < ports; x++)
1405
0
  {
1406
0
    cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1407
0
    WINPR_ASSERT(cnv.port <= UINT32_MAX);
1408
0
    const UINT32 port = (UINT32)cnv.port;
1409
0
    if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port))
1410
0
      return FALSE;
1411
0
  }
1412
0
  for (size_t i = 0; i < addresses; i++)
1413
0
  {
1414
0
    const char* maddr = ArrayList_GetItem(file->MachineAddresses, i);
1415
0
    if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr))
1416
0
      return FALSE;
1417
0
  }
1418
1419
0
  return TRUE;
1420
0
}
1421
1422
WINPR_ATTR_NODISCARD
1423
static BOOL setup_string(wArrayList* list)
1424
4.40k
{
1425
4.40k
  WINPR_ASSERT(list);
1426
1427
4.40k
  wObject* obj = ArrayList_Object(list);
1428
4.40k
  if (!obj)
1429
0
    return FALSE;
1430
4.40k
  obj->fnObjectFree = free;
1431
  // obj->fnObjectNew = wwinpr_ObjectStringClone;
1432
4.40k
  return TRUE;
1433
4.40k
}
1434
1435
rdpAssistanceFile* freerdp_assistance_file_new(void)
1436
2.20k
{
1437
2.20k
  if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
1438
0
    return nullptr;
1439
1440
2.20k
  rdpAssistanceFile* file = calloc(1, sizeof(rdpAssistanceFile));
1441
2.20k
  if (!file)
1442
0
    return nullptr;
1443
1444
2.20k
  file->MachineAddresses = ArrayList_New(FALSE);
1445
2.20k
  file->MachinePorts = ArrayList_New(FALSE);
1446
2.20k
  file->MachineUris = ArrayList_New(FALSE);
1447
1448
2.20k
  if (!file->MachineAddresses || !file->MachinePorts || !file->MachineUris)
1449
0
    goto fail;
1450
1451
2.20k
  if (!setup_string(file->MachineAddresses) || !setup_string(file->MachineUris))
1452
0
    goto fail;
1453
1454
2.20k
  return file;
1455
1456
0
fail:
1457
0
  WINPR_PRAGMA_DIAG_PUSH
1458
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1459
0
  freerdp_assistance_file_free(file);
1460
0
  WINPR_PRAGMA_DIAG_POP
1461
0
  return nullptr;
1462
2.20k
}
1463
1464
void freerdp_assistance_file_free(rdpAssistanceFile* file)
1465
2.20k
{
1466
2.20k
  if (!file)
1467
0
    return;
1468
1469
2.20k
  free(file->password);
1470
2.20k
  free(file->ConnectionString2);
1471
2.20k
  free(file->filename);
1472
2.20k
  free(file->Username);
1473
2.20k
  free(file->LHTicket);
1474
2.20k
  free(file->RCTicket);
1475
2.20k
  free(file->PassStub);
1476
2.20k
  free(file->ConnectionString1);
1477
2.20k
  free(file->EncryptedLHTicket);
1478
2.20k
  free(file->RASessionId);
1479
2.20k
  free(file->RASpecificParams);
1480
2.20k
  free(file->RASpecificParams2);
1481
2.20k
  free(file->EncryptedPassStub);
1482
1483
2.20k
  ArrayList_Free(file->MachineAddresses);
1484
2.20k
  ArrayList_Free(file->MachinePorts);
1485
2.20k
  ArrayList_Free(file->MachineUris);
1486
2.20k
  free(file);
1487
2.20k
}
1488
1489
void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level)
1490
0
{
1491
0
  WINPR_ASSERT(file);
1492
1493
0
  WLog_Print(log, level, "Username: %s", file->Username);
1494
0
  WLog_Print(log, level, "LHTicket: %s", file->LHTicket);
1495
0
  WLog_Print(log, level, "RCTicket: %s", file->RCTicket);
1496
0
  WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted);
1497
0
  WLog_Print(log, level, "PassStub: %s", file->PassStub);
1498
0
  WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart);
1499
0
  WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength);
1500
0
  WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed);
1501
0
  WLog_Print(log, level, "RASessionId: %s", file->RASessionId);
1502
0
  WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams);
1503
0
  WLog_Print(log, level, "RASpecificParams2: %s", file->RASpecificParams2);
1504
1505
0
  for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++)
1506
0
  {
1507
0
    UINT32 port = 0;
1508
0
    const char* uri = nullptr;
1509
0
    const char* addr = ArrayList_GetItem(file->MachineAddresses, x);
1510
0
    if (x < ArrayList_Count(file->MachinePorts))
1511
0
    {
1512
0
      union
1513
0
      {
1514
0
        uintptr_t port;
1515
0
        void* data;
1516
0
      } cnv;
1517
0
      cnv.data = ArrayList_GetItem(file->MachinePorts, x);
1518
0
      WINPR_ASSERT(cnv.port <= UINT32_MAX);
1519
0
      port = (UINT32)cnv.port;
1520
0
    }
1521
0
    if (x < ArrayList_Count(file->MachineUris))
1522
0
      uri = ArrayList_GetItem(file->MachineUris, x);
1523
1524
0
    WLog_Print(log, level, "MachineAddress [%" PRIuz ": %s", x, addr);
1525
0
    WLog_Print(log, level, "MachinePort    [%" PRIuz ": %" PRIu32, x, port);
1526
0
    WLog_Print(log, level, "MachineURI     [%" PRIuz ": %s", x, uri);
1527
0
  }
1528
0
}
1529
1530
BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd,
1531
                                                size_t* size)
1532
0
{
1533
0
  if (!file || !pwd || !size)
1534
0
    return FALSE;
1535
1536
0
  *pwd = (const char*)file->EncryptedPassStub;
1537
0
  *size = file->EncryptedPassStubLength;
1538
0
  return TRUE;
1539
0
}
1540
1541
int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string,
1542
                                              const char* password)
1543
0
{
1544
0
  if (!file || !string || !password)
1545
0
    return -1;
1546
1547
0
  char* str = _strdup(string);
1548
0
  if (!str)
1549
0
    return -1;
1550
1551
0
  if (!update_connectionstring2_nocopy(file, str))
1552
0
    return -1;
1553
0
  if (!update_password(file, password))
1554
0
    return -1;
1555
0
  return freerdp_assistance_parse_connection_string2(file);
1556
0
}