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>
40 #include <Security/SecItemInternal.h>
42 #include <security_ocspd/ocspdClient.h>
43 #include <security_ocspd/ocspdUtils.h>
45 #include <CoreFoundation/CoreFoundation.h>
46 #include <CoreFoundation/CFRunLoop.h>
47 #include <dispatch/dispatch.h>
48 #include <AssertMacros.h>
56 static void *SecTrustOSXCFRunloop(__unused
void *unused
) {
57 CFRunLoopTimerRef timer
= CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault
, (CFTimeInterval
) UINT_MAX
, 0, 0, 0, ^(__unused CFRunLoopTimerRef _timer
) {
61 /* add a timer to force the runloop to stay running */
62 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
63 /* Register for CertificateTrustNotification */
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();
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. */
84 secerror("Exception occurred in CFRunLoopRun; exiting");
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
;
96 dispatch_once(&once
, ^{
100 pthread_attr_init(&attrs
);
101 pthread_attr_setdetachstate(&attrs
, PTHREAD_CREATE_DETACHED
);
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
);
109 * MARK: ocspd CRL Interface
111 /* lengths of time strings without trailing NULL */
112 #define CSSM_TIME_STRLEN 14 /* no trailing 'Z' */
113 #define GENERALIZED_TIME_STRLEN 15
115 OSStatus
SecTrustLegacyCRLStatus(SecCertificateRef cert
, CFArrayRef chain
, CFURLRef currCRLDP
);
116 OSStatus
SecTrustLegacyCRLFetch(CFURLRef currCRLDP
, CFAbsoluteTime verifyTime
);
118 static OSStatus
cssmReturnToOSStatus(CSSM_RETURN crtn
) {
119 OSStatus status
= errSecInternalComponent
;
123 status
= errSecSuccess
;
125 case CSSMERR_TP_CERT_REVOKED
:
126 status
= errSecCertificateRevoked
;
128 case CSSMERR_APPLETP_NETWORK_FAILURE
:
129 status
= errSecNetworkFailure
;
131 case CSSMERR_APPLETP_CRL_NOT_FOUND
:
132 status
= errSecCRLNotFound
;
135 status
= errSecInternalComponent
;
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
,
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
;
163 if (!cert
|| !chain
) {
167 /* serialNumber is a CSSM_DATA with the value from the TBS Certificate. */
168 CSSM_DATA serialNumber
= { 0, NULL
};
169 serialData
= SecCertificateCopySerialNumber(cert
, NULL
);
171 serialNumber
.Data
= (uint8_t *)CFDataGetBytePtr(serialData
);
172 serialNumber
.Length
= CFDataGetLength(serialData
);
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
);
179 CFArrayRemoveValueAtIndex(issuersArray
, 0);
180 pemIssuers
= serializedPathToPemSequences(issuersArray
);
183 issuers
.Data
= (uint8_t *)CFDataGetBytePtr(pemIssuers
);
184 issuers
.Length
= CFDataGetLength(pemIssuers
);
187 /* crlUrl is CSSM_DATA with the CRLDP url*/
188 CSSM_DATA crlUrl
= { 0, NULL
};
189 crlDP
= CFURLCreateData(NULL
, currCRLDP
, kCFStringEncodingASCII
, true);
191 crlUrl
.Data
= (uint8_t *)CFDataGetBytePtr(crlDP
);
192 crlUrl
.Length
= CFDataGetLength(crlDP
);
195 if (serialNumber
.Data
&& issuers
.Data
&& crlUrl
.Data
) {
196 crtn
= ocspdCRLStatus(serialNumber
, issuers
, NULL
, &crlUrl
);
199 result
= cssmReturnToOSStatus(crtn
);
201 if (serialData
) { CFRelease(serialData
); }
202 if (issuersArray
) { CFRelease(issuersArray
); }
203 if (pemIssuers
) { CFRelease(pemIssuers
); }
204 if (crlDP
) { CFRelease(crlDP
); }
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
};
214 crtn
= ocspdCRLFetch(alloc
, crlURL
, NULL
, true, true, verifyTime
, crlData
);
215 if (crlData
.Data
) { alloc
.free(crlData
.Data
); }
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
;
229 /* crlUrl is CSSM_DATA with the CRLDP url*/
230 CSSM_DATA crlUrl
= { 0, NULL
};
231 crlDP
= CFURLCreateData(NULL
, currCRLDP
, kCFStringEncodingASCII
, true);
233 crlUrl
.Data
= (uint8_t *)CFDataGetBytePtr(crlDP
);
234 crlUrl
.Length
= CFDataGetLength(crlDP
);
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
);
244 cfAbsTimeToGgenTime(CFAbsoluteTimeGetCurrent(), genTime
);
246 memmove(cssmTime
, genTime
, GENERAL_TIME_STRLEN
- 1); // don't copy the Z
247 cssmTime
[CSSM_TIME_STRLEN
] = '\0';
250 if (crlUrl
.Data
&& cssmTime
) {
251 crtn
= ocspdCRLFetchToCache(crlUrl
, (CSSM_TIMESTRING
)cssmTime
);
254 result
= cssmReturnToOSStatus(crtn
);
256 if (crlDP
) { CFRelease(crlDP
); }
257 if (cssmTime
) { free(cssmTime
); }
258 if (genTime
) { free(genTime
); }
263 * MARK: async_ocspd methods
265 static void async_ocspd_complete(async_ocspd_t
*ocspd
) {
266 if (ocspd
->completed
) {
267 ocspd
->completed(ocspd
);
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
);
279 ocspd
->response
= SecTrustLegacyCRLStatus(cert
, chain
, currCRLDP
);
282 ocspd
->response
= status
;
285 async_ocspd_complete(ocspd
);
286 if (chain
) { CFRelease(chain
); }
289 return false; /* false -> something was scheduled. */