2 // System.Net.ServicePointManager
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 // Copyright (c) 2003-2010 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 #if MONO_SECURITY_ALIAS
35 extern alias MonoSecurity;
38 extern alias PrebuiltSystem;
41 #if MONO_SECURITY_ALIAS
42 using MonoSecurity::Mono.Security.Interface;
43 using MSX = MonoSecurity::Mono.Security.X509;
44 using MonoSecurity::Mono.Security.X509.Extensions;
46 using Mono.Security.Interface;
47 using MSX = Mono.Security.X509;
48 using Mono.Security.X509.Extensions;
51 using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
53 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
58 using System.Threading;
59 using System.Collections;
60 using System.Collections.Generic;
61 using System.Collections.Specialized;
62 using System.Configuration;
63 using System.Net.Configuration;
64 using System.Text.RegularExpressions;
65 using System.Security.Cryptography.X509Certificates;
67 using System.Globalization;
68 using System.Net.Security;
69 using System.Diagnostics;
71 namespace Mono.Net.Security
73 internal delegate bool ServerCertValidationCallbackWrapper (ServerCertValidationCallback callback, X509Certificate certificate, X509Chain chain, MonoSslPolicyErrors sslPolicyErrors);
75 internal class ChainValidationHelper : ICertificateValidator
77 readonly object sender;
78 readonly MonoTlsSettings settings;
79 readonly MonoTlsProvider provider;
80 readonly ServerCertValidationCallback certValidationCallback;
81 readonly LocalCertSelectionCallback certSelectionCallback;
82 readonly ServerCertValidationCallbackWrapper callbackWrapper;
83 readonly MonoTlsStream tlsStream;
84 readonly HttpWebRequest request;
86 static bool is_macosx;
87 static bool is_mobile;
89 static X509RevocationMode revocation_mode;
92 static ChainValidationHelper ()
101 is_macosx = System.IO.File.Exists (OSX509Certificates.SecurityLibrary);
106 revocation_mode = X509RevocationMode.NoCheck;
108 string str = Environment.GetEnvironmentVariable ("MONO_X509_REVOCATION_MODE");
109 if (String.IsNullOrEmpty (str))
111 revocation_mode = (X509RevocationMode)Enum.Parse (typeof(X509RevocationMode), str, true);
117 internal static ICertificateValidator GetDefaultValidator (MonoTlsProvider provider, MonoTlsSettings settings)
119 if (settings == null)
120 return new ChainValidationHelper (provider, null, false, null, null);
121 if (settings.CertificateValidator != null)
122 return settings.CertificateValidator;
123 return new ChainValidationHelper (provider, settings, false, null, null);
126 #region SslStream support
129 * This is a hack which is used in SslStream - see ReferenceSources/SslStream.cs for details.
131 internal static ChainValidationHelper CloneWithCallbackWrapper (MonoTlsProvider provider, ref MonoTlsSettings settings, ServerCertValidationCallbackWrapper wrapper)
133 var helper = (ChainValidationHelper)settings.CertificateValidator;
135 helper = new ChainValidationHelper (provider, settings, true, null, wrapper);
137 helper = new ChainValidationHelper (helper, provider, settings, wrapper);
138 settings = helper.settings;
142 internal static bool InvokeCallback (ServerCertValidationCallback callback, object sender, X509Certificate certificate, X509Chain chain, MonoSslPolicyErrors sslPolicyErrors)
144 return callback.Invoke (sender, certificate, chain, (SslPolicyErrors)sslPolicyErrors);
149 ChainValidationHelper (ChainValidationHelper other, MonoTlsProvider provider, MonoTlsSettings settings, ServerCertValidationCallbackWrapper callbackWrapper = null)
151 sender = other.sender;
152 certValidationCallback = other.certValidationCallback;
153 certSelectionCallback = other.certSelectionCallback;
154 tlsStream = other.tlsStream;
155 request = other.request;
157 this.provider = provider;
158 this.settings = settings = settings.CloneWithValidator (this);
159 this.callbackWrapper = callbackWrapper;
162 internal static ChainValidationHelper Create (MonoTlsProvider provider, ref MonoTlsSettings settings, MonoTlsStream stream)
164 var helper = new ChainValidationHelper (provider, settings, true, stream, null);
165 settings = helper.settings;
169 ChainValidationHelper (MonoTlsProvider provider, MonoTlsSettings settings, bool cloneSettings, MonoTlsStream stream, ServerCertValidationCallbackWrapper callbackWrapper)
172 settings = settings.CloneWithValidator (this);
174 this.provider = provider;
175 this.settings = settings;
176 this.tlsStream = stream;
177 this.callbackWrapper = callbackWrapper;
179 var fallbackToSPM = false;
181 if (settings != null) {
182 if (settings.RemoteCertificateValidationCallback != null) {
183 var callback = Private.CallbackHelpers.MonoToPublic (settings.RemoteCertificateValidationCallback);
184 certValidationCallback = new ServerCertValidationCallback (callback);
186 certSelectionCallback = Private.CallbackHelpers.MonoToInternal (settings.ClientCertificateSelectionCallback);
187 fallbackToSPM = settings.UseServicePointManagerCallback;
190 if (stream != null) {
191 this.request = stream.Request;
192 this.sender = request;
194 if (certValidationCallback == null)
195 certValidationCallback = request.ServerCertValidationCallback;
196 if (certSelectionCallback == null)
197 certSelectionCallback = new LocalCertSelectionCallback (DefaultSelectionCallback);
199 if (settings == null)
200 fallbackToSPM = true;
203 if (fallbackToSPM && certValidationCallback == null)
204 certValidationCallback = ServicePointManager.ServerCertValidationCallback;
207 static X509Certificate DefaultSelectionCallback (string targetHost, XX509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
209 X509Certificate clientCertificate;
210 if (localCertificates == null || localCertificates.Count == 0)
211 clientCertificate = null;
213 clientCertificate = localCertificates [0];
214 return clientCertificate;
217 public MonoTlsProvider Provider {
218 get { return provider; }
221 public MonoTlsSettings Settings {
222 get { return settings; }
225 public bool HasCertificateSelectionCallback {
226 get { return certSelectionCallback != null; }
229 public X509Certificate SelectClientCertificate (
230 string targetHost, XX509CertificateCollection localCertificates, X509Certificate remoteCertificate,
231 string[] acceptableIssuers)
233 if (certSelectionCallback == null)
235 return certSelectionCallback (targetHost, localCertificates, remoteCertificate, acceptableIssuers);
238 internal bool ValidateClientCertificate (X509Certificate certificate, MonoSslPolicyErrors errors)
240 var certs = new XX509CertificateCollection ();
241 certs.Add (new X509Certificate2 (certificate.GetRawCertData ()));
243 var result = ValidateChain (string.Empty, true, certs, (SslPolicyErrors)errors);
247 return result.Trusted && !result.UserDenied;
250 public ValidationResult ValidateClientCertificate (XX509CertificateCollection certs)
252 return ValidateChain (string.Empty, true, certs, 0);
255 public ValidationResult ValidateChain (string host, XX509CertificateCollection certs)
258 var result = ValidateChain (host, false, certs, 0);
259 if (tlsStream != null)
260 tlsStream.CertificateValidationFailed = result == null || !result.Trusted || result.UserDenied;
263 if (tlsStream != null)
264 tlsStream.CertificateValidationFailed = true;
269 ValidationResult ValidateChain (string host, bool server, XX509CertificateCollection certs, SslPolicyErrors errors)
271 // user_denied is true if the user callback is called and returns false
272 bool user_denied = false;
275 var hasCallback = certValidationCallback != null || callbackWrapper != null;
277 X509Certificate leaf;
278 if (certs == null || certs.Count == 0)
283 if (tlsStream != null)
284 request.ServicePoint.SetServerCertificate (leaf);
287 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
289 if (callbackWrapper != null)
290 result = callbackWrapper.Invoke (certValidationCallback, leaf, null, (MonoSslPolicyErrors)errors);
292 result = certValidationCallback.Invoke (sender, leaf, null, errors);
293 user_denied = !result;
295 return new ValidationResult (result, user_denied, 0, (MonoSslPolicyErrors)errors);
298 ICertificatePolicy policy = ServicePointManager.GetLegacyCertificatePolicy ();
300 int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
301 X509Chain chain = null;
303 if (provider != null && provider.HasCustomSystemCertificateValidator) {
304 if (SystemCertificateValidator.NeedsChain (settings))
305 throw new NotSupportedException ("Cannot use MonoTlsProvider.InvokeSystemCertificateValidator() when the X509Chain is required.");
306 var xerrors = (MonoSslPolicyErrors)errors;
307 result = provider.InvokeSystemCertificateValidator (this, host, server, certs, ref xerrors, ref status11);
308 errors = (SslPolicyErrors)xerrors;
310 result = SystemCertificateValidator.Evaluate (settings, host, certs, ref chain, ref errors, ref status11);
313 if (policy != null && (!(policy is DefaultCertificatePolicy) || certValidationCallback == null)) {
314 ServicePoint sp = null;
316 sp = request.ServicePointNoLock;
317 if (status11 == 0 && errors != 0) {
319 status11 = unchecked ((int)0x800B010B);
323 result = policy.CheckValidationResult (sp, leaf, request, status11);
324 user_denied = !result && !(policy is DefaultCertificatePolicy);
326 // If there's a 2.0 callback, it takes precedence
328 if (callbackWrapper != null)
329 result = callbackWrapper.Invoke (certValidationCallback, leaf, chain, (MonoSslPolicyErrors)errors);
331 result = certValidationCallback.Invoke (sender, leaf, chain, errors);
332 user_denied = !result;
334 return new ValidationResult (result, user_denied, status11, (MonoSslPolicyErrors)errors);
337 public bool InvokeSystemValidator (string targetHost, bool serverMode, XX509CertificateCollection certificates, ref MonoSslPolicyErrors xerrors, ref int status11)
339 if (SystemCertificateValidator.NeedsChain (settings))
340 throw new NotSupportedException ("Cannot use ICertificateValidator.InvokeSystemValidator() when the X509Chain is required.");
342 X509Chain chain = null;
343 var errors = (SslPolicyErrors)xerrors;
344 var result = SystemCertificateValidator.Evaluate (settings, targetHost, certificates, ref chain, ref errors, ref status11);
345 xerrors = (MonoSslPolicyErrors)errors;