Merge pull request #2819 from BrzVlad/fix-major-log
[mono.git] / mcs / class / System / Mono.Net.Security / ChainValidationHelper.cs
1 //
2 // System.Net.ServicePointManager
3 //
4 // Authors:
5 //   Lawrence Pit (loz@cable.a2000.nl)
6 //   Gonzalo Paniagua Javier (gonzalo@novell.com)
7 //
8 // Copyright (c) 2003-2010 Novell, Inc (http://www.novell.com)
9 //
10
11 //
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:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
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.
30 //
31
32 #if SECURITY_DEP
33
34 #if MONO_SECURITY_ALIAS
35 extern alias MonoSecurity;
36 #endif
37 #if MONO_X509_ALIAS
38 extern alias PrebuiltSystem;
39 #endif
40
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;
45 #else
46 using Mono.Security.Interface;
47 using MSX = Mono.Security.X509;
48 using Mono.Security.X509.Extensions;
49 #endif
50 #if MONO_X509_ALIAS
51 using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
52 using XX509Chain = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509Chain;
53 #else
54 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
55 using XX509Chain = System.Security.Cryptography.X509Certificates.X509Chain;
56 #endif
57
58 using System;
59 using System.Net;
60 using System.Threading;
61 using System.Collections;
62 using System.Collections.Generic;
63 using System.Collections.Specialized;
64 using System.Configuration;
65 using System.Net.Configuration;
66 using System.Text.RegularExpressions;
67 using System.Security.Cryptography.X509Certificates;
68
69 using System.Globalization;
70 using System.Net.Security;
71 using System.Diagnostics;
72
73 namespace Mono.Net.Security
74 {
75         internal delegate bool ServerCertValidationCallbackWrapper (ServerCertValidationCallback callback, X509Certificate certificate, X509Chain chain, MonoSslPolicyErrors sslPolicyErrors);
76
77         internal class ChainValidationHelper : ICertificateValidator2
78         {
79                 readonly object sender;
80                 readonly MonoTlsSettings settings;
81                 readonly MonoTlsProvider provider;
82                 readonly ServerCertValidationCallback certValidationCallback;
83                 readonly LocalCertSelectionCallback certSelectionCallback;
84                 readonly ServerCertValidationCallbackWrapper callbackWrapper;
85                 readonly MonoTlsStream tlsStream;
86                 readonly HttpWebRequest request;
87
88                 internal static ICertificateValidator GetDefaultValidator (MonoTlsProvider provider, MonoTlsSettings settings)
89                 {
90                         if (settings == null)
91                                 return new ChainValidationHelper (provider, null, false, null, null);
92                         if (settings.CertificateValidator != null)
93                                 return settings.CertificateValidator;
94                         return new ChainValidationHelper (provider, settings, false, null, null);
95                 }
96
97 #region SslStream support
98
99                 /*
100                  * This is a hack which is used in SslStream - see ReferenceSources/SslStream.cs for details.
101                  */
102                 internal static ChainValidationHelper CloneWithCallbackWrapper (MonoTlsProvider provider, ref MonoTlsSettings settings, ServerCertValidationCallbackWrapper wrapper)
103                 {
104                         var helper = (ChainValidationHelper)settings.CertificateValidator;
105                         if (helper == null)
106                                 helper = new ChainValidationHelper (provider, settings, true, null, wrapper);
107                         else
108                                 helper = new ChainValidationHelper (helper, provider, settings, wrapper);
109                         settings = helper.settings;
110                         return helper;
111                 }
112
113                 internal static bool InvokeCallback (ServerCertValidationCallback callback, object sender, X509Certificate certificate, X509Chain chain, MonoSslPolicyErrors sslPolicyErrors)
114                 {
115                         return callback.Invoke (sender, certificate, chain, (SslPolicyErrors)sslPolicyErrors);
116                 }
117
118 #endregion
119
120                 ChainValidationHelper (ChainValidationHelper other, MonoTlsProvider provider, MonoTlsSettings settings, ServerCertValidationCallbackWrapper callbackWrapper = null)
121                 {
122                         sender = other.sender;
123                         certValidationCallback = other.certValidationCallback;
124                         certSelectionCallback = other.certSelectionCallback;
125                         tlsStream = other.tlsStream;
126                         request = other.request;
127
128                         if (settings == null)
129                                 settings = MonoTlsSettings.DefaultSettings;
130
131                         this.provider = provider;
132                         this.settings = settings.CloneWithValidator (this);
133                         this.callbackWrapper = callbackWrapper;
134                 }
135
136                 internal static ChainValidationHelper Create (MonoTlsProvider provider, ref MonoTlsSettings settings, MonoTlsStream stream)
137                 {
138                         var helper = new ChainValidationHelper (provider, settings, true, stream, null);
139                         settings = helper.settings;
140                         return helper;
141                 }
142
143                 ChainValidationHelper (MonoTlsProvider provider, MonoTlsSettings settings, bool cloneSettings, MonoTlsStream stream, ServerCertValidationCallbackWrapper callbackWrapper)
144                 {
145                         if (settings == null)
146                                 settings = MonoTlsSettings.CopyDefaultSettings ();
147                         if (cloneSettings)
148                                 settings = settings.CloneWithValidator (this);
149
150                         this.provider = provider;
151                         this.settings = settings;
152                         this.tlsStream = stream;
153                         this.callbackWrapper = callbackWrapper;
154
155                         var fallbackToSPM = false;
156
157                         if (settings != null) {
158                                 if (settings.RemoteCertificateValidationCallback != null) {
159                                         var callback = Private.CallbackHelpers.MonoToPublic (settings.RemoteCertificateValidationCallback);
160                                         certValidationCallback = new ServerCertValidationCallback (callback);
161                                 }
162                                 certSelectionCallback = Private.CallbackHelpers.MonoToInternal (settings.ClientCertificateSelectionCallback);
163                                 fallbackToSPM = settings.UseServicePointManagerCallback ?? stream != null;
164                         }
165
166                         if (stream != null) {
167                                 this.request = stream.Request;
168                                 this.sender = request;
169
170                                 if (certValidationCallback == null)
171                                         certValidationCallback = request.ServerCertValidationCallback;
172                                 if (certSelectionCallback == null)
173                                         certSelectionCallback = new LocalCertSelectionCallback (DefaultSelectionCallback);
174
175                                 if (settings == null)
176                                         fallbackToSPM = true;
177                         }
178
179                         if (fallbackToSPM && certValidationCallback == null)
180                                 certValidationCallback = ServicePointManager.ServerCertValidationCallback;
181                 }
182
183                 static X509Certificate DefaultSelectionCallback (string targetHost, XX509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
184                 {
185                         X509Certificate clientCertificate;
186                         if (localCertificates == null || localCertificates.Count == 0)
187                                 clientCertificate = null;
188                         else
189                                 clientCertificate = localCertificates [0];
190                         return clientCertificate;
191                 }
192
193                 public MonoTlsProvider Provider {
194                         get { return provider; }
195                 }
196
197                 public MonoTlsSettings Settings {
198                         get { return settings; }
199                 }
200
201                 public bool HasCertificateSelectionCallback {
202                         get { return certSelectionCallback != null; }
203                 }
204
205                 public bool SelectClientCertificate (
206                         string targetHost, XX509CertificateCollection localCertificates, X509Certificate remoteCertificate,
207                         string[] acceptableIssuers, out X509Certificate clientCertificate)
208                 {
209                         if (certSelectionCallback == null) {
210                                 clientCertificate = null;
211                                 return false;
212                         }
213                         clientCertificate = certSelectionCallback (targetHost, localCertificates, remoteCertificate, acceptableIssuers);
214                         return true;
215                 }
216
217                 internal X509Certificate SelectClientCertificate (
218                         string targetHost, XX509CertificateCollection localCertificates, X509Certificate remoteCertificate,
219                         string[] acceptableIssuers)
220                 {
221                         if (certSelectionCallback == null)
222                                 return null;
223                         return certSelectionCallback (targetHost, localCertificates, remoteCertificate, acceptableIssuers);
224                 }
225
226                 internal bool ValidateClientCertificate (X509Certificate certificate, MonoSslPolicyErrors errors)
227                 {
228                         var certs = new XX509CertificateCollection ();
229                         certs.Add (new X509Certificate2 (certificate.GetRawCertData ()));
230
231                         var result = ValidateChain (string.Empty, true, certificate, null, certs, (SslPolicyErrors)errors);
232                         if (result == null)
233                                 return false;
234
235                         return result.Trusted && !result.UserDenied;
236                 }
237
238                 public ValidationResult ValidateCertificate (string host, bool serverMode, XX509CertificateCollection certs)
239                 {
240                         try {
241                                 X509Certificate leaf;
242                                 if (certs != null && certs.Count != 0)
243                                         leaf = certs [0];
244                                 else
245                                         leaf = null;
246                                 var result = ValidateChain (host, serverMode, leaf, null, certs, 0);
247                                 if (tlsStream != null)
248                                         tlsStream.CertificateValidationFailed = result == null || !result.Trusted || result.UserDenied;
249                                 return result;
250                         } catch {
251                                 if (tlsStream != null)
252                                         tlsStream.CertificateValidationFailed = true;
253                                 throw;
254                         }
255                 }
256
257                 public ValidationResult ValidateCertificate (string host, bool serverMode, X509Certificate leaf, XX509Chain xchain)
258                 {
259                         try {
260                                 var chain = (X509Chain)(object)xchain;
261                                 var result = ValidateChain (host, serverMode, leaf, chain, null, 0);
262                                 if (tlsStream != null)
263                                         tlsStream.CertificateValidationFailed = result == null || !result.Trusted || result.UserDenied;
264                                 return result;
265                         } catch {
266                                 if (tlsStream != null)
267                                         tlsStream.CertificateValidationFailed = true;
268                                 throw;
269                         }
270                 }
271
272                 ValidationResult ValidateChain (string host, bool server, X509Certificate leaf,
273                                                 X509Chain chain, XX509CertificateCollection certs,
274                                                 SslPolicyErrors errors)
275                 {
276                         var oldChain = chain;
277                         var ownsChain = chain == null;
278                         try {
279                                 var result = ValidateChain (host, server, leaf, ref chain, certs, errors);
280                                 if (chain != oldChain)
281                                         ownsChain = true;
282
283                                 return result;
284                         } finally {
285                                 // If ValidateChain() changed the chain, then we need to free it.
286                                 if (ownsChain && chain != null)
287                                         chain.Dispose ();
288                         }
289                 }
290
291                 ValidationResult ValidateChain (string host, bool server, X509Certificate leaf,
292                                                 ref X509Chain chain, XX509CertificateCollection certs,
293                                                 SslPolicyErrors errors)
294                 {
295                         // user_denied is true if the user callback is called and returns false
296                         bool user_denied = false;
297                         bool result = false;
298
299                         var hasCallback = certValidationCallback != null || callbackWrapper != null;
300
301                         if (tlsStream != null)
302                                 request.ServicePoint.UpdateServerCertificate (leaf);
303
304                         if (leaf == null) {
305                                 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
306                                 if (hasCallback) {
307                                         if (callbackWrapper != null)
308                                                 result = callbackWrapper.Invoke (certValidationCallback, leaf, null, (MonoSslPolicyErrors)errors);
309                                         else
310                                                 result = certValidationCallback.Invoke (sender, leaf, null, errors);
311                                         user_denied = !result;
312                                 }
313                                 return new ValidationResult (result, user_denied, 0, (MonoSslPolicyErrors)errors);
314                         }
315
316                         ICertificatePolicy policy = ServicePointManager.GetLegacyCertificatePolicy ();
317
318                         int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
319
320                         bool wantsChain = SystemCertificateValidator.NeedsChain (settings);
321                         if (!wantsChain && hasCallback) {
322                                 if (settings == null || settings.CallbackNeedsCertificateChain)
323                                         wantsChain = true;
324                         }
325
326                         bool providerValidated = false;
327                         if (provider != null && provider.HasCustomSystemCertificateValidator) {
328                                 var xerrors = (MonoSslPolicyErrors)errors;
329                                 var xchain = (XX509Chain)(object)chain;
330                                 providerValidated = provider.InvokeSystemCertificateValidator (this, host, server, certs, wantsChain, ref xchain, out result, ref xerrors, ref status11);
331                                 chain = (X509Chain)(object)xchain;
332                                 errors = (SslPolicyErrors)xerrors;
333                         } else if (wantsChain) {
334                                 chain = SystemCertificateValidator.CreateX509Chain (certs);
335                         }
336
337                         if (!providerValidated)
338                                 result = SystemCertificateValidator.Evaluate (settings, host, certs, chain, ref errors, ref status11);
339
340                         if (policy != null && (!(policy is DefaultCertificatePolicy) || certValidationCallback == null)) {
341                                 ServicePoint sp = null;
342                                 if (request != null)
343                                         sp = request.ServicePointNoLock;
344                                 if (status11 == 0 && errors != 0) {
345                                         // TRUST_E_FAIL
346                                         status11 = unchecked ((int)0x800B010B);
347                                 }
348
349                                 // pre 2.0 callback
350                                 result = policy.CheckValidationResult (sp, leaf, request, status11);
351                                 user_denied = !result && !(policy is DefaultCertificatePolicy);
352                         }
353                         // If there's a 2.0 callback, it takes precedence
354                         if (hasCallback) {
355                                 if (callbackWrapper != null)
356                                         result = callbackWrapper.Invoke (certValidationCallback, leaf, chain, (MonoSslPolicyErrors)errors);
357                                 else
358                                         result = certValidationCallback.Invoke (sender, leaf, chain, errors);
359                                 user_denied = !result;
360                         }
361                         return new ValidationResult (result, user_denied, status11, (MonoSslPolicyErrors)errors);
362                 }
363
364                 public bool InvokeSystemValidator (string targetHost, bool serverMode, XX509CertificateCollection certificates, XX509Chain xchain, ref MonoSslPolicyErrors xerrors, ref int status11)
365                 {
366                         X509Chain chain = (X509Chain)(object)xchain;
367                         var errors = (SslPolicyErrors)xerrors;
368                         var result = SystemCertificateValidator.Evaluate (settings, targetHost, certificates, chain, ref errors, ref status11);
369                         xerrors = (MonoSslPolicyErrors)errors;
370                         return result;
371                 }
372         }
373 }
374 #endif
375