]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecTrustOSXEntryPoints.cpp
1 /*
2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecTrustOSXEntryPoints - Interface for unified SecTrust into OS X Security
26 * Framework.
27 */
28
29 #include "SecTrustOSXEntryPoints.h"
30
31 #include <Security/Security.h>
32 #include <Security/cssmtype.h>
33 #include <Security/SecKeychain.h>
34 #include <Security/SecItemPriv.h>
35 #include <Security/SecTrustSettingsPriv.h>
36 #include <Security/SecCertificate.h>
37 #include <Security/SecImportExport.h>
38 #include <security_keychain/SecImportExportPem.h>
39 #include <security_utilities/debugging.h>
40 #include <Security/SecItemInternal.h>
41
42 #include <security_ocspd/ocspdClient.h>
43 #include <security_ocspd/ocspdUtils.h>
44
45 #include <CoreFoundation/CoreFoundation.h>
46 #include <CoreFoundation/CFRunLoop.h>
47 #include <dispatch/dispatch.h>
48 #include <AssertMacros.h>
49 #include <pthread.h>
50 #include <notify.h>
51
52 /*
53 * MARK: CFRunloop
54 */
55
56 static void *SecTrustOSXCFRunloop(__unused void *unused) {
57 CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, (CFTimeInterval) UINT_MAX, 0, 0, 0, ^(__unused CFRunLoopTimerRef _timer) {
58 /* do nothing */
59 });
60
61 /* add a timer to force the runloop to stay running */
62 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
63 /* Register for CertificateTrustNotification */
64
65 int out_token = 0;
66 notify_register_dispatch(kSecServerCertificateTrustNotification, &out_token,
67 dispatch_get_main_queue(),
68 ^(int token __unused) {
69 // Purge keychain parent cache
70 SecItemParentCachePurge();
71 // Purge unrestricted roots cache
72 SecTrustSettingsPurgeUserAdminCertsCache();
73
74 });
75
76 try {
77 CFRunLoopRun();
78 }
79 catch (...) {
80 /* An exception was rethrown from the runloop. Since we can't reliably
81 * obtain info about changes to keychains or trust settings anymore,
82 * just exit and respawn the process when needed. */
83
84 secerror("Exception occurred in CFRunLoopRun; exiting");
85 exit(0);
86 }
87 CFRelease(timer);
88 return NULL;
89 }
90
91 void SecTrustLegacySourcesEventRunloopCreate(void) {
92 /* A runloop is currently necessary to receive notifications about changes in the
93 * legacy keychains and trust settings. */
94 static dispatch_once_t once;
95
96 dispatch_once(&once, ^{
97 pthread_attr_t attrs;
98 pthread_t thread;
99
100 pthread_attr_init(&attrs);
101 pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
102
103 /* we do this with traditional pthread to avoid impacting our 512 WQ thread limit since this is a parked thread */
104 pthread_create(&thread, &attrs, SecTrustOSXCFRunloop, NULL);
105 });
106 }
107
108 /*
109 * MARK: ocspd CRL Interface
110 */
111 /* lengths of time strings without trailing NULL */
112 #define CSSM_TIME_STRLEN 14 /* no trailing 'Z' */
113 #define GENERALIZED_TIME_STRLEN 15
114
115 OSStatus SecTrustLegacyCRLStatus(SecCertificateRef cert, CFArrayRef chain, CFURLRef currCRLDP);
116 OSStatus SecTrustLegacyCRLFetch(CFURLRef currCRLDP, CFAbsoluteTime verifyTime);
117
118 static OSStatus cssmReturnToOSStatus(CSSM_RETURN crtn) {
119 OSStatus status = errSecInternalComponent;
120
121 switch (crtn) {
122 case CSSM_OK:
123 status = errSecSuccess;
124 break;
125 case CSSMERR_TP_CERT_REVOKED:
126 status = errSecCertificateRevoked;
127 break;
128 case CSSMERR_APPLETP_NETWORK_FAILURE:
129 status = errSecNetworkFailure;
130 break;
131 case CSSMERR_APPLETP_CRL_NOT_FOUND:
132 status = errSecCRLNotFound;
133 break;
134 default:
135 status = errSecInternalComponent;
136 }
137 return status;
138 }
139
140 #define PEM_STRING_X509 "CERTIFICATE"
141 static CFDataRef CF_RETURNS_RETAINED serializedPathToPemSequences(CFArrayRef certs) {
142 CFMutableDataRef result = NULL;
143 CFIndex certIX, certCount;
144 require_quiet(certs, out);
145 certCount = CFArrayGetCount(certs);
146 require_quiet(certCount > 0, out);
147 require_quiet(result = CFDataCreateMutable(NULL, 0), out);
148 for (certIX = 0; certIX < certCount; certIX++) {
149 CFDataRef certData = (CFDataRef)CFArrayGetValueAtIndex(certs, certIX);
150 require_noerr_quiet(impExpPemEncodeExportRep(certData, PEM_STRING_X509,
151 NULL, result), out);
152 }
153 out:
154 return result;
155 }
156
157 OSStatus SecTrustLegacyCRLStatus(SecCertificateRef cert, CFArrayRef chain, CFURLRef currCRLDP) {
158 OSStatus result = errSecParam;
159 CSSM_RETURN crtn = CSSMERR_TP_INTERNAL_ERROR;
160 CFDataRef serialData = NULL, pemIssuers = NULL, crlDP = NULL;
161 CFMutableArrayRef issuersArray = NULL;
162
163 if (!cert || !chain) {
164 return result;
165 }
166
167 /* serialNumber is a CSSM_DATA with the value from the TBS Certificate. */
168 CSSM_DATA serialNumber = { 0, NULL };
169 serialData = SecCertificateCopySerialNumber(cert, NULL);
170 if (serialData) {
171 serialNumber.Data = (uint8_t *)CFDataGetBytePtr(serialData);
172 serialNumber.Length = CFDataGetLength(serialData);
173 }
174
175 /* issuers is CSSM_DATA containing pem sequence of all issuers in the chain */
176 CSSM_DATA issuers = { 0, NULL };
177 issuersArray = CFArrayCreateMutableCopy(NULL, 0, chain);
178 if (issuersArray) {
179 CFArrayRemoveValueAtIndex(issuersArray, 0);
180 pemIssuers = serializedPathToPemSequences(issuersArray);
181 }
182 if (pemIssuers) {
183 issuers.Data = (uint8_t *)CFDataGetBytePtr(pemIssuers);
184 issuers.Length = CFDataGetLength(pemIssuers);
185 }
186
187 /* crlUrl is CSSM_DATA with the CRLDP url*/
188 CSSM_DATA crlUrl = { 0, NULL };
189 crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true);
190 if (crlDP) {
191 crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP);
192 crlUrl.Length = CFDataGetLength(crlDP);
193 }
194
195 if (serialNumber.Data && issuers.Data && crlUrl.Data) {
196 crtn = ocspdCRLStatus(serialNumber, issuers, NULL, &crlUrl);
197 }
198
199 result = cssmReturnToOSStatus(crtn);
200
201 if (serialData) { CFRelease(serialData); }
202 if (issuersArray) { CFRelease(issuersArray); }
203 if (pemIssuers) { CFRelease(pemIssuers); }
204 if (crlDP) { CFRelease(crlDP); }
205 return result;
206 }
207
208 static CSSM_RETURN ocspdCRLFetchToCache(const CSSM_DATA &crlURL,
209 CSSM_TIMESTRING verifyTime) {
210 Allocator &alloc(Allocator::standard(Allocator::normal));
211 CSSM_DATA crlData = { 0, NULL };
212 CSSM_RETURN crtn;
213
214 crtn = ocspdCRLFetch(alloc, crlURL, NULL, true, true, verifyTime, crlData);
215 if (crlData.Data) { alloc.free(crlData.Data); }
216 return crtn;
217 }
218
219 static OSStatus fetchCRL(CFURLRef currCRLDP, CFAbsoluteTime verifyTime) {
220 OSStatus result = errSecParam;
221 CSSM_RETURN crtn = CSSMERR_TP_INTERNAL_ERROR;
222 CFDataRef crlDP = NULL;
223 char *cssmTime = NULL, *genTime = NULL;
224
225 if (!currCRLDP) {
226 return result;
227 }
228
229 /* crlUrl is CSSM_DATA with the CRLDP url*/
230 CSSM_DATA crlUrl = { 0, NULL };
231 crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true);
232 if (crlDP) {
233 crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP);
234 crlUrl.Length = CFDataGetLength(crlDP);
235 }
236
237 /* determine verification time */
238 cssmTime = (char *)malloc(CSSM_TIME_STRLEN + 1);
239 genTime = (char *)malloc(GENERAL_TIME_STRLEN + 1);
240 if (cssmTime && genTime) {
241 if (verifyTime != 0.0) {
242 cfAbsTimeToGgenTime(verifyTime, genTime);
243 } else {
244 cfAbsTimeToGgenTime(CFAbsoluteTimeGetCurrent(), genTime);
245 }
246 memmove(cssmTime, genTime, GENERAL_TIME_STRLEN - 1); // don't copy the Z
247 cssmTime[CSSM_TIME_STRLEN] = '\0';
248 }
249
250 if (crlUrl.Data && cssmTime) {
251 crtn = ocspdCRLFetchToCache(crlUrl, (CSSM_TIMESTRING)cssmTime);
252 }
253
254 result = cssmReturnToOSStatus(crtn);
255
256 if (crlDP) { CFRelease(crlDP); }
257 if (cssmTime) { free(cssmTime); }
258 if (genTime) { free(genTime); }
259 return result;
260 }
261
262 /*
263 * MARK: async_ocspd methods
264 */
265 static void async_ocspd_complete(async_ocspd_t *ocspd) {
266 if (ocspd->completed) {
267 ocspd->completed(ocspd);
268 }
269 }
270
271 /* Return true, iff we didn't schedule any work, return false if we did. */
272 bool SecTrustLegacyCRLFetch(async_ocspd_t *ocspd,
273 CFURLRef currCRLDP, CFAbsoluteTime verifyTime,
274 SecCertificateRef cert, CFArrayRef chain) {
275 dispatch_async(ocspd->queue, ^ {
276 OSStatus status = fetchCRL(currCRLDP, verifyTime);
277 switch (status) {
278 case errSecSuccess:
279 ocspd->response= SecTrustLegacyCRLStatus(cert, chain, currCRLDP);
280 break;
281 default:
282 ocspd->response = status;
283 break;
284 }
285 async_ocspd_complete(ocspd);
286 if (chain) { CFRelease(chain); }
287 });
288
289 return false; /* false -> something was scheduled. */
290 }