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