Coverage Report

Created: 2025-07-11 06:41

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