[arm] various improvements

This patch moves the ARM configuration before starting the connection process, so
that we can do some provisioning of the FreeRDP settings with the items retrieved
from Azure.
Most notably that allows us to connect directly using RDSTLS security.
This commit is contained in:
David Fort 2023-08-15 14:06:35 +02:00 committed by akallabeth
parent 361da15eed
commit 8a39859612
11 changed files with 636 additions and 48 deletions

View File

@ -43,7 +43,6 @@
#define LB_PASSWORD_MAX_LENGTH 512 #define LB_PASSWORD_MAX_LENGTH 512
#define ELEMENT_TYPE_CERTIFICATE 32
#define ENCODING_TYPE_ASN1_DER 1 #define ENCODING_TYPE_ASN1_DER 1
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -43,6 +43,7 @@
#include "../crypto/crypto.h" #include "../crypto/crypto.h"
#include "../crypto/privatekey.h" #include "../crypto/privatekey.h"
#include "../crypto/certificate.h" #include "../crypto/certificate.h"
#include "gateway/arm.h"
#include "utils.h" #include "utils.h"
@ -326,12 +327,23 @@ BOOL rdp_client_connect(rdpRdp* rdp)
settings->EncryptionMethods = ENCRYPTION_METHOD_FIPS; 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; const char* hostname = settings->ServerHostname;
if (!hostname) if (!hostname)
{ {
WLog_ERR(TAG, "Missing hostname, can not connect to NULL target"); WLog_ERR(TAG, "Missing hostname, can not connect to NULL target");
return FALSE; return FALSE;
} }
nego_init(rdp->nego); nego_init(rdp->nego);
nego_set_target(rdp->nego, hostname, settings->ServerPort); 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 (!freerdp_settings_get_bool(settings, FreeRDP_TransportDumpReplay))
{ {
if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_NEGO)) if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_NEGO))
return FALSE; return FALSE;

View File

@ -3,6 +3,7 @@
* Azure Virtual Desktop Gateway / Azure Resource Manager * Azure Virtual Desktop Gateway / Azure Resource Manager
* *
* Copyright 2023 Michael Saxl <mike@mwsys.mine.bz> * Copyright 2023 Michael Saxl <mike@mwsys.mine.bz>
* Copyright 2023 David Fort <contact@hardening-consulting.com>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,9 +28,11 @@
#include <winpr/stream.h> #include <winpr/stream.h>
#include <winpr/winsock.h> #include <winpr/winsock.h>
#include <winpr/cred.h> #include <winpr/cred.h>
#include <winpr/bcrypt.h>
#include <freerdp/log.h> #include <freerdp/log.h>
#include <freerdp/error.h> #include <freerdp/error.h>
#include <freerdp/crypto/certificate.h>
#include <freerdp/utils/ringbuffer.h> #include <freerdp/utils/ringbuffer.h>
#include <freerdp/utils/smartcardlogon.h> #include <freerdp/utils/smartcardlogon.h>
@ -40,11 +43,15 @@
#include "../credssp_auth.h" #include "../credssp_auth.h"
#include "../proxy.h" #include "../proxy.h"
#include "../rdp.h" #include "../rdp.h"
#include "../../crypto/crypto.h"
#include "../../crypto/certificate.h"
#include "../../crypto/opensslcompat.h" #include "../../crypto/opensslcompat.h"
#include "rpc_fault.h" #include "rpc_fault.h"
#include "../utils.h" #include "../utils.h"
#include "../settings.h" #include "../settings.h"
#include "../redirection.h"
//#define WITH_AAD
#ifdef WITH_AAD #ifdef WITH_AAD
#include <cjson/cJSON.h> #include <cjson/cJSON.h>
#endif #endif
@ -311,6 +318,343 @@ arm_create_cleanup:
return message; 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) static BOOL arm_fill_gateway_parameters(rdpArm* arm, const char* message, size_t len)
{ {
WINPR_ASSERT(arm); WINPR_ASSERT(arm);
@ -321,31 +665,94 @@ static BOOL arm_fill_gateway_parameters(rdpArm* arm, const char* message, size_t
BOOL status = FALSE; BOOL status = FALSE;
if (!json) if (!json)
return FALSE; 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"); WLog_DBG(TAG, "extracted target url %s", gwurl->valuestring);
if (cJSON_IsString(gwurl) && (gwurl->valuestring != NULL)) if (!freerdp_settings_set_string(settings, FreeRDP_GatewayUrl, gwurl->valuestring))
{ status = FALSE;
WLog_DBG(TAG, "extracted target url %s", gwurl->valuestring); else
if (!freerdp_settings_set_string(arm->context->settings, FreeRDP_GatewayUrl, status = TRUE;
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);
} }
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; return status;
} }

View File

@ -344,14 +344,16 @@ static BOOL rdp_target_cert_write_element(wStream* s, UINT32 Type, UINT32 Encodi
return TRUE; return TRUE;
} }
static BOOL rdp_redirection_read_target_cert(rdpRedirection* redirection, const BYTE* data, BOOL rdp_redirection_read_target_cert(rdpCertificate** ptargetCertificate, const BYTE* data,
size_t length) size_t length)
{ {
WINPR_ASSERT(ptargetCertificate);
wStream sbuffer = { 0 }; wStream sbuffer = { 0 };
wStream* s = Stream_StaticConstInit(&sbuffer, data, length); wStream* s = Stream_StaticConstInit(&sbuffer, data, length);
freerdp_certificate_free(redirection->TargetCertificate); freerdp_certificate_free(*ptargetCertificate);
redirection->TargetCertificate = NULL; *ptargetCertificate = NULL;
size_t plength = 0; size_t plength = 0;
const BYTE* ptr = NULL; const BYTE* ptr = NULL;
@ -364,16 +366,15 @@ static BOOL rdp_redirection_read_target_cert(rdpRedirection* redirection, const
switch (type) switch (type)
{ {
case ELEMENT_TYPE_CERTIFICATE: case CERT_cert_file_element:
if (encoding == ENCODING_TYPE_ASN1_DER) if (encoding == ENCODING_TYPE_ASN1_DER)
{ {
if (redirection->TargetCertificate) if (*ptargetCertificate)
WLog_WARN(TAG, "Duplicate TargetCertificate in data detected!"); WLog_WARN(TAG, "Duplicate TargetCertificate in data detected!");
else else
{ {
redirection->TargetCertificate = *ptargetCertificate = freerdp_certificate_new_from_der(ptr, plength);
freerdp_certificate_new_from_der(ptr, plength); if (!*ptargetCertificate)
if (!redirection->TargetCertificate)
WLog_ERR(TAG, "TargetCertificate parsing DER data failed"); WLog_ERR(TAG, "TargetCertificate parsing DER data failed");
} }
} }
@ -384,15 +385,15 @@ static BOOL rdp_redirection_read_target_cert(rdpRedirection* redirection, const
} }
break; break;
default: /* ignore unknown fields */ default: /* ignore unknown fields */
WLog_WARN(TAG, WLog_VRB(TAG,
"Unknown TargetCertificate field type %" PRIu32 ", encoding %" PRIu32 "Unknown TargetCertificate field type %" PRIu32 ", encoding %" PRIu32
" of length %" PRIu32, " of length %" PRIu32,
type, encoding, plength); type, encoding, plength);
break; break;
} }
} }
return redirection->TargetCertificate != NULL; return *ptargetCertificate != NULL;
} }
static BOOL rdp_redirection_write_target_cert(wStream* s, const rdpRedirection* redirection) 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; size_t derlen = 0;
BYTE* der = freerdp_certificate_get_der(cert, &derlen); 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)) derlen))
goto fail; 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)) if (!rdp_redirection_read_base64_wchar(LB_TARGET_CERTIFICATE, s, &length, &ptr))
return FALSE; 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); free(ptr);
return rc; return rc;
} }
@ -1203,7 +1204,7 @@ BOOL redirection_set_byte_option(rdpRedirection* redirection, UINT32 flag, const
return redirection_copy_data(&redirection->RedirectionGuid, return redirection_copy_data(&redirection->RedirectionGuid,
&redirection->RedirectionGuidLength, data, length); &redirection->RedirectionGuidLength, data, length);
case LB_TARGET_CERTIFICATE: case LB_TARGET_CERTIFICATE:
return rdp_redirection_read_target_cert(redirection, data, length); return rdp_redirection_read_target_cert(&redirection->TargetCertificate, data, length);
default: default:
return redirection_unsupported(__func__, flag, return redirection_unsupported(__func__, flag,
LB_CLIENT_TSV_URL | LB_PASSWORD | LB_LOAD_BALANCE_INFO | LB_CLIENT_TSV_URL | LB_PASSWORD | LB_LOAD_BALANCE_INFO |

View File

@ -31,12 +31,20 @@ typedef struct rdp_redirection rdpRedirection;
#include <winpr/wlog.h> #include <winpr/wlog.h>
#include <winpr/stream.h> #include <winpr/stream.h>
#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 int rdp_redirection_apply_settings(rdpRdp* rdp);
FREERDP_LOCAL state_run_t rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s); FREERDP_LOCAL state_run_t rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s);
FREERDP_LOCAL BOOL FREERDP_LOCAL BOOL
rdp_write_enhanced_security_redirection_packet(wStream* s, const rdpRedirection* redirection); 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") #define REDIR_TAG FREERDP_TAG("core.redirection")
#ifdef WITH_DEBUG_REDIR #ifdef WITH_DEBUG_REDIR
#define DEBUG_REDIR(...) WLog_DBG(REDIR_TAG, __VA_ARGS__) #define DEBUG_REDIR(...) WLog_DBG(REDIR_TAG, __VA_ARGS__)

View File

@ -246,7 +246,7 @@ BOOL transport_connect_tls(rdpTransport* transport)
/* Only prompt for password if we use TLS (NLA also calls this function) */ /* Only prompt for password if we use TLS (NLA also calls this function) */
if (settings->SelectedProtocol == PROTOCOL_SSL) 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_SKIP:
case AUTH_SUCCESS: case AUTH_SUCCESS:
@ -471,11 +471,6 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por
if (transport->GatewayEnabled) if (transport->GatewayEnabled)
{ {
if (freerdp_settings_get_bool(settings, FreeRDP_GatewayArmTransport))
{
if (!arm_resolve_endpoint(context, timeout))
return FALSE;
}
if (settings->GatewayUrl) if (settings->GatewayUrl)
{ {
WINPR_ASSERT(!transport->wst); WINPR_ASSERT(!transport->wst);

View File

@ -45,7 +45,7 @@ static char* base64_encode_ex(const char* alphabet, const BYTE* data, size_t len
size_t outCounter = 0; size_t outCounter = 0;
q = data; q = data;
p = ret = (char*)malloc(outLen + extra + 1); p = ret = (char*)malloc(outLen + extra + 1ull);
if (!p) if (!p)
return NULL; return NULL;

View File

@ -1567,6 +1567,45 @@ X509* freerdp_certificate_get_x509(rdpCertificate* cert)
return cert->x509; 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) #if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
static RSA* freerdp_certificate_get_RSA(const rdpCertificate* cert) static RSA* freerdp_certificate_get_RSA(const rdpCertificate* cert)
{ {

View File

@ -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 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 */ #endif /* FREERDP_LIB_CORE_CERTIFICATE_H */

View File

@ -257,6 +257,27 @@ typedef PVOID BCRYPT_SECRET_HANDLE;
"d\x00H\x00" \ "d\x00H\x00" \
"a\x00s\x00h\x00\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 #ifdef __cplusplus
extern "C" extern "C"
{ {

View File

@ -297,6 +297,107 @@ typedef const CERT_CONTEXT* PCCERT_CONTEXT;
#define PKCS_7_ASN_ENCODING 0x00010000 #define PKCS_7_ASN_ENCODING 0x00010000
#define PKCS_7_NDR_ENCODING 0x00020000 #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_MASK 0xFFFF
#define CERT_COMPARE_SHIFT 16 #define CERT_COMPARE_SHIFT 16
#define CERT_COMPARE_ANY 0 #define CERT_COMPARE_ANY 0