Refactored certificate API:

* Proper encapsulation
* known_hosts2 backend extended (storing PEM)
* New backend storing each host certificate in a file
This commit is contained in:
akallabeth 2021-04-13 11:01:43 +02:00 committed by akallabeth
parent c3171b90cb
commit b494a193db
21 changed files with 1776 additions and 743 deletions

View File

@ -180,6 +180,7 @@ endif()
if(BUILD_TESTING) if(BUILD_TESTING)
set(EXPORT_ALL_SYMBOLS TRUE) set(EXPORT_ALL_SYMBOLS TRUE)
set(CTEST_OUTPUT_ON_FAILURE TRUE)
elseif(NOT DEFINED EXPORT_ALL_SYMBOLS) elseif(NOT DEFINED EXPORT_ALL_SYMBOLS)
set(EXPORT_ALL_SYMBOLS FALSE) set(EXPORT_ALL_SYMBOLS FALSE)
endif() endif()

View File

@ -80,7 +80,7 @@ static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* n
{ {
const char* path = settings->ConfigPath; const char* path = settings->ConfigPath;
char* dir = GetCombinedPath(path, "printers"); char* dir = GetCombinedPath(path, "printers");
char* bname = crypto_base64_encode((const BYTE*)name, (int)length); char* bname = crypto_base64_encode((const BYTE*)name, length);
char* config = GetCombinedPath(dir, bname); char* config = GetCombinedPath(dir, bname);
if (config && !PathFileExistsA(config)) if (config && !PathFileExistsA(config))
@ -197,8 +197,8 @@ fail:
if (rc && (lowSize <= INT_MAX)) if (rc && (lowSize <= INT_MAX))
{ {
int blen = 0; size_t blen = 0;
crypto_base64_decode(fdata, (int)lowSize, (BYTE**)data, &blen); crypto_base64_decode(fdata, lowSize, (BYTE**)data, &blen);
if (*data && (blen > 0)) if (*data && (blen > 0))
*length = (UINT32)blen; *length = (UINT32)blen;

View File

@ -540,7 +540,6 @@ DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT
const char* issuer, const char* fingerprint, DWORD flags) const char* issuer, const char* fingerprint, DWORD flags)
{ {
const char* type = "RDP-Server"; const char* type = "RDP-Server";
if (flags & VERIFY_CERT_FLAG_GATEWAY) if (flags & VERIFY_CERT_FLAG_GATEWAY)
type = "RDP-Gateway"; type = "RDP-Gateway";
@ -551,7 +550,17 @@ DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT
printf("\tCommon Name: %s\n", common_name); printf("\tCommon Name: %s\n", common_name);
printf("\tSubject: %s\n", subject); printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer); printf("\tIssuer: %s\n", issuer);
printf("\tThumbprint: %s\n", fingerprint); /* Newer versions of FreeRDP allow exposing the whole PEM by setting
* FreeRDP_CertificateCallbackPreferPEM to TRUE
*/
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
{
printf("\t----------- Certificate --------------\n");
printf("%s\n", fingerprint);
printf("\t--------------------------------------\n");
}
else
printf("\tThumbprint: %s\n", fingerprint);
printf("The above X.509 certificate could not be verified, possibly because you do not have\n" printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
"the CA certificate in your certificate store, or the certificate has expired.\n" "the CA certificate in your certificate store, or the certificate has expired.\n"
@ -641,12 +650,32 @@ DWORD client_cli_verify_changed_certificate_ex(freerdp* instance, const char* ho
printf("\tCommon Name: %s\n", common_name); printf("\tCommon Name: %s\n", common_name);
printf("\tSubject: %s\n", subject); printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer); printf("\tIssuer: %s\n", issuer);
printf("\tThumbprint: %s\n", fingerprint); /* Newer versions of FreeRDP allow exposing the whole PEM by setting
* FreeRDP_CertificateCallbackPreferPEM to TRUE
*/
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
{
printf("\t----------- Certificate --------------\n");
printf("%s\n", fingerprint);
printf("\t--------------------------------------\n");
}
else
printf("\tThumbprint: %s\n", fingerprint);
printf("\n"); printf("\n");
printf("Old Certificate details:\n"); printf("Old Certificate details:\n");
printf("\tSubject: %s\n", old_subject); printf("\tSubject: %s\n", old_subject);
printf("\tIssuer: %s\n", old_issuer); printf("\tIssuer: %s\n", old_issuer);
printf("\tThumbprint: %s\n", old_fingerprint); /* Newer versions of FreeRDP allow exposing the whole PEM by setting
* FreeRDP_CertificateCallbackPreferPEM to TRUE
*/
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
{
printf("\t----------- Certificate --------------\n");
printf("%s\n", old_fingerprint);
printf("\t--------------------------------------\n");
}
else
printf("\tThumbprint: %s\n", old_fingerprint);
printf("\n"); printf("\n");
if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1) if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
{ {

View File

@ -3116,16 +3116,15 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
CommandLineSwitchCase(arg, "reconnect-cookie") CommandLineSwitchCase(arg, "reconnect-cookie")
{ {
BYTE* base64 = NULL; BYTE* base64 = NULL;
int length; size_t length;
if (!arg->Value) if (!arg->Value)
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
crypto_base64_decode((const char*)(arg->Value), (int)strlen(arg->Value), &base64, crypto_base64_decode((const char*)(arg->Value), strlen(arg->Value), &base64, &length);
&length);
if ((base64 != NULL) && (length == sizeof(ARC_SC_PRIVATE_PACKET))) if ((base64 != NULL) && (length == sizeof(ARC_SC_PRIVATE_PACKET)))
{ {
memcpy(settings->ServerAutoReconnectCookie, base64, (size_t)length); memcpy(settings->ServerAutoReconnectCookie, base64, length);
} }
else else
{ {

View File

@ -32,43 +32,46 @@ typedef struct rdp_certificate_store rdpCertificateStore;
#include <winpr/print.h> #include <winpr/print.h>
#include <winpr/stream.h> #include <winpr/stream.h>
struct rdp_certificate_data
{
char* hostname;
UINT16 port;
char* subject;
char* issuer;
char* fingerprint;
};
struct rdp_certificate_store
{
char* path;
char* file;
rdpSettings* settings;
rdpCertificateData* certificate_data;
};
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
FREERDP_API rdpCertificateData* certificate_data_new(const char* hostname, UINT16 port, FREERDP_API rdpCertificateData* certificate_data_new(const char* hostname, UINT16 port);
const char* subject, const char* issuer,
const char* fingerprint);
FREERDP_API void certificate_data_free(rdpCertificateData* certificate_data); FREERDP_API void certificate_data_free(rdpCertificateData* certificate_data);
FREERDP_API rdpCertificateStore* certificate_store_new(rdpSettings* settings);
FREERDP_API BOOL certificate_data_replace(rdpCertificateStore* certificate_store, FREERDP_API const char* certificate_data_get_host(const rdpCertificateData* cert);
rdpCertificateData* certificate_data); FREERDP_API UINT16 certificate_data_get_port(const rdpCertificateData* cert);
FREERDP_API BOOL certificate_data_set_pem(rdpCertificateData* cert, const char* pem);
FREERDP_API BOOL certificate_data_set_subject(rdpCertificateData* cert, const char* subject);
FREERDP_API BOOL certificate_data_set_issuer(rdpCertificateData* cert, const char* issuer);
FREERDP_API BOOL certificate_data_set_fingerprint(rdpCertificateData* cert,
const char* fingerprint);
FREERDP_API const char* certificate_data_get_pem(const rdpCertificateData* cert);
FREERDP_API const char* certificate_data_get_subject(const rdpCertificateData* cert);
FREERDP_API const char* certificate_data_get_issuer(const rdpCertificateData* cert);
FREERDP_API const char* certificate_data_get_fingerprint(const rdpCertificateData* cert);
FREERDP_API rdpCertificateStore* certificate_store_new(const rdpSettings* settings);
FREERDP_API void certificate_store_free(rdpCertificateStore* certificate_store); FREERDP_API void certificate_store_free(rdpCertificateStore* certificate_store);
FREERDP_API int certificate_data_match(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data); FREERDP_API int certificate_store_contains_data(rdpCertificateStore* certificate_store,
FREERDP_API BOOL certificate_data_print(rdpCertificateStore* certificate_store, const rdpCertificateData* certificate_data);
rdpCertificateData* certificate_data); FREERDP_API rdpCertificateData*
FREERDP_API BOOL certificate_get_stored_data(rdpCertificateStore* certificate_store, certificate_store_load_data(rdpCertificateStore* certificate_store, const char* host,
rdpCertificateData* certificate_data, UINT16 port);
char** subject, char** issuer, char** fingerprint); FREERDP_API BOOL certificate_store_save_data(rdpCertificateStore* certificate_store,
const rdpCertificateData* certificate_data);
FREERDP_API BOOL certificate_store_remove_data(rdpCertificateStore* certificate_store,
const rdpCertificateData* certificate_data);
FREERDP_API const char*
certificate_store_get_hosts_file(const rdpCertificateStore* certificate_store);
FREERDP_API const char*
certificate_store_get_certs_path(const rdpCertificateStore* certificate_store);
FREERDP_API const char*
certificate_store_get_hosts_path(const rdpCertificateStore* certificate_store);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -57,6 +57,8 @@ extern "C"
FREERDP_API BYTE* crypto_cert_hash(X509* xcert, const char* hash, UINT32* length); FREERDP_API BYTE* crypto_cert_hash(X509* xcert, const char* hash, UINT32* length);
FREERDP_API char* crypto_cert_fingerprint_by_hash(X509* xcert, const char* hash); FREERDP_API char* crypto_cert_fingerprint_by_hash(X509* xcert, const char* hash);
FREERDP_API char* crypto_cert_fingerprint(X509* xcert); FREERDP_API char* crypto_cert_fingerprint(X509* xcert);
FREERDP_API BYTE* crypto_cert_pem(X509* xcert, STACK_OF(X509) * chain, size_t* length);
FREERDP_API X509* crypto_cert_from_pem(const char* data, size_t length, BOOL fromFile);
FREERDP_API char* crypto_cert_subject(X509* xcert); FREERDP_API char* crypto_cert_subject(X509* xcert);
FREERDP_API char* crypto_cert_subject_common_name(X509* xcert, int* length); FREERDP_API char* crypto_cert_subject_common_name(X509* xcert, int* length);
FREERDP_API char** crypto_cert_get_dns_names(X509* xcert, int* count, int** lengths); FREERDP_API char** crypto_cert_get_dns_names(X509* xcert, int* count, int** lengths);
@ -102,9 +104,9 @@ extern "C"
BYTE* output); BYTE* output);
FREERDP_API void crypto_reverse(BYTE* data, int length); FREERDP_API void crypto_reverse(BYTE* data, int length);
FREERDP_API char* crypto_base64_encode(const BYTE* data, int length); FREERDP_API char* crypto_base64_encode(const BYTE* data, size_t length);
FREERDP_API void crypto_base64_decode(const char* enc_data, int length, BYTE** dec_data, FREERDP_API void crypto_base64_decode(const char* enc_data, size_t length, BYTE** dec_data,
int* res_length); size_t* res_length);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -70,6 +70,7 @@ extern "C"
#define VERIFY_CERT_FLAG_CHANGED 0x40 #define VERIFY_CERT_FLAG_CHANGED 0x40
#define VERIFY_CERT_FLAG_MISMATCH 0x80 #define VERIFY_CERT_FLAG_MISMATCH 0x80
#define VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1 0x100 #define VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1 0x100
#define VERIFY_CERT_FLAG_FP_IS_PEM 0x200
/* Message types used by gateway messaging callback */ /* Message types used by gateway messaging callback */
#define GATEWAY_MESSAGE_CONSENT 1 #define GATEWAY_MESSAGE_CONSENT 1
@ -91,7 +92,8 @@ extern "C"
* @param common_name The certificate registered hostname. * @param common_name The certificate registered hostname.
* @param subject The common name of the certificate. * @param subject The common name of the certificate.
* @param issuer The issuer of the certificate. * @param issuer The issuer of the certificate.
* @param fingerprint The fingerprint of the certificate. * @param fingerprint The fingerprint of the certificate (old) or the certificate in PEM
* format
* @param host_mismatch A flag indicating the certificate * @param host_mismatch A flag indicating the certificate
* subject does not match the host connecting to. * subject does not match the host connecting to.
* *
@ -110,7 +112,8 @@ extern "C"
* @param common_name The certificate registered hostname. * @param common_name The certificate registered hostname.
* @param subject The common name of the certificate. * @param subject The common name of the certificate.
* @param issuer The issuer of the certificate. * @param issuer The issuer of the certificate.
* @param fingerprint The fingerprint of the certificate. * @param fingerprint The fingerprint of the certificate (old) or the certificate in PEM
* format (VERIFY_CERT_FLAG_FP_IS_PEM set)
* @param flags Flags of type VERIFY_CERT_FLAG* * @param flags Flags of type VERIFY_CERT_FLAG*
* *
* @return 1 to accept and store a certificate, 2 to accept * @return 1 to accept and store a certificate, 2 to accept
@ -149,10 +152,12 @@ extern "C"
* @param common_name The certificate registered hostname. * @param common_name The certificate registered hostname.
* @param subject The common name of the new certificate. * @param subject The common name of the new certificate.
* @param issuer The issuer of the new certificate. * @param issuer The issuer of the new certificate.
* @param fingerprint The fingerprint of the new certificate. * @param fingerprint The fingerprint of the new certificate (old) or the certificate in
* PEM format (VERIFY_CERT_FLAG_FP_IS_PEM set)
* @param old_subject The common name of the old certificate. * @param old_subject The common name of the old certificate.
* @param old_issuer The issuer of the new certificate. * @param old_issuer The issuer of the new certificate.
* @param old_fingerprint The fingerprint of the old certificate. * @param old_fingerprint The fingerprint of the old certificate (old) or the certificate in
* PEM format (VERIFY_CERT_FLAG_FP_IS_PEM set)
* @param flags Flags of type VERIFY_CERT_FLAG* * @param flags Flags of type VERIFY_CERT_FLAG*
* *
* @return 1 to accept and store a certificate, 2 to accept * @return 1 to accept and store a certificate, 2 to accept
@ -169,7 +174,7 @@ extern "C"
* a certificate. * a certificate.
* *
* @param instance Pointer to the freerdp instance. * @param instance Pointer to the freerdp instance.
* @param data Pointer to certificate data in PEM format. * @param data Pointer to certificate data (full chain) in PEM format.
* @param length The length of the certificate data. * @param length The length of the certificate data.
* @param hostname The hostname connecting to. * @param hostname The hostname connecting to.
* @param port The port connecting to. * @param port The port connecting to.

View File

@ -700,6 +700,8 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
#define FreeRDP_AutoAcceptCertificate (1419) #define FreeRDP_AutoAcceptCertificate (1419)
#define FreeRDP_AutoDenyCertificate (1420) #define FreeRDP_AutoDenyCertificate (1420)
#define FreeRDP_CertificateAcceptedFingerprints (1421) #define FreeRDP_CertificateAcceptedFingerprints (1421)
#define FreeRDP_CertificateUseKnownHosts (1422)
#define FreeRDP_CertificateCallbackPreferPEM (1423)
#define FreeRDP_Workarea (1536) #define FreeRDP_Workarea (1536)
#define FreeRDP_Fullscreen (1537) #define FreeRDP_Fullscreen (1537)
#define FreeRDP_PercentScreen (1538) #define FreeRDP_PercentScreen (1538)
@ -1176,7 +1178,9 @@ struct rdp_settings
ALIGN64 BOOL AutoAcceptCertificate; /* 1419 */ ALIGN64 BOOL AutoAcceptCertificate; /* 1419 */
ALIGN64 BOOL AutoDenyCertificate; /* 1420 */ ALIGN64 BOOL AutoDenyCertificate; /* 1420 */
ALIGN64 char* CertificateAcceptedFingerprints; /* 1421 */ ALIGN64 char* CertificateAcceptedFingerprints; /* 1421 */
UINT64 padding1472[1472 - 1422]; /* 1422 */ ALIGN64 BOOL CertificateUseKnownHosts; /* 1422 */
ALIGN64 BOOL CertificateCallbackPreferPEM; /* 1423 */
UINT64 padding1472[1472 - 1424]; /* 1424 */
UINT64 padding1536[1536 - 1472]; /* 1472 */ UINT64 padding1536[1536 - 1472]; /* 1472 */
/** /**

View File

@ -74,6 +74,12 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id)
case FreeRDP_BitmapCompressionDisabled: case FreeRDP_BitmapCompressionDisabled:
return settings->BitmapCompressionDisabled; return settings->BitmapCompressionDisabled;
case FreeRDP_CertificateCallbackPreferPEM:
return settings->CertificateCallbackPreferPEM;
case FreeRDP_CertificateUseKnownHosts:
return settings->CertificateUseKnownHosts;
case FreeRDP_ColorPointerFlag: case FreeRDP_ColorPointerFlag:
return settings->ColorPointerFlag; return settings->ColorPointerFlag;
@ -602,6 +608,14 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val)
settings->BitmapCompressionDisabled = val; settings->BitmapCompressionDisabled = val;
break; break;
case FreeRDP_CertificateCallbackPreferPEM:
settings->CertificateCallbackPreferPEM = val;
break;
case FreeRDP_CertificateUseKnownHosts:
settings->CertificateUseKnownHosts = val;
break;
case FreeRDP_ColorPointerFlag: case FreeRDP_ColorPointerFlag:
settings->ColorPointerFlag = val; settings->ColorPointerFlag = val;
break; break;

View File

@ -34,6 +34,8 @@ static const struct settings_str_entry settings_map[] = {
{ FreeRDP_BitmapCachePersistEnabled, 0, "FreeRDP_BitmapCachePersistEnabled" }, { FreeRDP_BitmapCachePersistEnabled, 0, "FreeRDP_BitmapCachePersistEnabled" },
{ FreeRDP_BitmapCacheV3Enabled, 0, "FreeRDP_BitmapCacheV3Enabled" }, { FreeRDP_BitmapCacheV3Enabled, 0, "FreeRDP_BitmapCacheV3Enabled" },
{ FreeRDP_BitmapCompressionDisabled, 0, "FreeRDP_BitmapCompressionDisabled" }, { FreeRDP_BitmapCompressionDisabled, 0, "FreeRDP_BitmapCompressionDisabled" },
{ FreeRDP_CertificateCallbackPreferPEM, 0, "FreeRDP_CertificateCallbackPreferPEM" },
{ FreeRDP_CertificateUseKnownHosts, 0, "FreeRDP_CertificateUseKnownHosts" },
{ FreeRDP_ColorPointerFlag, 0, "FreeRDP_ColorPointerFlag" }, { FreeRDP_ColorPointerFlag, 0, "FreeRDP_ColorPointerFlag" },
{ FreeRDP_CompressionEnabled, 0, "FreeRDP_CompressionEnabled" }, { FreeRDP_CompressionEnabled, 0, "FreeRDP_CompressionEnabled" },
{ FreeRDP_ConsoleSession, 0, "FreeRDP_ConsoleSession" }, { FreeRDP_ConsoleSession, 0, "FreeRDP_ConsoleSession" },

View File

@ -105,7 +105,7 @@ BOOL rpc_ncacn_http_send_in_channel_request(RpcChannel* inChannel)
BOOL rpc_ncacn_http_recv_in_channel_response(RpcChannel* inChannel, HttpResponse* response) BOOL rpc_ncacn_http_recv_in_channel_response(RpcChannel* inChannel, HttpResponse* response)
{ {
const char* token64 = NULL; const char* token64 = NULL;
int ntlmTokenLength = 0; size_t ntlmTokenLength = 0;
BYTE* ntlmTokenData = NULL; BYTE* ntlmTokenData = NULL;
rdpNtlm* ntlm; rdpNtlm* ntlm;
@ -259,7 +259,7 @@ BOOL rpc_ncacn_http_send_out_channel_request(RpcChannel* outChannel, BOOL replac
BOOL rpc_ncacn_http_recv_out_channel_response(RpcChannel* outChannel, HttpResponse* response) BOOL rpc_ncacn_http_recv_out_channel_response(RpcChannel* outChannel, HttpResponse* response)
{ {
const char* token64 = NULL; const char* token64 = NULL;
int ntlmTokenLength = 0; size_t ntlmTokenLength = 0;
BYTE* ntlmTokenData = NULL; BYTE* ntlmTokenData = NULL;
rdpNtlm* ntlm; rdpNtlm* ntlm;

View File

@ -1181,7 +1181,7 @@ static BOOL rdg_handle_ntlm_challenge(rdpNtlm* ntlm, HttpResponse* response)
BOOL continueNeeded = FALSE; BOOL continueNeeded = FALSE;
size_t len; size_t len;
const char* token64 = NULL; const char* token64 = NULL;
int ntlmTokenLength = 0; size_t ntlmTokenLength = 0;
BYTE* ntlmTokenData = NULL; BYTE* ntlmTokenData = NULL;
long StatusCode; long StatusCode;
@ -1203,16 +1203,7 @@ static BOOL rdg_handle_ntlm_challenge(rdpNtlm* ntlm, HttpResponse* response)
len = strlen(token64); len = strlen(token64);
if (len > INT_MAX) crypto_base64_decode(token64, len, &ntlmTokenData, &ntlmTokenLength);
return FALSE;
crypto_base64_decode(token64, (int)len, &ntlmTokenData, &ntlmTokenLength);
if (ntlmTokenLength < 0)
{
free(ntlmTokenData);
return FALSE;
}
if (ntlmTokenData && ntlmTokenLength) if (ntlmTokenData && ntlmTokenLength)
{ {

View File

@ -381,7 +381,9 @@ rdpSettings* freerdp_settings_new(DWORD flags)
!freerdp_settings_set_bool(settings, FreeRDP_DisableCredentialsDelegation, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_DisableCredentialsDelegation, FALSE) ||
!freerdp_settings_set_uint32(settings, FreeRDP_AuthenticationLevel, 2) || !freerdp_settings_set_uint32(settings, FreeRDP_AuthenticationLevel, 2) ||
!freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, 0) || !freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, 0) ||
!freerdp_settings_set_uint32(settings, FreeRDP_ChannelDefArraySize, 32)) !freerdp_settings_set_uint32(settings, FreeRDP_ChannelDefArraySize, 32) ||
!freerdp_settings_set_bool(settings, FreeRDP_CertificateUseKnownHosts, TRUE) ||
!freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, FALSE))
goto out_fail; goto out_fail;
settings->ChannelDefArray = (CHANNEL_DEF*)calloc( settings->ChannelDefArray = (CHANNEL_DEF*)calloc(

View File

@ -23,6 +23,8 @@ static const size_t bool_list_indices[] = {
FreeRDP_BitmapCachePersistEnabled, FreeRDP_BitmapCachePersistEnabled,
FreeRDP_BitmapCacheV3Enabled, FreeRDP_BitmapCacheV3Enabled,
FreeRDP_BitmapCompressionDisabled, FreeRDP_BitmapCompressionDisabled,
FreeRDP_CertificateCallbackPreferPEM,
FreeRDP_CertificateUseKnownHosts,
FreeRDP_ColorPointerFlag, FreeRDP_ColorPointerFlag,
FreeRDP_CompressionEnabled, FreeRDP_CompressionEnabled,
FreeRDP_ConsoleSession, FreeRDP_ConsoleSession,

View File

@ -27,7 +27,7 @@
static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char* crypto_base64_encode(const BYTE* data, int length) char* crypto_base64_encode(const BYTE* data, size_t length)
{ {
int c; int c;
const BYTE* q; const BYTE* q;
@ -114,12 +114,12 @@ static int base64_decode_char(char c)
return -1; return -1;
} }
static void* base64_decode(const char* s, int length, int* data_len) static void* base64_decode(const char* s, size_t length, size_t* data_len)
{ {
int n[4]; int n[4];
BYTE* q; BYTE* q;
BYTE* data; BYTE* data;
int nBlocks, i, outputLen; size_t nBlocks, i, outputLen;
if (length % 4) if (length % 4)
return NULL; return NULL;
@ -132,6 +132,12 @@ static void* base64_decode(const char* s, int length, int* data_len)
nBlocks = (length / 4); nBlocks = (length / 4);
outputLen = 0; outputLen = 0;
if (nBlocks < 1)
{
free(data);
return NULL;
}
for (i = 0; i < nBlocks - 1; i++, q += 3) for (i = 0; i < nBlocks - 1; i++, q += 3)
{ {
n[0] = base64_decode_char(*s++); n[0] = base64_decode_char(*s++);
@ -192,7 +198,7 @@ out_free:
return NULL; return NULL;
} }
void crypto_base64_decode(const char* enc_data, int length, BYTE** dec_data, int* res_length) void crypto_base64_decode(const char* enc_data, size_t length, BYTE** dec_data, size_t* res_length)
{ {
*dec_data = base64_decode(enc_data, length, res_length); *dec_data = base64_decode(enc_data, length, res_length);
} }

File diff suppressed because it is too large Load Diff

View File

@ -250,19 +250,27 @@ BYTE* crypto_cert_hash(X509* xcert, const char* hash, UINT32* length)
BYTE* fp; BYTE* fp;
const EVP_MD* md = EVP_get_digestbyname(hash); const EVP_MD* md = EVP_get_digestbyname(hash);
if (!md) if (!md)
{
WLog_ERR(TAG, "System does not support %s hash!", hash);
return NULL; return NULL;
if (!length) }
return NULL; if (!xcert || !length)
if (!xcert) {
WLog_ERR(TAG, "[%s] Invalid arugments: xcert=%p, length=%p", __FUNCTION__, xcert, length);
return NULL; return NULL;
}
fp = calloc(fp_len, sizeof(BYTE)); fp = calloc(fp_len, sizeof(BYTE));
if (!fp) if (!fp)
{
WLog_ERR(TAG, "[%s] could not allocate %" PRIuz " bytes", __FUNCTION__, fp_len);
return NULL; return NULL;
}
if (X509_digest(xcert, md, fp, &fp_len) != 1) if (X509_digest(xcert, md, fp, &fp_len) != 1)
{ {
free(fp); free(fp);
WLog_ERR(TAG, "certificate does not have a %s hash!", hash);
return NULL; return NULL;
} }
@ -276,7 +284,16 @@ char* crypto_cert_fingerprint_by_hash(X509* xcert, const char* hash)
BYTE* fp; BYTE* fp;
char* p; char* p;
char* fp_buffer; char* fp_buffer;
if (!xcert)
{
WLog_ERR(TAG, "Invalid certificate %p", xcert);
return NULL;
}
if (!hash)
{
WLog_ERR(TAG, "Invalid certificate hash %p", hash);
return NULL;
}
fp = crypto_cert_hash(xcert, hash, &fp_len); fp = crypto_cert_hash(xcert, hash, &fp_len);
if (!fp) if (!fp)
return NULL; return NULL;
@ -322,7 +339,16 @@ static char* crypto_print_name(X509_NAME* name)
char* crypto_cert_subject(X509* xcert) char* crypto_cert_subject(X509* xcert)
{ {
return crypto_print_name(X509_get_subject_name(xcert)); char* subject;
if (!xcert)
{
WLog_ERR(TAG, "Invalid certificate %p", xcert);
return NULL;
}
subject = crypto_print_name(X509_get_subject_name(xcert));
if (!subject)
WLog_ERR(TAG, "certificate does not have a subject!");
return subject;
} }
char* crypto_cert_subject_common_name(X509* xcert, int* length) char* crypto_cert_subject_common_name(X509* xcert, int* length)
@ -799,7 +825,16 @@ char** crypto_cert_get_dns_names(X509* x509, int* count, int** lengths)
char* crypto_cert_issuer(X509* xcert) char* crypto_cert_issuer(X509* xcert)
{ {
return crypto_print_name(X509_get_issuer_name(xcert)); char* issuer;
if (!xcert)
{
WLog_ERR(TAG, "Invalid certificate %p", xcert);
return NULL;
}
issuer = crypto_print_name(X509_get_issuer_name(xcert));
if (!issuer)
WLog_ERR(TAG, "certificate does not have an issuer!");
return issuer;
} }
static int verify_cb(int ok, X509_STORE_CTX* csc) static int verify_cb(int ok, X509_STORE_CTX* csc)
@ -893,22 +928,24 @@ end:
rdpCertificateData* crypto_get_certificate_data(X509* xcert, const char* hostname, UINT16 port) rdpCertificateData* crypto_get_certificate_data(X509* xcert, const char* hostname, UINT16 port)
{ {
char* issuer; char* pem = NULL;
char* subject; size_t length;
char* fp; rdpCertificateData* certdata = NULL;
rdpCertificateData* certdata;
fp = crypto_cert_fingerprint(xcert);
if (!fp) pem = (char*)crypto_cert_pem(xcert, NULL, &length);
return NULL; if (!pem)
goto fail;
issuer = crypto_cert_issuer(xcert); certdata = certificate_data_new(hostname, port);
subject = crypto_cert_subject(xcert); if (!certdata)
certdata = certificate_data_new(hostname, port, issuer, subject, fp); goto fail;
free(subject); if (!certificate_data_set_pem(certdata, pem))
free(issuer); goto fail;
free(fp); free(pem);
return certdata; return certdata;
fail:
certificate_data_free(certdata);
free(pem);
return NULL;
} }
void crypto_cert_print_info(X509* xcert) void crypto_cert_print_info(X509* xcert)
@ -939,3 +976,135 @@ out_free_issuer:
free(issuer); free(issuer);
free(subject); free(subject);
} }
BYTE* crypto_cert_pem(X509* xcert, STACK_OF(X509) * chain, size_t* plength)
{
BIO* bio;
int status, count, x;
size_t offset;
size_t length = 0;
BOOL rc = FALSE;
BYTE* pemCert = NULL;
if (!xcert || !plength)
return NULL;
/**
* Don't manage certificates internally, leave it up entirely to the external client
* implementation
*/
bio = BIO_new(BIO_s_mem());
if (!bio)
{
WLog_ERR(TAG, "BIO_new() failure");
return NULL;
}
status = PEM_write_bio_X509(bio, xcert);
if (status < 0)
{
WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status);
goto fail;
}
if (chain)
{
count = sk_X509_num(chain);
for (x = 0; x < count; x++)
{
X509* c = sk_X509_value(chain, x);
status = PEM_write_bio_X509(bio, c);
if (status < 0)
{
WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status);
goto fail;
}
}
}
offset = 0;
length = 2048;
pemCert = (BYTE*)malloc(length + 1);
if (!pemCert)
{
WLog_ERR(TAG, "error allocating pemCert");
goto fail;
}
status = BIO_read(bio, pemCert, length);
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
goto fail;
}
offset += (size_t)status;
while (offset >= length)
{
int new_len;
BYTE* new_cert;
new_len = length * 2;
new_cert = (BYTE*)realloc(pemCert, new_len + 1);
if (!new_cert)
goto fail;
length = new_len;
pemCert = new_cert;
status = BIO_read(bio, &pemCert[offset], length - offset);
if (status < 0)
break;
offset += status;
}
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
goto fail;
}
length = offset;
pemCert[length] = '\0';
*plength = length;
rc = TRUE;
fail:
if (!rc)
{
free(pemCert);
pemCert = NULL;
}
BIO_free_all(bio);
return pemCert;
}
X509* crypto_cert_from_pem(const char* data, size_t len, BOOL fromFile)
{
X509* x509 = NULL;
BIO* bio;
if (fromFile)
bio = BIO_new_file(data, "rb");
else
bio = BIO_new_mem_buf(data, len);
if (!bio)
{
WLog_ERR(TAG, "BIO_new failed for certificate");
return NULL;
}
x509 = PEM_read_bio_X509(bio, NULL, NULL, 0);
BIO_free_all(bio);
if (!x509)
WLog_ERR(TAG, "PEM_read_bio_X509 returned NULL [input length %" PRIuz "]", len);
return x509;
}

View File

@ -22,7 +22,7 @@
struct Encode64test struct Encode64test
{ {
const char* input; const char* input;
int len; size_t len;
const char* output; const char* output;
}; };
@ -41,7 +41,7 @@ static const struct Encode64test encodeTests[] = {
int TestBase64(int argc, char* argv[]) int TestBase64(int argc, char* argv[])
{ {
int i, testNb = 0; int i, testNb = 0;
int outLen; size_t outLen;
BYTE* decoded; BYTE* decoded;
WINPR_UNUSED(argc); WINPR_UNUSED(argc);
WINPR_UNUSED(argv); WINPR_UNUSED(argv);

View File

@ -23,11 +23,104 @@
#include <freerdp/crypto/certificate.h> #include <freerdp/crypto/certificate.h>
/* Some certificates copied from /usr/share/ca-certificates */
static const char pem1[] = "-----BEGIN CERTIFICATE-----\n"
"MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH\n"
"MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\n"
"QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy\n"
"MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl\n"
"cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB\n"
"AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM\n"
"f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX\n"
"mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7\n"
"zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P\n"
"fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc\n"
"vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4\n"
"Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp\n"
"zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO\n"
"Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW\n"
"k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+\n"
"DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF\n"
"lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n"
"HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW\n"
"Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1\n"
"d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z\n"
"XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR\n"
"gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3\n"
"d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv\n"
"J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg\n"
"DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM\n"
"+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy\n"
"F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9\n"
"SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws\n"
"E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl\n"
"-----END CERTIFICATE-----";
static const char pem2[] = "-----BEGIN CERTIFICATE-----\n"
"MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH\n"
"MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\n"
"QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy\n"
"MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl\n"
"cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB\n"
"AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv\n"
"CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg\n"
"GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu\n"
"XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd\n"
"re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu\n"
"PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1\n"
"mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K\n"
"8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj\n"
"x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR\n"
"nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0\n"
"kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok\n"
"twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n"
"HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp\n"
"8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT\n"
"vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT\n"
"z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA\n"
"pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb\n"
"pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB\n"
"R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R\n"
"RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk\n"
"0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC\n"
"5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF\n"
"izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn\n"
"yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC\n"
"-----END CERTIFICATE-----";
static const char pem3[] = "-----BEGIN CERTIFICATE-----\n"
"MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw\n"
"CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n"
"MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n"
"MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n"
"Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA\n"
"IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout\n"
"736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A\n"
"DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"
"DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk\n"
"fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA\n"
"njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd\n"
"-----END CERTIFICATE-----";
static const char pem4[] = "-----BEGIN CERTIFICATE-----\n"
"MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw\n"
"CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n"
"MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n"
"MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n"
"Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA\n"
"IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu\n"
"hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l\n"
"xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"
"DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0\n"
"CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx\n"
"sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w==\n"
"-----END CERTIFICATE-----";
static int prepare(const char* currentFileV2) static int prepare(const char* currentFileV2)
{ {
int rc = -1; int rc = -1;
const char* hosts[] = { "#somecomment\r\n" const char* hosts[] = { "#somecomment\r\n"
"someurl 3389 ff:11:22:dd subject issuer\r\n" "someurl 3389 ff:11:22:dd c3ViamVjdA== aXNzdWVy\r\n"
" \t#anothercomment\r\n" " \t#anothercomment\r\n"
"otherurl\t3389\taa:bb:cc:dd\tsubject2\tissuer2\r" }; "otherurl\t3389\taa:bb:cc:dd\tsubject2\tissuer2\r" };
FILE* fc = NULL; FILE* fc = NULL;
@ -52,21 +145,19 @@ finish:
return rc; return rc;
} }
int TestKnownHosts(int argc, char* argv[]) static BOOL setup_config(rdpSettings** settings)
{ {
int rc = -1; BOOL rc = FALSE;
rdpSettings current; char* path = NULL;
rdpCertificateData* data = NULL;
rdpCertificateStore* store = NULL;
char* currentFileV2 = NULL;
char* subject = NULL;
char* issuer = NULL;
char* fp = NULL;
char sname[8192]; char sname[8192];
SYSTEMTIME systemTime; SYSTEMTIME systemTime;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv); if (!settings)
goto fail;
*settings = freerdp_settings_new(0);
if (!*settings)
goto fail;
GetSystemTime(&systemTime); GetSystemTime(&systemTime);
sprintf_s(sname, sizeof(sname), sprintf_s(sname, sizeof(sname),
"TestKnownHostsCurrent-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16 "TestKnownHostsCurrent-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16
@ -74,18 +165,237 @@ int TestKnownHosts(int argc, char* argv[])
systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds); systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
current.ConfigPath = GetKnownSubPath(KNOWN_PATH_TEMP, sname); path = GetKnownSubPath(KNOWN_PATH_TEMP, sname);
if (!path)
if (!PathFileExistsA(current.ConfigPath)) goto fail;
if (!PathFileExistsA(path))
{ {
if (!CreateDirectoryA(current.ConfigPath, NULL)) if (!CreateDirectoryA(path, NULL))
{ {
fprintf(stderr, "Could not create %s!\n", current.ConfigPath); fprintf(stderr, "Could not create %s!\n", path);
goto finish; goto fail;
} }
} }
currentFileV2 = GetCombinedPath(current.ConfigPath, "known_hosts2"); rc = freerdp_settings_set_string(*settings, FreeRDP_ConfigPath, path);
fail:
free(path);
return rc;
}
/* Test if host is found in current file. */
static BOOL test_known_hosts_host_found(rdpCertificateStore* store)
{
BOOL rc = FALSE;
rdpCertificateData* stored_data = NULL;
rdpCertificateData* data;
printf("%s\n", __FUNCTION__);
data = certificate_data_new("someurl", 3389);
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_set_subject(data, "subject") ||
!certificate_data_set_issuer(data, "issuer") ||
!certificate_data_set_fingerprint(data, "ff:11:22:dd"))
goto finish;
if (0 != certificate_store_contains_data(store, data))
{
fprintf(stderr, "Could not find data in v2 file!\n");
goto finish;
}
/* Test if we can read out the old fingerprint. */
stored_data = certificate_store_load_data(store, certificate_data_get_host(data),
certificate_data_get_port(data));
if (!stored_data)
{
fprintf(stderr, "Could not read old fingerprint!\n");
goto finish;
}
printf("Got %s, %s '%s'\n", certificate_data_get_subject(stored_data),
certificate_data_get_issuer(stored_data), certificate_data_get_fingerprint(stored_data));
rc = TRUE;
finish:
printf("certificate_data_free %d\n", rc);
certificate_data_free(data);
certificate_data_free(stored_data);
return rc;
}
/* Test if host not found in current file. */
static BOOL test_known_hosts_host_not_found(rdpCertificateStore* store)
{
BOOL rc = FALSE;
rdpCertificateData* stored_data = NULL;
rdpCertificateData* data;
printf("%s\n", __FUNCTION__);
data = certificate_data_new("somehost", 1234);
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_set_fingerprint(data, "ff:aa:bb:cc"))
goto finish;
if (0 == certificate_store_contains_data(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
/* Test if we read out the old fingerprint fails. */
stored_data = certificate_store_load_data(store, certificate_data_get_host(data),
certificate_data_get_port(data));
if (stored_data)
{
fprintf(stderr, "Read out not existing old fingerprint succeeded?!\n");
goto finish;
}
rc = TRUE;
finish:
printf("certificate_data_free %d\n", rc);
certificate_data_free(data);
certificate_data_free(stored_data);
return rc;
}
/* Test host add current file. */
static BOOL test_known_hosts_host_add(rdpCertificateStore* store)
{
BOOL rc = FALSE;
rdpCertificateData* data;
printf("%s\n", __FUNCTION__);
data = certificate_data_new("somehost", 1234);
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_set_subject(data, "ff:aa:bb:cc") ||
!certificate_data_set_issuer(data, "ff:aa:bb:cc") ||
!certificate_data_set_fingerprint(data, "ff:aa:bb:cc"))
goto finish;
if (!certificate_store_save_data(store, data))
{
fprintf(stderr, "Could not add host to file!\n");
goto finish;
}
if (0 != certificate_store_contains_data(store, data))
{
fprintf(stderr, "Could not find host written in v2 file!\n");
goto finish;
}
rc = TRUE;
finish:
printf("certificate_data_free %d\n", rc);
certificate_data_free(data);
return rc;
}
/* Test host replace current file. */
static BOOL test_known_hosts_host_replace(rdpCertificateStore* store)
{
BOOL rc = FALSE;
rdpCertificateData* data;
printf("%s\n", __FUNCTION__);
data = certificate_data_new("somehost", 1234);
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_set_subject(data, "ff:aa:xx:cc") ||
!certificate_data_set_issuer(data, "ff:aa:bb:ee") ||
!certificate_data_set_fingerprint(data, "ff:aa:bb:dd:ee"))
goto finish;
if (!certificate_store_save_data(store, data))
{
fprintf(stderr, "Could not replace data!\n");
goto finish;
}
if (0 != certificate_store_contains_data(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
rc = TRUE;
finish:
printf("certificate_data_free %d\n", rc);
certificate_data_free(data);
return rc;
}
/* Test host replace invalid entry in current file. */
static BOOL test_known_hosts_host_replace_invalid(rdpCertificateStore* store)
{
BOOL rc = FALSE;
rdpCertificateData* data;
printf("%s\n", __FUNCTION__);
data = certificate_data_new("somehostXXXX", 1234);
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_set_fingerprint(data, "ff:aa:bb:dd:ee"))
goto finish;
if (certificate_store_save_data(store, data))
{
fprintf(stderr, "Invalid return for replace invalid entry!\n");
goto finish;
}
if (0 == certificate_store_contains_data(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
rc = TRUE;
finish:
printf("certificate_data_free %d\n", rc);
certificate_data_free(data);
return rc;
}
static BOOL test_known_hosts_file(void)
{
BOOL rc = FALSE;
rdpSettings* settings = NULL;
rdpCertificateStore* store = NULL;
char* currentFileV2 = NULL;
printf("%s", __FUNCTION__);
if (!setup_config(&settings))
goto finish;
if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateUseKnownHosts, TRUE))
goto finish;
currentFileV2 =
GetCombinedPath(freerdp_settings_get_string(settings, FreeRDP_ConfigPath), "known_hosts2");
if (!currentFileV2) if (!currentFileV2)
{ {
@ -93,7 +403,8 @@ int TestKnownHosts(int argc, char* argv[])
goto finish; goto finish;
} }
store = certificate_store_new(&current); printf("certificate_store_new\n");
store = certificate_store_new(settings);
if (!store) if (!store)
{ {
@ -104,139 +415,276 @@ int TestKnownHosts(int argc, char* argv[])
if (prepare(currentFileV2)) if (prepare(currentFileV2))
goto finish; goto finish;
/* Test if host is found in current file. */ if (!test_known_hosts_host_found(store))
data = certificate_data_new("someurl", 3389, "subject", "issuer", "ff:11:22:dd");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish; goto finish;
}
if (0 != certificate_data_match(store, data)) if (!test_known_hosts_host_not_found(store))
{
fprintf(stderr, "Could not find data in v2 file!\n");
goto finish; goto finish;
}
/* Test if we can read out the old fingerprint. */ if (!test_known_hosts_host_add(store))
if (!certificate_get_stored_data(store, data, &subject, &issuer, &fp))
{
fprintf(stderr, "Could not read old fingerprint!\n");
goto finish; goto finish;
}
printf("Got %s, %s '%s'\n", subject, issuer, fp); if (!test_known_hosts_host_replace(store))
free(subject);
free(issuer);
free(fp);
subject = NULL;
issuer = NULL;
fp = NULL;
certificate_data_free(data);
/* Test if host not found in current file. */
data = certificate_data_new("somehost", 1234, "", "", "ff:aa:bb:cc");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish; goto finish;
}
if (0 == certificate_data_match(store, data)) if (!test_known_hosts_host_replace_invalid(store))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish; goto finish;
}
/* Test if we read out the old fingerprint fails. */ rc = TRUE;
if (certificate_get_stored_data(store, data, &subject, &issuer, &fp))
{
fprintf(stderr, "Read out not existing old fingerprint succeeded?!\n");
goto finish;
}
certificate_data_free(data);
/* Test host add current file. */
data = certificate_data_new("somehost", 1234, "", "", "ff:aa:bb:cc");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_print(store, data))
{
fprintf(stderr, "Could not add host to file!\n");
goto finish;
}
if (0 != certificate_data_match(store, data))
{
fprintf(stderr, "Could not find host written in v2 file!\n");
goto finish;
}
certificate_data_free(data);
/* Test host replace current file. */
data = certificate_data_new("somehost", 1234, "", "", "ff:aa:bb:dd:ee");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_replace(store, data))
{
fprintf(stderr, "Could not replace data!\n");
goto finish;
}
if (0 != certificate_data_match(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
certificate_data_free(data);
/* Test host replace invalid entry in current file. */
data = certificate_data_new("somehostXXXX", 1234, "", "", "ff:aa:bb:dd:ee");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (certificate_data_replace(store, data))
{
fprintf(stderr, "Invalid return for replace invalid entry!\n");
goto finish;
}
if (0 == certificate_data_match(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
rc = 0;
finish: finish:
free(current.ConfigPath); freerdp_settings_free(settings);
if (store) printf("certificate_store_free\n");
certificate_store_free(store); certificate_store_free(store);
if (data)
certificate_data_free(data);
DeleteFileA(currentFileV2); DeleteFileA(currentFileV2);
// RemoveDirectoryA(current.ConfigPath);
free(currentFileV2); free(currentFileV2);
free(subject);
free(issuer);
free(fp);
return rc; return rc;
} }
static BOOL equal(const char* a, const char* b)
{
if (!a && !b)
return TRUE;
if (!a || !b)
return FALSE;
return strcmp(a, b) == 0;
}
static BOOL compare(const rdpCertificateData* data, const rdpCertificateData* stored)
{
if (!data || !stored)
return FALSE;
if (!equal(certificate_data_get_subject(data), certificate_data_get_subject(stored)))
return FALSE;
if (!equal(certificate_data_get_issuer(data), certificate_data_get_issuer(stored)))
return FALSE;
if (!equal(certificate_data_get_fingerprint(data), certificate_data_get_fingerprint(stored)))
return FALSE;
return TRUE;
}
static BOOL pem_equal(const char* a, const char* b)
{
BOOL rc = FALSE;
size_t sa = strlen(a);
size_t sb = strlen(b);
X509* x1 = crypto_cert_from_pem(a, sa, FALSE);
X509* x2 = crypto_cert_from_pem(b, sb, FALSE);
char* f1 = NULL;
char* f2 = NULL;
if (!x1 || !x2)
goto fail;
f1 = crypto_cert_fingerprint(x1);
f2 = crypto_cert_fingerprint(x1);
if (!f1 || !f2)
goto fail;
rc = strcmp(f1, f2) == 0;
fail:
free(f1);
free(f2);
X509_free(x1);
X509_free(x2);
return rc;
}
static BOOL compare_ex(const rdpCertificateData* data, const rdpCertificateData* stored)
{
if (!compare(data, stored))
return FALSE;
if (!pem_equal(certificate_data_get_pem(data), certificate_data_get_pem(stored)))
return FALSE;
return TRUE;
}
static BOOL test_get_data(rdpCertificateStore* store, const rdpCertificateData* data)
{
BOOL res;
rdpCertificateData* stored = certificate_store_load_data(store, certificate_data_get_host(data),
certificate_data_get_port(data));
if (!stored)
return FALSE;
res = compare(data, stored);
certificate_data_free(stored);
return res;
}
static BOOL test_get_data_ex(rdpCertificateStore* store, const rdpCertificateData* data)
{
BOOL res;
rdpCertificateData* stored = certificate_store_load_data(store, certificate_data_get_host(data),
certificate_data_get_port(data));
if (!stored)
return FALSE;
res = compare_ex(data, stored);
certificate_data_free(stored);
return res;
}
static BOOL test_certs_dir(BOOL useHostsFile)
{
BOOL rc = FALSE;
rdpSettings* settings = NULL;
rdpCertificateStore* store = NULL;
rdpCertificateData* data1 = NULL;
rdpCertificateData* data2 = NULL;
rdpCertificateData* data3 = NULL;
rdpCertificateData* data4 = NULL;
printf("%s %d\n", __FUNCTION__, useHostsFile);
if (!setup_config(&settings))
goto fail;
/* Initialize certificate folder backend */
if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateUseKnownHosts, useHostsFile))
goto fail;
printf("certificate_store_new()\n");
store = certificate_store_new(settings);
if (!store)
goto fail;
{
printf("certificate_data_new()\n");
data1 = certificate_data_new("somehost", 1234);
data2 = certificate_data_new("otherhost", 4321);
data3 = certificate_data_new("otherhost4", 444);
data4 = certificate_data_new("otherhost", 4321);
if (!data1 || !data2 || !data3 || !data4)
goto fail;
printf("certificate_data_set_pem(1 [%" PRIuz "])\n", strlen(pem1));
if (!certificate_data_set_pem(data1, pem1))
goto fail;
printf("certificate_data_set_pem(2 [%" PRIuz "])\n", strlen(pem2));
if (!certificate_data_set_pem(data2, pem2))
goto fail;
printf("certificate_data_set_pem(3 [%" PRIuz "])\n", strlen(pem3));
if (!certificate_data_set_pem(data3, pem3))
goto fail;
printf("certificate_data_set_pem(4 [%" PRIuz "])\n", strlen(pem4));
if (!certificate_data_set_pem(data4, pem4))
goto fail;
/* Find non existing in empty store */
printf("certificate_store_load_data on empty store\n");
if (test_get_data(store, data1))
goto fail;
if (test_get_data_ex(store, data1))
goto fail;
if (test_get_data(store, data2))
goto fail;
if (test_get_data_ex(store, data2))
goto fail;
if (test_get_data(store, data3))
goto fail;
if (test_get_data_ex(store, data3))
goto fail;
/* Add certificates */
printf("certificate_store_save_data\n");
if (!certificate_store_save_data(store, data1))
goto fail;
if (!certificate_store_save_data(store, data2))
goto fail;
/* Find non existing in non empty store */
printf("certificate_store_load_data on filled store, non existing value\n");
if (test_get_data(store, data3))
goto fail;
if (test_get_data_ex(store, data3))
goto fail;
/* Add remaining certs */
printf("certificate_store_save_data\n");
if (!certificate_store_save_data(store, data3))
goto fail;
/* Check existing can all be found */
printf("certificate_store_load_data on filled store, existing value\n");
if (!test_get_data(store, data1))
goto fail;
if (!test_get_data_ex(store, data1))
goto fail;
if (!test_get_data(store, data2))
goto fail;
if (!test_get_data_ex(store, data2))
goto fail;
if (!test_get_data(store, data3))
goto fail;
if (!test_get_data_ex(store, data3))
goto fail;
/* Modify existing entry */
printf("certificate_store_save_data modify data\n");
if (!certificate_store_save_data(store, data4))
goto fail;
/* Check new data is in store */
printf("certificate_store_load_data check modified data can be loaded\n");
if (!test_get_data(store, data4))
goto fail;
if (!test_get_data_ex(store, data4))
goto fail;
/* Check old data is no longer valid */
printf("certificate_store_load_data check original data no longer there\n");
if (test_get_data(store, data2))
goto fail;
if (test_get_data_ex(store, data2))
goto fail;
/* Delete a cert */
printf("certificate_store_remove_data\n");
if (!certificate_store_remove_data(store, data3))
goto fail;
/* Delete non existing, should succeed */
printf("certificate_store_remove_data missing value\n");
if (!certificate_store_remove_data(store, data3))
goto fail;
printf("certificate_store_load_data on filled store, existing value\n");
if (!test_get_data(store, data1))
goto fail;
if (!test_get_data_ex(store, data1))
goto fail;
if (!test_get_data(store, data4))
goto fail;
if (!test_get_data_ex(store, data4))
goto fail;
printf("certificate_store_load_data on filled store, removed value\n");
if (test_get_data(store, data3))
goto fail;
if (test_get_data_ex(store, data3))
goto fail;
}
rc = TRUE;
fail:
printf("certificate_data_free %d\n", rc);
certificate_data_free(data1);
certificate_data_free(data2);
certificate_data_free(data3);
certificate_data_free(data4);
certificate_store_free(store);
freerdp_settings_free(settings);
return rc;
}
int TestKnownHosts(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!test_known_hosts_file())
return -1;
if (!test_certs_dir(FALSE))
return -1;
if (!test_certs_dir(TRUE))
return -1;
return 0;
}

View File

@ -970,34 +970,17 @@ BOOL tls_accept(rdpTls* tls, BIO* underlying, rdpSettings* settings)
} }
if (settings->CertificateFile) if (settings->CertificateFile)
{ x509 = crypto_cert_from_pem(settings->CertificateFile, strlen(settings->CertificateFile),
bio = BIO_new_file(settings->CertificateFile, "rb"); TRUE);
if (!bio)
{
WLog_ERR(TAG, "BIO_new_file failed for certificate %s", settings->CertificateFile);
return FALSE;
}
}
else if (settings->CertificateContent) else if (settings->CertificateContent)
{ x509 = crypto_cert_from_pem(settings->CertificateContent,
bio = BIO_new_mem_buf(settings->CertificateContent, strlen(settings->CertificateContent)); strlen(settings->CertificateContent), FALSE);
if (!bio)
{
WLog_ERR(TAG, "BIO_new_mem_buf failed for certificate");
return FALSE;
}
}
else else
{ {
WLog_ERR(TAG, "no certificate defined"); WLog_ERR(TAG, "no certificate defined");
return FALSE; return FALSE;
} }
x509 = PEM_read_bio_X509(bio, NULL, NULL, 0);
BIO_free_all(bio);
if (!x509) if (!x509)
{ {
WLog_ERR(TAG, "invalid certificate"); WLog_ERR(TAG, "invalid certificate");
@ -1260,120 +1243,19 @@ static BOOL accept_cert(rdpTls* tls, const BYTE* pem, UINT32 length)
return TRUE; return TRUE;
} }
static BOOL tls_extract_pem(CryptoCert cert, BYTE** PublicKey, DWORD* PublicKeyLength) static BOOL tls_extract_pem(CryptoCert cert, BYTE** PublicKey, size_t* PublicKeyLength)
{ {
BIO* bio; if (!cert || !PublicKey)
int status, count, x;
size_t offset;
size_t length = 0;
BOOL rc = FALSE;
BYTE* pemCert = NULL;
if (!PublicKey || !PublicKeyLength)
return FALSE; return FALSE;
*PublicKey = crypto_cert_pem(cert->px509, cert->px509chain, PublicKeyLength);
*PublicKey = NULL; return *PublicKey != NULL;
*PublicKeyLength = 0;
/**
* Don't manage certificates internally, leave it up entirely to the external client
* implementation
*/
bio = BIO_new(BIO_s_mem());
if (!bio)
{
WLog_ERR(TAG, "BIO_new() failure");
return FALSE;
}
status = PEM_write_bio_X509(bio, cert->px509);
if (status < 0)
{
WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status);
goto fail;
}
if (cert->px509chain)
{
count = sk_X509_num(cert->px509chain);
for (x = 0; x < count; x++)
{
X509* c = sk_X509_value(cert->px509chain, x);
status = PEM_write_bio_X509(bio, c);
if (status < 0)
{
WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status);
goto fail;
}
}
}
offset = 0;
length = 2048;
pemCert = (BYTE*)malloc(length + 1);
if (!pemCert)
{
WLog_ERR(TAG, "error allocating pemCert");
goto fail;
}
status = BIO_read(bio, pemCert, length);
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
goto fail;
}
offset += (size_t)status;
while (offset >= length)
{
int new_len;
BYTE* new_cert;
new_len = length * 2;
new_cert = (BYTE*)realloc(pemCert, new_len + 1);
if (!new_cert)
goto fail;
length = new_len;
pemCert = new_cert;
status = BIO_read(bio, &pemCert[offset], length - offset);
if (status < 0)
break;
offset += status;
}
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
goto fail;
}
length = offset;
pemCert[length] = '\0';
*PublicKey = pemCert;
*PublicKeyLength = length;
rc = TRUE;
fail:
if (!rc)
free(pemCert);
BIO_free_all(bio);
return rc;
} }
int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port) int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port)
{ {
int match; int match;
int index; int index;
DWORD length; size_t length;
BOOL certificate_status; BOOL certificate_status;
char* common_name = NULL; char* common_name = NULL;
int common_name_length = 0; int common_name_length = 0;
@ -1445,7 +1327,8 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
hostname = tls->settings->CertificateName; hostname = tls->settings->CertificateName;
/* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */ /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */
certificate_status = x509_verify_certificate(cert, tls->certificate_store->path); certificate_status =
x509_verify_certificate(cert, certificate_store_get_certs_path(tls->certificate_store));
/* verify certificate name match */ /* verify certificate name match */
certificate_data = crypto_get_certificate_data(cert->px509, hostname, port); certificate_data = crypto_get_certificate_data(cert->px509, hostname, port);
/* extra common name and alternative names */ /* extra common name and alternative names */
@ -1486,31 +1369,17 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
* manual verification */ * manual verification */
if (!certificate_status || !hostname_match) if (!certificate_status || !hostname_match)
{ {
char* issuer;
char* subject;
char* fingerprint;
DWORD accept_certificate = 0; DWORD accept_certificate = 0;
issuer = crypto_cert_issuer(cert->px509); size_t length = 0;
subject = crypto_cert_subject(cert->px509); char* issuer = crypto_cert_issuer(cert->px509);
fingerprint = crypto_cert_fingerprint(cert->px509); char* subject = crypto_cert_subject(cert->px509);
char* pem = crypto_cert_pem(cert->px509, NULL, &length);
if (!pem)
goto end;
/* search for matching entry in known_hosts file */ /* search for matching entry in known_hosts file */
match = certificate_data_match(tls->certificate_store, certificate_data); match = certificate_store_contains_data(tls->certificate_store, certificate_data);
{
int match_old = -1;
char* sha1 = crypto_cert_fingerprint_by_hash(cert->px509, "sha1");
rdpCertificateData* certificate_data_sha1 =
certificate_data_new(hostname, port, subject, issuer, sha1);
if (sha1 && certificate_data_sha1)
match_old =
certificate_data_match(tls->certificate_store, certificate_data_sha1);
if (match_old == 0)
flags |= VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1;
certificate_data_free(certificate_data_sha1);
free(sha1);
}
if (match == 1) if (match == 1)
{ {
@ -1545,29 +1414,42 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
} }
else if (instance->VerifyCertificateEx) else if (instance->VerifyCertificateEx)
{ {
const BOOL use_pem = freerdp_settings_get_bool(
instance->settings, FreeRDP_CertificateCallbackPreferPEM);
char* fp = NULL;
DWORD cflags = flags;
if (use_pem)
{
cflags |= VERIFY_CERT_FLAG_FP_IS_PEM;
fp = pem;
}
else
fp = crypto_cert_fingerprint(cert->px509);
accept_certificate = instance->VerifyCertificateEx( accept_certificate = instance->VerifyCertificateEx(
instance, hostname, port, common_name, subject, issuer, fingerprint, flags); instance, hostname, port, common_name, subject, issuer, fp, cflags);
if (!use_pem)
free(fp);
} }
else if (instance->VerifyCertificate) else if (instance->VerifyCertificate)
{ {
char* fp = crypto_cert_fingerprint(cert->px509);
WLog_WARN(TAG, "The VerifyCertificate callback is deprecated, migrate your " WLog_WARN(TAG, "The VerifyCertificate callback is deprecated, migrate your "
"application to VerifyCertificateEx"); "application to VerifyCertificateEx");
accept_certificate = instance->VerifyCertificate( accept_certificate = instance->VerifyCertificate(instance, common_name, subject,
instance, common_name, subject, issuer, fingerprint, !hostname_match); issuer, fp, !hostname_match);
free(fp);
} }
} }
else if (match == -1) else if (match == -1)
{ {
char* old_subject = NULL; rdpCertificateData* stored_data =
char* old_issuer = NULL; certificate_store_load_data(tls->certificate_store, hostname, port);
char* old_fingerprint = NULL;
/* entry was found in known_hosts file, but fingerprint does not match. ask user /* entry was found in known_hosts file, but fingerprint does not match. ask user
* to use it */ * to use it */
tls_print_certificate_error(hostname, port, fingerprint, tls_print_certificate_error(
tls->certificate_store->file); hostname, port, pem, certificate_store_get_hosts_file(tls->certificate_store));
if (!certificate_get_stored_data(tls->certificate_store, certificate_data, if (!stored_data)
&old_subject, &old_issuer, &old_fingerprint))
WLog_WARN(TAG, "Failed to get certificate entry for %s:%d", hostname, port); WLog_WARN(TAG, "Failed to get certificate entry for %s:%d", hostname, port);
if (tls->settings->AutoDenyCertificate) if (tls->settings->AutoDenyCertificate)
@ -1590,22 +1472,45 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
} }
else if (instance->VerifyChangedCertificateEx) else if (instance->VerifyChangedCertificateEx)
{ {
DWORD cflags = flags | VERIFY_CERT_FLAG_CHANGED;
const char* old_subject = certificate_data_get_subject(stored_data);
const char* old_issuer = certificate_data_get_issuer(stored_data);
const char* old_fp = certificate_data_get_fingerprint(stored_data);
const char* old_pem = certificate_data_get_pem(stored_data);
char* fp;
if (old_pem && freerdp_settings_get_bool(instance->settings,
FreeRDP_CertificateCallbackPreferPEM))
{
cflags |= VERIFY_CERT_FLAG_FP_IS_PEM;
fp = pem;
old_fp = old_pem;
}
else
{
fp = crypto_cert_fingerprint(cert->px509);
}
accept_certificate = instance->VerifyChangedCertificateEx( accept_certificate = instance->VerifyChangedCertificateEx(
instance, hostname, port, common_name, subject, issuer, fingerprint, instance, hostname, port, common_name, subject, issuer, pem, old_subject,
old_subject, old_issuer, old_fingerprint, flags | VERIFY_CERT_FLAG_CHANGED); old_issuer, old_fp, cflags);
if (!old_pem)
free(fp);
} }
else if (instance->VerifyChangedCertificate) else if (instance->VerifyChangedCertificate)
{ {
char* fp = crypto_cert_fingerprint(cert->px509);
const char* old_subject = certificate_data_get_subject(stored_data);
const char* old_issuer = certificate_data_get_issuer(stored_data);
const char* old_fingerprint = certificate_data_get_fingerprint(stored_data);
WLog_WARN(TAG, "The VerifyChangedCertificate callback is deprecated, migrate " WLog_WARN(TAG, "The VerifyChangedCertificate callback is deprecated, migrate "
"your application to VerifyChangedCertificateEx"); "your application to VerifyChangedCertificateEx");
accept_certificate = instance->VerifyChangedCertificate( accept_certificate = instance->VerifyChangedCertificate(
instance, common_name, subject, issuer, fingerprint, old_subject, instance, common_name, subject, issuer, fp, old_subject, old_issuer,
old_issuer, old_fingerprint); old_fingerprint);
free(fp);
} }
free(old_subject); certificate_data_free(stored_data);
free(old_issuer);
free(old_fingerprint);
} }
else if (match == 0) else if (match == 0)
accept_certificate = 2; /* success! */ accept_certificate = 2; /* success! */
@ -1616,15 +1521,9 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
case 1: case 1:
/* user accepted certificate, add entry in known_hosts file */ /* user accepted certificate, add entry in known_hosts file */
if (match < 0) verification_status =
verification_status = certificate_store_save_data(tls->certificate_store, certificate_data) ? 1
certificate_data_replace(tls->certificate_store, certificate_data) ? 1 : -1;
: -1;
else
verification_status =
certificate_data_print(tls->certificate_store, certificate_data) ? 1
: -1;
break; break;
case 2: case 2:
@ -1640,7 +1539,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
free(issuer); free(issuer);
free(subject); free(subject);
free(fingerprint); free(pem);
} }
if (verification_status > 0) if (verification_status > 0)

View File

@ -550,7 +550,7 @@ BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
#endif #endif
if (ConvertFromUnicode(CP_UTF8, 0, path, -1, &dup, 0, NULL, NULL)) if (ConvertFromUnicode(CP_UTF8, 0, path, -1, &dup, 0, NULL, NULL) <= 0)
return FALSE; return FALSE;
#ifdef __OS2__ #ifdef __OS2__