<commit_before>/**
 * FreeRDP: A Remote Desktop Protocol Implementation
 * Remote Assistance
 *
 * Copyright 2014 <NAME> <<EMAIL>>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>

#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/crypto.h>
#include <winpr/print.h>
#include <winpr/windows.h>

#include <freerdp/log.h>
#include <freerdp/client/file.h>
#include <freerdp/client/cmdline.h>

#include <freerdp/assistance.h>

#define TAG FREERDP_TAG("common")


struct rdp_assistance_file
{
	UINT32 Type;

	char* Username;
	char* LHTicket;
	char* RCTicket;
	char* PassStub;
	UINT32 DtStart;
	UINT32 DtLength;
	BOOL LowSpeed;
	BOOL RCTicketEncrypted;

	char* ConnectionString1;
	char* ConnectionString2;

	BYTE* EncryptedPassStub;
	size_t EncryptedPassStubLength;

	BYTE* EncryptedLHTicket;
	size_t EncryptedLHTicketLength;

	UINT32 MachineCount;
	char** MachineAddresses;
	UINT32* MachinePorts;

	char* RASessionId;
	char* RASpecificParams;

	char* filename;
	char* password;
};

/**
 * Password encryption in establishing a remote assistance session of type 1:
 * http://blogs.msdn.com/b/openspecification/archive/2011/10/31/password-encryption-in-establishing-a-remote-assistance-session-of-type-1.aspx
 *
 * Creation of PassStub for the Remote Assistance Ticket:
 * http://social.msdn.microsoft.com/Forums/en-US/6316c3f4-ea09-4343-a4a1-9cca46d70d28/creation-of-passstub-for-the-remote-assistance-ticket?forum=os_windowsprotocols
 */

/**
 * CryptDeriveKey Function:
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916/
 *
 * Let n be the required derived key length, in bytes.
 * The derived key is the first n bytes of the hash value after the hash computation
 * has been completed by CryptDeriveKey. If the hash is not a member of the SHA-2
 * family and the required key is for either 3DES or AES, the key is derived as follows:
 *
 * Form a 64-byte buffer by repeating the constant 0x36 64 times.
 * Let k be the length of the hash value that is represented by the input parameter hBaseData.
 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
 * of the buffer with the hash value that is represented by the input parameter hBaseData.
 *
 * Form a 64-byte buffer by repeating the constant 0x5C 64 times.
 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
 * of the buffer with the hash value that is represented by the input parameter hBaseData.
 *
 * Hash the result of step 1 by using the same hash algorithm as that used to compute the hash
 * value that is represented by the hBaseData parameter.
 *
 * Hash the result of step 2 by using the same hash algorithm as that used to compute the hash
 * value that is represented by the hBaseData parameter.
 *
 * Concatenate the result of step 3 with the result of step 4.
 * Use the first n bytes of the result of step 5 as the derived key.
 */

static BOOL freerdp_assistance_crypt_derive_key_sha1(BYTE* hash, size_t hashLength, BYTE* key,
        size_t keyLength)
{
	BOOL rc = FALSE;
	size_t i;
	BYTE* buffer;
	BYTE pad1[64];
	BYTE pad2[64];
	memset(pad1, 0x36, 64);
	memset(pad2, 0x5C, 64);

	for (i = 0; i < hashLength; i++)
	{
		pad1[i] ^= hash[i];
		pad2[i] ^= hash[i];
	}

	buffer = (BYTE*) calloc(hashLength, 2);

	if (!buffer)
		goto fail;

	if (!winpr_Digest(WINPR_MD_SHA1, pad1, 64, buffer, hashLength))
		goto fail;

	if (!winpr_Digest(WINPR_MD_SHA1, pad2, 64, &buffer[hashLength], hashLength))
		goto fail;

	CopyMemory(key, buffer, keyLength);
	rc = TRUE;
fail:
	free(buffer);
	return rc;
}

static BOOL reallocate(rdpAssistanceFile* file, const char* host, UINT32 port)
{
	void* tmp1, *tmp2;
	file->MachineCount++;
	tmp1 = realloc(file->MachinePorts, sizeof(UINT32) * file->MachineCount);
	tmp2 = realloc(file->MachineAddresses, sizeof(char*) * file->MachineCount);

	if (!tmp1 || !tmp2)
		return FALSE;

	file->MachinePorts = tmp1;
	file->MachineAddresses = tmp2;
	file->MachinePorts[file->MachineCount - 1] = port;
	file->MachineAddresses[file->MachineCount - 1] = _strdup(host);
	return TRUE;
}

static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port)
{
	unsigned long p;

	if (!file || !host || !port)
		return FALSE;

	errno = 0;
	p = strtoul(port, NULL, 0);

	if ((errno != 0) || (p == 0) || (p > UINT16_MAX))
		return FALSE;

	return reallocate(file, host, p);
}

static BOOL freerdp_assistance_parse_address_list(rdpAssistanceFile* file, const char* list)
{
	BOOL rc = FALSE;
	char* p;
	char* str;

	if (!file || !list)
		return FALSE;

	str = _strdup(list);

	if (!str)
		return FALSE;

	p =	str;

	while ((p = strchr(p, ';')) != NULL)
	{
		char* q = strchr(p, ':');

		if (!q)
			goto out;

		*q = '\0';
		q++;

		if (!append_address(file, p, q))
			goto out;

		p = q;
	}

	rc = TRUE;
out:
	free(str);
	return TRUE;
}

static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file)
{
	size_t i;
	char* str;
	int count;
	size_t length;
	char* tokens[8];
	BOOL rc = FALSE;

	if (!file || !file->RCTicket)
		return FALSE;

	/**
	 * <ProtocolVersion>,<protocolType>,<machineAddressList>,<assistantAccountPwd>,
	 * <RASessionID>,<RASessionName>,<RASessionPwd>,<protocolSpecificParms>
	 */
	count = 1;
	str = _strdup(file->RCTicket);

	if (!str)
		goto error;

	length = strlen(str);

	for (i = 0; i < length; i++)
	{
		if (str[i] == ',')
			count++;
	}

	if (count != 8)
		goto error;

	count = 0;
	tokens[count++] = str;

	for (i = 0; i < length; i++)
	{
		if (str[i] == ',')
		{
			str[i] = '\0';
			tokens[count++] = &str[i + 1];
		}
	}

	if (strcmp(tokens[0], "65538") != 0)
		goto error;

	if (strcmp(tokens[1], "1") != 0)
		goto error;

	if (strcmp(tokens[3], "*") != 0)
		goto error;

	if (strcmp(tokens[5], "*") != 0)
		goto error;

	if (strcmp(tokens[6], "*") != 0)
		goto error;

	file->RASessionId = _strdup(tokens[4]);

	if (!file->RASessionId)
		goto error;

	file->RASpecificParams = _strdup(tokens[7]);

	if (!file->RASpecificParams)
		goto error;

	if (!freerdp_assistance_parse_address_list(file, tokens[2]))
		goto error;

	rc = TRUE;
error:
	free(str);
	return rc;
}

/**
 * Decrypted Connection String 2:
 *
 * <E>
 * <A KH="BNRjdu97DyczQSRuMRrDWoue+HA=" ID="+ULZ6ifjoCa6cGPMLQiGHRPwkg6VyJqGwxMnO6GcelwUh9a6/FBq3It5ADSndmLL"/>
 * <C>
 * <T ID="1" SID="0">
 * 	<L P="49228" N="fe80::1032:53d9:5a01:909b%3"/>
 * 	<L P="49229" N="fe80::3d8f:9b2d:6b4e:6aa%6"/>
 * 	<L P="49230" N="192.168.1.200"/>
 * 	<L P="49231" N="169.254.6.170"/>
 * </T>
 * </C>
 * </E>
 */

static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
{
	char* str;
	char* tag;
	char* end;
	char* p;
	BOOL rc = FALSE;

	if (!file || !file->ConnectionString2)
		return FALSE;

	str = file->ConnectionString2;

	if (!strstr(str, "<E>"))
		return FALSE;

	if (!strstr(str, "<C>"))
		return FALSE;

	str = _strdup(file->ConnectionString2);

	if (!str)
		goto out_fail;

	if (!(tag = strstr(str, "<A")))
		goto out_fail;

	/* Parse Auth String Node (<A>) */
	end = strstr(tag, "/>");

	if (!end)
		goto out_fail;

	*end = '\0';
	p = strstr(tag, "KH=\"");

	if (p)
	{
		char* q;
		size_t length;
		p += sizeof("KH=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		length = q - p;
		free(file->RASpecificParams);
		file->RASpecificParams = (char*) malloc(length + 1);

		if (!file->RASpecificParams)
			goto out_fail;

		CopyMemory(file->RASpecificParams, p, length);
		file->RASpecificParams[length] = '\0';
	}

	p = strstr(tag, "ID=\"");

	if (p)
	{
		char* q;
		size_t length;
		p += sizeof("ID=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		length = q - p;
		free(file->RASessionId);
		file->RASessionId = (char*) malloc(length + 1);

		if (!file->RASessionId)
			goto out_fail;

		CopyMemory(file->RASessionId, p, length);
		file->RASessionId[length] = '\0';
	}

	*end = '/';
	/* Parse <L  last address is used */
	p = strstr(str, "<L P=\"");

	while (p)
	{
		char* port;
		char* q;
		size_t length;
		p += sizeof("<L P=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		q[0] = '\0';
		q++;
		port = p;
		p = strstr(q, " N=\"");

		if (!p)
			goto out_fail;

		p += sizeof(" N=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		q[0] = '\0';
		q++;
		length = strlen(p);

		if (length > 8)
		{
			if (!append_address(file, p, port))
				goto out_fail;
		}

		p = strstr(q, "<L P=\"");
	}

	rc = TRUE;
out_fail:
	free(str);
	return rc;
}

char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
{
	size_t size;
	size_t nameLength;
	size_t passLength;
	char* ExpertBlob = NULL;

	if (!name || !pass)
		return NULL;

	nameLength = strlen(name) + strlen("NAME=");
	passLength = strlen(pass) + strlen("PASS=");
	size = nameLength + passLength + 64;
	ExpertBlob = (char*) calloc(1, size);

	if (!ExpertBlob)
		return NULL;

	sprintf_s(ExpertBlob, size, "%"PRIdz";NAME=%s%"PRIdz";PASS=%s",
	          nameLength, name, passLength, pass);
	return ExpertBlob;
}

char* freerdp_assistance_generate_pass_stub(DWORD flags)
{
	UINT32 nums[14];
	char* passStub = NULL;
	char set1[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*_";
	char set2[12] = "!@#$&^*()-+=";
	char set3[10] = "0123456789";
	char set4[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	char set5[26] = "abcdefghijklmnopqrstuvwxyz";
	passStub = (char*) malloc(15);

	if (!passStub)
		return NULL;

	/**
	 * PassStub generation:
	 *
	 * Characters 0 and 5-13 are from the set A-Z a-z 0-9 * _
	 * Character 1 is from the set !@#$&^*()-+=
	 * Character 2 is from the set 0-9
	 * Character 3 is from the set A-Z
	 * Character 4 is from the set a-z
	 *
	 * Example: WB^6HsrIaFmEpi
	 */
	winpr_RAND((BYTE*) nums, sizeof(nums));
	passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */
	passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */
	passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */
	passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */
	passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */
	passStub[5] = set1[nums[5] % sizeof(set1)]; /* character 5 */
	passStub[6] = set1[nums[6] % sizeof(set1)]; /* character 6 */
	passStub[7] = set1[nums[7] % sizeof(set1)]; /* character 7 */
	passStub[8] = set1[nums[8] % sizeof(set1)]; /* character 8 */
	passStub[9] = set1[nums[9] % sizeof(set1)]; /* character 9 */
	passStub[10] = set1[nums[10] % sizeof(set1)]; /* character 10 */
	passStub[11] = set1[nums[11] % sizeof(set1)]; /* character 11 */
	passStub[12] = set1[nums[12] % sizeof(set1)]; /* character 12 */
	passStub[13] = set1[nums[13] % sizeof(set1)]; /* character 13 */
	passStub[14] = '\0';
	return passStub;
}

BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
        size_t* pEncryptedSize)
{
	BOOL rc;
	int status;
	size_t cbPasswordW;
	size_t cbPassStubW;
	size_t EncryptedSize;
	BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH];
	WINPR_CIPHER_CTX* rc4Ctx = NULL;
	BYTE* pbIn = NULL;
	BYTE* pbOut = NULL;
	size_t cbOut, cbIn, cbFinal;
	WCHAR* PasswordW = NULL;
	WCHAR* PassStubW = NULL;
	status = ConvertToUnicode(CP_UTF8, 0, password, -1, &PasswordW, 0);

	if (status <= 0)
		return NULL;

	cbPasswordW = (size_t)(status - 1) * 2UL;

	if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*) PasswordHash,
	                  sizeof(PasswordHash)))
		goto fail;

	status = ConvertToUnicode(CP_UTF8, 0, passStub, -1, &PassStubW, 0);

	if (status <= 0)
		goto fail;

	cbPassStubW = (size_t)(status - 1) * 2UL;
	EncryptedSize = cbPassStubW + 4;
	pbIn = (BYTE*) calloc(1, EncryptedSize);
	pbOut = (BYTE*) calloc(1, EncryptedSize);

	if (!pbIn || !pbOut)
		goto fail;

	if ((EncryptedSize == 0) || (cbPassStubW > UINT32_MAX))
		goto fail;

	*((UINT32*) pbIn) = (UINT32)cbPassStubW;
	CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
	rc4Ctx = winpr_Cipher_New(WINPR_CIPHER_ARC4_128, WINPR_ENCRYPT,
	                          PasswordHash, NULL);

	if (!rc4Ctx)
	{
		WLog_ERR(TAG,  "EVP_CipherInit_ex failure");
		goto fail;
	}

	cbOut = cbFinal = 0;
	cbIn = EncryptedSize;
	rc = winpr_Cipher_Update(rc4Ctx, pbIn, cbIn, pbOut, &cbOut);

	if (!rc)
	{
		WLog_ERR(TAG,  "EVP_CipherUpdate failure");
		goto fail;
	}

	if (!winpr_Cipher_Final(rc4Ctx, pbOut + cbOut, &cbFinal))
	{
		WLog_ERR(TAG,  "EVP_CipherFinal_ex failure");
		goto fail;
	}

	winpr_Cipher_Free(rc4Ctx);
	free(pbIn);
	free(PasswordW);
	free(PassStubW);
	*pEncryptedSize = EncryptedSize;
	return pbOut;
fail:
	winpr_Cipher_Free(rc4Ctx);
	free(PasswordW);
	free(PassStubW);
	free(pbIn);
	free(pbOut);
	return NULL;
}

static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file, const char* password)
{
	BOOL rc = FALSE;
	int status = 0;
	size_t cbPasswordW;
	int cchOutW = 0;
	WCHAR* pbOutW = NULL;
	WINPR_CIPHER_CTX* aesDec = NULL;
	WCHAR* PasswordW = NULL;
	BYTE* pbIn = NULL;
	BYTE* pbOut = NULL;
	size_t cbOut, cbIn, cbFinal;
	BYTE DerivedKey[WINPR_AES_BLOCK_SIZE];
	BYTE InitializationVector[WINPR_AES_BLOCK_SIZE];
	BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH];

	if (!file || !password)
		return FALSE;

	status = ConvertToUnicode(CP_UTF8, 0, password, -1, &PasswordW, 0);

	if (status <= 0)
		return FALSE;

	cbPasswordW = (size_t)(status - 1) * 2UL;

	if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash, sizeof(PasswordHash)))
		goto fail;

	if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash),
	        DerivedKey, sizeof(DerivedKey)))
		goto fail;

	ZeroMemory(InitializationVector, sizeof(InitializationVector));
	aesDec = winpr_Cipher_New(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT,
	                          DerivedKey, InitializationVector);

	if (!aesDec)
		goto fail;

	cbOut = cbFinal = 0;
	cbIn = (size_t)file->EncryptedLHTicketLength;
	pbIn = (BYTE*) file->EncryptedLHTicket;
	pbOut = (BYTE*) calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);

	if (!pbOut)
		goto fail;

	if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
		goto fail;

	if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
	{
		WLog_ERR(TAG,  "EVP_DecryptFinal_ex failure");
		goto fail;
	}

	cbOut += cbFinal;
	cbFinal = 0;
	pbOutW = (WCHAR*) pbOut;

	if (cbOut > INT_MAX / 2)
		goto fail;

	cchOutW = (int)cbOut / 2;
	file->ConnectionString2 = NULL;
	status = ConvertFromUnicode(CP_UTF8, 0, pbOutW, cchOutW, &file->ConnectionString2, 0, NULL, NULL);

	if (status <= 0)
		goto fail;

	if (!freerdp_assistance_parse_connection_string2(file))
		goto fail;

	rc = TRUE;
fail:
	winpr_Cipher_Free(aesDec);
	free(PasswordW);
	free(pbOut);
	WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
	return rc;
}

int freerdp_assistance_decrypt(rdpAssistanceFile* file, const char* password)
{
	int status = 1;
	file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(password,
	                          file->PassStub, &file->EncryptedPassStubLength);

	if (!file->EncryptedPassStub)
		return -1;

	if (file->Type > 1)
	{
		if (!freerdp_assistance_decrypt2(file, password))
			status = -1;
	}

	return status;
}

BYTE* freerdp_assistance_hex_string_to_bin(const char* str, size_t* size)
{
	size_t length;
	BYTE* buffer;
	size_t i;
	length = strlen(str);

	if ((length % 2) != 0)
		return NULL;

	length /= 2;
	*size = length;
	buffer = (BYTE*) malloc(length);

	if (!buffer)
		return NULL;

	for (i = 0; i < length; i++)
	{
		int hn, ln;
		char c;
		hn = ln = 0;
		c = str[(i * 2) + 0];

		if ((c >= '0') && (c <= '9'))
			hn = c - '0';
		else if ((c >= 'a') && (c <= 'f'))
			hn = (c - 'a') + 10;
		else if ((c >= 'A') && (c <= 'F'))
			hn = (c - 'A') + 10;

		c = str[(i * 2) + 1];

		if ((c >= '0') && (c <= '9'))
			ln = c - '0';
		else if ((c >= 'a') && (c <= 'f'))
			ln = (c - 'a') + 10;
		else if ((c >= 'A') && (c <= 'F'))
			ln = (c - 'A') + 10;

		buffer[i] = ((hn << 4) | ln) & 0xFF;
	}

	return buffer;
}

char* freerdp_assistance_bin_to_hex_string(const BYTE* data, size_t size)
{
	size_t i;
	char* p;
	int ln, hn;
	char bin2hex[] = "0123456789ABCDEF";
	p = (char*) calloc((size + 1), 2);

	if (!p)
		return NULL;

	for (i = 0; i < size; i++)
	{
		ln = data[i] & 0xF;
		hn = (data[i] >> 4) & 0xF;
		p[i * 2] = bin2hex[hn];
		p[(i * 2) + 1] = bin2hex[ln];
	}

	p[size * 2] = '\0';
	return p;
}

int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* buffer, size_t size,
        const char* password)
{
	char* p;
	char* q;
	char* r;
	int status;
	size_t length;
	p = strstr(buffer, "UPLOADINFO");

	if (!p)
		return -1;

	p = strstr(p + sizeof("UPLOADINFO") - 1, "TYPE=\"");

	if (!p)
		return -1;

	p = strstr(buffer, "UPLOADDATA");

	if (!p)
		return -1;

	/* Parse USERNAME */
	p = strstr(buffer, "USERNAME=\"");

	if (p)
	{
		p += sizeof("USERNAME=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->Username = (char*) malloc(length + 1);

		if (!file->Username)
			return -1;

		CopyMemory(file->Username, p, length);
		file->Username[length] = '\0';
	}

	/* Parse LHTICKET */
	p = strstr(buffer, "LHTICKET=\"");

	if (p)
	{
		p += sizeof("LHTICKET=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->LHTicket = (char*) malloc(length + 1);

		if (!file->LHTicket)
			return -1;

		CopyMemory(file->LHTicket, p, length);
		file->LHTicket[length] = '\0';
	}

	/* Parse RCTICKET */
	p = strstr(buffer, "RCTICKET=\"");

	if (p)
	{
		p += sizeof("RCTICKET=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->RCTicket = (char*) malloc(length + 1);

		if (!file->RCTicket)
			return -1;

		CopyMemory(file->RCTicket, p, length);
		file->RCTicket[length] = '\0';
	}

	/* Parse RCTICKETENCRYPTED */
	p = strstr(buffer, "RCTICKETENCRYPTED=\"");

	if (p)
	{
		p += sizeof("RCTICKETENCRYPTED=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;

		if ((length == 1) && (p[0] == '1'))
			file->RCTicketEncrypted = TRUE;
	}

	/* Parse PassStub */
	p = strstr(buffer, "PassStub=\"");

	if (p)
	{
		p += sizeof("PassStub=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->PassStub = (char*) malloc(length + 1);

		if (!file->PassStub)
			return -1;

		CopyMemory(file->PassStub, p, length);
		file->PassStub[length] = '\0';
	}

	/* Parse DtStart */
	p = strstr(buffer, "DtStart=\"");

	if (p)
	{
		p += sizeof("DtStart=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		r = (char*) malloc(length + 1);

		if (!r)
			return -1;

		CopyMemory(r, p, length);
		r[length] = '\0';
		errno = 0;
		{
			unsigned long val = strtoul(r, NULL, 0);
			free(r);

			if ((errno != 0) || (val > UINT32_MAX))
				return -1;

			file->DtStart = val;
		}
	}

	/* Parse DtLength */
	p = strstr(buffer, "DtLength=\"");

	if (p)
	{
		p += sizeof("DtLength=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		r = (char*) malloc(length + 1);

		if (!r)
			return -1;

		CopyMemory(r, p, length);
		r[length] = '\0';
		errno = 0;
		{
			unsigned long val = strtoul(r, NULL, 0);
			free(r);

			if ((errno != 0) || (val > UINT32_MAX))
				return -1;

			file->DtLength = val;
		}
	}

	/* Parse L (LowSpeed) */
	p = strstr(buffer, " L=\"");

	if (p)
	{
		p += sizeof(" L=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;

		if ((length == 1) && (p[0] == '1'))
			file->LowSpeed = TRUE;
	}

	file->Type = (file->LHTicket) ? 2 : 1;
	status = 0;

	if (file->LHTicket)
	{
		file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(file->LHTicket,
		                          &file->EncryptedLHTicketLength);

		if (!freerdp_assistance_decrypt2(file, password))
			status = -1;
	}
	else
	{
		if (!freerdp_assistance_parse_connection_string1(file))
			status = -1;
	}

	if (status < 0)
	{
		WLog_ERR(TAG,  "freerdp_assistance_parse_connection_string1 failure: %d", status);
		return -1;
	}

	return 1;
}

int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
{
	int status;
	BYTE* buffer;
	FILE* fp = NULL;
	size_t readSize;
	INT64 fileSize;

	if (!name)
		return -1;

	free(file->filename);
	free(file->password);
	file->filename = _strdup(name);
	file->password = _strdup(password);
	fp = fopen(name, "r");

	if (!fp)
		return -1;

	_fseeki64(fp, 0, SEEK_END);
	fileSize = _ftelli64(fp);
	_fseeki64(fp, 0, SEEK_SET);

	if (fileSize < 1)
	{
		fclose(fp);
		return -1;
	}

	buffer = (BYTE*) malloc(fileSize + 2);

	if (!buffer)
	{
		fclose(fp);
		return -1;
	}

	readSize = fread(buffer, fileSize, 1, fp);

	if (!readSize)
	{
		if (!ferror(fp))
			readSize = fileSize;
	}

	fclose(fp);

	if (readSize < 1)
	{
		free(buffer);
		buffer = NULL;
		return -1;
	}

	buffer[fileSize] = '\0';
	buffer[fileSize + 1] = '\0';
	status = freerdp_assistance_parse_file_buffer(file, (char*) buffer, fileSize, password);
	free(buffer);
	return status;
}

BOOL freerdp_client_populate_settings_from_assistance_file(rdpAssistanceFile* file,
        rdpSettings* settings)
{
	UINT32 i;
	freerdp_set_param_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE);

	if (!file->RASessionId || !file->MachineAddresses)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_RemoteAssistanceSessionId, file->RASessionId) != 0)
		return FALSE;

	if (file->RCTicket &&
	    (freerdp_set_param_string(settings, FreeRDP_RemoteAssistanceRCTicket, file->RCTicket) != 0))
		return FALSE;

	if (file->PassStub)
	{
		if (freerdp_set_param_string(settings, FreeRDP_RemoteAssistancePassStub, file->PassStub) != 0)
			return FALSE;
	}

	if (freerdp_set_param_string(settings, FreeRDP_ServerHostname, file->MachineAddresses[0]) != 0)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_AssistanceFile, file->filename) != 0)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_RemoteAssistancePassword, file->password) != 0)
		return FALSE;

	settings->RemoteAssistanceMode = TRUE;
	freerdp_set_param_uint32(settings, FreeRDP_ServerPort, file->MachinePorts[0]);
	freerdp_target_net_addresses_free(settings);
	settings->TargetNetAddressCount = file->MachineCount;

	if (settings->TargetNetAddressCount)
	{
		settings->TargetNetAddresses = (char**) calloc(file->MachineCount, sizeof(char*));<commit_msg>Set username from assistance file.<commit_after>/**
 * FreeRDP: A Remote Desktop Protocol Implementation
 * Remote Assistance
 *
 * Copyright 2014 <NAME> <<EMAIL>>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>

#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/crypto.h>
#include <winpr/print.h>
#include <winpr/windows.h>

#include <freerdp/log.h>
#include <freerdp/client/file.h>
#include <freerdp/client/cmdline.h>

#include <freerdp/assistance.h>

#define TAG FREERDP_TAG("common")


struct rdp_assistance_file
{
	UINT32 Type;

	char* Username;
	char* LHTicket;
	char* RCTicket;
	char* PassStub;
	UINT32 DtStart;
	UINT32 DtLength;
	BOOL LowSpeed;
	BOOL RCTicketEncrypted;

	char* ConnectionString1;
	char* ConnectionString2;

	BYTE* EncryptedPassStub;
	size_t EncryptedPassStubLength;

	BYTE* EncryptedLHTicket;
	size_t EncryptedLHTicketLength;

	UINT32 MachineCount;
	char** MachineAddresses;
	UINT32* MachinePorts;

	char* RASessionId;
	char* RASpecificParams;

	char* filename;
	char* password;
};

/**
 * Password encryption in establishing a remote assistance session of type 1:
 * http://blogs.msdn.com/b/openspecification/archive/2011/10/31/password-encryption-in-establishing-a-remote-assistance-session-of-type-1.aspx
 *
 * Creation of PassStub for the Remote Assistance Ticket:
 * http://social.msdn.microsoft.com/Forums/en-US/6316c3f4-ea09-4343-a4a1-9cca46d70d28/creation-of-passstub-for-the-remote-assistance-ticket?forum=os_windowsprotocols
 */

/**
 * CryptDeriveKey Function:
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916/
 *
 * Let n be the required derived key length, in bytes.
 * The derived key is the first n bytes of the hash value after the hash computation
 * has been completed by CryptDeriveKey. If the hash is not a member of the SHA-2
 * family and the required key is for either 3DES or AES, the key is derived as follows:
 *
 * Form a 64-byte buffer by repeating the constant 0x36 64 times.
 * Let k be the length of the hash value that is represented by the input parameter hBaseData.
 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
 * of the buffer with the hash value that is represented by the input parameter hBaseData.
 *
 * Form a 64-byte buffer by repeating the constant 0x5C 64 times.
 * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes
 * of the buffer with the hash value that is represented by the input parameter hBaseData.
 *
 * Hash the result of step 1 by using the same hash algorithm as that used to compute the hash
 * value that is represented by the hBaseData parameter.
 *
 * Hash the result of step 2 by using the same hash algorithm as that used to compute the hash
 * value that is represented by the hBaseData parameter.
 *
 * Concatenate the result of step 3 with the result of step 4.
 * Use the first n bytes of the result of step 5 as the derived key.
 */

static BOOL freerdp_assistance_crypt_derive_key_sha1(BYTE* hash, size_t hashLength, BYTE* key,
        size_t keyLength)
{
	BOOL rc = FALSE;
	size_t i;
	BYTE* buffer;
	BYTE pad1[64];
	BYTE pad2[64];
	memset(pad1, 0x36, 64);
	memset(pad2, 0x5C, 64);

	for (i = 0; i < hashLength; i++)
	{
		pad1[i] ^= hash[i];
		pad2[i] ^= hash[i];
	}

	buffer = (BYTE*) calloc(hashLength, 2);

	if (!buffer)
		goto fail;

	if (!winpr_Digest(WINPR_MD_SHA1, pad1, 64, buffer, hashLength))
		goto fail;

	if (!winpr_Digest(WINPR_MD_SHA1, pad2, 64, &buffer[hashLength], hashLength))
		goto fail;

	CopyMemory(key, buffer, keyLength);
	rc = TRUE;
fail:
	free(buffer);
	return rc;
}

static BOOL reallocate(rdpAssistanceFile* file, const char* host, UINT32 port)
{
	void* tmp1, *tmp2;
	file->MachineCount++;
	tmp1 = realloc(file->MachinePorts, sizeof(UINT32) * file->MachineCount);
	tmp2 = realloc(file->MachineAddresses, sizeof(char*) * file->MachineCount);

	if (!tmp1 || !tmp2)
		return FALSE;

	file->MachinePorts = tmp1;
	file->MachineAddresses = tmp2;
	file->MachinePorts[file->MachineCount - 1] = port;
	file->MachineAddresses[file->MachineCount - 1] = _strdup(host);
	return TRUE;
}

static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port)
{
	unsigned long p;

	if (!file || !host || !port)
		return FALSE;

	errno = 0;
	p = strtoul(port, NULL, 0);

	if ((errno != 0) || (p == 0) || (p > UINT16_MAX))
		return FALSE;

	return reallocate(file, host, p);
}

static BOOL freerdp_assistance_parse_address_list(rdpAssistanceFile* file, const char* list)
{
	BOOL rc = FALSE;
	char* p;
	char* str;

	if (!file || !list)
		return FALSE;

	str = _strdup(list);

	if (!str)
		return FALSE;

	p =	str;

	while ((p = strchr(p, ';')) != NULL)
	{
		char* q = strchr(p, ':');

		if (!q)
			goto out;

		*q = '\0';
		q++;

		if (!append_address(file, p, q))
			goto out;

		p = q;
	}

	rc = TRUE;
out:
	free(str);
	return TRUE;
}

static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file)
{
	size_t i;
	char* str;
	int count;
	size_t length;
	char* tokens[8];
	BOOL rc = FALSE;

	if (!file || !file->RCTicket)
		return FALSE;

	/**
	 * <ProtocolVersion>,<protocolType>,<machineAddressList>,<assistantAccountPwd>,
	 * <RASessionID>,<RASessionName>,<RASessionPwd>,<protocolSpecificParms>
	 */
	count = 1;
	str = _strdup(file->RCTicket);

	if (!str)
		goto error;

	length = strlen(str);

	for (i = 0; i < length; i++)
	{
		if (str[i] == ',')
			count++;
	}

	if (count != 8)
		goto error;

	count = 0;
	tokens[count++] = str;

	for (i = 0; i < length; i++)
	{
		if (str[i] == ',')
		{
			str[i] = '\0';
			tokens[count++] = &str[i + 1];
		}
	}

	if (strcmp(tokens[0], "65538") != 0)
		goto error;

	if (strcmp(tokens[1], "1") != 0)
		goto error;

	if (strcmp(tokens[3], "*") != 0)
		goto error;

	if (strcmp(tokens[5], "*") != 0)
		goto error;

	if (strcmp(tokens[6], "*") != 0)
		goto error;

	file->RASessionId = _strdup(tokens[4]);

	if (!file->RASessionId)
		goto error;

	file->RASpecificParams = _strdup(tokens[7]);

	if (!file->RASpecificParams)
		goto error;

	if (!freerdp_assistance_parse_address_list(file, tokens[2]))
		goto error;

	rc = TRUE;
error:
	free(str);
	return rc;
}

/**
 * Decrypted Connection String 2:
 *
 * <E>
 * <A KH="BNRjdu97DyczQSRuMRrDWoue+HA=" ID="+ULZ6ifjoCa6cGPMLQiGHRPwkg6VyJqGwxMnO6GcelwUh9a6/FBq3It5ADSndmLL"/>
 * <C>
 * <T ID="1" SID="0">
 * 	<L P="49228" N="fe80::1032:53d9:5a01:909b%3"/>
 * 	<L P="49229" N="fe80::3d8f:9b2d:6b4e:6aa%6"/>
 * 	<L P="49230" N="192.168.1.200"/>
 * 	<L P="49231" N="169.254.6.170"/>
 * </T>
 * </C>
 * </E>
 */

static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file)
{
	char* str;
	char* tag;
	char* end;
	char* p;
	BOOL rc = FALSE;

	if (!file || !file->ConnectionString2)
		return FALSE;

	str = file->ConnectionString2;

	if (!strstr(str, "<E>"))
		return FALSE;

	if (!strstr(str, "<C>"))
		return FALSE;

	str = _strdup(file->ConnectionString2);

	if (!str)
		goto out_fail;

	if (!(tag = strstr(str, "<A")))
		goto out_fail;

	/* Parse Auth String Node (<A>) */
	end = strstr(tag, "/>");

	if (!end)
		goto out_fail;

	*end = '\0';
	p = strstr(tag, "KH=\"");

	if (p)
	{
		char* q;
		size_t length;
		p += sizeof("KH=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		length = q - p;
		free(file->RASpecificParams);
		file->RASpecificParams = (char*) malloc(length + 1);

		if (!file->RASpecificParams)
			goto out_fail;

		CopyMemory(file->RASpecificParams, p, length);
		file->RASpecificParams[length] = '\0';
	}

	p = strstr(tag, "ID=\"");

	if (p)
	{
		char* q;
		size_t length;
		p += sizeof("ID=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		length = q - p;
		free(file->RASessionId);
		file->RASessionId = (char*) malloc(length + 1);

		if (!file->RASessionId)
			goto out_fail;

		CopyMemory(file->RASessionId, p, length);
		file->RASessionId[length] = '\0';
	}

	*end = '/';
	/* Parse <L  last address is used */
	p = strstr(str, "<L P=\"");

	while (p)
	{
		char* port;
		char* q;
		size_t length;
		p += sizeof("<L P=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		q[0] = '\0';
		q++;
		port = p;
		p = strstr(q, " N=\"");

		if (!p)
			goto out_fail;

		p += sizeof(" N=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			goto out_fail;

		q[0] = '\0';
		q++;
		length = strlen(p);

		if (length > 8)
		{
			if (!append_address(file, p, port))
				goto out_fail;
		}

		p = strstr(q, "<L P=\"");
	}

	rc = TRUE;
out_fail:
	free(str);
	return rc;
}

char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass)
{
	size_t size;
	size_t nameLength;
	size_t passLength;
	char* ExpertBlob = NULL;

	if (!name || !pass)
		return NULL;

	nameLength = strlen(name) + strlen("NAME=");
	passLength = strlen(pass) + strlen("PASS=");
	size = nameLength + passLength + 64;
	ExpertBlob = (char*) calloc(1, size);

	if (!ExpertBlob)
		return NULL;

	sprintf_s(ExpertBlob, size, "%"PRIdz";NAME=%s%"PRIdz";PASS=%s",
	          nameLength, name, passLength, pass);
	return ExpertBlob;
}

char* freerdp_assistance_generate_pass_stub(DWORD flags)
{
	UINT32 nums[14];
	char* passStub = NULL;
	char set1[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*_";
	char set2[12] = "!@#$&^*()-+=";
	char set3[10] = "0123456789";
	char set4[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	char set5[26] = "abcdefghijklmnopqrstuvwxyz";
	passStub = (char*) malloc(15);

	if (!passStub)
		return NULL;

	/**
	 * PassStub generation:
	 *
	 * Characters 0 and 5-13 are from the set A-Z a-z 0-9 * _
	 * Character 1 is from the set !@#$&^*()-+=
	 * Character 2 is from the set 0-9
	 * Character 3 is from the set A-Z
	 * Character 4 is from the set a-z
	 *
	 * Example: WB^6HsrIaFmEpi
	 */
	winpr_RAND((BYTE*) nums, sizeof(nums));
	passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */
	passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */
	passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */
	passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */
	passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */
	passStub[5] = set1[nums[5] % sizeof(set1)]; /* character 5 */
	passStub[6] = set1[nums[6] % sizeof(set1)]; /* character 6 */
	passStub[7] = set1[nums[7] % sizeof(set1)]; /* character 7 */
	passStub[8] = set1[nums[8] % sizeof(set1)]; /* character 8 */
	passStub[9] = set1[nums[9] % sizeof(set1)]; /* character 9 */
	passStub[10] = set1[nums[10] % sizeof(set1)]; /* character 10 */
	passStub[11] = set1[nums[11] % sizeof(set1)]; /* character 11 */
	passStub[12] = set1[nums[12] % sizeof(set1)]; /* character 12 */
	passStub[13] = set1[nums[13] % sizeof(set1)]; /* character 13 */
	passStub[14] = '\0';
	return passStub;
}

BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub,
        size_t* pEncryptedSize)
{
	BOOL rc;
	int status;
	size_t cbPasswordW;
	size_t cbPassStubW;
	size_t EncryptedSize;
	BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH];
	WINPR_CIPHER_CTX* rc4Ctx = NULL;
	BYTE* pbIn = NULL;
	BYTE* pbOut = NULL;
	size_t cbOut, cbIn, cbFinal;
	WCHAR* PasswordW = NULL;
	WCHAR* PassStubW = NULL;
	status = ConvertToUnicode(CP_UTF8, 0, password, -1, &PasswordW, 0);

	if (status <= 0)
		return NULL;

	cbPasswordW = (size_t)(status - 1) * 2UL;

	if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*) PasswordHash,
	                  sizeof(PasswordHash)))
		goto fail;

	status = ConvertToUnicode(CP_UTF8, 0, passStub, -1, &PassStubW, 0);

	if (status <= 0)
		goto fail;

	cbPassStubW = (size_t)(status - 1) * 2UL;
	EncryptedSize = cbPassStubW + 4;
	pbIn = (BYTE*) calloc(1, EncryptedSize);
	pbOut = (BYTE*) calloc(1, EncryptedSize);

	if (!pbIn || !pbOut)
		goto fail;

	if ((EncryptedSize == 0) || (cbPassStubW > UINT32_MAX))
		goto fail;

	*((UINT32*) pbIn) = (UINT32)cbPassStubW;
	CopyMemory(&pbIn[4], PassStubW, cbPassStubW);
	rc4Ctx = winpr_Cipher_New(WINPR_CIPHER_ARC4_128, WINPR_ENCRYPT,
	                          PasswordHash, NULL);

	if (!rc4Ctx)
	{
		WLog_ERR(TAG,  "EVP_CipherInit_ex failure");
		goto fail;
	}

	cbOut = cbFinal = 0;
	cbIn = EncryptedSize;
	rc = winpr_Cipher_Update(rc4Ctx, pbIn, cbIn, pbOut, &cbOut);

	if (!rc)
	{
		WLog_ERR(TAG,  "EVP_CipherUpdate failure");
		goto fail;
	}

	if (!winpr_Cipher_Final(rc4Ctx, pbOut + cbOut, &cbFinal))
	{
		WLog_ERR(TAG,  "EVP_CipherFinal_ex failure");
		goto fail;
	}

	winpr_Cipher_Free(rc4Ctx);
	free(pbIn);
	free(PasswordW);
	free(PassStubW);
	*pEncryptedSize = EncryptedSize;
	return pbOut;
fail:
	winpr_Cipher_Free(rc4Ctx);
	free(PasswordW);
	free(PassStubW);
	free(pbIn);
	free(pbOut);
	return NULL;
}

static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file, const char* password)
{
	BOOL rc = FALSE;
	int status = 0;
	size_t cbPasswordW;
	int cchOutW = 0;
	WCHAR* pbOutW = NULL;
	WINPR_CIPHER_CTX* aesDec = NULL;
	WCHAR* PasswordW = NULL;
	BYTE* pbIn = NULL;
	BYTE* pbOut = NULL;
	size_t cbOut, cbIn, cbFinal;
	BYTE DerivedKey[WINPR_AES_BLOCK_SIZE];
	BYTE InitializationVector[WINPR_AES_BLOCK_SIZE];
	BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH];

	if (!file || !password)
		return FALSE;

	status = ConvertToUnicode(CP_UTF8, 0, password, -1, &PasswordW, 0);

	if (status <= 0)
		return FALSE;

	cbPasswordW = (size_t)(status - 1) * 2UL;

	if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash, sizeof(PasswordHash)))
		goto fail;

	if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash),
	        DerivedKey, sizeof(DerivedKey)))
		goto fail;

	ZeroMemory(InitializationVector, sizeof(InitializationVector));
	aesDec = winpr_Cipher_New(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT,
	                          DerivedKey, InitializationVector);

	if (!aesDec)
		goto fail;

	cbOut = cbFinal = 0;
	cbIn = (size_t)file->EncryptedLHTicketLength;
	pbIn = (BYTE*) file->EncryptedLHTicket;
	pbOut = (BYTE*) calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2);

	if (!pbOut)
		goto fail;

	if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut))
		goto fail;

	if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal))
	{
		WLog_ERR(TAG,  "EVP_DecryptFinal_ex failure");
		goto fail;
	}

	cbOut += cbFinal;
	cbFinal = 0;
	pbOutW = (WCHAR*) pbOut;

	if (cbOut > INT_MAX / 2)
		goto fail;

	cchOutW = (int)cbOut / 2;
	file->ConnectionString2 = NULL;
	status = ConvertFromUnicode(CP_UTF8, 0, pbOutW, cchOutW, &file->ConnectionString2, 0, NULL, NULL);

	if (status <= 0)
		goto fail;

	if (!freerdp_assistance_parse_connection_string2(file))
		goto fail;

	rc = TRUE;
fail:
	winpr_Cipher_Free(aesDec);
	free(PasswordW);
	free(pbOut);
	WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status);
	return rc;
}

int freerdp_assistance_decrypt(rdpAssistanceFile* file, const char* password)
{
	int status = 1;
	file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(password,
	                          file->PassStub, &file->EncryptedPassStubLength);

	if (!file->EncryptedPassStub)
		return -1;

	if (file->Type > 1)
	{
		if (!freerdp_assistance_decrypt2(file, password))
			status = -1;
	}

	return status;
}

BYTE* freerdp_assistance_hex_string_to_bin(const char* str, size_t* size)
{
	size_t length;
	BYTE* buffer;
	size_t i;
	length = strlen(str);

	if ((length % 2) != 0)
		return NULL;

	length /= 2;
	*size = length;
	buffer = (BYTE*) malloc(length);

	if (!buffer)
		return NULL;

	for (i = 0; i < length; i++)
	{
		int hn, ln;
		char c;
		hn = ln = 0;
		c = str[(i * 2) + 0];

		if ((c >= '0') && (c <= '9'))
			hn = c - '0';
		else if ((c >= 'a') && (c <= 'f'))
			hn = (c - 'a') + 10;
		else if ((c >= 'A') && (c <= 'F'))
			hn = (c - 'A') + 10;

		c = str[(i * 2) + 1];

		if ((c >= '0') && (c <= '9'))
			ln = c - '0';
		else if ((c >= 'a') && (c <= 'f'))
			ln = (c - 'a') + 10;
		else if ((c >= 'A') && (c <= 'F'))
			ln = (c - 'A') + 10;

		buffer[i] = ((hn << 4) | ln) & 0xFF;
	}

	return buffer;
}

char* freerdp_assistance_bin_to_hex_string(const BYTE* data, size_t size)
{
	size_t i;
	char* p;
	int ln, hn;
	char bin2hex[] = "0123456789ABCDEF";
	p = (char*) calloc((size + 1), 2);

	if (!p)
		return NULL;

	for (i = 0; i < size; i++)
	{
		ln = data[i] & 0xF;
		hn = (data[i] >> 4) & 0xF;
		p[i * 2] = bin2hex[hn];
		p[(i * 2) + 1] = bin2hex[ln];
	}

	p[size * 2] = '\0';
	return p;
}

int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* buffer, size_t size,
        const char* password)
{
	char* p;
	char* q;
	char* r;
	int status;
	size_t length;
	p = strstr(buffer, "UPLOADINFO");

	if (!p)
		return -1;

	p = strstr(p + sizeof("UPLOADINFO") - 1, "TYPE=\"");

	if (!p)
		return -1;

	p = strstr(buffer, "UPLOADDATA");

	if (!p)
		return -1;

	/* Parse USERNAME */
	p = strstr(buffer, "USERNAME=\"");

	if (p)
	{
		p += sizeof("USERNAME=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->Username = (char*) malloc(length + 1);

		if (!file->Username)
			return -1;

		CopyMemory(file->Username, p, length);
		file->Username[length] = '\0';
	}

	/* Parse LHTICKET */
	p = strstr(buffer, "LHTICKET=\"");

	if (p)
	{
		p += sizeof("LHTICKET=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->LHTicket = (char*) malloc(length + 1);

		if (!file->LHTicket)
			return -1;

		CopyMemory(file->LHTicket, p, length);
		file->LHTicket[length] = '\0';
	}

	/* Parse RCTICKET */
	p = strstr(buffer, "RCTICKET=\"");

	if (p)
	{
		p += sizeof("RCTICKET=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->RCTicket = (char*) malloc(length + 1);

		if (!file->RCTicket)
			return -1;

		CopyMemory(file->RCTicket, p, length);
		file->RCTicket[length] = '\0';
	}

	/* Parse RCTICKETENCRYPTED */
	p = strstr(buffer, "RCTICKETENCRYPTED=\"");

	if (p)
	{
		p += sizeof("RCTICKETENCRYPTED=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;

		if ((length == 1) && (p[0] == '1'))
			file->RCTicketEncrypted = TRUE;
	}

	/* Parse PassStub */
	p = strstr(buffer, "PassStub=\"");

	if (p)
	{
		p += sizeof("PassStub=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		file->PassStub = (char*) malloc(length + 1);

		if (!file->PassStub)
			return -1;

		CopyMemory(file->PassStub, p, length);
		file->PassStub[length] = '\0';
	}

	/* Parse DtStart */
	p = strstr(buffer, "DtStart=\"");

	if (p)
	{
		p += sizeof("DtStart=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		r = (char*) malloc(length + 1);

		if (!r)
			return -1;

		CopyMemory(r, p, length);
		r[length] = '\0';
		errno = 0;
		{
			unsigned long val = strtoul(r, NULL, 0);
			free(r);

			if ((errno != 0) || (val > UINT32_MAX))
				return -1;

			file->DtStart = val;
		}
	}

	/* Parse DtLength */
	p = strstr(buffer, "DtLength=\"");

	if (p)
	{
		p += sizeof("DtLength=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;
		r = (char*) malloc(length + 1);

		if (!r)
			return -1;

		CopyMemory(r, p, length);
		r[length] = '\0';
		errno = 0;
		{
			unsigned long val = strtoul(r, NULL, 0);
			free(r);

			if ((errno != 0) || (val > UINT32_MAX))
				return -1;

			file->DtLength = val;
		}
	}

	/* Parse L (LowSpeed) */
	p = strstr(buffer, " L=\"");

	if (p)
	{
		p += sizeof(" L=\"") - 1;
		q = strchr(p, '"');

		if (!q)
			return -1;

		length = q - p;

		if ((length == 1) && (p[0] == '1'))
			file->LowSpeed = TRUE;
	}

	file->Type = (file->LHTicket) ? 2 : 1;
	status = 0;

	if (file->LHTicket)
	{
		file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin(file->LHTicket,
		                          &file->EncryptedLHTicketLength);

		if (!freerdp_assistance_decrypt2(file, password))
			status = -1;
	}
	else
	{
		if (!freerdp_assistance_parse_connection_string1(file))
			status = -1;
	}

	if (status < 0)
	{
		WLog_ERR(TAG,  "freerdp_assistance_parse_connection_string1 failure: %d", status);
		return -1;
	}

	return 1;
}

int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password)
{
	int status;
	BYTE* buffer;
	FILE* fp = NULL;
	size_t readSize;
	INT64 fileSize;

	if (!name)
		return -1;

	free(file->filename);
	free(file->password);
	file->filename = _strdup(name);
	file->password = _strdup(password);
	fp = fopen(name, "r");

	if (!fp)
		return -1;

	_fseeki64(fp, 0, SEEK_END);
	fileSize = _ftelli64(fp);
	_fseeki64(fp, 0, SEEK_SET);

	if (fileSize < 1)
	{
		fclose(fp);
		return -1;
	}

	buffer = (BYTE*) malloc(fileSize + 2);

	if (!buffer)
	{
		fclose(fp);
		return -1;
	}

	readSize = fread(buffer, fileSize, 1, fp);

	if (!readSize)
	{
		if (!ferror(fp))
			readSize = fileSize;
	}

	fclose(fp);

	if (readSize < 1)
	{
		free(buffer);
		buffer = NULL;
		return -1;
	}

	buffer[fileSize] = '\0';
	buffer[fileSize + 1] = '\0';
	status = freerdp_assistance_parse_file_buffer(file, (char*) buffer, fileSize, password);
	free(buffer);
	return status;
}

BOOL freerdp_client_populate_settings_from_assistance_file(rdpAssistanceFile* file,
        rdpSettings* settings)
{
	UINT32 i;
	freerdp_set_param_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE);

	if (!file->RASessionId || !file->MachineAddresses)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_RemoteAssistanceSessionId, file->RASessionId) != 0)
		return FALSE;

	if (file->RCTicket &&
	    (freerdp_set_param_string(settings, FreeRDP_RemoteAssistanceRCTicket, file->RCTicket) != 0))
		return FALSE;

	if (file->PassStub)
	{
		if (freerdp_set_param_string(settings, FreeRDP_RemoteAssistancePassStub, file->PassStub) != 0)
			return FALSE;
	}

	if (freerdp_set_param_string(settings, FreeRDP_ServerHostname, file->MachineAddresses[0]) != 0)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_AssistanceFile, file->filename) != 0)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_RemoteAssistancePassword, file->password) != 0)
		return FALSE;

	if (freerdp_set_param_string(settings, FreeRDP_Username, file->Username) != 0)
		return FALSE;

	settings->RemoteAssistanceMode = TRUE;
	freerdp_set_param_uint32(settings, FreeRDP_ServerPort, file->MachinePorts[0]);
	freerdp_target_net_addresses_free(settings);
	settings->TargetNetAddressCount = file->MachineCount;

	if (settings->TargetNetAddressCount)
	{
		settings->TargetNetAddresses = (char**) calloc(file->MachineCount, sizeof(char*));
{'fraction_non_alphanumeric': 0.16238829507622218, 'fraction_numerical': 0.02713772560014018, 'mean_word_length': 3.714197212183789, 'pattern_counts': {'":': 0, '<': 123, '<?xml version=': 0, '>': 278, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5977250', 'n_tokens_mistral': 20020, 'n_tokens_neox': 18216, 'n_words': 4486}