]> git.saurik.com Git - apple/security.git/blob - Security/sec/Security/SecTrust.c
a7138f505d31de5d710d90ee03681c1a605ef25d
[apple/security.git] / Security / sec / Security / SecTrust.c
1 /*
2 * Copyright (c) 2006-2014 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 * SecTrust.c - CoreFoundation based certificate trust evaluator
24 *
25 */
26
27 #include <Security/SecTrustPriv.h>
28 #include <Security/SecItemPriv.h>
29 #include <Security/SecCertificateInternal.h>
30 #include <Security/SecCertificatePath.h>
31 #include <Security/SecFramework.h>
32 #include <Security/SecPolicyCerts.h>
33 #include <Security/SecPolicyInternal.h>
34 #include <Security/SecPolicyPriv.h>
35 #include <Security/SecuritydXPC.h>
36 #include <Security/SecInternal.h>
37 #include <Security/SecBasePriv.h>
38 #include <CoreFoundation/CFRuntime.h>
39 #include <CoreFoundation/CFSet.h>
40 #include <CoreFoundation/CFString.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFArray.h>
43 #include <CoreFoundation/CFPropertyList.h>
44 #include <AssertMacros.h>
45 #include <stdbool.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <pthread.h>
49
50 #include <utilities/SecIOFormat.h>
51 #include <utilities/SecCFError.h>
52 #include <utilities/SecCFWrappers.h>
53 #include <utilities/SecCertificateTrace.h>
54 #include <utilities/debugging.h>
55
56 #include "SecRSAKey.h"
57 #include <libDER/oids.h>
58
59 #include <ipc/securityd_client.h>
60
61 #include <securityd/SecTrustServer.h>
62
63 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
64
65 SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey, "ExtendedValidation");
66 SEC_CONST_DECL (kSecTrustInfoCompanyNameKey, "CompanyName");
67 SEC_CONST_DECL (kSecTrustInfoRevocationKey, "Revocation");
68 SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey, "RevocationValidUntil");
69
70 /* Public trust result constants */
71 SEC_CONST_DECL (kSecTrustEvaluationDate, "TrustEvaluationDate");
72 SEC_CONST_DECL (kSecTrustExtendedValidation, "TrustExtendedValidation");
73 SEC_CONST_DECL (kSecTrustOrganizationName, "Organization");
74 SEC_CONST_DECL (kSecTrustResultValue, "TrustResultValue");
75 SEC_CONST_DECL (kSecTrustRevocationChecked, "TrustRevocationChecked");
76 SEC_CONST_DECL (kSecTrustRevocationValidUntilDate, "TrustExpirationDate");
77 SEC_CONST_DECL (kSecTrustResultDetails, "TrustResultDetails");
78
79 #pragma mark -
80 #pragma mark SecTrust
81
82 /********************************************************
83 ****************** SecTrust object *********************
84 ********************************************************/
85 struct __SecTrust {
86 CFRuntimeBase _base;
87 CFArrayRef _certificates;
88 CFArrayRef _anchors;
89 CFTypeRef _policies;
90 CFArrayRef _responses;
91 CFDateRef _verifyDate;
92 SecCertificatePathRef _chain;
93 SecKeyRef _publicKey;
94 CFArrayRef _details;
95 CFDictionaryRef _info;
96 CFArrayRef _exceptions;
97
98 /* Note that a value of kSecTrustResultInvalid (0)
99 * indicates the trust must be (re)evaluated; any
100 * functions which modify trust parameters in a way
101 * that would invalidate the current result must set
102 * this value back to kSecTrustResultInvalid.
103 */
104 SecTrustResultType _trustResult;
105
106 /* If true we don't trust any anchors other than the ones in _anchors. */
107 bool _anchorsOnly;
108
109 /* Master switch to permit or disable network use in policy evaluation */
110 SecNetworkPolicy _networkPolicy;
111 };
112
113 /* Forward declarations of static functions. */
114 static OSStatus SecTrustEvaluateIfNecessary(SecTrustRef trust);
115
116 /* Static functions. */
117 static CFStringRef SecTrustCopyDescription(CFTypeRef cf) {
118 SecTrustRef trust = (SecTrustRef)cf;
119 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
120 CFSTR("<SecTrustRef: %p>"), trust);
121 }
122
123 static void SecTrustDestroy(CFTypeRef cf) {
124 SecTrustRef trust = (SecTrustRef)cf;
125 CFReleaseSafe(trust->_certificates);
126 CFReleaseSafe(trust->_policies);
127 CFReleaseSafe(trust->_responses);
128 CFReleaseSafe(trust->_verifyDate);
129 CFReleaseSafe(trust->_anchors);
130 CFReleaseSafe(trust->_chain);
131 CFReleaseSafe(trust->_publicKey);
132 CFReleaseSafe(trust->_details);
133 CFReleaseSafe(trust->_info);
134 CFReleaseSafe(trust->_exceptions);
135 }
136
137 /* Public API functions. */
138 CFGiblisFor(SecTrust)
139
140 OSStatus SecTrustCreateWithCertificates(CFTypeRef certificates,
141 CFTypeRef policies, SecTrustRef *trust) {
142 OSStatus status = errSecParam;
143 CFAllocatorRef allocator = kCFAllocatorDefault;
144 CFArrayRef l_certs = NULL, l_policies = NULL;
145 SecTrustRef result = NULL;
146
147 check(certificates);
148 check(trust);
149 CFTypeID certType = CFGetTypeID(certificates);
150 if (certType == CFArrayGetTypeID()) {
151 /* We need at least 1 certificate. */
152 require_quiet(CFArrayGetCount(certificates) > 0, errOut);
153 l_certs = CFArrayCreateCopy(allocator, certificates);
154 } else if (certType == SecCertificateGetTypeID()) {
155 l_certs = CFArrayCreate(allocator, &certificates, 1,
156 &kCFTypeArrayCallBacks);
157 } else {
158 goto errOut;
159 }
160 if (!l_certs) {
161 status = errSecAllocate;
162 goto errOut;
163 }
164
165 if (!policies) {
166 CFTypeRef policy = SecPolicyCreateBasicX509();
167 l_policies = CFArrayCreate(allocator, &policy, 1,
168 &kCFTypeArrayCallBacks);
169 CFRelease(policy);
170 } else if (CFGetTypeID(policies) == CFArrayGetTypeID()) {
171 l_policies = CFArrayCreateCopy(allocator, policies);
172 } else if (CFGetTypeID(policies) == SecPolicyGetTypeID()) {
173 l_policies = CFArrayCreate(allocator, &policies, 1,
174 &kCFTypeArrayCallBacks);
175 } else {
176 goto errOut;
177 }
178 if (!l_policies) {
179 status = errSecAllocate;
180 goto errOut;
181 }
182
183 CFIndex size = sizeof(struct __SecTrust);
184 require_quiet(result = (SecTrustRef)_CFRuntimeCreateInstance(allocator,
185 SecTrustGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
186 memset((char*)result + sizeof(result->_base), 0,
187 sizeof(*result) - sizeof(result->_base));
188 status = errSecSuccess;
189
190 errOut:
191 if (status) {
192 CFReleaseSafe(result);
193 CFReleaseSafe(l_certs);
194 CFReleaseSafe(l_policies);
195 } else {
196 result->_certificates = l_certs;
197 result->_policies = l_policies;
198 if (trust)
199 *trust = result;
200 else
201 CFReleaseSafe(result);
202 }
203 return status;
204 }
205
206 static void SetTrustSetNeedsEvaluation(SecTrustRef trust) {
207 check(trust);
208 if (trust) {
209 trust->_trustResult = kSecTrustResultInvalid;
210 }
211 }
212
213 OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust,
214 Boolean anchorCertificatesOnly) {
215 if (!trust) {
216 return errSecParam;
217 }
218 SetTrustSetNeedsEvaluation(trust);
219 trust->_anchorsOnly = anchorCertificatesOnly;
220
221 return errSecSuccess;
222 }
223
224 OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust,
225 CFArrayRef anchorCertificates) {
226 if (!trust) {
227 return errSecParam;
228 }
229 SetTrustSetNeedsEvaluation(trust);
230 if (anchorCertificates)
231 CFRetain(anchorCertificates);
232 if (trust->_anchors)
233 CFRelease(trust->_anchors);
234 trust->_anchors = anchorCertificates;
235 trust->_anchorsOnly = (anchorCertificates != NULL);
236
237 return errSecSuccess;
238 }
239
240 OSStatus SecTrustCopyCustomAnchorCertificates(SecTrustRef trust,
241 CFArrayRef *anchors) {
242 if (!trust|| !anchors) {
243 return errSecParam;
244 }
245 CFArrayRef anchorsArray = NULL;
246 if (trust->_anchors) {
247 anchorsArray = CFArrayCreateCopy(kCFAllocatorDefault, trust->_anchors);
248 if (!anchorsArray) {
249 return errSecAllocate;
250 }
251 }
252 *anchors = anchorsArray;
253 return errSecSuccess;
254 }
255
256 OSStatus SecTrustSetOCSPResponse(SecTrustRef trust, CFTypeRef responseData) {
257 if (!trust) {
258 return errSecParam;
259 }
260 SetTrustSetNeedsEvaluation(trust);
261 CFArrayRef responseArray = NULL;
262 if (responseData) {
263 if (CFGetTypeID(responseData) == CFArrayGetTypeID()) {
264 responseArray = CFArrayCreateCopy(kCFAllocatorDefault, responseData);
265 } else if (CFGetTypeID(responseData) == CFDataGetTypeID()) {
266 responseArray = CFArrayCreate(kCFAllocatorDefault, &responseData, 1,
267 &kCFTypeArrayCallBacks);
268 } else {
269 return errSecParam;
270 }
271 }
272 if (trust->_responses)
273 CFRelease(trust->_responses);
274 trust->_responses = responseArray;
275
276 return errSecSuccess;
277 }
278
279 OSStatus SecTrustSetVerifyDate(SecTrustRef trust, CFDateRef verifyDate) {
280 if (!trust) {
281 return errSecParam;
282 }
283 SetTrustSetNeedsEvaluation(trust);
284 check(verifyDate);
285 CFRetainSafe(verifyDate);
286 if (trust->_verifyDate)
287 CFRelease(trust->_verifyDate);
288 trust->_verifyDate = verifyDate;
289
290 return errSecSuccess;
291 }
292
293 OSStatus SecTrustSetPolicies(SecTrustRef trust, CFTypeRef newPolicies) {
294 if (!trust || !newPolicies) {
295 return errSecParam;
296 }
297 SetTrustSetNeedsEvaluation(trust);
298 check(newPolicies);
299
300 CFArrayRef policyArray = NULL;
301 if (CFGetTypeID(newPolicies) == CFArrayGetTypeID()) {
302 policyArray = CFArrayCreateCopy(kCFAllocatorDefault, newPolicies);
303 } else if (CFGetTypeID(newPolicies) == SecPolicyGetTypeID()) {
304 policyArray = CFArrayCreate(kCFAllocatorDefault, &newPolicies, 1,
305 &kCFTypeArrayCallBacks);
306 } else {
307 return errSecParam;
308 }
309
310 if (trust->_policies)
311 CFRelease(trust->_policies);
312 trust->_policies = policyArray;
313
314 return errSecSuccess;
315 }
316
317 OSStatus SecTrustCopyPolicies(SecTrustRef trust, CFArrayRef *policies) {
318 if (!trust|| !policies) {
319 return errSecParam;
320 }
321 if (!trust->_policies) {
322 return errSecInternal;
323 }
324 CFArrayRef policyArray = CFArrayCreateCopy(kCFAllocatorDefault, trust->_policies);
325 if (!policyArray) {
326 return errSecAllocate;
327 }
328 *policies = policyArray;
329 return errSecSuccess;
330 }
331
332 OSStatus SecTrustSetNetworkFetchAllowed(SecTrustRef trust, Boolean allowFetch) {
333 if (!trust) {
334 return errSecParam;
335 }
336 trust->_networkPolicy = (allowFetch) ? useNetworkEnabled : useNetworkDisabled;
337 return errSecSuccess;
338 }
339
340 OSStatus SecTrustGetNetworkFetchAllowed(SecTrustRef trust, Boolean *allowFetch) {
341 if (!trust || !allowFetch) {
342 return errSecParam;
343 }
344 Boolean allowed = false;
345 SecNetworkPolicy netPolicy = trust->_networkPolicy;
346 if (netPolicy == useNetworkDefault) {
347 // network fetch is enabled by default for SSL only
348 CFIndex idx, count = (trust->_policies) ? CFArrayGetCount(trust->_policies) : 0;
349 for (idx=0; idx<count; idx++) {
350 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(trust->_policies, idx);
351 if (policy) {
352 CFDictionaryRef props = SecPolicyCopyProperties(policy);
353 if (props) {
354 CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(props, kSecPolicyOid);
355 if (value) {
356 if (CFEqual(value, kSecPolicyAppleSSL)) {
357 allowed = true;
358 }
359 }
360 CFRelease(props);
361 }
362 }
363 }
364 } else {
365 // caller has explicitly set the network policy
366 allowed = (netPolicy == useNetworkEnabled);
367 }
368 *allowFetch = allowed;
369 return errSecSuccess;
370 }
371
372 CFAbsoluteTime SecTrustGetVerifyTime(SecTrustRef trust) {
373 CFAbsoluteTime verifyTime;
374 if (trust && trust->_verifyDate) {
375 verifyTime = CFDateGetAbsoluteTime(trust->_verifyDate);
376 } else {
377 verifyTime = CFAbsoluteTimeGetCurrent();
378 /* Record the verifyDate we ended up using. */
379 if (trust) {
380 trust->_verifyDate = CFDateCreate(CFGetAllocator(trust), verifyTime);
381 }
382 }
383 return verifyTime;
384 }
385
386 CFArrayRef SecTrustGetDetails(SecTrustRef trust) {
387 if (!trust) {
388 return NULL;
389 }
390 SecTrustEvaluateIfNecessary(trust);
391 return trust->_details;
392 }
393
394 OSStatus SecTrustGetTrustResult(SecTrustRef trust,
395 SecTrustResultType *result) {
396 if (!trust || !result) {
397 return errSecParam;
398 }
399 *result = trust->_trustResult;
400 return errSecSuccess;
401 }
402
403 static CFStringRef kSecCertificateDetailSHA1Digest = CFSTR("SHA1Digest");
404
405 static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix) {
406 if (!trust->_exceptions || ix >= CFArrayGetCount(trust->_exceptions))
407 return NULL;
408 CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_exceptions, ix);
409 if (CFGetTypeID(exception) != CFDictionaryGetTypeID())
410 return NULL;
411
412 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
413 if (!certificate)
414 return NULL;
415
416 /* If the exception contains the current certificates sha1Digest in the
417 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
418 CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificate);
419 CFTypeRef digestValue = CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest);
420 if (!digestValue || !CFEqual(sha1Digest, digestValue))
421 exception = NULL;
422
423 return exception;
424 }
425
426 struct SecTrustCheckExceptionContext {
427 CFDictionaryRef exception;
428 bool exceptionNotFound;
429 };
430
431 static void SecTrustCheckException(const void *key, const void *value, void *context) {
432 struct SecTrustCheckExceptionContext *cec = (struct SecTrustCheckExceptionContext *)context;
433 if (cec->exception) {
434 CFTypeRef exceptionValue = CFDictionaryGetValue(cec->exception, key);
435 if (!exceptionValue || !CFEqual(value, exceptionValue)) {
436 cec->exceptionNotFound = true;
437 }
438 } else {
439 cec->exceptionNotFound = true;
440 }
441 }
442
443 #if TARGET_OS_IPHONE
444 static CFArrayRef SecTrustCreatePolicyAnchorsArray(const UInt8* certData, CFIndex certLength)
445 {
446 CFArrayRef array = NULL;
447 CFAllocatorRef allocator = kCFAllocatorDefault;
448 SecCertificateRef cert = SecCertificateCreateWithBytes(allocator, certData, certLength);
449 if (cert) {
450 array = CFArrayCreate(allocator, (const void **)&cert, 1, &kCFTypeArrayCallBacks);
451 CFReleaseSafe(cert);
452 }
453 return array;
454 }
455 #endif
456
457 static void SecTrustAddPolicyAnchors(SecTrustRef trust)
458 {
459 /* Provide anchor certificates specifically required by certain policies.
460 This is used to evaluate test policies where the anchor is not provided
461 in the root store and may not be able to be supplied by the caller.
462 */
463 CFArrayRef policies = (trust) ? trust->_policies : NULL;
464 if (!policies) {
465 return;
466 }
467 CFIndex ix, count = CFArrayGetCount(policies);
468 for (ix = 0; ix < count; ++ix) {
469 SecPolicyRef policy = (SecPolicyRef) CFArrayGetValueAtIndex(policies, ix);
470 if (policy) {
471 #if TARGET_OS_IPHONE
472 if (CFEqual(policy->_oid, kSecPolicyAppleTestSMPEncryption)) {
473 CFReleaseSafe(trust->_anchors);
474 trust->_anchors = SecTrustCreatePolicyAnchorsArray(_SEC_TestAppleRootCAECC, sizeof(_SEC_TestAppleRootCAECC));
475 trust->_anchorsOnly = true;
476 break;
477 }
478 #endif
479 }
480 }
481 }
482
483
484 OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
485 if (!trust) {
486 return errSecParam;
487 }
488 OSStatus status = SecTrustEvaluateIfNecessary(trust);
489 if (status || !result)
490 return status;
491
492 /* post-process trust result based on exceptions */
493 SecTrustResultType trustResult = trust->_trustResult;
494 if (trustResult == kSecTrustResultUnspecified) {
495 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
496 if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
497 trustResult = kSecTrustResultProceed;
498 } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
499 /* If we have exceptions get details and match to exceptions. */
500 CFIndex pathLength = CFArrayGetCount(trust->_details);
501 struct SecTrustCheckExceptionContext context = {};
502 CFIndex ix;
503 for (ix = 0; ix < pathLength; ++ix) {
504 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_details, ix);
505
506 if ((ix == 0) && CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedLeaf))
507 {
508 trustResult = kSecTrustResultFatalTrustFailure;
509 goto DoneCheckingTrust;
510 }
511
512 if (CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedKey))
513 {
514 trustResult = kSecTrustResultFatalTrustFailure;
515 goto DoneCheckingTrust;
516 }
517
518 context.exception = SecTrustGetExceptionForCertificateAtIndex(trust, ix);
519 CFDictionaryApplyFunction(detail, SecTrustCheckException, &context);
520 if (context.exceptionNotFound) {
521 break;
522 }
523 }
524 if (!context.exceptionNotFound)
525 trustResult = kSecTrustResultProceed;
526 }
527 DoneCheckingTrust:
528 trust->_trustResult = trustResult;
529
530 /* log to syslog when there is a trust failure */
531 if (trustResult != kSecTrustResultProceed &&
532 trustResult != kSecTrustResultConfirm &&
533 trustResult != kSecTrustResultUnspecified) {
534 CFStringRef failureDesc = SecTrustCopyFailureDescription(trust);
535 secerror("%@", failureDesc);
536 CFRelease(failureDesc);
537 }
538
539
540 *result = trustResult;
541
542 return status;
543 }
544
545 OSStatus SecTrustEvaluateAsync(SecTrustRef trust,
546 dispatch_queue_t queue, SecTrustCallback result)
547 {
548 dispatch_async(queue, ^{
549 SecTrustResultType trustResult;
550 if (errSecSuccess != SecTrustEvaluate(trust, &trustResult)) {
551 trustResult = kSecTrustResultInvalid;
552 }
553 result(trust, trustResult);
554 });
555 return errSecSuccess;
556 }
557
558 static bool SecXPCDictionarySetCertificates(xpc_object_t message, const char *key, CFArrayRef certificates, CFErrorRef *error) {
559 xpc_object_t xpc_certificates = SecCertificateArrayCopyXPCArray(certificates, error);
560 if (!xpc_certificates)
561 return false;
562
563 xpc_dictionary_set_value(message, key, xpc_certificates);
564 xpc_release(xpc_certificates);
565
566 return true;
567 }
568
569 static bool SecXPCDictionarySetPolicies(xpc_object_t message, const char *key, CFArrayRef policies, CFErrorRef *error) {
570 xpc_object_t xpc_policies = SecPolicyArrayCopyXPCArray(policies, error);
571 if (!xpc_policies)
572 return false;
573 xpc_dictionary_set_value(message, key, xpc_policies);
574 xpc_release(xpc_policies);
575 return true;
576 }
577
578 static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message, const char *key, SecCertificatePathRef *path, CFErrorRef *error) {
579 xpc_object_t xpc_path = xpc_dictionary_get_value(message, key);
580 if (!xpc_path) {
581 *path = NULL;
582 return true;
583 }
584 *path = SecCertificatePathCreateWithXPCArray(xpc_path, error);
585 return *path;
586 }
587
588 static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message, const char *key, CFErrorRef *error) {
589 int64_t value = xpc_dictionary_get_int64(message, key);
590 if (!value) {
591 SecError(errSecInternal, error, CFSTR("object for key %s is 0"), key);
592 }
593 return (int)value;
594 }
595
596 static SecTrustResultType certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request(enum SecXPCOperation op, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *details, CFDictionaryRef *info, SecCertificatePathRef *chain, CFErrorRef *error)
597 {
598 __block SecTrustResultType tr = kSecTrustResultInvalid;
599 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
600 if (!SecXPCDictionarySetCertificates(message, kSecTrustCertificatesKey, certificates, error))
601 return false;
602 if (anchors && !SecXPCDictionarySetCertificates(message, kSecTrustAnchorsKey, anchors, error))
603 return false;
604 if (anchorsOnly)
605 xpc_dictionary_set_bool(message, kSecTrustAnchorsOnlyKey, anchorsOnly);
606 if (!SecXPCDictionarySetPolicies(message, kSecTrustPoliciesKey, policies, error))
607 return false;
608 xpc_dictionary_set_double(message, kSecTrustVerifyDateKey, verifyTime);
609 return true;
610 }, ^bool(xpc_object_t response, CFErrorRef *error) {
611 secdebug("trust", "response: %@", response);
612 return SecXPCDictionaryCopyArrayOptional(response, kSecTrustDetailsKey, details, error) &&
613 SecXPCDictionaryCopyDictionaryOptional(response, kSecTrustInfoKey, info, error) &&
614 SecXPCDictionaryCopyChainOptional(response, kSecTrustChainKey, chain, error) &&
615 ((tr = SecXPCDictionaryGetNonZeroInteger(response, kSecTrustResultKey, error)) != kSecTrustResultInvalid);
616 });
617 return tr;
618 }
619
620 static OSStatus SecTrustEvaluateIfNecessary(SecTrustRef trust) {
621 check(trust);
622 if (!trust)
623 return errSecParam;
624
625 if (trust->_trustResult != kSecTrustResultInvalid)
626 return errSecSuccess;
627
628 trust->_trustResult = kSecTrustResultOtherError; /* to avoid potential recursion */
629
630 CFReleaseNull(trust->_chain);
631 CFReleaseNull(trust->_details);
632 CFReleaseNull(trust->_info);
633
634 SecTrustAddPolicyAnchors(trust);
635
636 /* @@@ Consider an optimization where we keep a side dictionary with the SHA1 hash of ever SecCertificateRef we send, so we only send potential duplicates once, and have the server respond with either just the SHA1 hash of a certificate, or the complete certificate in the response depending on whether the client already sent it, so we don't send back certificates to the client it already has. */
637 return SecOSStatusWith(^bool (CFErrorRef *error) {
638 trust->_trustResult = SECURITYD_XPC(sec_trust_evaluate, certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request, trust->_certificates, trust->_anchors, trust->_anchorsOnly, trust->_policies, SecTrustGetVerifyTime(trust), SecAccessGroupsGetCurrent(), &trust->_details, &trust->_info, &trust->_chain, error);
639 if (trust->_trustResult == kSecTrustResultInvalid /* TODO check domain */ &&
640 SecErrorGetOSStatus(*error) == errSecNotAvailable &&
641 CFArrayGetCount(trust->_certificates)) {
642 /* We failed to talk to securityd. The only time this should
643 happen is when we are running prior to launchd enabling
644 registration of services. This currently happens when we
645 are running from the ramdisk. To make ASR happy we initialize
646 _chain and return success with a failure as the trustResult, to
647 make it seem like we did a cert evaluation, so ASR can extract
648 the public key from the leaf. */
649 trust->_chain = SecCertificatePathCreate(NULL, (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0));
650 if (error)
651 CFReleaseNull(*error);
652 return true;
653 }
654 return trust->_trustResult != kSecTrustResultInvalid;
655 });
656 }
657
658 /* Helper for the qsort below. */
659 static int compare_strings(const void *a1, const void *a2) {
660 CFStringRef s1 = *(CFStringRef *)a1;
661 CFStringRef s2 = *(CFStringRef *)a2;
662 return (int) CFStringCompare(s1, s2, kCFCompareForcedOrdering);
663 }
664
665 CFStringRef SecTrustCopyFailureDescription(SecTrustRef trust) {
666 CFMutableStringRef reason = CFStringCreateMutable(NULL, 0);
667 CFArrayRef details = SecTrustGetDetails(trust);
668 CFIndex pathLength = details ? CFArrayGetCount(details) : 0;
669 for (CFIndex ix = 0; ix < pathLength; ++ix) {
670 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
671 CFIndex dCount = CFDictionaryGetCount(detail);
672 if (dCount) {
673 if (ix == 0)
674 CFStringAppend(reason, CFSTR(" [leaf"));
675 else if (ix == pathLength - 1)
676 CFStringAppend(reason, CFSTR(" [root"));
677 else
678 CFStringAppendFormat(reason, NULL, CFSTR(" [ca%" PRIdCFIndex ), ix);
679
680 const void *keys[dCount];
681 CFDictionaryGetKeysAndValues(detail, &keys[0], NULL);
682 qsort(&keys[0], dCount, sizeof(keys[0]), compare_strings);
683 for (CFIndex kix = 0; kix < dCount; ++kix) {
684 CFStringRef key = keys[kix];
685 const void *value = CFDictionaryGetValue(detail, key);
686 CFStringAppendFormat(reason, NULL, CFSTR(" %@%@"), key,
687 (CFGetTypeID(value) == CFBooleanGetTypeID()
688 ? CFSTR("") : value));
689 }
690 CFStringAppend(reason, CFSTR("]"));
691 }
692 }
693 return reason;
694 }
695
696 SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) {
697 if (!trust) {
698 return NULL;
699 }
700 if (!trust->_publicKey) {
701 if (!trust->_chain) {
702 /* Trust hasn't been evaluated yet, first attempt to retrieve public key from leaf cert as is. */
703 trust->_publicKey = SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust, 0));
704 #if 0
705 if (!trust->_publicKey) {
706 /* If this fails use the passed in certs in order as if they are a valid cert path an attempt to extract the key. */
707 SecCertificatePathRef path;
708 // SecCertificatePathCreateWithArray Would have crashed if this code was every called
709 // since it expected an array of CFDataRefs not an array of certificates.
710 path = SecCertificatePathCreateWithArray(trust->_certificates);
711 trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(path, 0);
712 CFRelease(path);
713 }
714 #endif
715 if (!trust->_publicKey) {
716 /* Last resort, we evaluate the trust to get a _chain. */
717 SecTrustEvaluateIfNecessary(trust);
718 }
719 }
720 if (trust->_chain) {
721 trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(trust->_chain, 0);
722 }
723 }
724
725 if (trust->_publicKey)
726 CFRetain(trust->_publicKey);
727
728 return trust->_publicKey;
729 }
730
731 CFIndex SecTrustGetCertificateCount(SecTrustRef trust) {
732 if (!trust) {
733 return 0;
734 }
735 SecTrustEvaluateIfNecessary(trust);
736 return (trust->_chain) ? SecCertificatePathGetCount(trust->_chain) : 1;
737 }
738
739 SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust,
740 CFIndex ix) {
741 if (!trust) {
742 return NULL;
743 }
744 if (ix == 0) {
745 return (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0);
746 }
747 SecTrustEvaluateIfNecessary(trust);
748 return (trust->_chain) ? SecCertificatePathGetCertificateAtIndex(trust->_chain, ix) : NULL;
749 }
750
751 CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust) {
752 if (!trust) {
753 return NULL;
754 }
755 SecTrustEvaluateIfNecessary(trust);
756 CFDictionaryRef info = trust->_info;
757 if (info)
758 CFRetain(info);
759 return info;
760 }
761
762 CFDataRef SecTrustCopyExceptions(SecTrustRef trust) {
763 CFArrayRef details = SecTrustGetDetails(trust);
764 CFIndex pathLength = details ? CFArrayGetCount(details) : 0;
765 CFMutableArrayRef exceptions = CFArrayCreateMutable(kCFAllocatorDefault, pathLength, &kCFTypeArrayCallBacks);
766 CFIndex ix;
767 for (ix = 0; ix < pathLength; ++ix) {
768 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
769 CFIndex detailCount = CFDictionaryGetCount(detail);
770 CFMutableDictionaryRef exception;
771 if (ix == 0 || detailCount > 0) {
772 exception = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, detailCount + 1, detail);
773 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
774 CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
775 CFDictionaryAddValue(exception, kSecCertificateDetailSHA1Digest, digest);
776 } else {
777 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
778 exception = (CFMutableDictionaryRef)CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0,
779 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
780 }
781 CFArrayAppendValue(exceptions, exception);
782 CFRelease(exception);
783 }
784
785 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
786 since it will never be empty). */
787 for (ix = pathLength; ix-- > 1;) {
788 CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix);
789 if (CFDictionaryGetCount(exception) == 0) {
790 CFArrayRemoveValueAtIndex(exceptions, ix);
791 } else {
792 break;
793 }
794 }
795
796 CFDataRef encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault,
797 exceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
798 CFRelease(exceptions);
799
800 return encodedExceptions;
801 }
802
803 bool SecTrustSetExceptions(SecTrustRef trust, CFDataRef encodedExceptions) {
804 if (!trust) {
805 return false;
806 }
807 CFArrayRef exceptions = NULL;
808
809 if (NULL != encodedExceptions) {
810 exceptions = CFPropertyListCreateWithData(kCFAllocatorDefault,
811 encodedExceptions, kCFPropertyListImmutable, NULL, NULL);
812 }
813
814 if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) {
815 CFRelease(exceptions);
816 exceptions = NULL;
817 }
818 CFReleaseSafe(trust->_exceptions);
819 trust->_exceptions = exceptions;
820
821 /* If there is a valid exception entry for our current leaf we're golden. */
822 if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
823 return true;
824
825 /* The passed in exceptions didn't match our current leaf, so we discard it. */
826 CFReleaseNull(trust->_exceptions);
827 return false;
828 }
829
830 CFArrayRef SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
831 CFMutableArrayRef summary;
832 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
833 summary = SecCertificateCopySummaryProperties(certificate,
834 SecTrustGetVerifyTime(trust));
835 /* FIXME Add more details in the failure case. */
836
837 return summary;
838 }
839
840 CFArrayRef SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
841 CFArrayRef summary;
842 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
843 summary = SecCertificateCopyProperties(certificate);
844
845 return summary;
846 }
847
848 #if 0
849
850
851
852 /* Valid chain.
853 Can be on any non root cert in the chain.
854 Priority: Top down
855 Short circuit: Yes (No other errors matter after this one)
856 Non recoverable error
857 Trust UI: Invalid certificate chain linkage
858 Cert UI: Invalid linkage to parent certificate
859 */
860 CFStringRef kSecPolicyCheckIdLinkage = CFSTR("IdLinkage");
861
862 /* X.509 required checks.
863 Can be on any cert in the chain
864 Priority: Top down
865 Short circuit: Yes (No other errors matter after this one)
866 Non recoverable error
867 Trust UI: (One or more) unsupported critical extensions found.
868 */
869 /* If we have no names for the extention oids use:
870 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
871 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
872 */
873 CFStringRef kSecPolicyCheckCriticalExtensions = CFSTR("CriticalExtensions");
874 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
875 CFStringRef kSecPolicyCheckQualifiedCertStatements = CFSTR("QualifiedCertStatements");
876 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
877
878 /* Trusted root.
879 Only apply to the anchor.
880 Priority: N/A
881 Short circuit: No (Under discussion)
882 Recoverable
883 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
884 Cert UI: Not a valid anchor
885 */
886 CFStringRef kSecPolicyCheckAnchorTrusted = CFSTR("AnchorTrusted");
887 CFStringRef kSecPolicyCheckAnchorSHA1 = CFSTR("AnchorSHA1");
888
889 /* Binding.
890 Only applies to leaf
891 Priority: N/A
892 Short Circuit: No
893 Recoverable
894 Trust UI: (Hostname|email address) mismatch
895 */
896 CFStringRef kSecPolicyCheckSSLHostname = CFSTR("SSLHostname");
897
898 /* Policy specific checks.
899 Can be on any cert in the chain
900 Priority: Top down
901 Short Circuit: No
902 Recoverable
903 Trust UI: Certificate chain is not valid for the current policy.
904 OR: (One or more) certificates in the chain are not valid for the current policy/application
905 */
906 CFStringRef kSecPolicyCheckNonEmptySubject = CFSTR("NonEmptySubject");
907 /* Cert UI: Non CA certificate used as CA.
908 Cert UI: CA certificate used as leaf.
909 Cert UI: Cert chain length exceeded.
910 Cert UI: Basic constraints extension not critical (non fatal).
911 Cert UI: Leaf certificate has basic constraints extension (non fatal).
912 */
913 CFStringRef kSecPolicyCheckBasicContraints = CFSTR("BasicContraints");
914 CFStringRef kSecPolicyCheckKeyUsage = CFSTR("KeyUsage");
915 CFStringRef kSecPolicyCheckExtendedKeyUsage = CFSTR("ExtendedKeyUsage");
916 /* Checks that the issuer of the leaf has exactly one Common Name and that it
917 matches the specified string. */
918 CFStringRef kSecPolicyCheckIssuerCommonName = CFSTR("IssuerCommonName");
919 /* Checks that the leaf has exactly one Common Name and that it has the
920 specified string as a prefix. */
921 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix = CFSTR("SubjectCommonNamePrefix");
922 /* Check that the certificate chain length matches the specificed CFNumberRef
923 length. */
924 CFStringRef kSecPolicyCheckChainLength = CFSTR("ChainLength");
925 CFStringRef kSecPolicyCheckNotValidBefore = CFSTR("NotValidBefore");
926
927 /* Expiration.
928 Can be on any cert in the chain
929 Priority: Top down
930 Short Circuit: No
931 Recoverable
932 Trust UI: One or more certificates have expired or are not valid yet.
933 OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date')
934 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
935 */
936 CFStringRef kSecPolicyCheckValidIntermediates = CFSTR("ValidIntermediates");
937 CFStringRef kSecPolicyCheckValidLeaf = CFSTR("ValidLeaf");
938 CFStringRef kSecPolicyCheckValidRoot = CFSTR("ValidRoot");
939
940 #endif
941
942 struct TrustFailures {
943 bool badLinkage;
944 bool unknownCritExtn;
945 bool untrustedAnchor;
946 bool hostnameMismatch;
947 bool policyFail;
948 bool invalidCert;
949 };
950
951 static void applyDetailProperty(const void *_key, const void *_value,
952 void *context) {
953 CFStringRef key = (CFStringRef)_key;
954 struct TrustFailures *tf = (struct TrustFailures *)context;
955 if (CFGetTypeID(_value) != CFBooleanGetTypeID()) {
956 /* Value isn't a CFBooleanRef, oh no! */
957 return;
958 }
959 CFBooleanRef value = (CFBooleanRef)_value;
960 if (CFBooleanGetValue(value)) {
961 /* Not an actual failure so we don't report it. */
962 return;
963 }
964
965 /* @@@ FIXME: Report a different return value when something is in the
966 details but masked out by an exception and use that below for display
967 purposes. */
968 if (CFEqual(key, kSecPolicyCheckIdLinkage)) {
969 tf->badLinkage = true;
970 } else if (CFEqual(key, kSecPolicyCheckCriticalExtensions)
971 || CFEqual(key, kSecPolicyCheckQualifiedCertStatements)) {
972 tf->unknownCritExtn = true;
973 } else if (CFEqual(key, kSecPolicyCheckAnchorTrusted)
974 || CFEqual(key, kSecPolicyCheckAnchorSHA1)) {
975 tf->untrustedAnchor = true;
976 } else if (CFEqual(key, kSecPolicyCheckSSLHostname)) {
977 tf->hostnameMismatch = true;
978 } else if (CFEqual(key, kSecPolicyCheckValidIntermediates)
979 || CFEqual(key, kSecPolicyCheckValidLeaf)
980 || CFEqual(key, kSecPolicyCheckValidLeaf)) {
981 tf->invalidCert = true;
982 } else
983 /* Anything else is a policy failure. */
984 #if 0
985 if (CFEqual(key, kSecPolicyCheckNonEmptySubject)
986 || CFEqual(key, kSecPolicyCheckBasicContraints)
987 || CFEqual(key, kSecPolicyCheckKeyUsage)
988 || CFEqual(key, kSecPolicyCheckExtendedKeyUsage)
989 || CFEqual(key, kSecPolicyCheckIssuerCommonName)
990 || CFEqual(key, kSecPolicyCheckSubjectCommonNamePrefix)
991 || CFEqual(key, kSecPolicyCheckChainLength)
992 || CFEqual(key, kSecPolicyCheckNotValidBefore))
993 #endif
994 {
995 tf->policyFail = true;
996 }
997 }
998
999 static void appendError(CFMutableArrayRef properties, CFStringRef error) {
1000 CFStringRef localizedError = SecFrameworkCopyLocalizedString(error,
1001 CFSTR("SecCertificate"));
1002 appendProperty(properties, kSecPropertyTypeError, NULL, NULL,
1003 localizedError);
1004 CFReleaseNull(localizedError);
1005 }
1006
1007 CFArrayRef SecTrustCopyProperties(SecTrustRef trust) {
1008 CFArrayRef details = SecTrustGetDetails(trust);
1009 if (!details)
1010 return NULL;
1011
1012 struct TrustFailures tf = {};
1013
1014 CFIndex ix, count = CFArrayGetCount(details);
1015 for (ix = 0; ix < count; ++ix) {
1016 CFDictionaryRef detail = (CFDictionaryRef)
1017 CFArrayGetValueAtIndex(details, ix);
1018 /* We now have a detail dictionary for certificate at index ix, with
1019 a key value pair for each failed policy check. Let's convert it
1020 from Ro-Man form into something a Hu-Man can understand. */
1021 CFDictionaryApplyFunction(detail, applyDetailProperty, &tf);
1022 }
1023
1024 CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, 0,
1025 &kCFTypeArrayCallBacks);
1026 /* The badLinkage and unknownCritExtn failures are short circuited, since
1027 you can't recover from those errors. */
1028 if (tf.badLinkage) {
1029 appendError(properties, CFSTR("Invalid certificate chain linkage."));
1030 } else if (tf.unknownCritExtn) {
1031 appendError(properties, CFSTR("One or more unsupported critical extensions found."));
1032 } else {
1033 if (tf.untrustedAnchor) {
1034 appendError(properties, CFSTR("Root certificate is not trusted."));
1035 }
1036 if (tf.hostnameMismatch) {
1037 appendError(properties, CFSTR("Hostname mismatch."));
1038 }
1039 if (tf.policyFail) {
1040 appendError(properties, CFSTR("Policy requirements not met."));
1041 }
1042 if (tf.invalidCert) {
1043 appendError(properties, CFSTR("One or more certificates have expired or are not valid yet."));
1044 }
1045 }
1046
1047 if (CFArrayGetCount(properties) == 0) {
1048 /* The certificate chain is trusted, return an empty plist */
1049 CFReleaseNull(properties);
1050 }
1051
1052 return properties;
1053 }
1054
1055 CFDictionaryRef SecTrustCopyResult(SecTrustRef trust) {
1056 // Builds and returns a dictionary of evaluation results.
1057 if (!trust) {
1058 return NULL;
1059 }
1060 CFMutableDictionaryRef results = CFDictionaryCreateMutable(NULL, 0,
1061 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1062
1063 // kSecTrustResultDetails (per-cert results)
1064 CFArrayRef details = SecTrustGetDetails(trust);
1065 if (details) {
1066 CFDictionarySetValue(results, (const void *)kSecTrustResultDetails, (const void *)details);
1067 }
1068
1069 // kSecTrustResultValue (overall trust result)
1070 CFNumberRef numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, &trust->_trustResult);
1071 if (numValue) {
1072 CFDictionarySetValue(results, (const void *)kSecTrustResultValue, (const void *)numValue);
1073 CFRelease(numValue);
1074 }
1075 if (trust->_trustResult == kSecTrustResultInvalid || !trust->_info)
1076 return results; // we have nothing more to add
1077
1078 // kSecTrustEvaluationDate
1079 CFDateRef evaluationDate = trust->_verifyDate;
1080 if (evaluationDate) {
1081 CFDictionarySetValue(results, (const void *)kSecTrustEvaluationDate, (const void *)evaluationDate);
1082 }
1083
1084 // kSecTrustExtendedValidation, kSecTrustOrganizationName
1085 CFDictionaryRef info = trust->_info;
1086 if (info) {
1087 CFBooleanRef evValue;
1088 if (CFDictionaryGetValueIfPresent(info, kSecTrustInfoExtendedValidationKey, (const void **)&evValue)) {
1089 CFDictionarySetValue(results, (const void *)kSecTrustExtendedValidation, (const void *)evValue);
1090 }
1091 CFStringRef organizationName;
1092 if (CFDictionaryGetValueIfPresent(info, kSecTrustInfoCompanyNameKey, (const void **)&organizationName)) {
1093 CFDictionarySetValue(results, (const void *)kSecTrustOrganizationName, (const void *)organizationName);
1094 }
1095 }
1096
1097 #if 0
1098 //FIXME: need to add revocation results here
1099 // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate
1100 CFTypeRef expirationDate;
1101 if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecTrustExpirationDate, (const void **)&expirationDate)) {
1102 CFDictionarySetValue(results, (const void *)kSecTrustRevocationValidUntilDate, (const void *)expirationDate);
1103 CFDictionarySetValue(results, (const void *)kSecTrustRevocationChecked, (const void *)kCFBooleanTrue);
1104 }
1105 #endif
1106
1107 return results;
1108 }
1109
1110 // Return 0 upon error.
1111 static int to_int_error_request(enum SecXPCOperation op, CFErrorRef *error) {
1112 __block int64_t result = 0;
1113 securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) {
1114 result = xpc_dictionary_get_int64(response, kSecXPCKeyResult);
1115 if (!result)
1116 return SecError(errSecInternal, error, CFSTR("int64 missing in response"));
1117 return true;
1118 });
1119 return (int)result;
1120 }
1121
1122 // version 0 -> error, so we need to start at version 1 or later.
1123 OSStatus SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber)
1124 {
1125 return SecOSStatusWith(^bool(CFErrorRef *error) {
1126 if (!versionNumber)
1127 return SecError(errSecParam, error, CFSTR("versionNumber is NULL"));
1128
1129 return (*versionNumber = SECURITYD_XPC(sec_ota_pki_asset_version, to_int_error_request, error)) != 0;
1130 });
1131 }
1132
1133 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1134
1135 static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary, const char *key, xpc_type_t type)
1136 {
1137 xpc_object_t value = xpc_dictionary_get_value(dictionary, key);
1138
1139 return value && (xpc_get_type(value) == type);
1140 }
1141
1142 OSStatus SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset)
1143 {
1144 CFErrorRef error = NULL;
1145 do_if_registered(sec_ota_pki_get_new_asset, &error);
1146
1147 int64_t num = 0;
1148 xpc_object_t message = securityd_create_message(kSecXPCOpOTAPKIGetNewAsset, &error);
1149 if (message)
1150 {
1151 xpc_object_t response = securityd_message_with_reply_sync(message, &error);
1152
1153 if (response && xpc_dictionary_entry_is_type(response, kSecXPCKeyResult, XPC_TYPE_INT64))
1154 {
1155 num = (int64_t) xpc_dictionary_get_int64(response, kSecXPCKeyResult);
1156 xpc_release(response);
1157 }
1158
1159 xpc_release(message);
1160 }
1161
1162 if (NULL != didUpdateAsset)
1163 {
1164 *didUpdateAsset = (int)num;
1165 }
1166 return noErr;
1167 }
1168
1169
1170 #if 0
1171 // MARK: -
1172 // MARK: SecTrustNode
1173 /********************************************************
1174 **************** SecTrustNode object *******************
1175 ********************************************************/
1176 typedef uint8_t SecFetchingState;
1177 enum {
1178 kSecFetchingStatePassedIn = 0,
1179 kSecFetchingStateLocal,
1180 kSecFetchingStateFromURL,
1181 kSecFetchingStateDone,
1182 };
1183
1184 typedef uint8_t SecTrustState;
1185 enum {
1186 kSecTrustStateUnknown = 0,
1187 kSecTrustStateNotSigner,
1188 kSecTrustStateValidSigner,
1189 };
1190
1191 typedef struct __SecTrustNode *SecTrustNodeRef;
1192 struct __SecTrustNode {
1193 SecTrustNodeRef _child;
1194 SecCertificateRef _certificate;
1195
1196 /* Cached information about _certificate */
1197 bool _isAnchor;
1198 bool _isSelfSigned;
1199
1200 /* Set of all certificates we have ever considered as a parent. We use
1201 this to avoid having to recheck certs when we go to the next phase. */
1202 CFMutableSet _certificates;
1203
1204 /* Parents that are still partial chains we haven't yet considered. */
1205 CFMutableSet _partials;
1206 /* Parents that are still partial chains we have rejected. We reconsider
1207 these if we get to the final phase and we still haven't found a valid
1208 candidate. */
1209 CFMutableSet _rejected_partials;
1210
1211 /* Parents that are complete chains we haven't yet considered. */
1212 CFMutableSet _candidates;
1213 /* Parents that are complete chains we have rejected. */
1214 CFMutableSet _rejected_candidates;
1215
1216 /* State of candidate fetching. */
1217 SecFetchingState _fetchingState;
1218
1219 /* Trust state of _candidates[_candidateIndex] */
1220 SecTrustState _trustState;
1221 };
1222 typedef struct __SecTrustNode SecTrustNode;
1223
1224 /* Forward declarations of static functions. */
1225 static CFStringRef SecTrustNodeDescribe(CFTypeRef cf);
1226 static void SecTrustNodeDestroy(CFTypeRef cf);
1227
1228 /* Static functions. */
1229 static CFStringRef SecTrustNodeCopyDescription(CFTypeRef cf) {
1230 SecTrustNodeRef node = (SecTrustNodeRef)cf;
1231 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
1232 CFSTR("<SecTrustNodeRef: %p>"), node);
1233 }
1234
1235 static void SecTrustNodeDestroy(CFTypeRef cf) {
1236 SecTrustNodeRef trust = (SecTrustNodeRef)cf;
1237 if (trust->_child) {
1238 free(trust->_child);
1239 }
1240 if (trust->_certificate) {
1241 free(trust->_certificate);
1242 }
1243 if (trust->_candidates) {
1244 CFRelease(trust->_candidates);
1245 }
1246 }
1247
1248 /* SecTrustNode API functions. */
1249 CFGiblisFor(SecTrustNode)
1250
1251 SecTrustNodeRef SecTrustNodeCreate(SecTrustRef trust,
1252 SecCertificateRef certificate, SecTrustNodeRef child) {
1253 CFAllocatorRef allocator = kCFAllocatorDefault;
1254 check(trust);
1255 check(certificate);
1256
1257 CFIndex size = sizeof(struct __SecTrustNode);
1258 SecTrustNodeRef result = (SecTrustNodeRef)_CFRuntimeCreateInstance(
1259 allocator, SecTrustNodeGetTypeID(), size - sizeof(CFRuntimeBase), 0);
1260 if (!result)
1261 return NULL;
1262
1263 memset((char*)result + sizeof(result->_base), 0,
1264 sizeof(*result) - sizeof(result->_base));
1265 if (child) {
1266 CFRetain(child);
1267 result->_child = child;
1268 }
1269 CFRetain(certificate);
1270 result->_certificate = certificate;
1271 result->_isAnchor = SecTrustCertificateIsAnchor(certificate);
1272
1273 return result;
1274 }
1275
1276 SecCertificateRef SecTrustGetCertificate(SecTrustNodeRef node) {
1277 check(node);
1278 return node->_certificate;
1279 }
1280
1281 CFArrayRef SecTrustNodeCopyProperties(SecTrustNodeRef node,
1282 SecTrustRef trust) {
1283 check(node);
1284 check(trust);
1285 CFMutableArrayRef summary = SecCertificateCopySummaryProperties(
1286 node->_certificate, SecTrustGetVerifyTime(trust));
1287 /* FIXME Add more details in the failure case. */
1288 return summary;
1289 }
1290
1291 /* Attempt to verify this node's signature chain down to the child. */
1292 SecTrustState SecTrustNodeVerifySignatureChain(SecTrustNodeRef node) {
1293 /* FIXME */
1294 return kSecTrustStateUnknown;
1295 }
1296
1297
1298 /* See if the next candidate works. */
1299 SecTrustNodeRef SecTrustNodeCopyNextCandidate(SecTrustNodeRef node,
1300 SecTrustRef trust, SecFetchingState fetchingState) {
1301 check(node);
1302 check(trust);
1303
1304 CFAbsoluteTime verifyTime = SecTrustGetVerifyTime(trust);
1305
1306 for (;;) {
1307 /* If we have any unconsidered candidates left check those first. */
1308 while (node->_candidateIndex < CFArrayGetCount(node->_candidates)) {
1309 SecCertificateRef candidate = (SecCertificateRef)
1310 CFArrayGetValueAtIndex(node->_candidates, node->_candidateIndex);
1311 if (node->_fetchingState != kSecFetchingStateDone) {
1312 /* If we still have potential sources to fetch other candidates
1313 from we ignore expired candidates. */
1314 if (!SecCertificateIsValidOn(candidate, verifyTime)) {
1315 node->_candidateIndex++;
1316 continue;
1317 }
1318 }
1319
1320 SecTrustNodeRef parent = SecTrustNodeCreate(candidate, node);
1321 CFArrayRemoveValueAtIndex(node->_candidates, node->_candidateIndex);
1322 if (SecTrustNodeVerifySignatureChain(parent) ==
1323 kSecTrustStateNotSigner) {
1324 /* This candidate parent is not a valid signer of its
1325 child. */
1326 CFRelease(parent);
1327 /* If another signature failed further down the chain we need
1328 to backtrack down to whatever child is still a valid
1329 candidate and has additional candidates to consider.
1330 @@@ We really want to make the fetchingState a global of
1331 SecTrust itself as well and not have any node go beyond the
1332 current state of SecTrust if there are other (read cheap)
1333 options to consider. */
1334 continue;
1335 }
1336 return parent;
1337 }
1338
1339 /* We've run out of candidates in our current state so let's try to
1340 find some more. Note we fetch candidates in increasing order of
1341 cost in the hope we won't ever get to the more expensive fetching
1342 methods. */
1343 SecCertificateRef certificate = node->_certificate;
1344 switch (node->_fetchingState) {
1345 case kSecFetchingStatePassedIn:
1346 /* Get the list of candidates from SecTrust. */
1347 CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
1348 if (akid) {
1349 SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
1350 } else {
1351 CFDataRef issuer =
1352 SecCertificateGetNormalizedIssuerContent(certificate);
1353 SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
1354 }
1355 node->_fetchingState = kSecFetchingStateLocal;
1356 break;
1357 case kSecFetchingStateLocal:
1358 /* Lookup candidates in the local database. */
1359 node->_fetchingState = kSecFetchingStateFromURL;
1360 break;
1361 case kSecFetchingStateFromURL:
1362 node->_fetchingState = kSecFetchingStateCheckExpired;
1363 break;
1364 case kSecFetchingStateCheckExpired:
1365 /* Time to start considering expired candidates as well. */
1366 node->_candidateIndex = 0;
1367 node->_fetchingState = kSecFetchingStateDone;
1368 break;
1369 case kSecFetchingStateDone;
1370 return NULL;
1371 }
1372 }
1373
1374 CFAllocatorRef allocator = CFGetAllocator(node);
1375
1376 /* A trust node has a number of states.
1377 1) Look for issuing certificates by asking SecTrust about known
1378 parent certificates.
1379 2) Look for issuing certificates in certificate databases (keychains)
1380 3) Look for issuing certificates by going out to the web if the nodes
1381 certificate has a issuer location URL.
1382 4) Look through expired or not yet valid candidates we have put aside.
1383
1384 We go though the stages 1 though 3 looking for candidate issuer
1385 certificates. If a candidate certificate is not valid at verifyTime
1386 we put it in a to be examined later queue. If a candidate certificate
1387 is valid we verify if it actually signed our certificate (if possible).
1388 If not we discard it and continue on to the next candidate certificate.
1389 If it is we return a new SecTrustNodeRef for that certificate. */
1390
1391 CFMutableArrayRef issuers = CFArrayCreateMutable(allocator, 0,
1392 &kCFTypeArrayCallBacks);
1393
1394 /* Find a node's parent. */
1395 certificate = node->_certificate;
1396 CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
1397 CFTypeRef candidates = NULL;
1398 if (akid) {
1399 candidates = (CFTypeRef)CFDictionaryGetValueForKey(skidDict, akid);
1400 if (candidates) {
1401 addValidIssuersFrom(issuers, certificate, candidates, true);
1402 }
1403 }
1404 if (!candidates) {
1405 CFDataRef issuer =
1406 SecCertificateGetNormalizedIssuerContent(certificate);
1407 candidates = (CFTypeRef)
1408 CFDictionaryGetValueForKey(subjectDict, issuer);
1409 addValidIssuersFrom(issuers, certificate, candidates, false);
1410 }
1411
1412 if (CFArrayGetCount(issuers) == 0) {
1413 /* O no! we can't find an issuer for this certificate. Let's see
1414 if we can find one in the local database. */
1415 }
1416
1417 return errSecSuccess;
1418 }
1419
1420 CFArrayRef SecTrustNodeCopyNextChain(SecTrustNodeRef node,
1421 SecTrustRef trust) {
1422 /* Return the next full chain that isn't a reject unless we are in
1423 a state where we consider returning rejects. */
1424
1425 switch (node->_fetchingState) {
1426 case kSecFetchingStatePassedIn:
1427 /* Get the list of candidates from SecTrust. */
1428 CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
1429 if (akid) {
1430 SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
1431 } else {
1432 CFDataRef issuer =
1433 SecCertificateGetNormalizedIssuerContent(certificate);
1434 SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
1435 }
1436 node->_fetchingState = kSecFetchingStateLocal;
1437 break;
1438 case kSecFetchingStateLocal:
1439 /* Lookup candidates in the local database. */
1440 node->_fetchingState = kSecFetchingStateFromURL;
1441 break;
1442 case kSecFetchingStateFromURL:
1443 node->_fetchingState = kSecFetchingStateCheckExpired;
1444 break;
1445 case kSecFetchingStateCheckExpired:
1446 /* Time to start considering expired candidates as well. */
1447 node->_candidateIndex = 0;
1448 node->_fetchingState = kSecFetchingStateDone;
1449 break;
1450 case kSecFetchingStateDone;
1451 return NULL;
1452 }
1453 }
1454
1455 class Source {
1456 Iterator parentIterator(Cert);
1457 };
1458
1459 class NodeCache {
1460 Set nodes;
1461
1462 static bool unique(Node node) {
1463 if (nodes.contains(node))
1464 return false;
1465 nodes.add(node);
1466 return true;
1467 }
1468
1469 static bool isAnchor(Cert cert);
1470 };
1471
1472 class Node {
1473 Cert cert;
1474 Node child;
1475 int nextSource;
1476 Iterator parentIterator; /* For current source of parents. */
1477
1478 Node(Cert inCert) : child(nil), cert(inCert), nextSource(0) {}
1479 Node(Node inChild, Cert inCert) : child(inChild), cert(inCert),
1480 nextSource(0) {}
1481
1482 CertPath certPath() {
1483 CertPath path;
1484 Node node = this;
1485 while (node) {
1486 path.add(node.cert);
1487 node = node.child;
1488 }
1489 return path;
1490 }
1491
1492 void contains(Cert cert) {
1493 Node node = this;
1494 while (node) {
1495 if (cert == node.cert)
1496 return true;
1497 node = node.child;
1498 }
1499 return false;
1500 }
1501
1502 Node nextParent(Array currentSources) {
1503 for (;;) {
1504 if (!nextSource ||
1505 parentIterator == currentSources[nextSource - 1].end()) {
1506 if (nextSource == currentSources.count) {
1507 /* We ran out of parent sources. */
1508 return nil;
1509 }
1510 parentIterator = currentSources[nextSource++].begin();
1511 }
1512 Certificate cert = *parentIterator++;
1513 /* Check for cycles and self signed chains. */
1514 if (!contains(cert)) {
1515 Node node = Node(this, parent);
1516 if (!NodeCache.unique(node))
1517 return node;
1518 }
1519 }
1520 }
1521 };
1522
1523
1524 class PathBuilder {
1525 List nodes;
1526 List rejects;
1527 Array currentSources;
1528 Iterator nit;
1529 Array allSources;
1530 Iterator sourceIT;
1531 CertPath chain;
1532
1533 PathBuilder(Cert cert) {
1534 nodes.append(Node(cert));
1535 nit = nodes.begin();
1536 sourceIT = allSources.begin();
1537 }
1538
1539 nextAnchoredPath() {
1540 if (nit == nodes.end()) {
1541 /* We should add another source to the list of sources to
1542 search. */
1543 if (sourceIT == allSources.end()) {
1544 /* No more sources to add. */
1545 }
1546 currentSources += *sourceIT++;
1547 /* Resort nodes by size. */
1548 Nodes.sortBySize();
1549 nit = nodes.begin();
1550 /* Set the source list for all nodes. */
1551
1552 }
1553 while (Node node = *nit) {
1554 Node candidate = node.nextParent(currentSources);
1555 if (!candidate) {
1556 /* The current node has no more candidate parents so move
1557 along. */
1558 nit++;
1559 continue;
1560 }
1561
1562 if (candidate.isAnchored) {
1563 candidates.append(candidate);
1564 } else
1565 nodes.insert(candidate, nit);
1566 }
1567 }
1568
1569 findValidPath() {
1570 while (Node node = nextAnchoredPath()) {
1571 if (node.isValid()) {
1572 chain = node.certPath;
1573 break;
1574 }
1575 rejects.append(node);
1576 }
1577 }
1578 }
1579
1580
1581 #endif