]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecRevocationServer.c
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / sec / securityd / SecRevocationServer.c
1 /*
2 * Copyright (c) 2008-2018 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecRevocationServer.c - Engine for evaluating certificate revocation.
26 */
27
28 #include <AssertMacros.h>
29
30 #include <mach/mach_time.h>
31
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>
37
38 #include <utilities/debugging.h>
39 #include <utilities/SecCFWrappers.h>
40 #include <utilities/SecIOFormat.h>
41
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>
50
51 #include <securityd/SecRevocationServer.h>
52
53 // MARK: SecORVCRef
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)
60
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;
66 }
67 if (orvc->ocspResponse) {
68 SecOCSPResponseFinalize(orvc->ocspResponse);
69 orvc->ocspResponse = NULL;
70 if (orvc->ocspSingleResponse) {
71 SecOCSPSingleResponseDestroy(orvc->ocspSingleResponse);
72 orvc->ocspSingleResponse = NULL;
73 }
74 }
75 memset(orvc, 0, sizeof(struct OpaqueSecORVC));
76 }
77
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,
81 SecORVCRef rvc) {
82 bool processed;
83 switch (this->certStatus) {
84 case CS_Good:
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;
90 processed = true;
91 break;
92 case CS_Revoked:
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
97 not all reasons. */
98 SInt32 reason = this->crlReason;
99 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
100 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
101 cfreason, true);
102 if (rvc->builder) {
103 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
104 if (info) {
105 /* make the revocation reason available in the trust result */
106 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
107 }
108 }
109 CFRelease(cfreason);
110 processed = true;
111 break;
112 case CS_Unknown:
113 /* not an error, no per-cert status, nothing here */
114 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
115 processed = false;
116 break;
117 default:
118 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
119 (int)this->certStatus, rvc->certIX);
120 processed = false;
121 break;
122 }
123
124 return processed;
125 }
126
127 void SecORVCUpdatePVC(SecORVCRef rvc) {
128 if (rvc->ocspSingleResponse) {
129 SecOCSPSingleResponseProcess(rvc->ocspSingleResponse, rvc);
130 }
131 if (rvc->ocspResponse) {
132 rvc->nextUpdate = SecOCSPResponseGetExpirationTime(rvc->ocspResponse);
133 }
134 }
135
136 typedef void (^SecOCSPEvaluationCompleted)(SecTrustResultType tr);
137
138 static void
139 SecOCSPEvaluateCompleted(const void *userData,
140 CFArrayRef chain, CFArrayRef details, CFDictionaryRef info,
141 SecTrustResultType result) {
142 SecOCSPEvaluationCompleted evaluated = (SecOCSPEvaluationCompleted)userData;
143 evaluated(result);
144 Block_release(evaluated);
145
146 }
147
148 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc, CFArrayRef signers, CFArrayRef issuers, CFAbsoluteTime verifyTime) {
149 __block bool evaluated = false;
150 bool trusted = false;
151 if (!signers || !issuers) {
152 return trusted;
153 }
154
155 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
156 const void *ocspSigner = SecPolicyCreateOCSPSigner();
157 CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault,
158 &ocspSigner, 1, &kCFTypeArrayCallBacks);
159 CFRelease(ocspSigner);
160
161 SecOCSPEvaluationCompleted completed = Block_copy(^(SecTrustResultType result) {
162 if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
163 evaluated = true;
164 }
165 });
166
167 CFDataRef clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->builder);
168 SecPathBuilderRef oBuilder = SecPathBuilderCreate(clientAuditToken,
169 signers, issuers, true, false,
170 policies, NULL, NULL, NULL,
171 verifyTime, NULL, NULL,
172 SecOCSPEvaluateCompleted, completed);
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);
177
178 /* verify the public key of the issuer signed the OCSP signer */
179 if (evaluated) {
180 SecCertificateRef issuer = NULL, signer = NULL;
181 SecKeyRef issuerPubKey = NULL;
182
183 issuer = (SecCertificateRef)CFArrayGetValueAtIndex(issuers, 0);
184 signer = (SecCertificateRef)CFArrayGetValueAtIndex(signers, 0);
185
186 if (issuer) {
187 issuerPubKey = SecCertificateCopyKey(issuer);
188 }
189 if (signer && issuerPubKey && (errSecSuccess == SecCertificateIsSignedBy(signer, issuerPubKey))) {
190 trusted = true;
191 } else {
192 secnotice("ocsp", "ocsp signer cert not signed by issuer");
193 }
194 CFReleaseNull(issuerPubKey);
195 }
196
197 return trusted;
198 }
199
200 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecORVCRef rvc, CFAbsoluteTime verifyTime) {
201 bool trusted;
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);
206
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",
212 rvc->responder);
213 trusted = true;
214 } else {
215 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
216 rvc->responder);
217 CFMutableArrayRef signerCerts = NULL;
218 CFArrayRef issuerCerts = NULL;
219
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)));
224
225 if (issuers) {
226 issuerCerts = SecCertificatePathVCCopyCertificates(issuers);
227 }
228
229 if (SecOCSPResponseEvaluateSigner(rvc, signerCerts, issuerCerts, verifyTime)) {
230 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
231 rvc->responder);
232 trusted = true;
233 } else {
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");
237 trusted = false;
238 }
239 CFReleaseNull(signerCerts);
240 CFReleaseNull(issuerCerts);
241 }
242 } else {
243 /* @@@ No signer found for this ocsp response, discard it. */
244 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
245 rvc->responder);
246 trusted = false;
247 }
248
249 #if DUMP_OCSPRESPONSES
250 char buf[40];
251 snprintf(buf, 40, "/tmp/ocspresponse%ld%s.der",
252 rvc->certIX, (trusted ? "t" : "u"));
253 secdumpdata(ocspResponse->data, buf);
254 #endif
255 CFReleaseNull(issuers);
256 CFReleaseNull(issuer);
257 CFReleaseNull(signers);
258 CFReleaseNull(signer);
259 return trusted;
260 }
261
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);
273
274 CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
275 #if TARGET_OS_IPHONE
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. */
278 if (!fromCache) {
279 require_quiet(SecOCSPResponseVerify(ocspResponse, rvc,
280 sr->certStatus == CS_Revoked ? SecOCSPResponseProducedAt(ocspResponse) : verifyTime), errOut);
281 }
282 #else
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);
286 #endif
287
288 // If we get here, we have a properly signed ocsp response
289 // but we haven't checked dates yet.
290
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));
298 }
299
300 // Ok we like the new response, let's toss the old one.
301 if (updateCache)
302 SecOCSPCacheReplaceResponse(rvc->ocspResponse, ocspResponse, rvc->responder, verifyTime);
303
304 if (rvc->ocspResponse) SecOCSPResponseFinalize(rvc->ocspResponse);
305 rvc->ocspResponse = ocspResponse;
306 ocspResponse = NULL;
307
308 if (rvc->ocspSingleResponse) SecOCSPSingleResponseDestroy(rvc->ocspSingleResponse);
309 rvc->ocspSingleResponse = sr;
310 sr = NULL;
311
312 rvc->done = sr_valid;
313
314 errOut:
315 if (sr) SecOCSPSingleResponseDestroy(sr);
316 if (ocspResponse) SecOCSPResponseFinalize(ocspResponse);
317 }
318
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);
323 if (orvc) {
324 memset(orvc, 0, sizeof(struct OpaqueSecORVC));
325 orvc->builder = builder;
326 orvc->rvc = rvc;
327 orvc->certIX = certIX;
328
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);
333 }
334 }
335 return orvc;
336 }
337
338 static void SecORVCProcessStapledResponses(SecORVCRef rvc) {
339 /* Get stapled OCSP responses */
340 CFArrayRef ocspResponsesData = SecPathBuilderCopyOCSPResponses(rvc->builder);
341
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);
347 });
348 CFRelease(ocspResponsesData);
349 }
350 }
351
352 // MARK: SecCRVCRef
353 /********************************************************
354 ******************* CRL RVC Functions ******************
355 ********************************************************/
356 #if ENABLE_CRLS
357 #include <../trustd/macOS/SecTrustOSXEntryPoints.h>
358 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
359
360 /* CRL Revocation verification context. */
361 struct OpaqueSecCRVC {
362 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
363 async_ocspd_t async_ocspd;
364
365 /* Pointer to the builder for this revocation check. */
366 SecPathBuilderRef builder;
367
368 /* Pointer to the generic rvc for this revocation check */
369 SecRVCRef rvc;
370
371 /* The current CRL status from ocspd. */
372 OSStatus status;
373
374 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
375 CFIndex certIX;
376
377 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
378 current distribution point. */
379 CFIndex distributionPointIX;
380
381 /* URL of current distribution point. */
382 CFURLRef distributionPoint;
383
384 /* Date until which this revocation status is valid. */
385 CFAbsoluteTime nextUpdate;
386
387 bool done;
388 };
389
390 static void SecCRVCFinish(SecCRVCRef crvc) {
391 // nothing yet
392 }
393
394 #define MAX_CRL_DPS 3
395 #define CRL_REQUEST_THRESHOLD 10
396
397 static CFURLRef SecCRVCGetNextDistributionPoint(SecCRVCRef rvc) {
398 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
399 CFArrayRef crlDPs = SecCertificateGetCRLDistributionPoints(cert);
400 if (crlDPs) {
401 CFIndex crlDPCount = CFArrayGetCount(crlDPs);
402 if (crlDPCount >= CRL_REQUEST_THRESHOLD) {
403 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount);
404 return NULL;
405 }
406 while (rvc->distributionPointIX < crlDPCount && rvc->distributionPointIX < MAX_CRL_DPS) {
407 CFURLRef distributionPoint = CFArrayGetValueAtIndex(crlDPs, rvc->distributionPointIX);
408 rvc->distributionPointIX++;
409 CFStringRef scheme = CFURLCopyScheme(distributionPoint);
410 if (scheme) {
411 /* We only support http and https responders currently. */
412 bool valid_DP = (CFEqual(CFSTR("http"), scheme) ||
413 CFEqual(CFSTR("https"), scheme) ||
414 CFEqual(CFSTR("ldap"), scheme));
415 CFRelease(scheme);
416 if (valid_DP)
417 return distributionPoint;
418 }
419 }
420 }
421 return NULL;
422 }
423
424 static void SecCRVCGetCRLStatus(SecCRVCRef rvc) {
425 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
426 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
427 CFArrayRef serializedCertPath = SecCertificatePathVCCreateSerialized(path);
428 secdebug("rvc", "searching CRL cache for cert: %ld", rvc->certIX);
429 rvc->status = SecTrustLegacyCRLStatus(cert, serializedCertPath, rvc->distributionPoint);
430 CFReleaseNull(serializedCertPath);
431 /* we got a response indicating that the CRL was checked */
432 if (rvc->status == errSecSuccess || rvc->status == errSecCertificateRevoked) {
433 rvc->done = true;
434 /* ocspd doesn't give us the nextUpdate time, so set to default */
435 rvc->nextUpdate = SecPathBuilderGetVerifyTime(rvc->builder) + kSecDefaultCRLTTL;
436 }
437 }
438
439 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc) {
440 while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
441 SecCRVCGetCRLStatus(rvc);
442 if (rvc->status == errSecCertificateRevoked) {
443 return;
444 }
445 }
446 }
447
448 /* Fire off an async http request for this certs revocation status, return
449 false if request was queued, true if we're done. */
450 static bool SecCRVCFetchNext(SecCRVCRef rvc) {
451 while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
452 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
453 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
454 CFArrayRef serializedCertPath = SecCertificatePathVCCreateSerialized(path);
455 secinfo("rvc", "fetching CRL for cert: %ld", rvc->certIX);
456 if (!SecTrustLegacyCRLFetch(&rvc->async_ocspd, rvc->distributionPoint,
457 CFAbsoluteTimeGetCurrent(), cert, serializedCertPath)) {
458 CFDataRef clientAuditToken = NULL;
459 SecTaskRef task = NULL;
460 audit_token_t auditToken = {};
461 clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->builder);
462 require(clientAuditToken, out);
463 require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out);
464 CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken);
465 require(task = SecTaskCreateWithAuditToken(NULL, auditToken), out);
466 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
467 rvc->distributionPoint, task);
468
469 out:
470 CFReleaseNull(clientAuditToken);
471 CFReleaseNull(task);
472 /* Async request was posted, wait for reply. */
473 return false;
474 }
475 }
476 rvc->done = true;
477 return true;
478 }
479
480 static void SecCRVCUpdatePVC(SecCRVCRef rvc) {
481 if (rvc->status == errSecCertificateRevoked) {
482 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex, rvc->certIX);
483 SInt32 reason = 0; // unspecified, since ocspd didn't tell us
484 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
485 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
486 cfreason, true);
487 if (rvc->builder) {
488 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
489 if (info) {
490 /* make the revocation reason available in the trust result */
491 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
492 }
493 }
494 CFReleaseNull(cfreason);
495 }
496 }
497
498 static void SecCRVCFetchCompleted(async_ocspd_t *ocspd) {
499 SecCRVCRef rvc = ocspd->info;
500 SecPathBuilderRef builder = rvc->builder;
501 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(builder);
502 if (analytics) {
503 /* Add the time this fetch took to complete to the total time */
504 analytics->crl_fetch_time += (mach_absolute_time() - ocspd->start_time);
505 }
506 /* we got a response indicating that the CRL was checked */
507 if (ocspd->response == errSecSuccess || ocspd->response == errSecCertificateRevoked) {
508 rvc->status = ocspd->response;
509 rvc->done = true;
510 /* ocspd doesn't give us the nextUpdate time, so set to default */
511 rvc->nextUpdate = SecPathBuilderGetVerifyTime(rvc->builder) + kSecDefaultCRLTTL;
512 secdebug("rvc", "got CRL response for cert: %ld", rvc->certIX);
513 SecCRVCUpdatePVC(rvc);
514 if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
515 secdebug("rvc", "done with all async jobs");
516 SecPathBuilderStep(builder);
517 }
518 } else {
519 if (analytics) {
520 /* We didn't get any data back, so the fetch failed */
521 analytics->crl_fetch_failed++;
522 }
523 if(SecCRVCFetchNext(rvc)) {
524 if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
525 secdebug("rvc", "done with all async jobs");
526 SecPathBuilderStep(builder);
527 }
528 }
529 }
530 }
531
532 static SecCRVCRef SecCRVCCreate(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
533 SecCRVCRef crvc = NULL;
534 crvc = malloc(sizeof(struct OpaqueSecCRVC));
535 if (crvc) {
536 memset(crvc, 0, sizeof(struct OpaqueSecCRVC));
537 crvc->builder = builder;
538 crvc->rvc = rvc;
539 crvc->certIX = certIX;
540 crvc->status = errSecInternal;
541 crvc->distributionPointIX = 0;
542 crvc->distributionPoint = NULL;
543 crvc->nextUpdate = NULL_TIME;
544 crvc->async_ocspd.queue = SecPathBuilderGetQueue(builder);
545 crvc->async_ocspd.completed = SecCRVCFetchCompleted;
546 crvc->async_ocspd.response = errSecInternal;
547 crvc->async_ocspd.info = crvc;
548 crvc->done = false;
549 }
550 return crvc;
551 }
552
553 static bool SecRVCShouldCheckCRL(SecRVCRef rvc) {
554 CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
555 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(rvc->builder);
556 if (revocation_method &&
557 CFEqual(kSecPolicyCheckRevocationCRL, revocation_method)) {
558 /* Our client insists on CRLs */
559 secinfo("rvc", "client told us to check CRL");
560 if (analytics) {
561 analytics->crl_client = true;
562 }
563 return true;
564 }
565 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
566 CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
567 if ((!ocspResponders || CFArrayGetCount(ocspResponders) == 0) &&
568 (revocation_method && !CFEqual(kSecPolicyCheckRevocationOCSP, revocation_method))) {
569 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
570 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
571 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc->certIX);
572 if (analytics) {
573 analytics->crl_cert = true;
574 }
575 return true;
576 }
577 return false;
578 }
579 #endif /* ENABLE_CRLS */
580
581 void SecRVCDelete(SecRVCRef rvc) {
582 secdebug("alloc", "delete rvc %p", rvc);
583 if (rvc->orvc) {
584 SecORVCFinish(rvc->orvc);
585 free(rvc->orvc);
586 rvc->orvc = NULL;
587 }
588 #if ENABLE_CRLS
589 if (rvc->crvc) {
590 SecCRVCFinish(rvc->crvc);
591 free(rvc->crvc);
592 rvc->crvc = NULL;
593 }
594 #endif
595 if (rvc->valid_info) {
596 CFReleaseNull(rvc->valid_info);
597 }
598 }
599
600 // Forward declaration
601 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc);
602
603 static void SecRVCInit(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
604 secdebug("alloc", "rvc %p", rvc);
605 rvc->builder = builder;
606 rvc->certIX = certIX;
607 rvc->orvc = SecORVCCreate(rvc, builder, certIX);
608 #if ENABLE_CRLS
609 rvc->crvc = SecCRVCCreate(rvc, builder, certIX);
610 #endif
611 if (!rvc->orvc
612 #if ENABLE_CRLS
613 || !rvc->crvc
614 #endif
615 ) {
616 SecRVCDelete(rvc);
617 SecRVCSetFinishedWithoutNetwork(rvc);
618 } else {
619 rvc->done = false;
620 }
621 }
622
623 #if ENABLE_CRLS
624 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
625 CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
626 if (!revocation_method
627 || !CFEqual(revocation_method, kSecPolicyCheckRevocationCRL)) {
628 return true;
629 }
630 return false;
631 }
632 #else
633 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
634 return true;
635 }
636 #endif
637
638 static void SecRVCProcessValidDateConstraints(SecRVCRef rvc) {
639 if (!rvc || !rvc->valid_info || !rvc->builder) {
640 return;
641 }
642 if (!rvc->valid_info->hasDateConstraints) {
643 return;
644 }
645 SecCertificateRef certificate = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
646 if (!certificate) {
647 return;
648 }
649 CFAbsoluteTime certIssued = SecCertificateNotValidBefore(certificate);
650 CFAbsoluteTime caNotBefore = -3155760000.0; /* default: 1901-01-01 00:00:00-0000 */
651 CFAbsoluteTime caNotAfter = 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
652 if (rvc->valid_info->notBeforeDate) {
653 caNotBefore = CFDateGetAbsoluteTime(rvc->valid_info->notBeforeDate);
654 }
655 if (rvc->valid_info->notAfterDate) {
656 caNotAfter = CFDateGetAbsoluteTime(rvc->valid_info->notAfterDate);
657 /* per the Valid specification, if this date is in the past, we need to check CT. */
658 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
659 if (caNotAfter < now) {
660 rvc->valid_info->requireCT = true;
661 }
662 }
663 if ((certIssued < caNotBefore) && (rvc->certIX > 0)) {
664 /* not-before constraint is only applied to leaf certificate, for now. */
665 return;
666 }
667
668 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(rvc->builder);
669 if ((certIssued < caNotBefore) || (certIssued > caNotAfter)) {
670 /* We are outside the constrained validity period. */
671 secnotice("rvc", "certificate issuance date not within the allowed range for this CA%s",
672 (rvc->valid_info->overridable) ? "" : " (non-recoverable error)");
673 if (analytics) {
674 analytics->valid_status |= TAValidDateContrainedRevoked;
675 }
676 if (rvc->valid_info->overridable) {
677 /* error is recoverable, treat certificate as untrusted
678 (note this date check is different from kSecPolicyCheckTemporalValidity) */
679 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckGrayListedKey, rvc->certIX,
680 kCFBooleanFalse, true);
681 } else {
682 /* error is non-overridable, treat certificate as revoked */
683 SInt32 reason = 0; /* unspecified reason code */
684 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
685 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
686 cfreason, true);
687 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
688 if (info) {
689 /* make the revocation reason available in the trust result */
690 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
691 }
692 CFReleaseNull(cfreason);
693 }
694 } else if (analytics) {
695 analytics->valid_status |= TAValidDateConstrainedOK;
696 }
697 }
698
699 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc) {
700 if (!rvc || !rvc->valid_info) {
701 return false;
702 }
703 SecValidInfoRef info = rvc->valid_info;
704 /* outcomes as defined in Valid server specification */
705 if (info->format == kSecValidInfoFormatSerial ||
706 info->format == kSecValidInfoFormatSHA256) {
707 if (info->noCACheck || info->complete || info->isOnList) {
708 return true;
709 }
710 } else { /* info->format == kSecValidInfoFormatNto1 */
711 if (info->noCACheck || (info->complete && !info->isOnList)) {
712 return true;
713 }
714 }
715 return false;
716 }
717
718 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc) {
719 if (!rvc || !rvc->valid_info) {
720 return false;
721 }
722 SecValidInfoRef info = rvc->valid_info;
723 /* either not present on an allowlist, or present on a blocklist */
724 return (!info->isOnList && info->valid) || (info->isOnList && !info->valid);
725 }
726
727 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc) {
728 if (!rvc || !rvc->valid_info || !rvc->builder) {
729 return;
730 }
731 if (rvc->valid_info->overridable) {
732 /* error is recoverable, treat certificate as untrusted */
733 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckGrayListedLeaf, rvc->certIX,
734 kCFBooleanFalse, true);
735 return;
736 }
737 /* error is fatal at this point */
738 if (!SecRVCHasRevokedValidInfo(rvc) || rvc->valid_info->noCACheck) {
739 /* result key should indicate blocked instead of revoked,
740 * but result must be non-recoverable */
741 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckBlackListedLeaf, rvc->certIX,
742 kCFBooleanFalse, true);
743 return;
744 }
745 SInt32 reason = 0; /* unspecified, since the Valid db doesn't tell us */
746 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
747 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
748 cfreason, true);
749 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
750 if (info) {
751 /* make the revocation reason available in the trust result */
752 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
753 }
754 CFReleaseNull(cfreason);
755 }
756
757 static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
758 if (!rvc || !rvc->valid_info || !rvc->builder) {
759 return;
760 }
761 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
762 SecValidInfoRef info = rvc->valid_info;
763
764 bool definitive = SecRVCHasDefinitiveValidInfo(rvc);
765 bool revoked = SecRVCHasRevokedValidInfo(rvc);
766
767 /* set analytics */
768 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(rvc->builder);
769 if (analytics) {
770 if (revoked) {
771 analytics->valid_status |= definitive ? TAValidDefinitelyRevoked : TAValidProbablyRevoked;
772 } else {
773 analytics->valid_status |= definitive ? TAValidDefinitelyOK : TAValidProbablyOK;
774 }
775 analytics->valid_require_ct |= info->requireCT;
776 analytics->valid_known_intermediates_only |= info->knownOnly;
777 }
778
779 /* Handle no-ca cases */
780 if (info->noCACheck) {
781 bool allowed = (info->valid && info->complete && info->isOnList);
782 if (revoked) {
783 /* definitely revoked */
784 SecRVCSetValidDeterminedErrorResult(rvc);
785 } else if (allowed) {
786 /* definitely not revoked (allowlisted) */
787 SecCertificatePathVCSetIsAllowlisted(path, true);
788 }
789 /* no-ca is definitive; no need to check further. */
790 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
791 (allowed) ? "allowed" : "revoked", rvc->certIX);
792 rvc->done = true;
793 return;
794 }
795
796 /* Handle date constraints, if present.
797 * Note: a not-after date may set the CT requirement,
798 * so check requireCT after this function is called. */
799 SecRVCProcessValidDateConstraints(rvc);
800
801 /* Set CT requirement on path, if present. */
802 if (info->requireCT) {
803 SecPathCTPolicy ctp = kSecPathCTRequired;
804 if (info->overridable) {
805 ctp = kSecPathCTRequiredOverridable;
806 }
807 SecCertificatePathVCSetRequiresCT(path, ctp);
808 }
809
810 /* Trigger OCSP for any non-definitive or revoked cases */
811 if (!definitive || revoked) {
812 info->checkOCSP = true;
813 }
814
815 if (info->checkOCSP) {
816 CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
817 CFIndex issuerIX = rvc->certIX + 1;
818 if (issuerIX >= count) {
819 /* cannot perform a revocation check on the last cert in the
820 chain, since we don't have its issuer. */
821 return;
822 }
823 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex " (will check OCSP)",
824 (info->complete) ? "" : "possibly ", (info->valid) ? "allowed" : "revoked",
825 rvc->certIX);
826 SecPathBuilderSetRevocationMethod(rvc->builder, kSecPolicyCheckRevocationAny);
827 if (analytics) {
828 /* Valid DB results caused us to do OCSP */
829 analytics->valid_trigger_ocsp = true;
830 }
831 }
832 }
833
834 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc) {
835 /* Skip checking for OCSP Signer verification */
836 if (SecPathBuilderGetPVCCount(rvc->builder) == 1) {
837 SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, 0);
838 if (!pvc) { return false; }
839 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
840 CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
841 if (policyName && CFEqual(policyName, CFSTR("OCSPSigner"))) {
842 return false;
843 }
844 }
845
846 /* Make sure revocation db info is up-to-date.
847 * We don't care if the builder is allowed to access the network because
848 * the network fetching does not block the trust evaluation. */
849 SecRevocationDbCheckNextUpdate();
850
851 /* Check whether we have valid db info for this cert,
852 given the cert and its issuer */
853 SecValidInfoRef info = NULL;
854 CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
855 if (count) {
856 bool isSelfSigned = false;
857 SecCertificateRef cert = NULL;
858 SecCertificateRef issuer = NULL;
859 CFIndex issuerIX = rvc->certIX + 1;
860 if (count > issuerIX) {
861 issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, issuerIX);
862 } else if (count == issuerIX) {
863 CFIndex rootIX = SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc->builder));
864 if (rootIX == rvc->certIX) {
865 issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, rootIX);
866 isSelfSigned = true;
867 }
868 }
869 cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
870 if (!isSelfSigned) {
871 /* skip revocation db check for self-signed certificates [33137065] */
872 info = SecRevocationDbCopyMatching(cert, issuer);
873 }
874 SecValidInfoSetAnchor(info, SecPathBuilderGetCertificateAtIndex(rvc->builder, count-1));
875 }
876 if (info) {
877 SecValidInfoRef old_info = rvc->valid_info;
878 rvc->valid_info = info;
879 if (old_info) {
880 CFReleaseNull(old_info);
881 }
882 return true;
883 }
884 return false;
885 }
886
887 static void SecRVCCheckRevocationCaches(SecRVCRef rvc) {
888 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
889 if (SecRVCShouldCheckOCSP(rvc) && (rvc->orvc->ocspRequest)) {
890 secdebug("ocsp", "Checking cached responses for cert %ld", rvc->certIX);
891 SecOCSPResponseRef response = NULL;
892 if (SecPathBuilderGetCheckRevocationOnline(rvc->builder)) {
893 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
894 response = SecOCSPCacheCopyMatchingWithMinInsertTime(rvc->orvc->ocspRequest, NULL, now - kSecOCSPResponseOnlineTTL);
895 } else {
896 response = SecOCSPCacheCopyMatching(rvc->orvc->ocspRequest, NULL);
897 }
898 SecORVCConsumeOCSPResponse(rvc->orvc, response, NULL_TIME, false, true);
899 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(rvc->builder);
900 if (rvc->orvc->done && analytics) {
901 /* We found a valid OCSP response in the cache */
902 analytics->ocsp_cache_hit = true;
903 }
904 }
905 #if ENABLE_CRLS
906 /* Don't check CRL cache if policy requested OCSP only */
907 if (SecRVCShouldCheckCRL(rvc)) {
908 SecCRVCCheckRevocationCache(rvc->crvc);
909 }
910 #endif
911 }
912
913 static void SecRVCUpdatePVC(SecRVCRef rvc) {
914 SecRVCProcessValidInfoResults(rvc); /* restore the results we got from Valid */
915 if (rvc->orvc) { SecORVCUpdatePVC(rvc->orvc); }
916 #if ENABLE_CRLS
917 if (rvc->crvc) { SecCRVCUpdatePVC(rvc->crvc); }
918 #endif
919 }
920
921 static void SecRVCSetFinishedWithoutNetwork(SecRVCRef rvc) {
922 rvc->done = true;
923 SecRVCUpdatePVC(rvc);
924 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
925 #if ENABLE_CRLS
926 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
927 #endif
928 }
929
930 static bool SecRVCFetchNext(SecRVCRef rvc) {
931 bool OCSP_fetch_finished = true;
932 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(rvc->builder);
933 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
934 if (SecRVCShouldCheckOCSP(rvc)) {
935 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
936 SecCertificateRef cert = SecCertificatePathVCGetCertificateAtIndex(path, rvc->certIX);
937 OCSP_fetch_finished = SecORVCBeginFetches(rvc->orvc, cert);
938 if (analytics && !OCSP_fetch_finished) {
939 /* We did a network OCSP fetch, set report appropriately */
940 analytics->ocsp_network = true;
941 }
942 }
943 if (OCSP_fetch_finished) {
944 /* we didn't start an OCSP background job for this cert */
945 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
946 }
947
948 #if ENABLE_CRLS
949 bool CRL_fetch_finished = true;
950 /* Don't check CRL cache if policy requested OCSP only */
951 if (SecRVCShouldCheckCRL(rvc)) {
952 /* reset the distributionPointIX because we already iterated through the CRLDPs
953 * in SecCRVCCheckRevocationCache */
954 rvc->crvc->distributionPointIX = 0;
955 CRL_fetch_finished &= SecCRVCFetchNext(rvc->crvc);
956 }
957 if (CRL_fetch_finished) {
958 /* we didn't start a CRL background job for this cert */
959 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
960 } else if (analytics) {
961 /* We did a CRL fetch */
962 analytics->crl_fetches++;
963 }
964 OCSP_fetch_finished &= CRL_fetch_finished;
965 #endif
966
967 return OCSP_fetch_finished;
968 }
969
970 /* The SecPathBuilder state machine calls SecPathBuilderCheckRevocation twice --
971 * once in the ValidatePath state, and again in the ComputeDetails state. In the
972 * ValidatePath state we've not yet run the path checks, so for callers who set
973 * kSecRevocationCheckIfTrusted, we don't do any networking on that first call.
974 * Here, if we've already done revocation before (so we're in ComputeDetails now),
975 * we need to recheck (and enable networking) for trusted chains and
976 * kSecRevocationCheckIfTrusted. Otherwise, we skip the checks to save on the processing
977 * but update the PVCs with the revocation results from the last check. */
978 static bool SecRevocationDidCheckRevocation(SecPathBuilderRef builder, bool *first_check_done) {
979 SecCertificatePathVCRef path = SecPathBuilderGetPath(builder);
980 if (!SecCertificatePathVCIsRevocationDone(path)) {
981 return false;
982 }
983 if (first_check_done) {
984 *first_check_done = true;
985 }
986
987 SecPVCRef resultPVC = SecPathBuilderGetResultPVC(builder);
988 bool recheck = false;
989 if (SecPathBuilderGetCheckRevocationIfTrusted(builder) && SecPVCIsOkResult(resultPVC)) {
990 recheck = true;
991 secdebug("rvc", "Rechecking revocation because network now allowed");
992 } else {
993 secdebug("rvc", "Not rechecking revocation");
994 }
995
996 if (recheck) {
997 // reset the RVCs for the second pass
998 SecCertificatePathVCDeleteRVCs(path);
999 } else {
1000 CFIndex certIX, certCount = SecPathBuilderGetCertificateCount(builder);
1001 for (certIX = 0; certIX < certCount; ++certIX) {
1002 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
1003 if (rvc) {
1004 SecRVCUpdatePVC(rvc);
1005 }
1006 }
1007 }
1008
1009 return !recheck;
1010 }
1011
1012 static bool SecRevocationCanAccessNetwork(SecPathBuilderRef builder, bool first_check_done) {
1013 /* CheckRevocationIfTrusted overrides NoNetworkAccess for revocation */
1014 if (SecPathBuilderGetCheckRevocationIfTrusted(builder)) {
1015 if (first_check_done) {
1016 /* We're on the second pass. We need to now allow networking for revocation.
1017 * SecRevocationDidCheckRevocation takes care of not running a second pass
1018 * if the chain isn't trusted. */
1019 return true;
1020 } else {
1021 /* We're on the first pass of the revocation checks, where we aren't
1022 * supposed to do networking because we don't know if the chain
1023 * is trusted yet. */
1024 return false;
1025 }
1026 }
1027 return SecPathBuilderCanAccessNetwork(builder);
1028 }
1029
1030 void SecPathBuilderCheckKnownIntermediateConstraints(SecPathBuilderRef builder) {
1031 SecCertificatePathVCRef path = SecPathBuilderGetPath(builder);
1032 if (!path) {
1033 return;
1034 }
1035 /* only perform this check once per path! */
1036 CFIndex certIX = kCFNotFound;
1037 if (SecCertificatePathVCCheckedIssuers(path)) {
1038 certIX = SecCertificatePathVCUnknownCAIndex(path);
1039 goto checkedIssuers;
1040 }
1041 /* check full path: start with anchor and decrement to leaf */
1042 bool parentConstrained = false;
1043 CFIndex certCount = SecPathBuilderGetCertificateCount(builder);
1044 for (certIX = certCount - 1; certIX >= 0; --certIX) {
1045 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
1046 if (!rvc) {
1047 continue;
1048 }
1049 if (parentConstrained && !rvc->valid_info) {
1050 /* Parent had the known-only constraint, but our issuer is unknown.
1051 Bump index to point back at the issuer since it fails the constraint. */
1052 certIX++;
1053 break;
1054 }
1055 parentConstrained = (rvc->valid_info && rvc->valid_info->knownOnly);
1056 if (parentConstrained) {
1057 secdebug("validupdate", "Valid db found a known-intermediate constraint on %@ (index=%ld)",
1058 rvc->valid_info->issuerHash, certIX+1);
1059 if (certIX == 0) {
1060 /* check special case: unknown constrained CA in leaf position */
1061 SecCertificateRef cert = SecCertificatePathVCGetCertificateAtIndex(path, certIX);
1062 if (cert && SecCertificateIsCA(cert) && !SecRevocationDbContainsIssuer(cert)) {
1063 /* leaf is a CA which violates the constraint */
1064 break;
1065 }
1066 }
1067 }
1068 }
1069 /* At this point, certIX will either be -1, indicating no CA was found
1070 which failed a known-intermediates-only constraint on its parent, or it
1071 will be the index of the first unknown CA which fails the constraint. */
1072 if (certIX >= 0) {
1073 secnotice("validupdate", "CA at index %ld violates known-intermediate constraint", certIX);
1074 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(builder);
1075 if (analytics) {
1076 analytics->valid_unknown_intermediate = true;
1077 }
1078 /* [40648172] For now, log this error but do not fail the evaluation. */
1079 certIX = -1;
1080 }
1081 SecCertificatePathVCSetUnknownCAIndex(path, certIX);
1082 SecCertificatePathVCSetCheckedIssuers(path, true);
1083
1084 checkedIssuers:
1085 if (certIX >= 0) {
1086 /* Error is set on CA certificate which failed the constraint. */
1087 SecRVCSetValidDeterminedErrorResult(SecCertificatePathVCGetRVCAtIndex(path, certIX));
1088 }
1089 }
1090
1091 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder) {
1092 secdebug("rvc", "checking revocation");
1093 CFIndex certIX, certCount = SecPathBuilderGetCertificateCount(builder);
1094 SecCertificatePathVCRef path = SecPathBuilderGetPath(builder);
1095 if (certCount <= 1) {
1096 /* Can't verify without an issuer; we're done */
1097 return true;
1098 }
1099
1100 bool first_check_done = false;
1101 if (SecRevocationDidCheckRevocation(builder, &first_check_done)) {
1102 return true;
1103 }
1104
1105 /* Setup things so we check revocation status of all certs. */
1106 SecCertificatePathVCAllocateRVCs(path, certCount);
1107
1108 /* Note that if we are multi threaded and a job completes after it
1109 is started but before we return from this function, we don't want
1110 a callback to decrement asyncJobCount to zero before we finish issuing
1111 all the jobs. To avoid this we pretend we issued certCount async jobs,
1112 and decrement pvc->asyncJobCount for each cert that we don't start a
1113 background fetch for. We include the root, even though we'll never start
1114 an async job for it so that we count all active threads for this eval. */
1115 #if !ENABLE_CRLS
1116 SecPathBuilderSetAsyncJobCount(builder, (unsigned int)(certCount));
1117 #else
1118 /* If we enable CRLS, we may end up with two async jobs per cert: one
1119 * for OCSP and one for fetching the CRL. Except the root, which only
1120 * needs to track this thread. */
1121 SecPathBuilderSetAsyncJobCount(builder, 2 * (unsigned int)(certCount) - 1);
1122 #endif
1123
1124 /* Loop though certificates again and issue an ocsp fetch if the
1125 revocation status checking isn't done yet (and we have an issuer!) */
1126 for (certIX = 0; certIX < certCount; ++certIX) {
1127 secdebug("rvc", "checking revocation for cert: %ld", certIX);
1128 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
1129 if (!rvc) {
1130 continue;
1131 }
1132
1133 SecRVCInit(rvc, builder, certIX);
1134
1135 /* RFC 6960: OCSP No-Check extension says that we shouldn't check revocation. */
1136 if (SecCertificateHasMarkerExtension(SecCertificatePathVCGetCertificateAtIndex(path, certIX),
1137 CFSTR("1.3.6.1.5.5.7.48.1.5"))) // id-pkix-ocsp-nocheck
1138 {
1139 secdebug("rvc", "skipping revocation checks for no-check cert: %ld", certIX);
1140 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(builder);
1141 if (analytics) {
1142 /* This certificate has OCSP No-Check, so add to reporting analytics */
1143 analytics->ocsp_no_check = true;
1144 }
1145 SecRVCSetFinishedWithoutNetwork(rvc);
1146 }
1147
1148 if (rvc->done) {
1149 continue;
1150 }
1151
1152 #if !TARGET_OS_BRIDGE
1153 /* Check valid database first (separate from OCSP response cache) */
1154 if (SecRVCCheckValidInfoDatabase(rvc)) {
1155 SecRVCProcessValidInfoResults(rvc);
1156 }
1157 #endif
1158 /* Any other revocation method requires an issuer certificate to verify the response;
1159 * skip the last cert in the chain since it doesn't have one. */
1160 if (certIX + 1 >= certCount) {
1161 continue;
1162 }
1163
1164 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1165 * policy specifically requested CRLs only. */
1166 if (SecRVCShouldCheckOCSP(rvc)) {
1167 /* If we have any OCSP stapled responses, check those first */
1168 SecORVCProcessStapledResponses(rvc->orvc);
1169 }
1170
1171 #if TARGET_OS_BRIDGE
1172 /* The bridge has no writeable storage and no network. Nothing else we can
1173 * do here. */
1174 SecRVCSetFinishedWithoutNetwork(rvc);
1175 continue;
1176 #endif
1177
1178 /* Then check the caches for revocation results. */
1179 SecRVCCheckRevocationCaches(rvc);
1180
1181 /* The check is done if we found cached responses from either method. */
1182 if (rvc->done || rvc->orvc->done
1183 #if ENABLE_CRLS
1184 || rvc->crvc->done
1185 #endif
1186 ) {
1187 secdebug("rvc", "found cached response for cert: %ld", certIX);
1188 SecRVCSetFinishedWithoutNetwork(rvc);
1189 continue;
1190 }
1191
1192 /* If we got a cached response that is no longer valid (which can only be true for
1193 * revoked responses), let's try to get a fresher response even if no one asked.
1194 * This check resolves unrevocation events after the nextUpdate time. */
1195 bool old_cached_response = (!rvc->done && rvc->orvc->ocspResponse);
1196
1197 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1198 async http request for this cert's revocation status, unless we already successfully checked
1199 the revocation status of this cert based on the cache or stapled responses. */
1200 bool allow_fetch = SecRevocationCanAccessNetwork(builder, first_check_done) &&
1201 (SecCertificatePathVCIsEV(path) || SecCertificatePathVCIsOptionallyEV(path) ||
1202 SecPathBuilderGetRevocationMethod(builder) || old_cached_response);
1203 if (rvc->done || !allow_fetch) {
1204 /* We got a cache hit or we aren't allowed to access the network */
1205 SecRVCUpdatePVC(rvc);
1206 /* We didn't really start any background jobs for this cert. */
1207 (void)SecPathBuilderDecrementAsyncJobCount(builder);
1208 #if ENABLE_CRLS
1209 (void)SecPathBuilderDecrementAsyncJobCount(builder);
1210 #endif
1211 } else {
1212 (void)SecRVCFetchNext(rvc);
1213 }
1214 }
1215
1216 /* Return false if there are still async jobs running. */
1217 /* builder->asyncJobCount is atomic, so we know that if the job count is 0, all other
1218 * threads are finished. If the job count is > 0, other threads will decrement the job
1219 * count and SecPathBuilderStep to crank the state machine when the job count is 0. */
1220 return (SecPathBuilderDecrementAsyncJobCount(builder) == 0);
1221 }
1222
1223 CFAbsoluteTime SecRVCGetEarliestNextUpdate(SecRVCRef rvc) {
1224 CFAbsoluteTime enu = NULL_TIME;
1225 if (!rvc || !rvc->orvc) { return enu; }
1226 enu = rvc->orvc->nextUpdate;
1227 #if ENABLE_CRLS
1228 CFAbsoluteTime crlNextUpdate = rvc->crvc->nextUpdate;
1229 if (enu == NULL_TIME ||
1230 ((crlNextUpdate > NULL_TIME) && (enu > crlNextUpdate))) {
1231 /* We didn't check OCSP or CRL next update time was sooner */
1232 enu = crlNextUpdate;
1233 }
1234 #endif
1235 return enu;
1236 }