Added certificate callbacks with source indications.
authorArmin Novak <armin.novak@thincast.com>
Fri, 30 Nov 2018 09:25:06 +0000 (10:25 +0100)
committerArmin Novak <armin.novak@thincast.com>
Tue, 4 Dec 2018 08:35:24 +0000 (09:35 +0100)
include/freerdp/freerdp.h
libfreerdp/crypto/tls.c

index 0c33cc2..e4c109c 100644 (file)
@@ -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
index 721cc86..f2c08e3 100644 (file)
@@ -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;
 }