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
);
701 SecCRVCFinish(rvc
->crvc
);
706 if (rvc
->valid_info
) {
707 SecValidInfoRelease(rvc
->valid_info
);
708 rvc
->valid_info
= NULL
;
712 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
713 secdebug("alloc", "%p", rvc
);
714 rvc
->builder
= builder
;
715 rvc
->certIX
= certIX
;
716 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
718 rvc
->crvc
= SecCRVCCreate(rvc
, builder
, certIX
);
733 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
734 CFStringRef revocation_method
= SecPathBuilderGetRevocationMethod(rvc
->builder
);
735 if (!revocation_method
736 || !CFEqual(revocation_method
, kSecPolicyCheckRevocationCRL
)) {
742 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
747 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
748 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
751 SecValidInfoFormat format
= rvc
->valid_info
->format
;
752 bool valid
= rvc
->valid_info
->valid
;
753 bool noCACheck
= rvc
->valid_info
->noCACheck
;
754 bool checkOCSP
= rvc
->valid_info
->checkOCSP
;
755 bool complete
= rvc
->valid_info
->complete
;
756 bool isOnList
= rvc
->valid_info
->isOnList
;
757 bool definitive
= false;
759 if (format
== kSecValidInfoFormatSerial
|| format
== kSecValidInfoFormatSHA256
) {
760 /* serial or hash list: could be blocked or allowed; could be incomplete */
761 if (((!valid
&& complete
&& isOnList
) || (valid
&& complete
&& !isOnList
)) && noCACheck
) {
762 /* definitely revoked */
763 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
764 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
765 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
766 cfreason
, true, kSecTrustResultFatalTrustFailure
);
767 CFMutableDictionaryRef info
= SecPathBuilderGetInfo(rvc
->builder
);
769 /* make the revocation reason available in the trust result */
770 CFDictionarySetValue(info
, kSecTrustRevocationReason
, cfreason
);
772 CFReleaseNull(cfreason
);
775 else if (valid
&& complete
&& isOnList
&& noCACheck
) {
776 /* definitely not revoked (allowlisted) */
777 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
779 SecCertificatePathVCSetIsAllowlisted(path
, true);
781 secdebug("validupdate", "rvc: no certificate path for builder");
786 /* either definitely revoked or allowed; no need to check further. */
787 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
788 (valid
&& complete
&& isOnList
) ? "allowed" : "revoked", rvc
->certIX
);
792 /* verify our info with the OCSP server */
796 /* Handle non-definitive information.
797 We set rvc->done = true above ONLY if the result was definitive;
798 otherwise we require a revocation check for SSL usage.
800 if (format
== kSecValidInfoFormatNto1
) {
801 /* matched the filter */
806 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
807 CFIndex issuerIX
= rvc
->certIX
+ 1;
808 if (issuerIX
>= count
) {
809 /* cannot perform a revocation check on the last cert in the
810 chain, since we don't have its issuer. */
814 for (pvcIX
= 0; pvcIX
< SecPathBuilderGetPVCCount(rvc
->builder
); pvcIX
++) {
815 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, pvcIX
);
816 if (!pvc
) { continue; }
817 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
818 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
819 if (policyName
&& CFEqual(CFSTR("sslServer"), policyName
)) {
820 /* perform revocation check for SSL policy;
821 require for leaf if an OCSP responder is present. */
822 if (0 == rvc
->certIX
) {
823 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
824 CFArrayRef resps
= (cert
) ? SecCertificateGetOCSPResponders(cert
) : NULL
;
825 CFIndex rcount
= (resps
) ? CFArrayGetCount(resps
) : 0;
827 // %%% rdar://31279923
828 // This currently requires a valid revocation response for each cert,
829 // but we only want to require a leaf check. For now, do not require.
830 //SecPathBuilderSetRevocationResponseRequired(rvc->builder);
833 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
834 (complete
) ? "" : "possibly ", (valid
) ? "allowed" : "revoked",
836 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
842 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
843 /* Skip checking for OCSP Signer verification */
844 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
845 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
846 if (!pvc
) { return false; }
847 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
848 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
849 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
854 /* Make sure revocation db info is up-to-date.
855 * We don't care if the builder is allowed to access the network because
856 * the network fetching does not block the trust evaluation. */
857 SecRevocationDbCheckNextUpdate();
859 /* Check whether we have valid db info for this cert,
860 given the cert and its issuer */
861 SecValidInfoRef info
= NULL
;
862 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
864 bool isSelfSigned
= false;
865 SecCertificateRef cert
= NULL
;
866 SecCertificateRef issuer
= NULL
;
867 CFIndex issuerIX
= rvc
->certIX
+ 1;
868 if (count
> issuerIX
) {
869 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
870 } else if (count
== issuerIX
) {
871 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
872 if (rootIX
== rvc
->certIX
) {
873 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
877 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
879 /* skip revocation db check for self-signed certificates [33137065] */
880 info
= SecRevocationDbCopyMatching(cert
, issuer
);
882 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
885 SecValidInfoRef old_info
= rvc
->valid_info
;
886 rvc
->valid_info
= info
;
888 SecValidInfoRelease(old_info
);
895 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
896 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
897 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
898 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
899 SecOCSPResponseRef response
= NULL
;
900 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
901 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
902 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
904 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
906 SecORVCConsumeOCSPResponse(rvc
->orvc
,
911 /* Don't check CRL cache if policy requested OCSP only */
912 if (SecRVCShouldCheckCRL(rvc
)) {
913 SecCRVCCheckRevocationCache(rvc
->crvc
);
918 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
919 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
920 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
922 if (rvc
->crvc
) { SecCRVCUpdatePVC(rvc
->crvc
); }
926 static bool SecRVCFetchNext(SecRVCRef rvc
) {
927 bool OCSP_fetch_finished
= true;
928 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
929 if (SecRVCShouldCheckOCSP(rvc
)) {
930 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
932 if (OCSP_fetch_finished
) {
933 /* we didn't start an OCSP background job for this cert */
934 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
938 bool CRL_fetch_finished
= true;
939 /* Don't check CRL cache if policy requested OCSP only */
940 if (SecRVCShouldCheckCRL(rvc
)) {
941 /* reset the distributionPointIX because we already iterated through the CRLDPs
942 * in SecCRVCCheckRevocationCache */
943 rvc
->crvc
->distributionPointIX
= 0;
944 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
946 if (CRL_fetch_finished
) {
947 /* we didn't start a CRL background job for this cert */
948 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
950 OCSP_fetch_finished
&= CRL_fetch_finished
;
953 return OCSP_fetch_finished
;
956 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
957 secdebug("rvc", "checking revocation");
958 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
959 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
960 bool completed
= true;
961 if (certCount
<= 1) {
962 /* Can't verify without an issuer; we're done */
967 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
969 * Note: we can't check revocation for the last certificate in the chain
970 * via OCSP or CRL methods, since there isn't a separate issuer cert to
971 * sign those responses. However, since a self-signed root has an implied
972 * issuer of itself, we can check for it in the valid database.
975 if (SecCertificatePathVCIsRevocationDone(path
)) {
976 /* We have done revocation checking already, set PVCs with results. */
977 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
978 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
979 if (rvc
) { SecRVCUpdatePVC(rvc
); }
981 secdebug("rvc", "Not rechecking revocation");
985 /* Setup things so we check revocation status of all certs. */
986 SecCertificatePathVCAllocateRVCs(path
, certCount
);
988 /* Note that if we are multi threaded and a job completes after it
989 is started but before we return from this function, we don't want
990 a callback to decrement asyncJobCount to zero before we finish issuing
991 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
992 and decrement pvc->asyncJobCount for each cert that we don't start a
993 background fetch for. (We will never start an async job for the final
994 cert in the chain.) */
996 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
-1));
998 /* If we enable CRLS, we may end up with two async jobs per cert: one
999 * for OCSP and one for fetching the CRL */
1000 SecPathBuilderSetAsyncJobCount(builder
, 2 * (unsigned int)(certCount
-1));
1003 /* Loop though certificates again and issue an ocsp fetch if the
1004 revocation status checking isn't done yet (and we have an issuer!) */
1005 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
1006 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
1007 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
1012 SecRVCInit(rvc
, builder
, certIX
);
1017 #if !TARGET_OS_BRIDGE
1018 /* Check valid database first (separate from OCSP response cache) */
1019 if (SecRVCCheckValidInfoDatabase(rvc
)) {
1020 SecRVCProcessValidInfoResults(rvc
);
1023 /* Any other revocation method requires an issuer certificate;
1024 * skip the last cert in the chain since it doesn't have one. */
1025 if (certIX
+1 >= certCount
) {
1029 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1030 * policy specifically requested CRLs only. */
1031 if (SecRVCShouldCheckOCSP(rvc
)) {
1032 /* If we have any OCSP stapled responses, check those first */
1033 SecORVCProcessStapledResponses(rvc
->orvc
);
1036 #if TARGET_OS_BRIDGE
1037 /* The bridge has no writeable storage and no network. Nothing else we can
1043 /* Then check the caches for revocation results. */
1044 SecRVCCheckRevocationCaches(rvc
);
1046 /* The check is done if we found cached responses from either method. */
1052 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1056 /* If we got a cached response that is no longer valid (which can only be true for
1057 * revoked responses), let's try to get a fresher response even if no one asked.
1058 * This check resolves unrevocation events after the nextUpdate time. */
1059 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1061 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1062 async http request for this cert's revocation status, unless we already successfully checked
1063 the revocation status of this cert based on the cache or stapled responses. */
1064 bool allow_fetch
= SecPathBuilderCanAccessNetwork(builder
) &&
1065 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1066 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1067 bool fetch_done
= true;
1068 if (rvc
->done
|| !allow_fetch
) {
1069 /* We got a cache hit or we aren't allowed to access the network */
1070 SecRVCUpdatePVC(rvc
);
1071 /* We didn't really start any background jobs for this cert. */
1072 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1074 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1077 fetch_done
= SecRVCFetchNext(rvc
);
1080 /* We started at least one background fetch. */
1081 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
1086 /* Return false if we started any background jobs. */
1087 /* We can't just return !builder->asyncJobCount here, since if we started any
1088 jobs the completion callback will be called eventually and it will call
1089 SecPathBuilderStep(). If for some reason everything completed before we
1090 get here we still want the outer SecPathBuilderStep() to terminate so we
1091 keep track of whether we started any jobs and return false if so. */
1095 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1096 CFAbsoluteTime enu
= NULL_TIME
;
1097 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1098 enu
= rvc
->orvc
->nextUpdate
;
1100 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
1101 if (enu
== NULL_TIME
||
1102 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
1103 /* We didn't check OCSP or CRL next update time was sooner */
1104 enu
= crlNextUpdate
;