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