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 <securityd/SecTrustServer.h>
43 #include <securityd/SecOCSPRequest.h>
44 #include <securityd/SecOCSPResponse.h>
45 #include <securityd/SecOCSPCache.h>
46 #include <securityd/SecRevocationDb.h>
47 #include <securityd/SecCertificateServer.h>
48 #include <securityd/SecPolicyServer.h>
49 #include <securityd/SecRevocationNetworking.h>
51 #include <securityd/SecRevocationServer.h>
54 /********************************************************
55 ****************** OCSP RVC Functions ******************
56 ********************************************************/
57 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
58 const CFAbsoluteTime kSecOCSPResponseOnlineTTL
= 5.0 * 60.0;
59 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
61 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
);
292 if (sr
->certStatus
== CS_Good
) {
293 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
294 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
295 } else if (sr
->certStatus
== CS_Revoked
) {
296 // Expire revoked responses when the subject certificate itself expires.
297 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
));
300 // Ok we like the new response, let's toss the old one.
302 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
304 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
305 rvc
->ocspResponse
= ocspResponse
;
308 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
309 rvc
->ocspSingleResponse
= sr
;
312 rvc
->done
= sr_valid
;
315 if (sr
) SecOCSPSingleResponseDestroy(sr
);
316 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
319 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
320 SecORVCRef orvc
= NULL
;
321 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
322 secdebug("alloc", "orvc %p", orvc
);
324 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
325 orvc
->builder
= builder
;
327 orvc
->certIX
= certIX
;
329 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
);
330 if (SecPathBuilderGetCertificateCount(builder
) > (certIX
+ 1)) {
331 SecCertificateRef issuer
= SecPathBuilderGetCertificateAtIndex(builder
, certIX
+ 1);
332 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
338 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
339 /* Get stapled OCSP responses */
340 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->builder
);
342 if(ocspResponsesData
) {
343 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
344 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
345 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
346 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false, false);
348 CFRelease(ocspResponsesData
);
352 void SecRVCDelete(SecRVCRef rvc
) {
353 secdebug("alloc", "delete rvc %p", rvc
);
355 SecORVCFinish(rvc
->orvc
);
359 if (rvc
->valid_info
) {
360 CFReleaseNull(rvc
->valid_info
);
364 // Forward declaration
365 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
);
367 static void SecRVCInit(SecRVCRef rvc
, SecPathBuilderRef builder
, CFIndex certIX
) {
368 secdebug("alloc", "rvc %p", rvc
);
369 rvc
->builder
= builder
;
370 rvc
->certIX
= certIX
;
371 rvc
->orvc
= SecORVCCreate(rvc
, builder
, certIX
);
374 SecRVCSetFinishedWithoutNetwork(rvc
);
380 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
384 static bool SecRVCPolicyConstraintsPermitPolicy(SecValidPolicy
*constraints
, CFIndex count
, SecPolicyRef policy
) {
385 if (!constraints
|| !policy
) {
386 return true; /* nothing to constrain */
388 SecValidPolicy policyType
= kSecValidPolicyAny
;
389 CFStringRef policyName
= SecPolicyGetName(policy
);
390 /* determine if the policy is a candidate for being constrained */
391 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
) ||
392 CFEqualSafe(policyName
, kSecPolicyNameEAPServer
) ||
393 CFEqualSafe(policyName
, kSecPolicyNameIPSecServer
)) {
394 policyType
= kSecValidPolicyServerAuthentication
;
395 } else if (CFEqualSafe(policyName
, kSecPolicyNameSSLClient
) ||
396 CFEqualSafe(policyName
, kSecPolicyNameEAPClient
) ||
397 CFEqualSafe(policyName
, kSecPolicyNameIPSecClient
)) {
398 policyType
= kSecValidPolicyClientAuthentication
;
399 } else if (CFEqualSafe(policyName
, kSecPolicyNameSMIME
)) {
400 policyType
= kSecValidPolicyEmailProtection
;
401 } else if (CFEqualSafe(policyName
, kSecPolicyNameCodeSigning
)) {
402 policyType
= kSecValidPolicyCodeSigning
;
403 } else if (CFEqualSafe(policyName
, kSecPolicyNameTimeStamping
)) {
404 policyType
= kSecValidPolicyTimeStamping
;
406 if (policyType
== kSecValidPolicyAny
) {
407 return true; /* policy not subject to constraint */
409 /* policy is subject to constraint; do the constraints allow it? */
411 for (CFIndex ix
= 0; ix
< count
; ix
++) {
412 SecValidPolicy allowedPolicy
= constraints
[ix
];
413 if (allowedPolicy
== kSecValidPolicyAny
||
414 allowedPolicy
== policyType
) {
420 secnotice("rvc", "%@ not allowed by policy constraints on issuing CA", policyName
);
425 static bool SecRVCGetPolicyConstraints(CFDataRef data
, SecValidPolicy
**constraints
, CFIndex
*count
) {
426 /* Sanity-check the input policy constraints data, returning pointer and
427 * count values in output arguments. Function result is true if successful.
429 * The first byte of the policy constraints data contains the number of entries,
430 * followed by an array of 0..n policy constraint values of type SecValidPolicy.
431 * The maximum number of defined policies is not expected to approach 127, i.e.
432 * the largest value which can be expressed in a signed byte.
436 SecValidPolicy
*p
= NULL
;
438 length
= CFDataGetLength(data
);
439 p
= (SecValidPolicy
*)CFDataGetBytePtr(data
);
441 /* Verify that count is 0 or greater, and equal to remaining number of bytes */
442 CFIndex c
= (length
> 0) ? *p
++ : -1;
443 if (c
< 0 || c
!= (length
- 1)) {
444 secerror("invalid policy constraints array");
457 static void SecRVCProcessValidPolicyConstraints(SecRVCRef rvc
) {
458 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
461 if (!rvc
->valid_info
->hasPolicyConstraints
) {
465 SecValidPolicy
*constraints
= NULL
;
466 if (!SecRVCGetPolicyConstraints(rvc
->valid_info
->policyConstraints
, &constraints
, &count
)) {
469 secdebug("rvc", "found policy constraints for cert at index %ld", rvc
->certIX
);
471 /* check that policies being verified are permitted by the policy constraints */
472 bool policyDeniedByConstraints
= false;
473 CFIndex ix
, initialPVCCount
= SecPathBuilderGetPVCCount(rvc
->builder
);
474 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
475 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, ix
);
476 CFArrayRef policies
= CFRetainSafe(pvc
->policies
);
477 CFIndex policyCount
= (policies
) ? CFArrayGetCount(policies
) : 0;
478 for (CFIndex policyIX
= 0; policyIX
< policyCount
; policyIX
++) {
479 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
480 if (!SecRVCPolicyConstraintsPermitPolicy(constraints
, count
, policy
)) {
481 policyDeniedByConstraints
= true;
482 SecPVCSetResultForced(pvc
, kSecPolicyCheckIssuerPolicyConstraints
, rvc
->certIX
,
483 kCFBooleanFalse
, true);
484 pvc
->result
= kSecTrustResultRecoverableTrustFailure
;
485 if (!rvc
->valid_info
->overridable
) {
486 /* error for this check should be non-recoverable */
487 pvc
->result
= kSecTrustResultFatalTrustFailure
;
491 CFReleaseSafe(policies
);
493 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
495 TAValidStatus status
= (policyDeniedByConstraints
) ? TAValidPolicyConstrainedDenied
: TAValidPolicyConstrainedOK
;
496 analytics
->valid_status
|= status
;
500 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc
) {
501 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
504 if (!rvc
->valid_info
->hasDateConstraints
) {
507 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
511 CFAbsoluteTime certIssued
= SecCertificateNotValidBefore(certificate
);
512 CFAbsoluteTime caNotBefore
= -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
513 CFAbsoluteTime caNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
514 if (rvc
->valid_info
->notBeforeDate
) {
515 caNotBefore
= CFDateGetAbsoluteTime(rvc
->valid_info
->notBeforeDate
);
517 if (rvc
->valid_info
->notAfterDate
) {
518 caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
519 /* per the Valid specification, if this date is in the past, we need to check CT. */
520 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
521 if (caNotAfter
< now
) {
522 rvc
->valid_info
->requireCT
= true;
525 if ((certIssued
< caNotBefore
) && (rvc
->certIX
> 0)) {
526 /* not-before constraint is only applied to leaf certificate, for now. */
530 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
531 if ((certIssued
< caNotBefore
) || (certIssued
> caNotAfter
)) {
532 /* We are outside the constrained validity period. */
533 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
534 (rvc
->valid_info
->overridable
) ? "" : " (non-recoverable error)");
536 analytics
->valid_status
|= TAValidDateConstrainedRevoked
;
538 if (rvc
->valid_info
->overridable
) {
539 /* error is recoverable, treat certificate as untrusted
540 (note this date check is different from kSecPolicyCheckTemporalValidity) */
541 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedKey
, rvc
->certIX
,
542 kCFBooleanFalse
, true);
544 /* error is non-overridable, treat certificate as revoked */
545 SInt32 reason
= 0; /* unspecified reason code */
546 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
547 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
549 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
551 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
553 CFReleaseNull(cfreason
);
555 } else if (analytics
) {
556 analytics
->valid_status
|= TAValidDateConstrainedOK
;
560 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc
) {
561 if (!rvc
|| !rvc
->valid_info
) {
564 SecValidInfoRef info
= rvc
->valid_info
;
565 /* outcomes as defined in Valid server specification */
566 if (info
->format
== kSecValidInfoFormatSerial
||
567 info
->format
== kSecValidInfoFormatSHA256
) {
568 if (info
->noCACheck
|| info
->complete
|| info
->isOnList
) {
571 } else { /* info->format == kSecValidInfoFormatNto1 */
572 if (info
->noCACheck
|| (info
->complete
&& !info
->isOnList
)) {
579 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc
) {
580 if (!rvc
|| !rvc
->valid_info
) {
583 SecValidInfoRef info
= rvc
->valid_info
;
584 /* either not present on an allowlist, or present on a blocklist */
585 return (!info
->isOnList
&& info
->valid
) || (info
->isOnList
&& !info
->valid
);
588 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc
) {
589 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
592 if (rvc
->valid_info
->overridable
) {
593 /* error is recoverable, treat certificate as untrusted */
594 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckGrayListedLeaf
, rvc
->certIX
,
595 kCFBooleanFalse
, true);
598 /* error is fatal at this point */
599 if (!SecRVCHasRevokedValidInfo(rvc
) || rvc
->valid_info
->noCACheck
) {
600 /* result key should indicate blocked instead of revoked,
601 * but result must be non-recoverable */
602 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckBlackListedLeaf
, rvc
->certIX
,
603 kCFBooleanFalse
, true);
606 SInt32 reason
= 0; /* unspecified, since the Valid db doesn't tell us */
607 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
608 SecPathBuilderSetResultInPVCs(rvc
->builder
, kSecPolicyCheckRevocation
, rvc
->certIX
,
610 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
612 SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(path
, rvc
->certIX
, cfreason
);
614 CFReleaseNull(cfreason
);
617 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
618 if (!rvc
|| !rvc
->valid_info
|| !rvc
->builder
) {
621 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
622 SecValidInfoRef info
= rvc
->valid_info
;
624 bool definitive
= SecRVCHasDefinitiveValidInfo(rvc
);
625 bool revoked
= SecRVCHasRevokedValidInfo(rvc
);
628 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
631 analytics
->valid_status
|= definitive
? TAValidDefinitelyRevoked
: TAValidProbablyRevoked
;
633 analytics
->valid_status
|= definitive
? TAValidDefinitelyOK
: TAValidProbablyOK
;
635 analytics
->valid_require_ct
|= info
->requireCT
;
636 analytics
->valid_known_intermediates_only
|= info
->knownOnly
;
639 /* Handle no-ca cases */
640 if (info
->noCACheck
) {
641 bool allowed
= (info
->valid
&& info
->complete
&& info
->isOnList
);
643 /* definitely revoked */
644 SecRVCSetValidDeterminedErrorResult(rvc
);
645 } else if (allowed
) {
646 /* definitely not revoked (allowlisted) */
647 SecCertificatePathVCSetIsAllowlisted(path
, true);
649 /* no-ca is definitive; no need to check further. */
650 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex
,
651 (allowed
) ? "allowed" : "revoked", rvc
->certIX
);
656 /* Handle policy constraints, if present. */
657 SecRVCProcessValidPolicyConstraints(rvc
);
659 /* Handle date constraints, if present.
660 * Note: a not-after date may set the CT requirement,
661 * so check requireCT after this function is called. */
662 SecRVCProcessValidDateConstraints(rvc
);
664 /* Set CT requirement on path, if present. */
665 if (info
->requireCT
) {
666 SecPathCTPolicy ctp
= kSecPathCTRequired
;
667 if (info
->overridable
) {
668 ctp
= kSecPathCTRequiredOverridable
;
670 SecCertificatePathVCSetRequiresCT(path
, ctp
);
673 /* Trigger OCSP for any non-definitive or revoked cases */
674 if (!definitive
|| revoked
) {
675 info
->checkOCSP
= true;
678 if (info
->checkOCSP
) {
679 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
680 CFIndex issuerIX
= rvc
->certIX
+ 1;
681 if (issuerIX
>= count
) {
682 /* cannot perform a revocation check on the last cert in the
683 chain, since we don't have its issuer. */
686 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex
" (will check OCSP)",
687 (info
->complete
) ? "" : "possibly ", (info
->valid
) ? "allowed" : "revoked",
689 SecPathBuilderSetRevocationMethod(rvc
->builder
, kSecPolicyCheckRevocationAny
);
691 /* Valid DB results caused us to do OCSP */
692 analytics
->valid_trigger_ocsp
= true;
697 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
698 /* Skip checking for OCSP Signer verification */
699 if (SecPathBuilderGetPVCCount(rvc
->builder
) == 1) {
700 SecPVCRef pvc
= SecPathBuilderGetPVCAtIndex(rvc
->builder
, 0);
701 if (!pvc
) { return false; }
702 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
703 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
704 if (policyName
&& CFEqual(policyName
, CFSTR("OCSPSigner"))) {
709 /* Make sure revocation db info is up-to-date.
710 * We don't care if the builder is allowed to access the network because
711 * the network fetching does not block the trust evaluation. */
712 SecRevocationDbCheckNextUpdate();
714 /* Check whether we have valid db info for this cert,
715 given the cert and its issuer */
716 SecValidInfoRef info
= NULL
;
717 CFIndex count
= SecPathBuilderGetCertificateCount(rvc
->builder
);
719 bool isSelfSigned
= false;
720 SecCertificateRef cert
= NULL
;
721 SecCertificateRef issuer
= NULL
;
722 CFIndex issuerIX
= rvc
->certIX
+ 1;
723 if (count
> issuerIX
) {
724 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, issuerIX
);
725 } else if (count
== issuerIX
) {
726 CFIndex rootIX
= SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc
->builder
));
727 if (rootIX
== rvc
->certIX
) {
728 issuer
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rootIX
);
732 cert
= SecPathBuilderGetCertificateAtIndex(rvc
->builder
, rvc
->certIX
);
734 /* skip revocation db check for self-signed certificates [33137065] */
735 info
= SecRevocationDbCopyMatching(cert
, issuer
);
737 SecValidInfoSetAnchor(info
, SecPathBuilderGetCertificateAtIndex(rvc
->builder
, count
-1));
740 SecValidInfoRef old_info
= rvc
->valid_info
;
741 rvc
->valid_info
= info
;
743 CFReleaseNull(old_info
);
750 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
751 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
752 if (SecRVCShouldCheckOCSP(rvc
) && (rvc
->orvc
->ocspRequest
)) {
753 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
754 SecOCSPResponseRef response
= NULL
;
755 if (SecPathBuilderGetCheckRevocationOnline(rvc
->builder
)) {
756 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
757 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
759 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
761 SecORVCConsumeOCSPResponse(rvc
->orvc
, response
, NULL_TIME
, false, true);
762 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
763 if (rvc
->orvc
->done
&& analytics
) {
764 /* We found a valid OCSP response in the cache */
765 analytics
->ocsp_cache_hit
= true;
770 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
771 SecRVCProcessValidInfoResults(rvc
); /* restore the results we got from Valid */
772 if (rvc
->orvc
) { SecORVCUpdatePVC(rvc
->orvc
); }
775 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc
) {
777 SecRVCUpdatePVC(rvc
);
778 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
781 static bool SecRVCFetchNext(SecRVCRef rvc
) {
782 bool OCSP_fetch_finished
= true;
783 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(rvc
->builder
);
784 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
785 if (SecRVCShouldCheckOCSP(rvc
)) {
786 SecCertificatePathVCRef path
= SecPathBuilderGetPath(rvc
->builder
);
787 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
788 OCSP_fetch_finished
= SecORVCBeginFetches(rvc
->orvc
, cert
);
789 if (analytics
&& !OCSP_fetch_finished
) {
790 /* We did a network OCSP fetch, set report appropriately */
791 analytics
->ocsp_network
= true;
794 if (OCSP_fetch_finished
) {
795 /* we didn't start an OCSP background job for this cert */
796 (void)SecPathBuilderDecrementAsyncJobCount(rvc
->builder
);
798 return OCSP_fetch_finished
;
801 /* The SecPathBuilder state machine calls SecPathBuilderCheckRevocation twice --
802 * once in the ValidatePath state, and again in the ComputeDetails state. In the
803 * ValidatePath state we've not yet run the path checks, so for callers who set
804 * kSecRevocationCheckIfTrusted, we don't do any networking on that first call.
805 * Here, if we've already done revocation before (so we're in ComputeDetails now),
806 * we need to recheck (and enable networking) for trusted chains and
807 * kSecRevocationCheckIfTrusted. Otherwise, we skip the checks to save on the processing
808 * but update the PVCs with the revocation results from the last check. */
809 static bool SecRevocationDidCheckRevocation(SecPathBuilderRef builder
, bool *first_check_done
) {
810 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
811 if (!SecCertificatePathVCIsRevocationDone(path
)) {
814 if (first_check_done
) {
815 *first_check_done
= true;
818 SecPVCRef resultPVC
= SecPathBuilderGetResultPVC(builder
);
819 bool recheck
= false;
820 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
) && SecPVCIsOkResult(resultPVC
)) {
822 secdebug("rvc", "Rechecking revocation because network now allowed");
824 secdebug("rvc", "Not rechecking revocation");
828 // reset the RVCs for the second pass
829 SecCertificatePathVCDeleteRVCs(path
);
831 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
832 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
833 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
835 SecRVCUpdatePVC(rvc
);
843 static bool SecRevocationCanAccessNetwork(SecPathBuilderRef builder
, bool first_check_done
) {
844 /* CheckRevocationIfTrusted overrides NoNetworkAccess for revocation */
845 if (SecPathBuilderGetCheckRevocationIfTrusted(builder
)) {
846 if (first_check_done
) {
847 /* We're on the second pass. We need to now allow networking for revocation.
848 * SecRevocationDidCheckRevocation takes care of not running a second pass
849 * if the chain isn't trusted. */
852 /* We're on the first pass of the revocation checks, where we aren't
853 * supposed to do networking because we don't know if the chain
858 return SecPathBuilderCanAccessNetwork(builder
);
861 void SecPathBuilderCheckKnownIntermediateConstraints(SecPathBuilderRef builder
) {
862 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
866 /* only perform this check once per path! */
867 CFIndex certIX
= kCFNotFound
;
868 if (SecCertificatePathVCCheckedIssuers(path
)) {
869 certIX
= SecCertificatePathVCUnknownCAIndex(path
);
872 /* check full path: start with anchor and decrement to leaf */
873 bool parentConstrained
= false;
874 CFIndex certCount
= SecPathBuilderGetCertificateCount(builder
);
875 for (certIX
= certCount
- 1; certIX
>= 0; --certIX
) {
876 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
880 if (parentConstrained
&& !rvc
->valid_info
) {
881 /* Parent had the known-only constraint, but our issuer is unknown.
882 Bump index to point back at the issuer since it fails the constraint. */
886 parentConstrained
= (rvc
->valid_info
&& rvc
->valid_info
->knownOnly
);
887 if (parentConstrained
) {
888 secdebug("validupdate", "Valid db found a known-intermediate constraint on %@ (index=%ld)",
889 rvc
->valid_info
->issuerHash
, certIX
+1);
891 /* check special case: unknown constrained CA in leaf position */
892 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
893 if (cert
&& SecCertificateIsCA(cert
) && !SecRevocationDbContainsIssuer(cert
)) {
894 /* leaf is a CA which violates the constraint */
900 /* At this point, certIX will either be -1, indicating no CA was found
901 which failed a known-intermediates-only constraint on its parent, or it
902 will be the index of the first unknown CA which fails the constraint. */
904 secnotice("validupdate", "CA at index %ld violates known-intermediate constraint", certIX
);
905 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
907 analytics
->valid_unknown_intermediate
= true;
910 SecCertificatePathVCSetUnknownCAIndex(path
, certIX
);
911 SecCertificatePathVCSetCheckedIssuers(path
, true);
915 /* Error is set on CA certificate which failed the constraint. */
916 SecRVCSetValidDeterminedErrorResult(SecCertificatePathVCGetRVCAtIndex(path
, certIX
));
920 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder
) {
921 secdebug("rvc", "checking revocation");
922 CFIndex certIX
, certCount
= SecPathBuilderGetCertificateCount(builder
);
923 SecCertificatePathVCRef path
= SecPathBuilderGetPath(builder
);
924 if (certCount
<= 1) {
925 /* Can't verify without an issuer; we're done */
929 bool first_check_done
= false;
930 if (SecRevocationDidCheckRevocation(builder
, &first_check_done
)) {
934 /* Setup things so we check revocation status of all certs. */
935 SecCertificatePathVCAllocateRVCs(path
, certCount
);
937 /* Note that if we are multi threaded and a job completes after it
938 is started but before we return from this function, we don't want
939 a callback to decrement asyncJobCount to zero before we finish issuing
940 all the jobs. To avoid this we pretend we issued certCount async jobs,
941 and decrement pvc->asyncJobCount for each cert that we don't start a
942 background fetch for. We include the root, even though we'll never start
943 an async job for it so that we count all active threads for this eval. */
944 SecPathBuilderSetAsyncJobCount(builder
, (unsigned int)(certCount
));
946 /* Loop though certificates again and issue an ocsp fetch if the
947 revocation status checking isn't done yet (and we have an issuer!) */
948 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
949 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
950 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, certIX
);
955 SecRVCInit(rvc
, builder
, certIX
);
957 /* RFC 6960: id-pkix-ocsp-nocheck extension says that we shouldn't check revocation. */
958 if (SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path
, certIX
)))
960 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX
);
961 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(builder
);
963 /* This certificate has OCSP No-Check, so add to reporting analytics */
964 analytics
->ocsp_no_check
= true;
966 SecRVCSetFinishedWithoutNetwork(rvc
);
973 #if !TARGET_OS_BRIDGE
974 /* Check valid database first (separate from OCSP response cache) */
975 if (SecRVCCheckValidInfoDatabase(rvc
)) {
976 SecRVCProcessValidInfoResults(rvc
);
979 /* Any other revocation method requires an issuer certificate to verify the response;
980 * skip the last cert in the chain since it doesn't have one. */
981 if (certIX
+ 1 >= certCount
) {
985 /* Ignore stapled OCSP responses only if CRLs are enabled and the
986 * policy specifically requested CRLs only. */
987 if (SecRVCShouldCheckOCSP(rvc
)) {
988 /* If we have any OCSP stapled responses, check those first */
989 SecORVCProcessStapledResponses(rvc
->orvc
);
993 /* The bridge has no writeable storage and no network. Nothing else we can
995 SecRVCSetFinishedWithoutNetwork(rvc
);
997 #else // !TARGET_OS_BRIDGE
998 /* Then check the caches for revocation results. */
999 SecRVCCheckRevocationCaches(rvc
);
1001 /* The check is done if we found cached responses from either method. */
1002 if (rvc
->done
|| rvc
->orvc
->done
) {
1003 secdebug("rvc", "found cached response for cert: %ld", certIX
);
1004 SecRVCSetFinishedWithoutNetwork(rvc
);
1008 /* If we got a cached response that is no longer valid (which can only be true for
1009 * revoked responses), let's try to get a fresher response even if no one asked.
1010 * This check resolves unrevocation events after the nextUpdate time. */
1011 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
1013 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1014 async http request for this cert's revocation status, unless we already successfully checked
1015 the revocation status of this cert based on the cache or stapled responses. */
1016 bool allow_fetch
= SecRevocationCanAccessNetwork(builder
, first_check_done
) &&
1017 (SecCertificatePathVCIsEV(path
) || SecCertificatePathVCIsOptionallyEV(path
) ||
1018 SecPathBuilderGetRevocationMethod(builder
) || old_cached_response
);
1019 if (rvc
->done
|| !allow_fetch
) {
1020 /* We got a cache hit or we aren't allowed to access the network */
1021 SecRVCUpdatePVC(rvc
);
1022 /* We didn't really start any background jobs for this cert. */
1023 (void)SecPathBuilderDecrementAsyncJobCount(builder
);
1025 (void)SecRVCFetchNext(rvc
);
1027 #endif // !TARGET_OS_BRIDGE
1030 /* Return false if there are still async jobs running. */
1031 /* builder->asyncJobCount is atomic, so we know that if the job count is 0, all other
1032 * threads are finished. If the job count is > 0, other threads will decrement the job
1033 * count and SecPathBuilderStep to crank the state machine when the job count is 0. */
1034 return (SecPathBuilderDecrementAsyncJobCount(builder
) == 0);
1037 CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
1038 CFAbsoluteTime enu
= NULL_TIME
;
1039 if (!rvc
|| !rvc
->orvc
) { return enu
; }
1040 enu
= rvc
->orvc
->nextUpdate
;