[appletls] Trust SecTrustResult.Proceed as well. Fixes #58411.
[mono.git] / mcs / class / System / Mono.AppleTls / AppleCertificateHelper.cs
index d26abe989b415a863bc606a041fb63976513df85..073f16080044f4d5315b339ea99418573e80d51c 100644 (file)
@@ -36,14 +36,63 @@ namespace Mono.AppleTls
                         */
                        var certificate2 = certificate as X509Certificate2;
                        if (certificate2 != null)
+#if MONOTOUCH
                                return SecIdentity.Import (certificate2);
+#else
+                               return SecImportExport.ItemImport (certificate2);
+#endif
 
                        /*
-                        * Otherwise, we require the private key to be in the keychain.
+                        * Reading Certificates from the Mac Keychain
+                        * ==========================================
+                        *
+                        * Reading the private key from the keychain is a new feature introduced with
+                        * AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several
+                        * known issues and it also did not received any testing yet. We go back to the old
+                        * way of doing things, which is to explicitly provide an X509Certificate2 with a
+                        * private key.
+                        * 
+                        * Keychain Dialog Popups
+                        * ======================
+                        * 
+                        * When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain
+                        * for the certificate and private key.
+                        * 
+                        * On Xamarin.iOS, this is easy because each app has its own keychain.
+                        * 
+                        * On Xamarin.Mac, the .app package needs to be trusted via code-sign
+                        * to get permission to access the user's keychain. [FIXME: I still have to
+                        * research how to actually do that.] Without this, you will get a popup
+                        * message each time, asking you whether you want to allow the app to access
+                        * the keychain, but you can make these go away by selecting "Trust always".
+                        * 
+                        * On Desktop Mono, this is problematic because selecting "Trust always"
+                        * give the 'mono' binary (and thus everything you'll ever run with Mono)
+                        * permission to retrieve the private key from the keychain.
+                        * 
+                        * This code would also trigger constant keychain popup messages,
+                        * which could only be suppressed by granting full trust. It also makes it
+                        * impossible to run Mono in headless mode.
+                        * 
+                        * SecIdentityCreate
+                        * =================
+                        * 
+                        * To avoid these problems, we are currently using an undocumented API
+                        * called SecIdentityRef() to avoid using the Mac keychain whenever a
+                        * X509Certificate2 with a private key is used.
+                        * 
+                        * On iOS and XamMac, you can still provide the X509Certificate without
+                        * a private key - in this case, a keychain search will be performed (and you
+                        * may get a popup message on XamMac).
                         */
+
+#if MOBILE
                        using (var secCert = new SecCertificate (certificate)) {
                                return SecKeyChain.FindIdentity (secCert, true);
                        }
+#else
+                       return null;
+#endif
                }
 
                public static SecIdentity GetIdentity (X509Certificate certificate, out SecCertificate[] intermediateCerts)
@@ -84,22 +133,28 @@ namespace Mono.AppleTls
                                        targetHost = targetHost.Substring (0, pos);
                        }
 
-                       var policy = SecPolicy.CreateSslPolicy (!serverMode, targetHost);
-                       var trust = new SecTrust (certificates, policy);
+                       using (var policy = SecPolicy.CreateSslPolicy (!serverMode, targetHost))
+                       using (var trust = new SecTrust (certificates, policy)) {
+                               if (validator.Settings.TrustAnchors != null) {
+                                       var status = trust.SetAnchorCertificates (validator.Settings.TrustAnchors);
+                                       if (status != SecStatusCode.Success)
+                                               throw new InvalidOperationException (status.ToString ());
+                                       trust.SetAnchorCertificatesOnly (false);
+                               }
 
-                       if (validator.Settings.TrustAnchors != null) {
-                               var status = trust.SetAnchorCertificates (validator.Settings.TrustAnchors);
-                               if (status != SecStatusCode.Success)
-                                       throw new InvalidOperationException (status.ToString ());
-                               trust.SetAnchorCertificatesOnly (false);
-                       }
+                               if (validator.Settings.CertificateValidationTime != null) {
+                                       var status = trust.SetVerifyDate (validator.Settings.CertificateValidationTime.Value);
+                                       if (status != SecStatusCode.Success)
+                                               throw new InvalidOperationException (status.ToString ());
+                               }
 
-                       var result = trust.Evaluate ();
-                       if (result == SecTrustResult.Unspecified)
-                               return true;
+                               var result = trust.Evaluate ();
+                               if (result == SecTrustResult.Unspecified || result == SecTrustResult.Proceed)
+                                       return true;
 
-                       errors |= MonoSslPolicyErrors.RemoteCertificateChainErrors;
-                       return false;
+                               errors |= MonoSslPolicyErrors.RemoteCertificateChainErrors;
+                               return false;
+                       }
                }
        }
 }