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