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