2 * Copyright (c) 2008-2017 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 <Security/SecCertificatePriv.h>
31 #include <Security/SecCertificateInternal.h>
32 #include <Security/SecPolicyPriv.h>
33 #include <Security/SecTrustPriv.h>
34 #include <Security/SecInternal.h>
36 #include <utilities/debugging.h>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/SecIOFormat.h>
40 #include <securityd/SecTrustServer.h>
41 #include <securityd/SecOCSPRequest.h>
42 #include <securityd/SecOCSPResponse.h>
43 #include <securityd/asynchttp.h>
44 #include <securityd/SecOCSPCache.h>
45 #include <securityd/SecRevocationDb.h>
46 #include <securityd/SecCertificateServer.h>
48 #include <securityd/SecRevocationServer.h>
51 /********************************************************
52 ****************** OCSP RVC Functions ******************
53 ********************************************************/
54 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
55 const CFAbsoluteTime kSecOCSPResponseOnlineTTL
= 5.0 * 60.0;
56 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
58 /* OCSP Revocation verification context. */
59 struct OpaqueSecORVC
{
60 /* Will contain the response data. */
63 /* Pointer to the builder for this revocation check. */
64 SecPathBuilderRef builder
;
66 /* Pointer to the generic rvc for this revocation check */
69 /* The ocsp request we send to each responder. */
70 SecOCSPRequestRef ocspRequest
;
72 /* The freshest response we received so far, from stapling or cache or responder. */
73 SecOCSPResponseRef ocspResponse
;
75 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
76 SecOCSPSingleResponseRef ocspSingleResponse
;
78 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
81 /* Index in array returned by SecCertificateGetOCSPResponders() for current
85 /* URL of current responder. */
88 /* Date until which this revocation status is valid. */
89 CFAbsoluteTime nextUpdate
;
94 static void SecORVCFinish(SecORVCRef orvc
) {
95 secdebug("alloc", "%p", orvc
);
96 asynchttp_free(&orvc
->http
);
97 if (orvc
->ocspRequest
) {
98 SecOCSPRequestFinalize(orvc
->ocspRequest
);
99 orvc
->ocspRequest
= NULL
;
101 if (orvc
->ocspResponse
) {
102 SecOCSPResponseFinalize(orvc
->ocspResponse
);
103 orvc
->ocspResponse
= NULL
;
104 if (orvc
->ocspSingleResponse
) {
105 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
106 orvc
->ocspSingleResponse
= NULL
;
111 #define MAX_OCSP_RESPONDERS 3
112 #define OCSP_REQUEST_THRESHOLD 10
114 /* Return the next responder we should contact for this rvc or NULL if we
115 exhausted them all. */
116 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
117 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
118 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
119 if (ocspResponders
) {
120 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
121 if (responderCount
>= OCSP_REQUEST_THRESHOLD
) {
122 secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount
);
125 while (rvc
->responderIX
< responderCount
&& rvc
->responderIX
< MAX_OCSP_RESPONDERS
) {
126 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
128 CFStringRef scheme
= CFURLCopyScheme(responder
);
130 /* We only support http and https responders currently. */
131 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
132 CFEqual(CFSTR("https"), scheme
));
142 /* Fire off an async http request for this certs revocation status, return
143 false if request was queued, true if we're done. */
144 static bool SecORVCFetchNext(SecORVCRef rvc
) {
145 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
146 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
150 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
151 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
152 /* Async request was posted, wait for reply. */
162 /* Process a verified ocsp response for a given cert. Return true if the
163 certificate status was obtained. */
164 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
167 switch (this->certStatus
) {
169 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
170 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
171 in the info dictionary. */
172 //cert.revokeCheckGood(true);
173 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
177 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
178 /* @@@ Mark cert as revoked (with reason) at revocation date in
179 the info dictionary, or perhaps we should use a different key per
180 reason? That way a client using exceptions can ignore some but
182 SInt32 reason
= this->crlReason
;
183 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
184 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
185 cfreason
, true, kSecTrustResultFatalTrustFailure
);
187 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
189 /* make the revocation reason available in the trust result */
190 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
197 /* not an error, no per-cert status, nothing here */
198 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
202 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
203 (int)this->certStatus
, rvc
->certIX
);
211 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
212 if (rvc
->ocspSingleResponse
) {
213 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
215 if (rvc
->ocspResponse
) {
216 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
220 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
223 SecOCSPEvaluateCompleted(const void *userData
,
224 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
225 SecTrustResultType result
) {
226 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
228 Block_release(evaluated
);
232 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
233 __block
bool evaluated
= false;
234 bool trusted
= false;
235 if (!signers
|| !issuers
) {
239 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
240 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
241 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
242 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
243 CFRelease(ocspSigner
);
245 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
246 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
251 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
252 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
253 signers
, issuers
, true, false,
254 policies
, NULL
, NULL
, NULL
,
255 verifyTime
, NULL
, NULL
,
256 SecOCSPEvaluateCompleted
, completed
);
257 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
258 SecPathBuilderStep(oBuilder
);
259 CFReleaseNull(clientAuditToken
);
260 CFReleaseNull(policies
);
262 /* verify the public key of the issuer signed the OCSP signer */
264 SecCertificateRef issuer
= NULL
, signer
= NULL
;
265 SecKeyRef issuerPubKey
= NULL
;
267 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
268 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
272 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
274 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
277 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
280 secnotice("ocsp", "ocsp signer cert not signed by issuer");
282 CFReleaseNull(issuerPubKey
);
288 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
290 SecCertificatePathVCRef issuers
= SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc
->builder
), rvc
->certIX
+ 1);
291 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers
, 0)) : NULL
;
292 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
293 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
295 if (signer
&& signers
) {
296 if (issuer
&& CFEqual(signer
, issuer
)) {
297 /* We already know we trust issuer since it's the issuer of the
298 * cert we are verifying. */
299 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
303 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
305 CFMutableArrayRef signerCerts
= NULL
;
306 CFArrayRef issuerCerts
= NULL
;
308 /* Ensure the signer cert is the 0th cert for trust evaluation */
309 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
310 CFArrayAppendValue(signerCerts
, signer
);
311 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
314 issuerCerts
= SecCertificatePathVCCopyCertificates(issuers
);
317 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
318 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
322 /* @@@ We don't trust the cert so don't use this response. */
323 secnotice("ocsp", "ocsp response signed by certificate which "
324 "does not satisfy ocspSigner policy");
327 CFReleaseNull(signerCerts
);
328 CFReleaseNull(issuerCerts
);
331 /* @@@ No signer found for this ocsp response, discard it. */
332 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
337 #if DUMP_OCSPRESPONSES
339 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
340 rvc
->certIX
, (trusted
? "t" : "u"));
341 secdumpdata(ocspResponse
->data
, buf
);
343 CFReleaseNull(issuers
);
344 CFReleaseNull(issuer
);
345 CFReleaseNull(signers
);
346 CFReleaseNull(signer
);
350 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
351 SecOCSPSingleResponseRef sr
= NULL
;
352 require_quiet(ocspResponse
, errOut
);
353 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
354 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
355 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
356 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
357 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
358 // Check if this response is fresher than any (cached) response we might still have in the rvc.
359 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
361 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
362 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
363 check whether the leaf was revoked (we are already checking the rest of
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 SecOCSPResponseRef ocspResponse
= NULL
;
405 if (http
->response
) {
406 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
408 /* Parse the returned data as if it's an ocspResponse. */
409 ocspResponse
= SecOCSPResponseCreate(data
);
414 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
415 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
418 /* Clear the data for the next response. */
419 asynchttp_free(http
);
420 SecORVCFetchNext(rvc
);
424 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
425 SecORVCUpdatePVC(rvc
);
426 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
427 secdebug("rvc", "done with all async jobs");
428 SecPathBuilderStep(builder
);
433 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
434 SecORVCRef orvc
= NULL
;
435 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
437 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
438 orvc
->builder
= builder
;
440 orvc
->certIX
= certIX
;
441 orvc
->http
.queue
= SecPathBuilderGetQueue(builder
);
442 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(builder
);
443 orvc
->http
.completed
= SecOCSPFetchCompleted
;
444 orvc
->http
.info
= orvc
;
445 orvc
->ocspRequest
= NULL
;
446 orvc
->responderIX
= 0;
447 orvc
->responder
= NULL
;
448 orvc
->nextUpdate
= NULL_TIME
;
449 orvc
->ocspResponse
= NULL
;
450 orvc
->ocspSingleResponse
= NULL
;
453 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
454 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
455 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
456 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
462 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
463 /* Get stapled OCSP responses */
464 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
466 if(ocspResponsesData
) {
467 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
468 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
469 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
470 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
472 CFRelease(ocspResponsesData
);
477 /********************************************************
478 ******************* CRL RVC Functions ******************
479 ********************************************************/
481 #include <../trustd/macOS/SecTrustOSXEntryPoints.h>
482 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
484 /* CRL Revocation verification context. */
485 struct OpaqueSecCRVC
{
486 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
487 async_ocspd_t async_ocspd
;
489 /* Pointer to the builder for this revocation check. */
490 SecPathBuilderRef builder
;
492 /* Pointer to the generic rvc for this revocation check */
495 /* The current CRL status from ocspd. */
498 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
501 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
502 current distribution point. */
503 CFIndex distributionPointIX
;
505 /* URL of current distribution point. */
506 CFURLRef distributionPoint
;
508 /* Date until which this revocation status is valid. */
509 CFAbsoluteTime nextUpdate
;
514 static void SecCRVCFinish(SecCRVCRef crvc
) {
518 #define MAX_CRL_DPS 3
519 #define CRL_REQUEST_THRESHOLD 10
521 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
522 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
523 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
525 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
526 if (crlDPCount
>= CRL_REQUEST_THRESHOLD
) {
527 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount
);
530 while (rvc
->distributionPointIX
< crlDPCount
&& rvc
->distributionPointIX
< MAX_CRL_DPS
) {
531 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
532 rvc
->distributionPointIX
++;
533 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
535 /* We only support http and https responders currently. */
536 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
537 CFEqual(CFSTR("https"), scheme
) ||
538 CFEqual(CFSTR("ldap"), scheme
));
541 return distributionPoint
;
548 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
549 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
550 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
551 SecCertificatePathRef nonVCpath
= SecCertificatePathVCCopyCertificatePath(path
);
552 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(nonVCpath
, NULL
);
553 CFReleaseNull(nonVCpath
);
554 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
555 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
556 CFReleaseNull(serializedCertPath
);
557 /* we got a response indicating that the CRL was checked */
558 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
560 /* ocspd doesn't give us the nextUpdate time, so set to default */
561 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
565 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
566 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
567 SecCRVCGetCRLStatus(rvc
);
568 if (rvc
->status
== errSecCertificateRevoked
) {
574 /* Fire off an async http request for this certs revocation status, return
575 false if request was queued, true if we're done. */
576 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
577 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
578 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
579 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
580 SecCertificatePathRef nonVCpath
= SecCertificatePathVCCopyCertificatePath(path
);
581 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(nonVCpath
, NULL
);
582 CFReleaseNull(nonVCpath
);
583 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
584 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
585 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
586 CFDataRef clientAuditToken
= NULL
;
587 SecTaskRef task
= NULL
;
588 audit_token_t auditToken
= {};
589 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
590 require(clientAuditToken
, out
);
591 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
592 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
593 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
594 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
595 rvc
->distributionPoint
, task
);
598 CFReleaseNull(clientAuditToken
);
600 /* Async request was posted, wait for reply. */
608 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
609 if (rvc
->status
== errSecCertificateRevoked
) {
610 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
611 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
612 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
613 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
614 cfreason
, true, kSecTrustResultFatalTrustFailure
);
616 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
618 /* make the revocation reason available in the trust result */
619 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
622 CFReleaseNull(cfreason
);
626 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
627 SecCRVCRef rvc
= ocspd
->info
;
628 SecPathBuilderRef builder
= rvc
->builder
;
629 /* we got a response indicating that the CRL was checked */
630 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
631 rvc
->status
= ocspd
->response
;
633 /* ocspd doesn't give us the nextUpdate time, so set to default */
634 rvc
->nextUpdate
= SecPathBuilderGetVerifyTime(rvc
->builder
) + kSecDefaultCRLTTL
;
635 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
636 SecCRVCUpdatePVC(rvc
);
637 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
638 secdebug("rvc", "done with all async jobs");
639 SecPathBuilderStep(builder
);
642 if(SecCRVCFetchNext(rvc
)) {
643 if (!SecPathBuilderDecrementAsyncJobCount(builder
)) {
644 secdebug("rvc", "done with all async jobs");
645 SecPathBuilderStep(builder
);
651 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
652 SecCRVCRef crvc
= NULL
;
653 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
655 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
656 crvc
->builder
= builder
;
658 crvc
->certIX
= certIX
;
659 crvc
->status
= errSecInternal
;
660 crvc
->distributionPointIX
= 0;
661 crvc
->distributionPoint
= NULL
;
662 crvc
->nextUpdate
= NULL_TIME
;
663 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(builder
);
664 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
665 crvc
->async_ocspd
.response
= errSecInternal
;
666 crvc
->async_ocspd
.info
= crvc
;
672 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
673 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
674 if (revocation_method
&&
675 CFEqual(kSecPolicyCheckRevocationCRL
, revocation_method
)) {
676 /* Our client insists on CRLs */
677 secinfo("rvc", "client told us to check CRL");
680 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
681 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
682 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
683 (revocation_method
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, revocation_method
))) {
684 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
685 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
686 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
691 #endif /* ENABLE_CRLS */
693 void SecRVCDelete(SecRVCRef rvc
) {
695 SecORVCFinish(rvc
->orvc
);
700 SecCRVCFinish(rvc
->crvc
);
704 if (rvc
->valid_info
) {
705 SecValidInfoRelease(rvc
->valid_info
);
709 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
710 secdebug("alloc", "%p", rvc
);
711 rvc
->builder
= builder
;
712 rvc
->certIX
= certIX
;
713 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
715 rvc
->crvc
= SecCRVCCreate(rvc
, builder
, certIX
);
721 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
722 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
723 if (!revocation_method
724 || !CFEqual(revocation_method
, kSecPolicyCheckRevocationCRL
)) {
730 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
735 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
736 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
739 SecValidInfoFormat format
= rvc
->valid_info
->format
;
740 bool valid
= rvc
->valid_info
->valid
;
741 bool noCACheck
= rvc
->valid_info
->noCACheck
;
742 bool checkOCSP
= rvc
->valid_info
->checkOCSP
;
743 bool complete
= rvc
->valid_info
->complete
;
744 bool isOnList
= rvc
->valid_info
->isOnList
;
745 bool definitive
= false;
747 if (format
== kSecValidInfoFormatSerial
|| format
== kSecValidInfoFormatSHA256
) {
748 /* serial or hash list: could be blocked or allowed; could be incomplete */
749 if (((!valid
&& complete
&& isOnList
) || (valid
&& complete
&& !isOnList
)) && noCACheck
) {
750 /* definitely revoked */
751 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
752 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
753 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
754 cfreason
, true, kSecTrustResultFatalTrustFailure
);
755 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
757 /* make the revocation reason available in the trust result */
758 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
760 CFReleaseNull(cfreason
);
763 else if (valid
&& complete
&& isOnList
&& noCACheck
) {
764 /* definitely not revoked (allowlisted) */
765 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
767 SecCertificatePathVCSetIsAllowlisted(path
, true);
769 secdebug("validupdate", "rvc: no certificate path for builder");
774 /* either definitely revoked or allowed; no need to check further. */
775 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
776 (valid
&& complete
&& isOnList
) ? "allowed" : "revoked", rvc
->certIX
);
780 /* verify our info with the OCSP server */
784 /* Handle non-definitive information.
785 We set rvc->done = true above ONLY if the result was definitive;
786 otherwise we require a revocation check for SSL usage.
788 if (format
== kSecValidInfoFormatNto1
) {
789 /* matched the filter */
794 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
795 CFIndex issuerIX
= rvc
->certIX
+ 1;
796 if (issuerIX
>= count
) {
797 /* cannot perform a revocation check on the last cert in the
798 chain, since we don't have its issuer. */
802 for (pvcIX
= 0; pvcIX
< SecPathBuilderGetPVCCount(rvc
->builder
); pvcIX
++) {
803 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, pvcIX
);
804 if (!pvc
) { continue; }
805 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
806 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
807 if (policyName
&& CFEqual(CFSTR("sslServer"), policyName
)) {
808 /* perform revocation check for SSL policy;
809 require for leaf if an OCSP responder is present. */
810 if (0 == rvc
->certIX
) {
811 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
812 CFArrayRef resps
= (cert
) ? SecCertificateGetOCSPResponders(cert
) : NULL
;
813 CFIndex rcount
= (resps
) ? CFArrayGetCount(resps
) : 0;
815 // %%% rdar://31279923
816 // This currently requires a valid revocation response for each cert,
817 // but we only want to require a leaf check. For now, do not require.
818 //SecPathBuilderSetRevocationResponseRequired(rvc->builder);
821 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
822 (complete
) ? "" : "possibly ", (valid
) ? "allowed" : "revoked",
824 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
830 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
831 /* Skip checking for OCSP Signer verification */
832 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
833 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
834 if (!pvc
) { return false; }
835 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
836 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
837 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
842 /* Make sure revocation db info is up-to-date.
843 * We don't care if the builder is allowed to access the network because
844 * the network fetching does not block the trust evaluation. */
845 SecRevocationDbCheckNextUpdate();
847 /* Check whether we have valid db info for this cert,
848 given the cert and its issuer */
849 SecValidInfoRef info
= NULL
;
850 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
852 bool isSelfSigned
= false;
853 SecCertificateRef cert
= NULL
;
854 SecCertificateRef issuer
= NULL
;
855 CFIndex issuerIX
= rvc
->certIX
+ 1;
856 if (count
> issuerIX
) {
857 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
858 } else if (count
== issuerIX
) {
859 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
860 if (rootIX
== rvc
->certIX
) {
861 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
865 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
867 /* skip revocation db check for self-signed certificates [33137065] */
868 info
= SecRevocationDbCopyMatching(cert
, issuer
);
870 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
873 SecValidInfoRef old_info
= rvc
->valid_info
;
874 rvc
->valid_info
= info
;
876 SecValidInfoRelease(old_info
);
883 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
884 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
885 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
886 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
887 SecOCSPResponseRef response
= NULL
;
888 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
889 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
890 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
892 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
894 SecORVCConsumeOCSPResponse(rvc
->orvc
,
899 /* Don't check CRL cache if policy requested OCSP only */
900 if (SecRVCShouldCheckCRL(rvc
)) {
901 SecCRVCCheckRevocationCache(rvc
->crvc
);
906 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
907 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
908 SecORVCUpdatePVC(rvc
->orvc
);
910 SecCRVCUpdatePVC(rvc
->crvc
);
914 static bool SecRVCFetchNext(SecRVCRef rvc
) {
915 bool OCSP_fetch_finished
= true;
916 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
917 if (SecRVCShouldCheckOCSP(rvc
)) {
918 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
920 if (OCSP_fetch_finished
) {
921 /* we didn't start an OCSP background job for this cert */
922 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
926 bool CRL_fetch_finished
= true;
927 /* Don't check CRL cache if policy requested OCSP only */
928 if (SecRVCShouldCheckCRL(rvc
)) {
929 /* reset the distributionPointIX because we already iterated through the CRLDPs
930 * in SecCRVCCheckRevocationCache */
931 rvc
->crvc
->distributionPointIX
= 0;
932 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
934 if (CRL_fetch_finished
) {
935 /* we didn't start a CRL background job for this cert */
936 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
938 OCSP_fetch_finished
&= CRL_fetch_finished
;
941 return OCSP_fetch_finished
;
944 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
945 secdebug("rvc", "checking revocation");
946 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
947 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
948 bool completed
= true;
949 if (certCount
<= 1) {
950 /* Can't verify without an issuer; we're done */
955 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
957 * Note: we can't check revocation for the last certificate in the chain
958 * via OCSP or CRL methods, since there isn't a separate issuer cert to
959 * sign those responses. However, since a self-signed root has an implied
960 * issuer of itself, we can check for it in the valid database.
963 if (SecCertificatePathVCIsRevocationDone(path
)) {
964 /* We have done revocation checking already, set PVCs with results. */
965 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
966 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
967 if (rvc
) { SecRVCUpdatePVC(rvc
); }
969 secdebug("rvc", "Not rechecking revocation");
973 /* Setup things so we check revocation status of all certs. */
974 SecCertificatePathVCAllocateRVCs(path
, certCount
);
976 /* Note that if we are multi threaded and a job completes after it
977 is started but before we return from this function, we don't want
978 a callback to decrement asyncJobCount to zero before we finish issuing
979 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
980 and decrement pvc->asyncJobCount for each cert that we don't start a
981 background fetch for. (We will never start an async job for the final
982 cert in the chain.) */
984 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
-1));
986 /* If we enable CRLS, we may end up with two async jobs per cert: one
987 * for OCSP and one for fetching the CRL */
988 SecPathBuilderSetAsyncJobCount(builder
, 2 * (unsigned int)(certCount
-1));
991 /* Loop though certificates again and issue an ocsp fetch if the
992 revocation status checking isn't done yet (and we have an issuer!) */
993 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
994 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
995 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1000 SecRVCInit(rvc
, builder
, certIX
);
1005 #if !TARGET_OS_BRIDGE
1006 /* Check valid database first (separate from OCSP response cache) */
1007 if (SecRVCCheckValidInfoDatabase(rvc
)) {
1008 SecRVCProcessValidInfoResults(rvc
);
1011 /* Any other revocation method requires an issuer certificate;
1012 * skip the last cert in the chain since it doesn't have one. */
1013 if (certIX
+1 >= certCount
) {
1017 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1018 * policy specifically requested CRLs only. */
1019 if (SecRVCShouldCheckOCSP(rvc
)) {
1020 /* If we have any OCSP stapled responses, check those first */
1021 SecORVCProcessStapledResponses(rvc
->orvc
);
1024 #if TARGET_OS_BRIDGE
1025 /* The bridge has no writeable storage and no network. Nothing else we can
1031 /* Then check the caches for revocation results. */
1032 SecRVCCheckRevocationCaches(rvc
);
1034 /* The check is done if we found cached responses from either method. */
1040 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1044 /* If we got a cached response that is no longer valid (which can only be true for
1045 * revoked responses), let's try to get a fresher response even if no one asked.
1046 * This check resolves unrevocation events after the nextUpdate time. */
1047 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1049 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1050 async http request for this cert's revocation status, unless we already successfully checked
1051 the revocation status of this cert based on the cache or stapled responses. */
1052 bool allow_fetch
= SecPathBuilderCanAccessNetwork(builder
) &&
1053 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1054 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1055 bool fetch_done
= true;
1056 if (rvc
->done
|| !allow_fetch
) {
1057 /* We got a cache hit or we aren't allowed to access the network */
1058 SecRVCUpdatePVC(rvc
);
1059 /* We didn't really start any background jobs for this cert. */
1060 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1062 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1065 fetch_done
= SecRVCFetchNext(rvc
);
1068 /* We started at least one background fetch. */
1069 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
1074 /* Return false if we started any background jobs. */
1075 /* We can't just return !builder->asyncJobCount here, since if we started any
1076 jobs the completion callback will be called eventually and it will call
1077 SecPathBuilderStep(). If for some reason everything completed before we
1078 get here we still want the outer SecPathBuilderStep() to terminate so we
1079 keep track of whether we started any jobs and return false if so. */
1083 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1084 CFAbsoluteTime enu
= NULL_TIME
;
1085 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1086 enu
= rvc
->orvc
->nextUpdate
;
1088 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
1089 if (enu
== NULL_TIME
||
1090 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
1091 /* We didn't check OCSP or CRL next update time was sooner */
1092 enu
= crlNextUpdate
;