]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecTrustSettings.cpp
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecTrustSettings.cpp
1 /*
2 * Copyright (c) 2005,2011-2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecTrustSettings.cpp - Public interface for manipulation of Trust Settings.
26 *
27 */
28
29 #include "SecBridge.h"
30 #include "SecCertificatePriv.h"
31 #include "SecTrustSettings.h"
32 #include "SecTrustSettingsPriv.h"
33 #include "SecTrustSettingsCertificates.h"
34 #include "SecCFRelease.h"
35 #include "TrustSettingsUtils.h"
36 #include "TrustSettings.h"
37 #include "TrustSettingsSchema.h"
38 #include "TrustKeychains.h"
39 #include "Trust.h"
40 #include "SecKeychainPriv.h"
41 #include "Globals.h"
42 #include <security_utilities/threading.h>
43 #include <security_utilities/globalizer.h>
44 #include <security_utilities/errors.h>
45 #include <security_cdsa_utilities/cssmerrors.h>
46 #include <security_utilities/logging.h>
47 #include <security_utilities/debugging.h>
48 #include <security_utilities/simpleprefs.h>
49 #include <securityd_client/dictionary.h>
50 #include <securityd_client/ssclient.h>
51 #include <assert.h>
52 #include <dlfcn.h>
53 #include <libproc.h>
54 #include <syslog.h>
55 #include <vector>
56 #include <CommonCrypto/CommonDigest.h>
57 #include <CoreFoundation/CFPreferences.h>
58 #include <utilities/SecCFRelease.h>
59
60 #define trustSettingsDbg(args...) secinfo("trustSettings", ## args)
61
62 /*
63 * Ideally we'd like to implement our own lock to protect the state of the cert stores
64 * without grabbing the global Sec API lock, but we deal with SecCFObjects, so we'll have
65 * to bite the bullet and grab the big lock. We also have our own lock protecting the
66 * global trust settings cache which is also used by the keychain callback function
67 * (which does not grab the Sec API lock).
68 */
69
70 #define BEGIN_RCSAPI \
71 OSStatus __secapiresult; \
72 try {
73 #define END_RCSAPI \
74 __secapiresult=errSecSuccess; \
75 } \
76 catch (const MacOSError &err) { __secapiresult=err.osStatus(); } \
77 catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } \
78 catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } \
79 catch (...) { __secapiresult=errSecInternalComponent; } \
80 return __secapiresult;
81
82 #define END_RCSAPI0 \
83 catch (...) {} \
84 return;
85
86
87 #pragma mark --- TrustSettings preferences ---
88
89 /*
90 * If Colonel Klink wants to disable user-level Trust Settings, he'll have
91 * to restart the apps which will be affected after he does so. We are not
92 * going to consult system prefs every time we do a cert evaluation. We
93 * consult it once per process and cache the results here.
94 */
95 static bool tsUserTrustDisableValid = false; /* true once we consult prefs */
96 static bool tsUserTrustDisable = false; /* the cached value */
97
98 /*
99 * Determine whether user-level Trust Settings disabled.
100 */
101 static bool tsUserTrustSettingsDisabled()
102 {
103 if(tsUserTrustDisableValid) {
104 return tsUserTrustDisable;
105 }
106 tsUserTrustDisable = false;
107
108 Dictionary* dictionary = Dictionary::CreateDictionary(kSecTrustSettingsPrefsDomain, Dictionary::US_System);
109 if (dictionary)
110 {
111 auto_ptr<Dictionary> prefsDict(dictionary);
112 /* this returns false if the pref isn't there, just like we want */
113 tsUserTrustDisable = prefsDict->getBoolValue(kSecTrustSettingsDisableUserTrustSettings);
114 }
115
116 tsUserTrustDisableValid = true;
117 return tsUserTrustDisable;
118 }
119
120 #pragma mark --- TrustSettings global cache ---
121
122 /***
123 *** cache submodule - keeps per-app copy of zero or one TrustSettings
124 *** for each domain. Used only by SecTrustSettingsEvaluateCert()
125 *** and SecTrustSettingsCopyQualifiedCerts(); results of
126 *** manipulation by public API functions are not cached.
127 ***/
128
129 /*
130 * API/client code has to hold this lock when doing anything with any of
131 * the TrustSettings maintained here.
132 * It's recursive to accomodate CodeSigning's need to do cert verification
133 * (while we evaluate app equivalence).
134 */
135 static ModuleNexus<RecursiveMutex> sutCacheLock;
136
137 #define TRUST_SETTINGS_NUM_DOMAINS 3
138
139 /*
140 * The three global TrustSettings.
141 * We rely on the fact the the domain enums start with 0; we use
142 * the domain value as an index into the following two arrays.
143 */
144 static TrustSettings *globalTrustSettings[TRUST_SETTINGS_NUM_DOMAINS] =
145 {NULL, NULL, NULL};
146
147 /*
148 * Indicates "the associated global here is currently valid; if there isn't a
149 * globalTrustSettings[domain], don't try to find one"
150 */
151 static bool globalTrustSettingsValid[TRUST_SETTINGS_NUM_DOMAINS] =
152 {false, false, false};
153
154 /* remember the fact that we've registered our KC callback */
155 static bool sutRegisteredCallback = false;
156
157 static void tsRegisterCallback();
158
159 /*
160 * Assign global TrustSetting to new incoming value, which may be NULL.
161 * Caller holds sutCacheLock.
162 */
163 static void tsSetGlobalTrustSettings(
164 TrustSettings *ts,
165 SecTrustSettingsDomain domain)
166 {
167 assert(((int)domain >= 0) && ((int)domain < TRUST_SETTINGS_NUM_DOMAINS));
168
169 trustSettingsDbg("tsSetGlobalTrustSettings domain %d: caching TS %p old TS %p",
170 (int)domain, ts, globalTrustSettings[domain]);
171 delete globalTrustSettings[domain];
172 globalTrustSettings[domain] = ts;
173 globalTrustSettingsValid[domain] = ts ? true : false;
174 tsRegisterCallback();
175 }
176
177 /*
178 * Obtain global TrustSettings for specified domain if it exists.
179 * Returns NULL if there is simply no TS for that domain.
180 * The TS, if returned, belongs to this cache module.
181 * Caller holds sutCacheLock.
182 */
183 static TrustSettings *tsGetGlobalTrustSettings(
184 SecTrustSettingsDomain domain)
185 {
186 assert(((int)domain >= 0) && ((int)domain < TRUST_SETTINGS_NUM_DOMAINS));
187
188 if((domain == kSecTrustSettingsDomainUser) && tsUserTrustSettingsDisabled()) {
189 trustSettingsDbg("tsGetGlobalTrustSettings: skipping DISABLED user domain");
190 return NULL;
191 }
192
193 if(globalTrustSettingsValid[domain]) {
194 // ready or not, use this
195 return globalTrustSettings[domain];
196 }
197 assert(globalTrustSettings[domain] == NULL);
198
199 /* try to find one */
200 OSStatus result = errSecSuccess;
201 TrustSettings *ts = NULL;
202 /* don't create; trim if found */
203 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_YES, ts);
204 if ( (domain != kSecTrustSettingsDomainSystem)
205 && (result == errSecInternalComponent)) {
206 /*
207 * Could not connect to ocspd to get the user/admin domain trust settings
208 * This happens in single user mode for example.
209 * Valid flag is set to false and continue.
210 */
211 trustSettingsDbg("tsGetGlobalTrustSettings: could not connect to ocspd for domain (%d)",(int)domain);
212 globalTrustSettingsValid[domain] = false;
213 tsRegisterCallback();
214 return NULL;
215 }
216 else if (result == errSecNoTrustSettings) {
217 /*
218 * No TrustSettings for this domain, actually a fairly common case.
219 * Optimize: don't bother trying this again.
220 */
221 trustSettingsDbg("tsGetGlobalTrustSettings: flagging known NULL");
222 globalTrustSettingsValid[domain] = true;
223 tsRegisterCallback();
224 return NULL;
225 }
226 else if(result != errSecSuccess) {
227 /* gross error */
228 MacOSError::throwMe(result);
229 }
230
231 tsSetGlobalTrustSettings(ts, domain);
232 return ts;
233 }
234
235 /*
236 * Purge TrustSettings cache.
237 * Called by Keychain Event callback and by our API functions that
238 * modify trust settings.
239 * Caller can NOT hold sutCacheLock.
240 */
241 static void tsPurgeCache()
242 {
243 int domain;
244
245 StLock<Mutex> _(sutCacheLock());
246 trustSettingsDbg("tsPurgeCache");
247 for(domain=0; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
248 tsSetGlobalTrustSettings(NULL, (SecTrustSettingsDomain) domain);
249 }
250 }
251
252 /*
253 * Keychain event callback function, for notification by other processes that
254 * user trust list(s) has/have changed.
255 */
256 static OSStatus tsTrustSettingsCallback (
257 SecKeychainEvent keychainEvent,
258 SecKeychainCallbackInfo *info,
259 void *context)
260 {
261 trustSettingsDbg("tsTrustSettingsCallback, event %d", (int)keychainEvent);
262 if(keychainEvent != kSecTrustSettingsChangedEvent) {
263 /* should not happen, right? */
264 return errSecSuccess;
265 }
266 if(info->pid == getpid()) {
267 /*
268 * Avoid dup cache invalidates: we already dealt with this event.
269 */
270 trustSettingsDbg("cacheEventCallback: our pid, skipping");
271 }
272 else {
273 tsPurgeCache();
274 }
275 return errSecSuccess;
276 }
277
278 /*
279 * Ensure that we've registered for kSecTrustSettingsChangedEvent callbacks
280 */
281 static void tsRegisterCallback()
282 {
283 if(sutRegisteredCallback) {
284 return;
285 }
286 trustSettingsDbg("tsRegisterCallback: registering callback");
287 OSStatus ortn = SecKeychainAddCallback(tsTrustSettingsCallback,
288 kSecTrustSettingsChangedEventMask, NULL);
289 if(ortn) {
290 trustSettingsDbg("tsRegisterCallback: SecKeychainAddCallback returned %d", (int)ortn);
291 /* Not sure how this could ever happen - maybe if there is no run loop active? */
292 }
293 sutRegisteredCallback = true;
294 }
295
296 #pragma mark --- Static functions ---
297
298
299 /*
300 * Called by API code when a trust list has changed; we notify other processes
301 * and purge our own cache.
302 */
303 static void tsTrustSettingsChanged()
304 {
305 tsPurgeCache();
306
307 /* The only interesting data is our pid */
308 NameValueDictionary nvd;
309 pid_t ourPid = getpid();
310 nvd.Insert (new NameValuePair (PID_KEY,
311 CssmData (reinterpret_cast<void*>(&ourPid), sizeof (pid_t))));
312 CssmData data;
313 nvd.Export (data);
314
315 trustSettingsDbg("tsTrustSettingsChanged: posting notification");
316 SecurityServer::ClientSession cs (Allocator::standard(), Allocator::standard());
317 cs.postNotification (SecurityServer::kNotificationDomainDatabase,
318 kSecTrustSettingsChangedEvent, data);
319 free (data.data ());
320 }
321
322 /*
323 * Common code for SecTrustSettingsCopyTrustSettings(),
324 * SecTrustSettingsCopyModificationDate().
325 */
326 static OSStatus tsCopyTrustSettings(
327 SecCertificateRef cert,
328 SecTrustSettingsDomain domain,
329 CFArrayRef *trustSettings, /* optionally RETURNED */
330 CFDateRef *modDate) /* optionally RETURNED */
331 {
332 BEGIN_RCSAPI
333
334 TS_REQUIRED(cert)
335
336 /* obtain fresh full copy from disk */
337 OSStatus result;
338 TrustSettings* ts;
339
340 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
341
342 // rather than throw these results, just return them because we are at the top level
343 if (result == errSecNoTrustSettings) {
344 return errSecItemNotFound;
345 }
346 else if (result != errSecSuccess) {
347 return result;
348 }
349
350 auto_ptr<TrustSettings>_(ts); // make sure this gets deleted just in case something throws underneath
351
352 if(trustSettings) {
353 *trustSettings = ts->copyTrustSettings(cert);
354 }
355 if(modDate) {
356 *modDate = ts->copyModDate(cert);
357 }
358
359 END_RCSAPI
360 }
361
362 static void tsAddConditionalCerts(CFMutableArrayRef certArray);
363
364 /*
365 * Common code for SecTrustSettingsCopyQualifiedCerts() and
366 * SecTrustSettingsCopyUnrestrictedRoots().
367 */
368 static OSStatus tsCopyCertsCommon(
369 /* usage constraints, all optional */
370 const CSSM_OID *policyOID,
371 const char *policyString,
372 SecTrustSettingsKeyUsage keyUsage,
373 /* constrain to only roots */
374 bool onlyRoots,
375 /* per-domain enables */
376 bool user,
377 bool admin,
378 bool system,
379 CFArrayRef *certArray) /* RETURNED */
380 {
381 StLock<Mutex> _TC(sutCacheLock());
382 StLock<Mutex> _TK(SecTrustKeychainsGetMutex());
383
384 TS_REQUIRED(certArray)
385
386 /* this relies on the domain enums being numbered 0..2, user..system */
387 bool domainEnable[3] = {user, admin, system};
388
389 /* we'll retain it again before successful exit */
390 CFRef<CFMutableArrayRef> outArray(CFArrayCreateMutable(NULL, 0,
391 &kCFTypeArrayCallBacks));
392
393 /*
394 * Search all keychains - user's keychain list, System.keychain,
395 * and system root store
396 */
397 StorageManager::KeychainList keychains;
398 Keychain adminKc;
399 if(user) {
400 globals().storageManager.getSearchList(keychains);
401 }
402 if(user || admin) {
403 adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
404 keychains.push_back(adminKc);
405 }
406 Keychain sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
407 keychains.push_back(sysRootKc);
408
409 assert(kSecTrustSettingsDomainUser == 0);
410 for(unsigned domain=0; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
411 if(!domainEnable[domain]) {
412 continue;
413 }
414 TrustSettings *ts = tsGetGlobalTrustSettings((SecTrustSettingsDomain)domain);
415 if(ts == NULL) {
416 continue;
417 }
418 ts->findQualifiedCerts(keychains,
419 false, /* !findAll */
420 onlyRoots,
421 policyOID, policyString, keyUsage,
422 outArray);
423 }
424 if (system) {
425 tsAddConditionalCerts(outArray);
426 }
427 *certArray = outArray;
428 CFRetainSafe(*certArray);
429 trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
430 CFArrayGetCount(outArray));
431 return errSecSuccess;
432 }
433
434 static void tsAddConditionalCerts(CFMutableArrayRef certArray)
435 {
436 #if TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE && !TARGET_OS_NANO
437 struct certmap_entry_s {
438 CFStringRef bundleId;
439 const UInt8* data;
440 const CFIndex length;
441 };
442 typedef struct certmap_entry_s certmap_entry_t;
443
444 CFBundleRef bundle = CFBundleGetMainBundle();
445 CFStringRef bundleIdentifier = (bundle) ? CFBundleGetIdentifier(bundle) : NULL;
446 if (!bundleIdentifier || !certArray) { return; }
447
448 // conditionally include 1024-bit compatibility roots for specific apps
449 const certmap_entry_t certmap[] = {
450 { CFSTR("com.autodesk.AdSSO"), _GTECyberTrustGlobalRootCA, sizeof(_GTECyberTrustGlobalRootCA) }, // rdar://25916338
451 { CFSTR("com.clo3d.MD5"), _ThawtePremiumServerCA, sizeof(_ThawtePremiumServerCA) }, // rdar://26281864
452 };
453
454 unsigned int i, certmaplen = sizeof(certmap) / sizeof(certmap_entry_t);
455 for (i=0; i<certmaplen; i++) {
456 if (CFStringCompare(bundleIdentifier, certmap[i].bundleId, 0) == kCFCompareEqualTo) {
457 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, certmap[i].data, certmap[i].length);
458 if (!cert) { continue; }
459 CFArrayAppendValue(certArray, cert);
460 CFRelease(cert);
461 cert = NULL;
462 }
463 }
464 #else
465 // this function is a no-op on iOS platforms
466 #endif
467 }
468
469
470 #pragma mark --- SPI functions ---
471
472
473 /*
474 * Fundamental routine used by TP to ascertain status of one cert.
475 *
476 * Returns true in *foundMatchingEntry if a trust setting matching
477 * specific constraints was found for the cert. Returns true in
478 * *foundAnyEntry if any entry was found for the cert, even if it
479 * did not match the specified constraints. The TP uses this to
480 * optimize for the case where a cert is being evaluated for
481 * one type of usage, and then later for another type. If
482 * foundAnyEntry is false, the second evaluation need not occur.
483 *
484 * Returns the domain in which a setting was found in *foundDomain.
485 *
486 * Allowed errors applying to the specified cert evaluation
487 * are returned in a mallocd array in *allowedErrors and must
488 * be freed by caller.
489 *
490 * The design of the entire TrustSettings module is centered around
491 * optimizing the performance of this routine (security concerns
492 * aside, that is). It's why the per-cert dictionaries are stored
493 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
494 * are cached in memory by tsGetGlobalTrustSettings(), and why those
495 * cached TrustSettings objects are 'trimmed' of dictionary fields
496 * which are not needed to verify a cert.
497 *
498 * The API functions which are used to manipulate Trust Settings
499 * are called infrequently and need not be particularly fast since
500 * they result in user interaction for authentication. Thus they do
501 * not use cached TrustSettings as this function does.
502 */
503 OSStatus SecTrustSettingsEvaluateCert(
504 CFStringRef certHashStr,
505 /* parameters describing the current cert evalaution */
506 const CSSM_OID *policyOID,
507 const char *policyString, /* optional */
508 uint32 policyStringLen,
509 SecTrustSettingsKeyUsage keyUsage, /* optional */
510 bool isRootCert, /* for checking default setting */
511 /* RETURNED values */
512 SecTrustSettingsDomain *foundDomain,
513 CSSM_RETURN **allowedErrors, /* mallocd */
514 uint32 *numAllowedErrors,
515 SecTrustSettingsResult *resultType,
516 bool *foundMatchingEntry,
517 bool *foundAnyEntry)
518 {
519 BEGIN_RCSAPI
520
521 StLock<Mutex> _(sutCacheLock());
522
523 TS_REQUIRED(certHashStr)
524 TS_REQUIRED(foundDomain)
525 TS_REQUIRED(allowedErrors)
526 TS_REQUIRED(numAllowedErrors)
527 TS_REQUIRED(resultType)
528 TS_REQUIRED(foundMatchingEntry)
529 TS_REQUIRED(foundAnyEntry)
530
531 /* ensure a NULL_terminated string */
532 auto_array<char> polStr;
533 if(policyString != NULL && policyStringLen > 0) {
534 polStr.allocate(policyStringLen + 1);
535 memmove(polStr.get(), policyString, policyStringLen);
536 if(policyString[policyStringLen - 1] != '\0') {
537 (polStr.get())[policyStringLen] = '\0';
538 }
539 }
540
541 /* initial condition - this can grow if we inspect multiple TrustSettings */
542 *allowedErrors = NULL;
543 *numAllowedErrors = 0;
544
545 /*
546 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
547 * search user first, then admin, then system.
548 */
549 assert(kSecTrustSettingsDomainAdmin == (kSecTrustSettingsDomainUser + 1));
550 assert(kSecTrustSettingsDomainSystem == (kSecTrustSettingsDomainAdmin + 1));
551 bool foundAny = false;
552 for(unsigned domain=kSecTrustSettingsDomainUser;
553 domain<=kSecTrustSettingsDomainSystem;
554 domain++) {
555 TrustSettings *ts = tsGetGlobalTrustSettings((SecTrustSettingsDomain)domain);
556 if(ts == NULL) {
557 continue;
558 }
559
560 /* validate cert returns true if matching entry was found */
561 bool foundAnyHere = false;
562 bool found = ts->evaluateCert(certHashStr, policyOID,
563 polStr.get(), keyUsage, isRootCert,
564 allowedErrors, numAllowedErrors, resultType, &foundAnyHere);
565
566 if(found) {
567 /*
568 * Note this, even though we may overwrite it later if this
569 * is an Unspecified entry and we find a definitive entry
570 * later
571 */
572 *foundDomain = (SecTrustSettingsDomain)domain;
573 }
574 if(found && (*resultType != kSecTrustSettingsResultUnspecified)) {
575 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain);
576 *foundAnyEntry = true;
577 *foundMatchingEntry = true;
578 return errSecSuccess;
579 }
580 foundAny |= foundAnyHere;
581 }
582 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
583 *foundAnyEntry = foundAny;
584 *foundMatchingEntry = false;
585 return errSecSuccess;
586 END_RCSAPI
587 }
588
589 /*
590 * Obtain trusted certs which match specified usage.
591 * Only certs with a SecTrustSettingsResult of
592 * kSecTrustSettingsResultTrustRoot or
593 * or kSecTrustSettingsResultTrustAsRoot will be returned.
594 * To be used by SecureTransport for its SSLSetTrustedRoots() call;
595 * I hope nothing else has to use this...
596 * Caller must CFRelease the returned CFArrayRef.
597 */
598 OSStatus SecTrustSettingsCopyQualifiedCerts(
599 const CSSM_OID *policyOID,
600 const char *policyString, /* optional */
601 uint32 policyStringLen,
602 SecTrustSettingsKeyUsage keyUsage, /* optional */
603 CFArrayRef *certArray) /* RETURNED */
604 {
605 BEGIN_RCSAPI
606
607 /* ensure a NULL_terminated string */
608 auto_array<char> polStr;
609 if(policyString != NULL) {
610 polStr.allocate(policyStringLen + 1);
611 memmove(polStr.get(), policyString, policyStringLen);
612 if(policyString[policyStringLen - 1] != '\0') {
613 (polStr.get())[policyStringLen] = '\0';
614 }
615 }
616
617 return tsCopyCertsCommon(policyOID, polStr.get(), keyUsage,
618 false, /* !onlyRoots */
619 true, true, true, /* all domains */
620 certArray);
621
622 END_RCSAPI
623 }
624
625 /*
626 * Obtain unrestricted root certs from the specified domain(s).
627 * Only returns roots with no usage constraints.
628 * Caller must CFRelease the returned CFArrayRef.
629 */
630 OSStatus SecTrustSettingsCopyUnrestrictedRoots(
631 Boolean user,
632 Boolean admin,
633 Boolean system,
634 CFArrayRef *certArray) /* RETURNED */
635 {
636 BEGIN_RCSAPI
637
638 OSStatus status = tsCopyCertsCommon(NULL, NULL, NULL, /* no constraints */
639 true, /* onlyRoots */
640 user, admin, system,
641 certArray);
642
643 return status;
644
645 END_RCSAPI
646 }
647
648 static const char hexChars[16] = {
649 '0', '1', '2', '3', '4', '5', '6', '7',
650 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
651 };
652
653 /*
654 * Obtain a string representing a cert's SHA1 digest. This string is
655 * the key used to look up per-cert trust settings in a TrustSettings record.
656 */
657 CFStringRef SecTrustSettingsCertHashStrFromCert(
658 SecCertificateRef certRef)
659 {
660 if(certRef == NULL) {
661 return NULL;
662 }
663
664 if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
665 /* use this string instead of the cert hash as the dictionary key */
666 trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting");
667 secerror("Caller passed kSecTrustSettingsDefaultRootCertSetting. This constant is deprecated and no longer affects the behavior of the system.");
668 return kSecTrustRecordDefaultRootCert;
669 }
670
671 CSSM_DATA certData;
672 OSStatus ortn = SecCertificateGetData(certRef, &certData);
673 if(ortn) {
674 return NULL;
675 }
676 return SecTrustSettingsCertHashStrFromData(certData.Data, certData.Length);
677 }
678
679 CFStringRef SecTrustSettingsCertHashStrFromData(
680 const void *cert,
681 size_t certLen)
682 {
683 unsigned char digest[CC_SHA1_DIGEST_LENGTH];
684 char asciiDigest[(2 * CC_SHA1_DIGEST_LENGTH) + 1];
685 unsigned dex;
686 char *outp = asciiDigest;
687 unsigned char *inp = digest;
688
689 if(cert == NULL) {
690 return NULL;
691 }
692
693 CC_SHA1(cert, (CC_LONG)certLen, digest);
694
695 for(dex=0; dex<CC_SHA1_DIGEST_LENGTH; dex++) {
696 unsigned c = *inp++;
697 outp[1] = hexChars[c & 0xf];
698 c >>= 4;
699 outp[0] = hexChars[c];
700 outp += 2;
701 }
702 *outp = 0;
703 return CFStringCreateWithCString(NULL, asciiDigest, kCFStringEncodingASCII);
704 }
705
706 /*
707 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
708 * No locking or cache flushing here; it's all local to the TrustSettings
709 * we construct here.
710 */
711 OSStatus SecTrustSettingsSetTrustSettingsExternal(
712 CFDataRef settingsIn, /* optional */
713 SecCertificateRef certRef, /* optional */
714 CFTypeRef trustSettingsDictOrArray, /* optional */
715 CFDataRef *settingsOut) /* RETURNED */
716 {
717 BEGIN_RCSAPI
718
719 TS_REQUIRED(settingsOut)
720
721 OSStatus result;
722 TrustSettings* ts;
723
724 result = TrustSettings::CreateTrustSettings((SecTrustSettingsDomain)kSecTrustSettingsDomainMemory, settingsIn, ts);
725 if (result != errSecSuccess) {
726 return result;
727 }
728
729 auto_ptr<TrustSettings>_(ts);
730
731 if(certRef != NULL) {
732 ts->setTrustSettings(certRef, trustSettingsDictOrArray);
733 }
734 *settingsOut = ts->createExternal();
735 return errSecSuccess;
736
737 END_RCSAPI
738 }
739
740 #pragma mark --- API functions ---
741
742 OSStatus SecTrustSettingsCopyTrustSettings(
743 SecCertificateRef certRef,
744 SecTrustSettingsDomain domain,
745 CFArrayRef *trustSettings) /* RETURNED */
746 {
747 TS_REQUIRED(certRef)
748 TS_REQUIRED(trustSettings)
749
750 OSStatus result = tsCopyTrustSettings(certRef, domain, trustSettings, NULL);
751 if (result == errSecSuccess && *trustSettings == NULL) {
752 result = errSecItemNotFound; /* documented result if no trust settings exist */
753 }
754 return result;
755 }
756
757 OSStatus SecTrustSettingsCopyModificationDate(
758 SecCertificateRef certRef,
759 SecTrustSettingsDomain domain,
760 CFDateRef *modificationDate) /* RETURNED */
761 {
762 TS_REQUIRED(certRef)
763 TS_REQUIRED(modificationDate)
764
765 OSStatus result = tsCopyTrustSettings(certRef, domain, NULL, modificationDate);
766 if (result == errSecSuccess && *modificationDate == NULL) {
767 result = errSecItemNotFound; /* documented result if no trust settings exist */
768 }
769 return result;
770 }
771
772 /* works with existing and with new cert */
773 OSStatus SecTrustSettingsSetTrustSettings(
774 SecCertificateRef certRef,
775 SecTrustSettingsDomain domain,
776 CFTypeRef trustSettingsDictOrArray)
777 {
778 BEGIN_RCSAPI
779
780 TS_REQUIRED(certRef)
781
782 if(domain == kSecTrustSettingsDomainSystem) {
783 return errSecDataNotModifiable;
784 }
785
786 OSStatus result;
787 TrustSettings* ts;
788
789 result = TrustSettings::CreateTrustSettings(domain, CREATE_YES, TRIM_NO, ts);
790 if (result != errSecSuccess) {
791 return result;
792 }
793
794 auto_ptr<TrustSettings>_(ts);
795
796 ts->setTrustSettings(certRef, trustSettingsDictOrArray);
797 ts->flushToDisk();
798 tsTrustSettingsChanged();
799 return errSecSuccess;
800
801 END_RCSAPI
802 }
803
804 OSStatus SecTrustSettingsRemoveTrustSettings(
805 SecCertificateRef cert,
806 SecTrustSettingsDomain domain)
807 {
808 BEGIN_RCSAPI
809
810 TS_REQUIRED(cert)
811
812 if(domain == kSecTrustSettingsDomainSystem) {
813 return errSecDataNotModifiable;
814 }
815
816 OSStatus result;
817 TrustSettings* ts;
818
819 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
820 if (result != errSecSuccess) {
821 return result;
822 }
823
824 auto_ptr<TrustSettings>_(ts);
825
826 /* deleteTrustSettings throws if record not found */
827 trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
828 (int)domain);
829 ts->deleteTrustSettings(cert);
830 ts->flushToDisk();
831 tsTrustSettingsChanged();
832 return errSecSuccess;
833
834 END_RCSAPI
835 }
836
837 /* get all certs listed in specified domain */
838 OSStatus SecTrustSettingsCopyCertificates(
839 SecTrustSettingsDomain domain,
840 CFArrayRef *certArray)
841 {
842 BEGIN_RCSAPI
843
844 TS_REQUIRED(certArray)
845
846 OSStatus status;
847 TrustSettings* ts;
848 CFMutableArrayRef trustedCertArray = NULL;
849
850 status = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
851 if (status != errSecSuccess) {
852 return status;
853 }
854
855 auto_ptr<TrustSettings>_(ts);
856
857 CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
858
859 /*
860 * Keychains to search: user's search list, System.keychain, system root store
861 */
862 StorageManager::KeychainList keychains;
863 Keychain adminKc;
864 Keychain sysRootKc;
865 switch(domain) {
866 case kSecTrustSettingsDomainUser:
867 /* user search list */
868 globals().storageManager.getSearchList(keychains);
869 /* drop thru to next case */
870 case kSecTrustSettingsDomainAdmin:
871 /* admin certs in system keychain */
872 adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
873 keychains.push_back(adminKc);
874 /* drop thru to next case */
875 case kSecTrustSettingsDomainSystem:
876 /* and, for all cases, immutable system root store */
877 sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
878 keychains.push_back(sysRootKc);
879 default:
880 /* already validated when we created the TrustSettings */
881 break;
882 }
883 ts->findCerts(keychains, outArray);
884 CFIndex count = outArray ? CFArrayGetCount(outArray) : 0;
885 if(count == 0) {
886 CFReleaseSafe(outArray);
887 return errSecNoTrustSettings;
888 }
889 /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */
890 if (kSecTrustSettingsDomainSystem == domain) {
891 CFIndex i;
892 SecPolicyRef policy = SecPolicyCreateBasicX509();
893 trustedCertArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
894 for (i = 0; i < count ; i++) {
895 SecTrustRef trust;
896 SecTrustResultType result;
897 SecCertificateRef certificate = (SecCertificateRef) CFArrayGetValueAtIndex(outArray, i);
898 status = SecTrustCreateWithCertificates(certificate, policy, &trust);
899 if (status != errSecSuccess) {
900 CFReleaseSafe(policy);
901 goto out;
902 }
903 status = SecTrustEvaluate(trust, &result);
904 if (status != errSecSuccess) {
905 CFReleaseSafe(policy);
906 goto out;
907 }
908 if (result != kSecTrustResultFatalTrustFailure) {
909 CFArrayAppendValue(trustedCertArray, certificate);
910 }
911 }
912 tsAddConditionalCerts(trustedCertArray);
913 if (CFArrayGetCount(trustedCertArray) == 0) {
914 status = errSecNoTrustSettings;
915 } else {
916 *certArray = trustedCertArray;
917 CFReleaseSafe(outArray);
918 }
919 CFReleaseSafe(policy);
920 } else {
921 *certArray = outArray;
922 }
923 out:
924 if (status != errSecSuccess) {
925 CFReleaseSafe(outArray);
926 CFReleaseSafe(trustedCertArray);
927 }
928 return status;
929 END_RCSAPI
930 }
931
932 static CFArrayRef gUserAdminCerts = NULL;
933 static bool gUserAdminCertsCacheBuilt = false;
934 static ReadWriteLock gUserAdminCertsLock;
935
936 void SecTrustSettingsPurgeUserAdminCertsCache(void) {
937 StReadWriteLock _(gUserAdminCertsLock, StReadWriteLock::Write);
938 CFReleaseNull(gUserAdminCerts);
939 gUserAdminCertsCacheBuilt = false;
940 }
941
942 OSStatus SecTrustSettingsCopyCertificatesForUserAdminDomains(
943 CFArrayRef *certArray)
944 {
945 TS_REQUIRED(certArray);
946 OSStatus result = errSecSuccess;
947
948 { /* Hold the read lock for the check */
949 StReadWriteLock _(gUserAdminCertsLock, StReadWriteLock::Read);
950 if (gUserAdminCertsCacheBuilt) {
951 if (gUserAdminCerts) {
952 *certArray = (CFArrayRef)CFRetain(gUserAdminCerts);
953 return errSecSuccess;
954 } else {
955 return errSecNoTrustSettings;
956 }
957 }
958 }
959
960 /* There were no cached results. We'll have to recreate them. */
961 CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
962 if (!outArray) {
963 return errSecAllocate;
964 }
965
966 CFArrayRef userTrusted = NULL, adminTrusted = NULL;
967 OSStatus userStatus = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser, &userTrusted);
968 if ((userStatus == errSecSuccess) && (userTrusted != NULL)) {
969 CFArrayAppendArray(outArray, userTrusted, CFRangeMake(0, CFArrayGetCount(userTrusted)));
970 CFRelease(userTrusted);
971 }
972
973 OSStatus adminStatus = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin, &adminTrusted);
974 if ((adminStatus == errSecSuccess) && (adminTrusted != NULL)) {
975 CFArrayAppendArray(outArray, adminTrusted, CFRangeMake(0, CFArrayGetCount(adminTrusted)));
976 CFRelease(adminTrusted);
977 }
978
979 /* Lack of trust settings for a domain results in an error above. Only fail
980 * if we weren't able to get trust settings for both domains. */
981 if (userStatus != errSecSuccess && adminStatus != errSecSuccess) {
982 result = userStatus;
983 }
984
985 if (result != errSecSuccess && outArray) {
986 CFRelease(outArray);
987 outArray = NULL;
988 }
989
990 *certArray = outArray;
991
992 /* For valid results, update the global cache */
993 if (result == errSecSuccess || result == errSecNoTrustSettings) {
994 StReadWriteLock _(gUserAdminCertsLock, StReadWriteLock::Write);
995 CFReleaseNull(gUserAdminCerts);
996 gUserAdminCerts = (CFArrayRef)CFRetainSafe(outArray);
997 gUserAdminCertsCacheBuilt = true;
998 }
999
1000 return result;
1001 }
1002
1003 /*
1004 * Obtain an external, portable representation of the specified
1005 * domain's TrustSettings. Caller must CFRelease the returned data.
1006 */
1007 OSStatus SecTrustSettingsCreateExternalRepresentation(
1008 SecTrustSettingsDomain domain,
1009 CFDataRef *trustSettings)
1010 {
1011 BEGIN_RCSAPI
1012
1013 TS_REQUIRED(trustSettings)
1014
1015 OSStatus result;
1016 TrustSettings* ts;
1017
1018 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
1019 if (result != errSecSuccess) {
1020 return result;
1021 }
1022
1023 auto_ptr<TrustSettings>_(ts);
1024
1025 *trustSettings = ts->createExternal();
1026 return errSecSuccess;
1027
1028 END_RCSAPI
1029 }
1030
1031 /*
1032 * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation,
1033 * into the specified domain.
1034 */
1035 OSStatus SecTrustSettingsImportExternalRepresentation(
1036 SecTrustSettingsDomain domain,
1037 CFDataRef trustSettings) /* optional - NULL means empty settings */
1038 {
1039 BEGIN_RCSAPI
1040
1041 if(domain == kSecTrustSettingsDomainSystem) {
1042 return errSecDataNotModifiable;
1043 }
1044
1045 OSStatus result;
1046 TrustSettings* ts;
1047
1048 result = TrustSettings::CreateTrustSettings(domain, trustSettings, ts);
1049 if (result != errSecSuccess) {
1050 return result;
1051 }
1052
1053 auto_ptr<TrustSettings>_(ts);
1054
1055 ts->flushToDisk();
1056 tsTrustSettingsChanged();
1057 return errSecSuccess;
1058
1059 END_RCSAPI
1060 }
1061
1062 /*
1063 * SecTrustSettingsSetTrustSettings convenience wrapper function.
1064 */
1065 void SecTrustSettingsSetTrustedCertificateForSSLHost(
1066 SecCertificateRef certificate,
1067 CFStringRef hostname,
1068 void (^result)(SecTrustSettingsResult trustResult, CFErrorRef error))
1069 {
1070 __block CFMutableArrayRef trustSettings = NULL;
1071 __block CFNumberRef trustSettingsResult = NULL;
1072 __block SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
1073
1074 CFDictionaryRef policyProperties = NULL;
1075 CFStringRef policyOid = NULL;
1076 SecPolicyRef policy = NULL;
1077
1078 Boolean isSelfSigned = false;
1079 Boolean hasPolicyConstraint = false;
1080 Boolean hasPolicyValue = false;
1081 Boolean policyConstraintChanged = false;
1082 Boolean changed = false;
1083 CFIndex indexOfEntryWithAllowedErrorForExpiredCert = kCFNotFound;
1084 CFIndex indexOfEntryWithAllowedErrorForHostnameMismatch = kCFNotFound;
1085 CFIndex indexOfEntryWithAllowedErrorNotSet = kCFNotFound;
1086 CFIndex i, count;
1087 int32_t trustSettingsResultCode = kSecTrustSettingsResultTrustAsRoot;
1088 OSStatus status = errSecSuccess;
1089
1090 CFRetainSafe(certificate);
1091 CFRetainSafe(hostname);
1092 if (!certificate || !hostname) {
1093 status = errSecParam;
1094 } else {
1095 status = SecCertificateIsSelfSigned(certificate, &isSelfSigned);
1096 }
1097 if (status != errSecSuccess) {
1098 goto reportErr;
1099 }
1100 if (isSelfSigned) {
1101 trustSettingsResultCode = kSecTrustSettingsResultTrustRoot;
1102 }
1103 trustSettingsResult = CFNumberCreate(NULL, kCFNumberSInt32Type, &trustSettingsResultCode);
1104
1105 /* start with the existing trust settings for this certificate, if any */
1106 {
1107 CFArrayRef curTrustSettings = NULL;
1108 (void)SecTrustSettingsCopyTrustSettings(certificate, domain, &curTrustSettings);
1109 if (curTrustSettings) {
1110 trustSettings = CFArrayCreateMutableCopy(NULL, 0, curTrustSettings);
1111 CFReleaseNull(curTrustSettings);
1112 } else {
1113 trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1114 }
1115 }
1116 if (!trustSettings || !trustSettingsResult) {
1117 status = errSecAllocate;
1118 goto reportErr;
1119 }
1120
1121 /* set up policy and value instances to trust the certificate for SSL for a given hostname */
1122 policy = SecPolicyCreateSSL(true, hostname);
1123 if (!policy) {
1124 status = errSecInternal;
1125 goto reportErr;
1126 }
1127 policyProperties = SecPolicyCopyProperties(policy);
1128 if (!policyProperties) {
1129 status = errSecInternal;
1130 goto reportErr;
1131 }
1132 policyOid = (CFStringRef)CFDictionaryGetValue(policyProperties, kSecPolicyOid);
1133 CFRetainSafe(policyOid);
1134 if (!policyOid) {
1135 status = errSecInternal;
1136 goto reportErr;
1137 }
1138
1139 /* look for dictionaries in the trust settings array for this policy and value */
1140 count = CFArrayGetCount(trustSettings);
1141 for (i=0; i < count; i++) {
1142 CFDictionaryRef constraints = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, i);
1143 if (!constraints) { continue; }
1144 SecPolicyRef aPolicy = (SecPolicyRef)CFDictionaryGetValue(constraints, kSecTrustSettingsPolicy);
1145 if (!aPolicy) { continue; }
1146 CFDictionaryRef properties = SecPolicyCopyProperties(aPolicy);
1147 if (!properties) { continue; }
1148 CFStringRef aPolicyOid = (CFStringRef)CFDictionaryGetValue(properties, kSecPolicyOid);
1149 if (aPolicyOid && kCFCompareEqualTo == CFStringCompare(aPolicyOid, policyOid, 0)) {
1150 CFStringRef aPolicyString = (CFStringRef)CFDictionaryGetValue(constraints, kSecTrustSettingsPolicyString);
1151 if (aPolicyString && kCFCompareEqualTo == CFStringCompare(aPolicyString, hostname, kCFCompareCaseInsensitive)) {
1152 /* found existing entry */
1153 CFNumberRef allowedErr = (CFNumberRef)CFDictionaryGetValue(constraints, kSecTrustSettingsAllowedError);
1154 int32_t eOld = 0;
1155 if (!allowedErr || !CFNumberGetValue(allowedErr, kCFNumberSInt32Type, &eOld)) {
1156 eOld = CSSM_OK;
1157 }
1158 CFNumberRef tsResult = (CFNumberRef)CFDictionaryGetValue(constraints, kSecTrustSettingsResult);
1159 int32_t rOld = 0;
1160 if (!tsResult || !CFNumberGetValue(allowedErr, kCFNumberSInt32Type, &rOld)) {
1161 rOld = kSecTrustSettingsResultTrustRoot;
1162 }
1163 if (!hasPolicyValue) { hasPolicyValue = (aPolicyString != NULL); }
1164 if (!hasPolicyConstraint) { hasPolicyConstraint = true; }
1165 if (eOld == CSSMERR_TP_CERT_EXPIRED) {
1166 indexOfEntryWithAllowedErrorForExpiredCert = i;
1167 } else if (eOld == CSSMERR_APPLETP_HOSTNAME_MISMATCH) {
1168 indexOfEntryWithAllowedErrorForHostnameMismatch = i;
1169 } else if (eOld == CSSM_OK) {
1170 indexOfEntryWithAllowedErrorNotSet = i;
1171 }
1172 if (trustSettingsResultCode != rOld) {
1173 changed = policyConstraintChanged = true; // we are changing existing policy constraint's result
1174 }
1175 }
1176 }
1177 CFReleaseSafe(properties);
1178 }
1179
1180 if (!hasPolicyConstraint) {
1181 policyConstraintChanged = true; // we are adding a new policy constraint
1182 } else if (hostname && !hasPolicyValue) {
1183 policyConstraintChanged = true; // we need to add the hostname to an existing policy constraint
1184 } else if ((indexOfEntryWithAllowedErrorForExpiredCert == kCFNotFound) ||
1185 (indexOfEntryWithAllowedErrorForHostnameMismatch == kCFNotFound)) {
1186 policyConstraintChanged = true; // we are missing one of the expected allowed-error entries for this policy
1187 }
1188
1189 if (policyConstraintChanged) {
1190 CFMutableDictionaryRef policyDict[2] = { NULL, NULL };
1191 policyDict[0] = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1192 policyDict[1] = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1193 int32_t certExpiredCode = (int32_t)CSSMERR_TP_CERT_EXPIRED;
1194 CFNumberRef certExpired = CFNumberCreate(NULL, kCFNumberSInt32Type, &certExpiredCode);
1195 int32_t hostnameMismatchCode = (int32_t)CSSMERR_APPLETP_HOSTNAME_MISMATCH;
1196 CFNumberRef hostnameMismatch = CFNumberCreate(NULL, kCFNumberSInt32Type, &hostnameMismatchCode);
1197 if (!policyDict[0] || !policyDict[1] || !certExpired || !hostnameMismatch) {
1198 status = errSecInternal;
1199 } else {
1200 /* set up entry for policy, hostname, expired cert error, and result */
1201 CFDictionarySetValue(policyDict[0], kSecTrustSettingsPolicy, policy);
1202 CFDictionarySetValue(policyDict[0], kSecTrustSettingsPolicyString, hostname);
1203 CFDictionarySetValue(policyDict[0], kSecTrustSettingsAllowedError, certExpired);
1204 CFDictionarySetValue(policyDict[0], kSecTrustSettingsResult, trustSettingsResult);
1205 if (indexOfEntryWithAllowedErrorForExpiredCert != kCFNotFound) {
1206 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1207 CFArraySetValueAtIndex(trustSettings, indexOfEntryWithAllowedErrorForExpiredCert, policyDict[0]);
1208 } else if (!(hasPolicyValue)) {
1209 /* add a new policy constraint */
1210 CFArrayAppendValue(trustSettings, policyDict[0]);
1211 }
1212 /* set up additional entry for policy, hostname, hostname mismatch error, and result */
1213 CFDictionarySetValue(policyDict[1], kSecTrustSettingsPolicy, policy);
1214 CFDictionarySetValue(policyDict[1], kSecTrustSettingsPolicyString, hostname);
1215 CFDictionarySetValue(policyDict[1], kSecTrustSettingsAllowedError, hostnameMismatch);
1216 CFDictionarySetValue(policyDict[1], kSecTrustSettingsResult, trustSettingsResult);
1217 if (indexOfEntryWithAllowedErrorForHostnameMismatch != kCFNotFound) {
1218 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1219 CFArraySetValueAtIndex(trustSettings, indexOfEntryWithAllowedErrorForHostnameMismatch, policyDict[1]);
1220 } else if (!(hasPolicyValue)) {
1221 /* add a new policy constraint */
1222 CFArrayAppendValue(trustSettings, policyDict[1]);
1223 }
1224 }
1225 CFReleaseSafe(policyDict[0]);
1226 CFReleaseSafe(policyDict[1]);
1227 CFReleaseSafe(certExpired);
1228 CFReleaseSafe(hostnameMismatch);
1229 }
1230
1231 if (status != errSecSuccess) {
1232 goto reportErr;
1233 }
1234 CFReleaseSafe(policyOid);
1235 CFReleaseSafe(policyProperties);
1236 CFReleaseSafe(policy);
1237
1238 dispatch_async(dispatch_get_main_queue(), ^{
1239 /* add certificate to keychain first */
1240 OSStatus status = SecCertificateAddToKeychain(certificate, NULL);
1241 if (status == errSecSuccess || status == errSecDuplicateItem) {
1242 /* this will block on authorization UI... */
1243 status = SecTrustSettingsSetTrustSettings(certificate,
1244 domain, trustSettings);
1245 }
1246 if (result) {
1247 CFErrorRef error = NULL;
1248 if (status) {
1249 error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
1250 }
1251 int32_t tsrc;
1252 if (!CFNumberGetValue(trustSettingsResult, kCFNumberSInt32Type, (int32_t*)&tsrc)) {
1253 tsrc = (int32_t)kSecTrustSettingsResultUnspecified;
1254 }
1255 result((SecTrustSettingsResult)tsrc, error);
1256 CFReleaseSafe(error);
1257 }
1258 CFRelease(trustSettingsResult);
1259 CFRelease(trustSettings);
1260 CFRelease(certificate);
1261 CFRelease(hostname);
1262 });
1263
1264 return;
1265
1266 reportErr:
1267 CFReleaseSafe(policyOid);
1268 CFReleaseSafe(policyProperties);
1269 CFReleaseSafe(policy);
1270 CFReleaseSafe(trustSettingsResult);
1271 CFReleaseSafe(trustSettings);
1272 CFReleaseSafe(certificate);
1273 CFReleaseSafe(hostname);
1274 if (result) {
1275 CFErrorRef error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
1276 result(kSecTrustSettingsResultInvalid, error);
1277 CFReleaseSafe(error);
1278 }
1279 }