Coverage Report

Created: 2026-07-16 07:09

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