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>
49 #include <securityd/SecPolicyServer.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 /* OCSP Revocation verification context. */
62 struct OpaqueSecORVC
{
63 /* Will contain the response data. */
66 /* Pointer to the builder for this revocation check. */
67 SecPathBuilderRef builder
;
69 /* Pointer to the generic rvc for this revocation check */
72 /* The ocsp request we send to each responder. */
73 SecOCSPRequestRef ocspRequest
;
75 /* The freshest response we received so far, from stapling or cache or responder. */
76 SecOCSPResponseRef ocspResponse
;
78 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
79 SecOCSPSingleResponseRef ocspSingleResponse
;
81 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
84 /* Index in array returned by SecCertificateGetOCSPResponders() for current
88 /* URL of current responder. */
91 /* Date until which this revocation status is valid. */
92 CFAbsoluteTime nextUpdate
;
97 static void SecORVCFinish(SecORVCRef orvc
) {
98 secdebug("alloc", "%p", orvc
);
99 asynchttp_free(&orvc
->http
);
100 if (orvc
->ocspRequest
) {
101 SecOCSPRequestFinalize(orvc
->ocspRequest
);
102 orvc
->ocspRequest
= NULL
;
104 if (orvc
->ocspResponse
) {
105 SecOCSPResponseFinalize(orvc
->ocspResponse
);
106 orvc
->ocspResponse
= NULL
;
107 if (orvc
->ocspSingleResponse
) {
108 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
109 orvc
->ocspSingleResponse
= NULL
;
114 #define MAX_OCSP_RESPONDERS 3
115 #define OCSP_REQUEST_THRESHOLD 10
117 /* Return the next responder we should contact for this rvc or NULL if we
118 exhausted them all. */
119 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
120 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
121 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
122 if (ocspResponders
) {
123 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
124 if (responderCount
>= OCSP_REQUEST_THRESHOLD
) {
125 secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount
);
128 while (rvc
->responderIX
< responderCount
&& rvc
->responderIX
< MAX_OCSP_RESPONDERS
) {
129 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
131 CFStringRef scheme
= CFURLCopyScheme(responder
);
133 /* We only support http and https responders currently. */
134 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
135 CFEqual(CFSTR("https"), scheme
));
145 /* Fire off an async http request for this certs revocation status, return
146 false if request was queued, true if we're done. */
147 static bool SecORVCFetchNext(SecORVCRef rvc
) {
148 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
149 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
153 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
154 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
155 /* Async request was posted, wait for reply. */
165 /* Process a verified ocsp response for a given cert. Return true if the
166 certificate status was obtained. */
167 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
170 switch (this->certStatus
) {
172 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
173 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
174 in the info dictionary. */
175 //cert.revokeCheckGood(true);
176 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
180 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
181 /* @@@ Mark cert as revoked (with reason) at revocation date in
182 the info dictionary, or perhaps we should use a different key per
183 reason? That way a client using exceptions can ignore some but
185 SInt32 reason
= this->crlReason
;
186 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
187 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
190 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
192 /* make the revocation reason available in the trust result */
193 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
200 /* not an error, no per-cert status, nothing here */
201 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
205 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
206 (int)this->certStatus
, rvc
->certIX
);
214 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
215 if (rvc
->ocspSingleResponse
) {
216 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
218 if (rvc
->ocspResponse
) {
219 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
223 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
226 SecOCSPEvaluateCompleted(const void *userData
,
227 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
228 SecTrustResultType result
) {
229 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
231 Block_release(evaluated
);
235 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
236 __block
bool evaluated
= false;
237 bool trusted
= false;
238 if (!signers
|| !issuers
) {
242 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
243 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
244 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
245 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
246 CFRelease(ocspSigner
);
248 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
249 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
254 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
255 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
256 signers
, issuers
, true, false,
257 policies
, NULL
, NULL
, NULL
,
258 verifyTime
, NULL
, NULL
,
259 SecOCSPEvaluateCompleted
, completed
);
260 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
261 SecPathBuilderStep(oBuilder
);
262 CFReleaseNull(clientAuditToken
);
263 CFReleaseNull(policies
);
265 /* verify the public key of the issuer signed the OCSP signer */
267 SecCertificateRef issuer
= NULL
, signer
= NULL
;
268 SecKeyRef issuerPubKey
= NULL
;
270 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
271 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
275 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
277 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
280 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
283 secnotice("ocsp", "ocsp signer cert not signed by issuer");
285 CFReleaseNull(issuerPubKey
);
291 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
293 SecCertificatePathVCRef issuers
= SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc
->builder
), rvc
->certIX
+ 1);
294 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers
, 0)) : NULL
;
295 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
296 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
298 if (signer
&& signers
) {
299 if (issuer
&& CFEqual(signer
, issuer
)) {
300 /* We already know we trust issuer since it's the issuer of the
301 * cert we are verifying. */
302 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
306 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
308 CFMutableArrayRef signerCerts
= NULL
;
309 CFArrayRef issuerCerts
= NULL
;
311 /* Ensure the signer cert is the 0th cert for trust evaluation */
312 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
313 CFArrayAppendValue(signerCerts
, signer
);
314 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
317 issuerCerts
= SecCertificatePathVCCopyCertificates(issuers
);
320 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
321 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
325 /* @@@ We don't trust the cert so don't use this response. */
326 secnotice("ocsp", "ocsp response signed by certificate which "
327 "does not satisfy ocspSigner policy");
330 CFReleaseNull(signerCerts
);
331 CFReleaseNull(issuerCerts
);
334 /* @@@ No signer found for this ocsp response, discard it. */
335 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
340 #if DUMP_OCSPRESPONSES
342 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
343 rvc
->certIX
, (trusted
? "t" : "u"));
344 secdumpdata(ocspResponse
->data
, buf
);
346 CFReleaseNull(issuers
);
347 CFReleaseNull(issuer
);
348 CFReleaseNull(signers
);
349 CFReleaseNull(signer
);
353 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
354 SecOCSPSingleResponseRef sr
= NULL
;
355 require_quiet(ocspResponse
, errOut
);
356 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
357 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
358 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
359 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
360 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
361 // Check if this response is fresher than any (cached) response we might still have in the rvc.
362 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
364 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
365 /* Check the OCSP response signature and verify the response. */
366 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
367 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
369 // If we get here, we have a properly signed ocsp response
370 // but we haven't checked dates yet.
372 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
373 if (sr
->certStatus
== CS_Good
) {
374 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
375 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
376 } else if (sr
->certStatus
== CS_Revoked
) {
377 // Expire revoked responses when the subject certificate itself expires.
378 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
));
381 // Ok we like the new response, let's toss the old one.
383 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
385 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
386 rvc
->ocspResponse
= ocspResponse
;
389 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
390 rvc
->ocspSingleResponse
= sr
;
393 rvc
->done
= sr_valid
;
396 if (sr
) SecOCSPSingleResponseDestroy(sr
);
397 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
400 /* Callback from async http code after an ocsp response has been received. */
401 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
402 SecORVCRef rvc
= (SecORVCRef
)http
->info
;
403 SecPathBuilderRef builder
= rvc
->builder
;
404 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
406 /* Add the time this fetch took to complete to the total time */
407 analytics
->ocsp_fetch_time
+= (mach_absolute_time() - http
->start_time
);
409 SecOCSPResponseRef ocspResponse
= NULL
;
410 if (http
->response
) {
411 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
413 /* Parse the returned data as if it's an ocspResponse. */
414 ocspResponse
= SecOCSPResponseCreate(data
);
419 if ((!http
->response
|| !ocspResponse
) && analytics
) {
420 /* We didn't get any data back, so the fetch failed */
421 analytics
->ocsp_fetch_failed
++;
424 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
425 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
428 if (analytics
&& ocspResponse
) {
429 /* We got an OCSP response that didn't pass validation */
430 analytics
-> ocsp_validation_failed
= true;
432 /* Clear the data for the next response. */
433 asynchttp_free(http
);
434 SecORVCFetchNext(rvc
);
438 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
439 SecORVCUpdatePVC(rvc
);
440 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
441 secdebug("rvc", "done with all async jobs");
442 SecPathBuilderStep(builder
);
447 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
448 SecORVCRef orvc
= NULL
;
449 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
451 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
452 orvc
->builder
= builder
;
454 orvc
->certIX
= certIX
;
455 orvc
->http
.queue
= SecPathBuilderGetQueue(builder
);
456 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(builder
);
457 orvc
->http
.completed
= SecOCSPFetchCompleted
;
458 orvc
->http
.info
= orvc
;
459 orvc
->ocspRequest
= NULL
;
460 orvc
->responderIX
= 0;
461 orvc
->responder
= NULL
;
462 orvc
->nextUpdate
= NULL_TIME
;
463 orvc
->ocspResponse
= NULL
;
464 orvc
->ocspSingleResponse
= NULL
;
467 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
468 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
469 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
470 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
476 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
477 /* Get stapled OCSP responses */
478 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
480 if(ocspResponsesData
) {
481 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
482 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
483 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
484 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
486 CFRelease(ocspResponsesData
);
491 /********************************************************
492 ******************* CRL RVC Functions ******************
493 ********************************************************/
495 #include <../trustd/macOS/SecTrustOSXEntryPoints.h>
496 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
498 /* CRL Revocation verification context. */
499 struct OpaqueSecCRVC
{
500 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
501 async_ocspd_t async_ocspd
;
503 /* Pointer to the builder for this revocation check. */
504 SecPathBuilderRef builder
;
506 /* Pointer to the generic rvc for this revocation check */
509 /* The current CRL status from ocspd. */
512 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
515 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
516 current distribution point. */
517 CFIndex distributionPointIX
;
519 /* URL of current distribution point. */
520 CFURLRef distributionPoint
;
522 /* Date until which this revocation status is valid. */
523 CFAbsoluteTime nextUpdate
;
528 static void SecCRVCFinish(SecCRVCRef crvc
) {
532 #define MAX_CRL_DPS 3
533 #define CRL_REQUEST_THRESHOLD 10
535 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
536 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
537 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
539 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
540 if (crlDPCount
>= CRL_REQUEST_THRESHOLD
) {
541 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount
);
544 while (rvc
->distributionPointIX
< crlDPCount
&& rvc
->distributionPointIX
< MAX_CRL_DPS
) {
545 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
546 rvc
->distributionPointIX
++;
547 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
549 /* We only support http and https responders currently. */
550 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
551 CFEqual(CFSTR("https"), scheme
) ||
552 CFEqual(CFSTR("ldap"), scheme
));
555 return distributionPoint
;
562 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
563 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
564 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
565 CFArrayRef serializedCertPath
= SecCertificatePathVCCreateSerialized(path
);
566 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
567 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
568 CFReleaseNull(serializedCertPath
);
569 /* we got a response indicating that the CRL was checked */
570 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
572 /* ocspd doesn't give us the nextUpdate time, so set to default */
573 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
577 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
578 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
579 SecCRVCGetCRLStatus(rvc
);
580 if (rvc
->status
== errSecCertificateRevoked
) {
586 /* Fire off an async http request for this certs revocation status, return
587 false if request was queued, true if we're done. */
588 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
589 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
590 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
591 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
592 CFArrayRef serializedCertPath
= SecCertificatePathVCCreateSerialized(path
);
593 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
594 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
595 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
596 CFDataRef clientAuditToken
= NULL
;
597 SecTaskRef task
= NULL
;
598 audit_token_t auditToken
= {};
599 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
600 require(clientAuditToken
, out
);
601 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
602 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
603 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
604 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
605 rvc
->distributionPoint
, task
);
608 CFReleaseNull(clientAuditToken
);
610 /* Async request was posted, wait for reply. */
618 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
619 if (rvc
->status
== errSecCertificateRevoked
) {
620 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
621 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
622 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
623 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
626 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
628 /* make the revocation reason available in the trust result */
629 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
632 CFReleaseNull(cfreason
);
636 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
637 SecCRVCRef rvc
= ocspd
->info
;
638 SecPathBuilderRef builder
= rvc
->builder
;
639 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
641 /* Add the time this fetch took to complete to the total time */
642 analytics
->crl_fetch_time
+= (mach_absolute_time() - ocspd
->start_time
);
644 /* we got a response indicating that the CRL was checked */
645 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
646 rvc
->status
= ocspd
->response
;
648 /* ocspd doesn't give us the nextUpdate time, so set to default */
649 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
650 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
651 SecCRVCUpdatePVC(rvc
);
652 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
653 secdebug("rvc", "done with all async jobs");
654 SecPathBuilderStep(builder
);
658 /* We didn't get any data back, so the fetch failed */
659 analytics
->crl_fetch_failed
++;
661 if(SecCRVCFetchNext(rvc
)) {
662 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
663 secdebug("rvc", "done with all async jobs");
664 SecPathBuilderStep(builder
);
670 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
671 SecCRVCRef crvc
= NULL
;
672 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
674 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
675 crvc
->builder
= builder
;
677 crvc
->certIX
= certIX
;
678 crvc
->status
= errSecInternal
;
679 crvc
->distributionPointIX
= 0;
680 crvc
->distributionPoint
= NULL
;
681 crvc
->nextUpdate
= NULL_TIME
;
682 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(builder
);
683 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
684 crvc
->async_ocspd
.response
= errSecInternal
;
685 crvc
->async_ocspd
.info
= crvc
;
691 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
692 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
693 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
694 if (revocation_method
&&
695 CFEqual(kSecPolicyCheckRevocationCRL
, revocation_method
)) {
696 /* Our client insists on CRLs */
697 secinfo("rvc", "client told us to check CRL");
699 analytics
->crl_client
= true;
703 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
704 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
705 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
706 (revocation_method
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, revocation_method
))) {
707 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
708 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
709 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
711 analytics
->crl_cert
= true;
717 #endif /* ENABLE_CRLS */
719 void SecRVCDelete(SecRVCRef rvc
) {
721 SecORVCFinish(rvc
->orvc
);
727 SecCRVCFinish(rvc
->crvc
);
732 if (rvc
->valid_info
) {
733 SecValidInfoRelease(rvc
->valid_info
);
734 rvc
->valid_info
= NULL
;
738 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
739 secdebug("alloc", "%p", rvc
);
740 rvc
->builder
= builder
;
741 rvc
->certIX
= certIX
;
742 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
744 rvc
->crvc
= SecCRVCCreate(rvc
, builder
, certIX
);
759 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
760 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
761 if (!revocation_method
762 || !CFEqual(revocation_method
, kSecPolicyCheckRevocationCRL
)) {
768 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
773 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc
) {
774 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
777 if (!rvc
->valid_info
->hasDateConstraints
) {
780 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
784 CFAbsoluteTime certIssued
= SecCertificateNotValidBefore(certificate
);
785 CFAbsoluteTime caNotBefore
= -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
786 CFAbsoluteTime caNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
787 if (rvc
->valid_info
->notBeforeDate
) {
788 caNotBefore
= CFDateGetAbsoluteTime(rvc
->valid_info
->notBeforeDate
);
790 if (rvc
->valid_info
->notAfterDate
) {
791 caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
792 /* per the Valid specification, if this date is in the past, we need to check CT. */
793 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
794 if (caNotAfter
< now
) {
795 rvc
->valid_info
->requireCT
= true;
798 if ((certIssued
< caNotBefore
) && (rvc
->certIX
> 0)) {
799 /* not-before constraint is only applied to leaf certificate, for now. */
803 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
804 if ((certIssued
< caNotBefore
) || (certIssued
> caNotAfter
)) {
805 /* We are outside the constrained validity period. */
806 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
807 (rvc
->valid_info
->overridable
) ? "" : " (non-recoverable error)");
809 analytics
->valid_status
|= TAValidDateContrainedRevoked
;
811 if (rvc
->valid_info
->overridable
) {
812 /* error is recoverable, treat certificate as untrusted
813 (note this date check is different from kSecPolicyCheckTemporalValidity) */
814 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
815 kCFBooleanFalse
, true);
817 /* error is non-overridable, treat certificate as revoked */
818 SInt32 reason
= 0; /* unspecified reason code */
819 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
820 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
822 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
824 /* make the revocation reason available in the trust result */
825 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
827 CFReleaseNull(cfreason
);
829 } else if (analytics
) {
830 analytics
->valid_status
|= TAValidDateConstrainedOK
;
834 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc
) {
835 if (!rvc
|| !rvc
->valid_info
) {
838 SecValidInfoRef info
= rvc
->valid_info
;
839 /* outcomes as defined in Valid server specification */
840 if (info
->format
== kSecValidInfoFormatSerial
||
841 info
->format
== kSecValidInfoFormatSHA256
) {
842 if (info
->noCACheck
|| info
->complete
|| info
->isOnList
) {
845 } else { /* info->format == kSecValidInfoFormatNto1 */
846 if (info
->noCACheck
|| (info
->complete
&& !info
->isOnList
)) {
853 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc
) {
854 if (!rvc
|| !rvc
->valid_info
) {
857 SecValidInfoRef info
= rvc
->valid_info
;
858 /* either not present on an allowlist, or present on a blocklist */
859 return (!info
->isOnList
&& info
->valid
) || (info
->isOnList
&& !info
->valid
);
862 void SecRVCSetRevokedResult(SecRVCRef rvc
) {
863 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
866 if (rvc
->valid_info
->overridable
) {
867 /* error is recoverable, treat certificate as untrusted */
868 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
869 kCFBooleanFalse
, true);
872 /* error is fatal, treat certificate as revoked */
873 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
874 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
875 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
877 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
879 /* make the revocation reason available in the trust result */
880 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
882 CFReleaseNull(cfreason
);
885 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
886 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
889 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
890 SecValidInfoRef info
= rvc
->valid_info
;
892 bool definitive
= SecRVCHasDefinitiveValidInfo(rvc
);
893 bool revoked
= SecRVCHasRevokedValidInfo(rvc
);
896 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
899 analytics
->valid_status
|= definitive
? TAValidDefinitelyRevoked
: TAValidProbablyRevoked
;
901 analytics
->valid_status
|= definitive
? TAValidDefinitelyOK
: TAValidProbablyOK
;
905 /* Handle no-ca cases */
906 if (info
->noCACheck
) {
907 bool allowed
= (info
->valid
&& info
->complete
&& info
->isOnList
);
909 /* definitely revoked */
910 SecRVCSetRevokedResult(rvc
);
911 } else if (allowed
) {
912 /* definitely not revoked (allowlisted) */
913 SecCertificatePathVCSetIsAllowlisted(path
, true);
915 /* no-ca is definitive; no need to check further. */
916 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
917 (allowed
) ? "allowed" : "revoked", rvc
->certIX
);
922 /* Handle date constraints, if present.
923 * Note: a not-after date may set the CT requirement,
924 * so check requireCT after this function is called. */
925 SecRVCProcessValidDateConstraints(rvc
);
927 /* Set CT requirement on path, if present. */
928 if (info
->requireCT
) {
930 analytics
->valid_require_ct
|= info
->requireCT
;
932 SecPathCTPolicy ctp
= kSecPathCTRequired
;
933 if (info
->overridable
) {
934 ctp
= kSecPathCTRequiredOverridable
;
936 SecCertificatePathVCSetRequiresCT(path
, ctp
);
939 /* Trigger OCSP for any non-definitive or revoked cases */
940 if (!definitive
|| revoked
) {
941 info
->checkOCSP
= true;
944 if (info
->checkOCSP
) {
946 /* Valid DB results caused us to do OCSP */
947 analytics
->valid_trigger_ocsp
= true;
949 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
950 CFIndex issuerIX
= rvc
->certIX
+ 1;
951 if (issuerIX
>= count
) {
952 /* cannot perform a revocation check on the last cert in the
953 chain, since we don't have its issuer. */
957 for (pvcIX
= 0; pvcIX
< SecPathBuilderGetPVCCount(rvc
->builder
); pvcIX
++) {
958 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, pvcIX
);
959 if (!pvc
) { continue; }
960 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
961 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
962 if (policyName
&& CFEqual(CFSTR("sslServer"), policyName
)) {
963 /* perform revocation check for SSL policy;
964 require for leaf if an OCSP responder is present. */
965 if (0 == rvc
->certIX
) {
966 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
967 CFArrayRef resps
= (cert
) ? SecCertificateGetOCSPResponders(cert
) : NULL
;
968 CFIndex rcount
= (resps
) ? CFArrayGetCount(resps
) : 0;
970 // %%% rdar://31279923
971 // This currently requires a valid revocation response for each cert,
972 // but we only want to require a leaf check. For now, do not require.
973 //SecPathBuilderSetRevocationResponseRequired(rvc->builder);
976 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
977 (info
->complete
) ? "" : "possibly ", (info
->valid
) ? "allowed" : "revoked",
979 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
985 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
986 /* Skip checking for OCSP Signer verification */
987 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
988 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
989 if (!pvc
) { return false; }
990 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
991 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
992 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
997 /* Make sure revocation db info is up-to-date.
998 * We don't care if the builder is allowed to access the network because
999 * the network fetching does not block the trust evaluation. */
1000 SecRevocationDbCheckNextUpdate();
1002 /* Check whether we have valid db info for this cert,
1003 given the cert and its issuer */
1004 SecValidInfoRef info
= NULL
;
1005 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
1007 bool isSelfSigned
= false;
1008 SecCertificateRef cert
= NULL
;
1009 SecCertificateRef issuer
= NULL
;
1010 CFIndex issuerIX
= rvc
->certIX
+ 1;
1011 if (count
> issuerIX
) {
1012 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
1013 } else if (count
== issuerIX
) {
1014 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
1015 if (rootIX
== rvc
->certIX
) {
1016 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
1017 isSelfSigned
= true;
1020 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
1021 if (!isSelfSigned
) {
1022 /* skip revocation db check for self-signed certificates [33137065] */
1023 info
= SecRevocationDbCopyMatching(cert
, issuer
);
1025 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
1028 SecValidInfoRef old_info
= rvc
->valid_info
;
1029 rvc
->valid_info
= info
;
1031 SecValidInfoRelease(old_info
);
1038 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
1039 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
1040 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
1041 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
1042 SecOCSPResponseRef response
= NULL
;
1043 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
1044 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
1045 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
1047 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
1049 SecORVCConsumeOCSPResponse(rvc
->orvc
,
1052 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
1053 if (rvc
->orvc
->done
&& analytics
) {
1054 /* We found a valid OCSP response in the cache */
1055 analytics
->ocsp_cache_hit
= true;
1059 /* Don't check CRL cache if policy requested OCSP only */
1060 if (SecRVCShouldCheckCRL(rvc
)) {
1061 SecCRVCCheckRevocationCache(rvc
->crvc
);
1066 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
1067 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
1068 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
1070 if (rvc
->crvc
) { SecCRVCUpdatePVC(rvc
->crvc
); }
1074 static bool SecRVCFetchNext(SecRVCRef rvc
) {
1075 bool OCSP_fetch_finished
= true;
1076 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
1077 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
1078 if (SecRVCShouldCheckOCSP(rvc
)) {
1079 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
1081 if (OCSP_fetch_finished
) {
1082 /* we didn't start an OCSP background job for this cert */
1083 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
1084 } else if (analytics
) {
1085 /* We did a network OCSP fetch, set report appropriately */
1086 analytics
->ocsp_network
= true;
1087 analytics
->ocsp_fetches
++;
1091 bool CRL_fetch_finished
= true;
1092 /* Don't check CRL cache if policy requested OCSP only */
1093 if (SecRVCShouldCheckCRL(rvc
)) {
1094 /* reset the distributionPointIX because we already iterated through the CRLDPs
1095 * in SecCRVCCheckRevocationCache */
1096 rvc
->crvc
->distributionPointIX
= 0;
1097 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
1099 if (CRL_fetch_finished
) {
1100 /* we didn't start a CRL background job for this cert */
1101 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
1102 } else if (analytics
) {
1103 /* We did a CRL fetch */
1104 analytics
->crl_fetches
++;
1106 OCSP_fetch_finished
&= CRL_fetch_finished
;
1109 return OCSP_fetch_finished
;
1112 /* The SecPathBuilder state machine calls SecPathBuilderCheckRevocation twice --
1113 * once in the ValidatePath state, and again in the ComputeDetails state. In the
1114 * ValidatePath state we've not yet run the path checks, so for callers who set
1115 * kSecRevocationCheckIfTrusted, we don't do any networking on that first call.
1116 * Here, if we've already done revocation before (so we're in ComputeDetails now),
1117 * we need to recheck (and enable networking) for trusted chains and
1118 * kSecRevocationCheckIfTrusted. Otherwise, we skip the checks to save on the processing
1119 * but update the PVCs with the revocation results from the last check. */
1120 static bool SecRevocationDidCheckRevocation(SecPathBuilderRef builder
, bool *first_check_done
) {
1121 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
1122 if (!SecCertificatePathVCIsRevocationDone(path
)) {
1125 if (first_check_done
) {
1126 *first_check_done
= true;
1129 SecPVCRef resultPVC
= SecPathBuilderGetResultPVC(builder
);
1130 bool recheck
= false;
1131 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
) && SecPVCIsOkResult(resultPVC
)) {
1133 secdebug("rvc", "Rechecking revocation because network now allowed");
1135 secdebug("rvc", "Not rechecking revocation");
1138 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
1139 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1140 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1141 if (rvc
&& !recheck
) {
1142 SecRVCUpdatePVC(rvc
);
1144 SecRVCDelete(rvc
); // reset the RVC for the second pass
1150 static bool SecRevocationCanAccessNetwork(SecPathBuilderRef builder
, bool first_check_done
) {
1151 /* CheckRevocationIfTrusted overrides NoNetworkAccess for revocation */
1152 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
)) {
1153 if (first_check_done
) {
1154 /* We're on the second pass. We need to now allow networking for revocation.
1155 * SecRevocationDidCheckRevocation takes care of not running a second pass
1156 * if the chain isn't trusted. */
1159 /* We're on the first pass of the revocation checks, where we aren't
1160 * supposed to do networking because we don't know if the chain
1161 * is trusted yet. */
1165 return SecPathBuilderCanAccessNetwork(builder
);
1168 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
1169 secdebug("rvc", "checking revocation");
1170 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
1171 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
1172 bool completed
= true;
1173 if (certCount
<= 1) {
1174 /* Can't verify without an issuer; we're done */
1178 bool first_check_done
= false;
1179 if (SecRevocationDidCheckRevocation(builder
, &first_check_done
)) {
1183 /* Setup things so we check revocation status of all certs. */
1184 SecCertificatePathVCAllocateRVCs(path
, certCount
);
1186 /* Note that if we are multi threaded and a job completes after it
1187 is started but before we return from this function, we don't want
1188 a callback to decrement asyncJobCount to zero before we finish issuing
1189 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
1190 and decrement pvc->asyncJobCount for each cert that we don't start a
1191 background fetch for. (We will never start an async job for the final
1192 cert in the chain.) */
1194 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
-1));
1196 /* If we enable CRLS, we may end up with two async jobs per cert: one
1197 * for OCSP and one for fetching the CRL */
1198 SecPathBuilderSetAsyncJobCount(builder
, 2 * (unsigned int)(certCount
-1));
1201 /* Loop though certificates again and issue an ocsp fetch if the
1202 revocation status checking isn't done yet (and we have an issuer!) */
1203 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1204 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
1205 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1210 SecRVCInit(rvc
, builder
, certIX
);
1212 /* RFC 6960: OCSP No-Check extension says that we shouldn't check revocation. */
1213 if (SecCertificateHasMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path
, certIX
),
1214 CFSTR("1.3.6.1.5.5.7.48.1.5"))) // id-pkix-ocsp-nocheck
1216 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX
);
1217 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
1219 /* This certificate has OCSP No-Check, so add to reporting analytics */
1220 analytics
->ocsp_no_check
= true;
1229 #if !TARGET_OS_BRIDGE
1230 /* Check valid database first (separate from OCSP response cache) */
1231 if (SecRVCCheckValidInfoDatabase(rvc
)) {
1232 SecRVCProcessValidInfoResults(rvc
);
1235 /* Any other revocation method requires an issuer certificate to verify the response;
1236 * skip the last cert in the chain since it doesn't have one. */
1237 if (certIX
+1 >= certCount
) {
1241 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1242 * policy specifically requested CRLs only. */
1243 if (SecRVCShouldCheckOCSP(rvc
)) {
1244 /* If we have any OCSP stapled responses, check those first */
1245 SecORVCProcessStapledResponses(rvc
->orvc
);
1248 #if TARGET_OS_BRIDGE
1249 /* The bridge has no writeable storage and no network. Nothing else we can
1255 /* Then check the caches for revocation results. */
1256 SecRVCCheckRevocationCaches(rvc
);
1258 /* The check is done if we found cached responses from either method. */
1264 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1268 /* If we got a cached response that is no longer valid (which can only be true for
1269 * revoked responses), let's try to get a fresher response even if no one asked.
1270 * This check resolves unrevocation events after the nextUpdate time. */
1271 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1273 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1274 async http request for this cert's revocation status, unless we already successfully checked
1275 the revocation status of this cert based on the cache or stapled responses. */
1276 bool allow_fetch
= SecRevocationCanAccessNetwork(builder
, first_check_done
) &&
1277 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1278 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1279 bool fetch_done
= true;
1280 if (rvc
->done
|| !allow_fetch
) {
1281 /* We got a cache hit or we aren't allowed to access the network */
1282 SecRVCUpdatePVC(rvc
);
1283 /* We didn't really start any background jobs for this cert. */
1284 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1286 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1289 fetch_done
= SecRVCFetchNext(rvc
);
1292 /* We started at least one background fetch. */
1293 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
1298 /* Return false if we started any background jobs. */
1299 /* We can't just return !builder->asyncJobCount here, since if we started any
1300 jobs the completion callback will be called eventually and it will call
1301 SecPathBuilderStep(). If for some reason everything completed before we
1302 get here we still want the outer SecPathBuilderStep() to terminate so we
1303 keep track of whether we started any jobs and return false if so. */
1307 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1308 CFAbsoluteTime enu
= NULL_TIME
;
1309 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1310 enu
= rvc
->orvc
->nextUpdate
;
1312 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
1313 if (enu
== NULL_TIME
||
1314 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
1315 /* We didn't check OCSP or CRL next update time was sooner */
1316 enu
= crlNextUpdate
;