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 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
289 if (analytics
&& SecOCSPResponseIsWeakHash(ocspResponse
)) {
290 analytics
->ocsp_weak_hash
= true;
293 // If we get here, we have a properly signed ocsp response
294 // but we haven't checked dates yet.
296 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
298 rvc
->rvc
->revocation_checked
= true;
300 if (sr
->certStatus
== CS_Good
) {
301 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
302 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
303 } else if (sr
->certStatus
== CS_Revoked
) {
304 // Expire revoked responses when the subject certificate itself expires.
305 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
));
308 // Ok we like the new response, let's toss the old one.
310 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
312 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
313 rvc
->ocspResponse
= ocspResponse
;
316 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
317 rvc
->ocspSingleResponse
= sr
;
320 rvc
->done
= sr_valid
;
323 if (sr
) SecOCSPSingleResponseDestroy(sr
);
324 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
327 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
328 SecORVCRef orvc
= NULL
;
329 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
330 secdebug("alloc", "orvc %p", orvc
);
332 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
333 orvc
->builder
= builder
;
335 orvc
->certIX
= certIX
;
337 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
338 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
339 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
340 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
346 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
347 /* Get stapled OCSP responses */
348 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
350 if(ocspResponsesData
) {
351 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
352 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
353 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
354 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false, false);
356 CFRelease(ocspResponsesData
);
360 void SecRVCDelete(SecRVCRef rvc
) {
361 secdebug("alloc", "delete rvc %p", rvc
);
363 SecORVCFinish(rvc
->orvc
);
367 if (rvc
->valid_info
) {
368 CFReleaseNull(rvc
->valid_info
);
372 // Forward declaration
373 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
);
375 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
376 secdebug("alloc", "rvc %p", rvc
);
377 rvc
->builder
= builder
;
378 rvc
->certIX
= certIX
;
379 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
382 SecRVCSetFinishedWithoutNetwork(rvc
);
388 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
392 static bool SecRVCPolicyConstraintsPermitPolicy(SecValidPolicy
*constraints
, CFIndex count
, SecPolicyRef policy
) {
393 if (!constraints
|| !policy
) {
394 return true; /* nothing to constrain */
396 SecValidPolicy policyType
= kSecValidPolicyAny
;
397 CFStringRef policyName
= SecPolicyGetName(policy
);
398 /* determine if the policy is a candidate for being constrained */
399 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
) ||
400 CFEqualSafe(policyName
, kSecPolicyNameEAPServer
) ||
401 CFEqualSafe(policyName
, kSecPolicyNameIPSecServer
)) {
402 policyType
= kSecValidPolicyServerAuthentication
;
403 } else if (CFEqualSafe(policyName
, kSecPolicyNameSSLClient
) ||
404 CFEqualSafe(policyName
, kSecPolicyNameEAPClient
) ||
405 CFEqualSafe(policyName
, kSecPolicyNameIPSecClient
)) {
406 policyType
= kSecValidPolicyClientAuthentication
;
407 } else if (CFEqualSafe(policyName
, kSecPolicyNameSMIME
)) {
408 policyType
= kSecValidPolicyEmailProtection
;
409 } else if (CFEqualSafe(policyName
, kSecPolicyNameCodeSigning
)) {
410 policyType
= kSecValidPolicyCodeSigning
;
411 } else if (CFEqualSafe(policyName
, kSecPolicyNameTimeStamping
)) {
412 policyType
= kSecValidPolicyTimeStamping
;
414 if (policyType
== kSecValidPolicyAny
) {
415 return true; /* policy not subject to constraint */
417 /* policy is subject to constraint; do the constraints allow it? */
419 for (CFIndex ix
= 0; ix
< count
; ix
++) {
420 SecValidPolicy allowedPolicy
= constraints
[ix
];
421 if (allowedPolicy
== kSecValidPolicyAny
||
422 allowedPolicy
== policyType
) {
428 secnotice("rvc", "%@ not allowed by policy constraints on issuing CA", policyName
);
433 static bool SecRVCGetPolicyConstraints(CFDataRef data
, SecValidPolicy
**constraints
, CFIndex
*count
) {
434 /* Sanity-check the input policy constraints data, returning pointer and
435 * count values in output arguments. Function result is true if successful.
437 * The first byte of the policy constraints data contains the number of entries,
438 * followed by an array of 0..n policy constraint values of type SecValidPolicy.
439 * The maximum number of defined policies is not expected to approach 127, i.e.
440 * the largest value which can be expressed in a signed byte.
444 SecValidPolicy
*p
= NULL
;
446 length
= CFDataGetLength(data
);
447 p
= (SecValidPolicy
*)CFDataGetBytePtr(data
);
449 /* Verify that count is 0 or greater, and equal to remaining number of bytes */
450 CFIndex c
= (length
> 0) ? *p
++ : -1;
451 if (c
< 0 || c
!= (length
- 1)) {
452 secerror("invalid policy constraints array");
465 static void SecRVCProcessValidPolicyConstraints(SecRVCRef rvc
) {
466 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
469 if (!rvc
->valid_info
->hasPolicyConstraints
) {
473 SecValidPolicy
*constraints
= NULL
;
474 if (!SecRVCGetPolicyConstraints(rvc
->valid_info
->policyConstraints
, &constraints
, &count
)) {
477 secdebug("rvc", "found policy constraints for cert at index %ld", rvc
->certIX
);
479 /* check that policies being verified are permitted by the policy constraints */
480 bool policyDeniedByConstraints
= false;
481 CFIndex ix
, initialPVCCount
= SecPathBuilderGetPVCCount(rvc
->builder
);
482 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
483 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, ix
);
484 CFArrayRef policies
= CFRetainSafe(pvc
->policies
);
485 CFIndex policyCount
= (policies
) ? CFArrayGetCount(policies
) : 0;
486 for (CFIndex policyIX
= 0; policyIX
< policyCount
; policyIX
++) {
487 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
488 if (!SecRVCPolicyConstraintsPermitPolicy(constraints
, count
, policy
)) {
489 policyDeniedByConstraints
= true;
490 if (rvc
->valid_info
->overridable
) {
491 SecPVCSetResultForcedWithTrustResult(pvc
, kSecPolicyCheckIssuerPolicyConstraints
, rvc
->certIX
,
492 kCFBooleanFalse
, true, kSecTrustResultRecoverableTrustFailure
);
494 SecPVCSetResultForced(pvc
, kSecPolicyCheckIssuerPolicyConstraints
, rvc
->certIX
, kCFBooleanFalse
, true);
498 CFReleaseSafe(policies
);
500 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
502 TAValidStatus status
= (policyDeniedByConstraints
) ? TAValidPolicyConstrainedDenied
: TAValidPolicyConstrainedOK
;
503 analytics
->valid_status
|= status
;
507 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc
) {
508 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
511 if (!rvc
->valid_info
->hasDateConstraints
) {
514 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
518 CFAbsoluteTime certIssued
= SecCertificateNotValidBefore(certificate
);
519 CFAbsoluteTime caNotBefore
= -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
520 CFAbsoluteTime caNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
521 if (rvc
->valid_info
->notBeforeDate
) {
522 caNotBefore
= CFDateGetAbsoluteTime(rvc
->valid_info
->notBeforeDate
);
524 if (rvc
->valid_info
->notAfterDate
) {
525 caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
526 /* per the Valid specification, if this date is in the past, we need to check CT. */
527 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
528 if (caNotAfter
< now
) {
529 rvc
->valid_info
->requireCT
= true;
532 if ((certIssued
< caNotBefore
) && (rvc
->certIX
> 0)) {
533 /* not-before constraint is only applied to leaf certificate, for now. */
537 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
538 if ((certIssued
< caNotBefore
) || (certIssued
> caNotAfter
)) {
539 /* We are outside the constrained validity period. */
540 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
541 (rvc
->valid_info
->overridable
) ? "" : " (non-recoverable error)");
543 analytics
->valid_status
|= TAValidDateConstrainedRevoked
;
545 if (rvc
->valid_info
->overridable
) {
546 /* error is recoverable, treat certificate as untrusted
547 (note this date check is different from kSecPolicyCheckTemporalValidity) */
548 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
549 kCFBooleanFalse
, true);
551 /* error is non-overridable, treat certificate as revoked */
552 SInt32 reason
= 0; /* unspecified reason code */
553 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
554 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
556 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
558 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
560 CFReleaseNull(cfreason
);
562 } else if (analytics
) {
563 analytics
->valid_status
|= TAValidDateConstrainedOK
;
567 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc
) {
568 if (!rvc
|| !rvc
->valid_info
) {
571 SecValidInfoRef info
= rvc
->valid_info
;
572 /* outcomes as defined in Valid server specification */
573 if (info
->format
== kSecValidInfoFormatSerial
||
574 info
->format
== kSecValidInfoFormatSHA256
) {
575 if (info
->noCACheck
|| info
->complete
|| info
->isOnList
) {
578 } else { /* info->format == kSecValidInfoFormatNto1 */
579 if (info
->noCACheck
|| (info
->complete
&& !info
->isOnList
)) {
586 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc
) {
587 if (!rvc
|| !rvc
->valid_info
) {
590 SecValidInfoRef info
= rvc
->valid_info
;
591 /* either not present on an allowlist, or present on a blocklist */
592 return (!info
->isOnList
&& info
->valid
) || (info
->isOnList
&& !info
->valid
);
595 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc
) {
596 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
599 if (rvc
->valid_info
->overridable
) {
600 /* error is recoverable, treat certificate as untrusted */
601 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedLeaf
, rvc
->certIX
,
602 kCFBooleanFalse
, true);
605 /* error is fatal at this point */
606 if (!SecRVCHasRevokedValidInfo(rvc
) || rvc
->valid_info
->noCACheck
) {
607 /* result key should indicate blocked instead of revoked,
608 * but result must be non-recoverable */
609 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckBlackListedLeaf
, rvc
->certIX
,
610 kCFBooleanFalse
, true);
613 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
614 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
615 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
617 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
619 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
621 CFReleaseNull(cfreason
);
624 bool SecRVCRevocationChecked(SecRVCRef rvc
) {
625 return rvc
->revocation_checked
;
628 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
629 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
632 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
633 SecValidInfoRef info
= rvc
->valid_info
;
635 bool definitive
= SecRVCHasDefinitiveValidInfo(rvc
);
636 bool revoked
= SecRVCHasRevokedValidInfo(rvc
);
639 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
642 analytics
->valid_status
|= definitive
? TAValidDefinitelyRevoked
: TAValidProbablyRevoked
;
644 analytics
->valid_status
|= definitive
? TAValidDefinitelyOK
: TAValidProbablyOK
;
646 analytics
->valid_require_ct
|= info
->requireCT
;
647 analytics
->valid_known_intermediates_only
|= info
->knownOnly
;
650 /* Handle no-ca cases */
651 if (info
->noCACheck
) {
652 bool allowed
= (info
->valid
&& info
->complete
&& info
->isOnList
);
654 /* definitely revoked */
655 SecRVCSetValidDeterminedErrorResult(rvc
);
656 } else if (allowed
) {
657 /* definitely not revoked (allowlisted) */
658 SecCertificatePathVCSetIsAllowlisted(path
, true);
659 rvc
->revocation_checked
= true;
661 /* no-ca is definitive; no need to check further. */
662 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
663 (allowed
) ? "allowed" : "revoked", rvc
->certIX
);
668 /* Handle policy constraints, if present. */
669 SecRVCProcessValidPolicyConstraints(rvc
);
671 /* Handle date constraints, if present.
672 * Note: a not-after date may set the CT requirement,
673 * so check requireCT after this function is called. */
674 SecRVCProcessValidDateConstraints(rvc
);
676 /* Set CT requirement on path, if present. */
677 if (info
->requireCT
) {
678 SecPathCTPolicy ctp
= kSecPathCTRequired
;
679 if (info
->overridable
) {
680 ctp
= kSecPathCTRequiredOverridable
;
682 SecCertificatePathVCSetRequiresCT(path
, ctp
);
685 /* Trigger OCSP for any non-definitive or revoked cases */
686 if (!definitive
|| revoked
) {
687 info
->checkOCSP
= true;
690 if (info
->checkOCSP
) {
691 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
692 CFIndex issuerIX
= rvc
->certIX
+ 1;
693 if (issuerIX
>= count
) {
694 /* cannot perform a revocation check on the last cert in the
695 chain, since we don't have its issuer. */
698 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
699 (info
->complete
) ? "" : "possibly ", (info
->valid
) ? "allowed" : "revoked",
701 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
703 /* Valid DB results caused us to do OCSP */
704 analytics
->valid_trigger_ocsp
= true;
709 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
710 /* Skip checking for OCSP Signer verification */
711 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
712 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
713 if (!pvc
) { return false; }
714 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
715 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
716 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
721 /* Make sure revocation db info is up-to-date.
722 * We don't care if the builder is allowed to access the network because
723 * the network fetching does not block the trust evaluation. */
724 SecRevocationDbCheckNextUpdate();
726 /* Check whether we have valid db info for this cert,
727 given the cert and its issuer */
728 SecValidInfoRef info
= NULL
;
729 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
731 bool isSelfSigned
= false;
732 SecCertificateRef cert
= NULL
;
733 SecCertificateRef issuer
= NULL
;
734 CFIndex issuerIX
= rvc
->certIX
+ 1;
735 if (count
> issuerIX
) {
736 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
737 } else if (count
== issuerIX
) {
738 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
739 if (rootIX
== rvc
->certIX
) {
740 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
744 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
746 /* skip revocation db check for self-signed certificates [33137065] */
747 info
= SecRevocationDbCopyMatching(cert
, issuer
);
749 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
752 SecValidInfoRef old_info
= rvc
->valid_info
;
753 rvc
->valid_info
= info
;
755 CFReleaseNull(old_info
);
762 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
763 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
764 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
765 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
766 SecOCSPResponseRef response
= NULL
;
767 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
768 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
769 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
771 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
773 SecORVCConsumeOCSPResponse(rvc
->orvc
, response
, NULL_TIME
, false, true);
774 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
775 if (rvc
->orvc
->done
&& analytics
) {
776 /* We found a valid OCSP response in the cache */
777 analytics
->ocsp_cache_hit
= true;
782 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
783 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
784 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
787 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
) {
789 SecRVCUpdatePVC(rvc
);
790 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
793 static bool SecRVCFetchNext(SecRVCRef rvc
) {
794 bool OCSP_fetch_finished
= true;
795 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
796 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
797 if (SecRVCShouldCheckOCSP(rvc
)) {
798 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
799 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
800 OCSP_fetch_finished
= SecORVCBeginFetches(rvc
->orvc
, cert
);
801 if (analytics
&& !OCSP_fetch_finished
) {
802 /* We did a network OCSP fetch, set report appropriately */
803 analytics
->ocsp_network
= true;
806 if (OCSP_fetch_finished
) {
807 /* we didn't start an OCSP background job for this cert */
808 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
810 return OCSP_fetch_finished
;
813 /* The SecPathBuilder state machine calls SecPathBuilderCheckRevocation twice --
814 * once in the ValidatePath state, and again in the ComputeDetails state. In the
815 * ValidatePath state we've not yet run the path checks, so for callers who set
816 * kSecRevocationCheckIfTrusted, we don't do any networking on that first call.
817 * Here, if we've already done revocation before (so we're in ComputeDetails now),
818 * we need to recheck (and enable networking) for trusted chains and
819 * kSecRevocationCheckIfTrusted. Otherwise, we skip the checks to save on the processing
820 * but update the PVCs with the revocation results from the last check. */
821 static bool SecRevocationDidCheckRevocation(SecPathBuilderRef builder
, bool *first_check_done
) {
822 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
823 if (!SecCertificatePathVCIsRevocationDone(path
)) {
826 if (first_check_done
) {
827 *first_check_done
= true;
830 SecPVCRef resultPVC
= SecPathBuilderGetResultPVC(builder
);
831 bool recheck
= false;
832 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
) && SecPVCIsOkResult(resultPVC
)) {
834 secdebug("rvc", "Rechecking revocation because network now allowed");
836 secdebug("rvc", "Not rechecking revocation");
840 // reset the RVCs for the second pass
841 SecCertificatePathVCDeleteRVCs(path
);
843 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
844 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
845 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
847 SecRVCUpdatePVC(rvc
);
855 static bool SecRevocationCanAccessNetwork(SecPathBuilderRef builder
, bool first_check_done
) {
856 /* CheckRevocationIfTrusted overrides NoNetworkAccess for revocation */
857 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
)) {
858 if (first_check_done
) {
859 /* We're on the second pass. We need to now allow networking for revocation.
860 * SecRevocationDidCheckRevocation takes care of not running a second pass
861 * if the chain isn't trusted. */
864 /* We're on the first pass of the revocation checks, where we aren't
865 * supposed to do networking because we don't know if the chain
870 return SecPathBuilderCanAccessNetwork(builder
);
873 void SecPathBuilderCheckKnownIntermediateConstraints(SecPathBuilderRef builder
) {
874 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
878 /* only perform this check once per path! */
879 CFIndex certIX
= kCFNotFound
;
880 if (SecCertificatePathVCCheckedIssuers(path
)) {
881 certIX
= SecCertificatePathVCUnknownCAIndex(path
);
884 /* check full path: start with anchor and decrement to leaf */
885 bool parentConstrained
= false;
886 CFIndex certCount
= SecPathBuilderGetCertificateCount(builder
);
887 for (certIX
= certCount
- 1; certIX
>= 0; --certIX
) {
888 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
892 if (parentConstrained
&& !rvc
->valid_info
) {
893 /* Parent had the known-only constraint, but our issuer is unknown.
894 Bump index to point back at the issuer since it fails the constraint. */
898 parentConstrained
= (rvc
->valid_info
&& rvc
->valid_info
->knownOnly
);
899 if (parentConstrained
) {
900 secdebug("validupdate", "Valid db found a known-intermediate constraint on %@ (index=%ld)",
901 rvc
->valid_info
->issuerHash
, certIX
+1);
903 /* check special case: unknown constrained CA in leaf position */
904 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
905 if (cert
&& SecCertificateIsCA(cert
) && !SecRevocationDbContainsIssuer(cert
)) {
906 /* leaf is a CA which violates the constraint */
912 /* At this point, certIX will either be -1, indicating no CA was found
913 which failed a known-intermediates-only constraint on its parent, or it
914 will be the index of the first unknown CA which fails the constraint. */
916 secnotice("validupdate", "CA at index %ld violates known-intermediate constraint", certIX
);
917 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
919 analytics
->valid_unknown_intermediate
= true;
922 SecCertificatePathVCSetUnknownCAIndex(path
, certIX
);
923 SecCertificatePathVCSetCheckedIssuers(path
, true);
927 /* Error is set on CA certificate which failed the constraint. */
928 SecRVCSetValidDeterminedErrorResult(SecCertificatePathVCGetRVCAtIndex(path
, certIX
));
932 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
933 secdebug("rvc", "checking revocation");
934 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
935 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
936 if (certCount
<= 1) {
937 /* Can't verify without an issuer; we're done */
941 bool first_check_done
= false;
942 if (SecRevocationDidCheckRevocation(builder
, &first_check_done
)) {
946 /* Setup things so we check revocation status of all certs. */
947 SecCertificatePathVCAllocateRVCs(path
, certCount
);
949 /* Note that if we are multi threaded and a job completes after it
950 is started but before we return from this function, we don't want
951 a callback to decrement asyncJobCount to zero before we finish issuing
952 all the jobs. To avoid this we pretend we issued certCount async jobs,
953 and decrement pvc->asyncJobCount for each cert that we don't start a
954 background fetch for. We include the root, even though we'll never start
955 an async job for it so that we count all active threads for this eval. */
956 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
));
958 /* Loop though certificates again and issue an ocsp fetch if the
959 revocation status checking isn't done yet (and we have an issuer!) */
960 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
961 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
962 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
967 SecRVCInit(rvc
, builder
, certIX
);
969 /* RFC 6960: id-pkix-ocsp-nocheck extension says that we shouldn't check revocation. */
970 if (SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path
, certIX
)))
972 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX
);
973 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
975 /* This certificate has OCSP No-Check, so add to reporting analytics */
976 analytics
->ocsp_no_check
= true;
978 SecRVCSetFinishedWithoutNetwork(rvc
);
985 #if !TARGET_OS_BRIDGE
986 /* Check valid database first (separate from OCSP response cache) */
987 if (SecRVCCheckValidInfoDatabase(rvc
)) {
988 SecRVCProcessValidInfoResults(rvc
);
991 /* Any other revocation method requires an issuer certificate to verify the response;
992 * skip the last cert in the chain since it doesn't have one. */
993 if (certIX
+ 1 >= certCount
) {
997 /* Ignore stapled OCSP responses only if CRLs are enabled and the
998 * policy specifically requested CRLs only. */
999 if (SecRVCShouldCheckOCSP(rvc
)) {
1000 /* If we have any OCSP stapled responses, check those first */
1001 SecORVCProcessStapledResponses(rvc
->orvc
);
1004 #if TARGET_OS_BRIDGE
1005 /* The bridge has no writeable storage and no network. Nothing else we can
1007 SecRVCSetFinishedWithoutNetwork(rvc
);
1009 #else // !TARGET_OS_BRIDGE
1010 /* Then check the caches for revocation results. */
1011 SecRVCCheckRevocationCaches(rvc
);
1013 /* The check is done if we found cached responses from either method. */
1014 if (rvc
->done
|| rvc
->orvc
->done
) {
1015 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1016 SecRVCSetFinishedWithoutNetwork(rvc
);
1020 /* If we got a cached response that is no longer valid (which can only be true for
1021 * revoked responses), let's try to get a fresher response even if no one asked.
1022 * This check resolves unrevocation events after the nextUpdate time. */
1023 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1025 /* Check whether CA revocation additions match an issuer above us in this path.
1026 * If so, we will attempt revocation checking below. */
1027 bool has_ca_revocation
= (certIX
< SecCertificatePathVCIndexOfCAWithRevocationAdditions(path
));
1029 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1030 async http request for this cert's revocation status, unless we already successfully checked
1031 the revocation status of this cert based on the cache or stapled responses. */
1032 bool allow_fetch
= SecRevocationCanAccessNetwork(builder
, first_check_done
) &&
1033 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1034 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
|| has_ca_revocation
);
1035 if (rvc
->done
|| !allow_fetch
) {
1036 /* We got a cache hit or we aren't allowed to access the network */
1037 SecRVCUpdatePVC(rvc
);
1038 /* We didn't really start any background jobs for this cert. */
1039 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1041 (void)SecRVCFetchNext(rvc
);
1043 #endif // !TARGET_OS_BRIDGE
1046 /* Return false if there are still async jobs running. */
1047 /* builder->asyncJobCount is atomic, so we know that if the job count is 0, all other
1048 * threads are finished. If the job count is > 0, other threads will decrement the job
1049 * count and SecPathBuilderStep to crank the state machine when the job count is 0. */
1050 return (SecPathBuilderDecrementAsyncJobCount(builder
) == 0);
1053 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1054 CFAbsoluteTime enu
= NULL_TIME
;
1055 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1056 enu
= rvc
->orvc
->nextUpdate
;