From 7ab07ab980c05f5413666ac34ead3c159b0f33d7 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Fri, 30 Nov 2018 10:25:06 +0100 Subject: [PATCH] Added certificate callbacks with source indications. --- include/freerdp/freerdp.h | 82 +++++++++++++++++++++++++++++++++++++++++------ libfreerdp/crypto/tls.c | 69 ++++++++++++++++++++------------------- 2 files changed, 108 insertions(+), 43 deletions(-) diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 0c33cc2..e4c109c 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -59,6 +59,14 @@ typedef RDP_CLIENT_ENTRY_POINTS_V1 RDP_CLIENT_ENTRY_POINTS; extern "C" { #endif +/* Flags used by certificate callbacks */ +#define VERIFY_CERT_FLAG_NONE 0x00 +#define VERIFY_CERT_FLAG_LEGACY 0x02 +#define VERIFY_CERT_FLAG_REDIRECT 0x10 +#define VERIFY_CERT_FLAG_GATEWAY 0x20 +#define VERIFY_CERT_FLAG_CHANGED 0x40 +#define VERIFY_CERT_FLAG_MISMATCH 0x80 + typedef BOOL (*pContextNew)(freerdp* instance, rdpContext* context); typedef void (*pContextFree)(freerdp* instance, rdpContext* context); @@ -71,6 +79,7 @@ typedef BOOL (*pAuthenticate)(freerdp* instance, char** username, /** @brief Callback used if user interaction is required to accept * an unknown certificate. * + * @deprecated Use pVerifyCertificateEx * @param common_name The certificate registered hostname. * @param subject The common name of the certificate. * @param issuer The issuer of the certificate. @@ -89,8 +98,32 @@ typedef DWORD (*pVerifyCertificate)(freerdp* instance, BOOL host_mismatch); /** @brief Callback used if user interaction is required to accept + * an unknown certificate. + * + * @param host The hostname connecting to. + * @param port The port connecting to. + * @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 flags Flags of type VERIFY_CERT_FLAG* + * + * @return 1 to accept and store a certificate, 2 to accept + * a certificate only for this session, 0 otherwise. + */ +typedef DWORD (*pVerifyCertificateEx)(freerdp* instance, + const char* host, + UINT16 port, + const char* common_name, + const char* subject, + const char* issuer, + const char* fingerprint, + DWORD flags); + +/** @brief Callback used if user interaction is required to accept * a changed certificate. * + * @deprecated Use pVerifyChangedCertificateEx * @param common_name The certificate registered hostname. * @param subject The common name of the new certificate. * @param issuer The issuer of the new certificate. @@ -112,13 +145,35 @@ typedef DWORD (*pVerifyChangedCertificate)(freerdp* instance, const char* old_issuer, const char* old_fingerprint); +/** @brief Callback used if user interaction is required to accept + * a changed certificate. + * + * @param host The hostname connecting to. + * @param port The port connecting to. + * @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 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 flags Flags of type VERIFY_CERT_FLAG* + * + * @return 1 to accept and store a certificate, 2 to accept + * a certificate only for this session, 0 otherwise. + */ -#define VERIFY_X509_CERT_FLAG_NONE 0x00 -#define VERIFY_X509_CERT_FLAG_LEGACY 0x02 -#define VERIFY_X509_CERT_FLAG_REDIRECT 0x10 -#define VERIFY_X509_CERT_FLAG_GATEWAY 0x20 -#define VERIFY_X509_CERT_FLAG_CHANGED 0x40 -#define VERIFY_X509_CERT_FLAG_MISMATCH 0x80 +typedef DWORD (*pVerifyChangedCertificateEx)(freerdp* instance, + const char* host, + UINT16 port, + const char* common_name, + const char* subject, + const char* issuer, + const char* new_fingerprint, + const char* old_subject, + const char* old_issuer, + const char* old_fingerprint, + DWORD flags); /** @brief Callback used if user interaction is required to accept * a certificate. @@ -128,7 +183,7 @@ typedef DWORD (*pVerifyChangedCertificate)(freerdp* instance, * @param length The length of the certificate data. * @param hostname The hostname connecting to. * @param port The port connecting to. - * @param flags The issuer of the new certificate. + * @param flags Flags of type VERIFY_CERT_FLAG* * * @return 1 to accept and store a certificate, 2 to accept * a certificate only for this session, 0 otherwise. @@ -296,11 +351,12 @@ struct rdp_freerdp It is used to get the username/password when it was not provided at connection time. */ ALIGN64 pVerifyCertificate VerifyCertificate; /**< (offset 51) Callback for certificate validation. - Used to verify that an unknown certificate is trusted. */ + Used to verify that an unknown certificate is trusted. + DEPRECATED: Use VerifyChangedCertificateEx*/ ALIGN64 pVerifyChangedCertificate VerifyChangedCertificate; /**< (offset 52) Callback for changed certificate validation. Used when a certificate differs from stored fingerprint. - If returns TRUE, the new fingerprint will be trusted and old thrown out. */ + DEPRECATED: Use VerifyChangedCertificateEx */ ALIGN64 pVerifyX509Certificate VerifyX509Certificate; /**< (offset 53) Callback for X509 certificate verification (PEM format) */ @@ -327,7 +383,13 @@ struct rdp_freerdp This is called by freerdp_channel_process() (if not NULL). Clients will typically use a function that calls freerdp_channels_data() to perform the needed tasks. */ - UINT64 paddingE[80 - 66]; /* 66 */ + ALIGN64 pVerifyCertificateEx VerifyCertificateEx; /**< (offset 66) + Callback for certificate validation. + Used to verify that an unknown certificate is trusted. */ + ALIGN64 pVerifyChangedCertificateEx VerifyChangedCertificateEx; /**< (offset 67) + Callback for changed certificate validation. + Used when a certificate differs from stored fingerprint. */ + UINT64 paddingE[80 - 68]; /* 68 */ }; struct rdp_channel_handles diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 721cc86..f2c08e3 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -1318,30 +1318,27 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, BOOL certificate_status; BOOL hostname_match = FALSE; BOOL verification_status = FALSE; - rdpCertificateData* certificate_data; + rdpCertificateData* certificate_data = NULL; freerdp* instance = (freerdp*) tls->settings->instance; DWORD length; - BYTE* pemCert; - DWORD flags = VERIFY_X509_CERT_FLAG_NONE; + BYTE* pemCert = NULL; + DWORD flags = VERIFY_CERT_FLAG_NONE; if (!tls_extract_pem(cert, &pemCert, &length)) - return -1; + goto end; /* Check, if we already accepted this key. */ if (is_accepted(tls, pemCert, length)) - { - free(pemCert); - return 1; - } + goto end; if (tls->isGatewayTransport || is_redirected(tls)) - flags |= VERIFY_X509_CERT_FLAG_LEGACY; + flags |= VERIFY_CERT_FLAG_LEGACY; if (tls->isGatewayTransport) - flags |= VERIFY_X509_CERT_FLAG_GATEWAY; + flags |= VERIFY_CERT_FLAG_GATEWAY; if (is_redirected(tls)) - flags |= VERIFY_X509_CERT_FLAG_REDIRECT; + flags |= VERIFY_CERT_FLAG_REDIRECT; if (tls->settings->ExternalCertificateManagement) { @@ -1354,33 +1351,29 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, WLog_ERR(TAG, "No VerifyX509Certificate callback registered!"); if (status > 0) - { accept_cert(tls, pemCert, length); - } else if (status < 0) { WLog_ERR(TAG, "VerifyX509Certificate failed: (length = %d) status: [%d] %s", length, status, pemCert); - free(pemCert); - return -1; + goto end; } - else - free(pemCert); - return (status == 0) ? 0 : 1; + verification_status = (status == 0) ? FALSE : TRUE; + goto end; } /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) { - free(pemCert); - return 1; /* success! */ + verification_status = TRUE; + goto end; /* success! */ } if (!tls->isGatewayTransport && tls->settings->AuthenticationLevel == 0) { - free(pemCert); - return 1; /* success! */ + verification_status = TRUE; + goto end; /* success! */ } /* if user explicitly specified a certificate name, use it instead of the hostname */ @@ -1424,7 +1417,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, verification_status = TRUE; /* success! */ if (!hostname_match) - flags |= VERIFY_X509_CERT_FLAG_MISMATCH; + flags |= VERIFY_CERT_FLAG_MISMATCH; /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */ if (!certificate_status || !hostname_match) @@ -1466,6 +1459,13 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, else accept_certificate = 0; } + else if (instance->VerifyCertificateEx) + { + accept_certificate = instance->VerifyCertificateEx( + instance, hostname, port, common_name, + subject, issuer, + fingerprint, flags); + } else if (instance->VerifyCertificate) { accept_certificate = instance->VerifyCertificate( @@ -1511,7 +1511,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, if (instance->VerifyX509Certificate) { const int rc = instance->VerifyX509Certificate(instance, pemCert, length, hostname, - port, flags | VERIFY_X509_CERT_FLAG_CHANGED); + port, flags | VERIFY_CERT_FLAG_CHANGED); if (rc == 1) accept_certificate = 1; @@ -1520,6 +1520,13 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, else accept_certificate = 0; } + else if (instance->VerifyChangedCertificateEx) + { + accept_certificate = instance->VerifyChangedCertificateEx( + instance, hostname, port, common_name, subject, issuer, + fingerprint, old_subject, old_issuer, + old_fingerprint, flags | VERIFY_CERT_FLAG_CHANGED); + } else if (instance->VerifyChangedCertificate) { accept_certificate = instance->VerifyChangedCertificate( @@ -1559,6 +1566,10 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, free(fingerprint); } + if (verification_status > 0) + accept_cert(tls, pemCert, length); + +end: certificate_data_free(certificate_data); free(common_name); @@ -1566,15 +1577,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, crypto_cert_dns_names_free(dns_names_count, dns_names_lengths, dns_names); - if (verification_status > 0) - { - accept_cert(tls, pemCert, length); - } - else - { - free(pemCert); - } - + free(pemCert); return (verification_status == 0) ? 0 : 1; } -- 2.7.4