]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecRevocationServer.c
Security-58286.20.16.tar.gz
[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 rvc->orvc = NULL;
698 }
699 #if ENABLE_CRLS
700 if (rvc->crvc) {
701 SecCRVCFinish(rvc->crvc);
702 free(rvc->crvc);
703 rvc->crvc = NULL;
704 }
705 #endif
706 if (rvc->valid_info) {
707 SecValidInfoRelease(rvc->valid_info);
708 rvc->valid_info = NULL;
709 }
710 }
711
712 static void SecRVCInit(SecRVCRef rvc, SecPathBuilderRef builder, CFIndex certIX) {
713 secdebug("alloc", "%p", rvc);
714 rvc->builder = builder;
715 rvc->certIX = certIX;
716 rvc->orvc = SecORVCCreate(rvc, builder, certIX);
717 #if ENABLE_CRLS
718 rvc->crvc = SecCRVCCreate(rvc, builder, certIX);
719 #endif
720 if (!rvc->orvc
721 #if ENABLE_CRLS
722 || !rvc->crvc
723 #endif
724 ) {
725 SecRVCDelete(rvc);
726 rvc->done = true;
727 } else {
728 rvc->done = false;
729 }
730 }
731
732 #if ENABLE_CRLS
733 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
734 CFStringRef revocation_method = SecPathBuilderGetRevocationMethod(rvc->builder);
735 if (!revocation_method
736 || !CFEqual(revocation_method, kSecPolicyCheckRevocationCRL)) {
737 return true;
738 }
739 return false;
740 }
741 #else
742 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc) {
743 return true;
744 }
745 #endif
746
747 static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
748 if (!rvc || !rvc->valid_info || !rvc->builder) {
749 return;
750 }
751 SecValidInfoFormat format = rvc->valid_info->format;
752 bool valid = rvc->valid_info->valid;
753 bool noCACheck = rvc->valid_info->noCACheck;
754 bool checkOCSP = rvc->valid_info->checkOCSP;
755 bool complete = rvc->valid_info->complete;
756 bool isOnList = rvc->valid_info->isOnList;
757 bool definitive = false;
758
759 if (format == kSecValidInfoFormatSerial || format == kSecValidInfoFormatSHA256) {
760 /* serial or hash list: could be blocked or allowed; could be incomplete */
761 if (((!valid && complete && isOnList) || (valid && complete && !isOnList)) && noCACheck) {
762 /* definitely revoked */
763 SInt32 reason = 0; /* unspecified, since the Valid db doesn't tell us */
764 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
765 SecPathBuilderSetResultInPVCs(rvc->builder, kSecPolicyCheckRevocation, rvc->certIX,
766 cfreason, true, kSecTrustResultFatalTrustFailure);
767 CFMutableDictionaryRef info = SecPathBuilderGetInfo(rvc->builder);
768 if (info) {
769 /* make the revocation reason available in the trust result */
770 CFDictionarySetValue(info, kSecTrustRevocationReason, cfreason);
771 }
772 CFReleaseNull(cfreason);
773 definitive = true;
774 }
775 else if (valid && complete && isOnList && noCACheck) {
776 /* definitely not revoked (allowlisted) */
777 SecCertificatePathVCRef path = SecPathBuilderGetPath(rvc->builder);
778 if (path) {
779 SecCertificatePathVCSetIsAllowlisted(path, true);
780 } else {
781 secdebug("validupdate", "rvc: no certificate path for builder");
782 }
783 definitive = true;
784 }
785 if (definitive) {
786 /* either definitely revoked or allowed; no need to check further. */
787 secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
788 (valid && complete && isOnList) ? "allowed" : "revoked", rvc->certIX);
789 rvc->done = true;
790 return;
791 }
792 /* verify our info with the OCSP server */
793 checkOCSP = true;
794 }
795
796 /* Handle non-definitive information.
797 We set rvc->done = true above ONLY if the result was definitive;
798 otherwise we require a revocation check for SSL usage.
799 */
800 if (format == kSecValidInfoFormatNto1) {
801 /* matched the filter */
802 checkOCSP = true;
803 }
804
805 if (checkOCSP) {
806 CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
807 CFIndex issuerIX = rvc->certIX + 1;
808 if (issuerIX >= count) {
809 /* cannot perform a revocation check on the last cert in the
810 chain, since we don't have its issuer. */
811 return;
812 }
813 CFIndex pvcIX;
814 for (pvcIX = 0; pvcIX < SecPathBuilderGetPVCCount(rvc->builder); pvcIX++) {
815 SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, pvcIX);
816 if (!pvc) { continue; }
817 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
818 CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
819 if (policyName && CFEqual(CFSTR("sslServer"), policyName)) {
820 /* perform revocation check for SSL policy;
821 require for leaf if an OCSP responder is present. */
822 if (0 == rvc->certIX) {
823 SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
824 CFArrayRef resps = (cert) ? SecCertificateGetOCSPResponders(cert) : NULL;
825 CFIndex rcount = (resps) ? CFArrayGetCount(resps) : 0;
826 if (rcount > 0) {
827 // %%% rdar://31279923
828 // This currently requires a valid revocation response for each cert,
829 // but we only want to require a leaf check. For now, do not require.
830 //SecPathBuilderSetRevocationResponseRequired(rvc->builder);
831 }
832 }
833 secdebug("validupdate", "rvc: %s%s cert %" PRIdCFIndex " (will check OCSP)",
834 (complete) ? "" : "possibly ", (valid) ? "allowed" : "revoked",
835 rvc->certIX);
836 SecPathBuilderSetRevocationMethod(rvc->builder, kSecPolicyCheckRevocationAny);
837 }
838 }
839 }
840 }
841
842 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc) {
843 /* Skip checking for OCSP Signer verification */
844 if (SecPathBuilderGetPVCCount(rvc->builder) == 1) {
845 SecPVCRef pvc = SecPathBuilderGetPVCAtIndex(rvc->builder, 0);
846 if (!pvc) { return false; }
847 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, 0);
848 CFStringRef policyName = (policy) ? SecPolicyGetName(policy) : NULL;
849 if (policyName && CFEqual(policyName, CFSTR("OCSPSigner"))) {
850 return false;
851 }
852 }
853
854 /* Make sure revocation db info is up-to-date.
855 * We don't care if the builder is allowed to access the network because
856 * the network fetching does not block the trust evaluation. */
857 SecRevocationDbCheckNextUpdate();
858
859 /* Check whether we have valid db info for this cert,
860 given the cert and its issuer */
861 SecValidInfoRef info = NULL;
862 CFIndex count = SecPathBuilderGetCertificateCount(rvc->builder);
863 if (count) {
864 bool isSelfSigned = false;
865 SecCertificateRef cert = NULL;
866 SecCertificateRef issuer = NULL;
867 CFIndex issuerIX = rvc->certIX + 1;
868 if (count > issuerIX) {
869 issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, issuerIX);
870 } else if (count == issuerIX) {
871 CFIndex rootIX = SecCertificatePathVCSelfSignedIndex(SecPathBuilderGetPath(rvc->builder));
872 if (rootIX == rvc->certIX) {
873 issuer = SecPathBuilderGetCertificateAtIndex(rvc->builder, rootIX);
874 isSelfSigned = true;
875 }
876 }
877 cert = SecPathBuilderGetCertificateAtIndex(rvc->builder, rvc->certIX);
878 if (!isSelfSigned) {
879 /* skip revocation db check for self-signed certificates [33137065] */
880 info = SecRevocationDbCopyMatching(cert, issuer);
881 }
882 SecValidInfoSetAnchor(info, SecPathBuilderGetCertificateAtIndex(rvc->builder, count-1));
883 }
884 if (info) {
885 SecValidInfoRef old_info = rvc->valid_info;
886 rvc->valid_info = info;
887 if (old_info) {
888 SecValidInfoRelease(old_info);
889 }
890 return true;
891 }
892 return false;
893 }
894
895 static void SecRVCCheckRevocationCaches(SecRVCRef rvc) {
896 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
897 if (SecRVCShouldCheckOCSP(rvc) && (rvc->orvc->ocspRequest)) {
898 secdebug("ocsp", "Checking cached responses for cert %ld", rvc->certIX);
899 SecOCSPResponseRef response = NULL;
900 if (SecPathBuilderGetCheckRevocationOnline(rvc->builder)) {
901 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
902 response = SecOCSPCacheCopyMatchingWithMinInsertTime(rvc->orvc->ocspRequest, NULL, now - kSecOCSPResponseOnlineTTL);
903 } else {
904 response = SecOCSPCacheCopyMatching(rvc->orvc->ocspRequest, NULL);
905 }
906 SecORVCConsumeOCSPResponse(rvc->orvc,
907 response,
908 NULL_TIME, false);
909 }
910 #if ENABLE_CRLS
911 /* Don't check CRL cache if policy requested OCSP only */
912 if (SecRVCShouldCheckCRL(rvc)) {
913 SecCRVCCheckRevocationCache(rvc->crvc);
914 }
915 #endif
916 }
917
918 static void SecRVCUpdatePVC(SecRVCRef rvc) {
919 SecRVCProcessValidInfoResults(rvc); /* restore the results we got from Valid */
920 if (rvc->orvc) { SecORVCUpdatePVC(rvc->orvc); }
921 #if ENABLE_CRLS
922 if (rvc->crvc) { SecCRVCUpdatePVC(rvc->crvc); }
923 #endif
924 }
925
926 static bool SecRVCFetchNext(SecRVCRef rvc) {
927 bool OCSP_fetch_finished = true;
928 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
929 if (SecRVCShouldCheckOCSP(rvc)) {
930 OCSP_fetch_finished &= SecORVCFetchNext(rvc->orvc);
931 }
932 if (OCSP_fetch_finished) {
933 /* we didn't start an OCSP background job for this cert */
934 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
935 }
936
937 #if ENABLE_CRLS
938 bool CRL_fetch_finished = true;
939 /* Don't check CRL cache if policy requested OCSP only */
940 if (SecRVCShouldCheckCRL(rvc)) {
941 /* reset the distributionPointIX because we already iterated through the CRLDPs
942 * in SecCRVCCheckRevocationCache */
943 rvc->crvc->distributionPointIX = 0;
944 CRL_fetch_finished &= SecCRVCFetchNext(rvc->crvc);
945 }
946 if (CRL_fetch_finished) {
947 /* we didn't start a CRL background job for this cert */
948 (void)SecPathBuilderDecrementAsyncJobCount(rvc->builder);
949 }
950 OCSP_fetch_finished &= CRL_fetch_finished;
951 #endif
952
953 return OCSP_fetch_finished;
954 }
955
956 bool SecPathBuilderCheckRevocation(SecPathBuilderRef builder) {
957 secdebug("rvc", "checking revocation");
958 CFIndex certIX, certCount = SecPathBuilderGetCertificateCount(builder);
959 SecCertificatePathVCRef path = SecPathBuilderGetPath(builder);
960 bool completed = true;
961 if (certCount <= 1) {
962 /* Can't verify without an issuer; we're done */
963 return completed;
964 }
965
966 /*
967 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
968 *
969 * Note: we can't check revocation for the last certificate in the chain
970 * via OCSP or CRL methods, since there isn't a separate issuer cert to
971 * sign those responses. However, since a self-signed root has an implied
972 * issuer of itself, we can check for it in the valid database.
973 */
974
975 if (SecCertificatePathVCIsRevocationDone(path)) {
976 /* We have done revocation checking already, set PVCs with results. */
977 for (certIX = 0; certIX < certCount; ++certIX) {
978 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
979 if (rvc) { SecRVCUpdatePVC(rvc); }
980 }
981 secdebug("rvc", "Not rechecking revocation");
982 return completed;
983 }
984
985 /* Setup things so we check revocation status of all certs. */
986 SecCertificatePathVCAllocateRVCs(path, certCount);
987
988 /* Note that if we are multi threaded and a job completes after it
989 is started but before we return from this function, we don't want
990 a callback to decrement asyncJobCount to zero before we finish issuing
991 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
992 and decrement pvc->asyncJobCount for each cert that we don't start a
993 background fetch for. (We will never start an async job for the final
994 cert in the chain.) */
995 #if !ENABLE_CRLS
996 SecPathBuilderSetAsyncJobCount(builder, (unsigned int)(certCount-1));
997 #else
998 /* If we enable CRLS, we may end up with two async jobs per cert: one
999 * for OCSP and one for fetching the CRL */
1000 SecPathBuilderSetAsyncJobCount(builder, 2 * (unsigned int)(certCount-1));
1001 #endif
1002
1003 /* Loop though certificates again and issue an ocsp fetch if the
1004 revocation status checking isn't done yet (and we have an issuer!) */
1005 for (certIX = 0; certIX < certCount; ++certIX) {
1006 secdebug("rvc", "checking revocation for cert: %ld", certIX);
1007 SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, certIX);
1008 if (!rvc) {
1009 continue;
1010 }
1011
1012 SecRVCInit(rvc, builder, certIX);
1013 if (rvc->done){
1014 continue;
1015 }
1016
1017 #if !TARGET_OS_BRIDGE
1018 /* Check valid database first (separate from OCSP response cache) */
1019 if (SecRVCCheckValidInfoDatabase(rvc)) {
1020 SecRVCProcessValidInfoResults(rvc);
1021 }
1022 #endif
1023 /* Any other revocation method requires an issuer certificate;
1024 * skip the last cert in the chain since it doesn't have one. */
1025 if (certIX+1 >= certCount) {
1026 continue;
1027 }
1028
1029 /* Ignore stapled OCSP responses only if CRLs are enabled and the
1030 * policy specifically requested CRLs only. */
1031 if (SecRVCShouldCheckOCSP(rvc)) {
1032 /* If we have any OCSP stapled responses, check those first */
1033 SecORVCProcessStapledResponses(rvc->orvc);
1034 }
1035
1036 #if TARGET_OS_BRIDGE
1037 /* The bridge has no writeable storage and no network. Nothing else we can
1038 * do here. */
1039 rvc->done = true;
1040 return completed;
1041 #endif
1042
1043 /* Then check the caches for revocation results. */
1044 SecRVCCheckRevocationCaches(rvc);
1045
1046 /* The check is done if we found cached responses from either method. */
1047 if (rvc->orvc->done
1048 #if ENABLE_CRLS
1049 || rvc->crvc->done
1050 #endif
1051 ) {
1052 secdebug("rvc", "found cached response for cert: %ld", certIX);
1053 rvc->done = true;
1054 }
1055
1056 /* If we got a cached response that is no longer valid (which can only be true for
1057 * revoked responses), let's try to get a fresher response even if no one asked.
1058 * This check resolves unrevocation events after the nextUpdate time. */
1059 bool old_cached_response = (!rvc->done && rvc->orvc->ocspResponse);
1060
1061 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
1062 async http request for this cert's revocation status, unless we already successfully checked
1063 the revocation status of this cert based on the cache or stapled responses. */
1064 bool allow_fetch = SecPathBuilderCanAccessNetwork(builder) &&
1065 (SecCertificatePathVCIsEV(path) || SecCertificatePathVCIsOptionallyEV(path) ||
1066 SecPathBuilderGetRevocationMethod(builder) || old_cached_response);
1067 bool fetch_done = true;
1068 if (rvc->done || !allow_fetch) {
1069 /* We got a cache hit or we aren't allowed to access the network */
1070 SecRVCUpdatePVC(rvc);
1071 /* We didn't really start any background jobs for this cert. */
1072 (void)SecPathBuilderDecrementAsyncJobCount(builder);
1073 #if ENABLE_CRLS
1074 (void)SecPathBuilderDecrementAsyncJobCount(builder);
1075 #endif
1076 } else {
1077 fetch_done = SecRVCFetchNext(rvc);
1078 }
1079 if (!fetch_done) {
1080 /* We started at least one background fetch. */
1081 secdebug("rvc", "waiting on background fetch for cert %ld", certIX);
1082 completed = false;
1083 }
1084 }
1085
1086 /* Return false if we started any background jobs. */
1087 /* We can't just return !builder->asyncJobCount here, since if we started any
1088 jobs the completion callback will be called eventually and it will call
1089 SecPathBuilderStep(). If for some reason everything completed before we
1090 get here we still want the outer SecPathBuilderStep() to terminate so we
1091 keep track of whether we started any jobs and return false if so. */
1092 return completed;
1093 }
1094
1095 CFAbsoluteTime SecRVCGetEarliestNextUpdate(SecRVCRef rvc) {
1096 CFAbsoluteTime enu = NULL_TIME;
1097 if (!rvc || !rvc->orvc) { return enu; }
1098 enu = rvc->orvc->nextUpdate;
1099 #if ENABLE_CRLS
1100 CFAbsoluteTime crlNextUpdate = rvc->crvc->nextUpdate;
1101 if (enu == NULL_TIME ||
1102 ((crlNextUpdate > NULL_TIME) && (enu > crlNextUpdate))) {
1103 /* We didn't check OCSP or CRL next update time was sooner */
1104 enu = crlNextUpdate;
1105 }
1106 #endif
1107 return enu;
1108 }