]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecTrustOSXEntryPoints.cpp
Security-57740.31.2.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
41 #include <security_ocspd/ocspdClient.h>
42 #include <security_ocspd/ocspdUtils.h>
43
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <CoreFoundation/CFRunLoop.h>
46 #include <dispatch/dispatch.h>
47 #include <AssertMacros.h>
48 #include <pthread.h>
49
50 /*
51 * MARK: CFRunloop
52 */
53
54 static OSStatus SecLegacySourceChanged(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, __unused void *context) {
55 if (keychainEvent == kSecAddEvent || keychainEvent == kSecDeleteEvent || keychainEvent == kSecUpdateEvent) {
56 /* We don't need to purge the cache if the item changed wasn't a cert */
57 SecKeychainItemRef item = info->item;
58 if (item && CFGetTypeID(item) != SecCertificateGetTypeID()) {
59 return 0;
60 }
61 }
62 // Purge keychain parent cache
63 SecItemParentCachePurge();
64 // Purge unrestricted roots cache
65 SecTrustSettingsPurgeUserAdminCertsCache();
66 return 0;
67 }
68
69 static void *SecTrustOSXCFRunloop(__unused void *unused) {
70 CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, (CFTimeInterval) UINT_MAX, 0, 0, 0, ^(__unused CFRunLoopTimerRef _timer) {
71 /* do nothing */
72 });
73
74 /* add a timer to force the runloop to stay running */
75 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
76 /* add keychain callback before we initiate a runloop to avoid it exiting due to no sources */
77
78 SecKeychainEventMask trustdMask = (kSecAddEventMask | kSecDeleteEventMask | kSecUpdateEventMask |
79 kSecDefaultChangedEventMask | kSecKeychainListChangedMask |
80 kSecTrustSettingsChangedEventMask);
81 SecKeychainAddCallback(SecLegacySourceChanged, trustdMask, NULL);
82
83 try {
84 CFRunLoopRun();
85 }
86 catch (...) {
87 /* An exception was rethrown from the runloop. Since we can't reliably
88 * obtain info about changes to keychains or trust settings anymore,
89 * just exit and respawn the process when needed. */
90
91 secerror("Exception occurred in CFRunLoopRun; exiting");
92 exit(0);
93 }
94 CFRelease(timer);
95 return NULL;
96 }
97
98 void SecTrustLegacySourcesEventRunloopCreate(void) {
99 /* A runloop is currently necessary to receive notifications about changes in the
100 * legacy keychains and trust settings. */
101 static dispatch_once_t once;
102
103 dispatch_once(&once, ^{
104 pthread_attr_t attrs;
105 pthread_t thread;
106
107 pthread_attr_init(&attrs);
108 pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
109
110 /* we do this with traditional pthread to avoid impacting our 512 WQ thread limit since this is a parked thread */
111 pthread_create(&thread, &attrs, SecTrustOSXCFRunloop, NULL);
112 });
113 }
114
115 /*
116 * MARK: ocspd CRL Interface
117 */
118 /* lengths of time strings without trailing NULL */
119 #define CSSM_TIME_STRLEN 14 /* no trailing 'Z' */
120 #define GENERALIZED_TIME_STRLEN 15
121
122 OSStatus SecTrustLegacyCRLStatus(SecCertificateRef cert, CFArrayRef chain, CFURLRef currCRLDP);
123 OSStatus SecTrustLegacyCRLFetch(CFURLRef currCRLDP, CFAbsoluteTime verifyTime);
124
125 static OSStatus cssmReturnToOSStatus(CSSM_RETURN crtn) {
126 OSStatus status = errSecInternalComponent;
127
128 switch (crtn) {
129 case CSSM_OK:
130 status = errSecSuccess;
131 break;
132 case CSSMERR_TP_CERT_REVOKED:
133 status = errSecCertificateRevoked;
134 break;
135 case CSSMERR_APPLETP_NETWORK_FAILURE:
136 status = errSecNetworkFailure;
137 break;
138 case CSSMERR_APPLETP_CRL_NOT_FOUND:
139 status = errSecCRLNotFound;
140 break;
141 default:
142 status = errSecInternalComponent;
143 }
144 return status;
145 }
146
147 #define PEM_STRING_X509 "CERTIFICATE"
148 static CFDataRef serializedPathToPemSequences(CFArrayRef certs) {
149 CFMutableDataRef result = NULL;
150 CFIndex certIX, certCount;
151 require_quiet(certs, out);
152 certCount = CFArrayGetCount(certs);
153 require_quiet(certCount > 0, out);
154 require_quiet(result = CFDataCreateMutable(NULL, 0), out);
155 for (certIX = 0; certIX < certCount; certIX++) {
156 CFDataRef certData = (CFDataRef)CFArrayGetValueAtIndex(certs, certIX);
157 require_noerr_quiet(impExpPemEncodeExportRep(certData, PEM_STRING_X509,
158 NULL, result), out);
159 }
160 out:
161 return result;
162 }
163
164 OSStatus SecTrustLegacyCRLStatus(SecCertificateRef cert, CFArrayRef chain, CFURLRef currCRLDP) {
165 OSStatus result = errSecParam;
166 CSSM_RETURN crtn = CSSMERR_TP_INTERNAL_ERROR;
167 CFDataRef serialData = NULL, pemIssuers = NULL, crlDP = NULL;
168 CFMutableArrayRef issuersArray = NULL;
169
170 if (!cert || !chain) {
171 return result;
172 }
173
174 /* serialNumber is a CSSM_DATA with the value from the TBS Certificate. */
175 CSSM_DATA serialNumber = { 0, NULL };
176 serialData = SecCertificateCopySerialNumber(cert, NULL);
177 if (serialData) {
178 serialNumber.Data = (uint8_t *)CFDataGetBytePtr(serialData);
179 serialNumber.Length = CFDataGetLength(serialData);
180 }
181
182 /* issuers is CSSM_DATA containing pem sequence of all issuers in the chain */
183 CSSM_DATA issuers = { 0, NULL };
184 issuersArray = CFArrayCreateMutableCopy(NULL, 0, chain);
185 if (issuersArray) {
186 CFArrayRemoveValueAtIndex(issuersArray, 0);
187 pemIssuers = serializedPathToPemSequences(issuersArray);
188 }
189 if (pemIssuers) {
190 issuers.Data = (uint8_t *)CFDataGetBytePtr(pemIssuers);
191 issuers.Length = CFDataGetLength(pemIssuers);
192 }
193
194 /* crlUrl is CSSM_DATA with the CRLDP url*/
195 CSSM_DATA crlUrl = { 0, NULL };
196 crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true);
197 if (crlDP) {
198 crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP);
199 crlUrl.Length = CFDataGetLength(crlDP);
200 }
201
202 if (serialNumber.Data && issuers.Data && crlUrl.Data) {
203 crtn = ocspdCRLStatus(serialNumber, issuers, NULL, &crlUrl);
204 }
205
206 result = cssmReturnToOSStatus(crtn);
207
208 if (serialData) { CFRelease(serialData); }
209 if (issuersArray) { CFRelease(issuersArray); }
210 if (pemIssuers) { CFRelease(pemIssuers); }
211 if (crlDP) { CFRelease(crlDP); }
212 return result;
213 }
214
215 static CSSM_RETURN ocspdCRLFetchToCache(const CSSM_DATA &crlURL,
216 CSSM_TIMESTRING verifyTime) {
217 Allocator &alloc(Allocator::standard(Allocator::normal));
218 CSSM_DATA crlData = { 0, NULL };
219 CSSM_RETURN crtn;
220
221 crtn = ocspdCRLFetch(alloc, crlURL, NULL, true, true, verifyTime, crlData);
222 if (crlData.Data) { alloc.free(crlData.Data); }
223 return crtn;
224 }
225
226 static OSStatus fetchCRL(CFURLRef currCRLDP, CFAbsoluteTime verifyTime) {
227 OSStatus result = errSecParam;
228 CSSM_RETURN crtn = CSSMERR_TP_INTERNAL_ERROR;
229 CFDataRef crlDP = NULL;
230 char *cssmTime = NULL, *genTime = NULL;
231
232 if (!currCRLDP) {
233 return result;
234 }
235
236 /* crlUrl is CSSM_DATA with the CRLDP url*/
237 CSSM_DATA crlUrl = { 0, NULL };
238 crlDP = CFURLCreateData(NULL, currCRLDP, kCFStringEncodingASCII, true);
239 if (crlDP) {
240 crlUrl.Data = (uint8_t *)CFDataGetBytePtr(crlDP);
241 crlUrl.Length = CFDataGetLength(crlDP);
242 }
243
244 /* determine verification time */
245 cssmTime = (char *)malloc(CSSM_TIME_STRLEN + 1);
246 genTime = (char *)malloc(GENERAL_TIME_STRLEN + 1);
247 if (cssmTime && genTime) {
248 if (verifyTime != 0.0) {
249 cfAbsTimeToGgenTime(verifyTime, genTime);
250 } else {
251 cfAbsTimeToGgenTime(CFAbsoluteTimeGetCurrent(), genTime);
252 }
253 memmove(cssmTime, genTime, GENERAL_TIME_STRLEN - 1); // don't copy the Z
254 cssmTime[CSSM_TIME_STRLEN] = '\0';
255 }
256
257 if (crlUrl.Data && cssmTime) {
258 crtn = ocspdCRLFetchToCache(crlUrl, (CSSM_TIMESTRING)cssmTime);
259 }
260
261 result = cssmReturnToOSStatus(crtn);
262
263 if (crlDP) { CFRelease(crlDP); }
264 if (cssmTime) { free(cssmTime); }
265 if (genTime) { free(genTime); }
266 return result;
267 }
268
269 /*
270 * MARK: async_ocspd methods
271 */
272 static void async_ocspd_complete(async_ocspd_t *ocspd) {
273 if (ocspd->completed) {
274 ocspd->completed(ocspd);
275 }
276 }
277
278 /* Return true, iff we didn't schedule any work, return false if we did. */
279 bool SecTrustLegacyCRLFetch(async_ocspd_t *ocspd,
280 CFURLRef currCRLDP, CFAbsoluteTime verifyTime,
281 SecCertificateRef cert, CFArrayRef chain) {
282 dispatch_async(ocspd->queue, ^ {
283 OSStatus status = fetchCRL(currCRLDP, verifyTime);
284 switch (status) {
285 case errSecSuccess:
286 ocspd->response= SecTrustLegacyCRLStatus(cert, chain, currCRLDP);
287 break;
288 default:
289 ocspd->response = status;
290 break;
291 }
292 async_ocspd_complete(ocspd);
293 if (chain) { CFRelease(chain); }
294 });
295
296 return false; /* false -> something was scheduled. */
297 }