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:
parent
c3171b90cb
commit
b494a193db
@ -180,6 +180,7 @@ endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(EXPORT_ALL_SYMBOLS TRUE)
|
||||
set(CTEST_OUTPUT_ON_FAILURE TRUE)
|
||||
elseif(NOT DEFINED EXPORT_ALL_SYMBOLS)
|
||||
set(EXPORT_ALL_SYMBOLS FALSE)
|
||||
endif()
|
||||
|
@ -80,7 +80,7 @@ static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* n
|
||||
{
|
||||
const char* path = settings->ConfigPath;
|
||||
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);
|
||||
|
||||
if (config && !PathFileExistsA(config))
|
||||
@ -197,8 +197,8 @@ fail:
|
||||
|
||||
if (rc && (lowSize <= INT_MAX))
|
||||
{
|
||||
int blen = 0;
|
||||
crypto_base64_decode(fdata, (int)lowSize, (BYTE**)data, &blen);
|
||||
size_t blen = 0;
|
||||
crypto_base64_decode(fdata, lowSize, (BYTE**)data, &blen);
|
||||
|
||||
if (*data && (blen > 0))
|
||||
*length = (UINT32)blen;
|
||||
|
@ -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* type = "RDP-Server";
|
||||
|
||||
if (flags & VERIFY_CERT_FLAG_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("\tSubject: %s\n", subject);
|
||||
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"
|
||||
"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("\tSubject: %s\n", subject);
|
||||
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("Old Certificate details:\n");
|
||||
printf("\tSubject: %s\n", old_subject);
|
||||
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");
|
||||
if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
|
||||
{
|
||||
|
@ -3116,16 +3116,15 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
|
||||
CommandLineSwitchCase(arg, "reconnect-cookie")
|
||||
{
|
||||
BYTE* base64 = NULL;
|
||||
int length;
|
||||
size_t length;
|
||||
if (!arg->Value)
|
||||
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
|
||||
|
||||
crypto_base64_decode((const char*)(arg->Value), (int)strlen(arg->Value), &base64,
|
||||
&length);
|
||||
crypto_base64_decode((const char*)(arg->Value), strlen(arg->Value), &base64, &length);
|
||||
|
||||
if ((base64 != NULL) && (length == sizeof(ARC_SC_PRIVATE_PACKET)))
|
||||
{
|
||||
memcpy(settings->ServerAutoReconnectCookie, base64, (size_t)length);
|
||||
memcpy(settings->ServerAutoReconnectCookie, base64, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -32,43 +32,46 @@ typedef struct rdp_certificate_store rdpCertificateStore;
|
||||
#include <winpr/print.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
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
FREERDP_API rdpCertificateData* certificate_data_new(const char* hostname, UINT16 port,
|
||||
const char* subject, const char* issuer,
|
||||
const char* fingerprint);
|
||||
FREERDP_API rdpCertificateData* certificate_data_new(const char* hostname, UINT16 port);
|
||||
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,
|
||||
rdpCertificateData* certificate_data);
|
||||
|
||||
FREERDP_API const char* certificate_data_get_host(const rdpCertificateData* cert);
|
||||
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 int certificate_data_match(rdpCertificateStore* certificate_store,
|
||||
rdpCertificateData* certificate_data);
|
||||
FREERDP_API BOOL certificate_data_print(rdpCertificateStore* certificate_store,
|
||||
rdpCertificateData* certificate_data);
|
||||
FREERDP_API BOOL certificate_get_stored_data(rdpCertificateStore* certificate_store,
|
||||
rdpCertificateData* certificate_data,
|
||||
char** subject, char** issuer, char** fingerprint);
|
||||
|
||||
FREERDP_API int certificate_store_contains_data(rdpCertificateStore* certificate_store,
|
||||
const rdpCertificateData* certificate_data);
|
||||
FREERDP_API rdpCertificateData*
|
||||
certificate_store_load_data(rdpCertificateStore* certificate_store, const char* host,
|
||||
UINT16 port);
|
||||
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
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ extern "C"
|
||||
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(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_common_name(X509* xcert, int* length);
|
||||
FREERDP_API char** crypto_cert_get_dns_names(X509* xcert, int* count, int** lengths);
|
||||
@ -102,9 +104,9 @@ extern "C"
|
||||
BYTE* output);
|
||||
FREERDP_API void crypto_reverse(BYTE* data, int length);
|
||||
|
||||
FREERDP_API char* crypto_base64_encode(const BYTE* data, int length);
|
||||
FREERDP_API void crypto_base64_decode(const char* enc_data, int length, BYTE** dec_data,
|
||||
int* res_length);
|
||||
FREERDP_API char* crypto_base64_encode(const BYTE* data, size_t length);
|
||||
FREERDP_API void crypto_base64_decode(const char* enc_data, size_t length, BYTE** dec_data,
|
||||
size_t* res_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ extern "C"
|
||||
#define VERIFY_CERT_FLAG_CHANGED 0x40
|
||||
#define VERIFY_CERT_FLAG_MISMATCH 0x80
|
||||
#define VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1 0x100
|
||||
#define VERIFY_CERT_FLAG_FP_IS_PEM 0x200
|
||||
|
||||
/* Message types used by gateway messaging callback */
|
||||
#define GATEWAY_MESSAGE_CONSENT 1
|
||||
@ -91,7 +92,8 @@ extern "C"
|
||||
* @param common_name The certificate registered hostname.
|
||||
* @param subject The common name 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
|
||||
* subject does not match the host connecting to.
|
||||
*
|
||||
@ -110,7 +112,8 @@ extern "C"
|
||||
* @param common_name The certificate registered hostname.
|
||||
* @param subject The common name 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*
|
||||
*
|
||||
* @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 subject The common name 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_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*
|
||||
*
|
||||
* @return 1 to accept and store a certificate, 2 to accept
|
||||
@ -169,7 +174,7 @@ extern "C"
|
||||
* a certificate.
|
||||
*
|
||||
* @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 hostname The hostname connecting to.
|
||||
* @param port The port connecting to.
|
||||
|
@ -700,6 +700,8 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
|
||||
#define FreeRDP_AutoAcceptCertificate (1419)
|
||||
#define FreeRDP_AutoDenyCertificate (1420)
|
||||
#define FreeRDP_CertificateAcceptedFingerprints (1421)
|
||||
#define FreeRDP_CertificateUseKnownHosts (1422)
|
||||
#define FreeRDP_CertificateCallbackPreferPEM (1423)
|
||||
#define FreeRDP_Workarea (1536)
|
||||
#define FreeRDP_Fullscreen (1537)
|
||||
#define FreeRDP_PercentScreen (1538)
|
||||
@ -1176,7 +1178,9 @@ struct rdp_settings
|
||||
ALIGN64 BOOL AutoAcceptCertificate; /* 1419 */
|
||||
ALIGN64 BOOL AutoDenyCertificate; /* 1420 */
|
||||
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 */
|
||||
|
||||
/**
|
||||
|
@ -74,6 +74,12 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id)
|
||||
case FreeRDP_BitmapCompressionDisabled:
|
||||
return settings->BitmapCompressionDisabled;
|
||||
|
||||
case FreeRDP_CertificateCallbackPreferPEM:
|
||||
return settings->CertificateCallbackPreferPEM;
|
||||
|
||||
case FreeRDP_CertificateUseKnownHosts:
|
||||
return settings->CertificateUseKnownHosts;
|
||||
|
||||
case FreeRDP_ColorPointerFlag:
|
||||
return settings->ColorPointerFlag;
|
||||
|
||||
@ -602,6 +608,14 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val)
|
||||
settings->BitmapCompressionDisabled = val;
|
||||
break;
|
||||
|
||||
case FreeRDP_CertificateCallbackPreferPEM:
|
||||
settings->CertificateCallbackPreferPEM = val;
|
||||
break;
|
||||
|
||||
case FreeRDP_CertificateUseKnownHosts:
|
||||
settings->CertificateUseKnownHosts = val;
|
||||
break;
|
||||
|
||||
case FreeRDP_ColorPointerFlag:
|
||||
settings->ColorPointerFlag = val;
|
||||
break;
|
||||
|
@ -34,6 +34,8 @@ static const struct settings_str_entry settings_map[] = {
|
||||
{ FreeRDP_BitmapCachePersistEnabled, 0, "FreeRDP_BitmapCachePersistEnabled" },
|
||||
{ FreeRDP_BitmapCacheV3Enabled, 0, "FreeRDP_BitmapCacheV3Enabled" },
|
||||
{ FreeRDP_BitmapCompressionDisabled, 0, "FreeRDP_BitmapCompressionDisabled" },
|
||||
{ FreeRDP_CertificateCallbackPreferPEM, 0, "FreeRDP_CertificateCallbackPreferPEM" },
|
||||
{ FreeRDP_CertificateUseKnownHosts, 0, "FreeRDP_CertificateUseKnownHosts" },
|
||||
{ FreeRDP_ColorPointerFlag, 0, "FreeRDP_ColorPointerFlag" },
|
||||
{ FreeRDP_CompressionEnabled, 0, "FreeRDP_CompressionEnabled" },
|
||||
{ FreeRDP_ConsoleSession, 0, "FreeRDP_ConsoleSession" },
|
||||
|
@ -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)
|
||||
{
|
||||
const char* token64 = NULL;
|
||||
int ntlmTokenLength = 0;
|
||||
size_t ntlmTokenLength = 0;
|
||||
BYTE* ntlmTokenData = NULL;
|
||||
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)
|
||||
{
|
||||
const char* token64 = NULL;
|
||||
int ntlmTokenLength = 0;
|
||||
size_t ntlmTokenLength = 0;
|
||||
BYTE* ntlmTokenData = NULL;
|
||||
rdpNtlm* ntlm;
|
||||
|
||||
|
@ -1181,7 +1181,7 @@ static BOOL rdg_handle_ntlm_challenge(rdpNtlm* ntlm, HttpResponse* response)
|
||||
BOOL continueNeeded = FALSE;
|
||||
size_t len;
|
||||
const char* token64 = NULL;
|
||||
int ntlmTokenLength = 0;
|
||||
size_t ntlmTokenLength = 0;
|
||||
BYTE* ntlmTokenData = NULL;
|
||||
long StatusCode;
|
||||
|
||||
@ -1203,16 +1203,7 @@ static BOOL rdg_handle_ntlm_challenge(rdpNtlm* ntlm, HttpResponse* response)
|
||||
|
||||
len = strlen(token64);
|
||||
|
||||
if (len > INT_MAX)
|
||||
return FALSE;
|
||||
|
||||
crypto_base64_decode(token64, (int)len, &ntlmTokenData, &ntlmTokenLength);
|
||||
|
||||
if (ntlmTokenLength < 0)
|
||||
{
|
||||
free(ntlmTokenData);
|
||||
return FALSE;
|
||||
}
|
||||
crypto_base64_decode(token64, len, &ntlmTokenData, &ntlmTokenLength);
|
||||
|
||||
if (ntlmTokenData && ntlmTokenLength)
|
||||
{
|
||||
|
@ -381,7 +381,9 @@ rdpSettings* freerdp_settings_new(DWORD flags)
|
||||
!freerdp_settings_set_bool(settings, FreeRDP_DisableCredentialsDelegation, FALSE) ||
|
||||
!freerdp_settings_set_uint32(settings, FreeRDP_AuthenticationLevel, 2) ||
|
||||
!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;
|
||||
|
||||
settings->ChannelDefArray = (CHANNEL_DEF*)calloc(
|
||||
|
@ -23,6 +23,8 @@ static const size_t bool_list_indices[] = {
|
||||
FreeRDP_BitmapCachePersistEnabled,
|
||||
FreeRDP_BitmapCacheV3Enabled,
|
||||
FreeRDP_BitmapCompressionDisabled,
|
||||
FreeRDP_CertificateCallbackPreferPEM,
|
||||
FreeRDP_CertificateUseKnownHosts,
|
||||
FreeRDP_ColorPointerFlag,
|
||||
FreeRDP_CompressionEnabled,
|
||||
FreeRDP_ConsoleSession,
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
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;
|
||||
const BYTE* q;
|
||||
@ -114,12 +114,12 @@ static int base64_decode_char(char c)
|
||||
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];
|
||||
BYTE* q;
|
||||
BYTE* data;
|
||||
int nBlocks, i, outputLen;
|
||||
size_t nBlocks, i, outputLen;
|
||||
|
||||
if (length % 4)
|
||||
return NULL;
|
||||
@ -132,6 +132,12 @@ static void* base64_decode(const char* s, int length, int* data_len)
|
||||
nBlocks = (length / 4);
|
||||
outputLen = 0;
|
||||
|
||||
if (nBlocks < 1)
|
||||
{
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nBlocks - 1; i++, q += 3)
|
||||
{
|
||||
n[0] = base64_decode_char(*s++);
|
||||
@ -192,7 +198,7 @@ out_free:
|
||||
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);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -250,19 +250,27 @@ BYTE* crypto_cert_hash(X509* xcert, const char* hash, UINT32* length)
|
||||
BYTE* fp;
|
||||
const EVP_MD* md = EVP_get_digestbyname(hash);
|
||||
if (!md)
|
||||
{
|
||||
WLog_ERR(TAG, "System does not support %s hash!", hash);
|
||||
return NULL;
|
||||
if (!length)
|
||||
return NULL;
|
||||
if (!xcert)
|
||||
}
|
||||
if (!xcert || !length)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] Invalid arugments: xcert=%p, length=%p", __FUNCTION__, xcert, length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = calloc(fp_len, sizeof(BYTE));
|
||||
if (!fp)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] could not allocate %" PRIuz " bytes", __FUNCTION__, fp_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (X509_digest(xcert, md, fp, &fp_len) != 1)
|
||||
{
|
||||
free(fp);
|
||||
WLog_ERR(TAG, "certificate does not have a %s hash!", hash);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -276,7 +284,16 @@ char* crypto_cert_fingerprint_by_hash(X509* xcert, const char* hash)
|
||||
BYTE* fp;
|
||||
char* p;
|
||||
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);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
@ -322,7 +339,16 @@ static char* crypto_print_name(X509_NAME* name)
|
||||
|
||||
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)
|
||||
@ -799,7 +825,16 @@ char** crypto_cert_get_dns_names(X509* x509, int* count, int** lengths)
|
||||
|
||||
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)
|
||||
@ -893,22 +928,24 @@ end:
|
||||
|
||||
rdpCertificateData* crypto_get_certificate_data(X509* xcert, const char* hostname, UINT16 port)
|
||||
{
|
||||
char* issuer;
|
||||
char* subject;
|
||||
char* fp;
|
||||
rdpCertificateData* certdata;
|
||||
fp = crypto_cert_fingerprint(xcert);
|
||||
char* pem = NULL;
|
||||
size_t length;
|
||||
rdpCertificateData* certdata = NULL;
|
||||
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
issuer = crypto_cert_issuer(xcert);
|
||||
subject = crypto_cert_subject(xcert);
|
||||
certdata = certificate_data_new(hostname, port, issuer, subject, fp);
|
||||
free(subject);
|
||||
free(issuer);
|
||||
free(fp);
|
||||
pem = (char*)crypto_cert_pem(xcert, NULL, &length);
|
||||
if (!pem)
|
||||
goto fail;
|
||||
certdata = certificate_data_new(hostname, port);
|
||||
if (!certdata)
|
||||
goto fail;
|
||||
if (!certificate_data_set_pem(certdata, pem))
|
||||
goto fail;
|
||||
free(pem);
|
||||
return certdata;
|
||||
fail:
|
||||
certificate_data_free(certdata);
|
||||
free(pem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void crypto_cert_print_info(X509* xcert)
|
||||
@ -939,3 +976,135 @@ out_free_issuer:
|
||||
free(issuer);
|
||||
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;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
struct Encode64test
|
||||
{
|
||||
const char* input;
|
||||
int len;
|
||||
size_t len;
|
||||
const char* output;
|
||||
};
|
||||
|
||||
@ -41,7 +41,7 @@ static const struct Encode64test encodeTests[] = {
|
||||
int TestBase64(int argc, char* argv[])
|
||||
{
|
||||
int i, testNb = 0;
|
||||
int outLen;
|
||||
size_t outLen;
|
||||
BYTE* decoded;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
@ -23,11 +23,104 @@
|
||||
|
||||
#include <freerdp/crypto/certificate.h>
|
||||
|
||||
/* Some certificates copied from /usr/share/ca-certificates */
|
||||
static const char pem1[] = "-----BEGIN CERTIFICATE-----\n"
|
||||
"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\n"
|
||||
"-----END CERTIFICATE-----";
|
||||
|
||||
static const char pem2[] = "-----BEGIN CERTIFICATE-----\n"
|
||||
"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\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)
|
||||
{
|
||||
int rc = -1;
|
||||
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"
|
||||
"otherurl\t3389\taa:bb:cc:dd\tsubject2\tissuer2\r" };
|
||||
FILE* fc = NULL;
|
||||
@ -52,21 +145,19 @@ finish:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TestKnownHosts(int argc, char* argv[])
|
||||
static BOOL setup_config(rdpSettings** settings)
|
||||
{
|
||||
int rc = -1;
|
||||
rdpSettings current;
|
||||
rdpCertificateData* data = NULL;
|
||||
rdpCertificateStore* store = NULL;
|
||||
char* currentFileV2 = NULL;
|
||||
char* subject = NULL;
|
||||
char* issuer = NULL;
|
||||
char* fp = NULL;
|
||||
BOOL rc = FALSE;
|
||||
char* path = NULL;
|
||||
char sname[8192];
|
||||
|
||||
SYSTEMTIME systemTime;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
if (!settings)
|
||||
goto fail;
|
||||
*settings = freerdp_settings_new(0);
|
||||
if (!*settings)
|
||||
goto fail;
|
||||
|
||||
GetSystemTime(&systemTime);
|
||||
sprintf_s(sname, sizeof(sname),
|
||||
"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.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
|
||||
|
||||
current.ConfigPath = GetKnownSubPath(KNOWN_PATH_TEMP, sname);
|
||||
|
||||
if (!PathFileExistsA(current.ConfigPath))
|
||||
path = GetKnownSubPath(KNOWN_PATH_TEMP, sname);
|
||||
if (!path)
|
||||
goto fail;
|
||||
if (!PathFileExistsA(path))
|
||||
{
|
||||
if (!CreateDirectoryA(current.ConfigPath, NULL))
|
||||
if (!CreateDirectoryA(path, NULL))
|
||||
{
|
||||
fprintf(stderr, "Could not create %s!\n", current.ConfigPath);
|
||||
goto finish;
|
||||
fprintf(stderr, "Could not create %s!\n", path);
|
||||
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)
|
||||
{
|
||||
@ -93,7 +403,8 @@ int TestKnownHosts(int argc, char* argv[])
|
||||
goto finish;
|
||||
}
|
||||
|
||||
store = certificate_store_new(¤t);
|
||||
printf("certificate_store_new\n");
|
||||
store = certificate_store_new(settings);
|
||||
|
||||
if (!store)
|
||||
{
|
||||
@ -104,139 +415,276 @@ int TestKnownHosts(int argc, char* argv[])
|
||||
if (prepare(currentFileV2))
|
||||
goto finish;
|
||||
|
||||
/* Test if host is found in current file. */
|
||||
data = certificate_data_new("someurl", 3389, "subject", "issuer", "ff:11:22:dd");
|
||||
|
||||
if (!data)
|
||||
{
|
||||
fprintf(stderr, "Could not create certificate data!\n");
|
||||
if (!test_known_hosts_host_found(store))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (0 != certificate_data_match(store, data))
|
||||
{
|
||||
fprintf(stderr, "Could not find data in v2 file!\n");
|
||||
if (!test_known_hosts_host_not_found(store))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Test if we can read out the old fingerprint. */
|
||||
if (!certificate_get_stored_data(store, data, &subject, &issuer, &fp))
|
||||
{
|
||||
fprintf(stderr, "Could not read old fingerprint!\n");
|
||||
if (!test_known_hosts_host_add(store))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("Got %s, %s '%s'\n", subject, issuer, fp);
|
||||
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");
|
||||
if (!test_known_hosts_host_replace(store))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (0 == certificate_data_match(store, data))
|
||||
{
|
||||
fprintf(stderr, "Invalid host found in v2 file!\n");
|
||||
if (!test_known_hosts_host_replace_invalid(store))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Test if we read out the old fingerprint fails. */
|
||||
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;
|
||||
rc = TRUE;
|
||||
finish:
|
||||
free(current.ConfigPath);
|
||||
freerdp_settings_free(settings);
|
||||
|
||||
if (store)
|
||||
certificate_store_free(store);
|
||||
|
||||
if (data)
|
||||
certificate_data_free(data);
|
||||
printf("certificate_store_free\n");
|
||||
certificate_store_free(store);
|
||||
|
||||
DeleteFileA(currentFileV2);
|
||||
// RemoveDirectoryA(current.ConfigPath);
|
||||
free(currentFileV2);
|
||||
free(subject);
|
||||
free(issuer);
|
||||
free(fp);
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -970,34 +970,17 @@ BOOL tls_accept(rdpTls* tls, BIO* underlying, rdpSettings* settings)
|
||||
}
|
||||
|
||||
if (settings->CertificateFile)
|
||||
{
|
||||
bio = BIO_new_file(settings->CertificateFile, "rb");
|
||||
|
||||
if (!bio)
|
||||
{
|
||||
WLog_ERR(TAG, "BIO_new_file failed for certificate %s", settings->CertificateFile);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
x509 = crypto_cert_from_pem(settings->CertificateFile, strlen(settings->CertificateFile),
|
||||
TRUE);
|
||||
else if (settings->CertificateContent)
|
||||
{
|
||||
bio = BIO_new_mem_buf(settings->CertificateContent, strlen(settings->CertificateContent));
|
||||
|
||||
if (!bio)
|
||||
{
|
||||
WLog_ERR(TAG, "BIO_new_mem_buf failed for certificate");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
x509 = crypto_cert_from_pem(settings->CertificateContent,
|
||||
strlen(settings->CertificateContent), FALSE);
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "no certificate defined");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, 0);
|
||||
BIO_free_all(bio);
|
||||
|
||||
if (!x509)
|
||||
{
|
||||
WLog_ERR(TAG, "invalid certificate");
|
||||
@ -1260,120 +1243,19 @@ static BOOL accept_cert(rdpTls* tls, const BYTE* pem, UINT32 length)
|
||||
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;
|
||||
int status, count, x;
|
||||
size_t offset;
|
||||
size_t length = 0;
|
||||
BOOL rc = FALSE;
|
||||
BYTE* pemCert = NULL;
|
||||
|
||||
if (!PublicKey || !PublicKeyLength)
|
||||
if (!cert || !PublicKey)
|
||||
return FALSE;
|
||||
|
||||
*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;
|
||||
*PublicKey = crypto_cert_pem(cert->px509, cert->px509chain, PublicKeyLength);
|
||||
return *PublicKey != NULL;
|
||||
}
|
||||
|
||||
int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port)
|
||||
{
|
||||
int match;
|
||||
int index;
|
||||
DWORD length;
|
||||
size_t length;
|
||||
BOOL certificate_status;
|
||||
char* common_name = NULL;
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
certificate_data = crypto_get_certificate_data(cert->px509, hostname, port);
|
||||
/* extra common name and alternative names */
|
||||
@ -1486,31 +1369,17 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
|
||||
* manual verification */
|
||||
if (!certificate_status || !hostname_match)
|
||||
{
|
||||
char* issuer;
|
||||
char* subject;
|
||||
char* fingerprint;
|
||||
DWORD accept_certificate = 0;
|
||||
issuer = crypto_cert_issuer(cert->px509);
|
||||
subject = crypto_cert_subject(cert->px509);
|
||||
fingerprint = crypto_cert_fingerprint(cert->px509);
|
||||
size_t length = 0;
|
||||
char* issuer = crypto_cert_issuer(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 */
|
||||
match = certificate_data_match(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);
|
||||
}
|
||||
match = certificate_store_contains_data(tls->certificate_store, certificate_data);
|
||||
|
||||
if (match == 1)
|
||||
{
|
||||
@ -1545,29 +1414,42 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
|
||||
}
|
||||
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(
|
||||
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)
|
||||
{
|
||||
char* fp = crypto_cert_fingerprint(cert->px509);
|
||||
WLog_WARN(TAG, "The VerifyCertificate callback is deprecated, migrate your "
|
||||
"application to VerifyCertificateEx");
|
||||
accept_certificate = instance->VerifyCertificate(
|
||||
instance, common_name, subject, issuer, fingerprint, !hostname_match);
|
||||
accept_certificate = instance->VerifyCertificate(instance, common_name, subject,
|
||||
issuer, fp, !hostname_match);
|
||||
free(fp);
|
||||
}
|
||||
}
|
||||
else if (match == -1)
|
||||
{
|
||||
char* old_subject = NULL;
|
||||
char* old_issuer = NULL;
|
||||
char* old_fingerprint = NULL;
|
||||
rdpCertificateData* stored_data =
|
||||
certificate_store_load_data(tls->certificate_store, hostname, port);
|
||||
/* entry was found in known_hosts file, but fingerprint does not match. ask user
|
||||
* to use it */
|
||||
tls_print_certificate_error(hostname, port, fingerprint,
|
||||
tls->certificate_store->file);
|
||||
tls_print_certificate_error(
|
||||
hostname, port, pem, certificate_store_get_hosts_file(tls->certificate_store));
|
||||
|
||||
if (!certificate_get_stored_data(tls->certificate_store, certificate_data,
|
||||
&old_subject, &old_issuer, &old_fingerprint))
|
||||
if (!stored_data)
|
||||
WLog_WARN(TAG, "Failed to get certificate entry for %s:%d", hostname, port);
|
||||
|
||||
if (tls->settings->AutoDenyCertificate)
|
||||
@ -1590,22 +1472,45 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
|
||||
}
|
||||
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(
|
||||
instance, hostname, port, common_name, subject, issuer, fingerprint,
|
||||
old_subject, old_issuer, old_fingerprint, flags | VERIFY_CERT_FLAG_CHANGED);
|
||||
instance, hostname, port, common_name, subject, issuer, pem, old_subject,
|
||||
old_issuer, old_fp, cflags);
|
||||
if (!old_pem)
|
||||
free(fp);
|
||||
}
|
||||
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 "
|
||||
"your application to VerifyChangedCertificateEx");
|
||||
accept_certificate = instance->VerifyChangedCertificate(
|
||||
instance, common_name, subject, issuer, fingerprint, old_subject,
|
||||
old_issuer, old_fingerprint);
|
||||
instance, common_name, subject, issuer, fp, old_subject, old_issuer,
|
||||
old_fingerprint);
|
||||
free(fp);
|
||||
}
|
||||
|
||||
free(old_subject);
|
||||
free(old_issuer);
|
||||
free(old_fingerprint);
|
||||
certificate_data_free(stored_data);
|
||||
}
|
||||
else if (match == 0)
|
||||
accept_certificate = 2; /* success! */
|
||||
@ -1616,15 +1521,9 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
|
||||
case 1:
|
||||
|
||||
/* user accepted certificate, add entry in known_hosts file */
|
||||
if (match < 0)
|
||||
verification_status =
|
||||
certificate_data_replace(tls->certificate_store, certificate_data) ? 1
|
||||
: -1;
|
||||
else
|
||||
verification_status =
|
||||
certificate_data_print(tls->certificate_store, certificate_data) ? 1
|
||||
: -1;
|
||||
|
||||
verification_status =
|
||||
certificate_store_save_data(tls->certificate_store, certificate_data) ? 1
|
||||
: -1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
@ -1640,7 +1539,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, U
|
||||
|
||||
free(issuer);
|
||||
free(subject);
|
||||
free(fingerprint);
|
||||
free(pem);
|
||||
}
|
||||
|
||||
if (verification_status > 0)
|
||||
|
@ -550,7 +550,7 @@ BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
|
||||
|
||||
#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;
|
||||
|
||||
#ifdef __OS2__
|
||||
|
Loading…
Reference in New Issue
Block a user