[Mono.Security]: Add 'X509Chain' to ICertificateValidator.InvokeSystemValidator and...
authorMartin Baulig <martin.baulig@xamarin.com>
Tue, 17 Nov 2015 18:43:16 +0000 (13:43 -0500)
committerMartin Baulig <martin.baulig@xamarin.com>
Tue, 17 Nov 2015 20:54:44 +0000 (15:54 -0500)
On OS X and Mobile, the X509Chain will be initialized with all the
certificates from the X509CertificateCollection, but not actually
built.

ServicePointManager.ServerCertificateValidationCallback can use the 'chain' argument
to get all the certificates from the server.

(cherry picked from commit 73e37f0ecfd403780d60f36d8b0d68f27987b256)

mcs/class/Mono.Security/Mono.Security.Interface/CertificateValidationHelper.cs
mcs/class/Mono.Security/Mono.Security.Interface/MonoTlsProvider.cs
mcs/class/System/Mono.Net.Security/ChainValidationHelper.cs
mcs/class/System/Mono.Net.Security/SystemCertificateValidator.cs

index 7fab7f7f35d4127d4febd09c9445cafe8c3d39ef..a02a3f9316a75fc5c827426c846c66982232b29b 100644 (file)
@@ -98,9 +98,12 @@ namespace Mono.Security.Interface
                 */
                ValidationResult ValidateCertificate (string targetHost, bool serverMode, X509CertificateCollection certificates);
 
+               /*
+                * On OS X and Mobile, the @chain will be initialized with the @certificates, but not actually built.
+                */
                bool InvokeSystemValidator (
                        string targetHost, bool serverMode, X509CertificateCollection certificates,
-                       ref MonoSslPolicyErrors errors, ref int status11);
+                       X509Chain chain, ref MonoSslPolicyErrors errors, ref int status11);
        }
 
        public static class CertificateValidationHelper
index 7305c51784d6f7dc3e77c9335138d4c3c0cd6d52..dc81e862f2f07690e90e5cc01af83cbf38bc2f27 100644 (file)
@@ -117,13 +117,15 @@ namespace Mono.Security.Interface
                /*
                 * If @serverMode is true, then we're a server and want to validate a certificate
                 * that we received from a client.
-                * 
+                *
+                * On OS X and Mobile, the @chain will be initialized with the @certificates, but not actually built.
+                *
                 * Returns `true` if certificate validation has been performed and `false` to invoke the
                 * default system validator.
                 */
                public virtual bool InvokeSystemCertificateValidator (
                        ICertificateValidator validator, string targetHost, bool serverMode,
-                       X509CertificateCollection certificates, out bool success,
+                       X509CertificateCollection certificates, X509Chain chain, out bool success,
                        ref MonoSslPolicyErrors errors, ref int status11)
                {
                        success = false;
index b7f6d3be94edc9abc408925020d856559743a45f..7c7103fc61eef07b74f36b894c7acd819df4a698 100644 (file)
@@ -49,8 +49,10 @@ using Mono.Security.X509.Extensions;
 #endif
 #if MONO_X509_ALIAS
 using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
+using XX509Chain = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509Chain;
 #else
 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
+using XX509Chain = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509Chain;
 #endif
 
 using System;
@@ -307,17 +309,28 @@ namespace Mono.Net.Security
                        int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
                        X509Chain chain = null;
 
+                       bool wantsChain = SystemCertificateValidator.NeedsChain (settings);
+                       if (!wantsChain && hasCallback) {
+                               if (settings == null || settings.CallbackNeedsCertificateChain)
+                                       wantsChain = true;
+                       }
+
+                       if (wantsChain)
+                               chain = SystemCertificateValidator.CreateX509Chain (certs);
+
+                       if (wantsChain || SystemCertificateValidator.NeedsChain (settings))
+                               SystemCertificateValidator.BuildX509Chain (certs, chain, ref errors, ref status11);
+
                        bool providerValidated = false;
                        if (provider != null && provider.HasCustomSystemCertificateValidator) {
-                               if (SystemCertificateValidator.NeedsChain (settings))
-                                       throw new NotSupportedException ("Cannot use MonoTlsProvider.InvokeSystemCertificateValidator() when the X509Chain is required.");
                                var xerrors = (MonoSslPolicyErrors)errors;
-                               providerValidated = provider.InvokeSystemCertificateValidator (this, host, server, certs, out result, ref xerrors, ref status11);
+                               var xchain = (XX509Chain)(object)chain;
+                               providerValidated = provider.InvokeSystemCertificateValidator (this, host, server, certs, xchain, out result, ref xerrors, ref status11);
                                errors = (SslPolicyErrors)xerrors;
                        }
 
                        if (!providerValidated)
-                               result = SystemCertificateValidator.Evaluate (settings, host, certs, ref chain, ref errors, ref status11);
+                               result = SystemCertificateValidator.Evaluate (settings, host, certs, chain, ref errors, ref status11);
 
                        if (policy != null && (!(policy is DefaultCertificatePolicy) || certValidationCallback == null)) {
                                ServicePoint sp = null;
@@ -343,14 +356,11 @@ namespace Mono.Net.Security
                        return new ValidationResult (result, user_denied, status11, (MonoSslPolicyErrors)errors);
                }
 
-               public bool InvokeSystemValidator (string targetHost, bool serverMode, XX509CertificateCollection certificates, ref MonoSslPolicyErrors xerrors, ref int status11)
+               public bool InvokeSystemValidator (string targetHost, bool serverMode, XX509CertificateCollection certificates, XX509Chain xchain, ref MonoSslPolicyErrors xerrors, ref int status11)
                {
-                       if (SystemCertificateValidator.NeedsChain (settings))
-                               throw new NotSupportedException ("Cannot use ICertificateValidator.InvokeSystemValidator() when the X509Chain is required.");
-
-                       X509Chain chain = null;
+                       X509Chain chain = (X509Chain)(object)xchain;
                        var errors = (SslPolicyErrors)xerrors;
-                       var result = SystemCertificateValidator.Evaluate (settings, targetHost, certificates, ref chain, ref errors, ref status11);
+                       var result = SystemCertificateValidator.Evaluate (settings, targetHost, certificates, chain, ref errors, ref status11);
                        xerrors = (MonoSslPolicyErrors)errors;
                        return result;
                }
index 39cfa73e66d8e6162bbfdb5af2392a68dd4217ad..0809c44880be017ff2540c3929040b2c5eefc7df 100644 (file)
@@ -18,8 +18,10 @@ using Mono.Security.X509.Extensions;
 #endif
 #if MONO_X509_ALIAS
 using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
+using XX509Chain = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509Chain;
 #else
 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
+using XX509Chain = System.Security.Cryptography.X509Certificates.X509Chain;
 #endif
 
 using System;
@@ -72,27 +74,36 @@ namespace Mono.Net.Security
 #endif
                }
 
-               static X509Chain ComputeX509Chain (XX509CertificateCollection certs, ref SslPolicyErrors errors, ref int status11)
+               public static X509Chain CreateX509Chain (XX509CertificateCollection certs)
                {
-#if MOBILE
-                       return null;
-#else
-                       if (is_macosx)
-                               return null;
-
                        var chain = new X509Chain ();
                        chain.ChainPolicy = new X509ChainPolicy ();
 
+#if !MOBILE
                        chain.ChainPolicy.RevocationMode = revocation_mode;
+#endif
 
                        for (int i = 1; i < certs.Count; i++) {
                                chain.ChainPolicy.ExtraStore.Add (certs [i]);
                        }
 
+                       return chain;
+               }
+
+               public static bool BuildX509Chain (XX509CertificateCollection certs, X509Chain chain, ref SslPolicyErrors errors, ref int status11)
+               {
+#if MOBILE
+                       return true;
+#else
+                       if (is_macosx)
+                               return true;
+
                        var leaf = (X509Certificate2)certs [0];
 
+                       bool ok;
                        try {
-                               if (!chain.Build (leaf))
+                               ok = chain.Build (leaf);
+                               if (!ok)
                                        errors |= GetErrorsFromChain (chain);
                        } catch (Exception e) {
                                Console.Error.WriteLine ("ERROR building certificate chain: {0}", e);
@@ -100,13 +111,17 @@ namespace Mono.Net.Security
                                errors |= SslPolicyErrors.RemoteCertificateChainErrors;
                        }
 
-                       status11 = GetStatusFromChain (chain);
+                       try {
+                               status11 = GetStatusFromChain (chain);
+                       } catch {
+                               status11 = 0x800B010B; // TRUST_E_FAIL - generic
+                       }
 
                        return chain;
 #endif
                }
 
-               static void CheckUsage (XX509CertificateCollection certs, string host, ref SslPolicyErrors errors, ref int status11)
+               static bool CheckUsage (XX509CertificateCollection certs, string host, ref SslPolicyErrors errors, ref int status11)
                {
 #if !MONOTOUCH
                        var leaf = (X509Certificate2)certs[0];
@@ -115,14 +130,17 @@ namespace Mono.Net.Security
                                if (!CheckCertificateUsage (leaf)) {
                                        errors |= SslPolicyErrors.RemoteCertificateChainErrors;
                                        status11 = -2146762490; //CERT_E_PURPOSE 0x800B0106
+                                       return false;
                                }
 
                                if (host != null && !CheckServerIdentity (leaf, host)) {
                                        errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
                                        status11 = -2146762481; // CERT_E_CN_NO_MATCH 0x800B010F
+                                       return false;
                                }
                        }
 #endif
+                       return true;
                }
 
                static bool EvaluateSystem (XX509CertificateCollection certs, XX509CertificateCollection anchors, string host, X509Chain chain, ref SslPolicyErrors errors, ref int status11)
@@ -171,14 +189,10 @@ namespace Mono.Net.Security
 
                public static bool Evaluate (
                        MonoTlsSettings settings, string host, XX509CertificateCollection certs,
-                       ref X509Chain chain, ref SslPolicyErrors errors, ref int status11)
+                       X509Chain chain, ref SslPolicyErrors errors, ref int status11)
                {
-#if !MOBILE
-                       if (NeedsChain (settings) && chain == null)
-                               chain = ComputeX509Chain (certs, ref errors, ref status11);
-#endif
-
-                       CheckUsage (certs, host, ref errors, ref status11);
+                       if (!CheckUsage (certs, host, ref errors, ref status11))
+                               return false;
 
                        if (settings != null && settings.SkipSystemValidators)
                                return false;