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 <CoreFoundation/CoreFoundation.h>
32 #include <dispatch/dispatch.h>
33 #include <AssertMacros.h>
35 #include <mach/mach_time.h>
37 #include <Security/Security.h>
38 #include <Security/cssmtype.h>
39 #include <Security/SecKeychain.h>
40 #include <Security/SecItemPriv.h>
41 #include <Security/SecTrustSettingsPriv.h>
42 #include <Security/SecCertificate.h>
43 #include <Security/SecImportExport.h>
44 #include <security_keychain/SecImportExportPem.h>
45 #include <security_utilities/debugging.h>
46 #include <Security/SecItemInternal.h>
48 #include <security_ocspd/ocspdClient.h>
49 #include <security_ocspd/ocspdUtils.h>
52 void SecTrustLegacySourcesListenForKeychainEvents(void) {
53 /* Register for CertificateTrustNotification */
55 notify_register_dispatch(kSecServerCertificateTrustNotification
, &out_token
,
56 dispatch_get_main_queue(),
57 ^(int token __unused
) {
58 // Purge keychain parent cache
59 SecItemParentCachePurge();
60 // Purge unrestricted roots cache
61 SecTrustSettingsPurgeUserAdminCertsCache();
67 * MARK: ocspd CRL Interface
69 /* lengths of time strings without trailing NULL */
70 #define CSSM_TIME_STRLEN 14 /* no trailing 'Z' */
71 #define GENERALIZED_TIME_STRLEN 15
73 OSStatus
SecTrustLegacyCRLStatus(SecCertificateRef cert
, CFArrayRef chain
, CFURLRef currCRLDP
);
74 OSStatus
SecTrustLegacyCRLFetch(CFURLRef currCRLDP
, CFAbsoluteTime verifyTime
);
76 static OSStatus
cssmReturnToOSStatus(CSSM_RETURN crtn
) {
77 OSStatus status
= errSecInternalComponent
;
81 status
= errSecSuccess
;
83 case CSSMERR_TP_CERT_REVOKED
:
84 status
= errSecCertificateRevoked
;
86 case CSSMERR_APPLETP_NETWORK_FAILURE
:
87 status
= errSecNetworkFailure
;
89 case CSSMERR_APPLETP_CRL_NOT_FOUND
:
90 status
= errSecCRLNotFound
;
93 status
= errSecInternalComponent
;
98 #define PEM_STRING_X509 "CERTIFICATE"
99 static CFDataRef CF_RETURNS_RETAINED
serializedPathToPemSequences(CFArrayRef certs
) {
100 CFMutableDataRef result
= NULL
;
101 CFIndex certIX
, certCount
;
102 require_quiet(certs
, out
);
103 certCount
= CFArrayGetCount(certs
);
104 require_quiet(certCount
> 0, out
);
105 require_quiet(result
= CFDataCreateMutable(NULL
, 0), out
);
106 for (certIX
= 0; certIX
< certCount
; certIX
++) {
107 CFDataRef certData
= (CFDataRef
)CFArrayGetValueAtIndex(certs
, certIX
);
108 require_noerr_quiet(impExpPemEncodeExportRep(certData
, PEM_STRING_X509
,
115 OSStatus
SecTrustLegacyCRLStatus(SecCertificateRef cert
, CFArrayRef chain
, CFURLRef currCRLDP
) {
116 OSStatus result
= errSecParam
;
117 CSSM_RETURN crtn
= CSSMERR_TP_INTERNAL_ERROR
;
118 CFDataRef serialData
= NULL
, pemIssuers
= NULL
, crlDP
= NULL
;
119 CFMutableArrayRef issuersArray
= NULL
;
121 if (!cert
|| !chain
) {
125 /* serialNumber is a CSSM_DATA with the value from the TBS Certificate. */
126 CSSM_DATA serialNumber
= { 0, NULL
};
127 serialData
= SecCertificateCopySerialNumber(cert
, NULL
);
129 serialNumber
.Data
= (uint8_t *)CFDataGetBytePtr(serialData
);
130 serialNumber
.Length
= CFDataGetLength(serialData
);
133 /* issuers is CSSM_DATA containing pem sequence of all issuers in the chain */
134 CSSM_DATA issuers
= { 0, NULL
};
135 issuersArray
= CFArrayCreateMutableCopy(NULL
, 0, chain
);
137 CFArrayRemoveValueAtIndex(issuersArray
, 0);
138 pemIssuers
= serializedPathToPemSequences(issuersArray
);
141 issuers
.Data
= (uint8_t *)CFDataGetBytePtr(pemIssuers
);
142 issuers
.Length
= CFDataGetLength(pemIssuers
);
145 /* crlUrl is CSSM_DATA with the CRLDP url*/
146 CSSM_DATA crlUrl
= { 0, NULL
};
147 crlDP
= CFURLCreateData(NULL
, currCRLDP
, kCFStringEncodingASCII
, true);
149 crlUrl
.Data
= (uint8_t *)CFDataGetBytePtr(crlDP
);
150 crlUrl
.Length
= CFDataGetLength(crlDP
);
153 if (serialNumber
.Data
&& issuers
.Data
&& crlUrl
.Data
) {
154 crtn
= ocspdCRLStatus(serialNumber
, issuers
, NULL
, &crlUrl
);
157 result
= cssmReturnToOSStatus(crtn
);
159 if (serialData
) { CFRelease(serialData
); }
160 if (issuersArray
) { CFRelease(issuersArray
); }
161 if (pemIssuers
) { CFRelease(pemIssuers
); }
162 if (crlDP
) { CFRelease(crlDP
); }
166 static CSSM_RETURN
ocspdCRLFetchToCache(const CSSM_DATA
&crlURL
,
167 CSSM_TIMESTRING verifyTime
) {
168 Allocator
&alloc(Allocator::standard(Allocator::normal
));
169 CSSM_DATA crlData
= { 0, NULL
};
172 crtn
= ocspdCRLFetch(alloc
, crlURL
, NULL
, true, true, verifyTime
, crlData
);
173 if (crlData
.Data
) { alloc
.free(crlData
.Data
); }
177 static OSStatus
fetchCRL(CFURLRef currCRLDP
, CFAbsoluteTime verifyTime
) {
178 OSStatus result
= errSecParam
;
179 CSSM_RETURN crtn
= CSSMERR_TP_INTERNAL_ERROR
;
180 CFDataRef crlDP
= NULL
;
181 char *cssmTime
= NULL
, *genTime
= NULL
;
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 /* determine verification time */
196 cssmTime
= (char *)malloc(CSSM_TIME_STRLEN
+ 1);
197 genTime
= (char *)malloc(GENERAL_TIME_STRLEN
+ 1);
198 if (cssmTime
&& genTime
) {
199 if (verifyTime
!= 0.0) {
200 cfAbsTimeToGgenTime(verifyTime
, genTime
);
202 cfAbsTimeToGgenTime(CFAbsoluteTimeGetCurrent(), genTime
);
204 memmove(cssmTime
, genTime
, GENERAL_TIME_STRLEN
- 1); // don't copy the Z
205 cssmTime
[CSSM_TIME_STRLEN
] = '\0';
208 if (crlUrl
.Data
&& cssmTime
) {
209 crtn
= ocspdCRLFetchToCache(crlUrl
, (CSSM_TIMESTRING
)cssmTime
);
212 result
= cssmReturnToOSStatus(crtn
);
214 if (crlDP
) { CFRelease(crlDP
); }
215 if (cssmTime
) { free(cssmTime
); }
216 if (genTime
) { free(genTime
); }
221 * MARK: async_ocspd methods
223 static void async_ocspd_complete(async_ocspd_t
*ocspd
) {
224 if (ocspd
->completed
) {
225 ocspd
->completed(ocspd
);
229 /* Return true, iff we didn't schedule any work, return false if we did. */
230 bool SecTrustLegacyCRLFetch(async_ocspd_t
*ocspd
,
231 CFURLRef currCRLDP
, CFAbsoluteTime verifyTime
,
232 SecCertificateRef cert
, CFArrayRef chain
) {
233 ocspd
->start_time
= mach_absolute_time();
234 dispatch_async(ocspd
->queue
, ^ {
235 OSStatus status
= fetchCRL(currCRLDP
, verifyTime
);
238 ocspd
->response
= SecTrustLegacyCRLStatus(cert
, chain
, currCRLDP
);
241 ocspd
->response
= status
;
244 async_ocspd_complete(ocspd
);
245 if (chain
) { CFRelease(chain
); }
248 return false; /* false -> something was scheduled. */