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 #if MONO_SECURITY_ALIAS
39 using MonoSecurity::Mono.Security.Interface;
40 using MSX = MonoSecurity::Mono.Security.X509;
41 using MonoSecurity::Mono.Security.X509.Extensions;
43 using Mono.Security.Interface;
44 using MSX = Mono.Security.X509;
45 using Mono.Security.X509.Extensions;
50 using System.Threading;
51 using System.Collections;
52 using System.Collections.Generic;
53 using System.Collections.Specialized;
54 using System.Configuration;
55 using System.Net.Configuration;
56 using System.Text.RegularExpressions;
57 using System.Security.Cryptography.X509Certificates;
59 using System.Globalization;
60 using System.Net.Security;
61 using System.Diagnostics;
63 namespace Mono.Net.Security
65 internal delegate bool ServerCertValidationCallbackWrapper (ServerCertValidationCallback callback, X509Certificate certificate, X509Chain chain, MonoSslPolicyErrors sslPolicyErrors);
67 internal class ChainValidationHelper : ICertificateValidator2
69 readonly object sender;
70 readonly MonoTlsSettings settings;
71 readonly MonoTlsProvider provider;
72 readonly ServerCertValidationCallback certValidationCallback;
73 readonly LocalCertSelectionCallback certSelectionCallback;
74 readonly ServerCertValidationCallbackWrapper callbackWrapper;
75 readonly MonoTlsStream tlsStream;
76 readonly HttpWebRequest request;
78 #pragma warning disable 618
80 internal static ICertificateValidator GetInternalValidator (MonoTlsProvider provider, MonoTlsSettings settings)
83 return new ChainValidationHelper (provider, null, false, null, null);
84 if (settings.CertificateValidator != null)
85 return settings.CertificateValidator;
86 return new ChainValidationHelper (provider, settings, false, null, null);
89 internal static ICertificateValidator GetDefaultValidator (MonoTlsSettings settings)
91 var provider = MonoTlsProviderFactory.GetProvider ();
93 return new ChainValidationHelper (provider, null, false, null, null);
94 if (settings.CertificateValidator != null)
95 throw new NotSupportedException ();
96 return new ChainValidationHelper (provider, settings, false, null, null);
99 #region SslStream support
102 * This is a hack which is used in SslStream - see ReferenceSources/SslStream.cs for details.
104 internal static ChainValidationHelper CloneWithCallbackWrapper (MonoTlsProvider provider, ref MonoTlsSettings settings, ServerCertValidationCallbackWrapper wrapper)
106 var helper = (ChainValidationHelper)settings.CertificateValidator;
108 helper = new ChainValidationHelper (provider, settings, true, null, wrapper);
110 helper = new ChainValidationHelper (helper, provider, settings, wrapper);
111 settings = helper.settings;
115 internal static bool InvokeCallback (ServerCertValidationCallback callback, object sender, X509Certificate certificate, X509Chain chain, MonoSslPolicyErrors sslPolicyErrors)
117 return callback.Invoke (sender, certificate, chain, (SslPolicyErrors)sslPolicyErrors);
122 ChainValidationHelper (ChainValidationHelper other, MonoTlsProvider provider, MonoTlsSettings settings, ServerCertValidationCallbackWrapper callbackWrapper = null)
124 sender = other.sender;
125 certValidationCallback = other.certValidationCallback;
126 certSelectionCallback = other.certSelectionCallback;
127 tlsStream = other.tlsStream;
128 request = other.request;
130 if (settings == null)
131 settings = MonoTlsSettings.DefaultSettings;
133 this.provider = provider;
134 this.settings = settings.CloneWithValidator (this);
135 this.callbackWrapper = callbackWrapper;
138 internal static ChainValidationHelper Create (MonoTlsProvider provider, ref MonoTlsSettings settings, MonoTlsStream stream)
140 var helper = new ChainValidationHelper (provider, settings, true, stream, null);
141 settings = helper.settings;
145 ChainValidationHelper (MonoTlsProvider provider, MonoTlsSettings settings, bool cloneSettings, MonoTlsStream stream, ServerCertValidationCallbackWrapper callbackWrapper)
147 if (settings == null)
148 settings = MonoTlsSettings.CopyDefaultSettings ();
150 settings = settings.CloneWithValidator (this);
151 if (provider == null)
152 provider = MonoTlsProviderFactory.GetProvider ();
154 this.provider = provider;
155 this.settings = settings;
156 this.tlsStream = stream;
157 this.callbackWrapper = callbackWrapper;
159 var fallbackToSPM = false;
161 if (settings != null) {
162 if (settings.RemoteCertificateValidationCallback != null) {
163 var callback = Private.CallbackHelpers.MonoToPublic (settings.RemoteCertificateValidationCallback);
164 certValidationCallback = new ServerCertValidationCallback (callback);
166 certSelectionCallback = Private.CallbackHelpers.MonoToInternal (settings.ClientCertificateSelectionCallback);
167 fallbackToSPM = settings.UseServicePointManagerCallback ?? stream != null;
170 if (stream != null) {
171 this.request = stream.Request;
172 this.sender = request;
174 if (certValidationCallback == null)
175 certValidationCallback = request.ServerCertValidationCallback;
176 if (certSelectionCallback == null)
177 certSelectionCallback = new LocalCertSelectionCallback (DefaultSelectionCallback);
179 if (settings == null)
180 fallbackToSPM = true;
183 if (fallbackToSPM && certValidationCallback == null)
184 certValidationCallback = ServicePointManager.ServerCertValidationCallback;
187 #pragma warning restore 618
189 static X509Certificate DefaultSelectionCallback (string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
191 X509Certificate clientCertificate;
192 if (localCertificates == null || localCertificates.Count == 0)
193 clientCertificate = null;
195 clientCertificate = localCertificates [0];
196 return clientCertificate;
199 public MonoTlsProvider Provider {
200 get { return provider; }
203 public MonoTlsSettings Settings {
204 get { return settings; }
207 public bool HasCertificateSelectionCallback {
208 get { return certSelectionCallback != null; }
211 public bool SelectClientCertificate (
212 string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate,
213 string[] acceptableIssuers, out X509Certificate clientCertificate)
215 if (certSelectionCallback == null) {
216 clientCertificate = null;
219 clientCertificate = certSelectionCallback (targetHost, localCertificates, remoteCertificate, acceptableIssuers);
223 internal X509Certificate SelectClientCertificate (
224 string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate,
225 string[] acceptableIssuers)
227 if (certSelectionCallback == null)
229 return certSelectionCallback (targetHost, localCertificates, remoteCertificate, acceptableIssuers);
232 internal bool ValidateClientCertificate (X509Certificate certificate, MonoSslPolicyErrors errors)
234 var certs = new X509CertificateCollection ();
235 certs.Add (new X509Certificate2 (certificate.GetRawCertData ()));
237 var result = ValidateChain (string.Empty, true, certificate, null, certs, (SslPolicyErrors)errors);
241 return result.Trusted && !result.UserDenied;
244 public ValidationResult ValidateCertificate (string host, bool serverMode, X509CertificateCollection certs)
247 X509Certificate leaf;
248 if (certs != null && certs.Count != 0)
252 var result = ValidateChain (host, serverMode, leaf, null, certs, 0);
253 if (tlsStream != null)
254 tlsStream.CertificateValidationFailed = result == null || !result.Trusted || result.UserDenied;
257 if (tlsStream != null)
258 tlsStream.CertificateValidationFailed = true;
263 public ValidationResult ValidateCertificate (string host, bool serverMode, X509Certificate leaf, X509Chain chain)
266 var result = ValidateChain (host, serverMode, leaf, chain, null, 0);
267 if (tlsStream != null)
268 tlsStream.CertificateValidationFailed = result == null || !result.Trusted || result.UserDenied;
271 if (tlsStream != null)
272 tlsStream.CertificateValidationFailed = true;
277 ValidationResult ValidateChain (string host, bool server, X509Certificate leaf,
278 X509Chain chain, X509CertificateCollection certs,
279 SslPolicyErrors errors)
281 var oldChain = chain;
282 var ownsChain = chain == null;
284 var result = ValidateChain (host, server, leaf, ref chain, certs, errors);
285 if (chain != oldChain)
290 // If ValidateChain() changed the chain, then we need to free it.
291 if (ownsChain && chain != null)
296 ValidationResult ValidateChain (string host, bool server, X509Certificate leaf,
297 ref X509Chain chain, X509CertificateCollection certs,
298 SslPolicyErrors errors)
300 // user_denied is true if the user callback is called and returns false
301 bool user_denied = false;
304 var hasCallback = certValidationCallback != null || callbackWrapper != null;
306 if (tlsStream != null)
307 request.ServicePoint.UpdateServerCertificate (leaf);
310 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
312 if (callbackWrapper != null)
313 result = callbackWrapper.Invoke (certValidationCallback, leaf, null, (MonoSslPolicyErrors)errors);
315 result = certValidationCallback.Invoke (sender, leaf, null, errors);
316 user_denied = !result;
318 return new ValidationResult (result, user_denied, 0, (MonoSslPolicyErrors)errors);
321 // Ignore port number when validating certificates.
322 if (!string.IsNullOrEmpty (host)) {
323 var pos = host.IndexOf (':');
325 host = host.Substring (0, pos);
328 ICertificatePolicy policy = ServicePointManager.GetLegacyCertificatePolicy ();
330 int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
332 bool wantsChain = SystemCertificateValidator.NeedsChain (settings);
333 if (!wantsChain && hasCallback) {
334 if (settings == null || settings.CallbackNeedsCertificateChain)
338 var xerrors = (MonoSslPolicyErrors)errors;
339 result = provider.ValidateCertificate (this, host, server, certs, wantsChain, ref chain, ref xerrors, ref status11);
340 errors = (SslPolicyErrors)xerrors;
342 if (status11 == 0 && errors != 0) {
344 status11 = unchecked ((int)0x800B010B);
347 if (policy != null && (!(policy is DefaultCertificatePolicy) || certValidationCallback == null)) {
348 ServicePoint sp = null;
350 sp = request.ServicePointNoLock;
353 result = policy.CheckValidationResult (sp, leaf, request, status11);
354 user_denied = !result && !(policy is DefaultCertificatePolicy);
356 // If there's a 2.0 callback, it takes precedence
358 if (callbackWrapper != null)
359 result = callbackWrapper.Invoke (certValidationCallback, leaf, chain, (MonoSslPolicyErrors)errors);
361 result = certValidationCallback.Invoke (sender, leaf, chain, errors);
362 user_denied = !result;
364 return new ValidationResult (result, user_denied, status11, (MonoSslPolicyErrors)errors);
367 bool InvokeSystemValidator (string targetHost, bool serverMode, X509CertificateCollection certificates, X509Chain chain, ref MonoSslPolicyErrors xerrors, ref int status11)
369 var errors = (SslPolicyErrors)xerrors;
370 var result = SystemCertificateValidator.Evaluate (settings, targetHost, certificates, chain, ref errors, ref status11);
371 xerrors = (MonoSslPolicyErrors)errors;