]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecTrustSettings.cpp
Security-59306.101.1.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 <Security/SecCertificatePriv.h>
31 #include <Security/SecTrustSettings.h>
32 #include <Security/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 SecTrustSettingsPurgeUserAdminCertsCache();
307
308 /* The only interesting data is our pid */
309 NameValueDictionary nvd;
310 pid_t ourPid = getpid();
311 nvd.Insert (new NameValuePair (PID_KEY,
312 CssmData (reinterpret_cast<void*>(&ourPid), sizeof (pid_t))));
313 CssmData data;
314 nvd.Export (data);
315
316 trustSettingsDbg("tsTrustSettingsChanged: posting notification");
317 SecurityServer::ClientSession cs (Allocator::standard(), Allocator::standard());
318 cs.postNotification (SecurityServer::kNotificationDomainDatabase,
319 kSecTrustSettingsChangedEvent, data);
320 free (data.data ());
321 }
322
323 /*
324 * Common code for SecTrustSettingsCopyTrustSettings(),
325 * SecTrustSettingsCopyModificationDate().
326 */
327 static OSStatus tsCopyTrustSettings(
328 SecCertificateRef cert,
329 SecTrustSettingsDomain domain,
330 CFArrayRef *trustSettings, /* optionally RETURNED */
331 CFDateRef *modDate) /* optionally RETURNED */
332 {
333 BEGIN_RCSAPI
334
335 TS_REQUIRED(cert)
336
337 /* obtain fresh full copy from disk */
338 OSStatus result;
339 TrustSettings* ts;
340
341 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
342
343 // rather than throw these results, just return them because we are at the top level
344 if (result == errSecNoTrustSettings) {
345 return errSecItemNotFound;
346 }
347 else if (result != errSecSuccess) {
348 return result;
349 }
350
351 auto_ptr<TrustSettings>_(ts); // make sure this gets deleted just in case something throws underneath
352
353 if(trustSettings) {
354 *trustSettings = ts->copyTrustSettings(cert);
355 }
356 if(modDate) {
357 *modDate = ts->copyModDate(cert);
358 }
359
360 END_RCSAPI
361 }
362
363 static void tsAddConditionalCerts(CFMutableArrayRef certArray);
364
365 /*
366 * Common code for SecTrustSettingsCopyQualifiedCerts() and
367 * SecTrustSettingsCopyUnrestrictedRoots().
368 */
369 static OSStatus tsCopyCertsCommon(
370 /* usage constraints, all optional */
371 const CSSM_OID *policyOID,
372 const char *policyString,
373 SecTrustSettingsKeyUsage keyUsage,
374 /* constrain to only roots */
375 bool onlyRoots,
376 /* per-domain enables */
377 bool user,
378 bool admin,
379 bool system,
380 CFArrayRef *certArray) /* RETURNED */
381 {
382 StLock<Mutex> _TC(sutCacheLock());
383 StLock<Mutex> _TK(SecTrustKeychainsGetMutex());
384
385 TS_REQUIRED(certArray)
386
387 /* this relies on the domain enums being numbered 0..2, user..system */
388 bool domainEnable[3] = {user, admin, system};
389
390 /* we'll retain it again before successful exit */
391 CFRef<CFMutableArrayRef> outArray(CFArrayCreateMutable(NULL, 0,
392 &kCFTypeArrayCallBacks));
393
394 /*
395 * Search all keychains - user's keychain list, System.keychain,
396 * and system root store
397 */
398 StorageManager::KeychainList keychains;
399 Keychain adminKc;
400 if(user) {
401 globals().storageManager.getSearchList(keychains);
402 }
403 if(user || admin) {
404 adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
405 keychains.push_back(adminKc);
406 }
407 Keychain sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
408 keychains.push_back(sysRootKc);
409
410 assert(kSecTrustSettingsDomainUser == 0);
411 for(unsigned domain=0; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
412 if(!domainEnable[domain]) {
413 continue;
414 }
415 TrustSettings *ts = tsGetGlobalTrustSettings((SecTrustSettingsDomain)domain);
416 if(ts == NULL) {
417 continue;
418 }
419 ts->findQualifiedCerts(keychains,
420 false, /* !findAll */
421 onlyRoots,
422 policyOID, policyString, keyUsage,
423 outArray);
424 }
425 if (system) {
426 tsAddConditionalCerts(outArray);
427 }
428 *certArray = outArray;
429 CFRetainSafe(*certArray);
430 trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
431 CFArrayGetCount(outArray));
432 return errSecSuccess;
433 }
434
435 static void tsAddConditionalCerts(CFMutableArrayRef certArray)
436 {
437 #if TARGET_OS_OSX
438 struct certmap_entry_s {
439 CFStringRef bundleId;
440 const UInt8* data;
441 const CFIndex length;
442 };
443 typedef struct certmap_entry_s certmap_entry_t;
444
445 CFBundleRef bundle = CFBundleGetMainBundle();
446 CFStringRef bundleIdentifier = (bundle) ? CFBundleGetIdentifier(bundle) : NULL;
447 if (!bundleIdentifier || !certArray) { return; }
448
449 // conditionally include 1024-bit compatibility roots for specific apps
450 const certmap_entry_t certmap[] = {
451 { CFSTR("com.autodesk.AdSSO"), _GTECyberTrustGlobalRootCA, sizeof(_GTECyberTrustGlobalRootCA) }, // rdar://25916338
452 { CFSTR("com.clo3d.MD5"), _ThawtePremiumServerCA, sizeof(_ThawtePremiumServerCA) }, // rdar://26281864
453 };
454
455 unsigned int i, certmaplen = sizeof(certmap) / sizeof(certmap_entry_t);
456 for (i=0; i<certmaplen; i++) {
457 if (CFStringCompare(bundleIdentifier, certmap[i].bundleId, 0) == kCFCompareEqualTo) {
458 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, certmap[i].data, certmap[i].length);
459 if (!cert) { continue; }
460 CFArrayAppendValue(certArray, cert);
461 CFRelease(cert);
462 cert = NULL;
463 }
464 }
465 #else
466 // this function is a no-op on iOS platforms
467 #endif
468 }
469
470
471 #pragma mark --- SPI functions ---
472
473
474 /*
475 * Fundamental routine used by TP to ascertain status of one cert.
476 *
477 * Returns true in *foundMatchingEntry if a trust setting matching
478 * specific constraints was found for the cert. Returns true in
479 * *foundAnyEntry if any entry was found for the cert, even if it
480 * did not match the specified constraints. The TP uses this to
481 * optimize for the case where a cert is being evaluated for
482 * one type of usage, and then later for another type. If
483 * foundAnyEntry is false, the second evaluation need not occur.
484 *
485 * Returns the domain in which a setting was found in *foundDomain.
486 *
487 * Allowed errors applying to the specified cert evaluation
488 * are returned in a mallocd array in *allowedErrors and must
489 * be freed by caller.
490 *
491 * The design of the entire TrustSettings module is centered around
492 * optimizing the performance of this routine (security concerns
493 * aside, that is). It's why the per-cert dictionaries are stored
494 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
495 * are cached in memory by tsGetGlobalTrustSettings(), and why those
496 * cached TrustSettings objects are 'trimmed' of dictionary fields
497 * which are not needed to verify a cert.
498 *
499 * The API functions which are used to manipulate Trust Settings
500 * are called infrequently and need not be particularly fast since
501 * they result in user interaction for authentication. Thus they do
502 * not use cached TrustSettings as this function does.
503 */
504 OSStatus SecTrustSettingsEvaluateCert(
505 CFStringRef certHashStr,
506 /* parameters describing the current cert evalaution */
507 const CSSM_OID *policyOID,
508 const char *policyString, /* optional */
509 uint32 policyStringLen,
510 SecTrustSettingsKeyUsage keyUsage, /* optional */
511 bool isRootCert, /* for checking default setting */
512 /* RETURNED values */
513 SecTrustSettingsDomain *foundDomain,
514 CSSM_RETURN **allowedErrors, /* mallocd */
515 uint32 *numAllowedErrors,
516 SecTrustSettingsResult *resultType,
517 bool *foundMatchingEntry,
518 bool *foundAnyEntry)
519 {
520 BEGIN_RCSAPI
521
522 StLock<Mutex> _(sutCacheLock());
523
524 TS_REQUIRED(certHashStr)
525 TS_REQUIRED(foundDomain)
526 TS_REQUIRED(allowedErrors)
527 TS_REQUIRED(numAllowedErrors)
528 TS_REQUIRED(resultType)
529 TS_REQUIRED(foundMatchingEntry)
530 TS_REQUIRED(foundAnyEntry)
531
532 /* ensure a NULL_terminated string */
533 auto_array<char> polStr;
534 if(policyString != NULL && policyStringLen > 0) {
535 polStr.allocate(policyStringLen + 1);
536 memmove(polStr.get(), policyString, policyStringLen);
537 if(policyString[policyStringLen - 1] != '\0') {
538 (polStr.get())[policyStringLen] = '\0';
539 }
540 }
541
542 /* initial condition - this can grow if we inspect multiple TrustSettings */
543 *allowedErrors = NULL;
544 *numAllowedErrors = 0;
545
546 /*
547 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
548 * search user first, then admin, then system.
549 */
550 assert(kSecTrustSettingsDomainAdmin == (kSecTrustSettingsDomainUser + 1));
551 assert(kSecTrustSettingsDomainSystem == (kSecTrustSettingsDomainAdmin + 1));
552 bool foundAny = false;
553 for(unsigned domain=kSecTrustSettingsDomainUser;
554 domain<=kSecTrustSettingsDomainSystem;
555 domain++) {
556 TrustSettings *ts = tsGetGlobalTrustSettings((SecTrustSettingsDomain)domain);
557 if(ts == NULL) {
558 continue;
559 }
560
561 /* validate cert returns true if matching entry was found */
562 bool foundAnyHere = false;
563 bool found = ts->evaluateCert(certHashStr, policyOID,
564 polStr.get(), keyUsage, isRootCert,
565 allowedErrors, numAllowedErrors, resultType, &foundAnyHere);
566
567 if(found) {
568 /*
569 * Note this, even though we may overwrite it later if this
570 * is an Unspecified entry and we find a definitive entry
571 * later
572 */
573 *foundDomain = (SecTrustSettingsDomain)domain;
574 }
575 if(found && (*resultType != kSecTrustSettingsResultUnspecified)) {
576 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain);
577 *foundAnyEntry = true;
578 *foundMatchingEntry = true;
579 return errSecSuccess;
580 }
581 foundAny |= foundAnyHere;
582 }
583 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
584 *foundAnyEntry = foundAny;
585 *foundMatchingEntry = false;
586 return errSecSuccess;
587 END_RCSAPI
588 }
589
590 /*
591 * Obtain trusted certs which match specified usage.
592 * Only certs with a SecTrustSettingsResult of
593 * kSecTrustSettingsResultTrustRoot or
594 * or kSecTrustSettingsResultTrustAsRoot will be returned.
595 * To be used by SecureTransport for its SSLSetTrustedRoots() call;
596 * I hope nothing else has to use this...
597 * Caller must CFRelease the returned CFArrayRef.
598 */
599 OSStatus SecTrustSettingsCopyQualifiedCerts(
600 const CSSM_OID *policyOID,
601 const char *policyString, /* optional */
602 uint32 policyStringLen,
603 SecTrustSettingsKeyUsage keyUsage, /* optional */
604 CFArrayRef *certArray) /* RETURNED */
605 {
606 BEGIN_RCSAPI
607
608 /* ensure a NULL_terminated string */
609 auto_array<char> polStr;
610 if(policyString != NULL) {
611 polStr.allocate(policyStringLen + 1);
612 memmove(polStr.get(), policyString, policyStringLen);
613 if(policyString[policyStringLen - 1] != '\0') {
614 (polStr.get())[policyStringLen] = '\0';
615 }
616 }
617
618 return tsCopyCertsCommon(policyOID, polStr.get(), keyUsage,
619 false, /* !onlyRoots */
620 true, true, true, /* all domains */
621 certArray);
622
623 END_RCSAPI
624 }
625
626 /*
627 * Obtain unrestricted root certs from the specified domain(s).
628 * Only returns roots with no usage constraints.
629 * Caller must CFRelease the returned CFArrayRef.
630 */
631 OSStatus SecTrustSettingsCopyUnrestrictedRoots(
632 Boolean user,
633 Boolean admin,
634 Boolean system,
635 CFArrayRef *certArray) /* RETURNED */
636 {
637 BEGIN_RCSAPI
638
639 OSStatus status = tsCopyCertsCommon(NULL, NULL, NULL, /* no constraints */
640 true, /* onlyRoots */
641 user, admin, system,
642 certArray);
643
644 return status;
645
646 END_RCSAPI
647 }
648
649 static const char hexChars[16] = {
650 '0', '1', '2', '3', '4', '5', '6', '7',
651 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
652 };
653
654 /*
655 * Obtain a string representing a cert's SHA1 digest. This string is
656 * the key used to look up per-cert trust settings in a TrustSettings record.
657 */
658 CFStringRef SecTrustSettingsCertHashStrFromCert(
659 SecCertificateRef certRef)
660 {
661 if(certRef == NULL) {
662 return NULL;
663 }
664
665 if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
666 /* use this string instead of the cert hash as the dictionary key */
667 trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting");
668 secerror("Caller passed kSecTrustSettingsDefaultRootCertSetting. This constant is deprecated and no longer affects the behavior of the system.");
669 return kSecTrustRecordDefaultRootCert;
670 }
671
672 CSSM_DATA certData;
673 OSStatus ortn = SecCertificateGetData(certRef, &certData);
674 if(ortn) {
675 return NULL;
676 }
677 return SecTrustSettingsCertHashStrFromData(certData.Data, certData.Length);
678 }
679
680 CFStringRef SecTrustSettingsCertHashStrFromData(
681 const void *cert,
682 size_t certLen)
683 {
684 unsigned char digest[CC_SHA1_DIGEST_LENGTH];
685 char asciiDigest[(2 * CC_SHA1_DIGEST_LENGTH) + 1];
686 unsigned dex;
687 char *outp = asciiDigest;
688 unsigned char *inp = digest;
689
690 if(cert == NULL) {
691 return NULL;
692 }
693
694 CC_SHA1(cert, (CC_LONG)certLen, digest);
695
696 for(dex=0; dex<CC_SHA1_DIGEST_LENGTH; dex++) {
697 unsigned c = *inp++;
698 outp[1] = hexChars[c & 0xf];
699 c >>= 4;
700 outp[0] = hexChars[c];
701 outp += 2;
702 }
703 *outp = 0;
704 return CFStringCreateWithCString(NULL, asciiDigest, kCFStringEncodingASCII);
705 }
706
707 /*
708 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
709 * No locking or cache flushing here; it's all local to the TrustSettings
710 * we construct here.
711 */
712 OSStatus SecTrustSettingsSetTrustSettingsExternal(
713 CFDataRef settingsIn, /* optional */
714 SecCertificateRef certRef, /* optional */
715 CFTypeRef trustSettingsDictOrArray, /* optional */
716 CFDataRef *settingsOut) /* RETURNED */
717 {
718 BEGIN_RCSAPI
719
720 TS_REQUIRED(settingsOut)
721
722 OSStatus result;
723 TrustSettings* ts;
724
725 result = TrustSettings::CreateTrustSettings((SecTrustSettingsDomain)kSecTrustSettingsDomainMemory, settingsIn, ts);
726 if (result != errSecSuccess) {
727 return result;
728 }
729
730 auto_ptr<TrustSettings>_(ts);
731
732 if(certRef != NULL) {
733 ts->setTrustSettings(certRef, trustSettingsDictOrArray);
734 }
735 *settingsOut = ts->createExternal();
736 return errSecSuccess;
737
738 END_RCSAPI
739 }
740
741 #pragma mark --- API functions ---
742
743 OSStatus SecTrustSettingsCopyTrustSettings(
744 SecCertificateRef certRef,
745 SecTrustSettingsDomain domain,
746 CFArrayRef *trustSettings) /* RETURNED */
747 {
748 TS_REQUIRED(certRef)
749 TS_REQUIRED(trustSettings)
750
751 OSStatus result = tsCopyTrustSettings(certRef, domain, trustSettings, NULL);
752 if (result == errSecSuccess && *trustSettings == NULL) {
753 result = errSecItemNotFound; /* documented result if no trust settings exist */
754 }
755 return result;
756 }
757
758 OSStatus SecTrustSettingsCopyModificationDate(
759 SecCertificateRef certRef,
760 SecTrustSettingsDomain domain,
761 CFDateRef *modificationDate) /* RETURNED */
762 {
763 TS_REQUIRED(certRef)
764 TS_REQUIRED(modificationDate)
765
766 OSStatus result = tsCopyTrustSettings(certRef, domain, NULL, modificationDate);
767 if (result == errSecSuccess && *modificationDate == NULL) {
768 result = errSecItemNotFound; /* documented result if no trust settings exist */
769 }
770 return result;
771 }
772
773 /* works with existing and with new cert */
774 OSStatus SecTrustSettingsSetTrustSettings(
775 SecCertificateRef certRef,
776 SecTrustSettingsDomain domain,
777 CFTypeRef trustSettingsDictOrArray)
778 {
779 BEGIN_RCSAPI
780
781 TS_REQUIRED(certRef)
782
783 if(domain == kSecTrustSettingsDomainSystem) {
784 return errSecDataNotModifiable;
785 }
786
787 OSStatus result;
788 TrustSettings* ts;
789
790 result = TrustSettings::CreateTrustSettings(domain, CREATE_YES, TRIM_NO, ts);
791 if (result != errSecSuccess) {
792 return result;
793 }
794
795 auto_ptr<TrustSettings>_(ts);
796
797 ts->setTrustSettings(certRef, trustSettingsDictOrArray);
798 ts->flushToDisk();
799 tsTrustSettingsChanged();
800 return errSecSuccess;
801
802 END_RCSAPI
803 }
804
805 OSStatus SecTrustSettingsRemoveTrustSettings(
806 SecCertificateRef cert,
807 SecTrustSettingsDomain domain)
808 {
809 BEGIN_RCSAPI
810
811 TS_REQUIRED(cert)
812
813 if(domain == kSecTrustSettingsDomainSystem) {
814 return errSecDataNotModifiable;
815 }
816
817 OSStatus result;
818 TrustSettings* ts;
819
820 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
821 if (result != errSecSuccess) {
822 return result;
823 }
824
825 auto_ptr<TrustSettings>_(ts);
826
827 /* deleteTrustSettings throws if record not found */
828 trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
829 (int)domain);
830 ts->deleteTrustSettings(cert);
831 ts->flushToDisk();
832 tsTrustSettingsChanged();
833 return errSecSuccess;
834
835 END_RCSAPI
836 }
837
838 /* get all certs listed in specified domain */
839 OSStatus SecTrustSettingsCopyCertificates(
840 SecTrustSettingsDomain domain,
841 CFArrayRef *certArray)
842 {
843 BEGIN_RCSAPI
844
845 TS_REQUIRED(certArray)
846
847 OSStatus status;
848 TrustSettings* ts;
849 CFMutableArrayRef trustedCertArray = NULL;
850 SecTrustRef trust = NULL;
851
852 status = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
853 if (status != errSecSuccess) {
854 return status;
855 }
856
857 auto_ptr<TrustSettings>_(ts);
858
859 CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
860
861 /*
862 * Keychains to search: user's search list, System.keychain, system root store
863 */
864 StorageManager::KeychainList keychains;
865 Keychain adminKc;
866 Keychain sysRootKc;
867 switch(domain) {
868 case kSecTrustSettingsDomainUser:
869 /* user search list */
870 globals().storageManager.getSearchList(keychains);
871 /* drop thru to next case */
872 case kSecTrustSettingsDomainAdmin:
873 /* admin certs in system keychain */
874 adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
875 keychains.push_back(adminKc);
876 /* drop thru to next case */
877 case kSecTrustSettingsDomainSystem:
878 /* and, for all cases, immutable system root store */
879 sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
880 keychains.push_back(sysRootKc);
881 default:
882 /* already validated when we created the TrustSettings */
883 break;
884 }
885 ts->findCerts(keychains, outArray);
886 CFIndex count = outArray ? CFArrayGetCount(outArray) : 0;
887 if(count == 0) {
888 CFReleaseSafe(outArray);
889 return errSecNoTrustSettings;
890 }
891 /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */
892 if (kSecTrustSettingsDomainSystem == domain) {
893 CFIndex i;
894 SecPolicyRef policy = SecPolicyCreateBasicX509();
895 trustedCertArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
896 for (i = 0; i < count ; i++) {
897 SecTrustResultType result;
898 SecCertificateRef certificate = (SecCertificateRef) CFArrayGetValueAtIndex(outArray, i);
899 status = SecTrustCreateWithCertificates(certificate, policy, &trust);
900 if (status != errSecSuccess) {
901 CFReleaseSafe(policy);
902 goto out;
903 }
904 status = SecTrustEvaluate(trust, &result);
905 if (status != errSecSuccess) {
906 CFReleaseSafe(policy);
907 goto out;
908 }
909 if (result != kSecTrustResultFatalTrustFailure) {
910 CFArrayAppendValue(trustedCertArray, certificate);
911 }
912 CFReleaseNull(trust);
913 }
914 tsAddConditionalCerts(trustedCertArray);
915 if (CFArrayGetCount(trustedCertArray) == 0) {
916 status = errSecNoTrustSettings;
917 } else {
918 *certArray = trustedCertArray;
919 CFReleaseSafe(outArray);
920 }
921 CFReleaseSafe(policy);
922 } else {
923 *certArray = outArray;
924 }
925 out:
926 if (status != errSecSuccess) {
927 CFReleaseSafe(outArray);
928 CFReleaseSafe(trustedCertArray);
929 }
930 CFReleaseNull(trust);
931 return status;
932 END_RCSAPI
933 }
934
935 static CFArrayRef gUserAdminCerts = NULL;
936 static bool gUserAdminCertsCacheBuilt = false;
937 static ModuleNexus<ReadWriteLock> gUserAdminCertsLock;
938
939 void SecTrustSettingsPurgeUserAdminCertsCache(void) {
940 StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Write);
941 CFReleaseNull(gUserAdminCerts);
942 gUserAdminCertsCacheBuilt = false;
943 }
944
945 OSStatus SecTrustSettingsCopyCertificatesForUserAdminDomains(
946 CFArrayRef *certArray)
947 {
948 TS_REQUIRED(certArray);
949 OSStatus result = errSecSuccess;
950
951 { /* Hold the read lock for the check */
952 StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Read);
953 if (gUserAdminCertsCacheBuilt) {
954 if (gUserAdminCerts) {
955 *certArray = (CFArrayRef)CFRetain(gUserAdminCerts);
956 return errSecSuccess;
957 } else {
958 return errSecNoTrustSettings;
959 }
960 }
961 }
962
963 /* There were no cached results. We'll have to recreate them. */
964 CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
965 if (!outArray) {
966 return errSecAllocate;
967 }
968
969 CFArrayRef userTrusted = NULL, adminTrusted = NULL;
970 OSStatus userStatus = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser, &userTrusted);
971 if ((userStatus == errSecSuccess) && (userTrusted != NULL)) {
972 CFArrayAppendArray(outArray, userTrusted, CFRangeMake(0, CFArrayGetCount(userTrusted)));
973 CFRelease(userTrusted);
974 }
975
976 OSStatus adminStatus = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin, &adminTrusted);
977 if ((adminStatus == errSecSuccess) && (adminTrusted != NULL)) {
978 CFArrayAppendArray(outArray, adminTrusted, CFRangeMake(0, CFArrayGetCount(adminTrusted)));
979 CFRelease(adminTrusted);
980 }
981
982 /* Lack of trust settings for a domain results in an error above. Only fail
983 * if we weren't able to get trust settings for both domains. */
984 if (userStatus != errSecSuccess && adminStatus != errSecSuccess) {
985 result = userStatus;
986 }
987
988 if (result != errSecSuccess && outArray) {
989 CFRelease(outArray);
990 outArray = NULL;
991 }
992
993 *certArray = outArray;
994
995 /* For valid results, update the global cache */
996 if (result == errSecSuccess || result == errSecNoTrustSettings) {
997 StReadWriteLock _(gUserAdminCertsLock(), StReadWriteLock::Write);
998 CFReleaseNull(gUserAdminCerts);
999 gUserAdminCerts = (CFArrayRef)CFRetainSafe(outArray);
1000 gUserAdminCertsCacheBuilt = true;
1001 }
1002
1003 return result;
1004 }
1005
1006 /*
1007 * Obtain an external, portable representation of the specified
1008 * domain's TrustSettings. Caller must CFRelease the returned data.
1009 */
1010 OSStatus SecTrustSettingsCreateExternalRepresentation(
1011 SecTrustSettingsDomain domain,
1012 CFDataRef *trustSettings)
1013 {
1014 BEGIN_RCSAPI
1015
1016 TS_REQUIRED(trustSettings)
1017
1018 OSStatus result;
1019 TrustSettings* ts;
1020
1021 result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
1022 if (result != errSecSuccess) {
1023 return result;
1024 }
1025
1026 auto_ptr<TrustSettings>_(ts);
1027
1028 *trustSettings = ts->createExternal();
1029 return errSecSuccess;
1030
1031 END_RCSAPI
1032 }
1033
1034 /*
1035 * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation,
1036 * into the specified domain.
1037 */
1038 OSStatus SecTrustSettingsImportExternalRepresentation(
1039 SecTrustSettingsDomain domain,
1040 CFDataRef trustSettings) /* optional - NULL means empty settings */
1041 {
1042 BEGIN_RCSAPI
1043
1044 if(domain == kSecTrustSettingsDomainSystem) {
1045 return errSecDataNotModifiable;
1046 }
1047
1048 OSStatus result;
1049 TrustSettings* ts;
1050
1051 result = TrustSettings::CreateTrustSettings(domain, trustSettings, ts);
1052 if (result != errSecSuccess) {
1053 return result;
1054 }
1055
1056 auto_ptr<TrustSettings>_(ts);
1057
1058 ts->flushToDisk();
1059 tsTrustSettingsChanged();
1060 return errSecSuccess;
1061
1062 END_RCSAPI
1063 }
1064
1065 /*
1066 * SecTrustSettingsSetTrustSettings convenience wrapper function.
1067 */
1068 void SecTrustSettingsSetTrustedCertificateForSSLHost(
1069 SecCertificateRef certificate,
1070 CFStringRef hostname,
1071 void (^result)(SecTrustSettingsResult trustResult, CFErrorRef error))
1072 {
1073 __block CFMutableArrayRef trustSettings = NULL;
1074 __block CFNumberRef trustSettingsResult = NULL;
1075 __block SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
1076
1077 CFDictionaryRef policyProperties = NULL;
1078 CFStringRef policyOid = NULL;
1079 SecPolicyRef policy = NULL;
1080
1081 Boolean isSelfSigned = false;
1082 Boolean hasPolicyConstraint = false;
1083 Boolean hasPolicyValue = false;
1084 Boolean policyConstraintChanged = false;
1085 CFIndex indexOfEntryWithAllowedErrorForExpiredCert = kCFNotFound;
1086 CFIndex indexOfEntryWithAllowedErrorForHostnameMismatch = kCFNotFound;
1087 CFIndex i, count;
1088 int32_t trustSettingsResultCode = kSecTrustSettingsResultTrustAsRoot;
1089 OSStatus status = errSecSuccess;
1090
1091 CFRetainSafe(certificate);
1092 CFRetainSafe(hostname);
1093 if (!certificate || !hostname) {
1094 status = errSecParam;
1095 } else {
1096 status = SecCertificateIsSelfSigned(certificate, &isSelfSigned);
1097 }
1098 if (status != errSecSuccess) {
1099 goto reportErr;
1100 }
1101 if (isSelfSigned) {
1102 trustSettingsResultCode = kSecTrustSettingsResultTrustRoot;
1103 }
1104 trustSettingsResult = CFNumberCreate(NULL, kCFNumberSInt32Type, &trustSettingsResultCode);
1105
1106 /* start with the existing trust settings for this certificate, if any */
1107 {
1108 CFArrayRef curTrustSettings = NULL;
1109 (void)SecTrustSettingsCopyTrustSettings(certificate, domain, &curTrustSettings);
1110 if (curTrustSettings) {
1111 trustSettings = CFArrayCreateMutableCopy(NULL, 0, curTrustSettings);
1112 CFReleaseNull(curTrustSettings);
1113 } else {
1114 trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1115 }
1116 }
1117 if (!trustSettings || !trustSettingsResult) {
1118 status = errSecAllocate;
1119 goto reportErr;
1120 }
1121
1122 /* set up policy and value instances to trust the certificate for SSL for a given hostname */
1123 policy = SecPolicyCreateSSL(true, hostname);
1124 if (!policy) {
1125 status = errSecInternal;
1126 goto reportErr;
1127 }
1128 policyProperties = SecPolicyCopyProperties(policy);
1129 if (!policyProperties) {
1130 status = errSecInternal;
1131 goto reportErr;
1132 }
1133 policyOid = (CFStringRef)CFDictionaryGetValue(policyProperties, kSecPolicyOid);
1134 CFRetainSafe(policyOid);
1135 if (!policyOid) {
1136 status = errSecInternal;
1137 goto reportErr;
1138 }
1139
1140 /* look for dictionaries in the trust settings array for this policy and value */
1141 count = CFArrayGetCount(trustSettings);
1142 for (i=0; i < count; i++) {
1143 CFDictionaryRef constraints = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, i);
1144 if (!constraints) { continue; }
1145 SecPolicyRef aPolicy = (SecPolicyRef)CFDictionaryGetValue(constraints, kSecTrustSettingsPolicy);
1146 if (!aPolicy) { continue; }
1147 CFDictionaryRef properties = SecPolicyCopyProperties(aPolicy);
1148 if (!properties) { continue; }
1149 CFStringRef aPolicyOid = (CFStringRef)CFDictionaryGetValue(properties, kSecPolicyOid);
1150 if (aPolicyOid && kCFCompareEqualTo == CFStringCompare(aPolicyOid, policyOid, 0)) {
1151 CFStringRef aPolicyString = (CFStringRef)CFDictionaryGetValue(constraints, kSecTrustSettingsPolicyString);
1152 if (aPolicyString && kCFCompareEqualTo == CFStringCompare(aPolicyString, hostname, kCFCompareCaseInsensitive)) {
1153 /* found existing entry */
1154 CFNumberRef allowedErr = (CFNumberRef)CFDictionaryGetValue(constraints, kSecTrustSettingsAllowedError);
1155 int32_t eOld = 0;
1156 if (!allowedErr || !CFNumberGetValue(allowedErr, kCFNumberSInt32Type, &eOld)) {
1157 eOld = CSSM_OK;
1158 }
1159 CFNumberRef tsResult = (CFNumberRef)CFDictionaryGetValue(constraints, kSecTrustSettingsResult);
1160 int32_t rOld = 0;
1161 if (!tsResult || !CFNumberGetValue(allowedErr, kCFNumberSInt32Type, &rOld)) {
1162 rOld = kSecTrustSettingsResultTrustRoot;
1163 }
1164 if (!hasPolicyValue) { hasPolicyValue = (aPolicyString != NULL); }
1165 if (!hasPolicyConstraint) { hasPolicyConstraint = true; }
1166 if (eOld == CSSMERR_TP_CERT_EXPIRED) {
1167 indexOfEntryWithAllowedErrorForExpiredCert = i;
1168 } else if (eOld == CSSMERR_APPLETP_HOSTNAME_MISMATCH) {
1169 indexOfEntryWithAllowedErrorForHostnameMismatch = i;
1170 }
1171 if (trustSettingsResultCode != rOld) {
1172 policyConstraintChanged = true; // we are changing existing policy constraint's result
1173 }
1174 }
1175 }
1176 CFReleaseSafe(properties);
1177 }
1178
1179 if (!hasPolicyConstraint) {
1180 policyConstraintChanged = true; // we are adding a new policy constraint
1181 } else if (hostname && !hasPolicyValue) {
1182 policyConstraintChanged = true; // we need to add the hostname to an existing policy constraint
1183 } else if ((indexOfEntryWithAllowedErrorForExpiredCert == kCFNotFound) ||
1184 (indexOfEntryWithAllowedErrorForHostnameMismatch == kCFNotFound)) {
1185 policyConstraintChanged = true; // we are missing one of the expected allowed-error entries for this policy
1186 }
1187
1188 if (policyConstraintChanged) {
1189 CFMutableDictionaryRef policyDict[2] = { NULL, NULL };
1190 policyDict[0] = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1191 policyDict[1] = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1192 int32_t certExpiredCode = (int32_t)CSSMERR_TP_CERT_EXPIRED;
1193 CFNumberRef certExpired = CFNumberCreate(NULL, kCFNumberSInt32Type, &certExpiredCode);
1194 int32_t hostnameMismatchCode = (int32_t)CSSMERR_APPLETP_HOSTNAME_MISMATCH;
1195 CFNumberRef hostnameMismatch = CFNumberCreate(NULL, kCFNumberSInt32Type, &hostnameMismatchCode);
1196 if (!policyDict[0] || !policyDict[1] || !certExpired || !hostnameMismatch) {
1197 status = errSecInternal;
1198 } else {
1199 /* set up entry for policy, hostname, expired cert error, and result */
1200 CFDictionarySetValue(policyDict[0], kSecTrustSettingsPolicy, policy);
1201 CFDictionarySetValue(policyDict[0], kSecTrustSettingsPolicyString, hostname);
1202 CFDictionarySetValue(policyDict[0], kSecTrustSettingsAllowedError, certExpired);
1203 CFDictionarySetValue(policyDict[0], kSecTrustSettingsResult, trustSettingsResult);
1204 if (indexOfEntryWithAllowedErrorForExpiredCert != kCFNotFound) {
1205 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1206 CFArraySetValueAtIndex(trustSettings, indexOfEntryWithAllowedErrorForExpiredCert, policyDict[0]);
1207 } else if (!(hasPolicyValue)) {
1208 /* add a new policy constraint */
1209 CFArrayAppendValue(trustSettings, policyDict[0]);
1210 }
1211 /* set up additional entry for policy, hostname, hostname mismatch error, and result */
1212 CFDictionarySetValue(policyDict[1], kSecTrustSettingsPolicy, policy);
1213 CFDictionarySetValue(policyDict[1], kSecTrustSettingsPolicyString, hostname);
1214 CFDictionarySetValue(policyDict[1], kSecTrustSettingsAllowedError, hostnameMismatch);
1215 CFDictionarySetValue(policyDict[1], kSecTrustSettingsResult, trustSettingsResult);
1216 if (indexOfEntryWithAllowedErrorForHostnameMismatch != kCFNotFound) {
1217 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1218 CFArraySetValueAtIndex(trustSettings, indexOfEntryWithAllowedErrorForHostnameMismatch, policyDict[1]);
1219 } else if (!(hasPolicyValue)) {
1220 /* add a new policy constraint */
1221 CFArrayAppendValue(trustSettings, policyDict[1]);
1222 }
1223 }
1224 CFReleaseSafe(policyDict[0]);
1225 CFReleaseSafe(policyDict[1]);
1226 CFReleaseSafe(certExpired);
1227 CFReleaseSafe(hostnameMismatch);
1228 }
1229
1230 if (status != errSecSuccess) {
1231 goto reportErr;
1232 }
1233 CFReleaseSafe(policyOid);
1234 CFReleaseSafe(policyProperties);
1235 CFReleaseSafe(policy);
1236
1237 dispatch_async(dispatch_get_main_queue(), ^{
1238 /* add certificate to keychain first */
1239 OSStatus status = SecCertificateAddToKeychain(certificate, NULL);
1240 if (status == errSecSuccess || status == errSecDuplicateItem) {
1241 /* this will block on authorization UI... */
1242 status = SecTrustSettingsSetTrustSettings(certificate,
1243 domain, trustSettings);
1244 }
1245 if (result) {
1246 CFErrorRef error = NULL;
1247 if (status) {
1248 error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
1249 }
1250 int32_t tsrc;
1251 if (!CFNumberGetValue(trustSettingsResult, kCFNumberSInt32Type, (int32_t*)&tsrc)) {
1252 tsrc = (int32_t)kSecTrustSettingsResultUnspecified;
1253 }
1254 result((SecTrustSettingsResult)tsrc, error);
1255 CFReleaseSafe(error);
1256 }
1257 CFRelease(trustSettingsResult);
1258 CFRelease(trustSettings);
1259 CFRelease(certificate);
1260 CFRelease(hostname);
1261 });
1262
1263 return;
1264
1265 reportErr:
1266 CFReleaseSafe(policyOid);
1267 CFReleaseSafe(policyProperties);
1268 CFReleaseSafe(policy);
1269 CFReleaseSafe(trustSettingsResult);
1270 CFReleaseSafe(trustSettings);
1271 CFReleaseSafe(certificate);
1272 CFReleaseSafe(hostname);
1273 if (result) {
1274 CFErrorRef error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
1275 result(kSecTrustSettingsResultInvalid, error);
1276 CFReleaseSafe(error);
1277 }
1278 }