2 * Copyright (c) 2008-2018 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 * SecRevocationServer.c - Engine for evaluating certificate revocation.
28 #include <AssertMacros.h>
30 #include <mach/mach_time.h>
32 #include <Security/SecCertificatePriv.h>
33 #include <Security/SecCertificateInternal.h>
34 #include <Security/SecPolicyPriv.h>
35 #include <Security/SecTrustPriv.h>
36 #include <Security/SecInternal.h>
38 #include <utilities/debugging.h>
39 #include <utilities/SecCFWrappers.h>
40 #include <utilities/SecIOFormat.h>
42 #include <securityd/SecTrustServer.h>
43 #include <securityd/SecOCSPRequest.h>
44 #include <securityd/SecOCSPResponse.h>
45 #include <securityd/SecOCSPCache.h>
46 #include <securityd/SecRevocationDb.h>
47 #include <securityd/SecCertificateServer.h>
48 #include <securityd/SecPolicyServer.h>
49 #include <securityd/SecRevocationNetworking.h>
51 #include <securityd/SecRevocationServer.h>
54 /********************************************************
55 ****************** OCSP RVC Functions ******************
56 ********************************************************/
57 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
58 const CFAbsoluteTime kSecOCSPResponseOnlineTTL
= 5.0 * 60.0;
59 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
61 static void SecORVCFinish(SecORVCRef orvc
) {
62 secdebug("alloc", "finish orvc %p", orvc
);
63 if (orvc
->ocspRequest
) {
64 SecOCSPRequestFinalize(orvc
->ocspRequest
);
65 orvc
->ocspRequest
= NULL
;
67 if (orvc
->ocspResponse
) {
68 SecOCSPResponseFinalize(orvc
->ocspResponse
);
69 orvc
->ocspResponse
= NULL
;
70 if (orvc
->ocspSingleResponse
) {
71 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
72 orvc
->ocspSingleResponse
= NULL
;
75 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
78 /* Process a verified ocsp response for a given cert. Return true if the
79 certificate status was obtained. */
80 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
83 switch (this->certStatus
) {
85 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
86 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
87 in the info dictionary. */
88 //cert.revokeCheckGood(true);
89 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
93 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
94 /* @@@ Mark cert as revoked (with reason) at revocation date in
95 the info dictionary, or perhaps we should use a different key per
96 reason? That way a client using exceptions can ignore some but
98 SInt32 reason
= this->crlReason
;
99 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
100 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
103 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
105 /* make the revocation reason available in the trust result */
106 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
113 /* not an error, no per-cert status, nothing here */
114 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
118 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
119 (int)this->certStatus
, rvc
->certIX
);
127 void SecORVCUpdatePVC(SecORVCRef rvc
) {
128 if (rvc
->ocspSingleResponse
) {
129 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
131 if (rvc
->ocspResponse
) {
132 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
136 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
139 SecOCSPEvaluateCompleted(const void *userData
,
140 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
141 SecTrustResultType result
) {
142 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
144 Block_release(evaluated
);
148 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
149 __block
bool evaluated
= false;
150 bool trusted
= false;
151 if (!signers
|| !issuers
) {
155 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
156 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
157 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
158 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
159 CFRelease(ocspSigner
);
161 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
162 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
167 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
168 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
169 signers
, issuers
, true, false,
170 policies
, NULL
, NULL
, NULL
,
171 verifyTime
, NULL
, NULL
,
172 SecOCSPEvaluateCompleted
, completed
);
173 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
174 SecPathBuilderStep(oBuilder
);
175 CFReleaseNull(clientAuditToken
);
176 CFReleaseNull(policies
);
178 /* verify the public key of the issuer signed the OCSP signer */
180 SecCertificateRef issuer
= NULL
, signer
= NULL
;
181 SecKeyRef issuerPubKey
= NULL
;
183 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
184 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
187 issuerPubKey
= SecCertificateCopyKey(issuer
);
189 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
192 secnotice("ocsp", "ocsp signer cert not signed by issuer");
194 CFReleaseNull(issuerPubKey
);
200 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
202 SecCertificatePathVCRef issuers
= SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc
->builder
), rvc
->certIX
+ 1);
203 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers
, 0)) : NULL
;
204 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
205 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
207 if (signer
&& signers
) {
208 if (issuer
&& CFEqual(signer
, issuer
)) {
209 /* We already know we trust issuer since it's the issuer of the
210 * cert we are verifying. */
211 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
215 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
217 CFMutableArrayRef signerCerts
= NULL
;
218 CFArrayRef issuerCerts
= NULL
;
220 /* Ensure the signer cert is the 0th cert for trust evaluation */
221 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
222 CFArrayAppendValue(signerCerts
, signer
);
223 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
226 issuerCerts
= SecCertificatePathVCCopyCertificates(issuers
);
229 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
230 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
234 /* @@@ We don't trust the cert so don't use this response. */
235 secnotice("ocsp", "ocsp response signed by certificate which "
236 "does not satisfy ocspSigner policy");
239 CFReleaseNull(signerCerts
);
240 CFReleaseNull(issuerCerts
);
243 /* @@@ No signer found for this ocsp response, discard it. */
244 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
249 #if DUMP_OCSPRESPONSES
251 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
252 rvc
->certIX
, (trusted
? "t" : "u"));
253 secdumpdata(ocspResponse
->data
, buf
);
255 CFReleaseNull(issuers
);
256 CFReleaseNull(issuer
);
257 CFReleaseNull(signers
);
258 CFReleaseNull(signer
);
262 void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/,
263 CFTimeInterval maxAge
, bool updateCache
, bool fromCache
) {
264 SecOCSPSingleResponseRef sr
= NULL
;
265 require_quiet(ocspResponse
, errOut
);
266 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
267 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
268 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
269 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
270 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
271 // Check if this response is fresher than any (cached) response we might still have in the rvc.
272 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
274 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
276 /* Check the OCSP response signature and verify the response if not pulled from the cache.
277 * Performance optimization since we don't write invalid responses to the cache. */
279 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
280 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
283 /* Always check the OCSP response signature and verify the response (since the cache is user-modifiable). */
284 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
285 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
288 // If we get here, we have a properly signed ocsp response
289 // but we haven't checked dates yet.
291 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
292 if (sr
->certStatus
== CS_Good
) {
293 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
294 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
295 } else if (sr
->certStatus
== CS_Revoked
) {
296 // Expire revoked responses when the subject certificate itself expires.
297 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
));
300 // Ok we like the new response, let's toss the old one.
302 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
304 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
305 rvc
->ocspResponse
= ocspResponse
;
308 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
309 rvc
->ocspSingleResponse
= sr
;
312 rvc
->done
= sr_valid
;
315 if (sr
) SecOCSPSingleResponseDestroy(sr
);
316 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
319 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
320 SecORVCRef orvc
= NULL
;
321 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
322 secdebug("alloc", "orvc %p", orvc
);
324 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
325 orvc
->builder
= builder
;
327 orvc
->certIX
= certIX
;
329 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
330 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
331 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
332 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
338 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
339 /* Get stapled OCSP responses */
340 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
342 if(ocspResponsesData
) {
343 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
344 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
345 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
346 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false, false);
348 CFRelease(ocspResponsesData
);
353 /********************************************************
354 ******************* CRL RVC Functions ******************
355 ********************************************************/
357 #include <../trustd/macOS/SecTrustOSXEntryPoints.h>
358 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
360 /* CRL Revocation verification context. */
361 struct OpaqueSecCRVC
{
362 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
363 async_ocspd_t async_ocspd
;
365 /* Pointer to the builder for this revocation check. */
366 SecPathBuilderRef builder
;
368 /* Pointer to the generic rvc for this revocation check */
371 /* The current CRL status from ocspd. */
374 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
377 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
378 current distribution point. */
379 CFIndex distributionPointIX
;
381 /* URL of current distribution point. */
382 CFURLRef distributionPoint
;
384 /* Date until which this revocation status is valid. */
385 CFAbsoluteTime nextUpdate
;
390 static void SecCRVCFinish(SecCRVCRef crvc
) {
394 #define MAX_CRL_DPS 3
395 #define CRL_REQUEST_THRESHOLD 10
397 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
398 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
399 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
401 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
402 if (crlDPCount
>= CRL_REQUEST_THRESHOLD
) {
403 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount
);
406 while (rvc
->distributionPointIX
< crlDPCount
&& rvc
->distributionPointIX
< MAX_CRL_DPS
) {
407 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
408 rvc
->distributionPointIX
++;
409 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
411 /* We only support http and https responders currently. */
412 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
413 CFEqual(CFSTR("https"), scheme
) ||
414 CFEqual(CFSTR("ldap"), scheme
));
417 return distributionPoint
;
424 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
425 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
426 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
427 CFArrayRef serializedCertPath
= SecCertificatePathVCCreateSerialized(path
);
428 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
429 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
430 CFReleaseNull(serializedCertPath
);
431 /* we got a response indicating that the CRL was checked */
432 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
434 /* ocspd doesn't give us the nextUpdate time, so set to default */
435 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
439 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
440 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
441 SecCRVCGetCRLStatus(rvc
);
442 if (rvc
->status
== errSecCertificateRevoked
) {
448 /* Fire off an async http request for this certs revocation status, return
449 false if request was queued, true if we're done. */
450 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
451 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
452 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
453 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
454 CFArrayRef serializedCertPath
= SecCertificatePathVCCreateSerialized(path
);
455 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
456 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
457 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
458 CFDataRef clientAuditToken
= NULL
;
459 SecTaskRef task
= NULL
;
460 audit_token_t auditToken
= {};
461 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
462 require(clientAuditToken
, out
);
463 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
464 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
465 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
466 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
467 rvc
->distributionPoint
, task
);
470 CFReleaseNull(clientAuditToken
);
472 /* Async request was posted, wait for reply. */
480 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
481 if (rvc
->status
== errSecCertificateRevoked
) {
482 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
483 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
484 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
485 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
488 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
490 /* make the revocation reason available in the trust result */
491 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
494 CFReleaseNull(cfreason
);
498 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
499 SecCRVCRef rvc
= ocspd
->info
;
500 SecPathBuilderRef builder
= rvc
->builder
;
501 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
503 /* Add the time this fetch took to complete to the total time */
504 analytics
->crl_fetch_time
+= (mach_absolute_time() - ocspd
->start_time
);
506 /* we got a response indicating that the CRL was checked */
507 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
508 rvc
->status
= ocspd
->response
;
510 /* ocspd doesn't give us the nextUpdate time, so set to default */
511 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
512 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
513 SecCRVCUpdatePVC(rvc
);
514 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
515 secdebug("rvc", "done with all async jobs");
516 SecPathBuilderStep(builder
);
520 /* We didn't get any data back, so the fetch failed */
521 analytics
->crl_fetch_failed
++;
523 if(SecCRVCFetchNext(rvc
)) {
524 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
525 secdebug("rvc", "done with all async jobs");
526 SecPathBuilderStep(builder
);
532 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
533 SecCRVCRef crvc
= NULL
;
534 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
536 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
537 crvc
->builder
= builder
;
539 crvc
->certIX
= certIX
;
540 crvc
->status
= errSecInternal
;
541 crvc
->distributionPointIX
= 0;
542 crvc
->distributionPoint
= NULL
;
543 crvc
->nextUpdate
= NULL_TIME
;
544 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(builder
);
545 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
546 crvc
->async_ocspd
.response
= errSecInternal
;
547 crvc
->async_ocspd
.info
= crvc
;
553 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
554 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
555 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
556 if (revocation_method
&&
557 CFEqual(kSecPolicyCheckRevocationCRL
, revocation_method
)) {
558 /* Our client insists on CRLs */
559 secinfo("rvc", "client told us to check CRL");
561 analytics
->crl_client
= true;
565 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
566 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
567 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
568 (revocation_method
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, revocation_method
))) {
569 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
570 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
571 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
573 analytics
->crl_cert
= true;
579 #endif /* ENABLE_CRLS */
581 void SecRVCDelete(SecRVCRef rvc
) {
582 secdebug("alloc", "delete rvc %p", rvc
);
584 SecORVCFinish(rvc
->orvc
);
590 SecCRVCFinish(rvc
->crvc
);
595 if (rvc
->valid_info
) {
596 CFReleaseNull(rvc
->valid_info
);
600 // Forward declaration
601 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
);
603 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
604 secdebug("alloc", "rvc %p", rvc
);
605 rvc
->builder
= builder
;
606 rvc
->certIX
= certIX
;
607 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
609 rvc
->crvc
= SecCRVCCreate(rvc
, builder
, certIX
);
617 SecRVCSetFinishedWithoutNetwork(rvc
);
624 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
625 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
626 if (!revocation_method
627 || !CFEqual(revocation_method
, kSecPolicyCheckRevocationCRL
)) {
633 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
638 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc
) {
639 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
642 if (!rvc
->valid_info
->hasDateConstraints
) {
645 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
649 CFAbsoluteTime certIssued
= SecCertificateNotValidBefore(certificate
);
650 CFAbsoluteTime caNotBefore
= -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
651 CFAbsoluteTime caNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
652 if (rvc
->valid_info
->notBeforeDate
) {
653 caNotBefore
= CFDateGetAbsoluteTime(rvc
->valid_info
->notBeforeDate
);
655 if (rvc
->valid_info
->notAfterDate
) {
656 caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
657 /* per the Valid specification, if this date is in the past, we need to check CT. */
658 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
659 if (caNotAfter
< now
) {
660 rvc
->valid_info
->requireCT
= true;
663 if ((certIssued
< caNotBefore
) && (rvc
->certIX
> 0)) {
664 /* not-before constraint is only applied to leaf certificate, for now. */
668 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
669 if ((certIssued
< caNotBefore
) || (certIssued
> caNotAfter
)) {
670 /* We are outside the constrained validity period. */
671 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
672 (rvc
->valid_info
->overridable
) ? "" : " (non-recoverable error)");
674 analytics
->valid_status
|= TAValidDateContrainedRevoked
;
676 if (rvc
->valid_info
->overridable
) {
677 /* error is recoverable, treat certificate as untrusted
678 (note this date check is different from kSecPolicyCheckTemporalValidity) */
679 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
680 kCFBooleanFalse
, true);
682 /* error is non-overridable, treat certificate as revoked */
683 SInt32 reason
= 0; /* unspecified reason code */
684 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
685 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
687 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
689 /* make the revocation reason available in the trust result */
690 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
692 CFReleaseNull(cfreason
);
694 } else if (analytics
) {
695 analytics
->valid_status
|= TAValidDateConstrainedOK
;
699 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc
) {
700 if (!rvc
|| !rvc
->valid_info
) {
703 SecValidInfoRef info
= rvc
->valid_info
;
704 /* outcomes as defined in Valid server specification */
705 if (info
->format
== kSecValidInfoFormatSerial
||
706 info
->format
== kSecValidInfoFormatSHA256
) {
707 if (info
->noCACheck
|| info
->complete
|| info
->isOnList
) {
710 } else { /* info->format == kSecValidInfoFormatNto1 */
711 if (info
->noCACheck
|| (info
->complete
&& !info
->isOnList
)) {
718 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc
) {
719 if (!rvc
|| !rvc
->valid_info
) {
722 SecValidInfoRef info
= rvc
->valid_info
;
723 /* either not present on an allowlist, or present on a blocklist */
724 return (!info
->isOnList
&& info
->valid
) || (info
->isOnList
&& !info
->valid
);
727 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc
) {
728 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
731 if (rvc
->valid_info
->overridable
) {
732 /* error is recoverable, treat certificate as untrusted */
733 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedLeaf
, rvc
->certIX
,
734 kCFBooleanFalse
, true);
737 /* error is fatal at this point */
738 if (!SecRVCHasRevokedValidInfo(rvc
) || rvc
->valid_info
->noCACheck
) {
739 /* result key should indicate blocked instead of revoked,
740 * but result must be non-recoverable */
741 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckBlackListedLeaf
, rvc
->certIX
,
742 kCFBooleanFalse
, true);
745 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
746 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
747 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
749 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
751 /* make the revocation reason available in the trust result */
752 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
754 CFReleaseNull(cfreason
);
757 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
758 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
761 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
762 SecValidInfoRef info
= rvc
->valid_info
;
764 bool definitive
= SecRVCHasDefinitiveValidInfo(rvc
);
765 bool revoked
= SecRVCHasRevokedValidInfo(rvc
);
768 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
771 analytics
->valid_status
|= definitive
? TAValidDefinitelyRevoked
: TAValidProbablyRevoked
;
773 analytics
->valid_status
|= definitive
? TAValidDefinitelyOK
: TAValidProbablyOK
;
775 analytics
->valid_require_ct
|= info
->requireCT
;
776 analytics
->valid_known_intermediates_only
|= info
->knownOnly
;
779 /* Handle no-ca cases */
780 if (info
->noCACheck
) {
781 bool allowed
= (info
->valid
&& info
->complete
&& info
->isOnList
);
783 /* definitely revoked */
784 SecRVCSetValidDeterminedErrorResult(rvc
);
785 } else if (allowed
) {
786 /* definitely not revoked (allowlisted) */
787 SecCertificatePathVCSetIsAllowlisted(path
, true);
789 /* no-ca is definitive; no need to check further. */
790 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
791 (allowed
) ? "allowed" : "revoked", rvc
->certIX
);
796 /* Handle date constraints, if present.
797 * Note: a not-after date may set the CT requirement,
798 * so check requireCT after this function is called. */
799 SecRVCProcessValidDateConstraints(rvc
);
801 /* Set CT requirement on path, if present. */
802 if (info
->requireCT
) {
803 SecPathCTPolicy ctp
= kSecPathCTRequired
;
804 if (info
->overridable
) {
805 ctp
= kSecPathCTRequiredOverridable
;
807 SecCertificatePathVCSetRequiresCT(path
, ctp
);
810 /* Trigger OCSP for any non-definitive or revoked cases */
811 if (!definitive
|| revoked
) {
812 info
->checkOCSP
= true;
815 if (info
->checkOCSP
) {
816 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
817 CFIndex issuerIX
= rvc
->certIX
+ 1;
818 if (issuerIX
>= count
) {
819 /* cannot perform a revocation check on the last cert in the
820 chain, since we don't have its issuer. */
823 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
824 (info
->complete
) ? "" : "possibly ", (info
->valid
) ? "allowed" : "revoked",
826 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
828 /* Valid DB results caused us to do OCSP */
829 analytics
->valid_trigger_ocsp
= true;
834 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
835 /* Skip checking for OCSP Signer verification */
836 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
837 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
838 if (!pvc
) { return false; }
839 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
840 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
841 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
846 /* Make sure revocation db info is up-to-date.
847 * We don't care if the builder is allowed to access the network because
848 * the network fetching does not block the trust evaluation. */
849 SecRevocationDbCheckNextUpdate();
851 /* Check whether we have valid db info for this cert,
852 given the cert and its issuer */
853 SecValidInfoRef info
= NULL
;
854 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
856 bool isSelfSigned
= false;
857 SecCertificateRef cert
= NULL
;
858 SecCertificateRef issuer
= NULL
;
859 CFIndex issuerIX
= rvc
->certIX
+ 1;
860 if (count
> issuerIX
) {
861 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
862 } else if (count
== issuerIX
) {
863 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
864 if (rootIX
== rvc
->certIX
) {
865 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
869 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
871 /* skip revocation db check for self-signed certificates [33137065] */
872 info
= SecRevocationDbCopyMatching(cert
, issuer
);
874 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
877 SecValidInfoRef old_info
= rvc
->valid_info
;
878 rvc
->valid_info
= info
;
880 CFReleaseNull(old_info
);
887 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
888 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
889 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
890 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
891 SecOCSPResponseRef response
= NULL
;
892 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
893 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
894 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
896 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
898 SecORVCConsumeOCSPResponse(rvc
->orvc
, response
, NULL_TIME
, false, true);
899 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
900 if (rvc
->orvc
->done
&& analytics
) {
901 /* We found a valid OCSP response in the cache */
902 analytics
->ocsp_cache_hit
= true;
906 /* Don't check CRL cache if policy requested OCSP only */
907 if (SecRVCShouldCheckCRL(rvc
)) {
908 SecCRVCCheckRevocationCache(rvc
->crvc
);
913 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
914 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
915 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
917 if (rvc
->crvc
) { SecCRVCUpdatePVC(rvc
->crvc
); }
921 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
) {
923 SecRVCUpdatePVC(rvc
);
924 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
926 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
930 static bool SecRVCFetchNext(SecRVCRef rvc
) {
931 bool OCSP_fetch_finished
= true;
932 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
933 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
934 if (SecRVCShouldCheckOCSP(rvc
)) {
935 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
936 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
937 OCSP_fetch_finished
= SecORVCBeginFetches(rvc
->orvc
, cert
);
938 if (analytics
&& !OCSP_fetch_finished
) {
939 /* We did a network OCSP fetch, set report appropriately */
940 analytics
->ocsp_network
= true;
943 if (OCSP_fetch_finished
) {
944 /* we didn't start an OCSP background job for this cert */
945 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
949 bool CRL_fetch_finished
= true;
950 /* Don't check CRL cache if policy requested OCSP only */
951 if (SecRVCShouldCheckCRL(rvc
)) {
952 /* reset the distributionPointIX because we already iterated through the CRLDPs
953 * in SecCRVCCheckRevocationCache */
954 rvc
->crvc
->distributionPointIX
= 0;
955 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
957 if (CRL_fetch_finished
) {
958 /* we didn't start a CRL background job for this cert */
959 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
960 } else if (analytics
) {
961 /* We did a CRL fetch */
962 analytics
->crl_fetches
++;
964 OCSP_fetch_finished
&= CRL_fetch_finished
;
967 return OCSP_fetch_finished
;
970 /* The SecPathBuilder state machine calls SecPathBuilderCheckRevocation twice --
971 * once in the ValidatePath state, and again in the ComputeDetails state. In the
972 * ValidatePath state we've not yet run the path checks, so for callers who set
973 * kSecRevocationCheckIfTrusted, we don't do any networking on that first call.
974 * Here, if we've already done revocation before (so we're in ComputeDetails now),
975 * we need to recheck (and enable networking) for trusted chains and
976 * kSecRevocationCheckIfTrusted. Otherwise, we skip the checks to save on the processing
977 * but update the PVCs with the revocation results from the last check. */
978 static bool SecRevocationDidCheckRevocation(SecPathBuilderRef builder
, bool *first_check_done
) {
979 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
980 if (!SecCertificatePathVCIsRevocationDone(path
)) {
983 if (first_check_done
) {
984 *first_check_done
= true;
987 SecPVCRef resultPVC
= SecPathBuilderGetResultPVC(builder
);
988 bool recheck
= false;
989 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
) && SecPVCIsOkResult(resultPVC
)) {
991 secdebug("rvc", "Rechecking revocation because network now allowed");
993 secdebug("rvc", "Not rechecking revocation");
997 // reset the RVCs for the second pass
998 SecCertificatePathVCDeleteRVCs(path
);
1000 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
1001 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1002 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1004 SecRVCUpdatePVC(rvc
);
1012 static bool SecRevocationCanAccessNetwork(SecPathBuilderRef builder
, bool first_check_done
) {
1013 /* CheckRevocationIfTrusted overrides NoNetworkAccess for revocation */
1014 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
)) {
1015 if (first_check_done
) {
1016 /* We're on the second pass. We need to now allow networking for revocation.
1017 * SecRevocationDidCheckRevocation takes care of not running a second pass
1018 * if the chain isn't trusted. */
1021 /* We're on the first pass of the revocation checks, where we aren't
1022 * supposed to do networking because we don't know if the chain
1023 * is trusted yet. */
1027 return SecPathBuilderCanAccessNetwork(builder
);
1030 void SecPathBuilderCheckKnownIntermediateConstraints(SecPathBuilderRef builder
) {
1031 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
1035 /* only perform this check once per path! */
1036 CFIndex certIX
= kCFNotFound
;
1037 if (SecCertificatePathVCCheckedIssuers(path
)) {
1038 certIX
= SecCertificatePathVCUnknownCAIndex(path
);
1039 goto checkedIssuers
;
1041 /* check full path: start with anchor and decrement to leaf */
1042 bool parentConstrained
= false;
1043 CFIndex certCount
= SecPathBuilderGetCertificateCount(builder
);
1044 for (certIX
= certCount
- 1; certIX
>= 0; --certIX
) {
1045 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1049 if (parentConstrained
&& !rvc
->valid_info
) {
1050 /* Parent had the known-only constraint, but our issuer is unknown.
1051 Bump index to point back at the issuer since it fails the constraint. */
1055 parentConstrained
= (rvc
->valid_info
&& rvc
->valid_info
->knownOnly
);
1056 if (parentConstrained
) {
1057 secdebug("validupdate", "Valid db found a known-intermediate constraint on %@ (index=%ld)",
1058 rvc
->valid_info
->issuerHash
, certIX
+1);
1060 /* check special case: unknown constrained CA in leaf position */
1061 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
1062 if (cert
&& SecCertificateIsCA(cert
) && !SecRevocationDbContainsIssuer(cert
)) {
1063 /* leaf is a CA which violates the constraint */
1069 /* At this point, certIX will either be -1, indicating no CA was found
1070 which failed a known-intermediates-only constraint on its parent, or it
1071 will be the index of the first unknown CA which fails the constraint. */
1073 secnotice("validupdate", "CA at index %ld violates known-intermediate constraint", certIX
);
1074 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
1076 analytics
->valid_unknown_intermediate
= true;
1079 SecCertificatePathVCSetUnknownCAIndex(path
, certIX
);
1080 SecCertificatePathVCSetCheckedIssuers(path
, true);
1084 /* Error is set on CA certificate which failed the constraint. */
1085 SecRVCSetValidDeterminedErrorResult(SecCertificatePathVCGetRVCAtIndex(path
, certIX
));
1089 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
1090 secdebug("rvc", "checking revocation");
1091 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
1092 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
1093 if (certCount
<= 1) {
1094 /* Can't verify without an issuer; we're done */
1098 bool first_check_done
= false;
1099 if (SecRevocationDidCheckRevocation(builder
, &first_check_done
)) {
1103 /* Setup things so we check revocation status of all certs. */
1104 SecCertificatePathVCAllocateRVCs(path
, certCount
);
1106 /* Note that if we are multi threaded and a job completes after it
1107 is started but before we return from this function, we don't want
1108 a callback to decrement asyncJobCount to zero before we finish issuing
1109 all the jobs. To avoid this we pretend we issued certCount async jobs,
1110 and decrement pvc->asyncJobCount for each cert that we don't start a
1111 background fetch for. We include the root, even though we'll never start
1112 an async job for it so that we count all active threads for this eval. */
1114 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
));
1116 /* If we enable CRLS, we may end up with two async jobs per cert: one
1117 * for OCSP and one for fetching the CRL. Except the root, which only
1118 * needs to track this thread. */
1119 SecPathBuilderSetAsyncJobCount(builder
, 2 * (unsigned int)(certCount
) - 1);
1122 /* Loop though certificates again and issue an ocsp fetch if the
1123 revocation status checking isn't done yet (and we have an issuer!) */
1124 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1125 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
1126 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1131 SecRVCInit(rvc
, builder
, certIX
);
1133 /* RFC 6960: OCSP No-Check extension says that we shouldn't check revocation. */
1134 if (SecCertificateHasMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path
, certIX
),
1135 CFSTR("1.3.6.1.5.5.7.48.1.5"))) // id-pkix-ocsp-nocheck
1137 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX
);
1138 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
1140 /* This certificate has OCSP No-Check, so add to reporting analytics */
1141 analytics
->ocsp_no_check
= true;
1143 SecRVCSetFinishedWithoutNetwork(rvc
);
1150 #if !TARGET_OS_BRIDGE
1151 /* Check valid database first (separate from OCSP response cache) */
1152 if (SecRVCCheckValidInfoDatabase(rvc
)) {
1153 SecRVCProcessValidInfoResults(rvc
);
1156 /* Any other revocation method requires an issuer certificate to verify the response;
1157 * skip the last cert in the chain since it doesn't have one. */
1158 if (certIX
+ 1 >= certCount
) {
1162 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1163 * policy specifically requested CRLs only. */
1164 if (SecRVCShouldCheckOCSP(rvc
)) {
1165 /* If we have any OCSP stapled responses, check those first */
1166 SecORVCProcessStapledResponses(rvc
->orvc
);
1169 #if TARGET_OS_BRIDGE
1170 /* The bridge has no writeable storage and no network. Nothing else we can
1172 SecRVCSetFinishedWithoutNetwork(rvc
);
1176 /* Then check the caches for revocation results. */
1177 SecRVCCheckRevocationCaches(rvc
);
1179 /* The check is done if we found cached responses from either method. */
1180 if (rvc
->done
|| rvc
->orvc
->done
1185 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1186 SecRVCSetFinishedWithoutNetwork(rvc
);
1190 /* If we got a cached response that is no longer valid (which can only be true for
1191 * revoked responses), let's try to get a fresher response even if no one asked.
1192 * This check resolves unrevocation events after the nextUpdate time. */
1193 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1195 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1196 async http request for this cert's revocation status, unless we already successfully checked
1197 the revocation status of this cert based on the cache or stapled responses. */
1198 bool allow_fetch
= SecRevocationCanAccessNetwork(builder
, first_check_done
) &&
1199 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1200 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1201 if (rvc
->done
|| !allow_fetch
) {
1202 /* We got a cache hit or we aren't allowed to access the network */
1203 SecRVCUpdatePVC(rvc
);
1204 /* We didn't really start any background jobs for this cert. */
1205 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1207 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1210 (void)SecRVCFetchNext(rvc
);
1214 /* Return false if there are still async jobs running. */
1215 /* builder->asyncJobCount is atomic, so we know that if the job count is 0, all other
1216 * threads are finished. If the job count is > 0, other threads will decrement the job
1217 * count and SecPathBuilderStep to crank the state machine when the job count is 0. */
1218 return (SecPathBuilderDecrementAsyncJobCount(builder
) == 0);
1221 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1222 CFAbsoluteTime enu
= NULL_TIME
;
1223 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1224 enu
= rvc
->orvc
->nextUpdate
;
1226 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
1227 if (enu
== NULL_TIME
||
1228 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
1229 /* We didn't check OCSP or CRL next update time was sooner */
1230 enu
= crlNextUpdate
;