Merge pull request #5330 from alexanderkyte/dedup_mkbundle
[mono.git] / mcs / class / System / Mono.Btls / MonoBtlsProvider.cs
1 //
2 // MonoBtlsProvider.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 #if SECURITY_DEP && MONO_FEATURE_BTLS
27 #if MONO_SECURITY_ALIAS
28 extern alias MonoSecurity;
29 #endif
30
31 using System;
32 using System.IO;
33 using System.Threading;
34 using System.Threading.Tasks;
35 using System.Net.Security;
36 using System.Security.Cryptography.X509Certificates;
37 using System.Security.Authentication;
38
39 #if MONO_SECURITY_ALIAS
40 using MonoSecurity::Mono.Security.Interface;
41 using MX = MonoSecurity::Mono.Security.X509;
42 #else
43 using Mono.Security.Interface;
44 using MX = Mono.Security.X509;
45 #endif
46
47 using MNS = Mono.Net.Security;
48
49 namespace Mono.Btls
50 {
51         class MonoBtlsProvider : MonoTlsProvider
52         {
53                 public override Guid ID {
54                         get { return MNS.MonoTlsProviderFactory.BtlsId; }
55                 }
56                 public override string Name {
57                         get { return "btls"; }
58                 }
59
60                 internal MonoBtlsProvider ()
61                 {
62                         if (!MNS.MonoTlsProviderFactory.IsBtlsSupported ())
63                                 throw new NotSupportedException ("BTLS is not supported in this runtime.");
64                 }
65
66                 public override bool SupportsSslStream {
67                         get { return true; }
68                 }
69
70                 public override bool SupportsMonoExtensions {
71                         get { return true; }
72                 }
73
74                 public override bool SupportsConnectionInfo {
75                         get { return true; }
76                 }
77
78                 internal override bool SupportsCleanShutdown {
79                         get { return true; }
80                 }
81
82                 public override SslProtocols SupportedProtocols {
83                         get { return SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; }
84                 }
85
86                 public override IMonoSslStream CreateSslStream (
87                         Stream innerStream, bool leaveInnerStreamOpen,
88                         MonoTlsSettings settings = null)
89                 {
90                         return SslStream.CreateMonoSslStream (innerStream, leaveInnerStreamOpen, this, settings);
91                 }
92
93                 internal override IMonoSslStream CreateSslStreamInternal (
94                         SslStream sslStream, Stream innerStream, bool leaveInnerStreamOpen,
95                         MonoTlsSettings settings)
96                 {
97                         return new MonoBtlsStream (
98                                 innerStream, leaveInnerStreamOpen, sslStream, settings, this);
99                 }
100
101                 internal override bool HasNativeCertificates {
102                         get { return true; }
103                 }
104
105                 internal override X509Certificate2Impl GetNativeCertificate (
106                         byte[] data, string password, X509KeyStorageFlags flags)
107                 {
108                         var impl = new X509CertificateImplBtls (false);
109                         impl.Import (data, password, flags);
110                         return impl;
111                 }
112
113                 internal override X509Certificate2Impl GetNativeCertificate (
114                         X509Certificate certificate)
115                 {
116                         var impl = certificate.Impl as X509CertificateImplBtls;
117                         if (impl != null)
118                                 return (X509Certificate2Impl)impl.Clone ();
119
120                         var data = certificate.GetRawCertData ();
121                         return new X509CertificateImplBtls (data, MonoBtlsX509Format.DER, false);
122                 }
123
124                 internal static MonoBtlsX509VerifyParam GetVerifyParam (MonoTlsSettings settings, string targetHost, bool serverMode)
125                 {
126                         MonoBtlsX509VerifyParam param;
127                         if (serverMode)
128                                 param = MonoBtlsX509VerifyParam.GetSslClient ();
129                         else
130                                 param = MonoBtlsX509VerifyParam.GetSslServer ();
131
132                         if (targetHost == null && settings?.CertificateValidationTime == null)
133                                 return param;
134
135                         try {
136                                 var copy = param.Copy ();
137                                 if (targetHost != null)
138                                         copy.SetHost (targetHost);
139                                 if (settings?.CertificateValidationTime != null)
140                                         copy.SetTime (settings.CertificateValidationTime.Value);
141                                 return copy;
142                         } finally {
143                                 param.Dispose ();
144                         }
145                 }
146
147                 internal override bool ValidateCertificate (
148                         ICertificateValidator2 validator, string targetHost, bool serverMode,
149                         X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
150                         ref MonoSslPolicyErrors errors, ref int status11)
151                 {
152                         if (chain != null) {
153                                 var chainImpl = (X509ChainImplBtls)chain.Impl;
154                                 var success = chainImpl.StoreCtx.VerifyResult == 1;
155                                 CheckValidationResult (
156                                         validator, targetHost, serverMode, certificates,
157                                         wantsChain, chain, chainImpl.StoreCtx,
158                                         success, ref errors, ref status11);
159                                 return success;
160                         }
161
162                         using (var store = new MonoBtlsX509Store ())
163                         using (var nativeChain = MonoBtlsProvider.GetNativeChain (certificates))
164                         using (var param = GetVerifyParam (validator.Settings, targetHost, serverMode))
165                         using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
166                                 SetupCertificateStore (store, validator.Settings, serverMode);
167
168                                 storeCtx.Initialize (store, nativeChain);
169
170                                 storeCtx.SetVerifyParam (param);
171
172                                 var ret = storeCtx.Verify ();
173
174                                 var success = ret == 1;
175
176                                 if (wantsChain && chain == null) {
177                                         chain = GetManagedChain (nativeChain);
178                                 }
179
180                                 CheckValidationResult (
181                                         validator, targetHost, serverMode, certificates,
182                                         wantsChain, null, storeCtx,
183                                         success, ref errors, ref status11);
184                                 return success;
185                         }
186                 }
187
188                 internal static bool ValidateCertificate (MonoBtlsX509Chain chain, MonoBtlsX509VerifyParam param)
189                 {
190                         using (var store = new MonoBtlsX509Store ())
191                         using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
192                                 /*
193                                  * We're called from X509Certificate2.Verify() via X509CertificateImplBtls.Verify().
194                                  *
195                                  * Use the default settings and assume client-mode.
196                                  */
197                                 SetupCertificateStore (store, MonoTlsSettings.DefaultSettings, false);
198
199                                 storeCtx.Initialize (store, chain);
200
201                                 if (param != null)
202                                         storeCtx.SetVerifyParam (param);
203
204                                 var ret = storeCtx.Verify ();
205
206                                 return ret == 1;
207                         }
208                 }
209
210                 void CheckValidationResult (
211                         ICertificateValidator validator, string targetHost, bool serverMode,
212                         X509CertificateCollection certificates, bool wantsChain,
213                         X509Chain chain, MonoBtlsX509StoreCtx storeCtx,
214                         bool success, ref MonoSslPolicyErrors errors, ref int status11)
215                 {
216                         if (!success) {
217                                 errors = MonoSslPolicyErrors.RemoteCertificateChainErrors;
218                                 status11 = unchecked((int)0x800B010B);
219                         }
220                 }
221
222                 internal static void SetupCertificateStore (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
223                 {
224                         /*
225                          * In server-mode, we only add certificates which are explicitly trusted via
226                          * MonoTlsSettings.TrustAnchors.
227                          * 
228                          * MonoTlsSettings.CertificateSearchPaths is ignored on Android.
229                          * 
230                          */
231
232 #if MONODROID
233                         AddTrustedRoots (store, settings, server);
234                         if (!server)
235                                 SetupDefaultCertificateStore (store);
236                         return;
237 #else
238                         if (server || settings?.CertificateSearchPaths == null) {
239                                 AddTrustedRoots (store, settings, server);
240                                 if (!server)
241                                         SetupDefaultCertificateStore (store);
242                                 return;
243                         }
244
245                         foreach (var path in settings.CertificateSearchPaths) {
246                                 switch (path) {
247                                 case "@default":
248                                         AddTrustedRoots (store, settings, server);
249                                         AddUserStore (store);
250                                         AddMachineStore (store);
251                                         break;
252                                 case "@trusted":
253                                         AddTrustedRoots (store, settings, server);
254                                         break;
255                                 case "@user":
256                                         AddUserStore (store);
257                                         break;
258                                 case "@machine":
259                                         AddMachineStore (store);
260                                         break;
261                                 default:
262                                         if (path.StartsWith ("@pem:")) {
263                                                 var realPath = path.Substring (5);
264                                                 if (Directory.Exists (realPath))
265                                                         store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.PEM);
266                                                 break;
267                                         } else if (path.StartsWith ("@der:")) {
268                                                 var realPath = path.Substring (5);
269                                                 if (Directory.Exists (realPath))
270                                                         store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.ASN1);
271                                                 break;
272                                         }
273                                         throw new NotSupportedException (string.Format ("Invalid item `{0}' in MonoTlsSettings.CertificateSearchPaths.", path));
274                                 }
275                         }
276 #endif
277                 }
278
279                 static void SetupDefaultCertificateStore (MonoBtlsX509Store store)
280                 {
281 #if MONODROID
282                         store.SetDefaultPaths ();
283                         store.AddAndroidLookup ();
284 #else
285                         AddUserStore (store);
286                         AddMachineStore (store);
287 #endif
288                 }
289
290 #if !MONODROID
291                 static void AddUserStore (MonoBtlsX509Store store)
292                 {
293                         var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
294                         if (Directory.Exists (userPath))
295                                 store.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
296                 }
297
298                 static void AddMachineStore (MonoBtlsX509Store store)
299                 {
300                         var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
301                         if (Directory.Exists (machinePath))
302                                 store.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
303                 }
304 #endif
305
306                 static void AddTrustedRoots (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
307                 {
308                         if (settings?.TrustAnchors == null)
309                                 return;
310                         var trust = server ? MonoBtlsX509TrustKind.TRUST_CLIENT : MonoBtlsX509TrustKind.TRUST_SERVER;
311                         store.AddCollection (settings.TrustAnchors, trust);
312                 }
313
314                 public static string GetSystemStoreLocation ()
315                 {
316 #if MONODROID
317                         return "/system/etc/security/cacerts";
318 #else
319                         return MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
320 #endif
321                 }
322
323                 public static X509Certificate CreateCertificate (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
324                 {
325                         using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
326                                 return new X509Certificate (impl);
327                         }
328                 }
329
330                 public static X509Certificate2 CreateCertificate2 (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
331                 {
332                         using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
333                                 return new X509Certificate2 (impl);
334                         }
335                 }
336
337                 public static X509Certificate2 CreateCertificate2 (byte[] data, string password, bool disallowFallback = false)
338                 {
339                         using (var impl = new X509CertificateImplBtls (disallowFallback)) {
340                                 impl.Import (data, password, X509KeyStorageFlags.DefaultKeySet);
341                                 return new X509Certificate2 (impl);
342                         }
343                 }
344
345                 public static X509Certificate CreateCertificate (MonoBtlsX509 x509)
346                 {
347                         using (var impl = new X509CertificateImplBtls (x509, true))
348                                 return new X509Certificate (impl);
349                 }
350
351                 public static X509Chain CreateChain ()
352                 {
353                         using (var impl = new X509ChainImplBtls ())
354                                 return new X509Chain (impl);
355                 }
356
357                 public static X509Chain GetManagedChain (MonoBtlsX509Chain chain)
358                 {
359                         var impl = new X509ChainImplBtls (chain);
360                         return new X509Chain (impl);
361                 }
362
363                 public static MonoBtlsX509 GetBtlsCertificate (X509Certificate certificate)
364                 {
365                         var impl = certificate.Impl as X509CertificateImplBtls;
366                         if (impl != null)
367                                 return impl.X509.Copy ();
368
369                         return MonoBtlsX509.LoadFromData (certificate.GetRawCertData (), MonoBtlsX509Format.DER);
370                 }
371
372                 public static MonoBtlsX509Chain GetNativeChain (X509CertificateCollection certificates)
373                 {
374                         var chain = new MonoBtlsX509Chain ();
375                         try {
376                                 foreach (var cert in certificates) {
377                                         using (var x509 = GetBtlsCertificate (cert))
378                                                 chain.AddCertificate (x509);
379                                 }
380                                 return chain;
381                         } catch {
382                                 chain.Dispose ();
383                                 throw;
384                         }
385                 }
386         }
387 }
388 #endif