Remember accepted PEM cert to avoid unnecessary user input.

This commit is contained in:
Armin Novak 2018-07-10 10:03:49 +02:00
parent 0d1895e4e7
commit c9cebf6ed6
4 changed files with 140 additions and 94 deletions
include/freerdp
libfreerdp
core
crypto

@ -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 */

@ -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);

@ -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);

@ -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 (!crypto_cert_get_public_key(cert, &pemCert, &length))
return -1;
/* Check, if we already accepted this key. */
if (is_accepted(tls, pemCert, length))
{
free(pemCert);
return 1;
}
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 (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
return -1;
}
length = offset;
pemCert[length] = '\0';
status = -1;
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;
}