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