From c9cebf6ed67d61736bd075419a6cf4b4a2f3a64f Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Tue, 10 Jul 2018 10:03:49 +0200 Subject: [PATCH] Remember accepted PEM cert to avoid unnecessary user input. --- include/freerdp/crypto/tls.h | 16 ++-- include/freerdp/settings.h | 20 +++-- libfreerdp/core/settings.c | 10 ++- libfreerdp/crypto/tls.c | 188 +++++++++++++++++++++++++------------------ 4 files changed, 140 insertions(+), 94 deletions(-) diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index 06dc97c..3407375 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -85,24 +85,24 @@ struct rdp_tls }; #ifdef __cplusplus - extern "C" { +extern "C" { #endif -FREERDP_API int tls_connect(rdpTls* tls, BIO *underlying); -FREERDP_API BOOL tls_accept(rdpTls* tls, BIO *underlying, rdpSettings *settings); +FREERDP_API int tls_connect(rdpTls* tls, BIO* underlying); +FREERDP_API BOOL tls_accept(rdpTls* tls, BIO* underlying, rdpSettings* settings); FREERDP_API BOOL tls_send_alert(rdpTls* tls); FREERDP_API int tls_write_all(rdpTls* tls, const BYTE* data, int length); FREERDP_API int tls_set_alert_code(rdpTls* tls, int level, int description); -FREERDP_API BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname); +FREERDP_API BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname); FREERDP_API int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port); FREERDP_API void tls_print_certificate_error(char* hostname, UINT16 port, - char* fingerprint, char* hosts_file); + char* fingerprint, char* hosts_file); FREERDP_API void tls_print_certificate_name_mismatch_error( - char* hostname, UINT16 port, char* common_name, char** alt_names, - int alt_names_count); + char* hostname, UINT16 port, char* common_name, char** alt_names, + int alt_names_count); FREERDP_API BOOL tls_print_error(char* func, SSL* connection, int value); @@ -110,7 +110,7 @@ FREERDP_API rdpTls* tls_new(rdpSettings* settings); FREERDP_API void tls_free(rdpTls* tls); #ifdef __cplusplus - } +} #endif #endif /* FREERDP_CRYPTO_TLS_H */ diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index bf4e375..5970b8f 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -868,7 +868,9 @@ struct rdp_settings ALIGN64 char* PasswordHash; /* 24 */ ALIGN64 BOOL WaitForOutputBufferFlush; /* 25 */ ALIGN64 UINT32 MaxTimeInCheckLoop; /* 26 */ - UINT64 padding0064[64 - 27]; /* 27 */ + ALIGN64 char* AcceptedCert; /* 27 */ + ALIGN64 UINT32 AcceptedCertLength; /* 28 */ + UINT64 padding0064[64 - 29]; /* 29 */ UINT64 padding0128[128 - 64]; /* 64 */ /** @@ -1073,7 +1075,9 @@ struct rdp_settings ALIGN64 UINT32 TargetNetAddressCount; /* 1228 */ ALIGN64 char** TargetNetAddresses; /* 1229 */ ALIGN64 UINT32* TargetNetPorts; /* 1230 */ - UINT64 padding1280[1280 - 1231]; /* 1231 */ + ALIGN64 char* RedirectionAcceptedCert; /* 1231 */ + ALIGN64 UINT32 RedirectionAcceptedCertLength;/* 1232 */ + UINT64 padding1280[1280 - 1233]; /* 1233 */ /** * Security @@ -1185,7 +1189,9 @@ struct rdp_settings ALIGN64 BOOL GatewayHttpTransport; /* 1995 */ ALIGN64 BOOL GatewayUdpTransport; /* 1996 */ ALIGN64 char* GatewayAccessToken; /* 1997 */ - UINT64 padding2015[2015 - 1998]; /* 1998 */ + ALIGN64 char* GatewayAcceptedCert; /* 1998 */ + ALIGN64 UINT32 GatewayAcceptedCertLength; /* 1999 */ + UINT64 padding2015[2015 - 2000]; /* 2000 */ /* Proxy */ ALIGN64 UINT32 ProxyType; /* 2015 */ @@ -1501,7 +1507,7 @@ FREERDP_API int freerdp_addin_set_argument(ADDIN_ARGV* args, char* argument); FREERDP_API int freerdp_addin_replace_argument(ADDIN_ARGV* args, char* previous, char* argument); FREERDP_API int freerdp_addin_set_argument_value(ADDIN_ARGV* args, char* option, char* value); FREERDP_API int freerdp_addin_replace_argument_value(ADDIN_ARGV* args, char* previous, char* option, - char* value); + char* value); FREERDP_API BOOL freerdp_device_collection_add(rdpSettings* settings, RDPDR_DEVICE* device); FREERDP_API RDPDR_DEVICE* freerdp_device_collection_find(rdpSettings* settings, const char* name); @@ -1511,13 +1517,13 @@ FREERDP_API void freerdp_device_collection_free(rdpSettings* settings); FREERDP_API BOOL freerdp_static_channel_collection_add(rdpSettings* settings, ADDIN_ARGV* channel); FREERDP_API ADDIN_ARGV* freerdp_static_channel_collection_find(rdpSettings* settings, - const char* name); + const char* name); FREERDP_API ADDIN_ARGV* freerdp_static_channel_clone(ADDIN_ARGV* channel); FREERDP_API void freerdp_static_channel_collection_free(rdpSettings* settings); FREERDP_API BOOL freerdp_dynamic_channel_collection_add(rdpSettings* settings, ADDIN_ARGV* channel); FREERDP_API ADDIN_ARGV* freerdp_dynamic_channel_collection_find(rdpSettings* settings, - const char* name); + const char* name); FREERDP_API ADDIN_ARGV* freerdp_dynamic_channel_clone(ADDIN_ARGV* channel); FREERDP_API void freerdp_dynamic_channel_collection_free(rdpSettings* settings); @@ -1528,7 +1534,7 @@ FREERDP_API void freerdp_performance_flags_split(rdpSettings* settings); FREERDP_API void freerdp_set_gateway_usage_method(rdpSettings* settings, UINT32 GatewayUsageMethod); FREERDP_API void freerdp_update_gateway_usage_method(rdpSettings* settings, UINT32 GatewayEnabled, - UINT32 GatewayBypassLocal); + UINT32 GatewayBypassLocal); FREERDP_API BOOL freerdp_get_param_bool(rdpSettings* settings, int id); FREERDP_API int freerdp_set_param_bool(rdpSettings* settings, int id, BOOL param); diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index d76fb0c..d73251e 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -653,6 +653,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(Password); /* 22 */ CHECKED_STRDUP(Domain); /* 23 */ CHECKED_STRDUP(PasswordHash); /* 24 */ + CHECKED_STRDUP(AcceptedCert); /* 27 */ _settings->ClientHostname = NULL; /* 134 */ _settings->ClientProductId = NULL; /* 135 */ CHECKED_STRDUP(AlternateShell); /* 640 */ @@ -668,6 +669,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(AllowedTlsCiphers); /* 1101 */ CHECKED_STRDUP(NtlmSamFile); /* 1103 */ CHECKED_STRDUP(PreconnectionBlob); /* 1155 */ + CHECKED_STRDUP(RedirectionAcceptedCert); /* 1231 */ CHECKED_STRDUP(KerberosKdc); /* 1344 */ CHECKED_STRDUP(KerberosRealm); /* 1345 */ CHECKED_STRDUP(CertificateName); /* 1409 */ @@ -692,6 +694,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(GatewayPassword); /* 1988 */ CHECKED_STRDUP(GatewayDomain); /* 1989 */ CHECKED_STRDUP(GatewayAccessToken); /* 1997 */ + CHECKED_STRDUP(GatewayAcceptedCert); /* 1998 */ CHECKED_STRDUP(ProxyHostname); /* 2016 */ CHECKED_STRDUP(RemoteApplicationName); /* 2113 */ CHECKED_STRDUP(RemoteApplicationIcon); /* 2114 */ @@ -772,7 +775,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) if (_settings->ChannelDefArraySize > 0) { _settings->ChannelDefArray = (CHANNEL_DEF*) calloc(settings->ChannelDefArraySize, - sizeof(CHANNEL_DEF)); + sizeof(CHANNEL_DEF)); if (!_settings->ChannelDefArray) goto out_fail; @@ -789,7 +792,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) if (_settings->MonitorDefArraySize > 0) { _settings->MonitorDefArray = (rdpMonitor*) calloc(settings->MonitorDefArraySize, - sizeof(rdpMonitor)); + sizeof(rdpMonitor)); if (!_settings->MonitorDefArray) goto out_fail; @@ -1032,6 +1035,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->Password); free(settings->Domain); free(settings->PasswordHash); + free(settings->AcceptedCert); free(settings->AlternateShell); free(settings->ShellWorkingDirectory); free(settings->ComputerName); @@ -1076,6 +1080,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->RedirectionDomain); free(settings->RedirectionPassword); free(settings->RedirectionTsvUrl); + free(settings->RedirectionAcceptedCert); free(settings->RemoteAssistanceSessionId); free(settings->RemoteAssistancePassword); free(settings->RemoteAssistancePassStub); @@ -1086,6 +1091,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->GatewayPassword); free(settings->GatewayDomain); free(settings->GatewayAccessToken); + free(settings->GatewayAcceptedCert); free(settings->CertificateName); free(settings->DynamicDSTTimeZoneKeyName); free(settings->PreconnectionBlob); diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 42be8e2..4a3b148 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -1109,6 +1109,82 @@ BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname) return FALSE; } +static BOOL is_accepted(rdpTls* tls, const BYTE* pem, size_t length) +{ + rdpSettings* settings = tls->settings; + char* AccpetedKey; + UINT32 AcceptedKeyLength; + + if (tls->isGatewayTransport) + { + AccpetedKey = settings->GatewayAcceptedCert; + AcceptedKeyLength = settings->GatewayAcceptedCertLength; + } + else if (settings->RedirectionFlags != 0) + { + AccpetedKey = settings->RedirectionAcceptedCert; + AcceptedKeyLength = settings->RedirectionAcceptedCertLength; + } + else + { + AccpetedKey = settings->AcceptedCert; + AcceptedKeyLength = settings->AcceptedCertLength; + } + + if (AcceptedKeyLength > 0) + { + if (AcceptedKeyLength == length) + { + if (memcmp(AccpetedKey, pem, AcceptedKeyLength) == 0) + return TRUE; + } + } + + if (tls->isGatewayTransport) + { + free(settings->GatewayAcceptedCert); + settings->GatewayAcceptedCert = NULL; + settings->GatewayAcceptedCertLength = 0; + } + else if (settings->RedirectionFlags != 0) + { + free(settings->RedirectionAcceptedCert); + settings->RedirectionAcceptedCert = NULL; + settings->RedirectionAcceptedCertLength = 0; + } + else + { + free(settings->AcceptedCert); + settings->AcceptedCert = NULL; + settings->AcceptedCertLength = 0; + } + + return FALSE; +} + +static BOOL accept_cert(rdpTls* tls, const BYTE* pem, size_t length) +{ + rdpSettings* settings = tls->settings; + + if (tls->isGatewayTransport) + { + settings->GatewayAcceptedCert = pem; + settings->GatewayAcceptedCertLength = length; + } + else if (settings->RedirectionFlags != 0) + { + settings->RedirectionAcceptedCert = pem; + settings->RedirectionAcceptedCertLength = length; + } + else + { + settings->AcceptedCert = pem; + settings->AcceptedCertLength = length; + } + + return TRUE; +} + int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) { @@ -1123,83 +1199,23 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, BOOL hostname_match = FALSE; BOOL verification_status = FALSE; rdpCertificateData* certificate_data; + freerdp* instance = (freerdp*) tls->settings->instance; + DWORD length; + BYTE* pemCert; - if (tls->settings->ExternalCertificateManagement) - { - BIO* bio; - int status; - int length; - int offset; - BYTE* pemCert; - freerdp* instance = (freerdp*) tls->settings->instance; - /** - * 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 -1; - } - - status = PEM_write_bio_X509(bio, cert->px509); - - if (status < 0) - { - WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status); - return -1; - } - - offset = 0; - length = 2048; - pemCert = (BYTE*) malloc(length + 1); - - if (!pemCert) - { - WLog_ERR(TAG, "error allocating pemCert"); - return -1; - } - - status = BIO_read(bio, pemCert, length); - - if (status < 0) - { - WLog_ERR(TAG, "failed to read certificate"); - return -1; - } - - offset += 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) - return -1; - - length = new_len; - pemCert = new_cert; - status = BIO_read(bio, &pemCert[offset], length); - - if (status < 0) - break; - - offset += status; - } + if (!crypto_cert_get_public_key(cert, &pemCert, &length)) + return -1; - if (status < 0) - { - WLog_ERR(TAG, "failed to read certificate"); - return -1; - } + /* Check, if we already accepted this key. */ + if (is_accepted(tls, pemCert, length)) + { + free(pemCert); + return 1; + } - length = offset; - pemCert[length] = '\0'; - status = -1; + if (tls->settings->ExternalCertificateManagement) + { + int status = -1; if (instance->VerifyX509Certificate) status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, @@ -1207,8 +1223,12 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, else WLog_ERR(TAG, "No VerifyX509Certificate callback registered!"); - free(pemCert); - BIO_free(bio); + if (status > 0) + { + accept_cert(tls, pemCert, length); + } + else + free(pemCert); if (status < 0) { @@ -1222,10 +1242,16 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) + { + free(pemCert); return 1; /* success! */ + } if (!tls->isGatewayTransport && tls->settings->AuthenticationLevel == 0) + { + free(pemCert); return 1; /* success! */ + } /* if user explicitly specified a certificate name, use it instead of the hostname */ if (!tls->isGatewayTransport && tls->settings->CertificateName) @@ -1273,7 +1299,6 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, char* issuer; char* subject; char* fingerprint; - freerdp* instance = (freerdp*) tls->settings->instance; DWORD accept_certificate = 0; issuer = crypto_cert_issuer(cert->px509); subject = crypto_cert_subject(cert->px509); @@ -1384,6 +1409,15 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths, alt_names); + if (verification_status > 0) + { + accept_cert(tls, pemCert, length); + } + else + { + free(pemCert); + } + return (verification_status == 0) ? 0 : 1; } -- 2.7.4