diff --git a/include/freerdp/redirection.h b/include/freerdp/redirection.h index 9478a44b1..9b4da7936 100644 --- a/include/freerdp/redirection.h +++ b/include/freerdp/redirection.h @@ -43,7 +43,6 @@ #define LB_PASSWORD_MAX_LENGTH 512 -#define ELEMENT_TYPE_CERTIFICATE 32 #define ENCODING_TYPE_ASN1_DER 1 #ifdef __cplusplus diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 15b5ab289..9861a15ce 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -43,6 +43,7 @@ #include "../crypto/crypto.h" #include "../crypto/privatekey.h" #include "../crypto/certificate.h" +#include "gateway/arm.h" #include "utils.h" @@ -326,12 +327,23 @@ BOOL rdp_client_connect(rdpRdp* rdp) settings->EncryptionMethods = ENCRYPTION_METHOD_FIPS; } + UINT32 TcpConnectTimeout = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout); + if (settings->GatewayArmTransport) + { + if (!arm_resolve_endpoint(rdp->context, TcpConnectTimeout)) + { + WLog_ERR(TAG, "error retrieving ARM configuration"); + return FALSE; + } + } + const char* hostname = settings->ServerHostname; if (!hostname) { WLog_ERR(TAG, "Missing hostname, can not connect to NULL target"); return FALSE; } + nego_init(rdp->nego); nego_set_target(rdp->nego, hostname, settings->ServerPort); @@ -409,6 +421,7 @@ BOOL rdp_client_connect(rdpRdp* rdp) if (!freerdp_settings_get_bool(settings, FreeRDP_TransportDumpReplay)) { + if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_NEGO)) return FALSE; diff --git a/libfreerdp/core/gateway/arm.c b/libfreerdp/core/gateway/arm.c index 03ce72c09..1d2badbc2 100644 --- a/libfreerdp/core/gateway/arm.c +++ b/libfreerdp/core/gateway/arm.c @@ -3,6 +3,7 @@ * Azure Virtual Desktop Gateway / Azure Resource Manager * * Copyright 2023 Michael Saxl + * Copyright 2023 David Fort * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,9 +28,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -40,11 +43,15 @@ #include "../credssp_auth.h" #include "../proxy.h" #include "../rdp.h" +#include "../../crypto/crypto.h" +#include "../../crypto/certificate.h" #include "../../crypto/opensslcompat.h" #include "rpc_fault.h" #include "../utils.h" #include "../settings.h" +#include "../redirection.h" +//#define WITH_AAD #ifdef WITH_AAD #include #endif @@ -311,6 +318,343 @@ arm_create_cleanup: return message; } +/** + * treats the redirectedAuthBlob + * + * sample pbInput: + * 41004500530000004b44424d01000000200000006ee71b295810b3fd13799da3825d0efa3a628e8f4a6eda609ffa975408556546 + * 'A\x00E\x00S\x00\x00\x00KDBM\x01\x00\x00\x00 + * \x00\x00\x00n\xe7\x1b)X\x10\xb3\xfd\x13y\x9d\xa3\x82]\x0e\xfa:b\x8e\x8fJn\xda`\x9f\xfa\x97T\x08UeF' + * + * @param pbInput the raw auth blob (base64 and utf16 decoded) + * @param cbInput size of pbInput + * @return the corresponding WINPR_CIPHER_CTX if success, NULL otherwise + */ +static WINPR_CIPHER_CTX* treatAuthBlob(const BYTE* pbInput, size_t cbInput) +{ + WINPR_CIPHER_CTX* ret = NULL; + char algoName[100]; + + SSIZE_T algoSz = + ConvertWCharNToUtf8((WCHAR*)pbInput, cbInput / sizeof(WCHAR), algoName, sizeof(algoName)); + if (algoSz <= 0) + { + WLog_ERR(TAG, "invalid algoName"); + return NULL; + } + + algoName[algoSz] = 0; + if (strcmp(algoName, "AES") != 0) + { + WLog_ERR(TAG, "only AES is supported for now"); + return NULL; + } + + cbInput -= (algoSz + 1) * sizeof(WCHAR); + + if (cbInput < 12) + { + WLog_ERR(TAG, "invalid AuthBlob size"); + return NULL; + } + + /* BCRYPT_KEY_DATA_BLOB_HEADER */ + wStream staticStream; + wStream* s = + Stream_StaticConstInit(&staticStream, pbInput + (algoSz + 1) * sizeof(WCHAR), cbInput); + + UINT32 dwMagic; + Stream_Read_UINT32(s, dwMagic); + + if (dwMagic != BCRYPT_KEY_DATA_BLOB_MAGIC) + { + WLog_ERR(TAG, "unsupported authBlob type"); + return NULL; + } + + UINT32 dwVersion; + Stream_Read_UINT32(s, dwVersion); + if (dwVersion != BCRYPT_KEY_DATA_BLOB_VERSION1) + { + WLog_ERR(TAG, "unsupported authBlob version %d, expecting %d", dwVersion, + BCRYPT_KEY_DATA_BLOB_VERSION1); + return NULL; + } + + UINT32 cbKeyData; + Stream_Read_UINT32(s, cbKeyData); + cbInput -= 12; + + if (cbKeyData > cbInput) + { + WLog_ERR(TAG, "invalid authBlob size"); + return NULL; + } + + int cipherType; + switch (cbKeyData) + { + case 16: + cipherType = WINPR_CIPHER_AES_128_CBC; + break; + case 24: + cipherType = WINPR_CIPHER_AES_192_CBC; + break; + case 32: + cipherType = WINPR_CIPHER_AES_256_CBC; + break; + default: + WLog_ERR(TAG, "invalid authBlob cipher size"); + return NULL; + } + + ret = winpr_Cipher_New(cipherType, WINPR_ENCRYPT, Stream_Pointer(s), NULL); + if (!ret) + { + WLog_ERR(TAG, "error creating cipher"); + return NULL; + } + + if (!winpr_Cipher_SetPadding(ret, TRUE)) + { + WLog_ERR(TAG, "unable to enable padding on cipher"); + winpr_Cipher_Free(ret); + return NULL; + } + + return ret; +} + +static BOOL arm_stringEncodeW(const BYTE* pin, size_t cbIn, BYTE** ppOut, size_t* pcbOut) +{ + *ppOut = NULL; + *pcbOut = 0; + + /* encode to base64 with crlf */ + char* b64encoded = crypto_base64_encode_ex(pin, cbIn, TRUE); + if (!b64encoded) + return FALSE; + + /* and then convert to Unicode */ + size_t outSz; + *ppOut = (BYTE*)ConvertUtf8NToWCharAlloc(b64encoded, strlen(b64encoded), &outSz); + free(b64encoded); + + if (!*ppOut) + return FALSE; + + *pcbOut = (outSz + 1) * sizeof(WCHAR); + return TRUE; +} + +BOOL arm_encodeRedirectPasswd(rdpSettings* settings, const rdpCertificate* cert, + WINPR_CIPHER_CTX* cipher) +{ + BOOL ret = FALSE; + BYTE* output = NULL; + BYTE* finalOutput = NULL; + + /* let's prepare the encrypted password, first we do a + * cipheredPass = AES(redirectedAuthBlob, toUtf16(passwd)) + */ + + size_t wpasswdLen = 0; + WCHAR* wpasswd = freerdp_settings_get_string_as_utf16(settings, FreeRDP_Password, &wpasswdLen); + if (!wpasswd) + { + WLog_ERR(TAG, "error when converting password to UTF16"); + return FALSE; + } + + size_t wpasswdBytes = (wpasswdLen + 1) * sizeof(WCHAR); + BYTE* encryptedPass = calloc(1, wpasswdBytes + 16); /* 16: block size of AES (padding) */ + size_t encryptedPassLen = 0; + size_t finalLen = 0; + if (!encryptedPass || + !winpr_Cipher_Update(cipher, wpasswd, wpasswdBytes, encryptedPass, &encryptedPassLen) || + !winpr_Cipher_Final(cipher, encryptedPass + encryptedPassLen, &finalLen)) + { + WLog_ERR(TAG, "error when ciphering password"); + goto out; + } + encryptedPassLen += finalLen; + + /* then encrypt(cipheredPass, publicKey(redirectedServerCert) */ + size_t output_length = 0; + + if (!freerdp_certificate_publickey_encrypt(cert, encryptedPass, encryptedPassLen, &output, + &output_length)) + { + WLog_ERR(TAG, "unable to encrypt with the server's public key"); + goto out; + } + + size_t finalOutputLen = 0; + if (!arm_stringEncodeW(output, output_length, &finalOutput, &finalOutputLen)) + { + WLog_ERR(TAG, "unable to base64+utf16 final blob"); + goto out; + } + + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionPassword, finalOutput, + finalOutputLen)) + { + WLog_ERR(TAG, "unable to set the redirection password in settings"); + goto out; + } + + settings->RdstlsSecurity = TRUE; + settings->RedirectionFlags = LB_PASSWORD_IS_PK_ENCRYPTED; + ret = TRUE; +out: + free(finalOutput); + free(output); + free(encryptedPass); + free(wpasswd); + return ret; +} + +/** extract that stupidly over-encoded field that is the equivalent of + * base64.b64decode( base64.b64decode(input).decode('utf-16') ) + * in python + */ +static BOOL arm_pick_base64Utf16Field(cJSON* json, const char* name, BYTE** poutput, size_t* plen) +{ + *poutput = NULL; + *plen = 0; + + const cJSON* node = cJSON_GetObjectItemCaseSensitive(json, name); + if (!node || !cJSON_IsString(node)) + return TRUE; + + char* nodeValue = cJSON_GetStringValue(node); + if (!nodeValue) + return TRUE; + + BYTE* output1 = NULL; + size_t len1 = 0; + crypto_base64_decode(nodeValue, strlen(nodeValue), &output1, &len1); + if (!output1 || !len1) + { + WLog_ERR(TAG, "error when first unbase64 for %s", name); + return FALSE; + } + + size_t len2 = 0; + char* output2 = ConvertWCharNToUtf8Alloc((WCHAR*)output1, len1 / 2, &len2); + free(output1); + if (!output2 || !len2) + { + WLog_ERR(TAG, "error when decode('utf-16') for %s", name); + return FALSE; + } + + BYTE* output = NULL; + crypto_base64_decode(output2, len2, &output, plen); + free(output2); + if (!output || !*plen) + { + WLog_ERR(TAG, "error when second unbase64 for %s", name); + return FALSE; + } + + *poutput = output; + return TRUE; +} + +/** + * treats the Azure network meta data that will typically look like: + * + * {'interface': [ + * {'ipv4': { + * 'ipAddress': [ + * {'privateIpAddress': 'X.X.X.X', + * 'publicIpAddress': 'X.X.X.X'} + * ], + * 'subnet': [ + * {'address': 'X.X.X.X', 'prefix': '24'} + * ] + * }, + * 'ipv6': {'ipAddress': []}, + * 'macAddress': 'YYYYYYY'} + * ] + * } + * + */ + +static BOOL arm_treat_azureInstanceNetworkMetadata(const char* metadata, rdpSettings* settings) +{ + BOOL ret = FALSE; + cJSON* json = cJSON_Parse(metadata); + if (!json) + { + WLog_ERR(TAG, "invalid azureInstanceNetworkMetadata"); + return FALSE; + } + + const cJSON* interface = cJSON_GetObjectItem(json, "interface"); + if (!interface) + { + ret = TRUE; + goto out; + } + + if (!cJSON_IsArray(interface)) + { + WLog_ERR(TAG, "expecting interface to be an Array"); + goto out; + } + + int interfaceSz = cJSON_GetArraySize(interface); + if (interfaceSz == 0) + { + WLog_WARN(TAG, "no addresses in azure instance metadata"); + ret = TRUE; + goto out; + } + + for (int i = 0; i < interfaceSz; i++) + { + const cJSON* interN = cJSON_GetArrayItem(interface, i); + if (!interN) + continue; + + const cJSON* ipv4 = cJSON_GetObjectItem(interN, "ipv4"); + if (!ipv4) + continue; + + const cJSON* ipAddress = cJSON_GetObjectItem(ipv4, "ipAddress"); + if (!ipAddress || !cJSON_IsArray(ipAddress)) + continue; + + int naddresses = cJSON_GetArraySize(ipAddress); + for (int j = 0; j < naddresses; j++) + { + const cJSON* adressN = cJSON_GetArrayItem(ipAddress, j); + if (!adressN) + continue; + + const cJSON* publicIpNode = cJSON_GetObjectItem(adressN, "publicIpAddress"); + if (!publicIpNode || !cJSON_IsString(publicIpNode)) + continue; + + char* publicIp = cJSON_GetStringValue(publicIpNode); + if (freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetFQDN, publicIp)) + { + WLog_INFO(TAG, "connecting to publicIp %s", publicIp); + ret = TRUE; + goto out; + } + } + } + + ret = TRUE; + +out: + cJSON_Delete(json); + return ret; +} + static BOOL arm_fill_gateway_parameters(rdpArm* arm, const char* message, size_t len) { WINPR_ASSERT(arm); @@ -321,31 +665,94 @@ static BOOL arm_fill_gateway_parameters(rdpArm* arm, const char* message, size_t BOOL status = FALSE; if (!json) return FALSE; - else + + rdpSettings* settings = arm->context->settings; + const cJSON* gwurl = cJSON_GetObjectItemCaseSensitive(json, "gatewayLocation"); + if (cJSON_IsString(gwurl) && (gwurl->valuestring != NULL)) { - const cJSON* gwurl = cJSON_GetObjectItemCaseSensitive(json, "gatewayLocation"); - if (cJSON_IsString(gwurl) && (gwurl->valuestring != NULL)) - { - WLog_DBG(TAG, "extracted target url %s", gwurl->valuestring); - if (!freerdp_settings_set_string(arm->context->settings, FreeRDP_GatewayUrl, - gwurl->valuestring)) - status = FALSE; - else - status = TRUE; - } - - const cJSON* hostname = cJSON_GetObjectItem(json, "redirectedServerName"); - if (cJSON_GetStringValue(hostname)) - { - if (!freerdp_settings_set_string(arm->context->settings, FreeRDP_ServerHostname, - cJSON_GetStringValue(hostname))) - status = FALSE; - else - status = TRUE; - } - - cJSON_Delete(json); + WLog_DBG(TAG, "extracted target url %s", gwurl->valuestring); + if (!freerdp_settings_set_string(settings, FreeRDP_GatewayUrl, gwurl->valuestring)) + status = FALSE; + else + status = TRUE; } + + const cJSON* serverNameNode = cJSON_GetObjectItem(json, "redirectedServerName"); + if (serverNameNode) + { + char* serverName = cJSON_GetStringValue(serverNameNode); + if (serverName) + status = freerdp_settings_set_string(settings, FreeRDP_ServerHostname, serverName); + } + + const cJSON* azureMeta = cJSON_GetObjectItem(json, "azureInstanceNetworkMetadata"); + if (azureMeta && cJSON_IsString(azureMeta)) + { + if (!arm_treat_azureInstanceNetworkMetadata(cJSON_GetStringValue(azureMeta), settings)) + { + WLog_ERR(TAG, "error when treating azureInstanceNetworkMetadata"); + } + } + + WCHAR* wGUID = NULL; + BYTE* cert = NULL; + BYTE* authBlob = NULL; + rdpCertificate* redirectedServerCert = NULL; + + do + { + /* redirectedAuthGuid */ + const cJSON* redirectedAuthGuidNode = + cJSON_GetObjectItemCaseSensitive(json, "redirectedAuthGuid"); + if (!redirectedAuthGuidNode || !cJSON_IsString(redirectedAuthGuidNode)) + break; + + char* redirectedAuthGuid = cJSON_GetStringValue(redirectedAuthGuidNode); + if (!redirectedAuthGuid) + break; + + size_t wGUID_len = 0; + wGUID = ConvertUtf8ToWCharAlloc(redirectedAuthGuid, &wGUID_len); + if (!wGUID) + { + WLog_ERR(TAG, "unable to allocate space for redirectedAuthGuid"); + status = FALSE; + goto endOfFunction; + } + + status = freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionGuid, wGUID, + (wGUID_len + 1) * sizeof(WCHAR)); + if (!status) + goto endOfFunction; + + /* redirectedServerCert */ + size_t certLen = 0; + if (!arm_pick_base64Utf16Field(json, "redirectedServerCert", &cert, &certLen)) + break; + + rdpCertificate* redirectedServerCert = NULL; + if (!rdp_redirection_read_target_cert(&redirectedServerCert, cert, certLen)) + break; + + /* redirectedAuthBlob */ + size_t authBlobLen = 0; + if (!arm_pick_base64Utf16Field(json, "redirectedAuthBlob", &authBlob, &authBlobLen)) + break; + + WINPR_CIPHER_CTX* cipher = treatAuthBlob(authBlob, authBlobLen); + if (!cipher) + break; + + if (!arm_encodeRedirectPasswd(settings, redirectedServerCert, cipher)) + break; + + } while (FALSE); + + free(cert); + freerdp_certificate_free(redirectedServerCert); + free(authBlob); +endOfFunction: + cJSON_Delete(json); return status; } diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c index 7af6366d4..ae8910588 100644 --- a/libfreerdp/core/redirection.c +++ b/libfreerdp/core/redirection.c @@ -344,14 +344,16 @@ static BOOL rdp_target_cert_write_element(wStream* s, UINT32 Type, UINT32 Encodi return TRUE; } -static BOOL rdp_redirection_read_target_cert(rdpRedirection* redirection, const BYTE* data, - size_t length) +BOOL rdp_redirection_read_target_cert(rdpCertificate** ptargetCertificate, const BYTE* data, + size_t length) { + WINPR_ASSERT(ptargetCertificate); + wStream sbuffer = { 0 }; wStream* s = Stream_StaticConstInit(&sbuffer, data, length); - freerdp_certificate_free(redirection->TargetCertificate); - redirection->TargetCertificate = NULL; + freerdp_certificate_free(*ptargetCertificate); + *ptargetCertificate = NULL; size_t plength = 0; const BYTE* ptr = NULL; @@ -364,16 +366,15 @@ static BOOL rdp_redirection_read_target_cert(rdpRedirection* redirection, const switch (type) { - case ELEMENT_TYPE_CERTIFICATE: + case CERT_cert_file_element: if (encoding == ENCODING_TYPE_ASN1_DER) { - if (redirection->TargetCertificate) + if (*ptargetCertificate) WLog_WARN(TAG, "Duplicate TargetCertificate in data detected!"); else { - redirection->TargetCertificate = - freerdp_certificate_new_from_der(ptr, plength); - if (!redirection->TargetCertificate) + *ptargetCertificate = freerdp_certificate_new_from_der(ptr, plength); + if (!*ptargetCertificate) WLog_ERR(TAG, "TargetCertificate parsing DER data failed"); } } @@ -384,15 +385,15 @@ static BOOL rdp_redirection_read_target_cert(rdpRedirection* redirection, const } break; default: /* ignore unknown fields */ - WLog_WARN(TAG, - "Unknown TargetCertificate field type %" PRIu32 ", encoding %" PRIu32 - " of length %" PRIu32, - type, encoding, plength); + WLog_VRB(TAG, + "Unknown TargetCertificate field type %" PRIu32 ", encoding %" PRIu32 + " of length %" PRIu32, + type, encoding, plength); break; } } - return redirection->TargetCertificate != NULL; + return *ptargetCertificate != NULL; } static BOOL rdp_redirection_write_target_cert(wStream* s, const rdpRedirection* redirection) @@ -407,7 +408,7 @@ static BOOL rdp_redirection_write_target_cert(wStream* s, const rdpRedirection* size_t derlen = 0; BYTE* der = freerdp_certificate_get_der(cert, &derlen); - if (!rdp_target_cert_write_element(s, ELEMENT_TYPE_CERTIFICATE, ENCODING_TYPE_ASN1_DER, der, + if (!rdp_target_cert_write_element(s, CERT_cert_file_element, ENCODING_TYPE_ASN1_DER, der, derlen)) goto fail; @@ -446,7 +447,7 @@ static BOOL rdp_redirection_read_target_cert_stream(wStream* s, rdpRedirection* if (!rdp_redirection_read_base64_wchar(LB_TARGET_CERTIFICATE, s, &length, &ptr)) return FALSE; - const BOOL rc = rdp_redirection_read_target_cert(redirection, ptr, length); + const BOOL rc = rdp_redirection_read_target_cert(&redirection->TargetCertificate, ptr, length); free(ptr); return rc; } @@ -1203,7 +1204,7 @@ BOOL redirection_set_byte_option(rdpRedirection* redirection, UINT32 flag, const return redirection_copy_data(&redirection->RedirectionGuid, &redirection->RedirectionGuidLength, data, length); case LB_TARGET_CERTIFICATE: - return rdp_redirection_read_target_cert(redirection, data, length); + return rdp_redirection_read_target_cert(&redirection->TargetCertificate, data, length); default: return redirection_unsupported(__func__, flag, LB_CLIENT_TSV_URL | LB_PASSWORD | LB_LOAD_BALANCE_INFO | diff --git a/libfreerdp/core/redirection.h b/libfreerdp/core/redirection.h index 649e41e5b..e0c243214 100644 --- a/libfreerdp/core/redirection.h +++ b/libfreerdp/core/redirection.h @@ -31,12 +31,20 @@ typedef struct rdp_redirection rdpRedirection; #include #include +#define CERT_cert_file_element 32 +#define CERT_crl_file_element 33 +#define CERT_ctl_file_element 34 +#define CERT_keyid_file_element 35 + FREERDP_LOCAL int rdp_redirection_apply_settings(rdpRdp* rdp); FREERDP_LOCAL state_run_t rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s); FREERDP_LOCAL BOOL rdp_write_enhanced_security_redirection_packet(wStream* s, const rdpRedirection* redirection); +FREERDP_LOCAL BOOL rdp_redirection_read_target_cert(rdpCertificate** ptargetCertificate, + const BYTE* data, size_t length); + #define REDIR_TAG FREERDP_TAG("core.redirection") #ifdef WITH_DEBUG_REDIR #define DEBUG_REDIR(...) WLog_DBG(REDIR_TAG, __VA_ARGS__) diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index dd82257ee..72a7e1c17 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -246,7 +246,7 @@ BOOL transport_connect_tls(rdpTransport* transport) /* Only prompt for password if we use TLS (NLA also calls this function) */ if (settings->SelectedProtocol == PROTOCOL_SSL) { - switch (utils_authenticate(transport_get_context(transport)->instance, AUTH_TLS, FALSE)) + switch (utils_authenticate(context->instance, AUTH_TLS, FALSE)) { case AUTH_SKIP: case AUTH_SUCCESS: @@ -471,11 +471,6 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por if (transport->GatewayEnabled) { - if (freerdp_settings_get_bool(settings, FreeRDP_GatewayArmTransport)) - { - if (!arm_resolve_endpoint(context, timeout)) - return FALSE; - } if (settings->GatewayUrl) { WINPR_ASSERT(!transport->wst); diff --git a/libfreerdp/crypto/base64.c b/libfreerdp/crypto/base64.c index 6bbd9bb24..8b7b60faf 100644 --- a/libfreerdp/crypto/base64.c +++ b/libfreerdp/crypto/base64.c @@ -45,7 +45,7 @@ static char* base64_encode_ex(const char* alphabet, const BYTE* data, size_t len size_t outCounter = 0; q = data; - p = ret = (char*)malloc(outLen + extra + 1); + p = ret = (char*)malloc(outLen + extra + 1ull); if (!p) return NULL; diff --git a/libfreerdp/crypto/certificate.c b/libfreerdp/crypto/certificate.c index 48e5407ee..49a7e122a 100644 --- a/libfreerdp/crypto/certificate.c +++ b/libfreerdp/crypto/certificate.c @@ -1567,6 +1567,45 @@ X509* freerdp_certificate_get_x509(rdpCertificate* cert) return cert->x509; } +BOOL freerdp_certificate_publickey_encrypt(const rdpCertificate* cert, const BYTE* input, + size_t cbInput, BYTE** poutput, size_t* pcbOutput) +{ + WINPR_ASSERT(cert); + WINPR_ASSERT(input); + WINPR_ASSERT(poutput); + WINPR_ASSERT(pcbOutput); + + BOOL ret = FALSE; + BYTE* output = NULL; + EVP_PKEY* pkey = X509_get0_pubkey(cert->x509); + if (!pkey) + return FALSE; + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (!ctx) + return FALSE; + + size_t outputSize = EVP_PKEY_size(pkey); + output = malloc(outputSize); + *pcbOutput = outputSize; + + if (EVP_PKEY_encrypt_init(ctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) != 1 || + EVP_PKEY_encrypt(ctx, output, pcbOutput, input, cbInput) != 1) + { + WLog_ERR(TAG, "error when setting up public key"); + goto out; + } + + *poutput = output; + output = NULL; + ret = TRUE; +out: + EVP_PKEY_CTX_free(ctx); + free(output); + return ret; +} + #if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3) static RSA* freerdp_certificate_get_RSA(const rdpCertificate* cert) { diff --git a/libfreerdp/crypto/certificate.h b/libfreerdp/crypto/certificate.h index 35d76dd50..1a0cf36ba 100644 --- a/libfreerdp/crypto/certificate.h +++ b/libfreerdp/crypto/certificate.h @@ -57,4 +57,8 @@ FREERDP_LOCAL const rdpCertInfo* freerdp_certificate_get_info(const rdpCertifica */ FREERDP_LOCAL X509* freerdp_certificate_get_x509(rdpCertificate* certificate); +FREERDP_LOCAL BOOL freerdp_certificate_publickey_encrypt(const rdpCertificate* cert, + const BYTE* input, size_t cbInput, + BYTE** poutput, size_t* pcbOutput); + #endif /* FREERDP_LIB_CORE_CERTIFICATE_H */ diff --git a/winpr/include/winpr/bcrypt.h b/winpr/include/winpr/bcrypt.h index 3c50034c5..40d4c7255 100644 --- a/winpr/include/winpr/bcrypt.h +++ b/winpr/include/winpr/bcrypt.h @@ -257,6 +257,27 @@ typedef PVOID BCRYPT_SECRET_HANDLE; "d\x00H\x00" \ "a\x00s\x00h\x00\x00" +#define BCRYPT_KEY_DATA_BLOB \ + (const WCHAR*)"K\x00" \ + "e\x00y\x00" \ + "D\x00" \ + "a\x00t\x00" \ + "a\x00" \ + "B\x00l\x00o\x00" \ + "b\x00\x00" + +#define BCRYPT_BLOCK_PADDING 0x00000001 + +#define BCRYPT_KEY_DATA_BLOB_MAGIC 0x4d42444b +#define BCRYPT_KEY_DATA_BLOB_VERSION1 0x1 + +typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER +{ + ULONG dwMagic; + ULONG dwVersion; + ULONG cbKeyData; +} BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER; + #ifdef __cplusplus extern "C" { diff --git a/winpr/include/winpr/wincrypt.h b/winpr/include/winpr/wincrypt.h index 10a1274ba..cda3131a4 100644 --- a/winpr/include/winpr/wincrypt.h +++ b/winpr/include/winpr/wincrypt.h @@ -297,6 +297,107 @@ typedef const CERT_CONTEXT* PCCERT_CONTEXT; #define PKCS_7_ASN_ENCODING 0x00010000 #define PKCS_7_NDR_ENCODING 0x00020000 +#define CERT_KEY_PROV_HANDLE_PROP_ID 1 +#define CERT_KEY_PROV_INFO_PROP_ID 2 +#define CERT_SHA1_HASH_PROP_ID 3 +#define CERT_MD5_HASH_PROP_ID 4 +#define CERT_HASH_PROP_ID CERT_SHA1_HASH_PROP_ID +#define CERT_KEY_CONTEXT_PROP_ID 5 +#define CERT_KEY_SPEC_PROP_ID 6 +#define CERT_IE30_RESERVED_PROP_ID 7 +#define CERT_PUBKEY_HASH_RESERVED_PROP_ID 8 +#define CERT_ENHKEY_USAGE_PROP_ID 9 +#define CERT_CTL_USAGE_PROP_ID CERT_ENHKEY_USAGE_PROP_ID +#define CERT_NEXT_UPDATE_LOCATION_PROP_ID 10 +#define CERT_FRIENDLY_NAME_PROP_ID 11 +#define CERT_PVK_FILE_PROP_ID 12 +#define CERT_DESCRIPTION_PROP_ID 13 +#define CERT_ACCESS_STATE_PROP_ID 14 +#define CERT_SIGNATURE_HASH_PROP_ID 15 +#define CERT_SMART_CARD_DATA_PROP_ID 16 +#define CERT_EFS_PROP_ID 17 +#define CERT_FORTEZZA_DATA_PROP_ID 18 +#define CERT_ARCHIVED_PROP_ID 19 +#define CERT_KEY_IDENTIFIER_PROP_ID 20 +#define CERT_AUTO_ENROLL_PROP_ID 21 +#define CERT_PUBKEY_ALG_PARA_PROP_ID 22 +#define CERT_CROSS_CERT_DIST_POINTS_PROP_ID 23 +#define CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID 24 +#define CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID 25 +#define CERT_ENROLLMENT_PROP_ID 26 +#define CERT_DATE_STAMP_PROP_ID 27 +#define CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID 28 +#define CERT_SUBJECT_NAME_MD5_HASH_PROP_ID 29 +#define CERT_EXTENDED_ERROR_INFO_PROP_ID 30 +#define CERT_RENEWAL_PROP_ID 64 +#define CERT_ARCHIVED_KEY_HASH_PROP_ID 65 +#define CERT_AUTO_ENROLL_RETRY_PROP_ID 66 +#define CERT_AIA_URL_RETRIEVED_PROP_ID 67 +#define CERT_AUTHORITY_INFO_ACCESS_PROP_ID 68 +#define CERT_BACKED_UP_PROP_ID 69 +#define CERT_OCSP_RESPONSE_PROP_ID 70 +#define CERT_REQUEST_ORIGINATOR_PROP_ID 71 +#define CERT_SOURCE_LOCATION_PROP_ID 72 +#define CERT_SOURCE_URL_PROP_ID 73 +#define CERT_NEW_KEY_PROP_ID 74 +#define CERT_OCSP_CACHE_PREFIX_PROP_ID 75 +#define CERT_SMART_CARD_ROOT_INFO_PROP_ID 76 +#define CERT_NO_AUTO_EXPIRE_CHECK_PROP_ID 77 +#define CERT_NCRYPT_KEY_HANDLE_PROP_ID 78 +#define CERT_HCRYPTPROV_OR_NCRYPT_KEY_HANDLE_PROP_ID 79 +#define CERT_SUBJECT_INFO_ACCESS_PROP_ID 80 +#define CERT_CA_OCSP_AUTHORITY_INFO_ACCESS_PROP_ID 81 +#define CERT_CA_DISABLE_CRL_PROP_ID 82 +#define CERT_ROOT_PROGRAM_CERT_POLICIES_PROP_ID 83 +#define CERT_ROOT_PROGRAM_NAME_CONSTRAINTS_PROP_ID 84 +#define CERT_SUBJECT_OCSP_AUTHORITY_INFO_ACCESS_PROP_ID 85 +#define CERT_SUBJECT_DISABLE_CRL_PROP_ID 86 +#define CERT_CEP_PROP_ID 87 +#define CERT_SIGN_HASH_CNG_ALG_PROP_ID 89 +#define CERT_SCARD_PIN_ID_PROP_ID 90 +#define CERT_SCARD_PIN_INFO_PROP_ID 91 +#define CERT_SUBJECT_PUB_KEY_BIT_LENGTH_PROP_ID 92 +#define CERT_PUB_KEY_CNG_ALG_BIT_LENGTH_PROP_ID 93 +#define CERT_ISSUER_PUB_KEY_BIT_LENGTH_PROP_ID 94 +#define CERT_ISSUER_CHAIN_SIGN_HASH_CNG_ALG_PROP_ID 95 +#define CERT_ISSUER_CHAIN_PUB_KEY_CNG_ALG_BIT_LENGTH_PROP_ID 96 +#define CERT_NO_EXPIRE_NOTIFICATION_PROP_ID 97 +#define CERT_AUTH_ROOT_SHA256_HASH_PROP_ID 98 +#define CERT_NCRYPT_KEY_HANDLE_TRANSFER_PROP_ID 99 +#define CERT_HCRYPTPROV_TRANSFER_PROP_ID 100 +#define CERT_SMART_CARD_READER_PROP_ID 101 +#define CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID 102 +#define CERT_KEY_REPAIR_ATTEMPTED_PROP_ID 103 +#define CERT_DISALLOWED_FILETIME_PROP_ID 104 +#define CERT_ROOT_PROGRAM_CHAIN_POLICIES_PROP_ID 105 +#define CERT_SMART_CARD_READER_NON_REMOVABLE_PROP_ID 106 +#define CERT_SHA256_HASH_PROP_ID 107 +#define CERT_SCEP_SERVER_CERTS_PROP_ID 108 +#define CERT_SCEP_RA_SIGNATURE_CERT_PROP_ID 109 +#define CERT_SCEP_RA_ENCRYPTION_CERT_PROP_ID 110 +#define CERT_SCEP_CA_CERT_PROP_ID 111 +#define CERT_SCEP_SIGNER_CERT_PROP_ID 112 +#define CERT_SCEP_NONCE_PROP_ID 113 +#define CERT_SCEP_ENCRYPT_HASH_CNG_ALG_PROP_ID 114 +#define CERT_SCEP_FLAGS_PROP_ID 115 +#define CERT_SCEP_GUID_PROP_ID 116 +#define CERT_SERIALIZABLE_KEY_CONTEXT_PROP_ID 117 +#define CERT_ISOLATED_KEY_PROP_ID 118 +#define CERT_SERIAL_CHAIN_PROP_ID 119 +#define CERT_KEY_CLASSIFICATION_PROP_ID 120 +#define CERT_OCSP_MUST_STAPLE_PROP_ID 121 +#define CERT_DISALLOWED_ENHKEY_USAGE_PROP_ID 122 +#define CERT_NONCOMPLIANT_ROOT_URL_PROP_ID 123 +#define CERT_PIN_SHA256_HASH_PROP_ID 124 +#define CERT_CLR_DELETE_KEY_PROP_ID 125 +#define CERT_NOT_BEFORE_FILETIME_PROP_ID 126 +#define CERT_NOT_BEFORE_ENHKEY_USAGE_PROP_ID 127 + +#define CERT_FIRST_RESERVED_PROP_ID 107 +#define CERT_LAST_RESERVED_PROP_ID 0x00007fff +#define CERT_FIRST_USER_PROP_ID 0x8000 +#define CERT_LAST_USER_PROP_ID 0x0000ffff + #define CERT_COMPARE_MASK 0xFFFF #define CERT_COMPARE_SHIFT 16 #define CERT_COMPARE_ANY 0