Merge pull request #1068 from esdrubal/bug18421
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / OSX509Certificates.cs
1 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
2 // Copyright 2012-2014 Xamarin Inc.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 #if SECURITY_DEP
24
25 #if MONOTOUCH || MONODROID
26 using MSX = Mono.Security.X509;
27 #else
28 extern alias MonoSecurity;
29 using MSX = MonoSecurity::Mono.Security.X509;
30 #endif
31
32 using System;
33 using System.Runtime.InteropServices;
34
35 namespace System.Security.Cryptography.X509Certificates {
36
37         static class OSX509Certificates {
38                 public const string SecurityLibrary = "/System/Library/Frameworks/Security.framework/Security";
39                 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
40         
41                 [DllImport (SecurityLibrary)]
42                 extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr nsdataRef);
43                 
44                 [DllImport (SecurityLibrary)]
45                 extern static /* OSStatus */ int SecTrustCreateWithCertificates (IntPtr certOrCertArray, IntPtr policies, out IntPtr sectrustref);
46                 
47                 [DllImport (SecurityLibrary)]
48                 extern static IntPtr SecPolicyCreateSSL ([MarshalAs (UnmanagedType.I1)] bool server, IntPtr cfStringHostname);
49                 
50                 [DllImport (SecurityLibrary)]
51                 extern static /* OSStatus */ int SecTrustEvaluate (IntPtr secTrustRef, out SecTrustResult secTrustResultTime);
52
53                 [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)]
54                 extern static IntPtr CFStringCreateWithCharacters (IntPtr allocator, string str, /* CFIndex */ IntPtr count);
55
56                 [DllImport (CoreFoundationLibrary)]
57                 unsafe extern static IntPtr CFDataCreate (IntPtr allocator, byte *bytes, /* CFIndex */ IntPtr length);
58
59                 [DllImport (CoreFoundationLibrary)]
60                 extern static void CFRelease (IntPtr handle);
61
62                 [DllImport (CoreFoundationLibrary)]
63                 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
64
65                 // uint32_t
66                 public enum SecTrustResult {
67                         Invalid,
68                         Proceed,
69                         Confirm,
70                         Deny,
71                         Unspecified,
72                         RecoverableTrustFailure,
73                         FatalTrustFailure,
74                         ResultOtherError,
75                 }
76
77                 static IntPtr MakeCFData (byte [] data)
78                 {
79                         unsafe {
80                                 fixed (byte *ptr = &data [0])
81                                         return CFDataCreate (IntPtr.Zero, ptr, (IntPtr) data.Length);
82                         }
83                 }
84
85                 static unsafe IntPtr FromIntPtrs (IntPtr [] values)
86                 {
87                         fixed (IntPtr* pv = values) {
88                                 return CFArrayCreate (
89                                         IntPtr.Zero, 
90                                         (IntPtr) pv,
91                                         (IntPtr) values.Length,
92                                         IntPtr.Zero);
93                         }
94                 }
95                 
96                 public static SecTrustResult TrustEvaluateSsl (MSX.X509CertificateCollection certificates, string host)
97                 {
98                         if (certificates == null)
99                                 return SecTrustResult.Deny;
100
101                         try {
102                                 return _TrustEvaluateSsl (certificates, host);
103                         } catch {
104                                 return SecTrustResult.Deny;
105                         }
106                 }
107                 
108                 static SecTrustResult _TrustEvaluateSsl (MSX.X509CertificateCollection certificates, string hostName)
109                 {
110                         int certCount = certificates.Count;
111                         IntPtr [] cfDataPtrs = new IntPtr [certCount];
112                         IntPtr [] secCerts = new IntPtr [certCount];
113                         IntPtr certArray = IntPtr.Zero;
114                         IntPtr sslsecpolicy = IntPtr.Zero;
115                         IntPtr host = IntPtr.Zero;
116                         IntPtr sectrust = IntPtr.Zero;
117                         SecTrustResult result = SecTrustResult.Deny;
118
119                         try {
120                                 for (int i = 0; i < certCount; i++)
121                                         cfDataPtrs [i] = MakeCFData (certificates [i].RawData);
122                                 
123                                 for (int i = 0; i < certCount; i++){
124                                         secCerts [i] = SecCertificateCreateWithData (IntPtr.Zero, cfDataPtrs [i]);
125                                         if (secCerts [i] == IntPtr.Zero)
126                                                 return SecTrustResult.Deny;
127                                 }
128                                 certArray = FromIntPtrs (secCerts);
129                                 host = CFStringCreateWithCharacters (IntPtr.Zero, hostName, (IntPtr) hostName.Length);
130                                 sslsecpolicy = SecPolicyCreateSSL (true, host);
131
132                                 int code = SecTrustCreateWithCertificates (certArray, sslsecpolicy, out sectrust);
133                                 if (code == 0)
134                                         code = SecTrustEvaluate (sectrust, out result);
135                                 return result;
136                         } finally {
137                                 for (int i = 0; i < certCount; i++)
138                                         if (cfDataPtrs [i] != IntPtr.Zero)
139                                                 CFRelease (cfDataPtrs [i]);
140
141                                 if (certArray != IntPtr.Zero)
142                                         CFRelease (certArray);
143                                 
144                                 for (int i = 0; i < certCount; i++)
145                                         if (secCerts [i] != IntPtr.Zero)
146                                                 CFRelease (secCerts [i]);
147
148                                 if (sslsecpolicy != IntPtr.Zero)
149                                         CFRelease (sslsecpolicy);
150                                 if (host != IntPtr.Zero)
151                                         CFRelease (host);
152                                 if (sectrust != IntPtr.Zero)
153                                         CFRelease (sectrust);
154                         }
155                 }
156         }
157 }
158 #endif