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/asynchttp.h>
46 #include <securityd/SecOCSPCache.h>
47 #include <securityd/SecRevocationDb.h>
48 #include <securityd/SecCertificateServer.h>
50 #include <securityd/SecRevocationServer.h>
53 /********************************************************
54 ****************** OCSP RVC Functions ******************
55 ********************************************************/
56 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
57 const CFAbsoluteTime kSecOCSPResponseOnlineTTL
= 5.0 * 60.0;
58 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
60 /* OCSP Revocation verification context. */
61 struct OpaqueSecORVC
{
62 /* Will contain the response data. */
65 /* Pointer to the builder for this revocation check. */
66 SecPathBuilderRef builder
;
68 /* Pointer to the generic rvc for this revocation check */
71 /* The ocsp request we send to each responder. */
72 SecOCSPRequestRef ocspRequest
;
74 /* The freshest response we received so far, from stapling or cache or responder. */
75 SecOCSPResponseRef ocspResponse
;
77 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
78 SecOCSPSingleResponseRef ocspSingleResponse
;
80 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
83 /* Index in array returned by SecCertificateGetOCSPResponders() for current
87 /* URL of current responder. */
90 /* Date until which this revocation status is valid. */
91 CFAbsoluteTime nextUpdate
;
96 static void SecORVCFinish(SecORVCRef orvc
) {
97 secdebug("alloc", "%p", orvc
);
98 asynchttp_free(&orvc
->http
);
99 if (orvc
->ocspRequest
) {
100 SecOCSPRequestFinalize(orvc
->ocspRequest
);
101 orvc
->ocspRequest
= NULL
;
103 if (orvc
->ocspResponse
) {
104 SecOCSPResponseFinalize(orvc
->ocspResponse
);
105 orvc
->ocspResponse
= NULL
;
106 if (orvc
->ocspSingleResponse
) {
107 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
108 orvc
->ocspSingleResponse
= NULL
;
113 #define MAX_OCSP_RESPONDERS 3
114 #define OCSP_REQUEST_THRESHOLD 10
116 /* Return the next responder we should contact for this rvc or NULL if we
117 exhausted them all. */
118 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
119 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
120 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
121 if (ocspResponders
) {
122 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
123 if (responderCount
>= OCSP_REQUEST_THRESHOLD
) {
124 secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount
);
127 while (rvc
->responderIX
< responderCount
&& rvc
->responderIX
< MAX_OCSP_RESPONDERS
) {
128 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
130 CFStringRef scheme
= CFURLCopyScheme(responder
);
132 /* We only support http and https responders currently. */
133 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
134 CFEqual(CFSTR("https"), scheme
));
144 /* Fire off an async http request for this certs revocation status, return
145 false if request was queued, true if we're done. */
146 static bool SecORVCFetchNext(SecORVCRef rvc
) {
147 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
148 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
152 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
153 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
154 /* Async request was posted, wait for reply. */
164 /* Process a verified ocsp response for a given cert. Return true if the
165 certificate status was obtained. */
166 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
169 switch (this->certStatus
) {
171 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
172 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
173 in the info dictionary. */
174 //cert.revokeCheckGood(true);
175 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
179 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
180 /* @@@ Mark cert as revoked (with reason) at revocation date in
181 the info dictionary, or perhaps we should use a different key per
182 reason? That way a client using exceptions can ignore some but
184 SInt32 reason
= this->crlReason
;
185 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
186 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
189 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
191 /* make the revocation reason available in the trust result */
192 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
199 /* not an error, no per-cert status, nothing here */
200 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
204 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
205 (int)this->certStatus
, rvc
->certIX
);
213 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
214 if (rvc
->ocspSingleResponse
) {
215 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
217 if (rvc
->ocspResponse
) {
218 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
222 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
225 SecOCSPEvaluateCompleted(const void *userData
,
226 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
227 SecTrustResultType result
) {
228 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
230 Block_release(evaluated
);
234 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
235 __block
bool evaluated
= false;
236 bool trusted
= false;
237 if (!signers
|| !issuers
) {
241 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
242 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
243 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
244 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
245 CFRelease(ocspSigner
);
247 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
248 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
253 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
254 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
255 signers
, issuers
, true, false,
256 policies
, NULL
, NULL
, NULL
,
257 verifyTime
, NULL
, NULL
,
258 SecOCSPEvaluateCompleted
, completed
);
259 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
260 SecPathBuilderStep(oBuilder
);
261 CFReleaseNull(clientAuditToken
);
262 CFReleaseNull(policies
);
264 /* verify the public key of the issuer signed the OCSP signer */
266 SecCertificateRef issuer
= NULL
, signer
= NULL
;
267 SecKeyRef issuerPubKey
= NULL
;
269 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
270 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
274 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
276 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
279 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
282 secnotice("ocsp", "ocsp signer cert not signed by issuer");
284 CFReleaseNull(issuerPubKey
);
290 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
292 SecCertificatePathVCRef issuers
= SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc
->builder
), rvc
->certIX
+ 1);
293 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers
, 0)) : NULL
;
294 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
295 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
297 if (signer
&& signers
) {
298 if (issuer
&& CFEqual(signer
, issuer
)) {
299 /* We already know we trust issuer since it's the issuer of the
300 * cert we are verifying. */
301 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
305 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
307 CFMutableArrayRef signerCerts
= NULL
;
308 CFArrayRef issuerCerts
= NULL
;
310 /* Ensure the signer cert is the 0th cert for trust evaluation */
311 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
312 CFArrayAppendValue(signerCerts
, signer
);
313 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
316 issuerCerts
= SecCertificatePathVCCopyCertificates(issuers
);
319 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
320 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
324 /* @@@ We don't trust the cert so don't use this response. */
325 secnotice("ocsp", "ocsp response signed by certificate which "
326 "does not satisfy ocspSigner policy");
329 CFReleaseNull(signerCerts
);
330 CFReleaseNull(issuerCerts
);
333 /* @@@ No signer found for this ocsp response, discard it. */
334 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
339 #if DUMP_OCSPRESPONSES
341 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
342 rvc
->certIX
, (trusted
? "t" : "u"));
343 secdumpdata(ocspResponse
->data
, buf
);
345 CFReleaseNull(issuers
);
346 CFReleaseNull(issuer
);
347 CFReleaseNull(signers
);
348 CFReleaseNull(signer
);
352 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
353 SecOCSPSingleResponseRef sr
= NULL
;
354 require_quiet(ocspResponse
, errOut
);
355 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
356 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
357 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
358 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
359 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
360 // Check if this response is fresher than any (cached) response we might still have in the rvc.
361 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
363 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
364 /* Check the OCSP response signature and verify the response. */
365 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
366 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
368 // If we get here, we have a properly signed ocsp response
369 // but we haven't checked dates yet.
371 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
372 if (sr
->certStatus
== CS_Good
) {
373 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
374 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
375 } else if (sr
->certStatus
== CS_Revoked
) {
376 // Expire revoked responses when the subject certificate itself expires.
377 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
));
380 // Ok we like the new response, let's toss the old one.
382 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
384 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
385 rvc
->ocspResponse
= ocspResponse
;
388 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
389 rvc
->ocspSingleResponse
= sr
;
392 rvc
->done
= sr_valid
;
395 if (sr
) SecOCSPSingleResponseDestroy(sr
);
396 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
399 /* Callback from async http code after an ocsp response has been received. */
400 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
401 SecORVCRef rvc
= (SecORVCRef
)http
->info
;
402 SecPathBuilderRef builder
= rvc
->builder
;
403 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
405 /* Add the time this fetch took to complete to the total time */
406 analytics
->ocsp_fetch_time
+= (mach_absolute_time() - http
->start_time
);
408 SecOCSPResponseRef ocspResponse
= NULL
;
409 if (http
->response
) {
410 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
412 /* Parse the returned data as if it's an ocspResponse. */
413 ocspResponse
= SecOCSPResponseCreate(data
);
418 if ((!http
->response
|| !ocspResponse
) && analytics
) {
419 /* We didn't get any data back, so the fetch failed */
420 analytics
->ocsp_fetch_failed
++;
423 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
424 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
427 if (analytics
&& ocspResponse
) {
428 /* We got an OCSP response that didn't pass validation */
429 analytics
-> ocsp_validation_failed
= true;
431 /* Clear the data for the next response. */
432 asynchttp_free(http
);
433 SecORVCFetchNext(rvc
);
437 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
438 SecORVCUpdatePVC(rvc
);
439 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
440 secdebug("rvc", "done with all async jobs");
441 SecPathBuilderStep(builder
);
446 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
447 SecORVCRef orvc
= NULL
;
448 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
450 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
451 orvc
->builder
= builder
;
453 orvc
->certIX
= certIX
;
454 orvc
->http
.queue
= SecPathBuilderGetQueue(builder
);
455 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(builder
);
456 orvc
->http
.completed
= SecOCSPFetchCompleted
;
457 orvc
->http
.info
= orvc
;
458 orvc
->ocspRequest
= NULL
;
459 orvc
->responderIX
= 0;
460 orvc
->responder
= NULL
;
461 orvc
->nextUpdate
= NULL_TIME
;
462 orvc
->ocspResponse
= NULL
;
463 orvc
->ocspSingleResponse
= NULL
;
466 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
467 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
468 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
469 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
475 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
476 /* Get stapled OCSP responses */
477 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
479 if(ocspResponsesData
) {
480 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
481 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
482 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
483 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
485 CFRelease(ocspResponsesData
);
490 /********************************************************
491 ******************* CRL RVC Functions ******************
492 ********************************************************/
494 #include <../trustd/macOS/SecTrustOSXEntryPoints.h>
495 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
497 /* CRL Revocation verification context. */
498 struct OpaqueSecCRVC
{
499 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
500 async_ocspd_t async_ocspd
;
502 /* Pointer to the builder for this revocation check. */
503 SecPathBuilderRef builder
;
505 /* Pointer to the generic rvc for this revocation check */
508 /* The current CRL status from ocspd. */
511 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
514 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
515 current distribution point. */
516 CFIndex distributionPointIX
;
518 /* URL of current distribution point. */
519 CFURLRef distributionPoint
;
521 /* Date until which this revocation status is valid. */
522 CFAbsoluteTime nextUpdate
;
527 static void SecCRVCFinish(SecCRVCRef crvc
) {
531 #define MAX_CRL_DPS 3
532 #define CRL_REQUEST_THRESHOLD 10
534 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
535 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
536 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
538 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
539 if (crlDPCount
>= CRL_REQUEST_THRESHOLD
) {
540 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount
);
543 while (rvc
->distributionPointIX
< crlDPCount
&& rvc
->distributionPointIX
< MAX_CRL_DPS
) {
544 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
545 rvc
->distributionPointIX
++;
546 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
548 /* We only support http and https responders currently. */
549 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
550 CFEqual(CFSTR("https"), scheme
) ||
551 CFEqual(CFSTR("ldap"), scheme
));
554 return distributionPoint
;
561 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
562 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
563 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
564 CFArrayRef serializedCertPath
= SecCertificatePathVCCreateSerialized(path
);
565 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
566 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
567 CFReleaseNull(serializedCertPath
);
568 /* we got a response indicating that the CRL was checked */
569 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
571 /* ocspd doesn't give us the nextUpdate time, so set to default */
572 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
576 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
577 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
578 SecCRVCGetCRLStatus(rvc
);
579 if (rvc
->status
== errSecCertificateRevoked
) {
585 /* Fire off an async http request for this certs revocation status, return
586 false if request was queued, true if we're done. */
587 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
588 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
589 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
590 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
591 CFArrayRef serializedCertPath
= SecCertificatePathVCCreateSerialized(path
);
592 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
593 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
594 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
595 CFDataRef clientAuditToken
= NULL
;
596 SecTaskRef task
= NULL
;
597 audit_token_t auditToken
= {};
598 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
599 require(clientAuditToken
, out
);
600 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
601 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
602 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
603 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
604 rvc
->distributionPoint
, task
);
607 CFReleaseNull(clientAuditToken
);
609 /* Async request was posted, wait for reply. */
617 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
618 if (rvc
->status
== errSecCertificateRevoked
) {
619 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
620 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
621 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
622 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
625 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
627 /* make the revocation reason available in the trust result */
628 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
631 CFReleaseNull(cfreason
);
635 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
636 SecCRVCRef rvc
= ocspd
->info
;
637 SecPathBuilderRef builder
= rvc
->builder
;
638 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
640 /* Add the time this fetch took to complete to the total time */
641 analytics
->crl_fetch_time
+= (mach_absolute_time() - ocspd
->start_time
);
643 /* we got a response indicating that the CRL was checked */
644 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
645 rvc
->status
= ocspd
->response
;
647 /* ocspd doesn't give us the nextUpdate time, so set to default */
648 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
649 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
650 SecCRVCUpdatePVC(rvc
);
651 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
652 secdebug("rvc", "done with all async jobs");
653 SecPathBuilderStep(builder
);
657 /* We didn't get any data back, so the fetch failed */
658 analytics
->crl_fetch_failed
++;
660 if(SecCRVCFetchNext(rvc
)) {
661 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
662 secdebug("rvc", "done with all async jobs");
663 SecPathBuilderStep(builder
);
669 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
670 SecCRVCRef crvc
= NULL
;
671 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
673 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
674 crvc
->builder
= builder
;
676 crvc
->certIX
= certIX
;
677 crvc
->status
= errSecInternal
;
678 crvc
->distributionPointIX
= 0;
679 crvc
->distributionPoint
= NULL
;
680 crvc
->nextUpdate
= NULL_TIME
;
681 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(builder
);
682 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
683 crvc
->async_ocspd
.response
= errSecInternal
;
684 crvc
->async_ocspd
.info
= crvc
;
690 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
691 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
692 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
693 if (revocation_method
&&
694 CFEqual(kSecPolicyCheckRevocationCRL
, revocation_method
)) {
695 /* Our client insists on CRLs */
696 secinfo("rvc", "client told us to check CRL");
698 analytics
->crl_client
= true;
702 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
703 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
704 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
705 (revocation_method
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, revocation_method
))) {
706 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
707 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
708 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
710 analytics
->crl_cert
= true;
716 #endif /* ENABLE_CRLS */
718 void SecRVCDelete(SecRVCRef rvc
) {
720 SecORVCFinish(rvc
->orvc
);
726 SecCRVCFinish(rvc
->crvc
);
731 if (rvc
->valid_info
) {
732 SecValidInfoRelease(rvc
->valid_info
);
733 rvc
->valid_info
= NULL
;
737 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
738 secdebug("alloc", "%p", rvc
);
739 rvc
->builder
= builder
;
740 rvc
->certIX
= certIX
;
741 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
743 rvc
->crvc
= SecCRVCCreate(rvc
, builder
, certIX
);
758 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
759 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
760 if (!revocation_method
761 || !CFEqual(revocation_method
, kSecPolicyCheckRevocationCRL
)) {
767 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
772 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc
) {
773 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
776 if (!rvc
->valid_info
->hasDateConstraints
) {
779 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
783 CFAbsoluteTime certIssued
= SecCertificateNotValidBefore(certificate
);
784 CFAbsoluteTime caNotBefore
= -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
785 CFAbsoluteTime caNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
786 if (rvc
->valid_info
->notBeforeDate
) {
787 caNotBefore
= CFDateGetAbsoluteTime(rvc
->valid_info
->notBeforeDate
);
789 if (rvc
->valid_info
->notAfterDate
) {
790 caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
791 /* per the Valid specification, if this date is in the past, we need to check CT. */
792 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
793 if (caNotAfter
< now
) {
794 rvc
->valid_info
->requireCT
= true;
797 if ((certIssued
< caNotBefore
) && (rvc
->certIX
> 0)) {
798 /* not-before constraint is only applied to leaf certificate, for now. */
802 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
803 if ((certIssued
< caNotBefore
) || (certIssued
> caNotAfter
)) {
804 /* We are outside the constrained validity period. */
805 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
806 (rvc
->valid_info
->overridable
) ? "" : " (non-recoverable error)");
808 analytics
->valid_status
|= TAValidDateContrainedRevoked
;
810 if (rvc
->valid_info
->overridable
) {
811 /* error is recoverable, treat certificate as untrusted
812 (note this date check is different from kSecPolicyCheckTemporalValidity) */
813 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
814 kCFBooleanFalse
, true);
816 /* error is non-overridable, treat certificate as revoked */
817 SInt32 reason
= 0; /* unspecified reason code */
818 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
819 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
821 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
823 /* make the revocation reason available in the trust result */
824 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
826 CFReleaseNull(cfreason
);
828 } else if (analytics
) {
829 analytics
->valid_status
|= TAValidDateConstrainedOK
;
833 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc
) {
834 if (!rvc
|| !rvc
->valid_info
) {
837 SecValidInfoRef info
= rvc
->valid_info
;
838 /* outcomes as defined in Valid server specification */
839 if (info
->format
== kSecValidInfoFormatSerial
||
840 info
->format
== kSecValidInfoFormatSHA256
) {
841 if (info
->noCACheck
|| info
->complete
|| info
->isOnList
) {
844 } else { /* info->format == kSecValidInfoFormatNto1 */
845 if (info
->noCACheck
|| (info
->complete
&& !info
->isOnList
)) {
852 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc
) {
853 if (!rvc
|| !rvc
->valid_info
) {
856 SecValidInfoRef info
= rvc
->valid_info
;
857 /* either not present on an allowlist, or present on a blocklist */
858 return (!info
->isOnList
&& info
->valid
) || (info
->isOnList
&& !info
->valid
);
861 void SecRVCSetRevokedResult(SecRVCRef rvc
) {
862 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
865 if (rvc
->valid_info
->overridable
) {
866 /* error is recoverable, treat certificate as untrusted */
867 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
868 kCFBooleanFalse
, true);
871 /* error is fatal, treat certificate as revoked */
872 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
873 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
874 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
876 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
878 /* make the revocation reason available in the trust result */
879 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
881 CFReleaseNull(cfreason
);
884 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
885 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
888 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
889 SecValidInfoRef info
= rvc
->valid_info
;
891 bool definitive
= SecRVCHasDefinitiveValidInfo(rvc
);
892 bool revoked
= SecRVCHasRevokedValidInfo(rvc
);
895 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
898 analytics
->valid_status
|= definitive
? TAValidDefinitelyRevoked
: TAValidProbablyRevoked
;
900 analytics
->valid_status
|= definitive
? TAValidDefinitelyOK
: TAValidProbablyOK
;
904 /* Handle no-ca cases */
905 if (info
->noCACheck
) {
906 bool allowed
= (info
->valid
&& info
->complete
&& info
->isOnList
);
908 /* definitely revoked */
909 SecRVCSetRevokedResult(rvc
);
910 } else if (allowed
) {
911 /* definitely not revoked (allowlisted) */
912 SecCertificatePathVCSetIsAllowlisted(path
, true);
914 /* no-ca is definitive; no need to check further. */
915 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
916 (allowed
) ? "allowed" : "revoked", rvc
->certIX
);
921 /* Handle date constraints, if present.
922 * Note: a not-after date may set the CT requirement,
923 * so check requireCT after this function is called. */
924 SecRVCProcessValidDateConstraints(rvc
);
926 /* Set CT requirement on path, if present. */
927 if (info
->requireCT
) {
929 analytics
->valid_require_ct
|= info
->requireCT
;
931 SecPathCTPolicy ctp
= kSecPathCTRequired
;
932 if (info
->overridable
) {
933 ctp
= kSecPathCTRequiredOverridable
;
935 SecCertificatePathVCSetRequiresCT(path
, ctp
);
938 /* Trigger OCSP for any non-definitive or revoked cases */
939 if (!definitive
|| revoked
) {
940 info
->checkOCSP
= true;
943 if (info
->checkOCSP
) {
945 /* Valid DB results caused us to do OCSP */
946 analytics
->valid_trigger_ocsp
= true;
948 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
949 CFIndex issuerIX
= rvc
->certIX
+ 1;
950 if (issuerIX
>= count
) {
951 /* cannot perform a revocation check on the last cert in the
952 chain, since we don't have its issuer. */
956 for (pvcIX
= 0; pvcIX
< SecPathBuilderGetPVCCount(rvc
->builder
); pvcIX
++) {
957 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, pvcIX
);
958 if (!pvc
) { continue; }
959 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
960 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
961 if (policyName
&& CFEqual(CFSTR("sslServer"), policyName
)) {
962 /* perform revocation check for SSL policy;
963 require for leaf if an OCSP responder is present. */
964 if (0 == rvc
->certIX
) {
965 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
966 CFArrayRef resps
= (cert
) ? SecCertificateGetOCSPResponders(cert
) : NULL
;
967 CFIndex rcount
= (resps
) ? CFArrayGetCount(resps
) : 0;
969 // %%% rdar://31279923
970 // This currently requires a valid revocation response for each cert,
971 // but we only want to require a leaf check. For now, do not require.
972 //SecPathBuilderSetRevocationResponseRequired(rvc->builder);
975 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
976 (info
->complete
) ? "" : "possibly ", (info
->valid
) ? "allowed" : "revoked",
978 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
984 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
985 /* Skip checking for OCSP Signer verification */
986 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
987 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
988 if (!pvc
) { return false; }
989 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
990 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
991 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
996 /* Make sure revocation db info is up-to-date.
997 * We don't care if the builder is allowed to access the network because
998 * the network fetching does not block the trust evaluation. */
999 SecRevocationDbCheckNextUpdate();
1001 /* Check whether we have valid db info for this cert,
1002 given the cert and its issuer */
1003 SecValidInfoRef info
= NULL
;
1004 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
1006 bool isSelfSigned
= false;
1007 SecCertificateRef cert
= NULL
;
1008 SecCertificateRef issuer
= NULL
;
1009 CFIndex issuerIX
= rvc
->certIX
+ 1;
1010 if (count
> issuerIX
) {
1011 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
1012 } else if (count
== issuerIX
) {
1013 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
1014 if (rootIX
== rvc
->certIX
) {
1015 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
1016 isSelfSigned
= true;
1019 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
1020 if (!isSelfSigned
) {
1021 /* skip revocation db check for self-signed certificates [33137065] */
1022 info
= SecRevocationDbCopyMatching(cert
, issuer
);
1024 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
1027 SecValidInfoRef old_info
= rvc
->valid_info
;
1028 rvc
->valid_info
= info
;
1030 SecValidInfoRelease(old_info
);
1037 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
1038 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
1039 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
1040 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
1041 SecOCSPResponseRef response
= NULL
;
1042 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
1043 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
1044 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
1046 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
1048 SecORVCConsumeOCSPResponse(rvc
->orvc
,
1051 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
1052 if (rvc
->orvc
->done
&& analytics
) {
1053 /* We found a valid OCSP response in the cache */
1054 analytics
->ocsp_cache_hit
= true;
1058 /* Don't check CRL cache if policy requested OCSP only */
1059 if (SecRVCShouldCheckCRL(rvc
)) {
1060 SecCRVCCheckRevocationCache(rvc
->crvc
);
1065 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
1066 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
1067 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
1069 if (rvc
->crvc
) { SecCRVCUpdatePVC(rvc
->crvc
); }
1073 static bool SecRVCFetchNext(SecRVCRef rvc
) {
1074 bool OCSP_fetch_finished
= true;
1075 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
1076 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
1077 if (SecRVCShouldCheckOCSP(rvc
)) {
1078 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
1080 if (OCSP_fetch_finished
) {
1081 /* we didn't start an OCSP background job for this cert */
1082 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
1083 } else if (analytics
) {
1084 /* We did a network OCSP fetch, set report appropriately */
1085 analytics
->ocsp_network
= true;
1086 analytics
->ocsp_fetches
++;
1090 bool CRL_fetch_finished
= true;
1091 /* Don't check CRL cache if policy requested OCSP only */
1092 if (SecRVCShouldCheckCRL(rvc
)) {
1093 /* reset the distributionPointIX because we already iterated through the CRLDPs
1094 * in SecCRVCCheckRevocationCache */
1095 rvc
->crvc
->distributionPointIX
= 0;
1096 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
1098 if (CRL_fetch_finished
) {
1099 /* we didn't start a CRL background job for this cert */
1100 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
1101 } else if (analytics
) {
1102 /* We did a CRL fetch */
1103 analytics
->crl_fetches
++;
1105 OCSP_fetch_finished
&= CRL_fetch_finished
;
1108 return OCSP_fetch_finished
;
1111 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
1112 secdebug("rvc", "checking revocation");
1113 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
1114 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
1115 bool completed
= true;
1116 if (certCount
<= 1) {
1117 /* Can't verify without an issuer; we're done */
1122 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
1124 * Note: we can't check revocation for the last certificate in the chain
1125 * via OCSP or CRL methods, since there isn't a separate issuer cert to
1126 * sign those responses. However, since a self-signed root has an implied
1127 * issuer of itself, we can check for it in the valid database.
1130 if (SecCertificatePathVCIsRevocationDone(path
)) {
1131 /* We have done revocation checking already, set PVCs with results. */
1132 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1133 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1134 if (rvc
) { SecRVCUpdatePVC(rvc
); }
1136 secdebug("rvc", "Not rechecking revocation");
1140 /* Setup things so we check revocation status of all certs. */
1141 SecCertificatePathVCAllocateRVCs(path
, certCount
);
1143 /* Note that if we are multi threaded and a job completes after it
1144 is started but before we return from this function, we don't want
1145 a callback to decrement asyncJobCount to zero before we finish issuing
1146 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
1147 and decrement pvc->asyncJobCount for each cert that we don't start a
1148 background fetch for. (We will never start an async job for the final
1149 cert in the chain.) */
1151 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
-1));
1153 /* If we enable CRLS, we may end up with two async jobs per cert: one
1154 * for OCSP and one for fetching the CRL */
1155 SecPathBuilderSetAsyncJobCount(builder
, 2 * (unsigned int)(certCount
-1));
1158 /* Loop though certificates again and issue an ocsp fetch if the
1159 revocation status checking isn't done yet (and we have an issuer!) */
1160 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1161 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
1162 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1167 SecRVCInit(rvc
, builder
, certIX
);
1169 /* RFC 6960: OCSP No-Check extension says that we shouldn't check revocation. */
1170 if (SecCertificateHasMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path
, certIX
),
1171 CFSTR("1.3.6.1.5.5.7.48.1.5"))) // id-pkix-ocsp-nocheck
1173 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX
);
1174 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
1176 /* This certificate has OCSP No-Check, so add to reporting analytics */
1177 analytics
->ocsp_no_check
= true;
1186 #if !TARGET_OS_BRIDGE
1187 /* Check valid database first (separate from OCSP response cache) */
1188 if (SecRVCCheckValidInfoDatabase(rvc
)) {
1189 SecRVCProcessValidInfoResults(rvc
);
1192 /* Any other revocation method requires an issuer certificate;
1193 * skip the last cert in the chain since it doesn't have one. */
1194 if (certIX
+1 >= certCount
) {
1198 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1199 * policy specifically requested CRLs only. */
1200 if (SecRVCShouldCheckOCSP(rvc
)) {
1201 /* If we have any OCSP stapled responses, check those first */
1202 SecORVCProcessStapledResponses(rvc
->orvc
);
1205 #if TARGET_OS_BRIDGE
1206 /* The bridge has no writeable storage and no network. Nothing else we can
1212 /* Then check the caches for revocation results. */
1213 SecRVCCheckRevocationCaches(rvc
);
1215 /* The check is done if we found cached responses from either method. */
1221 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1225 /* If we got a cached response that is no longer valid (which can only be true for
1226 * revoked responses), let's try to get a fresher response even if no one asked.
1227 * This check resolves unrevocation events after the nextUpdate time. */
1228 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1230 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1231 async http request for this cert's revocation status, unless we already successfully checked
1232 the revocation status of this cert based on the cache or stapled responses. */
1233 bool allow_fetch
= SecPathBuilderCanAccessNetwork(builder
) &&
1234 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1235 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1236 bool fetch_done
= true;
1237 if (rvc
->done
|| !allow_fetch
) {
1238 /* We got a cache hit or we aren't allowed to access the network */
1239 SecRVCUpdatePVC(rvc
);
1240 /* We didn't really start any background jobs for this cert. */
1241 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1243 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1246 fetch_done
= SecRVCFetchNext(rvc
);
1249 /* We started at least one background fetch. */
1250 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
1255 /* Return false if we started any background jobs. */
1256 /* We can't just return !builder->asyncJobCount here, since if we started any
1257 jobs the completion callback will be called eventually and it will call
1258 SecPathBuilderStep(). If for some reason everything completed before we
1259 get here we still want the outer SecPathBuilderStep() to terminate so we
1260 keep track of whether we started any jobs and return false if so. */
1264 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1265 CFAbsoluteTime enu
= NULL_TIME
;
1266 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1267 enu
= rvc
->orvc
->nextUpdate
;
1269 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
1270 if (enu
== NULL_TIME
||
1271 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
1272 /* We didn't check OCSP or CRL next update time was sooner */
1273 enu
= crlNextUpdate
;