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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <errno.h>
25
26 #include <winpr/wtypes.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 #define TAG FREERDP_TAG("common")
41
42 struct rdp_assistance_file
43 {
44 UINT32 Type;
45
46 char* Username;
47 char* LHTicket;
48 char* RCTicket;
49 char* PassStub;
50 UINT32 DtStart;
51 UINT32 DtLength;
52 BOOL LowSpeed;
53 BOOL RCTicketEncrypted;
54
55 char* ConnectionString1;
56 char* ConnectionString2;
57
58 BYTE* EncryptedPassStub;
59 size_t EncryptedPassStubLength;
60
61 BYTE* EncryptedLHTicket;
62 size_t EncryptedLHTicketLength;
63
64 UINT32 MachineCount;
65 char** MachineAddresses;
66 UINT32* MachinePorts;
67
68 char* RASessionId;
69 char* RASpecificParams;
70
71 char* filename;
72 char* password;
73 };
74
75 /**
76 * Password encryption in establishing a remote assistance session of type 1:
77 * http://blogs.msdn.com/b/openspecification/archive/2011/10/31/password-encryption-in-establishing-a-remote-assistance-session-of-type-1.aspx
78 *
79 * Creation of PassStub for the Remote Assistance Ticket:
80 * http://social.msdn.microsoft.com/Forums/en-US/6316c3f4-ea09-4343-a4a1-9cca46d70d28/creation-of-passstub-for-the-remote-assistance-ticket?forum=os_windowsprotocols
81 */
82
83 /**
84 * CryptDeriveKey Function:
85 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916/
86 *
87 * Let n be the required derived key length, in bytes.
88 * The derived key is the first n bytes of the hash value after the hash computation
89 * has been completed by CryptDeriveKey. If the hash is not a member of the SHA-2
90 * family and the required key is for either 3DES or AES, the key is derived as follows:
91 *
92 * Form a 64-byte buffer by repeating the constant 0x36 64 times.
93 * Let k be the length of the hash value that is represented by the input parameter hBaseData.
94 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
95 * of the buffer with the hash value that is represented by the input parameter hBaseData.
96 *
97 * Form a 64-byte buffer by repeating the constant 0x5C 64 times.
98 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
99 * of the buffer with the hash value that is represented by the input parameter hBaseData.
100 *
101 * Hash the result of step 1 by using the same hash algorithm as that used to compute the hash
102 * value that is represented by the hBaseData parameter.
103 *
104 * Hash the result of step 2 by using the same hash algorithm as that used to compute the hash
105 * value that is represented by the hBaseData parameter.
106 *
107 * Concatenate the result of step 3 with the result of step 4.
108 * Use the first n bytes of the result of step 5 as the derived key.
109 */
110
freerdp_assistance_crypt_derive_key_sha1(BYTE * hash,size_t hashLength,BYTE * key,size_t keyLength)111 static BOOL freerdp_assistance_crypt_derive_key_sha1(BYTE* hash, size_t hashLength, BYTE* key,
112 size_t keyLength)
113 {
114 BOOL rc = FALSE;
115 size_t i;
116 BYTE* buffer;
117 BYTE pad1[64];
118 BYTE pad2[64];
119 memset(pad1, 0x36, 64);
120 memset(pad2, 0x5C, 64);
121
122 for (i = 0; i < hashLength; i++)
123 {
124 pad1[i] ^= hash[i];
125 pad2[i] ^= hash[i];
126 }
127
128 buffer = (BYTE*)calloc(hashLength, 2);
129
130 if (!buffer)
131 goto fail;
132
133 if (!winpr_Digest(WINPR_MD_SHA1, pad1, 64, buffer, hashLength))
134 goto fail;
135
136 if (!winpr_Digest(WINPR_MD_SHA1, pad2, 64, &buffer[hashLength], hashLength))
137 goto fail;
138
139 CopyMemory(key, buffer, keyLength);
140 rc = TRUE;
141 fail:
142 free(buffer);
143 return rc;
144 }
145
reallocate(rdpAssistanceFile * file,const char * host,UINT32 port)146 static BOOL reallocate(rdpAssistanceFile* file, const char* host, UINT32 port)
147 {
148 void *tmp1, *tmp2;
149 file->MachineCount++;
150 tmp1 = realloc(file->MachinePorts, sizeof(UINT32) * file->MachineCount);
151 tmp2 = realloc(file->MachineAddresses, sizeof(char*) * file->MachineCount);
152
153 if (!tmp1 || !tmp2)
154 {
155 free(tmp1);
156 free(tmp2);
157 return FALSE;
158 }
159
160 file->MachinePorts = tmp1;
161 file->MachineAddresses = tmp2;
162 file->MachinePorts[file->MachineCount - 1] = port;
163 file->MachineAddresses[file->MachineCount - 1] = _strdup(host);
164 return TRUE;
165 }
166
append_address(rdpAssistanceFile * file,const char * host,const char * port)167 static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port)
168 {
169 unsigned long p;
170 errno = 0;
171 p = strtoul(port, NULL, 0);
172
173 if ((errno != 0) || (p == 0) || (p > UINT16_MAX))
174 {
175 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid port value %s",
176 port);
177 return FALSE;
178 }
179
180 return reallocate(file, host, (UINT16)p);
181 }
182
freerdp_assistance_parse_address_list(rdpAssistanceFile * file,char * list)183 static BOOL freerdp_assistance_parse_address_list(rdpAssistanceFile* file, char* list)
184 {
185 BOOL rc = FALSE;
186 char* p;
187
188 if (!file || !list)
189 return FALSE;
190
191 p = list;
192
193 while ((p = strchr(p, ';')) != NULL)
194 {
195 char* q = strchr(p, ':');
196
197 if (!q)
198 goto out;
199
200 *q = '\0';
201 q++;
202
203 if (!append_address(file, p, q))
204 goto out;
205
206 p = q;
207 }
208
209 rc = TRUE;
210 out:
211 return rc;
212 }
213
freerdp_assistance_parse_connection_string1(rdpAssistanceFile * file)214 static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file)
215 {
216 size_t i;
217 char* str;
218 int count;
219 size_t length;
220 char* tokens[8];
221 BOOL rc = FALSE;
222
223 if (!file || !file->RCTicket)
224 return FALSE;
225
226 /**
227 * <ProtocolVersion>,<protocolType>,<machineAddressList>,<assistantAccountPwd>,
228 * <RASessionID>,<RASessionName>,<RASessionPwd>,<protocolSpecificParms>
229 */
230 count = 1;
231 str = _strdup(file->RCTicket);
232
233 if (!str)
234 goto error;
235
236 length = strlen(str);
237
238 for (i = 0; i < length; i++)
239 {
240 if (str[i] == ',')
241 count++;
242 }
243
244 if (count != 8)
245 goto error;
246
247 count = 0;
248 tokens[count++] = str;
249
250 for (i = 0; i < length; i++)
251 {
252 if (str[i] == ',')
253 {
254 str[i] = '\0';
255 tokens[count++] = &str[i + 1];
256 }
257 }
258
259 if (strcmp(tokens[0], "65538") != 0)
260 goto error;
261
262 if (strcmp(tokens[1], "1") != 0)
263 goto error;
264
265 if (strcmp(tokens[3], "*") != 0)
266 goto error;
267
268 if (strcmp(tokens[5], "*") != 0)
269 goto error;
270
271 if (strcmp(tokens[6], "*") != 0)
272 goto error;
273
274 file->RASessionId = _strdup(tokens[4]);
275
276 if (!file->RASessionId)
277 goto error;
278
279 file->RASpecificParams = _strdup(tokens[7]);
280
281 if (!file->RASpecificParams)
282 goto error;
283
284 if (!freerdp_assistance_parse_address_list(file, tokens[2]))
285 goto error;
286
287 rc = TRUE;
288 error:
289 free(str);
290 return rc;
291 }
292
293 /**
294 * Decrypted Connection String 2:
295 *
296 * <E>
297 * <A KH="BNRjdu97DyczQSRuMRrDWoue+HA="
298 * ID="+ULZ6ifjoCa6cGPMLQiGHRPwkg6VyJqGwxMnO6GcelwUh9a6/FBq3It5ADSndmLL"/> <C> <T ID="1" SID="0"> <L
299 * P="49228" N="fe80::1032:53d9:5a01:909b%3"/> <L P="49229" N="fe80::3d8f:9b2d:6b4e:6aa%6"/> <L
300 * P="49230" N="192.168.1.200"/> <L P="49231" N="169.254.6.170"/>
301 * </T>
302 * </C>
303 * </E>
304 */
305
freerdp_assistance_parse_connection_string2(rdpAssistanceFile * file)306 static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
307 {
308 char* str;
309 char* tag;
310 char* end;
311 char* p;
312 BOOL rc = FALSE;
313
314 if (!file || !file->ConnectionString2)
315 return FALSE;
316
317 str = file->ConnectionString2;
318
319 if (!strstr(str, "<E>"))
320 {
321 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field <E>");
322 return FALSE;
323 }
324
325 if (!strstr(str, "<C>"))
326 {
327 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field <C>");
328 return FALSE;
329 }
330
331 str = _strdup(file->ConnectionString2);
332
333 if (!str)
334 goto out_fail;
335
336 if (!(tag = strstr(str, "<A")))
337 {
338 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field <A");
339 goto out_fail;
340 }
341
342 /* Parse Auth String Node (<A>) */
343 end = strstr(tag, "/>");
344
345 if (!end)
346 goto out_fail;
347
348 *end = '\0';
349 p = strstr(tag, "KH=\"");
350
351 if (p)
352 {
353 char* q;
354 size_t length;
355 p += sizeof("KH=\"") - 1;
356 q = strchr(p, '"');
357
358 if (!q)
359 {
360 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field KH=%s",
361 q);
362 goto out_fail;
363 }
364
365 if (p > q)
366 {
367 WLog_ERR(
368 TAG,
369 "Failed to parse ASSISTANCE file: ConnectionString2 invalid field order for KH");
370 goto out_fail;
371 }
372
373 length = q - p;
374 free(file->RASpecificParams);
375 file->RASpecificParams = (char*)malloc(length + 1);
376
377 if (!file->RASpecificParams)
378 goto out_fail;
379
380 CopyMemory(file->RASpecificParams, p, length);
381 file->RASpecificParams[length] = '\0';
382 }
383
384 p = strstr(tag, "ID=\"");
385
386 if (p)
387 {
388 char* q;
389 size_t length;
390 p += sizeof("ID=\"") - 1;
391 q = strchr(p, '"');
392
393 if (!q)
394 {
395 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field ID=%s",
396 q);
397 goto out_fail;
398 }
399
400 if (p > q)
401 {
402 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field "
403 "order for ID");
404 return -1;
405 }
406 length = q - p;
407 free(file->RASessionId);
408 file->RASessionId = (char*)malloc(length + 1);
409
410 if (!file->RASessionId)
411 goto out_fail;
412
413 CopyMemory(file->RASessionId, p, length);
414 file->RASessionId[length] = '\0';
415 }
416
417 *end = '/';
418 /* Parse <L last address is used */
419 p = strstr(str, "<L P=\"");
420
421 while (p)
422 {
423 char* port;
424 char* q;
425 size_t length;
426 p += sizeof("<L P=\"") - 1;
427 q = strchr(p, '"');
428
429 if (!q)
430 {
431 WLog_ERR(TAG,
432 "Failed to parse ASSISTANCE file: ConnectionString2 invalid field <L P=%s", q);
433 goto out_fail;
434 }
435
436 q[0] = '\0';
437 q++;
438 port = p;
439 p = strstr(q, " N=\"");
440
441 if (!p)
442 {
443 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field N=%s",
444 p);
445 goto out_fail;
446 }
447
448 p += sizeof(" N=\"") - 1;
449 q = strchr(p, '"');
450
451 if (!q)
452 {
453 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field N=%s",
454 q);
455 goto out_fail;
456 }
457
458 q[0] = '\0';
459 q++;
460 length = strlen(p);
461
462 if (length > 8)
463 {
464 if (!append_address(file, p, port))
465 goto out_fail;
466 }
467
468 p = strstr(q, "<L P=\"");
469 }
470
471 rc = TRUE;
472 out_fail:
473 free(str);
474 return rc;
475 }
476
freerdp_assistance_construct_expert_blob(const char * name,const char * pass)477 char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
478 {
479 size_t size;
480 size_t nameLength;
481 size_t passLength;
482 char* ExpertBlob = NULL;
483
484 if (!name || !pass)
485 return NULL;
486
487 nameLength = strlen(name) + strlen("NAME=");
488 passLength = strlen(pass) + strlen("PASS=");
489 size = nameLength + passLength + 64;
490 ExpertBlob = (char*)calloc(1, size);
491
492 if (!ExpertBlob)
493 return NULL;
494
495 sprintf_s(ExpertBlob, size, "%" PRIdz ";NAME=%s%" PRIdz ";PASS=%s", nameLength, name,
496 passLength, pass);
497 return ExpertBlob;
498 }
499
freerdp_assistance_generate_pass_stub(DWORD flags)500 char* freerdp_assistance_generate_pass_stub(DWORD flags)
501 {
502 UINT32 nums[14];
503 char* passStub = NULL;
504 char set1[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*_";
505 char set2[12] = "!@#$&^*()-+=";
506 char set3[10] = "0123456789";
507 char set4[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
508 char set5[26] = "abcdefghijklmnopqrstuvwxyz";
509 passStub = (char*)malloc(15);
510
511 if (!passStub)
512 return NULL;
513
514 /**
515 * PassStub generation:
516 *
517 * Characters 0 and 5-13 are from the set A-Z a-z 0-9 * _
518 * Character 1 is from the set !@#$&^*()-+=
519 * Character 2 is from the set 0-9
520 * Character 3 is from the set A-Z
521 * Character 4 is from the set a-z
522 *
523 * Example: WB^6HsrIaFmEpi
524 */
525 winpr_RAND((BYTE*)nums, sizeof(nums));
526 passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */
527 passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */
528 passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */
529 passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */
530 passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */
531 passStub[5] = set1[nums[5] % sizeof(set1)]; /* character 5 */
532 passStub[6] = set1[nums[6] % sizeof(set1)]; /* character 6 */
533 passStub[7] = set1[nums[7] % sizeof(set1)]; /* character 7 */
534 passStub[8] = set1[nums[8] % sizeof(set1)]; /* character 8 */
535 passStub[9] = set1[nums[9] % sizeof(set1)]; /* character 9 */
536 passStub[10] = set1[nums[10] % sizeof(set1)]; /* character 10 */
537 passStub[11] = set1[nums[11] % sizeof(set1)]; /* character 11 */
538 passStub[12] = set1[nums[12] % sizeof(set1)]; /* character 12 */
539 passStub[13] = set1[nums[13] % sizeof(set1)]; /* character 13 */
540 passStub[14] = '\0';
541 return passStub;
542 }
543
freerdp_assistance_encrypt_pass_stub(const char * password,const char * passStub,size_t * pEncryptedSize)544 BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
545 size_t* pEncryptedSize)
546 {
547 BOOL rc;
548 int status;
549 size_t cbPasswordW;
550 size_t cbPassStubW;
551 size_t EncryptedSize;
552 BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH];
553 WINPR_CIPHER_CTX* rc4Ctx = NULL;
554 BYTE* pbIn = NULL;
555 BYTE* pbOut = NULL;
556 size_t cbOut, cbIn, cbFinal;
557 WCHAR* PasswordW = NULL;
558 WCHAR* PassStubW = NULL;
559 status = ConvertToUnicode(CP_UTF8, 0, password, -1, &PasswordW, 0);
560
561 if (status <= 0)
562 return NULL;
563
564 cbPasswordW = (size_t)(status - 1) * 2UL;
565
566 if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*)PasswordHash,
567 sizeof(PasswordHash)))
568 goto fail;
569
570 status = ConvertToUnicode(CP_UTF8, 0, passStub, -1, &PassStubW, 0);
571
572 if (status <= 0)
573 goto fail;
574
575 cbPassStubW = (size_t)(status - 1) * 2UL;
576 EncryptedSize = cbPassStubW + 4;
577 pbIn = (BYTE*)calloc(1, EncryptedSize);
578 pbOut = (BYTE*)calloc(1, EncryptedSize);
579
580 if (!pbIn || !pbOut)
581 goto fail;
582
583 *((UINT32*)pbIn) = (UINT32)cbPassStubW;
584 CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
585 rc4Ctx = winpr_Cipher_New(WINPR_CIPHER_ARC4_128, WINPR_ENCRYPT, PasswordHash, NULL);
586
587 if (!rc4Ctx)
588 {
589 WLog_ERR(TAG, "winpr_Cipher_New failure");
590 goto fail;
591 }
592
593 cbOut = cbFinal = 0;
594 cbIn = EncryptedSize;
595 rc = winpr_Cipher_Update(rc4Ctx, pbIn, cbIn, pbOut, &cbOut);
596
597 if (!rc)
598 {
599 WLog_ERR(TAG, "winpr_Cipher_Update failure");
600 goto fail;
601 }
602
603 if (!winpr_Cipher_Final(rc4Ctx, pbOut + cbOut, &cbFinal))
604 {
605 WLog_ERR(TAG, "winpr_Cipher_Final failure");
606 goto fail;
607 }
608
609 winpr_Cipher_Free(rc4Ctx);
610 free(pbIn);
611 free(PasswordW);
612 free(PassStubW);
613 *pEncryptedSize = EncryptedSize;
614 return pbOut;
615 fail:
616 winpr_Cipher_Free(rc4Ctx);
617 free(PasswordW);
618 free(PassStubW);
619 free(pbIn);
620 free(pbOut);
621 return NULL;
622 }
623
freerdp_assistance_decrypt2(rdpAssistanceFile * file,const char * password)624 static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file, const char* password)
625 {
626 BOOL rc = FALSE;
627 int status = 0;
628 size_t cbPasswordW;
629 int cchOutW = 0;
630 WCHAR* pbOutW = NULL;
631 WINPR_CIPHER_CTX* aesDec = NULL;
632 WCHAR* PasswordW = NULL;
633 BYTE* pbIn = NULL;
634 BYTE* pbOut = NULL;
635 size_t cbOut, cbIn, cbFinal;
636 BYTE DerivedKey[WINPR_AES_BLOCK_SIZE];
637 BYTE InitializationVector[WINPR_AES_BLOCK_SIZE];
638 BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH];
639
640 if (!file || !password)
641 return FALSE;
642
643 status = ConvertToUnicode(CP_UTF8, 0, password, -1, &PasswordW, 0);
644
645 if (status <= 0)
646 {
647 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
648 return FALSE;
649 }
650
651 cbPasswordW = (size_t)(status - 1) * 2UL;
652
653 if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash,
654 sizeof(PasswordHash)))
655 goto fail;
656
657 if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash), DerivedKey,
658 sizeof(DerivedKey)))
659 goto fail;
660
661 ZeroMemory(InitializationVector, sizeof(InitializationVector));
662 aesDec =
663 winpr_Cipher_New(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, DerivedKey, InitializationVector);
664
665 if (!aesDec)
666 goto fail;
667
668 cbOut = cbFinal = 0;
669 cbIn = (size_t)file->EncryptedLHTicketLength;
670 pbIn = (BYTE*)file->EncryptedLHTicket;
671 pbOut = (BYTE*)calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);
672
673 if (!pbOut)
674 goto fail;
675
676 if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
677 goto fail;
678
679 if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
680 {
681 WLog_ERR(TAG, "winpr_Cipher_Final failure");
682 goto fail;
683 }
684
685 cbOut += cbFinal;
686 cbFinal = 0;
687 pbOutW = (WCHAR*)pbOut;
688
689 if (cbOut > INT_MAX / 2)
690 goto fail;
691
692 cchOutW = (int)cbOut / 2;
693 file->ConnectionString2 = NULL;
694 status =
695 ConvertFromUnicode(CP_UTF8, 0, pbOutW, cchOutW, &file->ConnectionString2, 0, NULL, NULL);
696
697 if (status <= 0)
698 {
699 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed");
700 goto fail;
701 }
702
703 if (!freerdp_assistance_parse_connection_string2(file))
704 goto fail;
705
706 rc = TRUE;
707 fail:
708 winpr_Cipher_Free(aesDec);
709 free(PasswordW);
710 free(pbOut);
711 WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
712 return rc;
713 }
714
freerdp_assistance_hex_string_to_bin(const void * raw,size_t * size)715 BYTE* freerdp_assistance_hex_string_to_bin(const void* raw, size_t* size)
716 {
717 size_t length;
718 BYTE* buffer;
719 size_t i;
720 const char* str = (const char*)raw;
721 length = strlen(str);
722
723 if ((length % 2) != 0)
724 return NULL;
725
726 length /= 2;
727 *size = length;
728 buffer = (BYTE*)malloc(length);
729
730 if (!buffer)
731 return NULL;
732
733 for (i = 0; i < length; i++)
734 {
735 int hn, ln;
736 char c;
737 hn = ln = 0;
738 c = str[(i * 2) + 0];
739
740 if ((c >= '0') && (c <= '9'))
741 hn = c - '0';
742 else if ((c >= 'a') && (c <= 'f'))
743 hn = (c - 'a') + 10;
744 else if ((c >= 'A') && (c <= 'F'))
745 hn = (c - 'A') + 10;
746
747 c = str[(i * 2) + 1];
748
749 if ((c >= '0') && (c <= '9'))
750 ln = c - '0';
751 else if ((c >= 'a') && (c <= 'f'))
752 ln = (c - 'a') + 10;
753 else if ((c >= 'A') && (c <= 'F'))
754 ln = (c - 'A') + 10;
755
756 buffer[i] = ((hn << 4) | ln) & 0xFF;
757 }
758
759 return buffer;
760 }
761
freerdp_assistance_bin_to_hex_string(const void * raw,size_t size)762 char* freerdp_assistance_bin_to_hex_string(const void* raw, size_t size)
763 {
764 size_t i;
765 char* p;
766 int ln, hn;
767 const char* data = (const char*)raw;
768 char bin2hex[] = "0123456789ABCDEF";
769 p = (char*)calloc((size + 1), 2);
770
771 if (!p)
772 return NULL;
773
774 for (i = 0; i < size; i++)
775 {
776 ln = data[i] & 0xF;
777 hn = (data[i] >> 4) & 0xF;
778 p[i * 2] = bin2hex[hn];
779 p[(i * 2) + 1] = bin2hex[ln];
780 }
781
782 p[size * 2] = '\0';
783 return p;
784 }
785
freerdp_assistance_parse_file_buffer(rdpAssistanceFile * file,const char * buffer,size_t size,const char * password)786 int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* buffer, size_t size,
787 const char* password)
788 {
789 char* p;
790 char* q;
791 char* r;
792 int status;
793 size_t length;
794
795 free(file->password);
796 file->password = _strdup(password);
797
798 p = strstr(buffer, "UPLOADINFO");
799
800 if (p)
801 {
802 p = strstr(p + sizeof("UPLOADINFO") - 1, "TYPE=\"");
803
804 if (!p)
805 {
806 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing UPLOADINFO TYPE");
807 return -1;
808 }
809
810 p = strstr(buffer, "UPLOADDATA");
811
812 if (!p)
813 {
814 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing UPLOADDATA");
815 return -1;
816 }
817
818 /* Parse USERNAME */
819 p = strstr(buffer, "USERNAME=\"");
820
821 if (p)
822 {
823 p += sizeof("USERNAME=\"") - 1;
824 q = strchr(p, '"');
825
826 if (!q)
827 {
828 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid USERNAME=%s", p);
829 return -1;
830 }
831
832 if (p > q)
833 {
834 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
835 "order for USERNAME");
836 return -1;
837 }
838
839 length = q - p;
840 file->Username = (char*)malloc(length + 1);
841
842 if (!file->Username)
843 return -1;
844
845 CopyMemory(file->Username, p, length);
846 file->Username[length] = '\0';
847 }
848
849 /* Parse LHTICKET */
850 p = strstr(buffer, "LHTICKET=\"");
851
852 if (p)
853 {
854 p += sizeof("LHTICKET=\"") - 1;
855 q = strchr(p, '"');
856
857 if (!q)
858 {
859 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid LHTICKET=%s", p);
860 return -1;
861 }
862
863 if (p > q)
864 {
865 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
866 "order for LHTICKET");
867 return -1;
868 }
869
870 length = q - p;
871 file->LHTicket = (char*)malloc(length + 1);
872
873 if (!file->LHTicket)
874 return -1;
875
876 CopyMemory(file->LHTicket, p, length);
877 file->LHTicket[length] = '\0';
878 }
879
880 /* Parse RCTICKET */
881 p = strstr(buffer, "RCTICKET=\"");
882
883 if (p)
884 {
885 p += sizeof("RCTICKET=\"") - 1;
886 q = strchr(p, '"');
887
888 if (!q)
889 {
890 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid RCTICKET=%s", p);
891 return -1;
892 }
893
894 if (p > q)
895 {
896 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
897 "order for RCTICKET");
898 return -1;
899 }
900
901 length = q - p;
902 file->RCTicket = (char*)malloc(length + 1);
903
904 if (!file->RCTicket)
905 return -1;
906
907 CopyMemory(file->RCTicket, p, length);
908 file->RCTicket[length] = '\0';
909 }
910
911 /* Parse RCTICKETENCRYPTED */
912 p = strstr(buffer, "RCTICKETENCRYPTED=\"");
913
914 if (p)
915 {
916 p += sizeof("RCTICKETENCRYPTED=\"") - 1;
917 q = strchr(p, '"');
918
919 if (!q)
920 {
921 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid RCTICKETENCRYPTED=%s", p);
922 return -1;
923 }
924
925 if (p > q)
926 {
927 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
928 "order for RCTICKETENCRYPTED");
929 return -1;
930 }
931
932 length = q - p;
933
934 if ((length == 1) && (p[0] == '1'))
935 file->RCTicketEncrypted = TRUE;
936 }
937
938 /* Parse PassStub */
939 p = strstr(buffer, "PassStub=\"");
940
941 if (p)
942 {
943 p += sizeof("PassStub=\"") - 1;
944 q = strchr(p, '"');
945
946 if (!q)
947 {
948 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid PassStub=%s", p);
949 return -1;
950 }
951
952 if (p > q)
953 {
954 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
955 "order for PassStub");
956 return -1;
957 }
958
959 length = q - p;
960 file->PassStub = (char*)malloc(length + 1);
961
962 if (!file->PassStub)
963 return -1;
964
965 CopyMemory(file->PassStub, p, length);
966 file->PassStub[length] = '\0';
967 }
968
969 /* Parse DtStart */
970 p = strstr(buffer, "DtStart=\"");
971
972 if (p)
973 {
974 p += sizeof("DtStart=\"") - 1;
975 q = strchr(p, '"');
976
977 if (!q)
978 {
979 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtStart=%s", p);
980 return -1;
981 }
982
983 if (p > q)
984 {
985 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
986 "order for DtStart");
987 return -1;
988 }
989
990 length = q - p;
991 r = (char*)malloc(length + 1);
992
993 if (!r)
994 return -1;
995
996 CopyMemory(r, p, length);
997 r[length] = '\0';
998 errno = 0;
999 {
1000 unsigned long val = strtoul(r, NULL, 0);
1001
1002 if ((errno != 0) || (val > UINT32_MAX))
1003 {
1004 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtStart value %s", r);
1005 free(r);
1006 return -1;
1007 }
1008
1009 free(r);
1010 file->DtStart = val;
1011 }
1012 }
1013
1014 /* Parse DtLength */
1015 p = strstr(buffer, "DtLength=\"");
1016
1017 if (p)
1018 {
1019 p += sizeof("DtLength=\"") - 1;
1020 q = strchr(p, '"');
1021
1022 if (!q)
1023 {
1024 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtLength=%s", p);
1025 return -1;
1026 }
1027
1028 if (p > q)
1029 {
1030 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
1031 "order for DtLength");
1032 return -1;
1033 }
1034
1035 length = q - p;
1036 r = (char*)malloc(length + 1);
1037
1038 if (!r)
1039 return -1;
1040
1041 CopyMemory(r, p, length);
1042 r[length] = '\0';
1043 errno = 0;
1044 {
1045 unsigned long val = strtoul(r, NULL, 0);
1046
1047 if ((errno != 0) || (val > UINT32_MAX))
1048 {
1049 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtLength value %s", r);
1050 free(r);
1051 return -1;
1052 }
1053
1054 free(r);
1055 file->DtLength = val;
1056 }
1057 }
1058
1059 /* Parse L (LowSpeed) */
1060 p = strstr(buffer, " L=\"");
1061
1062 if (p)
1063 {
1064 p += sizeof(" L=\"") - 1;
1065 q = strchr(p, '"');
1066
1067 if (!q)
1068 {
1069 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid L=%s", p);
1070 return -1;
1071 }
1072
1073 if (p > q)
1074 {
1075 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field "
1076 "order for L");
1077 return -1;
1078 }
1079
1080 length = q - p;
1081
1082 if ((length == 1) && (p[0] == '1'))
1083 file->LowSpeed = TRUE;
1084 }
1085
1086 file->Type = (file->LHTicket) ? 2 : 1;
1087 status = 0;
1088
1089 switch (file->Type)
1090 {
1091 case 2:
1092 {
1093 file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(
1094 file->LHTicket, &file->EncryptedLHTicketLength);
1095
1096 if (!freerdp_assistance_decrypt2(file, password))
1097 status = -1;
1098 }
1099 break;
1100
1101 case 1:
1102 {
1103 if (!freerdp_assistance_parse_connection_string1(file))
1104 status = -1;
1105 }
1106 break;
1107
1108 default:
1109 return -1;
1110 }
1111
1112 if (status < 0)
1113 {
1114 WLog_ERR(TAG, "freerdp_assistance_parse_connection_string1 failure: %d", status);
1115 return -1;
1116 }
1117
1118 file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(
1119 password, file->PassStub, &file->EncryptedPassStubLength);
1120
1121 if (!file->EncryptedPassStub)
1122 return -1;
1123
1124 return 1;
1125 }
1126
1127 p = strstr(buffer, "<E>");
1128
1129 if (p)
1130 {
1131 q = strstr(buffer, "</E>");
1132
1133 if (!q)
1134 {
1135 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing </E> tag");
1136 return -1;
1137 }
1138
1139 if (p > q)
1140 {
1141 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field order for <E>");
1142 return -1;
1143 }
1144
1145 q += sizeof("</E>") - 1;
1146 length = q - p;
1147 file->ConnectionString2 = (char*)malloc(length + 1);
1148
1149 if (!file->ConnectionString2)
1150 return -1;
1151
1152 CopyMemory(file->ConnectionString2, p, length);
1153 file->ConnectionString2[length] = '\0';
1154
1155 if (!freerdp_assistance_parse_connection_string2(file))
1156 return -1;
1157
1158 return 1;
1159 }
1160
1161 WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor <E> found");
1162 return -1;
1163 }
1164
freerdp_assistance_parse_file(rdpAssistanceFile * file,const char * name,const char * password)1165 int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
1166 {
1167 int status;
1168 BYTE* buffer;
1169 FILE* fp = NULL;
1170 size_t readSize;
1171 INT64 fileSize;
1172
1173 if (!name)
1174 {
1175 WLog_ERR(TAG, "ASSISTANCE file %s invalid name", name);
1176 return -1;
1177 }
1178
1179 free(file->filename);
1180 file->filename = _strdup(name);
1181 fp = winpr_fopen(name, "r");
1182
1183 if (!fp)
1184 {
1185 WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name);
1186 return -1;
1187 }
1188
1189 _fseeki64(fp, 0, SEEK_END);
1190 fileSize = _ftelli64(fp);
1191 _fseeki64(fp, 0, SEEK_SET);
1192
1193 if (fileSize < 1)
1194 {
1195 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1196 fclose(fp);
1197 return -1;
1198 }
1199
1200 buffer = (BYTE*)malloc(fileSize + 2);
1201
1202 if (!buffer)
1203 {
1204 fclose(fp);
1205 return -1;
1206 }
1207
1208 readSize = fread(buffer, fileSize, 1, fp);
1209
1210 if (!readSize)
1211 {
1212 if (!ferror(fp))
1213 readSize = fileSize;
1214 }
1215
1216 fclose(fp);
1217
1218 if (readSize < 1)
1219 {
1220 WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name);
1221 free(buffer);
1222 buffer = NULL;
1223 return -1;
1224 }
1225
1226 buffer[fileSize] = '\0';
1227 buffer[fileSize + 1] = '\0';
1228 status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize, password);
1229 free(buffer);
1230 return status;
1231 }
1232
freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile * file,rdpSettings * settings)1233 BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file,
1234 rdpSettings* settings)
1235 {
1236 UINT32 i;
1237
1238 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
1239 return FALSE;
1240
1241 if (!file->RASessionId || !file->MachineAddresses)
1242 return FALSE;
1243
1244 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId,
1245 file->RASessionId))
1246 return FALSE;
1247
1248 if (file->RCTicket)
1249 {
1250 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1251 file->RCTicket))
1252 return FALSE;
1253 }
1254 else
1255 {
1256 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket,
1257 file->ConnectionString2))
1258 return FALSE;
1259 }
1260
1261 if (file->PassStub)
1262 {
1263 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub,
1264 file->PassStub))
1265 return FALSE;
1266 }
1267
1268 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, file->MachineAddresses[0]))
1269 return FALSE;
1270
1271 if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename))
1272 return FALSE;
1273
1274 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password))
1275 return FALSE;
1276
1277 if (file->Username)
1278 {
1279 if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username))
1280 return FALSE;
1281 }
1282
1283 settings->RemoteAssistanceMode = TRUE;
1284
1285 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, file->MachinePorts[0]))
1286 return FALSE;
1287
1288 freerdp_target_net_addresses_free(settings);
1289 settings->TargetNetAddressCount = file->MachineCount;
1290
1291 if (settings->TargetNetAddressCount)
1292 {
1293 settings->TargetNetAddresses = (char**)calloc(file->MachineCount, sizeof(char*));
1294 settings->TargetNetPorts = (UINT32*)calloc(file->MachineCount, sizeof(UINT32));
1295
1296 if (!settings->TargetNetAddresses || !settings->TargetNetPorts)
1297 return FALSE;
1298
1299 for (i = 0; i < settings->TargetNetAddressCount; i++)
1300 {
1301 settings->TargetNetAddresses[i] = _strdup(file->MachineAddresses[i]);
1302 settings->TargetNetPorts[i] = file->MachinePorts[i];
1303
1304 if (!settings->TargetNetAddresses[i])
1305 return FALSE;
1306 }
1307 }
1308
1309 return TRUE;
1310 }
1311
freerdp_assistance_file_new(void)1312 rdpAssistanceFile* freerdp_assistance_file_new(void)
1313 {
1314 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
1315 return (rdpAssistanceFile*)calloc(1, sizeof(rdpAssistanceFile));
1316 }
1317
freerdp_assistance_file_free(rdpAssistanceFile * file)1318 void freerdp_assistance_file_free(rdpAssistanceFile* file)
1319 {
1320 UINT32 i;
1321
1322 if (!file)
1323 return;
1324
1325 free(file->filename);
1326 free(file->password);
1327 free(file->Username);
1328 free(file->LHTicket);
1329 free(file->RCTicket);
1330 free(file->PassStub);
1331 free(file->ConnectionString1);
1332 free(file->ConnectionString2);
1333 free(file->EncryptedLHTicket);
1334 free(file->RASessionId);
1335 free(file->RASpecificParams);
1336 free(file->EncryptedPassStub);
1337
1338 for (i = 0; i < file->MachineCount; i++)
1339 {
1340 free(file->MachineAddresses[i]);
1341 }
1342
1343 free(file->MachineAddresses);
1344 free(file->MachinePorts);
1345 free(file);
1346 }
1347
freerdp_assistance_print_file(rdpAssistanceFile * file,wLog * log,DWORD level)1348 void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level)
1349 {
1350 size_t x;
1351 WLog_Print(log, level, "Username: %s", file->Username);
1352 WLog_Print(log, level, "LHTicket: %s", file->LHTicket);
1353 WLog_Print(log, level, "RCTicket: %s", file->RCTicket);
1354 WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted);
1355 WLog_Print(log, level, "PassStub: %s", file->PassStub);
1356 WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart);
1357 WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength);
1358 WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed);
1359 WLog_Print(log, level, "RASessionId: %s", file->RASessionId);
1360 WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams);
1361
1362 for (x = 0; x < file->MachineCount; x++)
1363 {
1364 WLog_Print(log, level, "MachineAddress [%" PRIdz ": %s", x, file->MachineAddresses[x]);
1365 WLog_Print(log, level, "MachinePort [%" PRIdz ": %" PRIu32, x, file->MachinePorts[x]);
1366 }
1367 }
1368
freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile * file,const char ** pwd,size_t * size)1369 BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd,
1370 size_t* size)
1371 {
1372 if (!file || !pwd || !size)
1373 return FALSE;
1374
1375 *pwd = (const char*)file->EncryptedPassStub;
1376 *size = file->EncryptedPassStubLength;
1377 return TRUE;
1378 }
1379
freerdp_assistance_set_connection_string2(rdpAssistanceFile * file,const char * string,const char * password)1380 int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string,
1381 const char* password)
1382 {
1383 if (!file || !string || !password)
1384 return -1;
1385
1386 free(file->ConnectionString2);
1387 free(file->password);
1388 file->ConnectionString2 = _strdup(string);
1389 file->password = _strdup(password);
1390 return freerdp_assistance_parse_connection_string2(file);
1391 }
1392