[sre-save] Handle ConstructorBuilder custom attribute constructors.
[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                 static readonly Guid id = new Guid ("432d18c9-9348-4b90-bfbf-9f2a10e1f15b");
54
55                 public override Guid ID {
56                         get { return id; }
57                 }
58                 public override string Name {
59                         get { return "btls"; }
60                 }
61
62                 internal MonoBtlsProvider ()
63                 {
64                         if (!MNS.MonoTlsProviderFactory.IsBtlsSupported ())
65                                 throw new NotSupportedException ("BTLS is not supported in this runtime.");
66                 }
67
68                 public override bool SupportsSslStream {
69                         get { return true; }
70                 }
71
72                 public override bool SupportsMonoExtensions {
73                         get { return true; }
74                 }
75
76                 public override bool SupportsConnectionInfo {
77                         get { return true; }
78                 }
79
80                 public override SslProtocols SupportedProtocols {
81                         get { return SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; }
82                 }
83
84                 public override IMonoSslStream CreateSslStream (
85                         Stream innerStream, bool leaveInnerStreamOpen,
86                         MonoTlsSettings settings = null)
87                 {
88                         return SslStream.CreateMonoSslStream (innerStream, leaveInnerStreamOpen, this, settings);
89                 }
90
91                 internal override IMonoSslStream CreateSslStreamInternal (
92                         SslStream sslStream, Stream innerStream, bool leaveInnerStreamOpen,
93                         MonoTlsSettings settings)
94                 {
95                         return new MonoBtlsStream (
96                                 innerStream, leaveInnerStreamOpen, sslStream, settings, this);
97                 }
98
99                 internal override bool HasNativeCertificates {
100                         get { return true; }
101                 }
102
103                 internal override X509Certificate2Impl GetNativeCertificate (
104                         byte[] data, string password, X509KeyStorageFlags flags)
105                 {
106                         var impl = new X509CertificateImplBtls (false);
107                         impl.Import (data, password, flags);
108                         return impl;
109                 }
110
111                 internal override X509Certificate2Impl GetNativeCertificate (
112                         X509Certificate certificate)
113                 {
114                         var impl = certificate.Impl as X509CertificateImplBtls;
115                         if (impl != null)
116                                 return (X509Certificate2Impl)impl.Clone ();
117
118                         var data = certificate.GetRawCertData ();
119                         return new X509CertificateImplBtls (data, MonoBtlsX509Format.DER, false);
120                 }
121
122                 internal static MonoBtlsX509VerifyParam GetVerifyParam (MonoTlsSettings settings, string targetHost, bool serverMode)
123                 {
124                         MonoBtlsX509VerifyParam param;
125                         if (serverMode)
126                                 param = MonoBtlsX509VerifyParam.GetSslClient ();
127                         else
128                                 param = MonoBtlsX509VerifyParam.GetSslServer ();
129
130                         if (targetHost == null && settings?.CertificateValidationTime == null)
131                                 return param;
132
133                         try {
134                                 var copy = param.Copy ();
135                                 if (targetHost != null)
136                                         copy.SetHost (targetHost);
137                                 if (settings?.CertificateValidationTime != null)
138                                         copy.SetTime (settings.CertificateValidationTime.Value);
139                                 return copy;
140                         } finally {
141                                 param.Dispose ();
142                         }
143                 }
144
145                 internal override bool ValidateCertificate (
146                         ICertificateValidator2 validator, string targetHost, bool serverMode,
147                         X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
148                         ref MonoSslPolicyErrors errors, ref int status11)
149                 {
150                         if (chain != null) {
151                                 var chainImpl = (X509ChainImplBtls)chain.Impl;
152                                 var success = chainImpl.StoreCtx.VerifyResult == 1;
153                                 CheckValidationResult (
154                                         validator, targetHost, serverMode, certificates,
155                                         wantsChain, chain, chainImpl.StoreCtx,
156                                         success, ref errors, ref status11);
157                                 return success;
158                         }
159
160                         using (var store = new MonoBtlsX509Store ())
161                         using (var nativeChain = MonoBtlsProvider.GetNativeChain (certificates))
162                         using (var param = GetVerifyParam (validator.Settings, targetHost, serverMode))
163                         using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
164                                 SetupCertificateStore (store, validator.Settings, serverMode);
165
166                                 storeCtx.Initialize (store, nativeChain);
167
168                                 storeCtx.SetVerifyParam (param);
169
170                                 var ret = storeCtx.Verify ();
171
172                                 var success = ret == 1;
173
174                                 if (wantsChain && chain == null) {
175                                         chain = GetManagedChain (nativeChain);
176                                 }
177
178                                 CheckValidationResult (
179                                         validator, targetHost, serverMode, certificates,
180                                         wantsChain, null, storeCtx,
181                                         success, ref errors, ref status11);
182                                 return success;
183                         }
184                 }
185
186                 internal static bool ValidateCertificate (MonoBtlsX509Chain chain, MonoBtlsX509VerifyParam param)
187                 {
188                         using (var store = new MonoBtlsX509Store ())
189                         using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
190                                 /*
191                                  * We're called from X509Certificate2.Verify() via X509CertificateImplBtls.Verify().
192                                  *
193                                  * Use the default settings and assume client-mode.
194                                  */
195                                 SetupCertificateStore (store, MonoTlsSettings.DefaultSettings, false);
196
197                                 storeCtx.Initialize (store, chain);
198
199                                 if (param != null)
200                                         storeCtx.SetVerifyParam (param);
201
202                                 var ret = storeCtx.Verify ();
203
204                                 return ret == 1;
205                         }
206                 }
207
208                 void CheckValidationResult (
209                         ICertificateValidator validator, string targetHost, bool serverMode,
210                         X509CertificateCollection certificates, bool wantsChain,
211                         X509Chain chain, MonoBtlsX509StoreCtx storeCtx,
212                         bool success, ref MonoSslPolicyErrors errors, ref int status11)
213                 {
214                         if (!success) {
215                                 errors = MonoSslPolicyErrors.RemoteCertificateChainErrors;
216                                 status11 = unchecked((int)0x800B010B);
217                         }
218                 }
219
220                 internal static void SetupCertificateStore (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
221                 {
222                         /*
223                          * In server-mode, we only add certificates which are explicitly trusted via
224                          * MonoTlsSettings.TrustAnchors.
225                          * 
226                          * MonoTlsSettings.CertificateSearchPaths is ignored on Android.
227                          * 
228                          */
229
230 #if MONODROID
231                         AddTrustedRoots (store, settings, server);
232                         if (!server)
233                                 SetupDefaultCertificateStore (store);
234                         return;
235 #else
236                         if (server || settings?.CertificateSearchPaths == null) {
237                                 AddTrustedRoots (store, settings, server);
238                                 if (!server)
239                                         SetupDefaultCertificateStore (store);
240                                 return;
241                         }
242
243                         foreach (var path in settings.CertificateSearchPaths) {
244                                 switch (path) {
245                                 case "@default":
246                                         AddTrustedRoots (store, settings, server);
247                                         AddUserStore (store);
248                                         AddMachineStore (store);
249                                         break;
250                                 case "@trusted":
251                                         AddTrustedRoots (store, settings, server);
252                                         break;
253                                 case "@user":
254                                         AddUserStore (store);
255                                         break;
256                                 case "@machine":
257                                         AddMachineStore (store);
258                                         break;
259                                 default:
260                                         if (path.StartsWith ("@pem:")) {
261                                                 var realPath = path.Substring (5);
262                                                 if (Directory.Exists (realPath))
263                                                         store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.PEM);
264                                                 break;
265                                         } else if (path.StartsWith ("@der:")) {
266                                                 var realPath = path.Substring (5);
267                                                 if (Directory.Exists (realPath))
268                                                         store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.ASN1);
269                                                 break;
270                                         }
271                                         throw new NotSupportedException (string.Format ("Invalid item `{0}' in MonoTlsSettings.CertificateSearchPaths.", path));
272                                 }
273                         }
274 #endif
275                 }
276
277                 static void SetupDefaultCertificateStore (MonoBtlsX509Store store)
278                 {
279 #if MONODROID
280                         store.SetDefaultPaths ();
281                         store.AddAndroidLookup ();
282 #else
283                         AddUserStore (store);
284                         AddMachineStore (store);
285 #endif
286                 }
287
288 #if !MONODROID
289                 static void AddUserStore (MonoBtlsX509Store store)
290                 {
291                         var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
292                         if (Directory.Exists (userPath))
293                                 store.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
294                 }
295
296                 static void AddMachineStore (MonoBtlsX509Store store)
297                 {
298                         var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
299                         if (Directory.Exists (machinePath))
300                                 store.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
301                 }
302 #endif
303
304                 static void AddTrustedRoots (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
305                 {
306                         if (settings?.TrustAnchors == null)
307                                 return;
308                         var trust = server ? MonoBtlsX509TrustKind.TRUST_CLIENT : MonoBtlsX509TrustKind.TRUST_SERVER;
309                         store.AddCollection (settings.TrustAnchors, trust);
310                 }
311
312                 public static string GetSystemStoreLocation ()
313                 {
314 #if MONODROID
315                         return "/system/etc/security/cacerts";
316 #else
317                         return MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
318 #endif
319                 }
320
321                 public static X509Certificate CreateCertificate (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
322                 {
323                         using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
324                                 return new X509Certificate (impl);
325                         }
326                 }
327
328                 public static X509Certificate2 CreateCertificate2 (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
329                 {
330                         using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
331                                 return new X509Certificate2 (impl);
332                         }
333                 }
334
335                 public static X509Certificate2 CreateCertificate2 (byte[] data, string password, bool disallowFallback = false)
336                 {
337                         using (var impl = new X509CertificateImplBtls (disallowFallback)) {
338                                 impl.Import (data, password, X509KeyStorageFlags.DefaultKeySet);
339                                 return new X509Certificate2 (impl);
340                         }
341                 }
342
343                 public static X509Certificate CreateCertificate (MonoBtlsX509 x509)
344                 {
345                         using (var impl = new X509CertificateImplBtls (x509, true))
346                                 return new X509Certificate (impl);
347                 }
348
349                 public static X509Chain CreateChain ()
350                 {
351                         using (var impl = new X509ChainImplBtls ())
352                                 return new X509Chain (impl);
353                 }
354
355                 public static X509Chain GetManagedChain (MonoBtlsX509Chain chain)
356                 {
357                         var impl = new X509ChainImplBtls (chain);
358                         return new X509Chain (impl);
359                 }
360
361                 public static MonoBtlsX509 GetBtlsCertificate (X509Certificate certificate)
362                 {
363                         var impl = certificate.Impl as X509CertificateImplBtls;
364                         if (impl != null)
365                                 return impl.X509.Copy ();
366
367                         return MonoBtlsX509.LoadFromData (certificate.GetRawCertData (), MonoBtlsX509Format.DER);
368                 }
369
370                 public static MonoBtlsX509Chain GetNativeChain (X509CertificateCollection certificates)
371                 {
372                         var chain = new MonoBtlsX509Chain ();
373                         try {
374                                 foreach (var cert in certificates) {
375                                         using (var x509 = GetBtlsCertificate (cert))
376                                                 chain.AddCertificate (x509);
377                                 }
378                                 return chain;
379                         } catch {
380                                 chain.Dispose ();
381                                 throw;
382                         }
383                 }
384         }
385 }
386 #endif