2 * Copyright (c) 2008-2019 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 "trust/trustd/SecTrustServer.h"
43 #include "trust/trustd/SecOCSPRequest.h"
44 #include "trust/trustd/SecOCSPResponse.h"
45 #include "trust/trustd/SecOCSPCache.h"
46 #include "trust/trustd/SecRevocationDb.h"
47 #include "trust/trustd/SecCertificateServer.h"
48 #include "trust/trustd/SecPolicyServer.h"
49 #include "trust/trustd/SecRevocationNetworking.h"
51 #include "trust/trustd/SecRevocationServer.h"
54 /********************************************************
55 ****************** OCSP RVC Functions ******************
56 ********************************************************/
57 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
58 const CFAbsoluteTime kSecOCSPResponseOnlineTTL
= 5.0 * 60.0;
59 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
61 static void SecORVCFinish(SecORVCRef orvc
) {
62 secdebug("alloc", "finish orvc %p", orvc
);
63 if (orvc
->ocspRequest
) {
64 SecOCSPRequestFinalize(orvc
->ocspRequest
);
65 orvc
->ocspRequest
= NULL
;
67 if (orvc
->ocspResponse
) {
68 SecOCSPResponseFinalize(orvc
->ocspResponse
);
69 orvc
->ocspResponse
= NULL
;
70 if (orvc
->ocspSingleResponse
) {
71 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
72 orvc
->ocspSingleResponse
= NULL
;
75 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
78 /* Process a verified ocsp response for a given cert. Return true if the
79 certificate status was obtained. */
80 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
83 switch (this->certStatus
) {
85 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
86 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
87 in the info dictionary. */
88 //cert.revokeCheckGood(true);
89 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
93 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
94 /* @@@ Mark cert as revoked (with reason) at revocation date in
95 the info dictionary, or perhaps we should use a different key per
96 reason? That way a client using exceptions can ignore some but
98 SInt32 reason
= this->crlReason
;
99 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
100 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
102 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
104 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
110 /* not an error, no per-cert status, nothing here */
111 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
115 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
116 (int)this->certStatus
, rvc
->certIX
);
124 void SecORVCUpdatePVC(SecORVCRef rvc
) {
125 if (rvc
->ocspSingleResponse
) {
126 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
128 if (rvc
->ocspResponse
) {
129 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
133 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
136 SecOCSPEvaluateCompleted(const void *userData
,
137 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
138 SecTrustResultType result
) {
139 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
141 Block_release(evaluated
);
145 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
146 __block
bool evaluated
= false;
147 bool trusted
= false;
148 if (!signers
|| !issuers
) {
152 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
153 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
154 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
155 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
156 CFRelease(ocspSigner
);
158 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
159 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
164 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->builder
);
165 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(NULL
, clientAuditToken
,
166 signers
, issuers
, true, false,
167 policies
, NULL
, NULL
, NULL
,
168 verifyTime
, NULL
, NULL
,
169 SecOCSPEvaluateCompleted
, completed
);
170 /* disable network access to avoid recursion */
171 SecPathBuilderSetCanAccessNetwork(oBuilder
, false);
173 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
174 SecPathBuilderStep(oBuilder
);
175 CFReleaseNull(clientAuditToken
);
176 CFReleaseNull(policies
);
178 /* verify the public key of the issuer signed the OCSP signer */
180 SecCertificateRef issuer
= NULL
, signer
= NULL
;
181 SecKeyRef issuerPubKey
= NULL
;
183 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
184 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
187 issuerPubKey
= SecCertificateCopyKey(issuer
);
189 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
192 secnotice("ocsp", "ocsp signer cert not signed by issuer");
194 CFReleaseNull(issuerPubKey
);
200 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
202 SecCertificatePathVCRef issuers
= SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc
->builder
), rvc
->certIX
+ 1);
203 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers
, 0)) : NULL
;
204 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
205 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
207 if (signer
&& signers
) {
208 if (issuer
&& CFEqual(signer
, issuer
)) {
209 /* We already know we trust issuer since it's the issuer of the
210 * cert we are verifying. */
211 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
215 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
217 CFMutableArrayRef signerCerts
= NULL
;
218 CFArrayRef issuerCerts
= NULL
;
220 /* Ensure the signer cert is the 0th cert for trust evaluation */
221 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
222 CFArrayAppendValue(signerCerts
, signer
);
223 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
226 issuerCerts
= SecCertificatePathVCCopyCertificates(issuers
);
229 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
230 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
234 /* @@@ We don't trust the cert so don't use this response. */
235 secnotice("ocsp", "ocsp response signed by certificate which "
236 "does not satisfy ocspSigner policy");
239 CFReleaseNull(signerCerts
);
240 CFReleaseNull(issuerCerts
);
243 /* @@@ No signer found for this ocsp response, discard it. */
244 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
249 #if DUMP_OCSPRESPONSES
251 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
252 rvc
->certIX
, (trusted
? "t" : "u"));
253 secdumpdata(ocspResponse
->data
, buf
);
255 CFReleaseNull(issuers
);
256 CFReleaseNull(issuer
);
257 CFReleaseNull(signers
);
258 CFReleaseNull(signer
);
262 void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/,
263 CFTimeInterval maxAge
, bool updateCache
, bool fromCache
) {
264 SecOCSPSingleResponseRef sr
= NULL
;
265 require_quiet(ocspResponse
, errOut
);
266 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
267 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
268 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
269 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
270 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
271 // Check if this response is fresher than any (cached) response we might still have in the rvc.
272 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
274 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
276 /* Check the OCSP response signature and verify the response if not pulled from the cache.
277 * Performance optimization since we don't write invalid responses to the cache. */
279 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
280 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
283 /* Always check the OCSP response signature and verify the response (since the cache is user-modifiable). */
284 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
285 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
288 // If we get here, we have a properly signed ocsp response
289 // but we haven't checked dates yet.
291 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
293 rvc
->rvc
->revocation_checked
= true;
295 if (sr
->certStatus
== CS_Good
) {
296 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
297 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
298 } else if (sr
->certStatus
== CS_Revoked
) {
299 // Expire revoked responses when the subject certificate itself expires.
300 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
));
303 // Ok we like the new response, let's toss the old one.
305 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
307 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
308 rvc
->ocspResponse
= ocspResponse
;
311 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
312 rvc
->ocspSingleResponse
= sr
;
315 rvc
->done
= sr_valid
;
318 if (sr
) SecOCSPSingleResponseDestroy(sr
);
319 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
322 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
323 SecORVCRef orvc
= NULL
;
324 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
325 secdebug("alloc", "orvc %p", orvc
);
327 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
328 orvc
->builder
= builder
;
330 orvc
->certIX
= certIX
;
332 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
333 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
334 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
335 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
341 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
342 /* Get stapled OCSP responses */
343 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
345 if(ocspResponsesData
) {
346 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
347 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
348 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
349 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false, false);
351 CFRelease(ocspResponsesData
);
355 void SecRVCDelete(SecRVCRef rvc
) {
356 secdebug("alloc", "delete rvc %p", rvc
);
358 SecORVCFinish(rvc
->orvc
);
362 if (rvc
->valid_info
) {
363 CFReleaseNull(rvc
->valid_info
);
367 // Forward declaration
368 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
);
370 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
371 secdebug("alloc", "rvc %p", rvc
);
372 rvc
->builder
= builder
;
373 rvc
->certIX
= certIX
;
374 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
377 SecRVCSetFinishedWithoutNetwork(rvc
);
383 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
387 static bool SecRVCPolicyConstraintsPermitPolicy(SecValidPolicy
*constraints
, CFIndex count
, SecPolicyRef policy
) {
388 if (!constraints
|| !policy
) {
389 return true; /* nothing to constrain */
391 SecValidPolicy policyType
= kSecValidPolicyAny
;
392 CFStringRef policyName
= SecPolicyGetName(policy
);
393 /* determine if the policy is a candidate for being constrained */
394 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
) ||
395 CFEqualSafe(policyName
, kSecPolicyNameEAPServer
) ||
396 CFEqualSafe(policyName
, kSecPolicyNameIPSecServer
)) {
397 policyType
= kSecValidPolicyServerAuthentication
;
398 } else if (CFEqualSafe(policyName
, kSecPolicyNameSSLClient
) ||
399 CFEqualSafe(policyName
, kSecPolicyNameEAPClient
) ||
400 CFEqualSafe(policyName
, kSecPolicyNameIPSecClient
)) {
401 policyType
= kSecValidPolicyClientAuthentication
;
402 } else if (CFEqualSafe(policyName
, kSecPolicyNameSMIME
)) {
403 policyType
= kSecValidPolicyEmailProtection
;
404 } else if (CFEqualSafe(policyName
, kSecPolicyNameCodeSigning
)) {
405 policyType
= kSecValidPolicyCodeSigning
;
406 } else if (CFEqualSafe(policyName
, kSecPolicyNameTimeStamping
)) {
407 policyType
= kSecValidPolicyTimeStamping
;
409 if (policyType
== kSecValidPolicyAny
) {
410 return true; /* policy not subject to constraint */
412 /* policy is subject to constraint; do the constraints allow it? */
414 for (CFIndex ix
= 0; ix
< count
; ix
++) {
415 SecValidPolicy allowedPolicy
= constraints
[ix
];
416 if (allowedPolicy
== kSecValidPolicyAny
||
417 allowedPolicy
== policyType
) {
423 secnotice("rvc", "%@ not allowed by policy constraints on issuing CA", policyName
);
428 static bool SecRVCGetPolicyConstraints(CFDataRef data
, SecValidPolicy
**constraints
, CFIndex
*count
) {
429 /* Sanity-check the input policy constraints data, returning pointer and
430 * count values in output arguments. Function result is true if successful.
432 * The first byte of the policy constraints data contains the number of entries,
433 * followed by an array of 0..n policy constraint values of type SecValidPolicy.
434 * The maximum number of defined policies is not expected to approach 127, i.e.
435 * the largest value which can be expressed in a signed byte.
439 SecValidPolicy
*p
= NULL
;
441 length
= CFDataGetLength(data
);
442 p
= (SecValidPolicy
*)CFDataGetBytePtr(data
);
444 /* Verify that count is 0 or greater, and equal to remaining number of bytes */
445 CFIndex c
= (length
> 0) ? *p
++ : -1;
446 if (c
< 0 || c
!= (length
- 1)) {
447 secerror("invalid policy constraints array");
460 static void SecRVCProcessValidPolicyConstraints(SecRVCRef rvc
) {
461 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
464 if (!rvc
->valid_info
->hasPolicyConstraints
) {
468 SecValidPolicy
*constraints
= NULL
;
469 if (!SecRVCGetPolicyConstraints(rvc
->valid_info
->policyConstraints
, &constraints
, &count
)) {
472 secdebug("rvc", "found policy constraints for cert at index %ld", rvc
->certIX
);
474 /* check that policies being verified are permitted by the policy constraints */
475 bool policyDeniedByConstraints
= false;
476 CFIndex ix
, initialPVCCount
= SecPathBuilderGetPVCCount(rvc
->builder
);
477 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
478 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, ix
);
479 CFArrayRef policies
= CFRetainSafe(pvc
->policies
);
480 CFIndex policyCount
= (policies
) ? CFArrayGetCount(policies
) : 0;
481 for (CFIndex policyIX
= 0; policyIX
< policyCount
; policyIX
++) {
482 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
483 if (!SecRVCPolicyConstraintsPermitPolicy(constraints
, count
, policy
)) {
484 policyDeniedByConstraints
= true;
485 if (rvc
->valid_info
->overridable
) {
486 SecPVCSetResultForcedWithTrustResult(pvc
, kSecPolicyCheckIssuerPolicyConstraints
, rvc
->certIX
,
487 kCFBooleanFalse
, true, kSecTrustResultRecoverableTrustFailure
);
489 SecPVCSetResultForced(pvc
, kSecPolicyCheckIssuerPolicyConstraints
, rvc
->certIX
, kCFBooleanFalse
, true);
493 CFReleaseSafe(policies
);
495 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
497 TAValidStatus status
= (policyDeniedByConstraints
) ? TAValidPolicyConstrainedDenied
: TAValidPolicyConstrainedOK
;
498 analytics
->valid_status
|= status
;
502 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc
) {
503 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
506 if (!rvc
->valid_info
->hasDateConstraints
) {
509 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
513 CFAbsoluteTime certIssued
= SecCertificateNotValidBefore(certificate
);
514 CFAbsoluteTime caNotBefore
= -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
515 CFAbsoluteTime caNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
516 if (rvc
->valid_info
->notBeforeDate
) {
517 caNotBefore
= CFDateGetAbsoluteTime(rvc
->valid_info
->notBeforeDate
);
519 if (rvc
->valid_info
->notAfterDate
) {
520 caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
521 /* per the Valid specification, if this date is in the past, we need to check CT. */
522 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
523 if (caNotAfter
< now
) {
524 rvc
->valid_info
->requireCT
= true;
527 if ((certIssued
< caNotBefore
) && (rvc
->certIX
> 0)) {
528 /* not-before constraint is only applied to leaf certificate, for now. */
532 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
533 if ((certIssued
< caNotBefore
) || (certIssued
> caNotAfter
)) {
534 /* We are outside the constrained validity period. */
535 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
536 (rvc
->valid_info
->overridable
) ? "" : " (non-recoverable error)");
538 analytics
->valid_status
|= TAValidDateConstrainedRevoked
;
540 if (rvc
->valid_info
->overridable
) {
541 /* error is recoverable, treat certificate as untrusted
542 (note this date check is different from kSecPolicyCheckTemporalValidity) */
543 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
544 kCFBooleanFalse
, true);
546 /* error is non-overridable, treat certificate as revoked */
547 SInt32 reason
= 0; /* unspecified reason code */
548 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
549 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
551 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
553 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
555 CFReleaseNull(cfreason
);
557 } else if (analytics
) {
558 analytics
->valid_status
|= TAValidDateConstrainedOK
;
562 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc
) {
563 if (!rvc
|| !rvc
->valid_info
) {
566 SecValidInfoRef info
= rvc
->valid_info
;
567 /* outcomes as defined in Valid server specification */
568 if (info
->format
== kSecValidInfoFormatSerial
||
569 info
->format
== kSecValidInfoFormatSHA256
) {
570 if (info
->noCACheck
|| info
->complete
|| info
->isOnList
) {
573 } else { /* info->format == kSecValidInfoFormatNto1 */
574 if (info
->noCACheck
|| (info
->complete
&& !info
->isOnList
)) {
581 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc
) {
582 if (!rvc
|| !rvc
->valid_info
) {
585 SecValidInfoRef info
= rvc
->valid_info
;
586 /* either not present on an allowlist, or present on a blocklist */
587 return (!info
->isOnList
&& info
->valid
) || (info
->isOnList
&& !info
->valid
);
590 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc
) {
591 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
594 if (rvc
->valid_info
->overridable
) {
595 /* error is recoverable, treat certificate as untrusted */
596 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedLeaf
, rvc
->certIX
,
597 kCFBooleanFalse
, true);
600 /* error is fatal at this point */
601 if (!SecRVCHasRevokedValidInfo(rvc
) || rvc
->valid_info
->noCACheck
) {
602 /* result key should indicate blocked instead of revoked,
603 * but result must be non-recoverable */
604 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckBlackListedLeaf
, rvc
->certIX
,
605 kCFBooleanFalse
, true);
608 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
609 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
610 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
612 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
614 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
616 CFReleaseNull(cfreason
);
619 bool SecRVCRevocationChecked(SecRVCRef rvc
) {
620 return rvc
->revocation_checked
;
623 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
624 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
627 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
628 SecValidInfoRef info
= rvc
->valid_info
;
630 bool definitive
= SecRVCHasDefinitiveValidInfo(rvc
);
631 bool revoked
= SecRVCHasRevokedValidInfo(rvc
);
634 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
637 analytics
->valid_status
|= definitive
? TAValidDefinitelyRevoked
: TAValidProbablyRevoked
;
639 analytics
->valid_status
|= definitive
? TAValidDefinitelyOK
: TAValidProbablyOK
;
641 analytics
->valid_require_ct
|= info
->requireCT
;
642 analytics
->valid_known_intermediates_only
|= info
->knownOnly
;
645 /* Handle no-ca cases */
646 if (info
->noCACheck
) {
647 bool allowed
= (info
->valid
&& info
->complete
&& info
->isOnList
);
649 /* definitely revoked */
650 SecRVCSetValidDeterminedErrorResult(rvc
);
651 } else if (allowed
) {
652 /* definitely not revoked (allowlisted) */
653 SecCertificatePathVCSetIsAllowlisted(path
, true);
654 rvc
->revocation_checked
= true;
656 /* no-ca is definitive; no need to check further. */
657 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
658 (allowed
) ? "allowed" : "revoked", rvc
->certIX
);
663 /* Handle policy constraints, if present. */
664 SecRVCProcessValidPolicyConstraints(rvc
);
666 /* Handle date constraints, if present.
667 * Note: a not-after date may set the CT requirement,
668 * so check requireCT after this function is called. */
669 SecRVCProcessValidDateConstraints(rvc
);
671 /* Set CT requirement on path, if present. */
672 if (info
->requireCT
) {
673 SecPathCTPolicy ctp
= kSecPathCTRequired
;
674 if (info
->overridable
) {
675 ctp
= kSecPathCTRequiredOverridable
;
677 SecCertificatePathVCSetRequiresCT(path
, ctp
);
680 /* Trigger OCSP for any non-definitive or revoked cases */
681 if (!definitive
|| revoked
) {
682 info
->checkOCSP
= true;
685 if (info
->checkOCSP
) {
686 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
687 CFIndex issuerIX
= rvc
->certIX
+ 1;
688 if (issuerIX
>= count
) {
689 /* cannot perform a revocation check on the last cert in the
690 chain, since we don't have its issuer. */
693 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
694 (info
->complete
) ? "" : "possibly ", (info
->valid
) ? "allowed" : "revoked",
696 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
698 /* Valid DB results caused us to do OCSP */
699 analytics
->valid_trigger_ocsp
= true;
704 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
705 /* Skip checking for OCSP Signer verification */
706 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
707 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
708 if (!pvc
) { return false; }
709 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
710 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
711 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
716 /* Make sure revocation db info is up-to-date.
717 * We don't care if the builder is allowed to access the network because
718 * the network fetching does not block the trust evaluation. */
719 SecRevocationDbCheckNextUpdate();
721 /* Check whether we have valid db info for this cert,
722 given the cert and its issuer */
723 SecValidInfoRef info
= NULL
;
724 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
726 bool isSelfSigned
= false;
727 SecCertificateRef cert
= NULL
;
728 SecCertificateRef issuer
= NULL
;
729 CFIndex issuerIX
= rvc
->certIX
+ 1;
730 if (count
> issuerIX
) {
731 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
732 } else if (count
== issuerIX
) {
733 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
734 if (rootIX
== rvc
->certIX
) {
735 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
739 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
741 /* skip revocation db check for self-signed certificates [33137065] */
742 info
= SecRevocationDbCopyMatching(cert
, issuer
);
744 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
747 SecValidInfoRef old_info
= rvc
->valid_info
;
748 rvc
->valid_info
= info
;
750 CFReleaseNull(old_info
);
757 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
758 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
759 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
760 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
761 SecOCSPResponseRef response
= NULL
;
762 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
763 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
764 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
766 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
768 SecORVCConsumeOCSPResponse(rvc
->orvc
, response
, NULL_TIME
, false, true);
769 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
770 if (rvc
->orvc
->done
&& analytics
) {
771 /* We found a valid OCSP response in the cache */
772 analytics
->ocsp_cache_hit
= true;
777 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
778 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
779 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
782 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
) {
784 SecRVCUpdatePVC(rvc
);
785 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
788 static bool SecRVCFetchNext(SecRVCRef rvc
) {
789 bool OCSP_fetch_finished
= true;
790 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
791 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
792 if (SecRVCShouldCheckOCSP(rvc
)) {
793 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
794 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
795 OCSP_fetch_finished
= SecORVCBeginFetches(rvc
->orvc
, cert
);
796 if (analytics
&& !OCSP_fetch_finished
) {
797 /* We did a network OCSP fetch, set report appropriately */
798 analytics
->ocsp_network
= true;
801 if (OCSP_fetch_finished
) {
802 /* we didn't start an OCSP background job for this cert */
803 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
805 return OCSP_fetch_finished
;
808 /* The SecPathBuilder state machine calls SecPathBuilderCheckRevocation twice --
809 * once in the ValidatePath state, and again in the ComputeDetails state. In the
810 * ValidatePath state we've not yet run the path checks, so for callers who set
811 * kSecRevocationCheckIfTrusted, we don't do any networking on that first call.
812 * Here, if we've already done revocation before (so we're in ComputeDetails now),
813 * we need to recheck (and enable networking) for trusted chains and
814 * kSecRevocationCheckIfTrusted. Otherwise, we skip the checks to save on the processing
815 * but update the PVCs with the revocation results from the last check. */
816 static bool SecRevocationDidCheckRevocation(SecPathBuilderRef builder
, bool *first_check_done
) {
817 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
818 if (!SecCertificatePathVCIsRevocationDone(path
)) {
821 if (first_check_done
) {
822 *first_check_done
= true;
825 SecPVCRef resultPVC
= SecPathBuilderGetResultPVC(builder
);
826 bool recheck
= false;
827 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
) && SecPVCIsOkResult(resultPVC
)) {
829 secdebug("rvc", "Rechecking revocation because network now allowed");
831 secdebug("rvc", "Not rechecking revocation");
835 // reset the RVCs for the second pass
836 SecCertificatePathVCDeleteRVCs(path
);
838 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
839 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
840 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
842 SecRVCUpdatePVC(rvc
);
850 static bool SecRevocationCanAccessNetwork(SecPathBuilderRef builder
, bool first_check_done
) {
851 /* CheckRevocationIfTrusted overrides NoNetworkAccess for revocation */
852 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
)) {
853 if (first_check_done
) {
854 /* We're on the second pass. We need to now allow networking for revocation.
855 * SecRevocationDidCheckRevocation takes care of not running a second pass
856 * if the chain isn't trusted. */
859 /* We're on the first pass of the revocation checks, where we aren't
860 * supposed to do networking because we don't know if the chain
865 return SecPathBuilderCanAccessNetwork(builder
);
868 void SecPathBuilderCheckKnownIntermediateConstraints(SecPathBuilderRef builder
) {
869 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
873 /* only perform this check once per path! */
874 CFIndex certIX
= kCFNotFound
;
875 if (SecCertificatePathVCCheckedIssuers(path
)) {
876 certIX
= SecCertificatePathVCUnknownCAIndex(path
);
879 /* check full path: start with anchor and decrement to leaf */
880 bool parentConstrained
= false;
881 CFIndex certCount
= SecPathBuilderGetCertificateCount(builder
);
882 for (certIX
= certCount
- 1; certIX
>= 0; --certIX
) {
883 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
887 if (parentConstrained
&& !rvc
->valid_info
) {
888 /* Parent had the known-only constraint, but our issuer is unknown.
889 Bump index to point back at the issuer since it fails the constraint. */
893 parentConstrained
= (rvc
->valid_info
&& rvc
->valid_info
->knownOnly
);
894 if (parentConstrained
) {
895 secdebug("validupdate", "Valid db found a known-intermediate constraint on %@ (index=%ld)",
896 rvc
->valid_info
->issuerHash
, certIX
+1);
898 /* check special case: unknown constrained CA in leaf position */
899 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
900 if (cert
&& SecCertificateIsCA(cert
) && !SecRevocationDbContainsIssuer(cert
)) {
901 /* leaf is a CA which violates the constraint */
907 /* At this point, certIX will either be -1, indicating no CA was found
908 which failed a known-intermediates-only constraint on its parent, or it
909 will be the index of the first unknown CA which fails the constraint. */
911 secnotice("validupdate", "CA at index %ld violates known-intermediate constraint", certIX
);
912 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
914 analytics
->valid_unknown_intermediate
= true;
917 SecCertificatePathVCSetUnknownCAIndex(path
, certIX
);
918 SecCertificatePathVCSetCheckedIssuers(path
, true);
922 /* Error is set on CA certificate which failed the constraint. */
923 SecRVCSetValidDeterminedErrorResult(SecCertificatePathVCGetRVCAtIndex(path
, certIX
));
927 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
928 secdebug("rvc", "checking revocation");
929 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
930 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
931 if (certCount
<= 1) {
932 /* Can't verify without an issuer; we're done */
936 bool first_check_done
= false;
937 if (SecRevocationDidCheckRevocation(builder
, &first_check_done
)) {
941 /* Setup things so we check revocation status of all certs. */
942 SecCertificatePathVCAllocateRVCs(path
, certCount
);
944 /* Note that if we are multi threaded and a job completes after it
945 is started but before we return from this function, we don't want
946 a callback to decrement asyncJobCount to zero before we finish issuing
947 all the jobs. To avoid this we pretend we issued certCount async jobs,
948 and decrement pvc->asyncJobCount for each cert that we don't start a
949 background fetch for. We include the root, even though we'll never start
950 an async job for it so that we count all active threads for this eval. */
951 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
));
953 /* Loop though certificates again and issue an ocsp fetch if the
954 revocation status checking isn't done yet (and we have an issuer!) */
955 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
956 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
957 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
962 SecRVCInit(rvc
, builder
, certIX
);
964 /* RFC 6960: id-pkix-ocsp-nocheck extension says that we shouldn't check revocation. */
965 if (SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path
, certIX
)))
967 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX
);
968 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
970 /* This certificate has OCSP No-Check, so add to reporting analytics */
971 analytics
->ocsp_no_check
= true;
973 SecRVCSetFinishedWithoutNetwork(rvc
);
980 #if !TARGET_OS_BRIDGE
981 /* Check valid database first (separate from OCSP response cache) */
982 if (SecRVCCheckValidInfoDatabase(rvc
)) {
983 SecRVCProcessValidInfoResults(rvc
);
986 /* Any other revocation method requires an issuer certificate to verify the response;
987 * skip the last cert in the chain since it doesn't have one. */
988 if (certIX
+ 1 >= certCount
) {
992 /* Ignore stapled OCSP responses only if CRLs are enabled and the
993 * policy specifically requested CRLs only. */
994 if (SecRVCShouldCheckOCSP(rvc
)) {
995 /* If we have any OCSP stapled responses, check those first */
996 SecORVCProcessStapledResponses(rvc
->orvc
);
1000 /* The bridge has no writeable storage and no network. Nothing else we can
1002 SecRVCSetFinishedWithoutNetwork(rvc
);
1004 #else // !TARGET_OS_BRIDGE
1005 /* Then check the caches for revocation results. */
1006 SecRVCCheckRevocationCaches(rvc
);
1008 /* The check is done if we found cached responses from either method. */
1009 if (rvc
->done
|| rvc
->orvc
->done
) {
1010 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1011 SecRVCSetFinishedWithoutNetwork(rvc
);
1015 /* If we got a cached response that is no longer valid (which can only be true for
1016 * revoked responses), let's try to get a fresher response even if no one asked.
1017 * This check resolves unrevocation events after the nextUpdate time. */
1018 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1020 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1021 async http request for this cert's revocation status, unless we already successfully checked
1022 the revocation status of this cert based on the cache or stapled responses. */
1023 bool allow_fetch
= SecRevocationCanAccessNetwork(builder
, first_check_done
) &&
1024 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1025 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1026 if (rvc
->done
|| !allow_fetch
) {
1027 /* We got a cache hit or we aren't allowed to access the network */
1028 SecRVCUpdatePVC(rvc
);
1029 /* We didn't really start any background jobs for this cert. */
1030 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1032 (void)SecRVCFetchNext(rvc
);
1034 #endif // !TARGET_OS_BRIDGE
1037 /* Return false if there are still async jobs running. */
1038 /* builder->asyncJobCount is atomic, so we know that if the job count is 0, all other
1039 * threads are finished. If the job count is > 0, other threads will decrement the job
1040 * count and SecPathBuilderStep to crank the state machine when the job count is 0. */
1041 return (SecPathBuilderDecrementAsyncJobCount(builder
) == 0);
1044 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1045 CFAbsoluteTime enu
= NULL_TIME
;
1046 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1047 enu
= rvc
->orvc
->nextUpdate
;