]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecRevocationServer.c
60abe20a7e61162d45659e090e661711e23d5638
[apple/security.git] / OSX / sec / securityd / SecRevocationServer.c
1 /*
2 * Copyright (c) 2008-2017 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 <Security/SecCertificatePriv.h>
31 #include <Security/SecCertificateInternal.h>
32 #include <Security/SecPolicyPriv.h>
33 #include <Security/SecTrustPriv.h>
34 #include <Security/SecInternal.h>
35
36 #include <utilities/debugging.h>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/SecIOFormat.h>
39
40 #include <securityd/SecTrustServer.h>
41 #include <securityd/SecOCSPRequest.h>
42 #include <securityd/SecOCSPResponse.h>
43 #include <securityd/asynchttp.h>
44 #include <securityd/SecOCSPCache.h>
45 #include <securityd/SecRevocationDb.h>
46 #include <securityd/SecCertificateServer.h>
47
48 #include <securityd/SecRevocationServer.h>
49
50 // MARK: SecORVCRef
51 /********************************************************
52 ****************** OCSP RVC Functions ******************
53 ********************************************************/
54 const CFAbsoluteTime kSecDefaultOCSPResponseTTL = 24.0 * 60.0 * 60.0;
55 const CFAbsoluteTime kSecOCSPResponseOnlineTTL = 5.0 * 60.0;
56 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
57
58 /* OCSP Revocation verification context. */
59 struct OpaqueSecORVC {
60 /* Will contain the response data. */
61 asynchttp_t http;
62
63 /* Pointer to the builder for this revocation check. */
64 SecPathBuilderRef builder;
65
66 /* Pointer to the generic rvc for this revocation check */
67 SecRVCRef rvc;
68
69 /* The ocsp request we send to each responder. */
70 SecOCSPRequestRef ocspRequest;
71
72 /* The freshest response we received so far, from stapling or cache or responder. */
73 SecOCSPResponseRef ocspResponse;
74
75 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
76 SecOCSPSingleResponseRef ocspSingleResponse;
77
78 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
79 CFIndex certIX;
80
81 /* Index in array returned by SecCertificateGetOCSPResponders() for current
82 responder. */
83 CFIndex responderIX;
84
85 /* URL of current responder. */
86 CFURLRef responder;
87
88 /* Date until which this revocation status is valid. */
89 CFAbsoluteTime nextUpdate;
90
91 bool done;
92 };
93
94 static void SecORVCFinish(SecORVCRef orvc) {
95 secdebug("alloc", "%p", orvc);
96 asynchttp_free(&orvc->http);
97 if (orvc->ocspRequest) {
98 SecOCSPRequestFinalize(orvc->ocspRequest);
99 orvc->ocspRequest = NULL;
100 }
101 if (orvc->ocspResponse) {
102 SecOCSPResponseFinalize(orvc->ocspResponse);
103 orvc->ocspResponse = NULL;
104 if (orvc->ocspSingleResponse) {
105 SecOCSPSingleResponseDestroy(orvc->ocspSingleResponse);
106 orvc->ocspSingleResponse = NULL;
107 }
108 }
109 }
110
111 #define MAX_OCSP_RESPONDERS 3
112 #define OCSP_REQUEST_THRESHOLD 10
113
114 /* Return the next responder we should contact for this rvc or NULL if we
115 exhausted them all. */
116 static CFURLRef SecORVCGetNextResponder(SecORVCRef rvc) {
117 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
118 CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
119 if (ocspResponders) {
120 CFIndex responderCount = CFArrayGetCount(ocspResponders);
121 if (responderCount >= OCSP_REQUEST_THRESHOLD) {
122 secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount);
123 return NULL;
124 }
125 while (rvc->responderIX < responderCount && rvc->responderIX < MAX_OCSP_RESPONDERS) {
126 CFURLRef responder = CFArrayGetValueAtIndex(ocspResponders, rvc->responderIX);
127 rvc->responderIX++;
128 CFStringRef scheme = CFURLCopyScheme(responder);
129 if (scheme) {
130 /* We only support http and https responders currently. */
131 bool valid_responder = (CFEqual(CFSTR("http"), scheme) ||
132 CFEqual(CFSTR("https"), scheme));
133 CFRelease(scheme);
134 if (valid_responder)
135 return responder;
136 }
137 }
138 }
139 return NULL;
140 }
141
142 /* Fire off an async http request for this certs revocation status, return
143 false if request was queued, true if we're done. */
144 static bool SecORVCFetchNext(SecORVCRef rvc) {
145 while ((rvc->responder = SecORVCGetNextResponder(rvc))) {
146 CFDataRef request = SecOCSPRequestGetDER(rvc->ocspRequest);
147 if (!request)
148 goto errOut;
149
150 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc->certIX);
151 if (!asyncHttpPost(rvc->responder, request, OCSP_RESPONSE_TIMEOUT, &rvc->http)) {
152 /* Async request was posted, wait for reply. */
153 return false;
154 }
155 }
156
157 errOut:
158 rvc->done = true;
159 return true;
160 }
161
162 /* Process a verified ocsp response for a given cert. Return true if the
163 certificate status was obtained. */
164 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef this,
165 SecORVCRef rvc) {
166 bool processed;
167 switch (this->certStatus) {
168 case CS_Good:
169 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX);
170 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
171 in the info dictionary. */
172 //cert.revokeCheckGood(true);
173 rvc->nextUpdate = this->nextUpdate == NULL_TIME ? this->thisUpdate + kSecDefaultOCSPResponseTTL : this->nextUpdate;
174 processed = true;
175 break;
176 case CS_Revoked:
177 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex, rvc->certIX);
178 /* @@@ Mark cert as revoked (with reason) at revocation date in
179 the info dictionary, or perhaps we should use a different key per
180 reason? That way a client using exceptions can ignore some but
181 not all reasons. */
182 SInt32 reason = this->crlReason;
183 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
184 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
185 cfreason, true, kSecTrustResultFatalTrustFailure);
186 if (rvc->builder) {
187 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
188 if (info) {
189 /* make the revocation reason available in the trust result */
190 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
191 }
192 }
193 CFRelease(cfreason);
194 processed = true;
195 break;
196 case CS_Unknown:
197 /* not an error, no per-cert status, nothing here */
198 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
199 processed = false;
200 break;
201 default:
202 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
203 (int)this->certStatus, rvc->certIX);
204 processed = false;
205 break;
206 }
207
208 return processed;
209 }
210
211 static void SecORVCUpdatePVC(SecORVCRef rvc) {
212 if (rvc->ocspSingleResponse) {
213 SecOCSPSingleResponseProcess(rvc->ocspSingleResponse, rvc);
214 }
215 if (rvc->ocspResponse) {
216 rvc->nextUpdate = SecOCSPResponseGetExpirationTime(rvc->ocspResponse);
217 }
218 }
219
220 typedef void (^SecOCSPEvaluationCompleted)(SecTrustResultType tr);
221
222 static void
223 SecOCSPEvaluateCompleted(const void *userData,
224 SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
225 SecTrustResultType result) {
226 SecOCSPEvaluationCompleted evaluated = (SecOCSPEvaluationCompleted)userData;
227 evaluated(result);
228 Block_release(evaluated);
229
230 }
231
232 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc, CFArrayRef signers, CFArrayRef issuers, CFAbsoluteTime verifyTime) {
233 __block bool evaluated = false;
234 bool trusted = false;
235 if (!signers || !issuers) {
236 return trusted;
237 }
238
239 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
240 const void *ocspSigner = SecPolicyCreateOCSPSigner();
241 CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault,
242 &ocspSigner, 1, &kCFTypeArrayCallBacks);
243 CFRelease(ocspSigner);
244
245 SecOCSPEvaluationCompleted completed = Block_copy(^(SecTrustResultType result) {
246 if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
247 evaluated = true;
248 }
249 });
250
251 CFDataRef clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->builder);
252 SecPathBuilderRef oBuilder = SecPathBuilderCreate(clientAuditToken,
253 signers, issuers, true, false,
254 policies, NULL, NULL, NULL,
255 verifyTime, NULL, NULL,
256 SecOCSPEvaluateCompleted, completed);
257 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
258 SecPathBuilderStep(oBuilder);
259 CFReleaseNull(clientAuditToken);
260 CFReleaseNull(policies);
261
262 /* verify the public key of the issuer signed the OCSP signer */
263 if (evaluated) {
264 SecCertificateRef issuer = NULL, signer = NULL;
265 SecKeyRef issuerPubKey = NULL;
266
267 issuer = (SecCertificateRef)CFArrayGetValueAtIndex(issuers, 0);
268 signer = (SecCertificateRef)CFArrayGetValueAtIndex(signers, 0);
269
270 if (issuer) {
271 #if TARGET_OS_IPHONE
272 issuerPubKey = SecCertificateCopyPublicKey(issuer);
273 #else
274 issuerPubKey = SecCertificateCopyPublicKey_ios(issuer);
275 #endif
276 }
277 if (signer && issuerPubKey && (errSecSuccess == SecCertificateIsSignedBy(signer, issuerPubKey))) {
278 trusted = true;
279 } else {
280 secnotice("ocsp", "ocsp signer cert not signed by issuer");
281 }
282 CFReleaseNull(issuerPubKey);
283 }
284
285 return trusted;
286 }
287
288 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecORVCRef rvc, CFAbsoluteTime verifyTime) {
289 bool trusted;
290 SecCertificatePathVCRef issuers = SecCertificatePathVCCopyFromParent(SecPathBuilderGetPath(rvc->builder), rvc->certIX + 1);
291 SecCertificateRef issuer = issuers ? CFRetainSafe(SecCertificatePathVCGetCertificateAtIndex(issuers, 0)) : NULL;
292 CFArrayRef signers = SecOCSPResponseCopySigners(ocspResponse);
293 SecCertificateRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer);
294
295 if (signer && signers) {
296 if (issuer && CFEqual(signer, issuer)) {
297 /* We already know we trust issuer since it's the issuer of the
298 * cert we are verifying. */
299 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
300 rvc->responder);
301 trusted = true;
302 } else {
303 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
304 rvc->responder);
305 CFMutableArrayRef signerCerts = NULL;
306 CFArrayRef issuerCerts = NULL;
307
308 /* Ensure the signer cert is the 0th cert for trust evaluation */
309 signerCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
310 CFArrayAppendValue(signerCerts, signer);
311 CFArrayAppendArray(signerCerts, signers, CFRangeMake(0, CFArrayGetCount(signers)));
312
313 if (issuers) {
314 issuerCerts = SecCertificatePathVCCopyCertificates(issuers);
315 }
316
317 if (SecOCSPResponseEvaluateSigner(rvc, signerCerts, issuerCerts, verifyTime)) {
318 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
319 rvc->responder);
320 trusted = true;
321 } else {
322 /* @@@ We don't trust the cert so don't use this response. */
323 secnotice("ocsp", "ocsp response signed by certificate which "
324 "does not satisfy ocspSigner policy");
325 trusted = false;
326 }
327 CFReleaseNull(signerCerts);
328 CFReleaseNull(issuerCerts);
329 }
330 } else {
331 /* @@@ No signer found for this ocsp response, discard it. */
332 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
333 rvc->responder);
334 trusted = false;
335 }
336
337 #if DUMP_OCSPRESPONSES
338 char buf[40];
339 snprintf(buf, 40, "/tmp/ocspresponse%ld%s.der",
340 rvc->certIX, (trusted ? "t" : "u"));
341 secdumpdata(ocspResponse->data, buf);
342 #endif
343 CFReleaseNull(issuers);
344 CFReleaseNull(issuer);
345 CFReleaseNull(signers);
346 CFReleaseNull(signer);
347 return trusted;
348 }
349
350 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse /*CF_CONSUMED*/, CFTimeInterval maxAge, bool updateCache) {
351 SecOCSPSingleResponseRef sr = NULL;
352 require_quiet(ocspResponse, errOut);
353 SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse);
354 require_action_quiet(orStatus == kSecOCSPSuccess, errOut,
355 secnotice("ocsp", "responder: %@ returned status: %d", rvc->responder, orStatus));
356 require_action_quiet(sr = SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest), errOut,
357 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc->responder));
358 // Check if this response is fresher than any (cached) response we might still have in the rvc.
359 require_quiet(!rvc->ocspSingleResponse || rvc->ocspSingleResponse->thisUpdate < sr->thisUpdate, errOut);
360
361 CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
362 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
363 check whether the leaf was revoked (we are already checking the rest of
364 the chain). */
365 /* Check the OCSP response signature and verify the response. */
366 require_quiet(SecOCSPResponseVerify(ocspResponse, rvc,
367 sr->certStatus == CS_Revoked ? SecOCSPResponseProducedAt(ocspResponse) : verifyTime), errOut);
368
369 // If we get here, we have a properly signed ocsp response
370 // but we haven't checked dates yet.
371
372 bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
373 if (sr->certStatus == CS_Good) {
374 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
375 require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
376 } else if (sr->certStatus == CS_Revoked) {
377 // Expire revoked responses when the subject certificate itself expires.
378 ocspResponse->expireTime = SecCertificateNotValidAfter(SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX));
379 }
380
381 // Ok we like the new response, let's toss the old one.
382 if (updateCache)
383 SecOCSPCacheReplaceResponse(rvc->ocspResponse, ocspResponse, rvc->responder, verifyTime);
384
385 if (rvc->ocspResponse) SecOCSPResponseFinalize(rvc->ocspResponse);
386 rvc->ocspResponse = ocspResponse;
387 ocspResponse = NULL;
388
389 if (rvc->ocspSingleResponse) SecOCSPSingleResponseDestroy(rvc->ocspSingleResponse);
390 rvc->ocspSingleResponse = sr;
391 sr = NULL;
392
393 rvc->done = sr_valid;
394
395 errOut:
396 if (sr) SecOCSPSingleResponseDestroy(sr);
397 if (ocspResponse) SecOCSPResponseFinalize(ocspResponse);
398 }
399
400 /* Callback from async http code after an ocsp response has been received. */
401 static void SecOCSPFetchCompleted(asynchttp_t *http, CFTimeInterval maxAge) {
402 SecORVCRef rvc = (SecORVCRef)http->info;
403 SecPathBuilderRef builder = rvc->builder;
404 SecOCSPResponseRef ocspResponse = NULL;
405 if (http->response) {
406 CFDataRef data = CFHTTPMessageCopyBody(http->response);
407 if (data) {
408 /* Parse the returned data as if it's an ocspResponse. */
409 ocspResponse = SecOCSPResponseCreate(data);
410 CFRelease(data);
411 }
412 }
413
414 SecORVCConsumeOCSPResponse(rvc, ocspResponse, maxAge, true);
415 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
416
417 if (!rvc->done) {
418 /* Clear the data for the next response. */
419 asynchttp_free(http);
420 SecORVCFetchNext(rvc);
421 }
422
423 if (rvc->done) {
424 secdebug("rvc", "got OCSP response for cert: %ld", rvc->certIX);
425 SecORVCUpdatePVC(rvc);
426 if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
427 secdebug("rvc", "done with all async jobs");
428 SecPathBuilderStep(builder);
429 }
430 }
431 }
432
433 static SecORVCRef SecORVCCreate(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
434 SecORVCRef orvc = NULL;
435 orvc = malloc(sizeof(struct OpaqueSecORVC));
436 if (orvc) {
437 memset(orvc, 0, sizeof(struct OpaqueSecORVC));
438 orvc->builder = builder;
439 orvc->rvc = rvc;
440 orvc->certIX = certIX;
441 orvc->http.queue = SecPathBuilderGetQueue(builder);
442 orvc->http.token = SecPathBuilderCopyClientAuditToken(builder);
443 orvc->http.completed = SecOCSPFetchCompleted;
444 orvc->http.info = orvc;
445 orvc->ocspRequest = NULL;
446 orvc->responderIX = 0;
447 orvc->responder = NULL;
448 orvc->nextUpdate = NULL_TIME;
449 orvc->ocspResponse = NULL;
450 orvc->ocspSingleResponse = NULL;
451 orvc->done = false;
452
453 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(builder, certIX);
454 if (SecPathBuilderGetCertificateCount(builder) > (certIX + 1)) {
455 SecCertificateRef issuer = SecPathBuilderGetCertificateAtIndex(builder, certIX + 1);
456 orvc->ocspRequest = SecOCSPRequestCreate(cert, issuer);
457 }
458 }
459 return orvc;
460 }
461
462 static void SecORVCProcessStapledResponses(SecORVCRef rvc) {
463 /* Get stapled OCSP responses */
464 CFArrayRef ocspResponsesData = SecPathBuilderCopyOCSPResponses(rvc->builder);
465
466 if(ocspResponsesData) {
467 secdebug("rvc", "Checking stapled responses for cert %ld", rvc->certIX);
468 CFArrayForEach(ocspResponsesData, ^(const void *value) {
469 SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
470 SecORVCConsumeOCSPResponse(rvc, ocspResponse, NULL_TIME, false);
471 });
472 CFRelease(ocspResponsesData);
473 }
474 }
475
476 // MARK: SecCRVCRef
477 /********************************************************
478 ******************* CRL RVC Functions ******************
479 ********************************************************/
480 #if ENABLE_CRLS
481 #include <../trustd/macOS/SecTrustOSXEntryPoints.h>
482 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
483
484 /* CRL Revocation verification context. */
485 struct OpaqueSecCRVC {
486 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
487 async_ocspd_t async_ocspd;
488
489 /* Pointer to the builder for this revocation check. */
490 SecPathBuilderRef builder;
491
492 /* Pointer to the generic rvc for this revocation check */
493 SecRVCRef rvc;
494
495 /* The current CRL status from ocspd. */
496 OSStatus status;
497
498 /* Index of cert in builder that this RVC is for 0 = leaf, etc. */
499 CFIndex certIX;
500
501 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
502 current distribution point. */
503 CFIndex distributionPointIX;
504
505 /* URL of current distribution point. */
506 CFURLRef distributionPoint;
507
508 /* Date until which this revocation status is valid. */
509 CFAbsoluteTime nextUpdate;
510
511 bool done;
512 };
513
514 static void SecCRVCFinish(SecCRVCRef crvc) {
515 // nothing yet
516 }
517
518 #define MAX_CRL_DPS 3
519 #define CRL_REQUEST_THRESHOLD 10
520
521 static CFURLRef SecCRVCGetNextDistributionPoint(SecCRVCRef rvc) {
522 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
523 CFArrayRef crlDPs = SecCertificateGetCRLDistributionPoints(cert);
524 if (crlDPs) {
525 CFIndex crlDPCount = CFArrayGetCount(crlDPs);
526 if (crlDPCount >= CRL_REQUEST_THRESHOLD) {
527 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount);
528 return NULL;
529 }
530 while (rvc->distributionPointIX < crlDPCount && rvc->distributionPointIX < MAX_CRL_DPS) {
531 CFURLRef distributionPoint = CFArrayGetValueAtIndex(crlDPs, rvc->distributionPointIX);
532 rvc->distributionPointIX++;
533 CFStringRef scheme = CFURLCopyScheme(distributionPoint);
534 if (scheme) {
535 /* We only support http and https responders currently. */
536 bool valid_DP = (CFEqual(CFSTR("http"), scheme) ||
537 CFEqual(CFSTR("https"), scheme) ||
538 CFEqual(CFSTR("ldap"), scheme));
539 CFRelease(scheme);
540 if (valid_DP)
541 return distributionPoint;
542 }
543 }
544 }
545 return NULL;
546 }
547
548 static void SecCRVCGetCRLStatus(SecCRVCRef rvc) {
549 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
550 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
551 SecCertificatePathRef nonVCpath = SecCertificatePathVCCopyCertificatePath(path);
552 CFArrayRef serializedCertPath = SecCertificatePathCreateSerialized(nonVCpath, NULL);
553 CFReleaseNull(nonVCpath);
554 secdebug("rvc", "searching CRL cache for cert: %ld", rvc->certIX);
555 rvc->status = SecTrustLegacyCRLStatus(cert, serializedCertPath, rvc->distributionPoint);
556 CFReleaseNull(serializedCertPath);
557 /* we got a response indicating that the CRL was checked */
558 if (rvc->status == errSecSuccess || rvc->status == errSecCertificateRevoked) {
559 rvc->done = true;
560 /* ocspd doesn't give us the nextUpdate time, so set to default */
561 rvc->nextUpdate = SecPathBuilderGetVerifyTime(rvc->builder) + kSecDefaultCRLTTL;
562 }
563 }
564
565 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc) {
566 while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
567 SecCRVCGetCRLStatus(rvc);
568 if (rvc->status == errSecCertificateRevoked) {
569 return;
570 }
571 }
572 }
573
574 /* Fire off an async http request for this certs revocation status, return
575 false if request was queued, true if we're done. */
576 static bool SecCRVCFetchNext(SecCRVCRef rvc) {
577 while ((rvc->distributionPoint = SecCRVCGetNextDistributionPoint(rvc))) {
578 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
579 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
580 SecCertificatePathRef nonVCpath = SecCertificatePathVCCopyCertificatePath(path);
581 CFArrayRef serializedCertPath = SecCertificatePathCreateSerialized(nonVCpath, NULL);
582 CFReleaseNull(nonVCpath);
583 secinfo("rvc", "fetching CRL for cert: %ld", rvc->certIX);
584 if (!SecTrustLegacyCRLFetch(&rvc->async_ocspd, rvc->distributionPoint,
585 CFAbsoluteTimeGetCurrent(), cert, serializedCertPath)) {
586 CFDataRef clientAuditToken = NULL;
587 SecTaskRef task = NULL;
588 audit_token_t auditToken = {};
589 clientAuditToken = SecPathBuilderCopyClientAuditToken(rvc->builder);
590 require(clientAuditToken, out);
591 require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out);
592 CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken);
593 require(task = SecTaskCreateWithAuditToken(NULL, auditToken), out);
594 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
595 rvc->distributionPoint, task);
596
597 out:
598 CFReleaseNull(clientAuditToken);
599 CFReleaseNull(task);
600 /* Async request was posted, wait for reply. */
601 return false;
602 }
603 }
604 rvc->done = true;
605 return true;
606 }
607
608 static void SecCRVCUpdatePVC(SecCRVCRef rvc) {
609 if (rvc->status == errSecCertificateRevoked) {
610 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex, rvc->certIX);
611 SInt32 reason = 0; // unspecified, since ocspd didn't tell us
612 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
613 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
614 cfreason, true, kSecTrustResultFatalTrustFailure);
615 if (rvc->builder) {
616 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
617 if (info) {
618 /* make the revocation reason available in the trust result */
619 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
620 }
621 }
622 CFReleaseNull(cfreason);
623 }
624 }
625
626 static void SecCRVCFetchCompleted(async_ocspd_t *ocspd) {
627 SecCRVCRef rvc = ocspd->info;
628 SecPathBuilderRef builder = rvc->builder;
629 /* we got a response indicating that the CRL was checked */
630 if (ocspd->response == errSecSuccess || ocspd->response == errSecCertificateRevoked) {
631 rvc->status = ocspd->response;
632 rvc->done = true;
633 /* ocspd doesn't give us the nextUpdate time, so set to default */
634 rvc->nextUpdate = SecPathBuilderGetVerifyTime(rvc->builder) + kSecDefaultCRLTTL;
635 secdebug("rvc", "got CRL response for cert: %ld", rvc->certIX);
636 SecCRVCUpdatePVC(rvc);
637 if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
638 secdebug("rvc", "done with all async jobs");
639 SecPathBuilderStep(builder);
640 }
641 } else {
642 if(SecCRVCFetchNext(rvc)) {
643 if (!SecPathBuilderDecrementAsyncJobCount(builder)) {
644 secdebug("rvc", "done with all async jobs");
645 SecPathBuilderStep(builder);
646 }
647 }
648 }
649 }
650
651 static SecCRVCRef SecCRVCCreate(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
652 SecCRVCRef crvc = NULL;
653 crvc = malloc(sizeof(struct OpaqueSecCRVC));
654 if (crvc) {
655 memset(crvc, 0, sizeof(struct OpaqueSecCRVC));
656 crvc->builder = builder;
657 crvc->rvc = rvc;
658 crvc->certIX = certIX;
659 crvc->status = errSecInternal;
660 crvc->distributionPointIX = 0;
661 crvc->distributionPoint = NULL;
662 crvc->nextUpdate = NULL_TIME;
663 crvc->async_ocspd.queue = SecPathBuilderGetQueue(builder);
664 crvc->async_ocspd.completed = SecCRVCFetchCompleted;
665 crvc->async_ocspd.response = errSecInternal;
666 crvc->async_ocspd.info = crvc;
667 crvc->done = false;
668 }
669 return crvc;
670 }
671
672 static bool SecRVCShouldCheckCRL(SecRVCRef rvc) {
673 CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
674 if (revocation_method &&
675 CFEqual(kSecPolicyCheckRevocationCRL, revocation_method)) {
676 /* Our client insists on CRLs */
677 secinfo("rvc", "client told us to check CRL");
678 return true;
679 }
680 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
681 CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert);
682 if ((!ocspResponders || CFArrayGetCount(ocspResponders) == 0) &&
683 (revocation_method && !CFEqual(kSecPolicyCheckRevocationOCSP, revocation_method))) {
684 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
685 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
686 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc->certIX);
687 return true;
688 }
689 return false;
690 }
691 #endif /* ENABLE_CRLS */
692
693 void SecRVCDelete(SecRVCRef rvc) {
694 if (rvc->orvc) {
695 SecORVCFinish(rvc->orvc);
696 free(rvc->orvc);
697 }
698 #if ENABLE_CRLS
699 if (rvc->crvc) {
700 SecCRVCFinish(rvc->crvc);
701 free(rvc->crvc);
702 }
703 #endif
704 if (rvc->valid_info) {
705 SecValidInfoRelease(rvc->valid_info);
706 }
707 }
708
709 static void SecRVCInit(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
710 secdebug("alloc", "%p", rvc);
711 rvc->builder = builder;
712 rvc->certIX = certIX;
713 rvc->orvc = SecORVCCreate(rvc, builder, certIX);
714 #if ENABLE_CRLS
715 rvc->crvc = SecCRVCCreate(rvc, builder, certIX);
716 #endif
717 rvc->done = false;
718 }
719
720 #if ENABLE_CRLS
721 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
722 CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
723 if (!revocation_method
724 || !CFEqual(revocation_method, kSecPolicyCheckRevocationCRL)) {
725 return true;
726 }
727 return false;
728 }
729 #else
730 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
731 return true;
732 }
733 #endif
734
735 static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
736 if (!rvc || !rvc->valid_info || !rvc->builder) {
737 return;
738 }
739 SecValidInfoFormat format = rvc->valid_info->format;
740 bool valid = rvc->valid_info->valid;
741 bool noCACheck = rvc->valid_info->noCACheck;
742 bool checkOCSP = rvc->valid_info->checkOCSP;
743 bool complete = rvc->valid_info->complete;
744 bool isOnList = rvc->valid_info->isOnList;
745 bool definitive = false;
746
747 if (format == kSecValidInfoFormatSerial || format == kSecValidInfoFormatSHA256) {
748 /* serial or hash list: could be blocked or allowed; could be incomplete */
749 if (((!valid && complete && isOnList) || (valid && complete && !isOnList)) && noCACheck) {
750 /* definitely revoked */
751 SInt32 reason = 0; /* unspecified, since the Valid db doesn't tell us */
752 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
753 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
754 cfreason, true, kSecTrustResultFatalTrustFailure);
755 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
756 if (info) {
757 /* make the revocation reason available in the trust result */
758 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
759 }
760 CFReleaseNull(cfreason);
761 definitive = true;
762 }
763 else if (valid && complete && isOnList && noCACheck) {
764 /* definitely not revoked (allowlisted) */
765 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
766 if (path) {
767 SecCertificatePathVCSetIsAllowlisted(path, true);
768 } else {
769 secdebug("validupdate", "rvc: no certificate path for builder");
770 }
771 definitive = true;
772 }
773 if (definitive) {
774 /* either definitely revoked or allowed; no need to check further. */
775 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
776 (valid && complete && isOnList) ? "allowed" : "revoked", rvc->certIX);
777 rvc->done = true;
778 return;
779 }
780 /* verify our info with the OCSP server */
781 checkOCSP = true;
782 }
783
784 /* Handle non-definitive information.
785 We set rvc->done = true above ONLY if the result was definitive;
786 otherwise we require a revocation check for SSL usage.
787 */
788 if (format == kSecValidInfoFormatNto1) {
789 /* matched the filter */
790 checkOCSP = true;
791 }
792
793 if (checkOCSP) {
794 CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
795 CFIndex issuerIX = rvc->certIX + 1;
796 if (issuerIX >= count) {
797 /* cannot perform a revocation check on the last cert in the
798 chain, since we don't have its issuer. */
799 return;
800 }
801 CFIndex pvcIX;
802 for (pvcIX = 0; pvcIX < SecPathBuilderGetPVCCount(rvc->builder); pvcIX++) {
803 SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, pvcIX);
804 if (!pvc) { continue; }
805 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
806 CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
807 if (policyName && CFEqual(CFSTR("sslServer"), policyName)) {
808 /* perform revocation check for SSL policy;
809 require for leaf if an OCSP responder is present. */
810 if (0 == rvc->certIX) {
811 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
812 CFArrayRef resps = (cert) ? SecCertificateGetOCSPResponders(cert) : NULL;
813 CFIndex rcount = (resps) ? CFArrayGetCount(resps) : 0;
814 if (rcount > 0) {
815 // %%% rdar://31279923
816 // This currently requires a valid revocation response for each cert,
817 // but we only want to require a leaf check. For now, do not require.
818 //SecPathBuilderSetRevocationResponseRequired(rvc->builder);
819 }
820 }
821 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex " (will check OCSP)",
822 (complete) ? "" : "possibly ", (valid) ? "allowed" : "revoked",
823 rvc->certIX);
824 SecPathBuilderSetRevocationMethod(rvc->builder, kSecPolicyCheckRevocationAny);
825 }
826 }
827 }
828 }
829
830 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc) {
831 /* Skip checking for OCSP Signer verification */
832 if (SecPathBuilderGetPVCCount(rvc->builder) == 1) {
833 SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, 0);
834 if (!pvc) { return false; }
835 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
836 CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
837 if (policyName && CFEqual(policyName, CFSTR("OCSPSigner"))) {
838 return false;
839 }
840 }
841
842 /* Make sure revocation db info is up-to-date.
843 * We don't care if the builder is allowed to access the network because
844 * the network fetching does not block the trust evaluation. */
845 SecRevocationDbCheckNextUpdate();
846
847 /* Check whether we have valid db info for this cert,
848 given the cert and its issuer */
849 SecValidInfoRef info = NULL;
850 CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
851 if (count) {
852 bool isSelfSigned = false;
853 SecCertificateRef cert = NULL;
854 SecCertificateRef issuer = NULL;
855 CFIndex issuerIX = rvc->certIX + 1;
856 if (count > issuerIX) {
857 issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, issuerIX);
858 } else if (count == issuerIX) {
859 CFIndex rootIX = SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc->builder));
860 if (rootIX == rvc->certIX) {
861 issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, rootIX);
862 isSelfSigned = true;
863 }
864 }
865 cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
866 if (!isSelfSigned) {
867 /* skip revocation db check for self-signed certificates [33137065] */
868 info = SecRevocationDbCopyMatching(cert, issuer);
869 }
870 SecValidInfoSetAnchor(info, SecPathBuilderGetCertificateAtIndex(rvc->builder, count-1));
871 }
872 if (info) {
873 SecValidInfoRef old_info = rvc->valid_info;
874 rvc->valid_info = info;
875 if (old_info) {
876 SecValidInfoRelease(old_info);
877 }
878 return true;
879 }
880 return false;
881 }
882
883 static void SecRVCCheckRevocationCaches(SecRVCRef rvc) {
884 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
885 if (SecRVCShouldCheckOCSP(rvc) && (rvc->orvc->ocspRequest)) {
886 secdebug("ocsp", "Checking cached responses for cert %ld", rvc->certIX);
887 SecOCSPResponseRef response = NULL;
888 if (SecPathBuilderGetCheckRevocationOnline(rvc->builder)) {
889 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
890 response = SecOCSPCacheCopyMatchingWithMinInsertTime(rvc->orvc->ocspRequest, NULL, now - kSecOCSPResponseOnlineTTL);
891 } else {
892 response = SecOCSPCacheCopyMatching(rvc->orvc->ocspRequest, NULL);
893 }
894 SecORVCConsumeOCSPResponse(rvc->orvc,
895 response,
896 NULL_TIME, false);
897 }
898 #if ENABLE_CRLS
899 /* Don't check CRL cache if policy requested OCSP only */
900 if (SecRVCShouldCheckCRL(rvc)) {
901 SecCRVCCheckRevocationCache(rvc->crvc);
902 }
903 #endif
904 }
905
906 static void SecRVCUpdatePVC(SecRVCRef rvc) {
907 SecRVCProcessValidInfoResults(rvc); /* restore the results we got from Valid */
908 SecORVCUpdatePVC(rvc->orvc);
909 #if ENABLE_CRLS
910 SecCRVCUpdatePVC(rvc->crvc);
911 #endif
912 }
913
914 static bool SecRVCFetchNext(SecRVCRef rvc) {
915 bool OCSP_fetch_finished = true;
916 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
917 if (SecRVCShouldCheckOCSP(rvc)) {
918 OCSP_fetch_finished &= SecORVCFetchNext(rvc->orvc);
919 }
920 if (OCSP_fetch_finished) {
921 /* we didn't start an OCSP background job for this cert */
922 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
923 }
924
925 #if ENABLE_CRLS
926 bool CRL_fetch_finished = true;
927 /* Don't check CRL cache if policy requested OCSP only */
928 if (SecRVCShouldCheckCRL(rvc)) {
929 /* reset the distributionPointIX because we already iterated through the CRLDPs
930 * in SecCRVCCheckRevocationCache */
931 rvc->crvc->distributionPointIX = 0;
932 CRL_fetch_finished &= SecCRVCFetchNext(rvc->crvc);
933 }
934 if (CRL_fetch_finished) {
935 /* we didn't start a CRL background job for this cert */
936 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
937 }
938 OCSP_fetch_finished &= CRL_fetch_finished;
939 #endif
940
941 return OCSP_fetch_finished;
942 }
943
944 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder) {
945 secdebug("rvc", "checking revocation");
946 CFIndex certIX, certCount = SecPathBuilderGetCertificateCount(builder);
947 SecCertificatePathVCRef path = SecPathBuilderGetPath(builder);
948 bool completed = true;
949 if (certCount <= 1) {
950 /* Can't verify without an issuer; we're done */
951 return completed;
952 }
953
954 /*
955 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
956 *
957 * Note: we can't check revocation for the last certificate in the chain
958 * via OCSP or CRL methods, since there isn't a separate issuer cert to
959 * sign those responses. However, since a self-signed root has an implied
960 * issuer of itself, we can check for it in the valid database.
961 */
962
963 if (SecCertificatePathVCIsRevocationDone(path)) {
964 /* We have done revocation checking already, set PVCs with results. */
965 for (certIX = 0; certIX < certCount; ++certIX) {
966 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
967 if (rvc) { SecRVCUpdatePVC(rvc); }
968 }
969 secdebug("rvc", "Not rechecking revocation");
970 return completed;
971 }
972
973 /* Setup things so we check revocation status of all certs. */
974 SecCertificatePathVCAllocateRVCs(path, certCount);
975
976 /* Note that if we are multi threaded and a job completes after it
977 is started but before we return from this function, we don't want
978 a callback to decrement asyncJobCount to zero before we finish issuing
979 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
980 and decrement pvc->asyncJobCount for each cert that we don't start a
981 background fetch for. (We will never start an async job for the final
982 cert in the chain.) */
983 #if !ENABLE_CRLS
984 SecPathBuilderSetAsyncJobCount(builder, (unsigned int)(certCount-1));
985 #else
986 /* If we enable CRLS, we may end up with two async jobs per cert: one
987 * for OCSP and one for fetching the CRL */
988 SecPathBuilderSetAsyncJobCount(builder, 2 * (unsigned int)(certCount-1));
989 #endif
990
991 /* Loop though certificates again and issue an ocsp fetch if the
992 revocation status checking isn't done yet (and we have an issuer!) */
993 for (certIX = 0; certIX < certCount; ++certIX) {
994 secdebug("rvc", "checking revocation for cert: %ld", certIX);
995 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
996 if (!rvc) {
997 continue;
998 }
999
1000 SecRVCInit(rvc, builder, certIX);
1001 if (rvc->done){
1002 continue;
1003 }
1004
1005 #if !TARGET_OS_BRIDGE
1006 /* Check valid database first (separate from OCSP response cache) */
1007 if (SecRVCCheckValidInfoDatabase(rvc)) {
1008 SecRVCProcessValidInfoResults(rvc);
1009 }
1010 #endif
1011 /* Any other revocation method requires an issuer certificate;
1012 * skip the last cert in the chain since it doesn't have one. */
1013 if (certIX+1 >= certCount) {
1014 continue;
1015 }
1016
1017 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1018 * policy specifically requested CRLs only. */
1019 if (SecRVCShouldCheckOCSP(rvc)) {
1020 /* If we have any OCSP stapled responses, check those first */
1021 SecORVCProcessStapledResponses(rvc->orvc);
1022 }
1023
1024 #if TARGET_OS_BRIDGE
1025 /* The bridge has no writeable storage and no network. Nothing else we can
1026 * do here. */
1027 rvc->done = true;
1028 return completed;
1029 #endif
1030
1031 /* Then check the caches for revocation results. */
1032 SecRVCCheckRevocationCaches(rvc);
1033
1034 /* The check is done if we found cached responses from either method. */
1035 if (rvc->orvc->done
1036 #if ENABLE_CRLS
1037 || rvc->orvc->done
1038 #endif
1039 ) {
1040 secdebug("rvc", "found cached response for cert: %ld", certIX);
1041 rvc->done = true;
1042 }
1043
1044 /* If we got a cached response that is no longer valid (which can only be true for
1045 * revoked responses), let's try to get a fresher response even if no one asked.
1046 * This check resolves unrevocation events after the nextUpdate time. */
1047 bool old_cached_response = (!rvc->done && rvc->orvc->ocspResponse);
1048
1049 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1050 async http request for this cert's revocation status, unless we already successfully checked
1051 the revocation status of this cert based on the cache or stapled responses. */
1052 bool allow_fetch = SecPathBuilderCanAccessNetwork(builder) &&
1053 (SecCertificatePathVCIsEV(path) || SecCertificatePathVCIsOptionallyEV(path) ||
1054 SecPathBuilderGetRevocationMethod(builder) || old_cached_response);
1055 bool fetch_done = true;
1056 if (rvc->done || !allow_fetch) {
1057 /* We got a cache hit or we aren't allowed to access the network */
1058 SecRVCUpdatePVC(rvc);
1059 /* We didn't really start any background jobs for this cert. */
1060 (void)SecPathBuilderDecrementAsyncJobCount(builder);
1061 #if ENABLE_CRLS
1062 (void)SecPathBuilderDecrementAsyncJobCount(builder);
1063 #endif
1064 } else {
1065 fetch_done = SecRVCFetchNext(rvc);
1066 }
1067 if (!fetch_done) {
1068 /* We started at least one background fetch. */
1069 secdebug("rvc", "waiting on background fetch for cert %ld", certIX);
1070 completed = false;
1071 }
1072 }
1073
1074 /* Return false if we started any background jobs. */
1075 /* We can't just return !builder->asyncJobCount here, since if we started any
1076 jobs the completion callback will be called eventually and it will call
1077 SecPathBuilderStep(). If for some reason everything completed before we
1078 get here we still want the outer SecPathBuilderStep() to terminate so we
1079 keep track of whether we started any jobs and return false if so. */
1080 return completed;
1081 }
1082
1083 CFAbsoluteTime SecRVCGetEarliestNextUpdate(SecRVCRef rvc) {
1084 CFAbsoluteTime enu = NULL_TIME;
1085 if (!rvc || !rvc->orvc) { return enu; }
1086 enu = rvc->orvc->nextUpdate;
1087 #if ENABLE_CRLS
1088 CFAbsoluteTime crlNextUpdate = rvc->crvc->nextUpdate;
1089 if (enu == NULL_TIME ||
1090 ((crlNextUpdate > NULL_TIME) && (enu > crlNextUpdate))) {
1091 /* We didn't check OCSP or CRL next update time was sooner */
1092 enu = crlNextUpdate;
1093 }
1094 #endif
1095 return enu;
1096 }