BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob, BYTE* signature)
{
	wStream* s;

	/* Client License Information: */
	UINT32 PlatformId = PLATFORMID;
	UINT32 PreferredKeyExchangeAlg = KEY_EXCHANGE_ALG_RSA;

	s = license_send_stream_init(license);
	if (!s)
		return FALSE;

	Stream_Write_UINT32(s, PreferredKeyExchangeAlg); /* PreferredKeyExchangeAlg (4 bytes) */
	Stream_Write_UINT32(s, PlatformId);              /* PlatformId (4 bytes) */

	/* ClientRandom (32 bytes) */
	Stream_Write(s, license->ClientRandom, CLIENT_RANDOM_LENGTH);

	/* Licensing Binary Blob with EncryptedPreMasterSecret: */
	if (!license_write_encrypted_premaster_secret_blob(s, license->EncryptedPremasterSecret,
	                                                   license->ModulusLength))
		goto error;

	/* Licensing Binary Blob with LicenseInfo: */
	if (!license_write_binary_blob(s, calBlob))
		goto error;

	/* Licensing Binary Blob with EncryptedHWID */
	if (!license_write_binary_blob(s, license->EncryptedHardwareId))
		goto error;

	/* MACData */
	Stream_Write(s, signature, LICENSING_ENCRYPTION_KEY_LENGTH);

	return license_send(license, s, LICENSE_INFO);

error:
	Stream_Release(s);
	return FALSE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

#define PLATFORMID (CLIENT_OS_ID_WINNT_POST_52 | CLIENT_IMAGE_ID_MICROSOFT)

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                      const BYTE* macData)
{
	if (!license_write_binary_blob(
	        s,
	        license->EncryptedPlatformChallengeResponse) || /* EncryptedPlatformChallengeResponse */
	    !license_write_binary_blob(s, license->EncryptedHardwareId) || /* EncryptedHWID */
	    !Stream_EnsureRemainingCapacity(s, 16))
	{
		return FALSE;
	}

	Stream_Write(s, macData, 16); /* MACData */
	return TRUE;
}

Fixed OOB Read in license_read_new_or_upgrade_license_packet

CVE-2020-11099 thanks to @antonio-morales for finding this.

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo)
{
	if (Stream_GetRemainingLength(s) < 8)
		return FALSE;

	Stream_Read_UINT32(s, productInfo->dwVersion);     /* dwVersion (4 bytes) */
	Stream_Read_UINT32(s, productInfo->cbCompanyName); /* cbCompanyName (4 bytes) */

	/* Name must be >0, but there is no upper limit defined, use UINT32_MAX */
	if ((productInfo->cbCompanyName < 2) || (productInfo->cbCompanyName % 2 != 0))
		return FALSE;

	if (Stream_GetRemainingLength(s) < productInfo->cbCompanyName)
		return FALSE;

	productInfo->pbProductId = NULL;
	productInfo->pbCompanyName = (BYTE*)malloc(productInfo->cbCompanyName);
	if (!productInfo->pbCompanyName)
		return FALSE;
	Stream_Read(s, productInfo->pbCompanyName, productInfo->cbCompanyName);

	if (Stream_GetRemainingLength(s) < 4)
		goto out_fail;

	Stream_Read_UINT32(s, productInfo->cbProductId); /* cbProductId (4 bytes) */

	if ((productInfo->cbProductId < 2) || (productInfo->cbProductId % 2 != 0))
		goto out_fail;

	if (Stream_GetRemainingLength(s) < productInfo->cbProductId)
		goto out_fail;

	productInfo->pbProductId = (BYTE*)malloc(productInfo->cbProductId);
	if (!productInfo->pbProductId)
		goto out_fail;
	Stream_Read(s, productInfo->pbProductId, productInfo->cbProductId);
	return TRUE;

out_fail:
	free(productInfo->pbCompanyName);
	free(productInfo->pbProductId);
	productInfo->pbCompanyName = NULL;
	productInfo->pbProductId = NULL;
	return FALSE;
}

static wStream* license_send_stream_init(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


static BOOL license_write_preamble(wStream* s, BYTE bMsgType, BYTE flags, UINT16 wMsgSize)
{
	if (!Stream_EnsureRemainingCapacity(s, 4))
		return FALSE;

	/* preamble (4 bytes) */
	Stream_Write_UINT8(s, bMsgType);  /* bMsgType (1 byte) */
	Stream_Write_UINT8(s, flags);     /* flags (1 byte) */
	Stream_Write_UINT16(s, wMsgSize); /* wMsgSize (2 bytes) */
	return TRUE;
}

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s)
{
	UINT32 PlatformId = PLATFORMID;
	UINT32 PreferredKeyExchangeAlg = KEY_EXCHANGE_ALG_RSA;

	Stream_Write_UINT32(s, PreferredKeyExchangeAlg); /* PreferredKeyExchangeAlg (4 bytes) */
	Stream_Write_UINT32(s, PlatformId);              /* PlatformId (4 bytes) */
	Stream_Write(s, license->ClientRandom, 32);      /* ClientRandom (32 bytes) */

	if (/* EncryptedPremasterSecret */
	    !license_write_encrypted_premaster_secret_blob(s, license->EncryptedPremasterSecret,
	                                                   license->ModulusLength) ||
	    /* ClientUserName */
	    !license_write_binary_blob(s, license->ClientUserName) ||
	    /* ClientMachineName */
	    !license_write_binary_blob(s, license->ClientMachineName))
	{
		return FALSE;
	}

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "PreferredKeyExchangeAlg: 0x%08" PRIX32 "", PreferredKeyExchangeAlg);
	WLog_DBG(TAG, "ClientRandom:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->ClientRandom, 32);
	WLog_DBG(TAG, "EncryptedPremasterSecret");
	winpr_HexDump(TAG, WLOG_DEBUG, license->EncryptedPremasterSecret->data,
	              license->EncryptedPremasterSecret->length);
	WLog_DBG(TAG, "ClientUserName (%" PRIu16 "): %s", license->ClientUserName->length,
	         (char*)license->ClientUserName->data);
	WLog_DBG(TAG, "ClientMachineName (%" PRIu16 "): %s", license->ClientMachineName->length,
	         (char*)license->ClientMachineName->data);
#endif
	return TRUE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

#define PLATFORMID (CLIENT_OS_ID_WINNT_POST_52 | CLIENT_IMAGE_ID_MICROSOFT)

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


BOOL license_read_license_request_packet(rdpLicense* license, wStream* s)
{
	/* ServerRandom (32 bytes) */
	if (Stream_GetRemainingLength(s) < 32)
		return FALSE;

	Stream_Read(s, license->ServerRandom, 32);

	/* ProductInfo */
	if (!license_read_product_info(s, license->ProductInfo))
		return FALSE;

	/* KeyExchangeList */
	if (!license_read_binary_blob(s, license->KeyExchangeList))
		return FALSE;

	/* ServerCertificate */
	if (!license_read_binary_blob(s, license->ServerCertificate))
		return FALSE;

	/* ScopeList */
	if (!license_read_scope_list(s, license->ScopeList))
		return FALSE;

	/* Parse Server Certificate */
	if (!certificate_read_server_certificate(license->certificate, license->ServerCertificate->data,
	                                         license->ServerCertificate->length))
		return FALSE;

	if (!license_generate_keys(license) || !license_generate_hwid(license) ||
	    !license_encrypt_premaster_secret(license))
		return FALSE;

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "ServerRandom:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->ServerRandom, 32);
	license_print_product_info(license->ProductInfo);
	license_print_scope_list(license->ScopeList);
#endif
	return TRUE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


void license_free_binary_blob(LICENSE_BLOB* blob)
{
	if (blob)
	{
		free(blob->data);
		free(blob);
	}
}

static void license_generate_randoms(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


static void license_print_product_info(const LICENSE_PRODUCT_INFO* productInfo)
{
	char* CompanyName = NULL;
	char* ProductId = NULL;
	ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)productInfo->pbCompanyName,
	                   productInfo->cbCompanyName / 2, &CompanyName, 0, NULL, NULL);
	ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)productInfo->pbProductId, productInfo->cbProductId / 2,
	                   &ProductId, 0, NULL, NULL);
	WLog_INFO(TAG, "ProductInfo:");
	WLog_INFO(TAG, "\tdwVersion: 0x%08" PRIX32 "", productInfo->dwVersion);
	WLog_INFO(TAG, "\tCompanyName: %s", CompanyName);
	WLog_INFO(TAG, "\tProductId: %s", ProductId);
	free(CompanyName);
	free(ProductId);
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


static BOOL license_decrypt_and_check_MAC(rdpLicense* license, const BYTE* input, size_t len,
                                          LICENSE_BLOB* target, const BYTE* packetMac)
{
	BYTE macData[16];

	return license_rc4_with_licenseKey(license, input, len, target) &&
	       security_mac_data(license->MacSaltKey, target->data, len, macData) &&
	       (memcmp(packetMac, macData, sizeof(macData)) == 0);
}


static BOOL license_read_preamble(wStream* s, BYTE* bMsgType, BYTE* flags, UINT16* wMsgSize)
{
	/* preamble (4 bytes) */
	if (Stream_GetRemainingLength(s) < 4)
		return FALSE;

	Stream_Read_UINT8(s, *bMsgType);  /* bMsgType (1 byte) */
	Stream_Read_UINT8(s, *flags);     /* flags (1 byte) */
	Stream_Read_UINT16(s, *wMsgSize); /* wMsgSize (2 bytes) */
	return TRUE;
}


void license_free(rdpLicense* license)
{
	if (license)
	{
		free(license->Modulus);
		certificate_free(license->certificate);
		license_free_product_info(license->ProductInfo);
		license_free_binary_blob(license->ErrorInfo);
		license_free_binary_blob(license->KeyExchangeList);
		license_free_binary_blob(license->ServerCertificate);
		license_free_binary_blob(license->ClientUserName);
		license_free_binary_blob(license->ClientMachineName);
		license_free_binary_blob(license->PlatformChallenge);
		license_free_binary_blob(license->EncryptedPlatformChallenge);
		license_free_binary_blob(license->EncryptedPlatformChallengeResponse);
		license_free_binary_blob(license->EncryptedPremasterSecret);
		license_free_binary_blob(license->EncryptedHardwareId);
		license_free_scope_list(license->ScopeList);
		free(license);
	}
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo)
{
	if (productInfo)
	{
		free(productInfo->pbCompanyName);
		free(productInfo->pbProductId);
		free(productInfo);
	}
}

static void license_generate_randoms(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);


void license_generate_randoms(rdpLicense* license)
{
#ifdef LICENSE_NULL_CLIENT_RANDOM
	ZeroMemory(license->ClientRandom, CLIENT_RANDOM_LENGTH); /* ClientRandom */
#else
	winpr_RAND(license->ClientRandom, CLIENT_RANDOM_LENGTH);       /* ClientRandom */
#endif

#ifdef LICENSE_NULL_PREMASTER_SECRET
	ZeroMemory(license->PremasterSecret, PREMASTER_SECRET_LENGTH); /* PremasterSecret */
#else
	winpr_RAND(license->PremasterSecret, PREMASTER_SECRET_LENGTH); /* PremasterSecret */
#endif
}

#define TAG FREERDP_TAG("core.license")

#define LICENSE_NULL_CLIENT_RANDOM 1

#define LICENSE_NULL_PREMASTER_SECRET 1

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


rdpLicense* license_new(rdpRdp* rdp)
{
	rdpLicense* license;
	license = (rdpLicense*)calloc(1, sizeof(rdpLicense));
	if (!license)
		return NULL;

	license->rdp = rdp;
	license->state = LICENSE_STATE_AWAIT;
	if (!(license->certificate = certificate_new()))
		goto out_error;
	if (!(license->ProductInfo = license_new_product_info()))
		goto out_error;
	if (!(license->ErrorInfo = license_new_binary_blob(BB_ERROR_BLOB)))
		goto out_error;
	if (!(license->KeyExchangeList = license_new_binary_blob(BB_KEY_EXCHG_ALG_BLOB)))
		goto out_error;
	if (!(license->ServerCertificate = license_new_binary_blob(BB_CERTIFICATE_BLOB)))
		goto out_error;
	if (!(license->ClientUserName = license_new_binary_blob(BB_CLIENT_USER_NAME_BLOB)))
		goto out_error;
	if (!(license->ClientMachineName = license_new_binary_blob(BB_CLIENT_MACHINE_NAME_BLOB)))
		goto out_error;
	if (!(license->PlatformChallenge = license_new_binary_blob(BB_ANY_BLOB)))
		goto out_error;
	if (!(license->EncryptedPlatformChallenge = license_new_binary_blob(BB_ANY_BLOB)))
		goto out_error;
	if (!(license->EncryptedPlatformChallengeResponse =
	          license_new_binary_blob(BB_ENCRYPTED_DATA_BLOB)))
		goto out_error;
	if (!(license->EncryptedPremasterSecret = license_new_binary_blob(BB_ANY_BLOB)))
		goto out_error;
	if (!(license->EncryptedHardwareId = license_new_binary_blob(BB_ENCRYPTED_DATA_BLOB)))
		goto out_error;
	if (!(license->ScopeList = license_new_scope_list()))
		goto out_error;

	license_generate_randoms(license);

	return license;

out_error:
	license_free(license);
	return NULL;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static SCOPE_LIST* license_new_scope_list(void);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


static BOOL license_get_server_rsa_public_key(rdpLicense* license)
{
	BYTE* Exponent;
	BYTE* Modulus;
	int ModulusLength;
	rdpSettings* settings = license->rdp->settings;

	if (license->ServerCertificate->length < 1)
	{
		if (!certificate_read_server_certificate(license->certificate, settings->ServerCertificate,
		                                         settings->ServerCertificateLength))
			return FALSE;
	}

	Exponent = license->certificate->cert_info.exponent;
	Modulus = license->certificate->cert_info.Modulus;
	ModulusLength = license->certificate->cert_info.ModulusLength;
	CopyMemory(license->Exponent, Exponent, 4);
	license->ModulusLength = ModulusLength;
	license->Modulus = (BYTE*)malloc(ModulusLength);
	if (!license->Modulus)
		return FALSE;
	CopyMemory(license->Modulus, Modulus, ModulusLength);
	return TRUE;
}


BOOL license_send_valid_client_error_packet(rdpRdp* rdp)
{
	rdpLicense* license = rdp->license;
	wStream* s = license_send_stream_init(license);
	if (!s)
		return FALSE;

	DEBUG_LICENSE("Sending Error Alert Packet");
	Stream_Write_UINT32(s, STATUS_VALID_CLIENT); /* dwErrorCode */
	Stream_Write_UINT32(s, ST_NO_TRANSITION);    /* dwStateTransition */

	if (license_write_binary_blob(s, license->ErrorInfo))
		return license_send(license, s, ERROR_ALERT);

	Stream_Release(s);
	return FALSE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };


static BOOL license_read_encrypted_blob(const rdpLicense* license, wStream* s, LICENSE_BLOB* target)
{
	UINT16 wBlobType, wBlobLen;
	BYTE* encryptedData;

	if (Stream_GetRemainingLength(s) < 4)
		return FALSE;

	Stream_Read_UINT16(s, wBlobType);
	if (wBlobType != BB_ENCRYPTED_DATA_BLOB)
	{
		WLog_DBG(
		    TAG,
		    "expecting BB_ENCRYPTED_DATA_BLOB blob, probably a windows 2003 server, continuing...");
	}

	Stream_Read_UINT16(s, wBlobLen);

	if (Stream_GetRemainingLength(s) < wBlobLen)
		return FALSE;

	encryptedData = Stream_Pointer(s);
	Stream_Seek(s, wBlobLen);
	return license_rc4_with_licenseKey(license, encryptedData, wBlobLen, target);
}


static BOOL license_send(rdpLicense* license, wStream* s, BYTE type)
{
	size_t length;
	BYTE flags;
	UINT16 wMsgSize;
	rdpRdp* rdp = license->rdp;
	BOOL ret;

	DEBUG_LICENSE("Sending %s Packet", LICENSE_MESSAGE_STRINGS[type & 0x1F]);
	length = Stream_GetPosition(s);
	wMsgSize = length - license->PacketHeaderLength;
	Stream_SetPosition(s, license->PacketHeaderLength);
	flags = PREAMBLE_VERSION_3_0;

	/**
	 * Using EXTENDED_ERROR_MSG_SUPPORTED here would cause mstsc to crash when
	 * running in server mode! This flag seems to be incorrectly documented.
	 */

	if (!rdp->settings->ServerMode)
		flags |= EXTENDED_ERROR_MSG_SUPPORTED;

	if (!license_write_preamble(s, type, flags, wMsgSize))
		return FALSE;

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "Sending %s Packet, length %" PRIu16 "", LICENSE_MESSAGE_STRINGS[type & 0x1F],
	         wMsgSize);
	winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(s) - LICENSE_PREAMBLE_LENGTH, wMsgSize);
#endif
	Stream_SetPosition(s, length);
	ret = rdp_send(rdp, s, MCS_GLOBAL_CHANNEL_ID);
	rdp->sec_flags = 0;
	return ret;
}


BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s)
{
	UINT32 dwErrorCode;
	UINT32 dwStateTransition;

	if (Stream_GetRemainingLength(s) < 8)
		return FALSE;

	Stream_Read_UINT32(s, dwErrorCode);       /* dwErrorCode (4 bytes) */
	Stream_Read_UINT32(s, dwStateTransition); /* dwStateTransition (4 bytes) */

	if (!license_read_binary_blob(s, license->ErrorInfo)) /* bbErrorInfo */
		return FALSE;

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "dwErrorCode: %s, dwStateTransition: %s", error_codes[dwErrorCode],
	         state_transitions[dwStateTransition]);
#endif

	if (dwErrorCode == STATUS_VALID_CLIENT)
	{
		license->state = LICENSE_STATE_COMPLETED;
		return TRUE;
	}

	switch (dwStateTransition)
	{
		case ST_TOTAL_ABORT:
			license->state = LICENSE_STATE_ABORTED;
			break;
		case ST_NO_TRANSITION:
			license->state = LICENSE_STATE_COMPLETED;
			break;
		case ST_RESET_PHASE_TO_START:
			license->state = LICENSE_STATE_AWAIT;
			break;
		case ST_RESEND_LAST_MESSAGE:
			break;
		default:
			break;
	}

	return TRUE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };


BOOL license_send_platform_challenge_response_packet(rdpLicense* license)
{
	wStream* s;
	wStream* challengeRespData;
	int length;
	BYTE* buffer;
	BYTE mac_data[16];
	BOOL status;

	DEBUG_LICENSE("Sending Platform Challenge Response Packet");
	s = license_send_stream_init(license);
	license->EncryptedPlatformChallenge->type = BB_DATA_BLOB;

	/* prepare the PLATFORM_CHALLENGE_RESPONSE_DATA */
	challengeRespData = Stream_New(NULL, 8 + license->PlatformChallenge->length);
	if (!challengeRespData)
		return FALSE;
	Stream_Write_UINT16(challengeRespData, 0x0100);                        /* wVersion */
	Stream_Write_UINT16(challengeRespData, OTHER_PLATFORM_CHALLENGE_TYPE); /* wClientType */
	Stream_Write_UINT16(challengeRespData, LICENSE_DETAIL_DETAIL);         /* wLicenseDetailLevel */
	Stream_Write_UINT16(challengeRespData, license->PlatformChallenge->length); /* cbChallenge */
	Stream_Write(challengeRespData, license->PlatformChallenge->data,
	             license->PlatformChallenge->length); /* pbChallenge */
	Stream_SealLength(challengeRespData);

	/* compute MAC of PLATFORM_CHALLENGE_RESPONSE_DATA + HWID */
	length = Stream_Length(challengeRespData) + HWID_LENGTH;
	buffer = (BYTE*)malloc(length);
	if (!buffer)
	{
		Stream_Free(challengeRespData, TRUE);
		return FALSE;
	}

	CopyMemory(buffer, Stream_Buffer(challengeRespData), Stream_Length(challengeRespData));
	CopyMemory(&buffer[Stream_Length(challengeRespData)], license->HardwareId, HWID_LENGTH);
	status = security_mac_data(license->MacSaltKey, buffer, length, mac_data);
	free(buffer);

	if (!status)
	{
		Stream_Free(challengeRespData, TRUE);
		return FALSE;
	}

	license->EncryptedHardwareId->type = BB_ENCRYPTED_DATA_BLOB;
	if (!license_rc4_with_licenseKey(license, license->HardwareId, HWID_LENGTH,
	                                 license->EncryptedHardwareId))
	{
		Stream_Free(challengeRespData, TRUE);
		return FALSE;
	}

	status = license_rc4_with_licenseKey(license, Stream_Buffer(challengeRespData),
	                                     Stream_Length(challengeRespData),
	                                     license->EncryptedPlatformChallengeResponse);
	Stream_Free(challengeRespData, TRUE);
	if (!status)
		return FALSE;

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "LicensingEncryptionKey:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->LicensingEncryptionKey, 16);
	WLog_DBG(TAG, "HardwareId:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->HardwareId, HWID_LENGTH);
	WLog_DBG(TAG, "EncryptedHardwareId:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->EncryptedHardwareId->data, HWID_LENGTH);
#endif
	if (license_write_platform_challenge_response_packet(license, s, mac_data))
		return license_send(license, s, PLATFORM_CHALLENGE_RESPONSE);

	Stream_Release(s);
	return FALSE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };


static BOOL saveCal(rdpSettings* settings, const BYTE* data, int length, char* hostname)
{
	char hash[41];
	FILE* fp;
	char* licenseStorePath = NULL;
	char filename[MAX_PATH], filenameNew[MAX_PATH];
	char *filepath = NULL, *filepathNew = NULL;
	size_t written;
	BOOL ret = FALSE;

	if (!PathFileExistsA(settings->ConfigPath))
	{
		if (!PathMakePathA(settings->ConfigPath, 0))
		{
			WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath);
			goto out;
		}
		WLog_INFO(TAG, "creating directory %s", settings->ConfigPath);
	}

	if (!(licenseStorePath = GetCombinedPath(settings->ConfigPath, licenseStore)))
		goto out;

	if (!PathFileExistsA(licenseStorePath))
	{
		if (!PathMakePathA(licenseStorePath, 0))
		{
			WLog_ERR(TAG, "error creating directory '%s'", licenseStorePath);
			goto out;
		}
		WLog_INFO(TAG, "creating directory %s", licenseStorePath);
	}

	if (!computeCalHash(hostname, hash))
		goto out;
	sprintf_s(filename, sizeof(filename) - 1, "%s.cal", hash);
	sprintf_s(filenameNew, sizeof(filenameNew) - 1, "%s.cal.new", hash);

	if (!(filepath = GetCombinedPath(licenseStorePath, filename)))
		goto out;

	if (!(filepathNew = GetCombinedPath(licenseStorePath, filenameNew)))
		goto out;

	fp = fopen(filepathNew, "wb");
	if (!fp)
		goto out;

	written = fwrite(data, length, 1, fp);
	fclose(fp);

	if (written != 1)
	{
		DeleteFile(filepathNew);
		goto out;
	}

	ret = MoveFileEx(filepathNew, filepath, MOVEFILE_REPLACE_EXISTING);

out:
	free(filepathNew);
	free(filepath);
	free(licenseStorePath);
	return ret;
}


static BOOL license_rc4_with_licenseKey(const rdpLicense* license, const BYTE* input, size_t len,
                                        LICENSE_BLOB* target)
{
	WINPR_RC4_CTX* rc4;
	BYTE* buffer = NULL;

	rc4 =
	    winpr_RC4_New_Allow_FIPS(license->LicensingEncryptionKey, LICENSING_ENCRYPTION_KEY_LENGTH);
	if (!rc4)
		return FALSE;

	buffer = (BYTE*)realloc(target->data, len);
	if (!buffer)
		goto error_buffer;

	target->data = buffer;
	target->length = len;

	if (!winpr_RC4_Update(rc4, len, input, buffer))
		goto error_buffer;

	winpr_RC4_Free(rc4);
	return TRUE;

error_buffer:
	winpr_RC4_Free(rc4);
	return FALSE;
}


BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s)
{
	UINT32 os_major;
	UINT32 os_minor;
	UINT32 cbScope, cbCompanyName, cbProductId, cbLicenseInfo;
	wStream* licenseStream = NULL;
	BOOL ret = FALSE;
	BYTE computedMac[16];
	LICENSE_BLOB* calBlob;

	DEBUG_LICENSE("Receiving Server New/Upgrade License Packet");

	calBlob = license_new_binary_blob(BB_DATA_BLOB);
	if (!calBlob)
		return FALSE;

	/* EncryptedLicenseInfo */
	if (!license_read_encrypted_blob(license, s, calBlob))
		goto out_free_blob;

	/* compute MAC and check it */
	if (Stream_GetRemainingLength(s) < 16)
		goto out_free_blob;

	if (!security_mac_data(license->MacSaltKey, calBlob->data, calBlob->length, computedMac))
		goto out_free_blob;

	if (memcmp(computedMac, Stream_Pointer(s), sizeof(computedMac)) != 0)
	{
		WLog_ERR(TAG, "new or upgrade license MAC mismatch");
		goto out_free_blob;
	}

	if (!Stream_SafeSeek(s, 16))
		goto out_free_blob;

	licenseStream = Stream_New(calBlob->data, calBlob->length);
	if (!licenseStream)
		goto out_free_blob;

	if (Stream_GetRemainingLength(licenseStream) < 8)
		goto out_free_stream;

	Stream_Read_UINT16(licenseStream, os_minor);
	Stream_Read_UINT16(licenseStream, os_major);

	/* Scope */
	Stream_Read_UINT32(licenseStream, cbScope);
	if (Stream_GetRemainingLength(licenseStream) < cbScope)
		goto out_free_stream;
#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "Scope:");
	winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(licenseStream), cbScope);
#endif
	Stream_Seek(licenseStream, cbScope);

	/* CompanyName */
	if (Stream_GetRemainingLength(licenseStream) < 4)
		goto out_free_stream;
	Stream_Read_UINT32(licenseStream, cbCompanyName);
	if (Stream_GetRemainingLength(licenseStream) < cbCompanyName)
		goto out_free_stream;
#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "Company name:");
	winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(licenseStream), cbCompanyName);
#endif
	Stream_Seek(licenseStream, cbCompanyName);

	/* productId */
	if (Stream_GetRemainingLength(licenseStream) < 4)
		goto out_free_stream;
	Stream_Read_UINT32(licenseStream, cbProductId);
	if (Stream_GetRemainingLength(licenseStream) < cbProductId)
		goto out_free_stream;
#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "Product id:");
	winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(licenseStream), cbProductId);
#endif
	Stream_Seek(licenseStream, cbProductId);

	/* licenseInfo */
	if (Stream_GetRemainingLength(licenseStream) < 4)
		goto out_free_stream;
	Stream_Read_UINT32(licenseStream, cbLicenseInfo);
	if (Stream_GetRemainingLength(licenseStream) < cbLicenseInfo)
		goto out_free_stream;

	license->state = LICENSE_STATE_COMPLETED;

	ret = TRUE;
	if (!license->rdp->settings->OldLicenseBehaviour)
		ret = saveCal(license->rdp->settings, Stream_Pointer(licenseStream), cbLicenseInfo,
		              license->rdp->settings->ClientHostname);

out_free_stream:
	Stream_Free(licenseStream, FALSE);
out_free_blob:
	license_free_binary_blob(calBlob);
	return ret;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };


BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList)
{
	UINT32 i;
	UINT32 scopeCount;

	if (Stream_GetRemainingLength(s) < 4)
		return FALSE;

	Stream_Read_UINT32(s, scopeCount); /* ScopeCount (4 bytes) */

	if (scopeCount > Stream_GetRemainingLength(s) / 4) /* every blob is at least 4 bytes */
		return FALSE;

	scopeList->count = scopeCount;
	scopeList->array = (LICENSE_BLOB*)calloc(scopeCount, sizeof(LICENSE_BLOB));
	if (!scopeList->array)
		return FALSE;

	/* ScopeArray */
	for (i = 0; i < scopeCount; i++)
	{
		scopeList->array[i].type = BB_SCOPE_BLOB;

		if (!license_read_binary_blob(s, &scopeList->array[i]))
			return FALSE;
	}

	return TRUE;
}

static wStream* license_send_stream_init(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


BOOL license_generate_hwid(rdpLicense* license)
{
	const BYTE* hashTarget;
	size_t targetLen;
	BYTE macAddress[6];

	ZeroMemory(license->HardwareId, HWID_LENGTH);

	if (license->rdp->settings->OldLicenseBehaviour)
	{
		ZeroMemory(macAddress, sizeof(macAddress));
		hashTarget = macAddress;
		targetLen = sizeof(macAddress);
	}
	else
	{
		wStream s;
		const char* hostname = license->rdp->settings->ClientHostname;
		Stream_StaticInit(&s, license->HardwareId, 4);
		Stream_Write_UINT32(&s, PLATFORMID);
		Stream_Free(&s, TRUE);

		hashTarget = (const BYTE*)hostname;
		targetLen = strlen(hostname);
	}

	/* Allow FIPS override for use of MD5 here, really this does not have to be MD5 as we are just
	 * taking a MD5 hash of the 6 bytes of 0's(macAddress) */
	/* and filling in the Data1-Data4 fields of the CLIENT_HARDWARE_ID structure(from MS-RDPELE
	 * section 2.2.2.3.1). This is for RDP licensing packets */
	/* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data
	 * protection. */
	return winpr_Digest_Allow_FIPS(WINPR_MD_MD5, hashTarget, targetLen,
	                               &license->HardwareId[HWID_PLATFORM_ID_LENGTH],
	                               WINPR_MD5_DIGEST_LENGTH);
}


BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob)
{
	UINT16 wBlobType;

	if (Stream_GetRemainingLength(s) < 4)
		return FALSE;

	Stream_Read_UINT16(s, wBlobType);    /* wBlobType (2 bytes) */
	Stream_Read_UINT16(s, blob->length); /* wBlobLen (2 bytes) */

	if (Stream_GetRemainingLength(s) < blob->length)
		return FALSE;

	/*
	 * Server can choose to not send data by setting length to 0.
	 * If so, it may not bother to set the type, so shortcut the warning
	 */
	if ((blob->type != BB_ANY_BLOB) && (blob->length == 0))
		return TRUE;

	if ((blob->type != wBlobType) && (blob->type != BB_ANY_BLOB))
	{
		WLog_ERR(TAG,
		         "license binary blob type (0x%" PRIx16 ") does not match expected type (0x%" PRIx16
		         ").",
		         wBlobType, blob->type);
	}

	blob->type = wBlobType;
	blob->data = (BYTE*)malloc(blob->length);
	if (!blob->data)
		return FALSE;
	Stream_Read(s, blob->data, blob->length); /* blobData */
	return TRUE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


static void license_print_scope_list(const SCOPE_LIST* scopeList)
{
	UINT32 index;
	const LICENSE_BLOB* scope;
	WLog_INFO(TAG, "ScopeList (%" PRIu32 "):", scopeList->count);

	for (index = 0; index < scopeList->count; index++)
	{
		scope = &scopeList->array[index];
		WLog_INFO(TAG, "\t%s", (const char*)scope->data);
	}
}


static BOOL license_generate_keys(rdpLicense* license)
{
	BOOL ret;

	if (
	    /* MasterSecret */
	    !security_master_secret(license->PremasterSecret, license->ClientRandom,
	                            license->ServerRandom, license->MasterSecret) ||
	    /* SessionKeyBlob */
	    !security_session_key_blob(license->MasterSecret, license->ClientRandom,
	                               license->ServerRandom, license->SessionKeyBlob))
	{
		return FALSE;
	}
	security_mac_salt_key(license->SessionKeyBlob, license->ClientRandom, license->ServerRandom,
	                      license->MacSaltKey); /* MacSaltKey */
	ret = security_licensing_encryption_key(
	    license->SessionKeyBlob, license->ClientRandom, license->ServerRandom,
	    license->LicensingEncryptionKey); /* LicensingEncryptionKey */
#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "ClientRandom:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->ClientRandom, CLIENT_RANDOM_LENGTH);
	WLog_DBG(TAG, "ServerRandom:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->ServerRandom, SERVER_RANDOM_LENGTH);
	WLog_DBG(TAG, "PremasterSecret:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->PremasterSecret, PREMASTER_SECRET_LENGTH);
	WLog_DBG(TAG, "MasterSecret:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->MasterSecret, MASTER_SECRET_LENGTH);
	WLog_DBG(TAG, "SessionKeyBlob:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->SessionKeyBlob, SESSION_KEY_BLOB_LENGTH);
	WLog_DBG(TAG, "MacSaltKey:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->MacSaltKey, MAC_SALT_KEY_LENGTH);
	WLog_DBG(TAG, "LicensingEncryptionKey:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->LicensingEncryptionKey,
	              LICENSING_ENCRYPTION_KEY_LENGTH);
#endif
	return ret;
}


BOOL license_answer_license_request(rdpLicense* license)
{
	wStream* s;
	BYTE* license_data = NULL;
	int license_size = 0;
	BOOL status;
	char* username;

	if (!license->rdp->settings->OldLicenseBehaviour)
		license_data = loadCalFile(license->rdp->settings, license->rdp->settings->ClientHostname,
		                           &license_size);

	if (license_data)
	{
		LICENSE_BLOB* calBlob = NULL;
		BYTE signature[LICENSING_ENCRYPTION_KEY_LENGTH];

		DEBUG_LICENSE("Sending Saved License Packet");

		license->EncryptedHardwareId->type = BB_ENCRYPTED_DATA_BLOB;
		if (!license_encrypt_and_MAC(license, license->HardwareId, HWID_LENGTH,
		                             license->EncryptedHardwareId, signature))
		{
			free(license_data);
			return FALSE;
		}

		calBlob = license_new_binary_blob(BB_DATA_BLOB);
		if (!calBlob)
		{
			free(license_data);
			return FALSE;
		}
		calBlob->data = license_data;
		calBlob->length = license_size;

		status = license_send_client_info(license, calBlob, signature);
		license_free_binary_blob(calBlob);

		return status;
	}

	DEBUG_LICENSE("Sending New License Packet");

	s = license_send_stream_init(license);
	if (!s)
		return FALSE;
	if (license->rdp->settings->Username != NULL)
		username = license->rdp->settings->Username;
	else
		username = "username";

	license->ClientUserName->data = (BYTE*)username;
	license->ClientUserName->length = strlen(username) + 1;
	license->ClientMachineName->data = (BYTE*)license->rdp->settings->ClientHostname;
	license->ClientMachineName->length = strlen(license->rdp->settings->ClientHostname) + 1;

	status = license_write_new_license_request_packet(license, s);

	license->ClientUserName->data = NULL;
	license->ClientUserName->length = 0;
	license->ClientMachineName->data = NULL;
	license->ClientMachineName->length = 0;

	if (!status)
	{
		Stream_Release(s);
		return FALSE;
	}

	return license_send(license, s, NEW_LICENSE_REQUEST);
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


void license_free_scope_list(SCOPE_LIST* scopeList)
{
	UINT32 i;

	if (!scopeList)
		return;

	/*
	 * We must NOT call license_free_binary_blob() on each scopelist->array[i] element,
	 * because scopelist->array was allocated at once, by a single call to malloc. The elements
	 * it contains cannot be deallocated separately then.
	 * To make things clean, we must deallocate each scopelist->array[].data,
	 * and finish by deallocating scopelist->array with a single call to free().
	 */
	for (i = 0; i < scopeList->count; i++)
	{
		free(scopeList->array[i].data);
	}

	free(scopeList->array);
	free(scopeList);
}

static void license_generate_randoms(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);


LICENSE_PRODUCT_INFO* license_new_product_info()
{
	LICENSE_PRODUCT_INFO* productInfo;
	productInfo = (LICENSE_PRODUCT_INFO*)malloc(sizeof(LICENSE_PRODUCT_INFO));
	if (!productInfo)
		return NULL;
	productInfo->dwVersion = 0;
	productInfo->cbCompanyName = 0;
	productInfo->pbCompanyName = NULL;
	productInfo->cbProductId = 0;
	productInfo->pbProductId = NULL;
	return productInfo;
}

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);


void imap_error(const char *where, const char *msg)
{
  mutt_error("%s [%s]", where, msg);
}

Fix seqset iterator when it ends in a comma

If the seqset ended with a comma, the substr_end marker would be just
before the trailing nul.  In the next call, the loop to skip the
marker would iterate right past the end of string too.

The fix is simple: place the substr_end marker and skip past it
immediately.

/* No explicit global definitions found. */


int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
{
  if (!mdata->hcache)
    return -1;

  char key[16];

  sprintf(key, "/%u", uid);
  return mutt_hcache_delete_record(mdata->hcache, key, mutt_str_len(key));
}


void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
{
  char *buf = mutt_str_dup(src);
  imap_utf_encode(unicode, &buf);

  imap_quote_string(dest, dlen, buf, false);

  FREE(&buf);
}


void imap_get_parent(const char *mbox, char delim, char *buf, size_t buflen)
{
  /* Make a copy of the mailbox name, but only if the pointers are different */
  if (mbox != buf)
    mutt_str_copy(buf, mbox, buflen);

  int n = mutt_str_len(buf);

  /* Let's go backwards until the next delimiter
   *
   * If buf[n] is a '/', the first n-- will allow us
   * to ignore it. If it isn't, then buf looks like
   * "/aaaaa/bbbb". There is at least one "b", so we can't skip
   * the "/" after the 'a's.
   *
   * If buf == '/', then n-- => n == 0, so the loop ends
   * immediately */
  for (n--; (n >= 0) && (buf[n] != delim); n--)
    ; // do nothing

  /* We stopped before the beginning. There is a trailing slash.  */
  if (n > 0)
  {
    /* Strip the trailing delimiter.  */
    buf[n] = '\0';
  }
  else
  {
    buf[0] = (n == 0) ? delim : '\0';
  }
}


void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
{
  struct ConnAccount cac_target = { { 0 } };
  struct ConnAccount cac_home = { { 0 } };
  struct Url url = { 0 };
  const char *delim = NULL;
  int tlen;
  int hlen = 0;
  bool home_match = false;
  char target_mailbox[1024];
  char home_mailbox[1024];

  if (imap_parse_path(path, &cac_target, target_mailbox, sizeof(target_mailbox)) < 0)
    return;

  if (imap_path_probe(folder, NULL) != MUTT_IMAP)
    goto fallback;

  if (imap_parse_path(folder, &cac_home, home_mailbox, sizeof(home_mailbox)) < 0)
    goto fallback;

  tlen = mutt_str_len(target_mailbox);
  hlen = mutt_str_len(home_mailbox);

  /* check whether we can do '+' substitution */
  if (tlen && imap_account_match(&cac_home, &cac_target) &&
      mutt_strn_equal(home_mailbox, target_mailbox, hlen))
  {
    const char *const c_imap_delim_chars =
        cs_subset_string(NeoMutt->sub, "imap_delim_chars");
    if (hlen == 0)
      home_match = true;
    else if (c_imap_delim_chars)
    {
      for (delim = c_imap_delim_chars; *delim != '\0'; delim++)
        if (target_mailbox[hlen] == *delim)
          home_match = true;
    }
  }

  /* do the '+' substitution */
  if (home_match)
  {
    *path++ = '+';
    /* copy remaining path, skipping delimiter */
    if (hlen == 0)
      hlen = -1;
    memcpy(path, target_mailbox + hlen + 1, tlen - hlen - 1);
    path[tlen - hlen - 1] = '\0';
    return;
  }

fallback:
  mutt_account_tourl(&cac_target, &url);
  url.path = target_mailbox;
  url_tostring(&url, path, pathlen, U_NO_FLAGS);
}


int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
{
  if (!mdata->hcache)
    return -1;

  /* The seqset is likely large.  Preallocate to reduce reallocs */
  struct Buffer buf = mutt_buffer_make(8192);
  imap_msn_index_to_uid_seqset(&buf, mdata);

  int rc = mutt_hcache_store_raw(mdata->hcache, "/UIDSEQSET", 10, buf.data,
                                 mutt_buffer_len(&buf) + 1);
  mutt_debug(LL_DEBUG3, "Stored /UIDSEQSET %s\n", buf.data);
  mutt_buffer_dealloc(&buf);
  return rc;
}


void imap_mdata_cache_reset(struct ImapMboxData *mdata)
{
  mutt_hash_free(&mdata->uid_hash);
  imap_msn_free(&mdata->msn);
  mutt_bcache_close(&mdata->bcache);
}


void imap_disallow_reopen(struct Mailbox *m)
{
  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);
  if (!adata || !adata->mailbox || (adata->mailbox != m) || !mdata)
    return;
  mdata->reopen &= ~IMAP_REOPEN_ALLOW;
}


void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *cac, char *path)
{
  struct Url url = { 0 };
  mutt_account_tourl(cac, &url);
  url.path = path;
  url_tostring(&url, buf, buflen, U_NO_FLAGS);
}


void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
{
  if (!adata || !mdata)
    return;

  if (mdata->hcache)
    return;

  struct HeaderCache *hc = NULL;
  struct Buffer *mbox = mutt_buffer_pool_get();
  struct Buffer *cachepath = mutt_buffer_pool_get();

  imap_cachepath(adata->delim, mdata->name, mbox);

  if (strstr(mutt_buffer_string(mbox), "/../") ||
      mutt_str_equal(mutt_buffer_string(mbox), "..") ||
      mutt_strn_equal(mutt_buffer_string(mbox), "../", 3))
  {
    goto cleanup;
  }
  size_t len = mutt_buffer_len(mbox);
  if ((len > 3) && (strcmp(mutt_buffer_string(mbox) + len - 3, "/..") == 0))
    goto cleanup;

  struct Url url = { 0 };
  mutt_account_tourl(&adata->conn->account, &url);
  url.path = mbox->data;
  url_tobuffer(&url, cachepath, U_PATH);

  const char *const c_header_cache =
      cs_subset_path(NeoMutt->sub, "header_cache");
  hc = mutt_hcache_open(c_header_cache, mutt_buffer_string(cachepath), imap_hcache_namer);

cleanup:
  mutt_buffer_pool_release(&mbox);
  mutt_buffer_pool_release(&cachepath);
  mdata->hcache = hc;
}


void imap_get_parent_path(const char *path, char *buf, size_t buflen)
{
  struct ImapAccountData *adata = NULL;
  struct ImapMboxData *mdata = NULL;
  char mbox[1024];

  if (imap_adata_find(path, &adata, &mdata) < 0)
  {
    mutt_str_copy(buf, path, buflen);
    return;
  }

  /* Gets the parent mbox in mbox */
  imap_get_parent(mdata->name, adata->delim, mbox, sizeof(mbox));

  /* Returns a fully qualified IMAP url */
  imap_qualify_path(buf, buflen, &adata->conn->account, mbox);
  imap_mdata_free((void *) &mdata);
}


int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
{
  static unsigned short ImapPort = 0;
  static unsigned short ImapsPort = 0;

  if (ImapPort == 0)
  {
    struct servent *service = getservbyname("imap", "tcp");
    if (service)
      ImapPort = ntohs(service->s_port);
    else
      ImapPort = IMAP_PORT;
    mutt_debug(LL_DEBUG3, "Using default IMAP port %d\n", ImapPort);
  }

  if (ImapsPort == 0)
  {
    struct servent *service = getservbyname("imaps", "tcp");
    if (service)
      ImapsPort = ntohs(service->s_port);
    else
      ImapsPort = IMAP_SSL_PORT;
    mutt_debug(LL_DEBUG3, "Using default IMAPS port %d\n", ImapsPort);
  }

  /* Defaults */
  cac->port = ImapPort;
  cac->type = MUTT_ACCT_TYPE_IMAP;
  cac->service = "imap";
  cac->get_field = imap_get_field;

  struct Url *url = url_parse(path);
  if (!url)
    return -1;

  if ((url->scheme != U_IMAP) && (url->scheme != U_IMAPS))
  {
    url_free(&url);
    return -1;
  }

  if ((mutt_account_fromurl(cac, url) < 0) || (cac->host[0] == '\0'))
  {
    url_free(&url);
    return -1;
  }

  if (url->scheme == U_IMAPS)
    cac->flags |= MUTT_ACCT_SSL;

  mutt_str_copy(mailbox, url->path, mailboxlen);

  url_free(&url);

  if ((cac->flags & MUTT_ACCT_SSL) && !(cac->flags & MUTT_ACCT_PORT))
    cac->port = ImapsPort;

  return 0;
}


char *imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
{
  if (!mdata->hcache)
    return NULL;

  char *seqset = NULL;
  size_t dlen = 0;
  char *hc_seqset = mutt_hcache_fetch_raw(mdata->hcache, "/UIDSEQSET", 10, &dlen);
  if (hc_seqset)
  {
    seqset = mutt_strn_dup(hc_seqset, dlen);
    mutt_hcache_free_raw(mdata->hcache, (void **) &hc_seqset);
  }
  mutt_debug(LL_DEBUG3, "Retrieved /UIDSEQSET %s\n", NONULL(seqset));

  return seqset;
}


enum QuadOption imap_continue(const char *msg, const char *resp)
{
  imap_error(msg, resp);
  return mutt_yesorno(_("Continue?"), MUTT_NO);
}


struct SeqsetIterator *mutt_seqset_iterator_new(const char *seqset)
{
  if (!seqset || (*seqset == '\0'))
    return NULL;

  struct SeqsetIterator *iter = mutt_mem_calloc(1, sizeof(struct SeqsetIterator));
  iter->full_seqset = mutt_str_dup(seqset);
  iter->eostr = strchr(iter->full_seqset, '\0');
  iter->substr_cur = iter->substr_end = iter->full_seqset;

  return iter;
}


int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
{
  if (!mdata->hcache)
    return -1;

  return mutt_hcache_delete_record(mdata->hcache, "/UIDSEQSET", 10);
}


void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
{
  if (!ptr || !*ptr)
    return;

  struct SeqsetIterator *iter = *ptr;
  FREE(&iter->full_seqset);
  FREE(ptr);
}


int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
{
  if (!iter || !next)
    return -1;

  if (iter->in_range)
  {
    if ((iter->down && (iter->range_cur == (iter->range_end - 1))) ||
        (!iter->down && (iter->range_cur == (iter->range_end + 1))))
    {
      iter->in_range = 0;
    }
  }

  if (!iter->in_range)
  {
    iter->substr_cur = iter->substr_end;
    if (iter->substr_cur == iter->eostr)
      return 1;

    iter->substr_end = strchr(iter->substr_cur, ',');
    if (!iter->substr_end)
      iter->substr_end = iter->eostr;
    else
      *(iter->substr_end++) = '\0';

    char *range_sep = strchr(iter->substr_cur, ':');
    if (range_sep)
      *range_sep++ = '\0';

    if (mutt_str_atoui(iter->substr_cur, &iter->range_cur) != 0)
      return -1;
    if (range_sep)
    {
      if (mutt_str_atoui(range_sep, &iter->range_end) != 0)
        return -1;
    }
    else
      iter->range_end = iter->range_cur;

    iter->down = (iter->range_end < iter->range_cur);
    iter->in_range = 1;
  }

  *next = iter->range_cur;
  if (iter->down)
    iter->range_cur--;
  else
    iter->range_cur++;

  return 0;
}


int imap_adata_find(const char *path, struct ImapAccountData **adata,
                    struct ImapMboxData **mdata)
{
  struct ConnAccount cac = { { 0 } };
  struct ImapAccountData *tmp_adata = NULL;
  char tmp[1024];

  if (imap_parse_path(path, &cac, tmp, sizeof(tmp)) < 0)
    return -1;

  struct Account *np = NULL;
  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
  {
    if (np->type != MUTT_IMAP)
      continue;

    tmp_adata = np->adata;
    if (!tmp_adata)
      continue;
    if (imap_account_match(&tmp_adata->conn->account, &cac))
    {
      *mdata = imap_mdata_new(tmp_adata, tmp);
      *adata = tmp_adata;
      return 0;
    }
  }
  mutt_debug(LL_DEBUG3, "no ImapAccountData found\n");
  return -1;
}


int imap_mxcmp(const char *mx1, const char *mx2)
{
  char *b1 = NULL;
  char *b2 = NULL;
  int rc;

  if (!mx1 || (*mx1 == '\0'))
    mx1 = "INBOX";
  if (!mx2 || (*mx2 == '\0'))
    mx2 = "INBOX";
  if (mutt_istr_equal(mx1, "INBOX") && mutt_istr_equal(mx2, "INBOX"))
  {
    return 0;
  }

  b1 = mutt_mem_malloc(strlen(mx1) + 1);
  b2 = mutt_mem_malloc(strlen(mx2) + 1);

  imap_fix_path('\0', mx1, b1, strlen(mx1) + 1);
  imap_fix_path('\0', mx2, b2, strlen(mx2) + 1);

  rc = mutt_str_cmp(b1, b2);
  FREE(&b1);
  FREE(&b2);

  return rc;
}


char *imap_next_word(char *s)
{
  bool quoted = false;

  while (*s)
  {
    if (*s == '\\')
    {
      s++;
      if (*s)
        s++;
      continue;
    }
    if (*s == '\"')
      quoted = !quoted;
    if (!quoted && IS_SPACE(*s))
      break;
    s++;
  }

  SKIPWS(s);
  return s;
}


int imap_wait_keepalive(pid_t pid)
{
  struct sigaction oldalrm;
  struct sigaction act;
  sigset_t oldmask;
  int rc;

  const bool c_imap_passive = cs_subset_bool(NeoMutt->sub, "imap_passive");
  cs_subset_str_native_set(NeoMutt->sub, "imap_passive", true, NULL);
  OptKeepQuiet = true;

  sigprocmask(SIG_SETMASK, NULL, &oldmask);

  sigemptyset(&act.sa_mask);
  act.sa_handler = mutt_sig_empty_handler;
#ifdef SA_INTERRUPT
  act.sa_flags = SA_INTERRUPT;
#else
  act.sa_flags = 0;
#endif

  sigaction(SIGALRM, &act, &oldalrm);

  const short c_imap_keepalive =
      cs_subset_number(NeoMutt->sub, "imap_keepalive");
  alarm(c_imap_keepalive);
  while ((waitpid(pid, &rc, 0) < 0) && (errno == EINTR))
  {
    alarm(0); /* cancel a possibly pending alarm */
    imap_keepalive();
    alarm(c_imap_keepalive);
  }

  alarm(0); /* cancel a possibly pending alarm */

  sigaction(SIGALRM, &oldalrm, NULL);
  sigprocmask(SIG_SETMASK, &oldmask, NULL);

  OptKeepQuiet = false;
  cs_subset_str_native_set(NeoMutt->sub, "imap_passive", c_imap_passive, NULL);

  return rc;
}


static void imap_hcache_namer(const char *path, struct Buffer *dest)
{
  mutt_buffer_printf(dest, "%s.hcache", path);
}


void imap_unmunge_mbox_name(bool unicode, char *s)
{
  imap_unquote_string(s);

  char *buf = mutt_str_dup(s);
  if (buf)
  {
    imap_utf_decode(unicode, &buf);
    strncpy(s, buf, strlen(s));
  }

  FREE(&buf);
}