695f5e822209a0964b0c604cdca50c421e48c52c
[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 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
26
27 using System;
28 using System.Runtime.InteropServices;
29
30 namespace System.Security.Cryptography.X509Certificates {
31
32         static class OSX509Certificates {
33                 public const string SecurityLibrary = "/System/Library/Frameworks/Security.framework/Security";
34                 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
35         
36                 [DllImport (SecurityLibrary)]
37                 extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr nsdataRef);
38                 
39                 [DllImport (SecurityLibrary)]
40                 extern static /* OSStatus */ int SecTrustCreateWithCertificates (IntPtr certOrCertArray, IntPtr policies, out IntPtr sectrustref);
41                 
42                 [DllImport (SecurityLibrary)]
43                 extern static /* OSStatus */ int SecTrustSetAnchorCertificates (IntPtr /* SecTrustRef */ trust, IntPtr /* CFArrayRef */ anchorCertificates);
44
45                 [DllImport (SecurityLibrary)]
46                 extern static IntPtr SecPolicyCreateSSL ([MarshalAs (UnmanagedType.I1)] bool server, IntPtr cfStringHostname);
47                 
48                 [DllImport (SecurityLibrary)]
49                 extern static /* OSStatus */ int SecTrustEvaluate (IntPtr secTrustRef, out SecTrustResult secTrustResultTime);
50
51                 [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)]
52                 extern static IntPtr CFStringCreateWithCharacters (IntPtr allocator, string str, /* CFIndex */ IntPtr count);
53
54                 [DllImport (CoreFoundationLibrary)]
55                 unsafe extern static IntPtr CFDataCreate (IntPtr allocator, byte *bytes, /* CFIndex */ IntPtr length);
56
57                 [DllImport (CoreFoundationLibrary)]
58                 extern static void CFRetain (IntPtr handle);
59
60                 [DllImport (CoreFoundationLibrary)]
61                 extern static void CFRelease (IntPtr handle);
62
63                 [DllImport (CoreFoundationLibrary)]
64                 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
65
66                 // uint32_t
67                 public enum SecTrustResult {
68                         Invalid,
69                         Proceed,
70                         Confirm,
71                         Deny,
72                         Unspecified,
73                         RecoverableTrustFailure,
74                         FatalTrustFailure,
75                         ResultOtherError,
76                 }
77
78                 static IntPtr MakeCFData (byte [] data)
79                 {
80                         unsafe {
81                                 fixed (byte *ptr = &data [0])
82                                         return CFDataCreate (IntPtr.Zero, ptr, (IntPtr) data.Length);
83                         }
84                 }
85
86                 static unsafe IntPtr FromIntPtrs (IntPtr [] values)
87                 {
88                         fixed (IntPtr* pv = values) {
89                                 return CFArrayCreate (
90                                         IntPtr.Zero, 
91                                         (IntPtr) pv,
92                                         (IntPtr) values.Length,
93                                         IntPtr.Zero);
94                         }
95                 }
96
97                 static IntPtr GetCertificate (X509Certificate certificate)
98                 {
99                         var handle = certificate.Impl.GetNativeAppleCertificate ();
100                         if (handle != IntPtr.Zero) {
101                                 CFRetain (handle);
102                                 return handle;
103                         }
104                         var dataPtr = MakeCFData (certificate.GetRawCertData ());
105                         handle = SecCertificateCreateWithData (IntPtr.Zero, dataPtr);
106                         CFRelease (dataPtr);
107                         return handle;
108                 }
109                 
110                 public static SecTrustResult TrustEvaluateSsl (XX509CertificateCollection certificates, XX509CertificateCollection anchors, string host)
111                 {
112                         if (certificates == null)
113                                 return SecTrustResult.Deny;
114
115                         try {
116                                 return _TrustEvaluateSsl (certificates, anchors, host);
117                         } catch {
118                                 return SecTrustResult.Deny;
119                         }
120                 }
121
122                 static SecTrustResult _TrustEvaluateSsl (XX509CertificateCollection certificates, XX509CertificateCollection anchors, string hostName)
123                 {
124                         int certCount = certificates.Count;
125                         int anchorCount = anchors != null ? anchors.Count : 0;
126                         IntPtr [] secCerts = new IntPtr [certCount];
127                         IntPtr [] secCertAnchors = new IntPtr [anchorCount];
128                         IntPtr certArray = IntPtr.Zero;
129                         IntPtr anchorArray = IntPtr.Zero;
130                         IntPtr sslsecpolicy = IntPtr.Zero;
131                         IntPtr host = IntPtr.Zero;
132                         IntPtr sectrust = IntPtr.Zero;
133                         SecTrustResult result = SecTrustResult.Deny;
134
135                         try {
136                                 for (int i = 0; i < certCount; i++) {
137                                         secCerts [i] = GetCertificate (certificates [i]);
138                                         if (secCerts [i] == IntPtr.Zero)
139                                                 return SecTrustResult.Deny;
140                                 }
141
142                                 for (int i = 0; i < anchorCount; i++) {
143                                         secCertAnchors [i] = GetCertificate (anchors [i]);
144                                         if (secCertAnchors [i] == IntPtr.Zero)
145                                                 return SecTrustResult.Deny;
146                                 }
147
148                                 certArray = FromIntPtrs (secCerts);
149
150                                 host = CFStringCreateWithCharacters (IntPtr.Zero, hostName, (IntPtr) hostName.Length);
151                                 sslsecpolicy = SecPolicyCreateSSL (true, host);
152
153                                 int code = SecTrustCreateWithCertificates (certArray, sslsecpolicy, out sectrust);
154                                 if (code != 0)
155                                         return SecTrustResult.Deny;
156
157                                 if (anchorCount > 0) {
158                                         anchorArray = FromIntPtrs (secCertAnchors);
159                                         SecTrustSetAnchorCertificates (sectrust, anchorArray);
160                                 }
161
162                                 code = SecTrustEvaluate (sectrust, out result);
163                                 return result;
164                         } finally {
165                                 if (certArray != IntPtr.Zero)
166                                         CFRelease (certArray);
167
168                                 if (anchorArray != IntPtr.Zero)
169                                         CFRelease (anchorArray);
170                                 
171                                 for (int i = 0; i < certCount; i++)
172                                         if (secCerts [i] != IntPtr.Zero)
173                                                 CFRelease (secCerts [i]);
174
175                                 for (int i = 0; i < anchorCount; i++)
176                                         if (secCertAnchors [i] != IntPtr.Zero)
177                                                 CFRelease (secCertAnchors [i]);
178
179                                 if (sslsecpolicy != IntPtr.Zero)
180                                         CFRelease (sslsecpolicy);
181                                 if (host != IntPtr.Zero)
182                                         CFRelease (host);
183                                 if (sectrust != IntPtr.Zero)
184                                         CFRelease (sectrust);
185                         }
186                 }
187         }
188 }
189 #endif