]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecTrustServer.c
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / sec / securityd / SecTrustServer.c
1 /*
2 * Copyright (c) 2006-2010,2012-2015 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 * SecTrustServer.c - certificate trust evaluation engine
24 *
25 *
26 */
27
28 #include <securityd/SecTrustServer.h>
29 #include <securityd/SecPolicyServer.h>
30 #include <securityd/SecTrustStoreServer.h>
31 #include <securityd/SecCAIssuerRequest.h>
32 #include <securityd/SecItemServer.h>
33
34 #include <utilities/SecIOFormat.h>
35 #include <utilities/SecDispatchRelease.h>
36
37 #include <Security/SecTrustPriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <Security/SecCertificatePath.h>
41 #include <Security/SecFramework.h>
42 #include <Security/SecPolicyInternal.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <CoreFoundation/CFSet.h>
45 #include <CoreFoundation/CFString.h>
46 #include <CoreFoundation/CFNumber.h>
47 #include <CoreFoundation/CFArray.h>
48 #include <CoreFoundation/CFPropertyList.h>
49 #include <AssertMacros.h>
50 #include <stdbool.h>
51 #include <string.h>
52 #include <stdlib.h>
53 #include <limits.h>
54 #include <Security/SecBase.h>
55 #include "SecRSAKey.h"
56 #include <libDER/oids.h>
57 #include <utilities/debugging.h>
58 #include <utilities/SecCFWrappers.h>
59 #include <Security/SecInternal.h>
60 #include <ipc/securityd_client.h>
61 #include <CommonCrypto/CommonDigest.h>
62 #include "OTATrustUtilities.h"
63
64
65 /********************************************************
66 ***************** OTA Trust support ********************
67 ********************************************************/
68
69
70 //#ifndef SECITEM_SHIM_OSX
71
72 static CFArrayRef subject_to_anchors(CFDataRef nic);
73 static CFArrayRef CopyCertsFromIndices(CFArrayRef offsets);
74
75 static CFArrayRef subject_to_anchors(CFDataRef nic)
76 {
77 CFArrayRef result = NULL;
78
79 if (NULL == nic)
80 {
81 return result;
82 }
83
84 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
85 if (NULL == otapkiref)
86 {
87 return result;
88 }
89
90 CFDictionaryRef lookupTable = SecOTAPKICopyAnchorLookupTable(otapkiref);
91 CFRelease(otapkiref);
92
93 if (NULL == lookupTable)
94 {
95 return result;
96 }
97
98 unsigned char subject_digest[CC_SHA1_DIGEST_LENGTH];
99 memset(subject_digest, 0, CC_SHA1_DIGEST_LENGTH);
100
101 (void)CC_SHA1(CFDataGetBytePtr(nic), (CC_LONG)CFDataGetLength(nic), subject_digest);
102 CFDataRef sha1Digest = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, subject_digest, CC_SHA1_DIGEST_LENGTH, kCFAllocatorNull);
103
104
105 result = (CFArrayRef)CFDictionaryGetValue(lookupTable, sha1Digest);
106 CFReleaseSafe(lookupTable);
107 CFReleaseSafe(sha1Digest);
108
109 return result;
110 }
111
112 static CFArrayRef CopyCertDataFromIndices(CFArrayRef offsets)
113 {
114 CFMutableArrayRef result = NULL;
115
116 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
117 if (NULL == otapkiref)
118 {
119 return result;
120 }
121
122 const char* anchorTable = SecOTAPKIGetAnchorTable(otapkiref);
123 if (NULL == anchorTable)
124 {
125 CFReleaseSafe(otapkiref);
126 return result;
127 }
128
129 CFIndex num_offsets = CFArrayGetCount(offsets);
130
131 result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
132
133 for (CFIndex idx = 0; idx < num_offsets; idx++)
134 {
135 CFNumberRef offset = (CFNumberRef)CFArrayGetValueAtIndex(offsets, idx);
136 uint32_t offset_value = 0;
137 if (CFNumberGetValue(offset, kCFNumberSInt32Type, &offset_value))
138 {
139 char* pDataPtr = (char *)(anchorTable + offset_value);
140 //int32_t record_length = *((int32_t * )pDataPtr);
141 //record_length = record_length;
142 pDataPtr += sizeof(uint32_t);
143
144 int32_t cert_data_length = *((int32_t * )pDataPtr);
145 pDataPtr += sizeof(uint32_t);
146
147 CFDataRef cert_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)pDataPtr,
148 cert_data_length, kCFAllocatorNull);
149 if (NULL != cert_data)
150 {
151 CFArrayAppendValue(result, cert_data);
152 CFReleaseSafe(cert_data);
153 }
154 }
155 }
156 CFReleaseSafe(otapkiref);
157 return result;
158 }
159
160 static CFArrayRef CopyCertsFromIndices(CFArrayRef offsets)
161 {
162 CFMutableArrayRef result = NULL;
163
164 CFArrayRef cert_data_array = CopyCertDataFromIndices(offsets);
165
166 if (NULL != cert_data_array)
167 {
168 result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
169 CFIndex num_cert_datas = CFArrayGetCount(cert_data_array);
170 for (CFIndex idx = 0; idx < num_cert_datas; idx++)
171 {
172 CFDataRef cert_data = (CFDataRef)CFArrayGetValueAtIndex(cert_data_array, idx);
173 if (NULL != cert_data)
174 {
175 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_data);
176 if (NULL != cert)
177 {
178 CFArrayAppendValue(result, cert);
179 CFRelease(cert);
180 }
181 }
182 }
183 CFRelease(cert_data_array);
184 }
185 return result;
186
187 }
188 //#endif // SECITEM_SHIM_OSX
189
190 /********************************************************
191 *************** END OTA Trust support ******************
192 ********************************************************/
193
194 #define MAX_CHAIN_LENGTH 15
195
196 /* Forward declaration for use in SecCertificateSource. */
197 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
198
199
200 // MARK: -
201 // MARK: SecCertificateSource
202 /********************************************************
203 ************ SecCertificateSource object ***************
204 ********************************************************/
205
206 typedef struct SecCertificateSource *SecCertificateSourceRef;
207 typedef void(*SecCertificateSourceParents)(void *, CFArrayRef);
208 typedef bool(*CopyParents)(SecCertificateSourceRef source,
209 SecCertificateRef certificate, void *context, SecCertificateSourceParents);
210 typedef bool(*Contains)(SecCertificateSourceRef source,
211 SecCertificateRef certificate);
212
213 struct SecCertificateSource {
214 CopyParents copyParents;
215 Contains contains;
216 };
217
218 static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source,
219 SecCertificateRef certificate,
220 void *context, SecCertificateSourceParents callback) {
221 return source->copyParents(source, certificate, context, callback);
222 }
223
224 static bool SecCertificateSourceContains(SecCertificateSourceRef source,
225 SecCertificateRef certificate) {
226 return source->contains(source, certificate);
227 }
228
229 // MARK: -
230 // MARK: SecItemCertificateSource
231 /********************************************************
232 *********** SecItemCertificateSource object ************
233 ********************************************************/
234 struct SecItemCertificateSource {
235 struct SecCertificateSource base;
236 CFArrayRef accessGroups;
237 };
238 typedef struct SecItemCertificateSource *SecItemCertificateSourceRef;
239
240 static CFTypeRef SecItemCertificateSourceResultsPost(CFTypeRef raw_results) {
241 if (isArray(raw_results)) {
242 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(raw_results), &kCFTypeArrayCallBacks);
243 CFArrayForEach(raw_results, ^(const void *value) {
244 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, value);
245 if (cert) {
246 CFArrayAppendValue(result, cert);
247 CFRelease(cert);
248 }
249 });
250 return result;
251 } else if (isData(raw_results)) {
252 return SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)raw_results);
253 }
254 return NULL;
255 }
256
257 static bool SecItemCertificateSourceCopyParents(
258 SecCertificateSourceRef source, SecCertificateRef certificate,
259 void *context, SecCertificateSourceParents callback) {
260 SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
261 /* FIXME: Search for things other than just subject of our issuer if we
262 have a subjectID or authorityKeyIdentifier. */
263 CFDataRef normalizedIssuer =
264 SecCertificateGetNormalizedIssuerContent(certificate);
265 const void *keys[] = {
266 kSecClass,
267 kSecReturnData,
268 kSecMatchLimit,
269 kSecAttrSubject
270 },
271 *values[] = {
272 kSecClassCertificate,
273 kCFBooleanTrue,
274 kSecMatchLimitAll,
275 normalizedIssuer
276 };
277 CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4,
278 NULL, NULL);
279 CFTypeRef results = NULL;
280 SecurityClient client = {
281 .task = NULL,
282 .accessGroups = msource->accessGroups,
283 .allowSystemKeychain = true,
284 .allowSyncBubbleKeychain = false,
285 .isNetworkExtension = false,
286 };
287
288 /* We can make this async or run this on a queue now easily. */
289 CFErrorRef localError = NULL;
290 if (!_SecItemCopyMatching(query, &client, &results, &localError)) {
291 if (CFErrorGetCode(localError) != errSecItemNotFound) {
292 secdebug("trust", "_SecItemCopyMatching: %@", localError);
293 }
294 CFRelease(localError);
295 }
296 CFRelease(query);
297 CFTypeRef certs = SecItemCertificateSourceResultsPost(results);
298 CFReleaseSafe(results);
299 callback(context, certs);
300 CFReleaseSafe(certs);
301 return true;
302 }
303
304 static bool SecItemCertificateSourceContains(SecCertificateSourceRef source,
305 SecCertificateRef certificate) {
306 SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
307 /* Lookup a certificate by issuer and serial number. */
308 CFDataRef normalizedSubject =
309 SecCertificateGetNormalizedSubjectContent(certificate);
310 CFDataRef serialNumber =
311 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
312 SecCertificateCopySerialNumber(certificate, NULL);
313 #else
314 SecCertificateCopySerialNumber(certificate);
315 #endif
316 const void *keys[] = {
317 kSecClass,
318 kSecMatchLimit,
319 kSecAttrIssuer,
320 kSecAttrSerialNumber
321 },
322 *values[] = {
323 kSecClassCertificate,
324 kSecMatchLimitOne,
325 normalizedSubject,
326 serialNumber
327 };
328 SecurityClient client = {
329 .task = NULL,
330 .accessGroups = msource->accessGroups,
331 .allowSystemKeychain = true,
332 .allowSyncBubbleKeychain = false,
333 .isNetworkExtension = false,
334 };
335 CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5,
336 NULL, NULL);
337 CFErrorRef localError = NULL;
338 CFTypeRef results = NULL;
339 bool ok = _SecItemCopyMatching(query, &client, &results, &localError);
340 CFRelease(query);
341 CFRelease(serialNumber);
342 CFReleaseSafe(results);
343 if (!ok) {
344 if (CFErrorGetCode(localError) != errSecItemNotFound) {
345 secdebug("trust", "_SecItemCopyMatching: %@", localError);
346 }
347 CFRelease(localError);
348 return false;
349 }
350 return true;
351 }
352
353 static SecCertificateSourceRef SecItemCertificateSourceCreate(CFArrayRef accessGroups) {
354 SecItemCertificateSourceRef result = (SecItemCertificateSourceRef)malloc(sizeof(*result));
355 result->base.copyParents = SecItemCertificateSourceCopyParents;
356 result->base.contains = SecItemCertificateSourceContains;
357 result->accessGroups = accessGroups;
358 CFRetainSafe(accessGroups);
359 return (SecCertificateSourceRef)result;
360 }
361
362 static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source) {
363 SecItemCertificateSourceRef msource = (SecItemCertificateSourceRef)source;
364 CFReleaseSafe(msource->accessGroups);
365 free(msource);
366 }
367
368 // MARK: -
369 // MARK: SecSystemAnchorSource
370 /********************************************************
371 *********** SecSystemAnchorSource object ************
372 ********************************************************/
373
374 static bool SecSystemAnchorSourceCopyParents(
375 SecCertificateSourceRef source, SecCertificateRef certificate,
376 void *context, SecCertificateSourceParents callback) {
377 //#ifndef SECITEM_SHIM_OSX
378 CFArrayRef parents = NULL;
379 CFArrayRef anchors = NULL;
380 SecOTAPKIRef otapkiref = NULL;
381
382 CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate);
383 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
384 It does not matter since we would be returning the wrong anchors */
385 assert((unsigned long)CFDataGetLength(nic)<UINT_MAX); /* Debug check. correct as long as CFIndex is signed long */
386
387 otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
388 require_quiet(otapkiref, errOut);
389 anchors = subject_to_anchors(nic);
390 require_quiet(anchors, errOut);
391 parents = CopyCertsFromIndices(anchors);
392
393 errOut:
394 callback(context, parents);
395 CFReleaseSafe(parents);
396 CFReleaseSafe(otapkiref);
397 //#endif // SECITEM_SHIM_OSX
398 return true;
399 }
400
401 /* Quick thought: we can eliminate this method if we search anchor sources
402 before all others and we remember if we got a cert from an anchorsource. */
403 static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source,
404 SecCertificateRef certificate) {
405 bool result = false;
406 //#ifndef SECITEM_SHIM_OSX
407 CFArrayRef anchors = NULL;
408 SecOTAPKIRef otapkiref = NULL;
409 CFArrayRef cert_datas = NULL;
410
411 CFDataRef nic = SecCertificateGetNormalizedSubjectContent(certificate);
412 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
413 It does not matter since we would be returning the wrong anchors */
414 assert((unsigned long)CFDataGetLength(nic)<UINT_MAX); /* Debug check. correct as long as CFIndex is signed long */
415
416 otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
417 require_quiet(otapkiref, errOut);
418 anchors = subject_to_anchors(nic);
419 require_quiet(anchors, errOut);
420 cert_datas = CopyCertDataFromIndices(anchors);
421 require_quiet(cert_datas, errOut);
422
423 CFIndex cert_length = SecCertificateGetLength(certificate);
424 const UInt8 *cert_data_ptr = SecCertificateGetBytePtr(certificate);
425
426 CFIndex num_cert_datas = CFArrayGetCount(cert_datas);
427 for (CFIndex idx = 0; idx < num_cert_datas; idx++)
428 {
429 CFDataRef cert_data = (CFDataRef)CFArrayGetValueAtIndex(cert_datas, idx);
430
431 if (NULL != cert_data)
432 {
433 if (CFGetTypeID(cert_data) == CFDataGetTypeID())
434 {
435 CFIndex aCert_Length = CFDataGetLength(cert_data);
436 const UInt8* aCert_Data_Ptr = CFDataGetBytePtr(cert_data);
437
438 if (aCert_Length == cert_length)
439 {
440 if (!memcmp(cert_data_ptr, aCert_Data_Ptr, cert_length))
441 {
442 result = true;
443 break;
444 }
445 }
446 }
447 }
448 }
449
450 errOut:
451 CFReleaseSafe(cert_datas);
452 CFReleaseSafe(otapkiref);
453 //#endif // SECITEM_SHIM_OSX
454 return result;
455 }
456
457
458
459 struct SecCertificateSource kSecSystemAnchorSource = {
460 SecSystemAnchorSourceCopyParents,
461 SecSystemAnchorSourceContains
462 };
463
464 // MARK: -
465 // MARK: SecUserAnchorSource
466 /********************************************************
467 *********** SecUserAnchorSource object ************
468 ********************************************************/
469 static bool SecUserAnchorSourceCopyParents(
470 SecCertificateSourceRef source, SecCertificateRef certificate,
471 void *context, SecCertificateSourceParents callback) {
472 CFArrayRef parents = SecTrustStoreCopyParents(
473 SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate, NULL);
474 callback(context, parents);
475 CFReleaseSafe(parents);
476 return true;
477 }
478
479 static bool SecUserAnchorSourceContains(SecCertificateSourceRef source,
480 SecCertificateRef certificate) {
481 return SecTrustStoreContains(
482 SecTrustStoreForDomain(kSecTrustStoreDomainUser), certificate);
483 }
484
485 struct SecCertificateSource kSecUserAnchorSource = {
486 SecUserAnchorSourceCopyParents,
487 SecUserAnchorSourceContains
488 };
489
490 // MARK: -
491 // MARK: SecMemoryCertificateSource
492 /********************************************************
493 *********** SecMemoryCertificateSource object ************
494 ********************************************************/
495 struct SecMemoryCertificateSource {
496 struct SecCertificateSource base;
497 CFMutableSetRef certificates;
498 CFMutableDictionaryRef subjects;
499 };
500 typedef struct SecMemoryCertificateSource *SecMemoryCertificateSourceRef;
501
502 static bool SecMemoryCertificateSourceCopyParents(
503 SecCertificateSourceRef source, SecCertificateRef certificate,
504 void *context, SecCertificateSourceParents callback) {
505 SecMemoryCertificateSourceRef msource =
506 (SecMemoryCertificateSourceRef)source;
507 CFDataRef normalizedIssuer =
508 SecCertificateGetNormalizedIssuerContent(certificate);
509 CFArrayRef parents = (normalizedIssuer) ? CFDictionaryGetValue(msource->subjects,
510 normalizedIssuer) : NULL;
511 /* FIXME filter parents by subjectID if certificate has an
512 authorityKeyIdentifier. */
513 secdebug("trust", "%@ parents -> %@", certificate, parents);
514 callback(context, parents);
515 return true;
516 }
517
518 static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source,
519 SecCertificateRef certificate) {
520 SecMemoryCertificateSourceRef msource =
521 (SecMemoryCertificateSourceRef)source;
522 return CFSetContainsValue(msource->certificates, certificate);
523 }
524
525 static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict,
526 const void *key, const void *value) {
527 if (!key)
528 return;
529
530 CFMutableArrayRef values =
531 (CFMutableArrayRef)CFDictionaryGetValue(dict, key);
532 if (!values) {
533 values = CFArrayCreateMutable(kCFAllocatorDefault, 0,
534 &kCFTypeArrayCallBacks);
535 CFDictionaryAddValue(dict, key, values);
536 CFRelease(values);
537 }
538
539 if (values)
540 CFArrayAppendValue(values, value);
541 }
542
543 static void SecMemoryCertificateSourceApplierFunction(const void *value,
544 void *context) {
545 SecMemoryCertificateSourceRef msource =
546 (SecMemoryCertificateSourceRef)context;
547 SecCertificateRef certificate = (SecCertificateRef)value;
548
549 /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
550 if (CFSetContainsValue(msource->certificates, certificate))
551 return;
552 CFSetAddValue(msource->certificates, certificate);
553
554 CFDataRef key = SecCertificateGetNormalizedSubjectContent(certificate);
555 dictAddValueToArrayForKey(msource->subjects, key, value);
556 }
557
558 static SecCertificateSourceRef SecMemoryCertificateSourceCreate(
559 CFArrayRef certificates) {
560 SecMemoryCertificateSourceRef result = (SecMemoryCertificateSourceRef)
561 malloc(sizeof(*result));
562 result->base.copyParents = SecMemoryCertificateSourceCopyParents;
563 result->base.contains = SecMemoryCertificateSourceContains;
564 CFIndex count = CFArrayGetCount(certificates);
565 result->certificates = CFSetCreateMutable(kCFAllocatorDefault, count,
566 &kCFTypeSetCallBacks);
567 result->subjects = CFDictionaryCreateMutable(kCFAllocatorDefault,
568 count, &kCFTypeDictionaryKeyCallBacks,
569 &kCFTypeDictionaryValueCallBacks);
570 CFRange range = { 0, count };
571 CFArrayApplyFunction(certificates, range,
572 SecMemoryCertificateSourceApplierFunction, result);
573
574 return (SecCertificateSourceRef)result;
575 }
576
577 static void SecMemoryCertificateSourceDestroy(
578 SecCertificateSourceRef source) {
579 SecMemoryCertificateSourceRef msource =
580 (SecMemoryCertificateSourceRef)source;
581 CFRelease(msource->certificates);
582 CFRelease(msource->subjects);
583 free(msource);
584 }
585
586 // MARK: -
587 // MARK: SecCAIssuerCertificateSource
588 /********************************************************
589 ********* SecCAIssuerCertificateSource object **********
590 ********************************************************/
591 static bool SecCAIssuerCertificateSourceCopyParents(
592 SecCertificateSourceRef source, SecCertificateRef certificate,
593 void *context, SecCertificateSourceParents callback) {
594 return SecCAIssuerCopyParents(certificate, SecPathBuilderGetQueue((SecPathBuilderRef)context), context, callback);
595 }
596
597 static bool SecCAIssuerCertificateSourceContains(
598 SecCertificateSourceRef source, SecCertificateRef certificate) {
599 return false;
600 }
601
602 struct SecCertificateSource kSecCAIssuerSource = {
603 SecCAIssuerCertificateSourceCopyParents,
604 SecCAIssuerCertificateSourceContains
605 };
606
607 // MARK: -
608 // MARK: SecPathBuilder
609 /********************************************************
610 *************** SecPathBuilder object ******************
611 ********************************************************/
612 struct SecPathBuilder {
613 dispatch_queue_t queue;
614 CFDataRef clientAuditToken;
615 SecCertificateSourceRef certificateSource;
616 SecCertificateSourceRef itemCertificateSource;
617 SecCertificateSourceRef anchorSource;
618 CFMutableArrayRef anchorSources;
619 CFIndex nextParentSource;
620 CFMutableArrayRef parentSources;
621 CFArrayRef ocspResponses; // Stapled OCSP responses
622 CFArrayRef signedCertificateTimestamps; // Stapled SCTs
623 CFArrayRef trustedLogs; // Trusted CT logs
624
625 /* Hashed set of all paths we've constructed so far, used to prevent
626 re-considering a path that was already constructed once before.
627 Note that this is the only container in which certificatePath
628 objects are retained.
629 Every certificatePath being considered is always in allPaths and in at
630 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
631 all of which don't retain their values. */
632 CFMutableSetRef allPaths;
633
634 /* No trusted anchor, satisfies the linking to intermediates for all
635 policies (unless considerRejected is true). */
636 CFMutableArrayRef partialPaths;
637 /* No trusted anchor, does not satisfy linking to intermediates for all
638 policies. */
639 CFMutableArrayRef rejectedPaths;
640 /* Trusted anchor, satisfies the policies so far. */
641 CFMutableArrayRef candidatePaths;
642
643 CFIndex partialIX;
644
645 CFArrayRef leafDetails;
646
647 CFIndex rejectScore;
648
649 bool considerRejected;
650 bool considerPartials;
651 bool canAccessNetwork;
652
653 struct OpaqueSecPVC path;
654 SecCertificatePathRef bestPath;
655 bool bestPathIsEV;
656
657 CFIndex activations;
658 bool (*state)(SecPathBuilderRef);
659 SecPathBuilderCompleted completed;
660 const void *context;
661 };
662
663 /* State functions. Return false if a async job was scheduled, return
664 true to execute the next state. */
665 static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
666 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
667 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder);
668 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
669 static bool SecPathBuilderReportResult(SecPathBuilderRef builder);
670
671 /* Forward declarations. */
672 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
673 SecCertificateRef certificate);
674
675 /* IDEA: policies could be made cabable of replacing incoming anchors and
676 anchorsOnly argument values. For example some policies require the
677 Apple Inc. CA and not any other anchor. This can be done in
678 SecPathBuilderLeafCertificateChecks since this only runs once. */
679 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder,
680 SecCertificatePathRef path) {
681 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
682 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
683 &kCFTypeDictionaryValueCallBacks);
684 builder->leafDetails = CFArrayCreate(kCFAllocatorDefault,
685 (const void **)&certDetail, 1, &kCFTypeArrayCallBacks);
686 CFRelease(certDetail);
687 SecPVCRef pvc = &builder->path;
688 SecPVCSetPath(pvc, path, builder->leafDetails);
689 builder->considerRejected = !SecPVCLeafChecks(pvc);
690 }
691
692 static void SecPathBuilderInit(SecPathBuilderRef builder,
693 CFDataRef clientAuditToken, CFArrayRef certificates,
694 CFArrayRef anchors, bool anchorsOnly,
695 CFArrayRef policies, CFArrayRef ocspResponses,
696 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
697 CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
698 SecPathBuilderCompleted completed, const void *context) {
699 secdebug("alloc", "%p", builder);
700 CFAllocatorRef allocator = kCFAllocatorDefault;
701
702 builder->clientAuditToken = (CFDataRef)
703 ((clientAuditToken) ? CFRetain(clientAuditToken) : NULL);
704 builder->queue = dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL);
705
706 builder->nextParentSource = 1;
707 builder->considerPartials = false;
708 builder->canAccessNetwork = true;
709
710 builder->anchorSources = CFArrayCreateMutable(allocator, 0, NULL);
711 builder->parentSources = CFArrayCreateMutable(allocator, 0, NULL);
712 builder->allPaths = CFSetCreateMutable(allocator, 0,
713 &kCFTypeSetCallBacks);
714
715 builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL);
716 builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL);
717 builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL);
718 builder->partialIX = 0;
719
720 /* Init the policy verification context. */
721 SecPVCInit(&builder->path, builder, policies, verifyTime);
722 builder->bestPath = NULL;
723 builder->bestPathIsEV = false;
724 builder->rejectScore = 0;
725
726 /* Let's create all the certificate sources we might want to use. */
727 builder->certificateSource =
728 SecMemoryCertificateSourceCreate(certificates);
729 if (anchors)
730 builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
731 else
732 builder->anchorSource = NULL;
733
734 /* We always search certificateSource for parents since it includes the
735 leaf itself and it might be self signed. */
736 CFArrayAppendValue(builder->parentSources, builder->certificateSource);
737 if (builder->anchorSource) {
738 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
739 }
740 builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
741 CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
742 if (anchorsOnly) {
743 /* Add the system and user anchor certificate db to the search list
744 if we don't explicitly trust them. */
745 CFArrayAppendValue(builder->parentSources, &kSecSystemAnchorSource);
746 CFArrayAppendValue(builder->parentSources, &kSecUserAnchorSource);
747 } else {
748 /* Only add the system and user anchor certificate db to the
749 anchorSources if we are supposed to trust them. */
750 CFArrayAppendValue(builder->anchorSources, &kSecSystemAnchorSource);
751 CFArrayAppendValue(builder->anchorSources, &kSecUserAnchorSource);
752 }
753 CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
754
755 /* Now let's get the leaf cert and turn it into a path. */
756 SecCertificateRef leaf =
757 (SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
758 SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf);
759 CFSetAddValue(builder->allPaths, path);
760 CFArrayAppendValue(builder->partialPaths, path);
761 if (SecPathBuilderIsAnchor(builder, leaf)) {
762 SecCertificatePathSetIsAnchored(path);
763 CFArrayAppendValue(builder->candidatePaths, path);
764 }
765 SecPathBuilderLeafCertificateChecks(builder, path);
766 CFRelease(path);
767
768 builder->ocspResponses = CFRetainSafe(ocspResponses);
769 builder->signedCertificateTimestamps = CFRetainSafe(signedCertificateTimestamps);
770
771 if(trustedLogs) {
772 builder->trustedLogs = CFRetainSafe(trustedLogs);
773 } else {
774 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
775 builder->trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
776 CFReleaseSafe(otapkiref);
777 }
778
779 builder->activations = 0;
780 builder->state = SecPathBuilderGetNext;
781 builder->completed = completed;
782 builder->context = context;
783 }
784
785 SecPathBuilderRef SecPathBuilderCreate(CFDataRef clientAuditToken,
786 CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
787 CFArrayRef policies, CFArrayRef ocspResponses,
788 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
789 CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
790 SecPathBuilderCompleted completed, const void *context) {
791 SecPathBuilderRef builder = malloc(sizeof(*builder));
792 SecPathBuilderInit(builder, clientAuditToken, certificates,
793 anchors, anchorsOnly, policies, ocspResponses,
794 signedCertificateTimestamps, trustedLogs, verifyTime,
795 accessGroups, completed, context);
796 return builder;
797 }
798
799 static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
800 secdebug("alloc", "%p", builder);
801 dispatch_release_null(builder->queue);
802 if (builder->anchorSource)
803 SecMemoryCertificateSourceDestroy(builder->anchorSource);
804 if (builder->certificateSource)
805 SecMemoryCertificateSourceDestroy(builder->certificateSource);
806 if (builder->itemCertificateSource)
807 SecItemCertificateSourceDestroy(builder->itemCertificateSource);
808 CFReleaseSafe(builder->clientAuditToken);
809 CFReleaseSafe(builder->anchorSources);
810 CFReleaseSafe(builder->parentSources);
811 CFReleaseSafe(builder->allPaths);
812 CFReleaseSafe(builder->partialPaths);
813 CFReleaseSafe(builder->rejectedPaths);
814 CFReleaseSafe(builder->candidatePaths);
815 CFReleaseSafe(builder->leafDetails);
816
817 SecPVCDelete(&builder->path);
818 }
819
820 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
821 return builder->canAccessNetwork;
822 }
823
824 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
825 if (builder->canAccessNetwork != allow) {
826 builder->canAccessNetwork = allow;
827 if (allow) {
828 secdebug("http", "network access re-enabled by policy");
829 /* re-enabling network_access re-adds kSecCAIssuerSource as
830 a parent source. */
831 CFArrayAppendValue(builder->parentSources, &kSecCAIssuerSource);
832 } else {
833 secdebug("http", "network access disabled by policy");
834 /* disabling network_access removes kSecCAIssuerSource from
835 the list of parent sources. */
836 CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
837 CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
838 &kSecCAIssuerSource);
839 if (ix >= 0)
840 CFArrayRemoveValueAtIndex(builder->parentSources, ix);
841 }
842 }
843 }
844
845 CFArrayRef SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder)
846 {
847 return CFRetainSafe(builder->ocspResponses);
848 }
849
850 CFArrayRef SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder)
851 {
852 return CFRetainSafe(builder->signedCertificateTimestamps);
853 }
854
855 CFArrayRef SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder)
856 {
857 return CFRetainSafe(builder->trustedLogs);
858 }
859
860 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
861 SecCertificateRef certificate) {
862 /* We always look through all anchor sources. */
863 CFIndex count = CFArrayGetCount(builder->anchorSources);
864 CFIndex ix;
865 for (ix = 0; ix < count; ++ix) {
866 SecCertificateSourceRef source = (SecCertificateSourceRef)
867 CFArrayGetValueAtIndex(builder->anchorSources, ix);
868 if (SecCertificateSourceContains(source, certificate)) {
869 return true;
870 }
871 }
872 return false;
873 }
874
875 /* Return false if path is not a partial, if path was a valid candidate it
876 will have been added to builder->candidatePaths, if path was rejected
877 by the parent certificate checks (because it's expired or some other
878 static chaining check failed) it will have been added to rejectedPaths.
879 Return true path if path is a partial. */
880 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
881 SecCertificatePathRef path) {
882 SecPVCRef pvc = &builder->path;
883 SecPVCSetPath(pvc, path, NULL);
884
885 if (!builder->considerRejected && !SecPVCParentCertificateChecks(pvc,
886 SecPVCGetCertificateCount(pvc) - 1)) {
887 secdebug("trust", "Found rejected path %@", path);
888 CFArrayAppendValue(builder->rejectedPaths, path);
889 return false;
890 }
891
892 SecPathVerifyStatus vstatus = SecCertificatePathVerify(path);
893 /* Candidate paths with failed signatures are discarded. */
894 if (vstatus == kSecPathVerifyFailed) {
895 secdebug("trust", "Verify failed for path %@", path);
896 return false;
897 }
898
899 if (vstatus == kSecPathVerifySuccess) {
900 /* The signature chain verified sucessfully, now let's find
901 out if we have an anchor for path. */
902 if (SecCertificatePathIsAnchored(path)) {
903 secdebug("trust", "Adding candidate %@", path);
904 CFArrayAppendValue(builder->candidatePaths, path);
905 return false;
906 }
907 }
908
909 return true;
910 }
911
912 /* Given the builder, a partial chain partial and the parents array, construct
913 a SecCertificatePath for each parent. After discarding previously
914 considered paths and paths with cycles, sort out which array each path
915 should go in, if any. */
916 static void SecPathBuilderProcessParents(SecPathBuilderRef builder,
917 SecCertificatePathRef partial, CFArrayRef parents) {
918 CFIndex rootIX = SecCertificatePathGetCount(partial) - 1;
919 CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
920 CFIndex parentIX;
921 bool is_anchor = SecCertificatePathGetNextSourceIndex(partial) <=
922 CFArrayGetCount(builder->anchorSources);
923 secdebug("trust", "found %" PRIdCFIndex " candidate %s", num_parents,
924 (is_anchor ? "anchors" : "parents"));
925 for (parentIX = 0; parentIX < num_parents; ++parentIX) {
926 SecCertificateRef parent = (SecCertificateRef)
927 CFArrayGetValueAtIndex(parents, parentIX);
928 CFIndex ixOfParent = SecCertificatePathGetIndexOfCertificate(partial,
929 parent);
930 if (ixOfParent != kCFNotFound) {
931 /* partial already contains parent. Let's not add the same
932 certificate again. */
933 if (ixOfParent == rootIX) {
934 /* parent is equal to the root of the partial, so partial
935 looks to be self issued. */
936 SecCertificatePathSetSelfIssued(partial);
937 }
938 continue;
939 }
940
941 /* FIXME Add more sanity checks to see that parent really can be
942 a parent of partial_root. subjectKeyID == authorityKeyID,
943 signature algorithm matches public key algorithm, etc. */
944 SecCertificatePathRef path = SecCertificatePathCreate(partial, parent);
945 if (!path)
946 continue;
947 if (!CFSetContainsValue(builder->allPaths, path)) {
948 CFSetAddValue(builder->allPaths, path);
949 if (is_anchor)
950 SecCertificatePathSetIsAnchored(path);
951 if (SecPathBuilderIsPartial(builder, path)) {
952 /* Insert path right at the current position since it's a new
953 candiate partial. */
954 CFArrayInsertValueAtIndex(builder->partialPaths,
955 ++builder->partialIX, path);
956 secdebug("trust", "Adding partial for parent %" PRIdCFIndex "/%" PRIdCFIndex " %@",
957 parentIX + 1, num_parents, path);
958 }
959 secdebug("trust", "found new path %@", path);
960 }
961 CFRelease(path);
962 }
963 }
964
965 /* Callback for the SecPathBuilderGetNext() functions call to
966 SecCertificateSourceCopyParents(). */
967 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
968 SecPathBuilderRef builder = (SecPathBuilderRef)context;
969 SecCertificatePathRef partial = (SecCertificatePathRef)
970 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
971 secdebug("async", "%@ parents %@", partial, parents);
972 SecPathBuilderProcessParents(builder, partial, parents);
973
974 builder->state = SecPathBuilderGetNext;
975 SecPathBuilderStep(builder);
976 }
977
978 static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
979 /* If we have any candidates left to go return those first. */
980 if (CFArrayGetCount(builder->candidatePaths)) {
981 SecCertificatePathRef path = (SecCertificatePathRef)
982 CFArrayGetValueAtIndex(builder->candidatePaths, 0);
983 CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
984 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
985 path);
986 SecPVCSetPath(&builder->path, path, NULL);
987 builder->state = SecPathBuilderValidatePath;
988 return true;
989 }
990
991 /* If we are considering rejected chains we check each rejected path
992 with SecPathBuilderIsPartial() which checks the signature chain and
993 either drops the path if it's not properly signed, add it as a
994 candidate if it has a trusted anchor, or adds it as a partial
995 to be considered once we finish considering all the rejects. */
996 if (builder->considerRejected) {
997 CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
998 if (rejectedIX) {
999 rejectedIX--;
1000 SecCertificatePathRef path = (SecCertificatePathRef)
1001 CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
1002 if (SecPathBuilderIsPartial(builder, path)) {
1003 CFArrayInsertValueAtIndex(builder->partialPaths,
1004 ++builder->partialIX, path);
1005 }
1006 CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
1007
1008 /* Keep going until we have moved all rejected partials into
1009 the regular partials or candidates array. */
1010 return true;
1011 }
1012 }
1013
1014 /* If builder->partialIX is < 0 we have considered all partial chains
1015 this block must ensure partialIX >= 0 if execution continues past
1016 it's end. */
1017 if (builder->partialIX < 0) {
1018 CFIndex num_sources = CFArrayGetCount(builder->parentSources);
1019 if (builder->nextParentSource < num_sources) {
1020 builder->nextParentSource++;
1021 secdebug("trust", "broading search to %" PRIdCFIndex "/%" PRIdCFIndex " sources",
1022 builder->nextParentSource, num_sources);
1023 } else {
1024 /* We've run out of new sources to consider so let's look at
1025 rejected chains and after that even consider partials
1026 directly.
1027 FIXME we might not want to consider partial paths that
1028 are subsets of other partial paths, or not consider them
1029 at all if we already have an anchored reject. */
1030 if (!builder->considerRejected) {
1031 builder->considerRejected = true;
1032 secdebug("trust", "considering rejected paths");
1033 } else if (!builder->considerPartials) {
1034 builder->considerPartials = true;
1035 secdebug("trust", "considering partials");
1036 } else {
1037 /* We're all out of options, so we can't produce any more
1038 candidates. Let's calculate details and return the best
1039 path we found. */
1040 builder->state = SecPathBuilderComputeDetails;
1041 return true;
1042 }
1043 }
1044 builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
1045 secdebug("trust", "re-checking %" PRIdCFIndex " partials", builder->partialIX + 1);
1046 return true;
1047 }
1048
1049 /* We know builder->partialIX >= 0 if we get here. */
1050 SecCertificatePathRef partial = (SecCertificatePathRef)
1051 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
1052 /* Don't try to extend partials anymore once we are in the considerPartials
1053 state, since at this point every partial has been extended with every
1054 possible parentSource already. */
1055 if (builder->considerPartials) {
1056 --builder->partialIX;
1057 SecPVCSetPath(&builder->path, partial, NULL);
1058 builder->state = SecPathBuilderValidatePath;
1059 return true;
1060 }
1061
1062 /* Attempt to extend this partial path with another certificate. This
1063 should give us a list of potential parents to consider. */
1064 secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
1065 builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
1066 partial);
1067
1068 /* Attempt to extend partial, leaving all possible extended versions
1069 of partial in builder->extendedPaths. */
1070 CFIndex sourceIX = SecCertificatePathGetNextSourceIndex(partial);
1071 CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
1072 if (sourceIX < num_anchor_sources + builder->nextParentSource) {
1073 SecCertificateSourceRef source;
1074 if (sourceIX < num_anchor_sources) {
1075 source = (SecCertificateSourceRef)
1076 CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
1077 secdebug("trust", "searching anchor source %" PRIdCFIndex "/%" PRIdCFIndex, sourceIX + 1,
1078 num_anchor_sources);
1079 } else {
1080 CFIndex parentIX = sourceIX - num_anchor_sources;
1081 source = (SecCertificateSourceRef)
1082 CFArrayGetValueAtIndex(builder->parentSources, parentIX);
1083 secdebug("trust", "searching parent source %" PRIdCFIndex "/%" PRIdCFIndex, parentIX + 1,
1084 builder->nextParentSource);
1085 }
1086 SecCertificatePathSetNextSourceIndex(partial, sourceIX + 1);
1087 SecCertificateRef root = SecCertificatePathGetRoot(partial);
1088 return SecCertificateSourceCopyParents(source, root,
1089 builder, SecPathBuilderExtendPaths);
1090 } else {
1091 --builder->partialIX;
1092 }
1093
1094 return true;
1095 }
1096
1097 /* One or more of the policies did not accept the candidate path. */
1098 static void SecPathBuilderReject(SecPathBuilderRef builder) {
1099 check(builder);
1100 SecPVCRef pvc = &builder->path;
1101
1102 builder->state = SecPathBuilderGetNext;
1103
1104 if (builder->bestPathIsEV && !pvc->is_ev) {
1105 /* We never replace an ev reject with a non ev reject. */
1106 return;
1107 }
1108
1109 CFIndex rejectScore = builder->rejectScore;
1110 CFIndex score = SecCertificatePathScore(builder->path.path,
1111 SecPVCGetVerifyTime(&builder->path));
1112
1113 /* The current chain is valid for EV, but revocation checking failed. We
1114 replace any previously accepted or rejected non EV chains with the
1115 current one. */
1116 if (pvc->is_ev && !builder->bestPathIsEV) {
1117 rejectScore = 0;
1118 }
1119
1120 #if 0
1121 if (pvc->is_ev) {
1122 /* Since this means we found a valid ev chain that was revoked,
1123 we might want to switch directly to the
1124 SecPathBuilderComputeDetails state here if we think further
1125 searching for new chains is pointless. For now we'll keep
1126 going, since we could accept an alternate EV certification
1127 path that isn't revoked. */
1128 builder->state = SecPathBuilderComputeDetails;
1129 }
1130 #endif
1131
1132 /* Do this last so that changes to rejectScore above will take affect. */
1133 if (!builder->bestPath || score > rejectScore) {
1134 if (builder->bestPath) {
1135 secdebug("reject",
1136 "replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex " %@",
1137 (builder->bestPathIsEV ? "" : "non "),
1138 (builder->rejectScore == INTPTR_MAX ? "accept" : "reject"),
1139 builder->rejectScore,
1140 (pvc->is_ev ? "" : "non "), (long)score, builder->path.path);
1141 } else {
1142 secdebug("reject", "%sev reject score: %" PRIdCFIndex " %@",
1143 (pvc->is_ev ? "" : "non "), score, builder->path.path);
1144 }
1145
1146 builder->rejectScore = score;
1147 builder->bestPath = pvc->path;
1148 builder->bestPathIsEV = pvc->is_ev;
1149 } else {
1150 secdebug("reject", "%sev reject score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
1151 (pvc->is_ev ? "" : "non "), score, rejectScore, builder->path.path);
1152 }
1153 }
1154
1155 /* All policies accepted the candidate path. */
1156 static void SecPathBuilderAccept(SecPathBuilderRef builder) {
1157 check(builder);
1158 SecPVCRef pvc = &builder->path;
1159 if (pvc->is_ev || !builder->bestPathIsEV) {
1160 secdebug("accept", "replacing %sev accept with %sev %@",
1161 (builder->bestPathIsEV ? "" : "non "),
1162 (pvc->is_ev ? "" : "non "), builder->path.path);
1163 builder->rejectScore = INTPTR_MAX; /* CFIndex is signed long which is INTPTR_T */
1164 builder->bestPathIsEV = pvc->is_ev;
1165 builder->bestPath = pvc->path;
1166 }
1167
1168 /* If we found the best accept we can we want to switch directly to the
1169 SecPathBuilderComputeDetails state here, since we're done. */
1170 if (pvc->is_ev || !pvc->optionally_ev)
1171 builder->state = SecPathBuilderComputeDetails;
1172 else
1173 builder->state = SecPathBuilderGetNext;
1174 }
1175
1176 /* Return true iff a given path satisfies all the specified policies at
1177 verifyTime. */
1178 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
1179 SecPVCRef pvc = &builder->path;
1180
1181 if (builder->considerRejected) {
1182 SecPathBuilderReject(builder);
1183 return true;
1184 }
1185
1186 builder->state = SecPathBuilderDidValidatePath;
1187 return SecPVCPathChecks(pvc);
1188 }
1189
1190 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
1191 SecPVCRef pvc = &builder->path;
1192 if (pvc->result) {
1193 SecPathBuilderAccept(builder);
1194 } else {
1195 SecPathBuilderReject(builder);
1196 }
1197 assert(builder->state != SecPathBuilderDidValidatePath);
1198 return true;
1199 }
1200
1201 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
1202 // foobar
1203 SecPVCRef pvc = &builder->path;
1204 #if 0
1205 if (!builder->caller_wants_details) {
1206 SecPVCSetPath(pvc, builder->bestPath, NULL);
1207 pvc->result = builder->rejectScore == INTPTR_MAX;
1208 builder->state = SecPathBuilderReportResult;
1209 return true;
1210 }
1211 #endif
1212 CFIndex ix, pathLength = SecCertificatePathGetCount(builder->bestPath);
1213 CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
1214 pathLength, builder->leafDetails);
1215 CFRetainSafe(details);
1216 SecPVCSetPath(pvc, builder->bestPath, details);
1217 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1218 pvc->optionally_ev = builder->bestPathIsEV;
1219 pvc->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
1220 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1221 for (ix = 1; ix < pathLength; ++ix) {
1222 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
1223 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
1224 &kCFTypeDictionaryValueCallBacks);
1225 CFArrayAppendValue(details, certDetail);
1226 CFRelease(certDetail);
1227 SecPVCParentCertificateChecks(pvc, ix);
1228 SecPVCGrayListedKeyChecks(pvc, ix);
1229 SecPVCBlackListedKeyChecks(pvc, ix);
1230 }
1231 builder->state = SecPathBuilderReportResult;
1232 bool completed = SecPVCPathChecks(pvc);
1233
1234 /* Reject the certificate if it was accepted before but we failed it now. */
1235 if (builder->rejectScore == INTPTR_MAX && !pvc->result) {
1236 builder->rejectScore = 0;
1237 }
1238
1239 CFReleaseSafe(details);
1240
1241 return completed;
1242 }
1243
1244 static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
1245 SecPVCRef pvc = &builder->path;
1246 bool haveRevocationResponse = false;
1247 if (pvc->info && pvc->is_ev && pvc->result) {
1248 CFDictionarySetValue(pvc->info, kSecTrustInfoExtendedValidationKey,
1249 kCFBooleanTrue); /* iOS key */
1250 CFDictionarySetValue(pvc->info, kSecTrustExtendedValidation,
1251 kCFBooleanTrue); /* unified API key */
1252 SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
1253 CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
1254 if (leafCompanyName) {
1255 CFDictionarySetValue(pvc->info, kSecTrustInfoCompanyNameKey,
1256 leafCompanyName); /* iOS key */
1257 CFDictionarySetValue(pvc->info, kSecTrustOrganizationName,
1258 leafCompanyName); /* unified API key */
1259 CFRelease(leafCompanyName);
1260 }
1261 if (pvc->rvcs) {
1262 CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
1263 if (nextUpdate == 0) {
1264 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
1265 kCFBooleanFalse); /* iOS key */
1266 CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
1267 kCFBooleanFalse); /* unified API key */
1268 } else {
1269 haveRevocationResponse = true;
1270 CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
1271 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
1272 validUntil); /* iOS key */
1273 CFDictionarySetValue(pvc->info, kSecTrustRevocationValidUntilDate,
1274 validUntil); /* unified API key */
1275 CFRelease(validUntil);
1276 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
1277 kCFBooleanTrue); /* iOS key */
1278 CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
1279 kCFBooleanTrue); /* unified API key */
1280 }
1281 }
1282 }
1283
1284 if (pvc->info && pvc->result && pvc->response_required && !haveRevocationResponse) {
1285 builder->rejectScore = 0;
1286 SecPVCSetResultForced(pvc, kSecPolicyCheckRevocationResponseRequired,
1287 0, kCFBooleanFalse, true);
1288 }
1289
1290 if (pvc->info && pvc->is_ct && pvc->result) {
1291 CFDictionarySetValue(pvc->info, kSecTrustInfoCertificateTransparencyKey,
1292 kCFBooleanTrue);
1293 }
1294
1295
1296 /* This will trigger the outer step function to call the completion
1297 function. */
1298 builder->state = NULL;
1299 return false;
1300 }
1301
1302 /* @function SecPathBuilderStep
1303 @summary This is the core of the async engine.
1304 @description Return false iff job is complete, true if a network request
1305 is pending.
1306 builder->state is a function pointer which is to be invoked.
1307 If you call this function from within a builder->state invocation it
1308 immediately returns true.
1309 Otherwise the following steps are repeated endlessly (unless a step returns)
1310 builder->state is invoked. If it returns true and builder->state is still
1311 non NULL this proccess is repeated.
1312 If a state returns false, SecPathBuilder will return true
1313 if builder->state is non NULL.
1314 If builder->state is NULL then regardless of what the state function returns
1315 the completion callback will be invoked and the builder will be deallocated.
1316 */
1317 bool SecPathBuilderStep(SecPathBuilderRef builder) {
1318 if (builder->activations) {
1319 secdebug("async", "activations: %lu returning true",
1320 builder->activations);
1321 return true;
1322 }
1323
1324 secdebug("async", "activations: %lu", builder->activations);
1325 builder->activations++;
1326 while (builder->state && builder->state(builder));
1327 --builder->activations;
1328
1329 if (builder->state) {
1330 secdebug("async", "waiting for async reply, exiting");
1331 /* A state returned false, it's waiting for network traffic. Let's
1332 return. */
1333 return true;
1334 }
1335
1336 if (builder->activations) {
1337 /* There is still at least one other running instance of this builder
1338 somewhere on the stack, we let that instance take care of sending
1339 the client a response. */
1340 return false;
1341 }
1342
1343 SecTrustResultType result = (builder->rejectScore == INTPTR_MAX
1344 ? kSecTrustResultUnspecified : kSecTrustResultRecoverableTrustFailure);
1345
1346 secdebug("trust", "completed: %@ details: %@ result: %d",
1347 builder->bestPath, builder->path.details, result);
1348
1349 if (builder->completed) {
1350 builder->completed(builder->context, builder->bestPath,
1351 builder->path.details, builder->path.info, result);
1352 }
1353
1354 /* Finally, destroy the builder and free it. */
1355 SecPathBuilderDestroy(builder);
1356 free(builder);
1357
1358 return false;
1359 }
1360
1361 dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
1362 return (builder) ? builder->queue : NULL;
1363 }
1364
1365 CFDataRef SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder) {
1366 return (builder) ? (CFDataRef)CFRetainSafe(builder->clientAuditToken) : NULL;
1367 }
1368
1369 // MARK: -
1370 // MARK: SecTrustServer
1371 /********************************************************
1372 ****************** SecTrustServer **********************
1373 ********************************************************/
1374
1375 typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
1376
1377 static void
1378 SecTrustServerEvaluateCompleted(const void *userData,
1379 SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
1380 SecTrustResultType result) {
1381 SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
1382 evaluated(result, details, info, chain, NULL);
1383 Block_release(evaluated);
1384 }
1385
1386 void
1387 SecTrustServerEvaluateBlock(CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
1388 SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
1389 /* Call the actual evaluator function. */
1390 SecPathBuilderRef builder = SecPathBuilderCreate(clientAuditToken,
1391 certificates, anchors,
1392 anchorsOnly, policies,
1393 responses, SCTs, trustedLogs,
1394 verifyTime, accessGroups,
1395 SecTrustServerEvaluateCompleted, userData);
1396 dispatch_async(builder->queue, ^{ SecPathBuilderStep(builder); });
1397 }
1398
1399
1400 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1401 SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
1402 dispatch_semaphore_t done = dispatch_semaphore_create(0);
1403 __block SecTrustResultType result = kSecTrustResultInvalid;
1404 SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
1405 result = tr;
1406 if (tr == kSecTrustResultInvalid) {
1407 if (perror) {
1408 *perror = error;
1409 CFRetainSafe(error);
1410 }
1411 } else {
1412 if (pdetails) {
1413 *pdetails = details;
1414 CFRetainSafe(details);
1415 }
1416 if (pinfo) {
1417 *pinfo = info;
1418 CFRetainSafe(info);
1419 }
1420 if (pchain) {
1421 *pchain = chain;
1422 CFRetainSafe(chain);
1423 }
1424 }
1425 dispatch_semaphore_signal(done);
1426 });
1427 dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
1428
1429 return result;
1430 }