diff --git a/include/freerdp/license.h b/include/freerdp/license.h index 046e9757d..85f88f35d 100644 --- a/include/freerdp/license.h +++ b/include/freerdp/license.h @@ -3,6 +3,8 @@ * Licensing API * * Copyright 2018 David Fort + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,142 +23,39 @@ #define FREERDP_LICENSE_H #include +#include -typedef struct rdp_license rdpLicense; - -/** @brief Licensing Packet Types */ -enum -{ - LICENSE_REQUEST = 0x01, - PLATFORM_CHALLENGE = 0x02, - NEW_LICENSE = 0x03, - UPGRADE_LICENSE = 0x04, - LICENSE_INFO = 0x12, - NEW_LICENSE_REQUEST = 0x13, - PLATFORM_CHALLENGE_RESPONSE = 0x15, - ERROR_ALERT = 0xFF -}; - -#define LICENSE_PKT_CS_MASK \ - (LICENSE_INFO | NEW_LICENSE_REQUEST | PLATFORM_CHALLENGE_RESPONSE | ERROR_ALERT) -#define LICENSE_PKT_SC_MASK \ - (LICENSE_REQUEST | PLATFORM_CHALLENGE | NEW_LICENSE | UPGRADE_LICENSE | ERROR_ALERT) -#define LICENSE_PKT_MASK (LICENSE_PKT_CS_MASK | LICENSE_PKT_SC_MASK) - -#define LICENSE_PREAMBLE_LENGTH 4 - -/* Cryptographic Lengths */ - -#define CLIENT_RANDOM_LENGTH 32 -#define SERVER_RANDOM_LENGTH 32 -#define MASTER_SECRET_LENGTH 48 -#define PREMASTER_SECRET_LENGTH 48 -#define SESSION_KEY_BLOB_LENGTH 48 -#define MAC_SALT_KEY_LENGTH 16 -#define LICENSING_ENCRYPTION_KEY_LENGTH 16 -#define HWID_PLATFORM_ID_LENGTH 4 -#define HWID_UNIQUE_DATA_LENGTH 16 -#define HWID_LENGTH 20 -#define LICENSING_PADDING_SIZE 8 - -/* Preamble Flags */ - -#define PREAMBLE_VERSION_2_0 0x02 -#define PREAMBLE_VERSION_3_0 0x03 -#define LicenseProtocolVersionMask 0x0F -#define EXTENDED_ERROR_MSG_SUPPORTED 0x80 - -/** @brief binary Blob Types */ -enum -{ - BB_ANY_BLOB = 0x0000, - BB_DATA_BLOB = 0x0001, - BB_RANDOM_BLOB = 0x0002, - BB_CERTIFICATE_BLOB = 0x0003, - BB_ERROR_BLOB = 0x0004, - BB_ENCRYPTED_DATA_BLOB = 0x0009, - BB_KEY_EXCHG_ALG_BLOB = 0x000D, - BB_SCOPE_BLOB = 0x000E, - BB_CLIENT_USER_NAME_BLOB = 0x000F, - BB_CLIENT_MACHINE_NAME_BLOB = 0x0010 -}; - -/* License Key Exchange Algorithms */ - -#define KEY_EXCHANGE_ALG_RSA 0x00000001 - -/** @brief license Error Codes */ -enum -{ - ERR_INVALID_SERVER_CERTIFICATE = 0x00000001, - ERR_NO_LICENSE = 0x00000002, - ERR_INVALID_MAC = 0x00000003, - ERR_INVALID_SCOPE = 0x00000004, - ERR_NO_LICENSE_SERVER = 0x00000006, - STATUS_VALID_CLIENT = 0x00000007, - ERR_INVALID_CLIENT = 0x00000008, - ERR_INVALID_PRODUCT_ID = 0x0000000B, - ERR_INVALID_MESSAGE_LENGTH = 0x0000000C -}; - -/** @brief state Transition Codes */ -enum -{ - ST_TOTAL_ABORT = 0x00000001, - ST_NO_TRANSITION = 0x00000002, - ST_RESET_PHASE_TO_START = 0x00000003, - ST_RESEND_LAST_MESSAGE = 0x00000004 -}; - -/** @brief Platform Challenge Types */ -enum -{ - WIN32_PLATFORM_CHALLENGE_TYPE = 0x0100, - WIN16_PLATFORM_CHALLENGE_TYPE = 0x0200, - WINCE_PLATFORM_CHALLENGE_TYPE = 0x0300, - OTHER_PLATFORM_CHALLENGE_TYPE = 0xFF00 -}; - -/** @brief License Detail Levels */ -enum -{ - LICENSE_DETAIL_SIMPLE = 0x0001, - LICENSE_DETAIL_MODERATE = 0x0002, - LICENSE_DETAIL_DETAIL = 0x0003 -}; - -/* - * PlatformId: - * - * The most significant byte of the PlatformId field contains the operating system version of the - * client. The second most significant byte of the PlatformId field identifies the ISV that provided - * the client image. The remaining two bytes in the PlatformId field are used by the ISV to identify - * the build number of the operating system. - * - * 0x04010000: - * - * CLIENT_OS_ID_WINNT_POST_52 (0x04000000) - * CLIENT_IMAGE_ID_MICROSOFT (0x00010000) - */ -enum -{ - CLIENT_OS_ID_WINNT_351 = 0x01000000, - CLIENT_OS_ID_WINNT_40 = 0x02000000, - CLIENT_OS_ID_WINNT_50 = 0x03000000, - CLIENT_OS_ID_WINNT_POST_52 = 0x04000000, - - CLIENT_IMAGE_ID_MICROSOFT = 0x00010000, - CLIENT_IMAGE_ID_CITRIX = 0x00020000, -}; - -#ifdef __cpluscplus +#ifdef __cplusplus extern "C" { #endif - FREERDP_API BOOL license_send_valid_client_error_packet(rdpRdp* rdp); + typedef enum + { + LICENSE_STATE_INITIAL, + LICENSE_STATE_CONFIGURED, + LICENSE_STATE_REQUEST, + LICENSE_STATE_NEW_REQUEST, + LICENSE_STATE_PLATFORM_CHALLENGE, + LICENSE_STATE_PLATFORM_CHALLENGE_RESPONSE, + LICENSE_STATE_COMPLETED, + LICENSE_STATE_ABORTED + } LICENSE_STATE; -#ifdef __cpluscplus + typedef enum + { + LICENSE_TYPE_INVALID = 0, + LICENSE_TYPE_NONE, + LICENSE_TYPE_ISSUED + } LICENSE_TYPE; + + typedef struct rdp_license rdpLicense; + + FREERDP_API rdpLicense* license_get(rdpContext* context); + FREERDP_API LICENSE_STATE license_get_state(const rdpLicense* license); + FREERDP_API LICENSE_TYPE license_get_type(const rdpLicense* license); + +#ifdef __cplusplus } #endif diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index ad5cead87..13932f965 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -550,6 +550,12 @@ typedef struct #define FreeRDP_ServerCertificateLength (199) #define FreeRDP_ClientRandom (200) #define FreeRDP_ClientRandomLength (201) +#define FreeRDP_ServerLicenseRequired (202) +#define FreeRDP_ServerLicenseCompanyName (203) +#define FreeRDP_ServerLicenseProductVersion (204) +#define FreeRDP_ServerLicenseProductName (205) +#define FreeRDP_ServerLicenseProductIssuers (206) +#define FreeRDP_ServerLicenseProductIssuersCount (207) #define FreeRDP_ChannelCount (256) #define FreeRDP_ChannelDefArraySize (257) #define FreeRDP_ChannelDefArray (258) @@ -1003,7 +1009,13 @@ struct rdp_settings ALIGN64 UINT32 ServerCertificateLength; /* 199 */ ALIGN64 BYTE* ClientRandom; /* 200 */ ALIGN64 UINT32 ClientRandomLength; /* 201 */ - UINT64 padding0256[256 - 202]; /* 202 */ + ALIGN64 BOOL ServerLicenseRequired; /* 202 */ + ALIGN64 char* ServerLicenseCompanyName; /* 203 */ + ALIGN64 UINT32 ServerLicenseProductVersion; /* 204 */ + ALIGN64 char* ServerLicenseProductName; /* 205 */ + ALIGN64 char** ServerLicenseProductIssuers; /* 206 */ + ALIGN64 UINT32 ServerLicenseProductIssuersCount; /* 207 */ + UINT64 padding0256[256 - 208]; /* 208 */ /* Client Network Data */ ALIGN64 UINT32 ChannelCount; /* 256 */ @@ -1747,6 +1759,10 @@ extern "C" FREERDP_API void freerdp_capability_buffer_free(rdpSettings* settings); FREERDP_API BOOL freerdp_capability_buffer_copy(rdpSettings* settings, const rdpSettings* src); + FREERDP_API void freerdp_server_license_issuers_free(rdpSettings* settings); + FREERDP_API BOOL freerdp_server_license_issuers_copy(rdpSettings* settings, char** addresses, + UINT32 count); + FREERDP_API void freerdp_target_net_addresses_free(rdpSettings* settings); FREERDP_API BOOL freerdp_target_net_addresses_copy(rdpSettings* settings, char** addresses, UINT32 count); diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index 2fc4631e9..bde4892ba 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -901,6 +901,43 @@ void freerdp_target_net_addresses_free(rdpSettings* settings) settings->TargetNetPorts = NULL; } +void freerdp_server_license_issuers_free(rdpSettings* settings) +{ + UINT32 x; + WINPR_ASSERT(settings); + + if (settings->ServerLicenseProductIssuers) + { + for (x = 0; x < settings->ServerLicenseProductIssuersCount; x++) + free(settings->ServerLicenseProductIssuers[x]); + } + free(settings->ServerLicenseProductIssuers); + settings->ServerLicenseProductIssuers = NULL; + settings->ServerLicenseProductIssuersCount = 0; +} + +BOOL freerdp_server_license_issuers_copy(rdpSettings* settings, char** issuers, UINT32 count) +{ + UINT32 x; + + WINPR_ASSERT(settings); + WINPR_ASSERT(issuers || (count == 0)); + + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ServerLicenseProductIssuers, NULL, + count)) + return FALSE; + + for (x = 0; x < count; x++) + { + char* issuer = _strdup(issuers[x]); + if (!issuer) + return FALSE; + settings->ServerLicenseProductIssuers[x] = issuer; + } + + return TRUE; +} + void freerdp_performance_flags_make(rdpSettings* settings) { UINT32 PerformanceFlags = PERF_FLAG_NONE; @@ -1309,6 +1346,12 @@ BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, size_t id, const vo return freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetAddresses, FreeRDP_TargetNetAddressCount, data, len, sizeof(char*)); + case FreeRDP_ServerLicenseProductIssuers: + if (data == NULL) + freerdp_server_license_issuers_free(settings); + return freerdp_settings_set_pointer_len_(settings, FreeRDP_ServerLicenseProductIssuers, + FreeRDP_ServerLicenseProductIssuersCount, data, + len, sizeof(char*)); case FreeRDP_TargetNetPorts: if (data == NULL) freerdp_target_net_addresses_free(settings); diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 0cc02d84e..414ae286f 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -435,6 +435,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id) case FreeRDP_SendPreconnectionPdu: return settings->SendPreconnectionPdu; + case FreeRDP_ServerLicenseRequired: + return settings->ServerLicenseRequired; + case FreeRDP_ServerMode: return settings->ServerMode; @@ -1111,6 +1114,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val) settings->SendPreconnectionPdu = cnv.c; break; + case FreeRDP_ServerLicenseRequired: + settings->ServerLicenseRequired = cnv.c; + break; + case FreeRDP_ServerMode: settings->ServerMode = cnv.c; break; @@ -1745,6 +1752,12 @@ UINT32 freerdp_settings_get_uint32(const rdpSettings* settings, size_t id) case FreeRDP_ServerCertificateLength: return settings->ServerCertificateLength; + case FreeRDP_ServerLicenseProductIssuersCount: + return settings->ServerLicenseProductIssuersCount; + + case FreeRDP_ServerLicenseProductVersion: + return settings->ServerLicenseProductVersion; + case FreeRDP_ServerPort: return settings->ServerPort; @@ -2223,6 +2236,14 @@ BOOL freerdp_settings_set_uint32(rdpSettings* settings, size_t id, UINT32 val) settings->ServerCertificateLength = cnv.c; break; + case FreeRDP_ServerLicenseProductIssuersCount: + settings->ServerLicenseProductIssuersCount = cnv.c; + break; + + case FreeRDP_ServerLicenseProductVersion: + settings->ServerLicenseProductVersion = cnv.c; + break; + case FreeRDP_ServerPort: settings->ServerPort = cnv.c; break; @@ -2666,6 +2687,12 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_ServerHostname: return settings->ServerHostname; + case FreeRDP_ServerLicenseCompanyName: + return settings->ServerLicenseCompanyName; + + case FreeRDP_ServerLicenseProductName: + return settings->ServerLicenseProductName; + case FreeRDP_ShellWorkingDirectory: return settings->ShellWorkingDirectory; @@ -2941,6 +2968,12 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_ServerHostname: return settings->ServerHostname; + case FreeRDP_ServerLicenseCompanyName: + return settings->ServerLicenseCompanyName; + + case FreeRDP_ServerLicenseProductName: + return settings->ServerLicenseProductName; + case FreeRDP_ShellWorkingDirectory: return settings->ShellWorkingDirectory; @@ -3226,6 +3259,12 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_ServerHostname: return update_string(&settings->ServerHostname, cnv.cc, len, cleanup); + case FreeRDP_ServerLicenseCompanyName: + return update_string(&settings->ServerLicenseCompanyName, cnv.cc, len, cleanup); + + case FreeRDP_ServerLicenseProductName: + return update_string(&settings->ServerLicenseProductName, cnv.cc, len, cleanup); + case FreeRDP_ShellWorkingDirectory: return update_string(&settings->ShellWorkingDirectory, cnv.cc, len, cleanup); @@ -3359,6 +3398,9 @@ void* freerdp_settings_get_pointer_writable(rdpSettings* settings, size_t id) case FreeRDP_ServerCertificate: return settings->ServerCertificate; + case FreeRDP_ServerLicenseProductIssuers: + return settings->ServerLicenseProductIssuers; + case FreeRDP_ServerRandom: return settings->ServerRandom; @@ -3487,6 +3529,10 @@ BOOL freerdp_settings_set_pointer(rdpSettings* settings, size_t id, const void* settings->ServerCertificate = cnv.v; break; + case FreeRDP_ServerLicenseProductIssuers: + settings->ServerLicenseProductIssuers = cnv.v; + break; + case FreeRDP_ServerRandom: settings->ServerRandom = cnv.v; break; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 355536730..4ac79847a 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -191,6 +191,7 @@ static const struct settings_str_entry settings_map[] = { "FreeRDP_RestrictedAdminModeRequired" }, { FreeRDP_SaltedChecksum, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_SaltedChecksum" }, { FreeRDP_SendPreconnectionPdu, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_SendPreconnectionPdu" }, + { FreeRDP_ServerLicenseRequired, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_ServerLicenseRequired" }, { FreeRDP_ServerMode, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_ServerMode" }, { FreeRDP_SmartSizing, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_SmartSizing" }, { FreeRDP_SmartcardEmulation, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_SmartcardEmulation" }, @@ -383,6 +384,10 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_SelectedProtocol, FREERDP_SETTINGS_TYPE_UINT32, "FreeRDP_SelectedProtocol" }, { FreeRDP_ServerCertificateLength, FREERDP_SETTINGS_TYPE_UINT32, "FreeRDP_ServerCertificateLength" }, + { FreeRDP_ServerLicenseProductIssuersCount, FREERDP_SETTINGS_TYPE_UINT32, + "FreeRDP_ServerLicenseProductIssuersCount" }, + { FreeRDP_ServerLicenseProductVersion, FREERDP_SETTINGS_TYPE_UINT32, + "FreeRDP_ServerLicenseProductVersion" }, { FreeRDP_ServerPort, FREERDP_SETTINGS_TYPE_UINT32, "FreeRDP_ServerPort" }, { FreeRDP_ServerRandomLength, FREERDP_SETTINGS_TYPE_UINT32, "FreeRDP_ServerRandomLength" }, { FreeRDP_ShareId, FREERDP_SETTINGS_TYPE_UINT32, "FreeRDP_ShareId" }, @@ -503,6 +508,10 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_RemoteAssistanceSessionId, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_RemoteAssistanceSessionId" }, { FreeRDP_ServerHostname, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_ServerHostname" }, + { FreeRDP_ServerLicenseCompanyName, FREERDP_SETTINGS_TYPE_STRING, + "FreeRDP_ServerLicenseCompanyName" }, + { FreeRDP_ServerLicenseProductName, FREERDP_SETTINGS_TYPE_STRING, + "FreeRDP_ServerLicenseProductName" }, { FreeRDP_ShellWorkingDirectory, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_ShellWorkingDirectory" }, { FreeRDP_SmartcardCertificate, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_SmartcardCertificate" }, @@ -545,6 +554,8 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_ServerAutoReconnectCookie, FREERDP_SETTINGS_TYPE_POINTER, "FreeRDP_ServerAutoReconnectCookie" }, { FreeRDP_ServerCertificate, FREERDP_SETTINGS_TYPE_POINTER, "FreeRDP_ServerCertificate" }, + { FreeRDP_ServerLicenseProductIssuers, FREERDP_SETTINGS_TYPE_POINTER, + "FreeRDP_ServerLicenseProductIssuers" }, { FreeRDP_ServerRandom, FREERDP_SETTINGS_TYPE_POINTER, "FreeRDP_ServerRandom" }, { FreeRDP_StaticChannelArray, FREERDP_SETTINGS_TYPE_POINTER, "FreeRDP_StaticChannelArray" }, { FreeRDP_TargetNetAddresses, FREERDP_SETTINGS_TYPE_POINTER, "FreeRDP_TargetNetAddresses" }, diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 302b83731..e17cbba97 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -1053,23 +1053,40 @@ BOOL rdp_client_connect_auto_detect(rdpRdp* rdp, wStream* s) int rdp_client_connect_license(rdpRdp* rdp, wStream* s) { int status; + LICENSE_STATE state; + UINT16 length, channelId, securityFlags; + + if (!rdp_read_header(rdp, s, &length, &channelId)) + return -1; + + if (!rdp_read_security_header(s, &securityFlags, &length)) + return -1; + + if (securityFlags & SEC_ENCRYPT) + { + if (!rdp_decrypt(rdp, s, &length, securityFlags)) + return -1; + } + + if ((securityFlags & SEC_LICENSE_PKT) == 0) + return -1; status = license_recv(rdp->license, s); if (status < 0) return status; - if (rdp->license->state == LICENSE_STATE_ABORTED) + state = license_get_state(rdp->license); + switch (state) { - WLog_ERR(TAG, "license connection sequence aborted."); - return -1; + case LICENSE_STATE_ABORTED: + WLog_ERR(TAG, "license connection sequence aborted."); + return -1; + case LICENSE_STATE_COMPLETED: + rdp_client_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); + return 0; + default: + return 0; } - - if (rdp->license->state == LICENSE_STATE_COMPLETED) - { - rdp_client_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); - } - - return 0; } int rdp_client_connect_demand_active(rdpRdp* rdp, wStream* s) diff --git a/libfreerdp/core/license.c b/libfreerdp/core/license.c index b0cff6303..69a34b260 100644 --- a/libfreerdp/core/license.c +++ b/libfreerdp/core/license.c @@ -5,6 +5,8 @@ * Copyright 2011-2013 Marc-Andre Moreau * Copyright 2014 Norbert Federa * Copyright 2018 David Fort + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +22,10 @@ */ #include +#include #include +#include #include #include #include @@ -41,6 +45,249 @@ #define LICENSE_NULL_PREMASTER_SECRET 1 #endif +#define PLATFORM_CHALLENGE_RESPONSE_VERSION 0x0100 + +/** @brief Licensing Packet Types */ +enum LicenseRequestType +{ + LICENSE_REQUEST = 0x01, + PLATFORM_CHALLENGE = 0x02, + NEW_LICENSE = 0x03, + UPGRADE_LICENSE = 0x04, + LICENSE_INFO = 0x12, + NEW_LICENSE_REQUEST = 0x13, + PLATFORM_CHALLENGE_RESPONSE = 0x15, + ERROR_ALERT = 0xFF +}; + +#define LICENSE_PKT_CS_MASK \ + (LICENSE_INFO | NEW_LICENSE_REQUEST | PLATFORM_CHALLENGE_RESPONSE | ERROR_ALERT) +#define LICENSE_PKT_SC_MASK \ + (LICENSE_REQUEST | PLATFORM_CHALLENGE | NEW_LICENSE | UPGRADE_LICENSE | ERROR_ALERT) +#define LICENSE_PKT_MASK (LICENSE_PKT_CS_MASK | LICENSE_PKT_SC_MASK) + +#define LICENSE_PREAMBLE_LENGTH 4 + +/* Cryptographic Lengths */ + +#define SERVER_RANDOM_LENGTH 32 +#define MASTER_SECRET_LENGTH 48 +#define PREMASTER_SECRET_LENGTH 48 +#define SESSION_KEY_BLOB_LENGTH 48 +#define MAC_SALT_KEY_LENGTH 16 +#define LICENSING_ENCRYPTION_KEY_LENGTH 16 +#define HWID_PLATFORM_ID_LENGTH 4 +#define HWID_UNIQUE_DATA_LENGTH 16 +#define HWID_LENGTH 20 +#define LICENSING_PADDING_SIZE 8 + +/* Preamble Flags */ + +#define PREAMBLE_VERSION_2_0 0x02 +#define PREAMBLE_VERSION_3_0 0x03 +#define LicenseProtocolVersionMask 0x0F +#define EXTENDED_ERROR_MSG_SUPPORTED 0x80 + +/** @brief binary Blob Types */ +enum +{ + BB_ANY_BLOB = 0x0000, + BB_DATA_BLOB = 0x0001, + BB_RANDOM_BLOB = 0x0002, + BB_CERTIFICATE_BLOB = 0x0003, + BB_ERROR_BLOB = 0x0004, + BB_ENCRYPTED_DATA_BLOB = 0x0009, + BB_KEY_EXCHG_ALG_BLOB = 0x000D, + BB_SCOPE_BLOB = 0x000E, + BB_CLIENT_USER_NAME_BLOB = 0x000F, + BB_CLIENT_MACHINE_NAME_BLOB = 0x0010 +}; + +/* License Key Exchange Algorithms */ + +#define KEY_EXCHANGE_ALG_RSA 0x00000001 + +/** @brief license Error Codes */ +enum +{ + ERR_INVALID_SERVER_CERTIFICATE = 0x00000001, + ERR_NO_LICENSE = 0x00000002, + ERR_INVALID_MAC = 0x00000003, + ERR_INVALID_SCOPE = 0x00000004, + ERR_NO_LICENSE_SERVER = 0x00000006, + STATUS_VALID_CLIENT = 0x00000007, + ERR_INVALID_CLIENT = 0x00000008, + ERR_INVALID_PRODUCT_ID = 0x0000000B, + ERR_INVALID_MESSAGE_LENGTH = 0x0000000C +}; + +/** @brief state Transition Codes */ +enum +{ + ST_TOTAL_ABORT = 0x00000001, + ST_NO_TRANSITION = 0x00000002, + ST_RESET_PHASE_TO_START = 0x00000003, + ST_RESEND_LAST_MESSAGE = 0x00000004 +}; + +/** @brief Platform Challenge Types */ +enum +{ + WIN32_PLATFORM_CHALLENGE_TYPE = 0x0100, + WIN16_PLATFORM_CHALLENGE_TYPE = 0x0200, + WINCE_PLATFORM_CHALLENGE_TYPE = 0x0300, + OTHER_PLATFORM_CHALLENGE_TYPE = 0xFF00 +}; + +/** @brief License Detail Levels */ +enum +{ + LICENSE_DETAIL_SIMPLE = 0x0001, + LICENSE_DETAIL_MODERATE = 0x0002, + LICENSE_DETAIL_DETAIL = 0x0003 +}; + +/* + * PlatformId: + * + * The most significant byte of the PlatformId field contains the operating system version of the + * client. The second most significant byte of the PlatformId field identifies the ISV that provided + * the client image. The remaining two bytes in the PlatformId field are used by the ISV to identify + * the build number of the operating system. + * + * 0x04010000: + * + * CLIENT_OS_ID_WINNT_POST_52 (0x04000000) + * CLIENT_IMAGE_ID_MICROSOFT (0x00010000) + */ +enum +{ + CLIENT_OS_ID_WINNT_351 = 0x01000000, + CLIENT_OS_ID_WINNT_40 = 0x02000000, + CLIENT_OS_ID_WINNT_50 = 0x03000000, + CLIENT_OS_ID_WINNT_POST_52 = 0x04000000, + + CLIENT_IMAGE_ID_MICROSOFT = 0x00010000, + CLIENT_IMAGE_ID_CITRIX = 0x00020000, +}; + +struct rdp_license +{ + LICENSE_STATE state; + LICENSE_TYPE type; + rdpRdp* rdp; + rdpCertificate* certificate; + BYTE* Modulus; + UINT32 ModulusLength; + BYTE Exponent[4]; + BYTE HardwareId[HWID_LENGTH]; + BYTE ClientRandom[CLIENT_RANDOM_LENGTH]; + BYTE ServerRandom[SERVER_RANDOM_LENGTH]; + BYTE MasterSecret[MASTER_SECRET_LENGTH]; + BYTE PremasterSecret[PREMASTER_SECRET_LENGTH]; + BYTE SessionKeyBlob[SESSION_KEY_BLOB_LENGTH]; + BYTE MacSaltKey[MAC_SALT_KEY_LENGTH]; + BYTE LicensingEncryptionKey[LICENSING_ENCRYPTION_KEY_LENGTH]; + LICENSE_PRODUCT_INFO* ProductInfo; + LICENSE_BLOB* ErrorInfo; + LICENSE_BLOB* LicenseInfo; /* Client -> Server */ + LICENSE_BLOB* KeyExchangeList; + LICENSE_BLOB* ServerCertificate; + LICENSE_BLOB* ClientUserName; + LICENSE_BLOB* ClientMachineName; + LICENSE_BLOB* PlatformChallenge; + LICENSE_BLOB* EncryptedPremasterSecret; + LICENSE_BLOB* EncryptedPlatformChallenge; + LICENSE_BLOB* EncryptedPlatformChallengeResponse; + LICENSE_BLOB* EncryptedHardwareId; + LICENSE_BLOB* EncryptedLicenseInfo; + BYTE MACData[LICENSING_ENCRYPTION_KEY_LENGTH]; + SCOPE_LIST* ScopeList; + UINT32 PacketHeaderLength; + UINT32 PreferredKeyExchangeAlg; + UINT32 PlatformId; + UINT16 ClientType; + UINT16 LicenseDetailLevel; + BOOL update; +}; + +static BOOL license_send_error_alert(rdpLicense* license, UINT32 dwErrorCode, + UINT32 dwStateTransition, const LICENSE_BLOB* info); +static BOOL license_set_state(rdpLicense* license, LICENSE_STATE state); +static const char* license_get_state_string(LICENSE_STATE state); + +static const char* license_preferred_key_exchange_alg_string(UINT32 alg) +{ + static char buffer[64] = { 0 }; + const char* name = NULL; + + switch (alg) + { + case KEY_EXCHANGE_ALG_RSA: + name = "KEY_EXCHANGE_ALG_RSA"; + break; + default: + name = "KEY_EXCHANGE_ALG_UNKNOWN"; + break; + } + + _snprintf(buffer, sizeof(buffer), "%s [0x%08" PRIx32 "]", name, alg); + return buffer; +} + +static const char* license_request_type_string(UINT32 type) +{ + switch (type) + { + case LICENSE_REQUEST: + return "LICENSE_REQUEST"; + case PLATFORM_CHALLENGE: + return "PLATFORM_CHALLENGE"; + case NEW_LICENSE: + return "NEW_LICENSE"; + case UPGRADE_LICENSE: + return "UPGRADE_LICENSE"; + case LICENSE_INFO: + return "LICENSE_INFO"; + case NEW_LICENSE_REQUEST: + return "NEW_LICENSE_REQUEST"; + case PLATFORM_CHALLENGE_RESPONSE: + return "PLATFORM_CHALLENGE_RESPONSE"; + case ERROR_ALERT: + return "ERROR_ALERT"; + default: + return "LICENSE_REQUEST_TYPE_UNKNOWN"; + } +} + +static const char* licencse_blob_type_string(UINT16 type) +{ + switch (type) + { + case BB_ANY_BLOB: + return "BB_ANY_BLOB"; + case BB_DATA_BLOB: + return "BB_DATA_BLOB"; + case BB_RANDOM_BLOB: + return "BB_RANDOM_BLOB"; + case BB_CERTIFICATE_BLOB: + return "BB_CERTIFICATE_BLOB"; + case BB_ERROR_BLOB: + return "BB_ERROR_BLOB"; + case BB_ENCRYPTED_DATA_BLOB: + return "BB_ENCRYPTED_DATA_BLOB"; + case BB_KEY_EXCHG_ALG_BLOB: + return "BB_KEY_EXCHG_ALG_BLOB"; + case BB_SCOPE_BLOB: + return "BB_SCOPE_BLOB"; + case BB_CLIENT_USER_NAME_BLOB: + return "BB_CLIENT_USER_NAME_BLOB"; + case BB_CLIENT_MACHINE_NAME_BLOB: + return "BB_CLIENT_MACHINE_NAME_BLOB"; + default: + return "BB_UNKNOWN"; + } +} static wStream* license_send_stream_init(rdpLicense* license); static void license_generate_randoms(rdpLicense* license); @@ -54,64 +301,48 @@ static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productI static LICENSE_BLOB* license_new_binary_blob(UINT16 type); static void license_free_binary_blob(LICENSE_BLOB* blob); +static BOOL license_read_binary_blob_data(LICENSE_BLOB* blob, UINT32 type, const void* data, + size_t length); 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 BOOL license_scope_list_resize(SCOPE_LIST* scopeList, UINT32 count); static void license_free_scope_list(SCOPE_LIST* scopeList); static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList); +static BOOL license_write_scope_list(wStream* s, const SCOPE_LIST* scopeList); static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s); +static BOOL license_write_license_request_packet(const rdpLicense* license, wStream* s); + static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s); +static BOOL license_send_platform_challenge_packet(rdpLicense* license); 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_write_new_license_request_packet(const rdpLicense* license, wStream* s); +static BOOL license_read_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_send_platform_challenge_response(rdpLicense* license); +static BOOL license_read_platform_challenge_response(rdpLicense* license, wStream* s); + +static BOOL license_read_client_platform_challenge_response(rdpLicense* license, wStream* s); +static BOOL license_write_client_platform_challenge_response(rdpLicense* license, wStream* s); + +static BOOL license_read_server_upgrade_license(rdpLicense* license, wStream* s); +static BOOL license_write_server_upgrade_license(const rdpLicense* license, wStream* s); + +static BOOL license_send_license_info(rdpLicense* license, const LICENSE_BLOB* calBlob, + BYTE* signature); +static BOOL license_read_license_info(rdpLicense* license, wStream* s); +static int license_client_recv(rdpLicense* license, wStream* s); +static int license_server_recv(rdpLicense* license, wStream* s); #define PLATFORMID (CLIENT_OS_ID_WINNT_POST_52 | CLIENT_IMAGE_ID_MICROSOFT) #ifdef WITH_DEBUG_LICENSE -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", @@ -134,6 +365,9 @@ static void license_print_product_info(const LICENSE_PRODUCT_INFO* productInfo) { char* CompanyName = NULL; char* ProductId = NULL; + + WINPR_ASSERT(productInfo); + ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)productInfo->pbCompanyName, productInfo->cbCompanyName / 2, &CompanyName, 0, NULL, NULL); ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)productInfo->pbProductId, productInfo->cbProductId / 2, @@ -149,12 +383,19 @@ static void license_print_product_info(const LICENSE_PRODUCT_INFO* productInfo) static void license_print_scope_list(const SCOPE_LIST* scopeList) { UINT32 index; - const LICENSE_BLOB* scope; + + WINPR_ASSERT(scopeList); + WLog_INFO(TAG, "ScopeList (%" PRIu32 "):", scopeList->count); for (index = 0; index < scopeList->count; index++) { - scope = &scopeList->array[index]; + const LICENSE_BLOB* scope; + + WINPR_ASSERT(scopeList->array); + scope = scopeList->array[index]; + WINPR_ASSERT(scope); + WLog_INFO(TAG, "\t%s", (const char*)scope->data); } } @@ -162,13 +403,79 @@ static void license_print_scope_list(const SCOPE_LIST* scopeList) static const char licenseStore[] = "licenses"; +static BOOL license_ensure_state(rdpLicense* license, LICENSE_STATE state, UINT32 msg) +{ + const LICENSE_STATE cstate = license_get_state(license); + + WINPR_ASSERT(license); + + if (cstate != state) + { + const char* scstate = license_get_state_string(cstate); + const char* sstate = license_get_state_string(state); + const char* where = license_request_type_string(msg); + + WLog_WARN(TAG, "Received [%s], but found invalid licensing state %s, expected %s", where, + scstate, sstate); + return FALSE; + } + return TRUE; +} + +int license_recv(rdpLicense* license, wStream* s) +{ + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + WINPR_ASSERT(license->rdp->settings); + + if (freerdp_settings_get_bool(license->rdp->settings, FreeRDP_ServerMode)) + return license_server_recv(license, s); + else + return license_client_recv(license, s); +} + +static BOOL license_check_stream_length(wStream* s, SSIZE_T expect, const char* where) +{ + const size_t remain = Stream_GetRemainingLength(s); + + WINPR_ASSERT(where); + + if (expect < 0) + { + WLog_WARN(TAG, "invalid %s, expected value %" PRIdz " invalid", where, expect); + return FALSE; + } + if (remain < expect) + { + WLog_WARN(TAG, "short %s, expected %" PRIdz " bytes, got %" PRIuz, where, expect, remain); + return FALSE; + } + return TRUE; +} + +static BOOL license_check_stream_capacity(wStream* s, size_t expect, const char* where) +{ + WINPR_ASSERT(where); + + if (Stream_GetRemainingCapacity(s) < expect) + { + WLog_WARN(TAG, "short capacity %s, expected %" PRIuz " bytes, got %" PRIuz, where, expect, + Stream_GetRemainingCapacity(s)); + return FALSE; + } + return TRUE; +} + static BOOL computeCalHash(const char* hostname, char* hashStr) { WINPR_DIGEST_CTX* sha1 = NULL; BOOL ret = FALSE; - BYTE hash[20]; + BYTE hash[20] = { 0 }; size_t i; + WINPR_ASSERT(hostname); + WINPR_ASSERT(hashStr); + if (!(sha1 = winpr_Digest_New())) goto out; if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) @@ -187,7 +494,8 @@ out: return ret; } -static BOOL saveCal(rdpSettings* settings, const BYTE* data, size_t length, const char* hostname) +static BOOL saveCal(const rdpSettings* settings, const BYTE* data, size_t length, + const char* hostname) { char hash[41] = { 0 }; FILE* fp; @@ -197,18 +505,23 @@ static BOOL saveCal(rdpSettings* settings, const BYTE* data, size_t length, cons size_t written; BOOL ret = FALSE; + const char* path = freerdp_settings_get_string(settings, FreeRDP_ConfigPath); - if (!winpr_PathFileExists(settings->ConfigPath)) + WINPR_ASSERT(path); + WINPR_ASSERT(data || (length == 0)); + WINPR_ASSERT(hostname); + + if (!winpr_PathFileExists(path)) { - if (!winpr_PathMakePath(settings->ConfigPath, 0)) + if (!winpr_PathMakePath(path, 0)) { - WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath); + WLog_ERR(TAG, "error creating directory '%s'", path); goto out; } - WLog_INFO(TAG, "creating directory %s", settings->ConfigPath); + WLog_INFO(TAG, "creating directory %s", path); } - if (!(licenseStorePath = GetCombinedPath(settings->ConfigPath, licenseStore))) + if (!(licenseStorePath = GetCombinedPath(path, licenseStore))) goto out; if (!winpr_PathFileExists(licenseStorePath)) @@ -254,16 +567,20 @@ out: return ret; } -static BYTE* loadCalFile(rdpSettings* settings, const char* hostname, size_t* dataLen) +static BYTE* loadCalFile(const rdpSettings* settings, const char* hostname, size_t* dataLen) { char *licenseStorePath = NULL, *calPath = NULL; - char calFilename[MAX_PATH]; - char hash[41]; + char calFilename[MAX_PATH] = { 0 }; + char hash[41] = { 0 }; INT64 length; int status; FILE* fp; BYTE* ret = NULL; + WINPR_ASSERT(settings); + WINPR_ASSERT(hostname); + WINPR_ASSERT(dataLen); + if (!computeCalHash(hostname, hash)) { WLog_ERR(TAG, "loadCalFile: unable to compute hostname hash"); @@ -272,7 +589,8 @@ static BYTE* loadCalFile(rdpSettings* settings, const char* hostname, size_t* da sprintf_s(calFilename, sizeof(calFilename) - 1, "%s.cal", hash); - if (!(licenseStorePath = GetCombinedPath(settings->ConfigPath, licenseStore))) + if (!(licenseStorePath = GetCombinedPath( + freerdp_settings_get_string(settings, FreeRDP_ConfigPath), licenseStore))) return NULL; if (!(calPath = GetCombinedPath(licenseStorePath, calFilename))) @@ -326,14 +644,18 @@ error_path: static BOOL license_read_preamble(wStream* s, BYTE* bMsgType, BYTE* flags, UINT16* wMsgSize) { + WINPR_ASSERT(bMsgType); + WINPR_ASSERT(flags); + WINPR_ASSERT(wMsgSize); + /* preamble (4 bytes) */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + if (!license_check_stream_length(s, 4, "license preamble")) 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; + return license_check_stream_length(s, *wMsgSize - 4ll, "license preamble::wMsgSize"); } /** @@ -366,7 +688,12 @@ static BOOL license_write_preamble(wStream* s, BYTE bMsgType, BYTE flags, UINT16 wStream* license_send_stream_init(rdpLicense* license) { wStream* s; - BOOL do_crypt = license->rdp->do_crypt; + BOOL do_crypt; + + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + + do_crypt = license->rdp->do_crypt; license->rdp->sec_flags = SEC_LICENSE_PKT; @@ -411,10 +738,16 @@ static BOOL license_send(rdpLicense* license, wStream* s, BYTE type) size_t length; BYTE flags; UINT16 wMsgSize; - rdpRdp* rdp = license->rdp; + rdpRdp* rdp; BOOL ret; - DEBUG_LICENSE("Sending %s Packet", LICENSE_MESSAGE_STRINGS[type & 0x1F]); + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + + rdp = license->rdp; + WINPR_ASSERT(rdp->settings); + + DEBUG_LICENSE("Sending %s Packet", license_request_type_string(type)); length = Stream_GetPosition(s); wMsgSize = length - license->PacketHeaderLength; Stream_SetPosition(s, license->PacketHeaderLength); @@ -432,7 +765,7 @@ static BOOL license_send(rdpLicense* license, wStream* s, BYTE type) return FALSE; #ifdef WITH_DEBUG_LICENSE - WLog_DBG(TAG, "Sending %s Packet, length %" PRIu16 "", LICENSE_MESSAGE_STRINGS[type & 0x1F], + WLog_DBG(TAG, "Sending %s Packet, length %" PRIu16 "", license_request_type_string(type), wMsgSize); winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(s) - LICENSE_PREAMBLE_LENGTH, wMsgSize); #endif @@ -442,6 +775,50 @@ static BOOL license_send(rdpLicense* license, wStream* s, BYTE type) return ret; } +BOOL license_read_server_upgrade_license(rdpLicense* license, wStream* s) +{ + WINPR_ASSERT(license); + + if (!license_read_binary_blob(s, license->EncryptedLicenseInfo)) + return FALSE; + if (!license_check_stream_length(s, sizeof(license->MACData), + "SERVER_UPGRADE_LICENSE::MACData")) + return FALSE; + Stream_Read(s, license->MACData, sizeof(license->MACData)); + return TRUE; +} + +BOOL license_write_server_upgrade_license(const rdpLicense* license, wStream* s) +{ + WINPR_ASSERT(license); + + if (!license_write_binary_blob(s, license->EncryptedLicenseInfo)) + return FALSE; + if (!license_check_stream_capacity(s, sizeof(license->MACData), + "SERVER_UPGRADE_LICENSE::MACData")) + return FALSE; + Stream_Write(s, license->MACData, sizeof(license->MACData)); + return TRUE; +} + +static BOOL license_server_send_new_or_upgrade_license(rdpLicense* license, BOOL upgrade) +{ + wStream* s = license_send_stream_init(license); + const BYTE type = upgrade ? UPGRADE_LICENSE : NEW_LICENSE; + + if (!s) + return FALSE; + + if (!license_write_server_upgrade_license(license, s)) + goto fail; + + return license_send(license, s, type); + +fail: + Stream_Release(s); + return FALSE; +} + /** * Receive an RDP licensing packet.\n * @msdn{cc240479} @@ -450,72 +827,55 @@ static BOOL license_send(rdpLicense* license, wStream* s, BYTE type) * @return if the operation completed successfully */ -int license_recv(rdpLicense* license, wStream* s) +int license_client_recv(rdpLicense* license, wStream* s) { BYTE flags; BYTE bMsgType; UINT16 wMsgSize; - UINT16 length; - UINT16 channelId; - UINT16 securityFlags = 0; + const size_t length = Stream_GetRemainingLength(s); - if (!rdp_read_header(license->rdp, s, &length, &channelId)) - return -1; - - if (!rdp_read_security_header(s, &securityFlags, &length)) - return -1; - - if (securityFlags & SEC_ENCRYPT) - { - if (!rdp_decrypt(license->rdp, s, &length, securityFlags)) - { - WLog_ERR(TAG, "rdp_decrypt failed"); - return -1; - } - } - - if (!(securityFlags & SEC_LICENSE_PKT)) - { - int status; - - if (!(securityFlags & SEC_ENCRYPT)) - Stream_Rewind(s, RDP_SECURITY_HEADER_LENGTH); - - status = rdp_recv_out_of_sequence_pdu(license->rdp, s); - if (status < 0) - { - WLog_ERR(TAG, "unexpected license packet."); - return status; - } - - return 0; - } + WINPR_ASSERT(license); if (!license_read_preamble(s, &bMsgType, &flags, &wMsgSize)) /* preamble (4 bytes) */ return -1; - DEBUG_LICENSE("Receiving %s Packet", LICENSE_MESSAGE_STRINGS[bMsgType & 0x1F]); + DEBUG_LICENSE("Receiving %s Packet", license_request_type_string(bMsgType)); switch (bMsgType) { case LICENSE_REQUEST: + /* Client does not require configuration, so skip this state */ + if (license_get_state(license) == LICENSE_STATE_INITIAL) + license_set_state(license, LICENSE_STATE_CONFIGURED); + + if (!license_ensure_state(license, LICENSE_STATE_CONFIGURED, bMsgType)) + return -1; + if (!license_read_license_request_packet(license, s)) return -1; if (!license_answer_license_request(license)) return -1; + + license_set_state(license, LICENSE_STATE_NEW_REQUEST); break; case PLATFORM_CHALLENGE: + if (!license_ensure_state(license, LICENSE_STATE_NEW_REQUEST, bMsgType)) + return -1; + if (!license_read_platform_challenge_packet(license, s)) return -1; - if (!license_send_platform_challenge_response_packet(license)) + if (!license_send_platform_challenge_response(license)) return -1; + license_set_state(license, LICENSE_STATE_PLATFORM_CHALLENGE_RESPONSE); break; case NEW_LICENSE: case UPGRADE_LICENSE: + if (!license_ensure_state(license, LICENSE_STATE_PLATFORM_CHALLENGE_RESPONSE, bMsgType)) + return -1; if (!license_read_new_or_upgrade_license_packet(license, s)) return -1; break; @@ -535,14 +895,115 @@ int license_recv(rdpLicense* license, wStream* s) return 0; } +int license_server_recv(rdpLicense* license, wStream* s) +{ + int rc = -1; + BYTE flags; + BYTE bMsgType; + UINT16 wMsgSize; + const size_t length = Stream_GetRemainingLength(s); + + WINPR_ASSERT(license); + + if (!license_read_preamble(s, &bMsgType, &flags, &wMsgSize)) /* preamble (4 bytes) */ + goto fail; + + DEBUG_LICENSE("Receiving %s Packet", license_request_type_string(bMsgType)); + + switch (bMsgType) + { + case NEW_LICENSE_REQUEST: + if (!license_ensure_state(license, LICENSE_STATE_REQUEST, bMsgType)) + goto fail; + if (!license_read_new_license_request_packet(license, s)) + goto fail; + // TODO: Validate if client is allowed + if (!license_send_error_alert(license, ERR_INVALID_MAC, ST_TOTAL_ABORT, + license->ErrorInfo)) + goto fail; + if (!license_send_platform_challenge_packet(license)) + goto fail; + license->update = FALSE; + if (!license_set_state(license, LICENSE_STATE_PLATFORM_CHALLENGE)) + goto fail; + break; + case LICENSE_INFO: + if (!license_ensure_state(license, LICENSE_STATE_REQUEST, bMsgType)) + goto fail; + if (!license_read_license_info(license, s)) + goto fail; + // TODO: Validate license info + if (!license_send_platform_challenge_packet(license)) + goto fail; + if (!license_set_state(license, LICENSE_STATE_PLATFORM_CHALLENGE)) + goto fail; + license->update = TRUE; + break; + + case PLATFORM_CHALLENGE_RESPONSE: + if (!license_ensure_state(license, LICENSE_STATE_PLATFORM_CHALLENGE, bMsgType)) + goto fail; + if (!license_read_client_platform_challenge_response(license, s)) + goto fail; + + // TODO: validate challenge response + if (FALSE) + { + if (license_send_error_alert(license, ERR_INVALID_CLIENT, ST_TOTAL_ABORT, + license->ErrorInfo)) + goto fail; + } + else + { + if (!license_server_send_new_or_upgrade_license(license, license->update)) + goto fail; + + license->type = LICENSE_TYPE_ISSUED; + license_set_state(license, LICENSE_STATE_COMPLETED); + + rc = 2; /* License issued, switch state */ + } + break; + + case ERROR_ALERT: + if (!license_read_error_alert_packet(license, s)) + goto fail; + break; + + default: + WLog_ERR(TAG, "invalid bMsgType:%" PRIu8 "", bMsgType); + goto fail; + } + + if (!tpkt_ensure_stream_consumed(s, length)) + goto fail; + + if (rc < 0) + rc = 0; + +fail: + if (rc < 0) + { + if (flags & EXTENDED_ERROR_MSG_SUPPORTED) + license_send_error_alert(license, ERR_INVALID_CLIENT, ST_TOTAL_ABORT, NULL); + license_set_state(license, LICENSE_STATE_ABORTED); + } + + return rc; +} + void license_generate_randoms(rdpLicense* license) { + WINPR_ASSERT(license); + #ifdef LICENSE_NULL_CLIENT_RANDOM ZeroMemory(license->ClientRandom, CLIENT_RANDOM_LENGTH); /* ClientRandom */ #else winpr_RAND(license->ClientRandom, CLIENT_RANDOM_LENGTH); /* ClientRandom */ #endif + winpr_RAND(license->ServerRandom, SERVER_RANDOM_LENGTH); /* ServerRandom */ + #ifdef LICENSE_NULL_PREMASTER_SECRET ZeroMemory(license->PremasterSecret, PREMASTER_SECRET_LENGTH); /* PremasterSecret */ #else @@ -559,6 +1020,8 @@ static BOOL license_generate_keys(rdpLicense* license) { BOOL ret; + WINPR_ASSERT(license); + if ( /* MasterSecret */ !security_master_secret(license->PremasterSecret, license->ClientRandom, @@ -605,6 +1068,10 @@ BOOL license_generate_hwid(rdpLicense* license) size_t targetLen; BYTE macAddress[6] = { 0 }; + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + WINPR_ASSERT(license->rdp->settings); + ZeroMemory(license->HardwareId, HWID_LENGTH); if (license->rdp->settings->OldLicenseBehaviour) @@ -618,7 +1085,7 @@ BOOL license_generate_hwid(rdpLicense* license) wStream* s; const char* hostname = license->rdp->settings->ClientHostname; s = Stream_StaticInit(&buffer, license->HardwareId, 4); - Stream_Write_UINT32(s, PLATFORMID); + Stream_Write_UINT32(s, license->PlatformId); Stream_Free(s, TRUE); hashTarget = (const BYTE*)hostname; @@ -641,7 +1108,14 @@ static BOOL license_get_server_rsa_public_key(rdpLicense* license) BYTE* Exponent; BYTE* Modulus; int ModulusLength; - rdpSettings* settings = license->rdp->settings; + rdpSettings* settings; + + WINPR_ASSERT(license); + WINPR_ASSERT(license->certificate); + WINPR_ASSERT(license->rdp); + + settings = license->rdp->settings; + WINPR_ASSERT(settings); if (license->ServerCertificate->length < 1) { @@ -666,9 +1140,13 @@ BOOL license_encrypt_premaster_secret(rdpLicense* license) { BYTE* EncryptedPremasterSecret; + WINPR_ASSERT(license); + if (!license_get_server_rsa_public_key(license)) return FALSE; + WINPR_ASSERT(license->EncryptedPremasterSecret); + #ifdef WITH_DEBUG_LICENSE WLog_DBG(TAG, "Modulus (%" PRIu32 " bits):", license->ModulusLength * 8); winpr_HexDump(TAG, WLOG_DEBUG, license->Modulus, license->ModulusLength); @@ -702,6 +1180,10 @@ static BOOL license_rc4_with_licenseKey(const rdpLicense* license, const BYTE* i WINPR_RC4_CTX* rc4; BYTE* buffer = NULL; + WINPR_ASSERT(license); + WINPR_ASSERT(input || (len == 0)); + WINPR_ASSERT(target); + rc4 = winpr_RC4_New_Allow_FIPS(license->LicensingEncryptionKey, LICENSING_ENCRYPTION_KEY_LENGTH); if (!rc4) @@ -738,6 +1220,7 @@ error_buffer: static BOOL license_encrypt_and_MAC(rdpLicense* license, const BYTE* input, size_t len, LICENSE_BLOB* target, BYTE* mac) { + WINPR_ASSERT(license); return license_rc4_with_licenseKey(license, input, len, target) && security_mac_data(license->MacSaltKey, input, len, mac); } @@ -755,7 +1238,13 @@ static BOOL license_encrypt_and_MAC(rdpLicense* license, const BYTE* input, size static BOOL license_decrypt_and_check_MAC(rdpLicense* license, const BYTE* input, size_t len, LICENSE_BLOB* target, const BYTE* packetMac) { - BYTE macData[16]; + BYTE macData[16] = { 0 }; + + WINPR_ASSERT(license); + WINPR_ASSERT(target); + + if (freerdp_settings_get_bool(license->rdp->settings, FreeRDP_TransportDumpReplay)) + return TRUE; return license_rc4_with_licenseKey(license, input, len, target) && security_mac_data(license->MacSaltKey, target->data, len, macData) && @@ -771,7 +1260,9 @@ static BOOL license_decrypt_and_check_MAC(rdpLicense* license, const BYTE* input BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo) { - if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + WINPR_ASSERT(productInfo); + + if (!license_check_stream_length(s, 8, "license product info::cbCompanyName")) return FALSE; Stream_Read_UINT32(s, productInfo->dwVersion); /* dwVersion (4 bytes) */ @@ -779,26 +1270,36 @@ BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo) /* Name must be >0, but there is no upper limit defined, use UINT32_MAX */ if ((productInfo->cbCompanyName < 2) || (productInfo->cbCompanyName % 2 != 0)) + { + WLog_WARN(TAG, "license product info invalid cbCompanyName %" PRIu32, + productInfo->cbCompanyName); return FALSE; + } - if (!Stream_CheckAndLogRequiredLength(TAG, s, productInfo->cbCompanyName)) + if (!license_check_stream_length(s, productInfo->cbCompanyName, + "license product info::CompanyName")) return FALSE; productInfo->pbProductId = NULL; productInfo->pbCompanyName = (BYTE*)malloc(productInfo->cbCompanyName); if (!productInfo->pbCompanyName) - return FALSE; + goto out_fail; Stream_Read(s, productInfo->pbCompanyName, productInfo->cbCompanyName); - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + if (!license_check_stream_length(s, 4, "license product info::cbProductId")) goto out_fail; Stream_Read_UINT32(s, productInfo->cbProductId); /* cbProductId (4 bytes) */ if ((productInfo->cbProductId < 2) || (productInfo->cbProductId % 2 != 0)) + { + WLog_WARN(TAG, "license product info invalid cbProductId %" PRIu32, + productInfo->cbProductId); goto out_fail; + } - if (!Stream_CheckAndLogRequiredLength(TAG, s, productInfo->cbProductId)) + if (!license_check_stream_length(s, productInfo->cbProductId, + "license product info::ProductId")) goto out_fail; productInfo->pbProductId = (BYTE*)malloc(productInfo->cbProductId); @@ -815,23 +1316,64 @@ out_fail: return FALSE; } +static BOOL license_write_product_info(wStream* s, const LICENSE_PRODUCT_INFO* productInfo) +{ + WINPR_ASSERT(productInfo); + + if (!license_check_stream_capacity(s, 8, "license product info::cbCompanyName")) + return FALSE; + + Stream_Write_UINT32(s, productInfo->dwVersion); /* dwVersion (4 bytes) */ + Stream_Write_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) || + !productInfo->pbCompanyName) + { + WLog_WARN(TAG, "license product info invalid cbCompanyName %" PRIu32, + productInfo->cbCompanyName); + return FALSE; + } + + if (!license_check_stream_capacity(s, productInfo->cbCompanyName, + "license product info::CompanyName")) + return FALSE; + + Stream_Write(s, productInfo->pbCompanyName, productInfo->cbCompanyName); + + if (!license_check_stream_capacity(s, 4, "license product info::cbProductId")) + return FALSE; + + Stream_Write_UINT32(s, productInfo->cbProductId); /* cbProductId (4 bytes) */ + + if ((productInfo->cbProductId < 2) || (productInfo->cbProductId % 2 != 0) || + !productInfo->pbProductId) + { + WLog_WARN(TAG, "license product info invalid cbProductId %" PRIu32, + productInfo->cbProductId); + return FALSE; + } + + if (!license_check_stream_capacity(s, productInfo->cbProductId, + "license product info::ProductId")) + return FALSE; + + Stream_Write(s, productInfo->pbProductId, productInfo->cbProductId); + return TRUE; +} + /** * Allocate New Product Information (LICENSE_PRODUCT_INFO).\n * @msdn{cc241915} * @return new product information */ -LICENSE_PRODUCT_INFO* license_new_product_info() +LICENSE_PRODUCT_INFO* license_new_product_info(void) { - LICENSE_PRODUCT_INFO* productInfo; - productInfo = (LICENSE_PRODUCT_INFO*)malloc(sizeof(LICENSE_PRODUCT_INFO)); + LICENSE_PRODUCT_INFO* productInfo = + (LICENSE_PRODUCT_INFO*)calloc(1, 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; } @@ -851,6 +1393,42 @@ void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo) } } +BOOL license_read_binary_blob_data(LICENSE_BLOB* blob, UINT32 wBlobType, const void* data, + size_t length) +{ + WINPR_ASSERT(blob); + WINPR_ASSERT(length <= UINT16_MAX); + WINPR_ASSERT(data || (length == 0)); + + blob->length = (UINT16)length; + free(blob->data); + blob->data = NULL; + + if ((blob->type != wBlobType) && (blob->type != BB_ANY_BLOB)) + { + WLog_ERR(TAG, "license binary blob::type expected %s, got %s", + licencse_blob_type_string(wBlobType), licencse_blob_type_string(blob->type)); + } + + /* + * 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)) + { + WLog_WARN(TAG, "license binary blob::type %s, length=0, skipping.", + licencse_blob_type_string(blob->type)); + return TRUE; + } + + blob->type = wBlobType; + blob->data = (BYTE*)malloc(blob->length); + if (!blob->data) + return FALSE; + memcpy(blob->data, data, blob->length); /* blobData */ + return TRUE; +} + /** * Read License Binary Blob (LICENSE_BINARY_BLOB).\n * @msdn{cc240481} @@ -861,37 +1439,23 @@ void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo) BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob) { UINT16 wBlobType; + UINT16 length; - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + WINPR_ASSERT(blob); + + if (!license_check_stream_length(s, 4, "license binary blob::type")) return FALSE; - Stream_Read_UINT16(s, wBlobType); /* wBlobType (2 bytes) */ - Stream_Read_UINT16(s, blob->length); /* wBlobLen (2 bytes) */ + Stream_Read_UINT16(s, wBlobType); /* wBlobType (2 bytes) */ + Stream_Read_UINT16(s, length); /* wBlobLen (2 bytes) */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, blob->length)) + if (!license_check_stream_length(s, length, "license binary 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) + if (!license_read_binary_blob_data(blob, wBlobType, Stream_Pointer(s), length)) return FALSE; - Stream_Read(s, blob->data, blob->length); /* blobData */ - return TRUE; + + return Stream_SafeSeek(s, length); } /** @@ -903,6 +1467,8 @@ BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob) BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob) { + WINPR_ASSERT(blob); + if (!Stream_EnsureRemainingCapacity(s, blob->length + 4)) return FALSE; @@ -917,8 +1483,9 @@ BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob) static BOOL license_write_encrypted_premaster_secret_blob(wStream* s, const LICENSE_BLOB* blob, UINT32 ModulusLength) { - UINT32 length; - length = ModulusLength + 8; + const UINT32 length = ModulusLength + 8; + + WINPR_ASSERT(blob); if (blob->length > ModulusLength) { @@ -938,6 +1505,15 @@ static BOOL license_write_encrypted_premaster_secret_blob(wStream* s, const LICE return TRUE; } +static BOOL license_read_encrypted_premaster_secret_blob(wStream* s, LICENSE_BLOB* blob, + UINT32* ModulusLength) +{ + if (!license_read_binary_blob(s, blob)) + return FALSE; + // TODO + return TRUE; +} + /** * Allocate New License Binary Blob (LICENSE_BINARY_BLOB).\n * @msdn{cc240481} @@ -946,8 +1522,7 @@ static BOOL license_write_encrypted_premaster_secret_blob(wStream* s, const LICE LICENSE_BLOB* license_new_binary_blob(UINT16 type) { - LICENSE_BLOB* blob; - blob = (LICENSE_BLOB*)calloc(1, sizeof(LICENSE_BLOB)); + LICENSE_BLOB* blob = (LICENSE_BLOB*)calloc(1, sizeof(LICENSE_BLOB)); if (blob) blob->type = type; return blob; @@ -980,26 +1555,49 @@ BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList) UINT32 i; UINT32 scopeCount; - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + WINPR_ASSERT(scopeList); + + if (!license_check_stream_length(s, 4, "license scope list")) return FALSE; Stream_Read_UINT32(s, scopeCount); /* ScopeCount (4 bytes) */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, - 4ull * scopeCount)) /* every blob is at least 4 bytes */ + if (!license_check_stream_length(s, scopeCount * 4ull, "license scope list::count")) return FALSE; - scopeList->count = scopeCount; - scopeList->array = (LICENSE_BLOB*)calloc(scopeCount, sizeof(LICENSE_BLOB)); - if (!scopeList->array) + if (!license_scope_list_resize(scopeList, scopeCount)) 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; + } - if (!license_read_binary_blob(s, &scopeList->array[i])) + return TRUE; +} + +BOOL license_write_scope_list(wStream* s, const SCOPE_LIST* scopeList) +{ + UINT32 i; + + WINPR_ASSERT(scopeList); + + if (!license_check_stream_capacity(s, 4, "license scope list")) + return FALSE; + + Stream_Write_UINT32(s, scopeList->count); /* ScopeCount (4 bytes) */ + + if (!license_check_stream_capacity(s, scopeList->count * 4ull, "license scope list::count")) + return FALSE; + + /* ScopeArray */ + WINPR_ASSERT(scopeList->array || (scopeList->count == 0)); + for (i = 0; i < scopeList->count; i++) + { + const LICENSE_BLOB* element = scopeList->array[i]; + + if (!license_write_binary_blob(s, element)) return FALSE; } @@ -1012,9 +1610,51 @@ BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList) * @return new scope list */ -SCOPE_LIST* license_new_scope_list() +SCOPE_LIST* license_new_scope_list(void) { - return (SCOPE_LIST*)calloc(1, sizeof(SCOPE_LIST)); + SCOPE_LIST* list = calloc(1, sizeof(SCOPE_LIST)); + return list; +} + +BOOL license_scope_list_resize(SCOPE_LIST* scopeList, UINT32 count) +{ + UINT32 x; + + WINPR_ASSERT(scopeList); + WINPR_ASSERT(scopeList->array || (scopeList->count == 0)); + + for (x = count; x < scopeList->count; x++) + { + license_free_binary_blob(scopeList->array[x]); + scopeList->array[x] = NULL; + } + + if (count > 0) + { + LICENSE_BLOB** tmp = realloc(scopeList->array, count * sizeof(LICENSE_BLOB*)); + if (!tmp) + return FALSE; + scopeList->array = tmp; + } + else + { + free(scopeList->array); + scopeList->array = NULL; + } + + for (x = scopeList->count; x < count; x++) + { + LICENSE_BLOB* blob = license_new_binary_blob(BB_SCOPE_BLOB); + if (!blob) + { + scopeList->count = x; + return FALSE; + } + scopeList->array[x] = blob; + } + + scopeList->count = count; + return TRUE; } /** @@ -1025,41 +1665,29 @@ SCOPE_LIST* license_new_scope_list() 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); + license_scope_list_resize(scopeList, 0); free(scopeList); } -BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob, BYTE* signature) +BOOL license_send_license_info(rdpLicense* license, const LICENSE_BLOB* calBlob, BYTE* signature) { - wStream* s; + wStream* s = license_send_stream_init(license); - /* Client License Information: */ - UINT32 PlatformId = PLATFORMID; - UINT32 PreferredKeyExchangeAlg = KEY_EXCHANGE_ALG_RSA; + WINPR_ASSERT(calBlob); + WINPR_ASSERT(signature); - 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) */ + if (!license_check_stream_capacity(s, 8 + CLIENT_RANDOM_LENGTH, "license info::ClientRandom")) + return FALSE; + + Stream_Write_UINT32(s, + license->PreferredKeyExchangeAlg); /* PreferredKeyExchangeAlg (4 bytes) */ + Stream_Write_UINT32(s, license->PlatformId); /* PlatformId (4 bytes) */ /* ClientRandom (32 bytes) */ Stream_Write(s, license->ClientRandom, CLIENT_RANDOM_LENGTH); @@ -1078,6 +1706,8 @@ BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob, goto error; /* MACData */ + if (!license_check_stream_capacity(s, LICENSING_ENCRYPTION_KEY_LENGTH, "license info::MACData")) + goto error; Stream_Write(s, signature, LICENSING_ENCRYPTION_KEY_LENGTH); return license_send(license, s, LICENSE_INFO); @@ -1087,6 +1717,65 @@ error: return FALSE; } +static BOOL license_check_preferred_alg(rdpLicense* license, UINT32 PreferredKeyExchangeAlg, + const char* where) +{ + WINPR_ASSERT(license); + WINPR_ASSERT(where); + + if (license->PreferredKeyExchangeAlg != PreferredKeyExchangeAlg) + { + WLog_WARN(TAG, "%s::PreferredKeyExchangeAlg, expected %s, got %s", where, + license_preferred_key_exchange_alg_string(license->PreferredKeyExchangeAlg), + license_preferred_key_exchange_alg_string(PreferredKeyExchangeAlg)); + return FALSE; + } + return TRUE; +} + +BOOL license_read_license_info(rdpLicense* license, wStream* s) +{ + BOOL rc = FALSE; + UINT32 PreferredKeyExchangeAlg; + + WINPR_ASSERT(license); + + /* ClientRandom (32 bytes) */ + if (!license_check_stream_length(s, 8 + CLIENT_RANDOM_LENGTH, "license info")) + goto error; + + Stream_Read_UINT32(s, PreferredKeyExchangeAlg); /* PreferredKeyExchangeAlg (4 bytes) */ + if (!license_check_preferred_alg(license, PreferredKeyExchangeAlg, "license info")) + goto error; + Stream_Read_UINT32(s, license->PlatformId); /* PlatformId (4 bytes) */ + + /* ClientRandom (32 bytes) */ + Stream_Read(s, license->ClientRandom, CLIENT_RANDOM_LENGTH); + + /* Licensing Binary Blob with EncryptedPreMasterSecret: */ + if (!license_read_encrypted_premaster_secret_blob(s, license->EncryptedPremasterSecret, + &license->ModulusLength)) + goto error; + + /* Licensing Binary Blob with LicenseInfo: */ + if (!license_read_binary_blob(s, license->LicenseInfo)) + goto error; + + /* Licensing Binary Blob with EncryptedHWID */ + if (!license_read_binary_blob(s, license->EncryptedHardwareId)) + goto error; + + /* MACData */ + if (!license_check_stream_length(s, LICENSING_ENCRYPTION_KEY_LENGTH, "license info::MACData")) + goto error; + Stream_Read(s, license->MACData, LICENSING_ENCRYPTION_KEY_LENGTH); + + rc = TRUE; + +error: + return rc; +} + /** * Read a LICENSE_REQUEST packet.\n * @msdn{cc241914} @@ -1096,11 +1785,13 @@ error: BOOL license_read_license_request_packet(rdpLicense* license, wStream* s) { + WINPR_ASSERT(license); + /* ServerRandom (32 bytes) */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, 32)) + if (!license_check_stream_length(s, SERVER_RANDOM_LENGTH, "license request")) return FALSE; - Stream_Read(s, license->ServerRandom, 32); + Stream_Read(s, license->ServerRandom, SERVER_RANDOM_LENGTH); /* ProductInfo */ if (!license_read_product_info(s, license->ProductInfo)) @@ -1129,13 +1820,57 @@ BOOL license_read_license_request_packet(rdpLicense* license, wStream* s) #ifdef WITH_DEBUG_LICENSE WLog_DBG(TAG, "ServerRandom:"); - winpr_HexDump(TAG, WLOG_DEBUG, license->ServerRandom, 32); + winpr_HexDump(TAG, WLOG_DEBUG, license->ServerRandom, SERVER_RANDOM_LENGTH); license_print_product_info(license->ProductInfo); license_print_scope_list(license->ScopeList); #endif return TRUE; } +BOOL license_write_license_request_packet(const rdpLicense* license, wStream* s) +{ + WINPR_ASSERT(license); + + /* ServerRandom (32 bytes) */ + if (!license_check_stream_capacity(s, SERVER_RANDOM_LENGTH, "license request")) + return FALSE; + Stream_Write(s, license->ServerRandom, SERVER_RANDOM_LENGTH); + + /* ProductInfo */ + if (!license_write_product_info(s, license->ProductInfo)) + return FALSE; + + /* KeyExchangeList */ + if (!license_write_binary_blob(s, license->KeyExchangeList)) + return FALSE; + + /* ServerCertificate */ + if (!license_write_binary_blob(s, license->ServerCertificate)) + return FALSE; + + /* ScopeList */ + if (!license_write_scope_list(s, license->ScopeList)) + return FALSE; + + return TRUE; +} + +static BOOL license_send_license_request_packet(rdpLicense* license) +{ + wStream* s = license_send_stream_init(license); + if (!s) + return FALSE; + + if (!license_write_license_request_packet(license, s)) + goto fail; + + return license_send(license, s, LICENSE_REQUEST); + +fail: + Stream_Release(s); + return FALSE; +} + /* * Read a PLATFORM_CHALLENGE packet.\n * @msdn{cc241921} @@ -1145,12 +1880,14 @@ BOOL license_read_license_request_packet(rdpLicense* license, wStream* s) BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s) { - BYTE macData[16]; + BYTE macData[16] = { 0 }; UINT32 ConnectFlags = 0; + WINPR_ASSERT(license); + DEBUG_LICENSE("Receiving Platform Challenge Packet"); - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + if (!license_check_stream_length(s, 4, "license platform challenge")) return FALSE; Stream_Read_UINT32(s, ConnectFlags); /* ConnectFlags, Reserved (4 bytes) */ @@ -1162,7 +1899,8 @@ BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s) license->EncryptedPlatformChallenge->type = BB_ENCRYPTED_DATA_BLOB; /* MACData (16 bytes) */ - if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + if (!license_check_stream_length(s, LICENSING_ENCRYPTION_KEY_LENGTH, + "license platform challenge::MAC")) return FALSE; Stream_Read(s, macData, 16); @@ -1180,34 +1918,94 @@ BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s) winpr_HexDump(TAG, WLOG_DEBUG, license->PlatformChallenge->data, license->PlatformChallenge->length); WLog_DBG(TAG, "MacData:"); - winpr_HexDump(TAG, WLOG_DEBUG, macData, 16); + winpr_HexDump(TAG, WLOG_DEBUG, macData, LICENSING_ENCRYPTION_KEY_LENGTH); #endif return TRUE; } +BOOL license_send_error_alert(rdpLicense* license, UINT32 dwErrorCode, UINT32 dwStateTransition, + const LICENSE_BLOB* info) +{ + wStream* s = license_send_stream_init(license); + + if (!s) + goto fail; + + if (!license_check_stream_capacity(s, 8, "license error alert")) + goto fail; + Stream_Write_UINT32(s, dwErrorCode); + Stream_Write_UINT32(s, dwStateTransition); + + if (!license_write_binary_blob(s, info)) + goto fail; + + return license_send(license, s, ERROR_ALERT); +fail: + Stream_Release(s); + return FALSE; +} + +BOOL license_send_platform_challenge_packet(rdpLicense* license) +{ + wStream* s = license_send_stream_init(license); + + if (!s) + goto fail; + + DEBUG_LICENSE("Receiving Platform Challenge Packet"); + + if (!license_check_stream_capacity(s, 4, "license platform challenge")) + goto fail; + + Stream_Zero(s, 4); /* ConnectFlags, Reserved (4 bytes) */ + + /* EncryptedPlatformChallenge */ + if (!license_write_binary_blob(s, license->EncryptedPlatformChallenge)) + goto fail; + + /* MACData (16 bytes) */ + if (!license_check_stream_length(s, LICENSING_ENCRYPTION_KEY_LENGTH, + "license platform challenge::MAC")) + goto fail; + + Stream_Write(s, license->MACData, LICENSING_ENCRYPTION_KEY_LENGTH); + + return license_send(license, s, PLATFORM_CHALLENGE); +fail: + Stream_Release(s); + return FALSE; +} + static BOOL license_read_encrypted_blob(const rdpLicense* license, wStream* s, LICENSE_BLOB* target) { UINT16 wBlobType, wBlobLen; BYTE* encryptedData; - if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + WINPR_ASSERT(license); + WINPR_ASSERT(target); + + if (!license_check_stream_length(s, 4, "license encrypted blob")) return FALSE; Stream_Read_UINT16(s, wBlobType); if (wBlobType != BB_ENCRYPTED_DATA_BLOB) { - WLog_DBG( + WLog_WARN( TAG, "expecting BB_ENCRYPTED_DATA_BLOB blob, probably a windows 2003 server, continuing..."); } Stream_Read_UINT16(s, wBlobLen); - if (!Stream_CheckAndLogRequiredLength(TAG, s, wBlobLen)) - return FALSE; - encryptedData = Stream_Pointer(s); - Stream_Seek(s, wBlobLen); + if (!Stream_SafeSeek(s, wBlobLen)) + { + WLog_WARN(TAG, + "short license encrypted blob::length, expected %" PRIu16 " bytes, got %" PRIuz, + wBlobLen, Stream_GetRemainingLength(s)); + return FALSE; + } + return license_rc4_with_licenseKey(license, encryptedData, wBlobLen, target); } @@ -1223,10 +2021,14 @@ BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s) UINT32 os_major; UINT32 os_minor; UINT32 cbScope, cbCompanyName, cbProductId, cbLicenseInfo; + wStream sbuffer; wStream* licenseStream = NULL; BOOL ret = FALSE; - BYTE computedMac[16]; + BYTE computedMac[16] = { 0 }; LICENSE_BLOB* calBlob; + const BYTE* readMac; + + WINPR_ASSERT(license); DEBUG_LICENSE("Receiving Server New/Upgrade License Packet"); @@ -1236,38 +2038,41 @@ BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s) /* EncryptedLicenseInfo */ if (!license_read_encrypted_blob(license, s, calBlob)) - goto out_free_blob; + goto fail; /* compute MAC and check it */ - if (!Stream_CheckAndLogRequiredLength(TAG, 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) + readMac = Stream_Pointer(s); + if (!Stream_SafeSeek(s, 16)) { - WLog_ERR(TAG, "new or upgrade license MAC mismatch"); - goto out_free_blob; + WLog_WARN(TAG, "short license new/upgrade, expected 16 bytes, got %" PRIuz, + Stream_GetRemainingLength(s)); + goto fail; } - if (!Stream_SafeSeek(s, 16)) - goto out_free_blob; + if (!security_mac_data(license->MacSaltKey, calBlob->data, calBlob->length, computedMac)) + goto fail; - licenseStream = Stream_New(calBlob->data, calBlob->length); + if (memcmp(computedMac, readMac, sizeof(computedMac)) != 0) + { + WLog_ERR(TAG, "new or upgrade license MAC mismatch"); + goto fail; + } + + licenseStream = Stream_StaticConstInit(&sbuffer, calBlob->data, calBlob->length); if (!licenseStream) - goto out_free_blob; + goto fail; - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, 8)) - goto out_free_stream; + if (!license_check_stream_length(s, 8, "license new/upgrade::blob::version")) + goto fail; Stream_Read_UINT16(licenseStream, os_minor); Stream_Read_UINT16(licenseStream, os_major); /* Scope */ Stream_Read_UINT32(licenseStream, cbScope); - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, cbScope)) - goto out_free_stream; + if (!license_check_stream_length(s, cbScope, "license new/upgrade::blob::scope")) + goto fail; + #ifdef WITH_DEBUG_LICENSE WLog_DBG(TAG, "Scope:"); winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(licenseStream), cbScope); @@ -1275,11 +2080,13 @@ BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s) Stream_Seek(licenseStream, cbScope); /* CompanyName */ - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, 4)) - goto out_free_stream; + if (!license_check_stream_length(s, 4, "license new/upgrade::blob::cbCompanyName")) + goto fail; + Stream_Read_UINT32(licenseStream, cbCompanyName); - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, cbCompanyName)) - goto out_free_stream; + if (!license_check_stream_length(s, cbCompanyName, "license new/upgrade::blob::CompanyName")) + goto fail; + #ifdef WITH_DEBUG_LICENSE WLog_DBG(TAG, "Company name:"); winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(licenseStream), cbCompanyName); @@ -1287,11 +2094,14 @@ BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s) Stream_Seek(licenseStream, cbCompanyName); /* productId */ - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, 4)) - goto out_free_stream; + if (!license_check_stream_length(s, 4, "license new/upgrade::blob::cbProductId")) + goto fail; + Stream_Read_UINT32(licenseStream, cbProductId); - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, cbProductId)) - goto out_free_stream; + + if (!license_check_stream_length(s, cbProductId, "license new/upgrade::blob::ProductId")) + goto fail; + #ifdef WITH_DEBUG_LICENSE WLog_DBG(TAG, "Product id:"); winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(licenseStream), cbProductId); @@ -1299,22 +2109,22 @@ BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s) Stream_Seek(licenseStream, cbProductId); /* licenseInfo */ - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, 4)) - goto out_free_stream; - Stream_Read_UINT32(licenseStream, cbLicenseInfo); - if (!Stream_CheckAndLogRequiredLength(TAG, licenseStream, cbLicenseInfo)) - goto out_free_stream; + if (!license_check_stream_length(s, 4, "license new/upgrade::blob::cbLicenseInfo")) + goto fail; - license->state = LICENSE_STATE_COMPLETED; + Stream_Read_UINT32(licenseStream, cbLicenseInfo); + if (!license_check_stream_length(s, cbLicenseInfo, "license new/upgrade::blob::LicenseInfo")) + goto fail; + + license->type = LICENSE_TYPE_ISSUED; + ret = license_set_state(license, 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: +fail: license_free_binary_blob(calBlob); return ret; } @@ -1331,7 +2141,10 @@ BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s) UINT32 dwErrorCode; UINT32 dwStateTransition; - if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + + if (!license_check_stream_length(s, 8ul, "error alert")) return FALSE; Stream_Read_UINT32(s, dwErrorCode); /* dwErrorCode (4 bytes) */ @@ -1347,20 +2160,20 @@ BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s) if (dwErrorCode == STATUS_VALID_CLIENT) { - license->state = LICENSE_STATE_COMPLETED; - return TRUE; + license->type = LICENSE_TYPE_NONE; + return license_set_state(license, LICENSE_STATE_COMPLETED); } switch (dwStateTransition) { case ST_TOTAL_ABORT: - license->state = LICENSE_STATE_ABORTED; + license_set_state(license, LICENSE_STATE_ABORTED); break; case ST_NO_TRANSITION: - license->state = LICENSE_STATE_COMPLETED; + license_set_state(license, LICENSE_STATE_COMPLETED); break; case ST_RESET_PHASE_TO_START: - license->state = LICENSE_STATE_AWAIT; + license_set_state(license, LICENSE_STATE_CONFIGURED); break; case ST_RESEND_LAST_MESSAGE: break; @@ -1378,14 +2191,17 @@ BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s) * @param s stream */ -BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s) +BOOL license_write_new_license_request_packet(const rdpLicense* license, wStream* s) { - UINT32 PlatformId = PLATFORMID; - UINT32 PreferredKeyExchangeAlg = KEY_EXCHANGE_ALG_RSA; + WINPR_ASSERT(license); - 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 (!license_check_stream_capacity(s, 8 + CLIENT_RANDOM_LENGTH, "License Request")) + return FALSE; + + Stream_Write_UINT32(s, + license->PreferredKeyExchangeAlg); /* PreferredKeyExchangeAlg (4 bytes) */ + Stream_Write_UINT32(s, license->PlatformId); /* PlatformId (4 bytes) */ + Stream_Write(s, license->ClientRandom, CLIENT_RANDOM_LENGTH); /* ClientRandom (32 bytes) */ if (/* EncryptedPremasterSecret */ !license_write_encrypted_premaster_secret_blob(s, license->EncryptedPremasterSecret, @@ -1399,9 +2215,9 @@ BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s) } #ifdef WITH_DEBUG_LICENSE - WLog_DBG(TAG, "PreferredKeyExchangeAlg: 0x%08" PRIX32 "", PreferredKeyExchangeAlg); + WLog_DBG(TAG, "PreferredKeyExchangeAlg: 0x%08" PRIX32 "", license->PreferredKeyExchangeAlg); WLog_DBG(TAG, "ClientRandom:"); - winpr_HexDump(TAG, WLOG_DEBUG, license->ClientRandom, 32); + winpr_HexDump(TAG, WLOG_DEBUG, license->ClientRandom, CLIENT_RANDOM_LENGTH); WLog_DBG(TAG, "EncryptedPremasterSecret"); winpr_HexDump(TAG, WLOG_DEBUG, license->EncryptedPremasterSecret->data, license->EncryptedPremasterSecret->length); @@ -1413,6 +2229,37 @@ BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s) return TRUE; } +BOOL license_read_new_license_request_packet(rdpLicense* license, wStream* s) +{ + UINT32 PreferredKeyExchangeAlg; + + WINPR_ASSERT(license); + + if (!license_check_stream_length(s, 8ull + CLIENT_RANDOM_LENGTH, "new license request")) + return FALSE; + + Stream_Read_UINT32(s, PreferredKeyExchangeAlg); /* PreferredKeyExchangeAlg (4 bytes) */ + if (!license_check_preferred_alg(license, PreferredKeyExchangeAlg, "new license request")) + return FALSE; + + Stream_Read_UINT32(s, license->PlatformId); /* PlatformId (4 bytes) */ + Stream_Read(s, license->ClientRandom, CLIENT_RANDOM_LENGTH); /* ClientRandom (32 bytes) */ + + /* EncryptedPremasterSecret */ + if (!license_read_encrypted_premaster_secret_blob(s, license->EncryptedPremasterSecret, + &license->ModulusLength)) + return FALSE; + + /* ClientUserName */ + if (!license_read_binary_blob(s, license->ClientUserName)) + return FALSE; + /* ClientMachineName */ + if (!license_read_binary_blob(s, license->ClientMachineName)) + return FALSE; + + return TRUE; +} + /** * Send a NEW_LICENSE_REQUEST packet.\n * @msdn{cc241918} @@ -1427,6 +2274,10 @@ BOOL license_answer_license_request(rdpLicense* license) BOOL status; char* username; + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + WINPR_ASSERT(license->rdp->settings); + if (!license->rdp->settings->OldLicenseBehaviour) license_data = loadCalFile(license->rdp->settings, license->rdp->settings->ClientHostname, &license_size); @@ -1434,10 +2285,11 @@ BOOL license_answer_license_request(rdpLicense* license) if (license_data) { LICENSE_BLOB* calBlob = NULL; - BYTE signature[LICENSING_ENCRYPTION_KEY_LENGTH]; + BYTE signature[LICENSING_ENCRYPTION_KEY_LENGTH] = { 0 }; DEBUG_LICENSE("Sending Saved License Packet"); + WINPR_ASSERT(license->EncryptedHardwareId); license->EncryptedHardwareId->type = BB_ENCRYPTED_DATA_BLOB; if (!license_encrypt_and_MAC(license, license->HardwareId, HWID_LENGTH, license->EncryptedHardwareId, signature)) @@ -1455,7 +2307,7 @@ BOOL license_answer_license_request(rdpLicense* license) calBlob->data = license_data; calBlob->length = license_size; - status = license_send_client_info(license, calBlob, signature); + status = license_send_license_info(license, calBlob, signature); license_free_binary_blob(calBlob); return status; @@ -1471,15 +2323,21 @@ BOOL license_answer_license_request(rdpLicense* license) else username = "username"; + WINPR_ASSERT(license->ClientUserName); license->ClientUserName->data = (BYTE*)username; license->ClientUserName->length = strlen(username) + 1; + + WINPR_ASSERT(license->ClientMachineName); 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); + WINPR_ASSERT(license->ClientUserName); license->ClientUserName->data = NULL; license->ClientUserName->length = 0; + + WINPR_ASSERT(license->ClientMachineName); license->ClientMachineName->data = NULL; license->ClientMachineName->length = 0; @@ -1492,56 +2350,37 @@ BOOL license_answer_license_request(rdpLicense* license) return license_send(license, s, NEW_LICENSE_REQUEST); } -/** - * Write Client Challenge Response Packet.\n - * @msdn{cc241922} - * @param license license module - * @param s stream - * @param mac_data signature - */ - -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; -} - /** * Send Client Challenge Response Packet.\n * @msdn{cc241922} * @param license license module */ -BOOL license_send_platform_challenge_response_packet(rdpLicense* license) +BOOL license_send_platform_challenge_response(rdpLicense* license) { - wStream* s; + wStream* s = license_send_stream_init(license); wStream* challengeRespData; int length; BYTE* buffer; - BYTE mac_data[16]; BOOL status; + WINPR_ASSERT(license); + WINPR_ASSERT(license->PlatformChallenge); + WINPR_ASSERT(license->MacSaltKey); + WINPR_ASSERT(license->EncryptedPlatformChallenge); + WINPR_ASSERT(license->EncryptedHardwareId); + 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, PLATFORM_CHALLENGE_RESPONSE_VERSION); /* wVersion */ + Stream_Write_UINT16(challengeRespData, license->ClientType); /* wClientType */ + Stream_Write_UINT16(challengeRespData, license->LicenseDetailLevel); /* wLicenseDetailLevel */ Stream_Write_UINT16(challengeRespData, license->PlatformChallenge->length); /* cbChallenge */ Stream_Write(challengeRespData, license->PlatformChallenge->data, license->PlatformChallenge->length); /* pbChallenge */ @@ -1558,7 +2397,7 @@ BOOL license_send_platform_challenge_response_packet(rdpLicense* license) 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); + status = security_mac_data(license->MacSaltKey, buffer, length, license->MACData); free(buffer); if (!status) @@ -1590,13 +2429,84 @@ BOOL license_send_platform_challenge_response_packet(rdpLicense* license) 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)) + if (license_write_client_platform_challenge_response(license, s)) return license_send(license, s, PLATFORM_CHALLENGE_RESPONSE); Stream_Release(s); return FALSE; } +BOOL license_read_platform_challenge_response(rdpLicense* license, wStream* s) +{ + UINT16 wVersion; + UINT16 cbChallenge; + const BYTE* pbChallenge; + + WINPR_ASSERT(license); + WINPR_ASSERT(license->PlatformChallenge); + WINPR_ASSERT(license->MacSaltKey); + WINPR_ASSERT(license->EncryptedPlatformChallenge); + WINPR_ASSERT(license->EncryptedHardwareId); + + DEBUG_LICENSE("Receiving Platform Challenge Response Packet"); + + if (!license_check_stream_length(s, 8, "PLATFORM_CHALLENGE_RESPONSE_DATA")) + return FALSE; + + Stream_Read_UINT16(s, wVersion); + if (wVersion != PLATFORM_CHALLENGE_RESPONSE_VERSION) + { + WLog_WARN(TAG, + "Invalid PLATFORM_CHALLENGE_RESPONSE_DATA::wVersion 0x%04" PRIx16 + ", expected 0x04" PRIx16, + wVersion, PLATFORM_CHALLENGE_RESPONSE_VERSION); + return FALSE; + } + Stream_Read_UINT16(s, license->ClientType); + Stream_Read_UINT16(s, license->LicenseDetailLevel); + Stream_Read_UINT16(s, cbChallenge); + + if (!license_check_stream_length(s, cbChallenge, + "PLATFORM_CHALLENGE_RESPONSE_DATA::pbChallenge")) + return FALSE; + + pbChallenge = Stream_Pointer(s); + if (!license_read_binary_blob_data(license->EncryptedPlatformChallengeResponse, BB_DATA_BLOB, + pbChallenge, cbChallenge)) + return FALSE; + return Stream_SafeSeek(s, cbChallenge); +} + +BOOL license_write_client_platform_challenge_response(rdpLicense* license, wStream* s) +{ + WINPR_ASSERT(license); + + if (!license_write_binary_blob(s, license->EncryptedPlatformChallengeResponse)) + return FALSE; + if (!license_write_binary_blob(s, license->EncryptedHardwareId)) + return FALSE; + if (!license_check_stream_capacity(s, sizeof(license->MACData), + "CLIENT_PLATFORM_CHALLENGE_RESPONSE::MACData")) + return FALSE; + Stream_Write(s, license->MACData, sizeof(license->MACData)); + return TRUE; +} + +BOOL license_read_client_platform_challenge_response(rdpLicense* license, wStream* s) +{ + WINPR_ASSERT(license); + + if (!license_read_binary_blob(s, license->EncryptedPlatformChallengeResponse)) + return FALSE; + if (!license_read_binary_blob(s, license->EncryptedHardwareId)) + return FALSE; + if (!license_check_stream_length(s, sizeof(license->MACData), + "CLIENT_PLATFORM_CHALLENGE_RESPONSE::MACData")) + return FALSE; + Stream_Read(s, license->MACData, sizeof(license->MACData)); + return TRUE; +} + /** * Send Server License Error - Valid Client Packet.\n * @msdn{cc241922} @@ -1605,20 +2515,14 @@ BOOL license_send_platform_challenge_response_packet(rdpLicense* license) BOOL license_send_valid_client_error_packet(rdpRdp* rdp) { + WINPR_ASSERT(rdp); rdpLicense* license = rdp->license; - wStream* s = license_send_stream_init(license); - if (!s) - return FALSE; + WINPR_ASSERT(license); - 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; + license->state = LICENSE_STATE_COMPLETED; + license->type = LICENSE_TYPE_NONE; + return license_send_error_alert(license, STATUS_VALID_CLIENT, ST_NO_TRANSITION, + license->ErrorInfo); } /** @@ -1630,18 +2534,27 @@ BOOL license_send_valid_client_error_packet(rdpRdp* rdp) rdpLicense* license_new(rdpRdp* rdp) { rdpLicense* license; + WINPR_ASSERT(rdp); + license = (rdpLicense*)calloc(1, sizeof(rdpLicense)); if (!license) return NULL; + license->PlatformId = PLATFORMID; + license->ClientType = OTHER_PLATFORM_CHALLENGE_TYPE; + license->LicenseDetailLevel = LICENSE_DETAIL_DETAIL; + license->PreferredKeyExchangeAlg = KEY_EXCHANGE_ALG_RSA; license->rdp = rdp; - license->state = LICENSE_STATE_AWAIT; + + license_set_state(license, LICENSE_STATE_INITIAL); 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->LicenseInfo = license_new_binary_blob(BB_DATA_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))) @@ -1661,6 +2574,8 @@ rdpLicense* license_new(rdpRdp* rdp) goto out_error; if (!(license->EncryptedHardwareId = license_new_binary_blob(BB_ENCRYPTED_DATA_BLOB))) goto out_error; + if (!(license->EncryptedLicenseInfo = license_new_binary_blob(BB_ENCRYPTED_DATA_BLOB))) + goto out_error; if (!(license->ScopeList = license_new_scope_list())) goto out_error; @@ -1686,6 +2601,7 @@ void license_free(rdpLicense* license) certificate_free(license->certificate); license_free_product_info(license->ProductInfo); license_free_binary_blob(license->ErrorInfo); + license_free_binary_blob(license->LicenseInfo); license_free_binary_blob(license->KeyExchangeList); license_free_binary_blob(license->ServerCertificate); license_free_binary_blob(license->ClientUserName); @@ -1695,7 +2611,171 @@ void license_free(rdpLicense* license) license_free_binary_blob(license->EncryptedPlatformChallengeResponse); license_free_binary_blob(license->EncryptedPremasterSecret); license_free_binary_blob(license->EncryptedHardwareId); + license_free_binary_blob(license->EncryptedLicenseInfo); license_free_scope_list(license->ScopeList); free(license); } } + +LICENSE_STATE license_get_state(const rdpLicense* license) +{ + WINPR_ASSERT(license); + return license->state; +} + +LICENSE_TYPE license_get_type(const rdpLicense* license) +{ + WINPR_ASSERT(license); + return license->type; +} + +BOOL license_set_state(rdpLicense* license, LICENSE_STATE state) +{ + WINPR_ASSERT(license); + license->state = state; + switch (state) + { + case LICENSE_STATE_COMPLETED: + break; + case LICENSE_STATE_ABORTED: + default: + license->type = LICENSE_TYPE_INVALID; + break; + } + + return TRUE; +} + +const char* license_get_state_string(LICENSE_STATE state) +{ + switch (state) + { + case LICENSE_STATE_INITIAL: + return "LICENSE_STATE_INITIAL"; + case LICENSE_STATE_CONFIGURED: + return "LICENSE_STATE_CONFIGURED"; + case LICENSE_STATE_REQUEST: + return "LICENSE_STATE_REQUEST"; + case LICENSE_STATE_NEW_REQUEST: + return "LICENSE_STATE_NEW_REQUEST"; + case LICENSE_STATE_PLATFORM_CHALLENGE: + return "LICENSE_STATE_PLATFORM_CHALLENGE"; + case LICENSE_STATE_PLATFORM_CHALLENGE_RESPONSE: + return "LICENSE_STATE_PLATFORM_CHALLENGE_RESPONSE"; + case LICENSE_STATE_COMPLETED: + return "LICENSE_STATE_COMPLETED"; + case LICENSE_STATE_ABORTED: + return "LICENSE_STATE_ABORTED"; + default: + return "LICENSE_STATE_UNKNOWN"; + } +} + +BOOL license_server_send_request(rdpLicense* license) +{ + if (!license_ensure_state(license, LICENSE_STATE_CONFIGURED, LICENSE_REQUEST)) + return FALSE; + if (!license_send_license_request_packet(license)) + return FALSE; + return license_set_state(license, LICENSE_STATE_REQUEST); +} + +BOOL license_server_configure(rdpLicense* license) +{ + + int len; + wStream* s; + UINT32 algs[] = { KEY_EXCHANGE_ALG_RSA }; + UINT32 x; + const rdpSettings* settings; + UINT32 ProductVersion, issuerCount; + const char* CompanyName; + const char* ProductName; + const char** issuers; + + WINPR_ASSERT(license); + WINPR_ASSERT(license->rdp); + + settings = license->rdp->settings; + + CompanyName = freerdp_settings_get_string(settings, FreeRDP_ServerLicenseCompanyName); + + ProductName = freerdp_settings_get_string(settings, FreeRDP_ServerLicenseProductName); + ProductVersion = freerdp_settings_get_uint32(settings, FreeRDP_ServerLicenseProductVersion); + issuerCount = freerdp_settings_get_uint32(settings, FreeRDP_ServerLicenseProductIssuersCount); + issuers = + (const char**)freerdp_settings_get_pointer(settings, FreeRDP_ServerLicenseProductIssuers); + + WINPR_ASSERT(CompanyName); + WINPR_ASSERT(ProductName); + WINPR_ASSERT(ProductVersion > 0); + WINPR_ASSERT(issuers || (issuerCount == 0)); + + if (!license_ensure_state(license, LICENSE_STATE_INITIAL, LICENSE_REQUEST)) + return FALSE; + + license->ProductInfo->dwVersion = ProductVersion; + len = ConvertToUnicode(CP_UTF8, 0, CompanyName, -1, + (WCHAR**)&license->ProductInfo->pbCompanyName, 0); + if (!license->ProductInfo->pbCompanyName) + return FALSE; + license->ProductInfo->cbCompanyName = len * sizeof(WCHAR); + + len = ConvertToUnicode(CP_UTF8, 0, ProductName, -1, (WCHAR**)&license->ProductInfo->pbProductId, + 0); + if (!license->ProductInfo->pbProductId) + return FALSE; + license->ProductInfo->cbProductId = len * sizeof(WCHAR); + + if (!license_read_binary_blob_data(license->KeyExchangeList, BB_KEY_EXCHG_ALG_BLOB, algs, + sizeof(algs))) + return FALSE; + + if (!certificate_read_server_certificate(license->certificate, settings->ServerCertificate, + settings->ServerCertificateLength)) + return FALSE; + + s = Stream_New(NULL, 1024); + if (!s) + return FALSE; + else + { + BOOL r = + certificate_write_server_certificate(license->certificate, CERT_CHAIN_VERSION_2, s); + if (r) + r = license_read_binary_blob_data(license->ServerCertificate, BB_CERTIFICATE_BLOB, + Stream_Buffer(s), Stream_GetPosition(s)); + + Stream_Free(s, TRUE); + if (!r) + return FALSE; + } + + if (!license_scope_list_resize(license->ScopeList, issuerCount)) + return FALSE; + for (x = 0; x < issuerCount; x++) + { + LICENSE_BLOB* blob = license->ScopeList->array[x]; + const char* name = issuers[x]; + const size_t length = strnlen(name, UINT16_MAX) + 1; + if ((length == 0) || (length > UINT16_MAX)) + { + WLog_WARN(TAG, + "%s: Invalid issuer at position %" PRIuz ": length 0 < %" PRIuz " <= %" PRIu16 + " ['%s']", + x, length, UINT16_MAX, name); + return FALSE; + } + if (!license_read_binary_blob_data(blob, BB_SCOPE_BLOB, name, length)) + return FALSE; + } + + return license_set_state(license, LICENSE_STATE_CONFIGURED); +} + +rdpLicense* license_get(rdpContext* context) +{ + WINPR_ASSERT(context); + WINPR_ASSERT(context->rdp); + return context->rdp->license; +} diff --git a/libfreerdp/core/license.h b/libfreerdp/core/license.h index 6c5393e95..d2d3c0b00 100644 --- a/libfreerdp/core/license.h +++ b/libfreerdp/core/license.h @@ -3,6 +3,8 @@ * RDP Licensing * * Copyright 2011 Marc-Andre Moreau + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,17 +24,16 @@ #include "rdp.h" -#include #include #include -#include #include -#include #include #include +#define CLIENT_RANDOM_LENGTH 32 + typedef struct { UINT32 dwVersion; @@ -52,56 +53,23 @@ typedef struct typedef struct { UINT32 count; - LICENSE_BLOB* array; + LICENSE_BLOB** array; } SCOPE_LIST; -typedef enum -{ - LICENSE_STATE_AWAIT, - LICENSE_STATE_PROCESS, - LICENSE_STATE_ABORTED, - LICENSE_STATE_COMPLETED -} LICENSE_STATE; - -struct rdp_license -{ - LICENSE_STATE state; - rdpRdp* rdp; - rdpCertificate* certificate; - BYTE* Modulus; - UINT32 ModulusLength; - BYTE Exponent[4]; - BYTE HardwareId[HWID_LENGTH]; - BYTE ClientRandom[CLIENT_RANDOM_LENGTH]; - BYTE ServerRandom[SERVER_RANDOM_LENGTH]; - BYTE MasterSecret[MASTER_SECRET_LENGTH]; - BYTE PremasterSecret[PREMASTER_SECRET_LENGTH]; - BYTE SessionKeyBlob[SESSION_KEY_BLOB_LENGTH]; - BYTE MacSaltKey[MAC_SALT_KEY_LENGTH]; - BYTE LicensingEncryptionKey[LICENSING_ENCRYPTION_KEY_LENGTH]; - LICENSE_PRODUCT_INFO* ProductInfo; - LICENSE_BLOB* ErrorInfo; - LICENSE_BLOB* KeyExchangeList; - LICENSE_BLOB* ServerCertificate; - LICENSE_BLOB* ClientUserName; - LICENSE_BLOB* ClientMachineName; - LICENSE_BLOB* PlatformChallenge; - LICENSE_BLOB* EncryptedPremasterSecret; - LICENSE_BLOB* EncryptedPlatformChallenge; - LICENSE_BLOB* EncryptedPlatformChallengeResponse; - LICENSE_BLOB* EncryptedHardwareId; - SCOPE_LIST* ScopeList; - UINT32 PacketHeaderLength; -}; +FREERDP_LOCAL BOOL license_send_valid_client_error_packet(rdpRdp* rdp); FREERDP_LOCAL int license_recv(rdpLicense* license, wStream* s); +/* the configuration is applied from settings. Set FreeRDP_ServerLicense* settings */ +FREERDP_LOCAL BOOL license_server_configure(rdpLicense* license); +FREERDP_LOCAL BOOL license_server_send_request(rdpLicense* license); + FREERDP_LOCAL rdpLicense* license_new(rdpRdp* rdp); FREERDP_LOCAL void license_free(rdpLicense* license); #define LICENSE_TAG FREERDP_TAG("core.license") #ifdef WITH_DEBUG_LICENSE -#define DEBUG_LICENSE(...) WLog_DBG(LICENSE_TAG, __VA_ARGS__) +#define DEBUG_LICENSE(...) WLog_INFO(LICENSE_TAG, __VA_ARGS__) #else #define DEBUG_LICENSE(...) \ do \ diff --git a/libfreerdp/core/test/TestSettings.c b/libfreerdp/core/test/TestSettings.c index 3860482c8..ea4fff280 100644 --- a/libfreerdp/core/test/TestSettings.c +++ b/libfreerdp/core/test/TestSettings.c @@ -454,6 +454,7 @@ static BOOL check_key_helpers(size_t key, const char* stype) FreeRDP_ServerCertificate, FreeRDP_TargetNetAddresses, FreeRDP_ReceivedCapabilities, + FreeRDP_ServerLicenseProductIssuers, FreeRDP_TargetNetPorts, FreeRDP_DeviceArray, FreeRDP_ChannelDefArray, diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index 53f0513b7..bfb1578a0 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -137,6 +137,7 @@ static const size_t bool_list_indices[] = { FreeRDP_RestrictedAdminModeRequired, FreeRDP_SaltedChecksum, FreeRDP_SendPreconnectionPdu, + FreeRDP_ServerLicenseRequired, FreeRDP_ServerMode, FreeRDP_SmartSizing, FreeRDP_SmartcardEmulation, @@ -297,6 +298,8 @@ static const size_t uint32_list_indices[] = { FreeRDP_RequestedProtocols, FreeRDP_SelectedProtocol, FreeRDP_ServerCertificateLength, + FreeRDP_ServerLicenseProductIssuersCount, + FreeRDP_ServerLicenseProductVersion, FreeRDP_ServerPort, FreeRDP_ServerRandomLength, FreeRDP_ShareId, @@ -404,6 +407,8 @@ static const size_t string_list_indices[] = { FreeRDP_RemoteAssistanceRCTicket, FreeRDP_RemoteAssistanceSessionId, FreeRDP_ServerHostname, + FreeRDP_ServerLicenseCompanyName, + FreeRDP_ServerLicenseProductName, FreeRDP_ShellWorkingDirectory, FreeRDP_SmartcardCertificate, FreeRDP_SmartcardPrivateKey, @@ -443,6 +448,7 @@ static const size_t pointer_list_indices[] = { FreeRDP_RedirectionTsvUrl, FreeRDP_ServerAutoReconnectCookie, FreeRDP_ServerCertificate, + FreeRDP_ServerLicenseProductIssuers, FreeRDP_ServerRandom, FreeRDP_StaticChannelArray, FreeRDP_TargetNetAddresses,