2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecTrustOSXEntryPoints - Interface for unified SecTrust into OS X Security
29 #include "SecTrustOSXEntryPoints.h"
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>
41 #include <security_ocspd/ocspdClient.h>
42 #include <security_ocspd/ocspdUtils.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <CoreFoundation/CFRunLoop.h>
46 #include <dispatch/dispatch.h>
47 #include <AssertMacros.h>
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()) {
62 // Purge keychain parent cache
63 SecItemParentCachePurge();
64 // Purge unrestricted roots cache
65 SecTrustSettingsPurgeUserAdminCertsCache();
69 static void *SecTrustOSXCFRunloop(__unused
void *unused
) {
70 CFRunLoopTimerRef timer
= CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault
, (CFTimeInterval
) UINT_MAX
, 0, 0, 0, ^(__unused CFRunLoopTimerRef _timer
) {
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 */
78 SecKeychainEventMask trustdMask
= (kSecAddEventMask
| kSecDeleteEventMask
| kSecUpdateEventMask
|
79 kSecDefaultChangedEventMask
| kSecKeychainListChangedMask
|
80 kSecTrustSettingsChangedEventMask
);
81 SecKeychainAddCallback(SecLegacySourceChanged
, trustdMask
, NULL
);
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. */
91 secerror("Exception occurred in CFRunLoopRun; exiting");
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
;
103 dispatch_once(&once
, ^{
104 pthread_attr_t attrs
;
107 pthread_attr_init(&attrs
);
108 pthread_attr_setdetachstate(&attrs
, PTHREAD_CREATE_DETACHED
);
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
);
116 * MARK: ocspd CRL Interface
118 /* lengths of time strings without trailing NULL */
119 #define CSSM_TIME_STRLEN 14 /* no trailing 'Z' */
120 #define GENERALIZED_TIME_STRLEN 15
122 OSStatus
SecTrustLegacyCRLStatus(SecCertificateRef cert
, CFArrayRef chain
, CFURLRef currCRLDP
);
123 OSStatus
SecTrustLegacyCRLFetch(CFURLRef currCRLDP
, CFAbsoluteTime verifyTime
);
125 static OSStatus
cssmReturnToOSStatus(CSSM_RETURN crtn
) {
126 OSStatus status
= errSecInternalComponent
;
130 status
= errSecSuccess
;
132 case CSSMERR_TP_CERT_REVOKED
:
133 status
= errSecCertificateRevoked
;
135 case CSSMERR_APPLETP_NETWORK_FAILURE
:
136 status
= errSecNetworkFailure
;
138 case CSSMERR_APPLETP_CRL_NOT_FOUND
:
139 status
= errSecCRLNotFound
;
142 status
= errSecInternalComponent
;
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
,
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
;
170 if (!cert
|| !chain
) {
174 /* serialNumber is a CSSM_DATA with the value from the TBS Certificate. */
175 CSSM_DATA serialNumber
= { 0, NULL
};
176 serialData
= SecCertificateCopySerialNumber(cert
, NULL
);
178 serialNumber
.Data
= (uint8_t *)CFDataGetBytePtr(serialData
);
179 serialNumber
.Length
= CFDataGetLength(serialData
);
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
);
186 CFArrayRemoveValueAtIndex(issuersArray
, 0);
187 pemIssuers
= serializedPathToPemSequences(issuersArray
);
190 issuers
.Data
= (uint8_t *)CFDataGetBytePtr(pemIssuers
);
191 issuers
.Length
= CFDataGetLength(pemIssuers
);
194 /* crlUrl is CSSM_DATA with the CRLDP url*/
195 CSSM_DATA crlUrl
= { 0, NULL
};
196 crlDP
= CFURLCreateData(NULL
, currCRLDP
, kCFStringEncodingASCII
, true);
198 crlUrl
.Data
= (uint8_t *)CFDataGetBytePtr(crlDP
);
199 crlUrl
.Length
= CFDataGetLength(crlDP
);
202 if (serialNumber
.Data
&& issuers
.Data
&& crlUrl
.Data
) {
203 crtn
= ocspdCRLStatus(serialNumber
, issuers
, NULL
, &crlUrl
);
206 result
= cssmReturnToOSStatus(crtn
);
208 if (serialData
) { CFRelease(serialData
); }
209 if (issuersArray
) { CFRelease(issuersArray
); }
210 if (pemIssuers
) { CFRelease(pemIssuers
); }
211 if (crlDP
) { CFRelease(crlDP
); }
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
};
221 crtn
= ocspdCRLFetch(alloc
, crlURL
, NULL
, true, true, verifyTime
, crlData
);
222 if (crlData
.Data
) { alloc
.free(crlData
.Data
); }
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
;
236 /* crlUrl is CSSM_DATA with the CRLDP url*/
237 CSSM_DATA crlUrl
= { 0, NULL
};
238 crlDP
= CFURLCreateData(NULL
, currCRLDP
, kCFStringEncodingASCII
, true);
240 crlUrl
.Data
= (uint8_t *)CFDataGetBytePtr(crlDP
);
241 crlUrl
.Length
= CFDataGetLength(crlDP
);
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
);
251 cfAbsTimeToGgenTime(CFAbsoluteTimeGetCurrent(), genTime
);
253 memmove(cssmTime
, genTime
, GENERAL_TIME_STRLEN
- 1); // don't copy the Z
254 cssmTime
[CSSM_TIME_STRLEN
] = '\0';
257 if (crlUrl
.Data
&& cssmTime
) {
258 crtn
= ocspdCRLFetchToCache(crlUrl
, (CSSM_TIMESTRING
)cssmTime
);
261 result
= cssmReturnToOSStatus(crtn
);
263 if (crlDP
) { CFRelease(crlDP
); }
264 if (cssmTime
) { free(cssmTime
); }
265 if (genTime
) { free(genTime
); }
270 * MARK: async_ocspd methods
272 static void async_ocspd_complete(async_ocspd_t
*ocspd
) {
273 if (ocspd
->completed
) {
274 ocspd
->completed(ocspd
);
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
);
286 ocspd
->response
= SecTrustLegacyCRLStatus(cert
, chain
, currCRLDP
);
289 ocspd
->response
= status
;
292 async_ocspd_complete(ocspd
);
293 if (chain
) { CFRelease(chain
); }
296 return false; /* false -> something was scheduled. */