2  * Copyright (c) 2005,2011-2016 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25  * SecTrustSettings.cpp - Public interface for manipulation of Trust Settings. 
  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" 
  40 #include "SecKeychainPriv.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> 
  56 #include <CommonCrypto/CommonDigest.h> 
  57 #include <CoreFoundation/CFPreferences.h> 
  58 #include <utilities/SecCFRelease.h> 
  60 #define trustSettingsDbg(args...)       secinfo("trustSettings", ## args) 
  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). 
  70 #define BEGIN_RCSAPI    \ 
  71         OSStatus __secapiresult; \ 
  74                 __secapiresult=errSecSuccess; \ 
  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; 
  87 #pragma mark --- TrustSettings preferences --- 
  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. 
  95 static bool tsUserTrustDisableValid 
= false;    /* true once we consult prefs */ 
  96 static bool tsUserTrustDisable 
= false;                 /* the cached value */ 
  99  * Determine whether user-level Trust Settings disabled. 
 101 static bool tsUserTrustSettingsDisabled() 
 103         if(tsUserTrustDisableValid
) { 
 104                 return tsUserTrustDisable
; 
 106         tsUserTrustDisable 
= false; 
 108         Dictionary
* dictionary 
= Dictionary::CreateDictionary(kSecTrustSettingsPrefsDomain
, Dictionary::US_System
); 
 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
); 
 116         tsUserTrustDisableValid 
= true; 
 117         return tsUserTrustDisable
; 
 120 #pragma mark --- TrustSettings global cache --- 
 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. 
 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). 
 135 static ModuleNexus
<RecursiveMutex
> sutCacheLock
; 
 137 #define TRUST_SETTINGS_NUM_DOMAINS              3 
 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. 
 144 static TrustSettings 
*globalTrustSettings
[TRUST_SETTINGS_NUM_DOMAINS
] = 
 148  * Indicates "the associated global here is currently valid; if there isn't a 
 149  * globalTrustSettings[domain], don't try to find one" 
 151 static bool globalTrustSettingsValid
[TRUST_SETTINGS_NUM_DOMAINS
] = 
 152                 {false, false, false}; 
 154 /* remember the fact that we've registered our KC callback */ 
 155 static bool sutRegisteredCallback 
= false; 
 157 static void tsRegisterCallback(); 
 160  * Assign global TrustSetting to new incoming value, which may be NULL. 
 161  * Caller holds sutCacheLock. 
 163 static void tsSetGlobalTrustSettings( 
 165         SecTrustSettingsDomain domain
) 
 167         assert(((int)domain 
>= 0) && ((int)domain 
< TRUST_SETTINGS_NUM_DOMAINS
)); 
 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(); 
 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. 
 183 static TrustSettings 
*tsGetGlobalTrustSettings( 
 184         SecTrustSettingsDomain domain
) 
 186         assert(((int)domain 
>= 0) && ((int)domain 
< TRUST_SETTINGS_NUM_DOMAINS
)); 
 188         if((domain 
== kSecTrustSettingsDomainUser
) && tsUserTrustSettingsDisabled()) { 
 189                 trustSettingsDbg("tsGetGlobalTrustSettings: skipping DISABLED user domain"); 
 193         if(globalTrustSettingsValid
[domain
]) { 
 194                 // ready or not, use this 
 195                 return globalTrustSettings
[domain
]; 
 197         assert(globalTrustSettings
[domain
] == NULL
); 
 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
)) { 
 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. 
 211         trustSettingsDbg("tsGetGlobalTrustSettings: could not connect to ocspd for domain (%d)",(int)domain
); 
 212         globalTrustSettingsValid
[domain
] = false; 
 213         tsRegisterCallback(); 
 216     else if (result 
== errSecNoTrustSettings
) { 
 218                  * No TrustSettings for this domain, actually a fairly common case. 
 219                  * Optimize: don't bother trying this again. 
 221                 trustSettingsDbg("tsGetGlobalTrustSettings: flagging known NULL"); 
 222                 globalTrustSettingsValid
[domain
] = true; 
 223                 tsRegisterCallback(); 
 226     else if(result 
!= errSecSuccess
) { 
 228         MacOSError::throwMe(result
); 
 231         tsSetGlobalTrustSettings(ts
, domain
); 
 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. 
 241 static void tsPurgeCache() 
 245         StLock
<Mutex
>   _(sutCacheLock()); 
 246         trustSettingsDbg("tsPurgeCache"); 
 247         for(domain
=0; domain
<TRUST_SETTINGS_NUM_DOMAINS
; domain
++) { 
 248                 tsSetGlobalTrustSettings(NULL
, (SecTrustSettingsDomain
) domain
); 
 253  * Keychain event callback function, for notification by other processes that 
 254  * user trust list(s) has/have changed. 
 256 static OSStatus 
tsTrustSettingsCallback ( 
 257    SecKeychainEvent keychainEvent
, 
 258    SecKeychainCallbackInfo 
*info
, 
 261         trustSettingsDbg("tsTrustSettingsCallback, event %d", (int)keychainEvent
); 
 262         if(keychainEvent 
!= kSecTrustSettingsChangedEvent
) { 
 263                 /* should not happen, right? */ 
 264                 return errSecSuccess
; 
 266         if(info
->pid 
== getpid()) { 
 268                  * Avoid dup cache invalidates: we already dealt with this event. 
 270                 trustSettingsDbg("cacheEventCallback: our pid, skipping"); 
 275         return errSecSuccess
; 
 279  * Ensure that we've registered for kSecTrustSettingsChangedEvent callbacks 
 281 static void tsRegisterCallback() 
 283         if(sutRegisteredCallback
) { 
 286         trustSettingsDbg("tsRegisterCallback: registering callback"); 
 287         OSStatus ortn 
= SecKeychainAddCallback(tsTrustSettingsCallback
, 
 288                 kSecTrustSettingsChangedEventMask
, NULL
); 
 290                 trustSettingsDbg("tsRegisterCallback: SecKeychainAddCallback returned %d", (int)ortn
); 
 291                 /* Not sure how this could ever happen - maybe if there is no run loop active? */ 
 293         sutRegisteredCallback 
= true; 
 296 #pragma mark --- Static functions --- 
 300  * Called by API code when a trust list has changed; we notify other processes 
 301  * and purge our own cache. 
 303 static void tsTrustSettingsChanged() 
 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
)))); 
 315         trustSettingsDbg("tsTrustSettingsChanged: posting notification"); 
 316         SecurityServer::ClientSession 
cs (Allocator::standard(), Allocator::standard()); 
 317         cs
.postNotification (SecurityServer::kNotificationDomainDatabase
, 
 318                 kSecTrustSettingsChangedEvent
, data
); 
 323  * Common code for SecTrustSettingsCopyTrustSettings(), 
 324  * SecTrustSettingsCopyModificationDate(). 
 326 static OSStatus 
tsCopyTrustSettings( 
 327         SecCertificateRef cert
, 
 328         SecTrustSettingsDomain domain
, 
 329         CFArrayRef 
*trustSettings
,              /* optionally RETURNED */ 
 330         CFDateRef 
*modDate
)                             /* optionally RETURNED */ 
 336         /* obtain fresh full copy from disk */ 
 340         result 
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
); 
 342         // rather than throw these results, just return them because we are at the top level 
 343         if (result 
== errSecNoTrustSettings
) { 
 344                 return errSecItemNotFound
; 
 346         else if (result 
!= errSecSuccess
) { 
 350         auto_ptr
<TrustSettings
>_(ts
); // make sure this gets deleted just in case something throws underneath 
 353                 *trustSettings 
= ts
->copyTrustSettings(cert
); 
 356                 *modDate 
= ts
->copyModDate(cert
); 
 362 static void tsAddConditionalCerts(CFMutableArrayRef certArray
); 
 365  * Common code for SecTrustSettingsCopyQualifiedCerts() and 
 366  * SecTrustSettingsCopyUnrestrictedRoots(). 
 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 */ 
 375         /* per-domain enables */ 
 379         CFArrayRef                              
*certArray
)             /* RETURNED */ 
 381         StLock
<Mutex
> _TC(sutCacheLock()); 
 382         StLock
<Mutex
> _TK(SecTrustKeychainsGetMutex()); 
 384         TS_REQUIRED(certArray
) 
 386         /* this relies on the domain enums being numbered 0..2, user..system */ 
 387         bool domainEnable
[3] = {user
, admin
, system
}; 
 389         /* we'll retain it again before successful exit */ 
 390         CFRef
<CFMutableArrayRef
> outArray(CFArrayCreateMutable(NULL
, 0, 
 391                 &kCFTypeArrayCallBacks
)); 
 394          * Search all keychains - user's keychain list, System.keychain, 
 395          * and system root store 
 397         StorageManager::KeychainList keychains
; 
 400                 globals().storageManager
.getSearchList(keychains
); 
 403                 adminKc 
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false); 
 404                 keychains
.push_back(adminKc
); 
 406         Keychain sysRootKc 
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false); 
 407         keychains
.push_back(sysRootKc
); 
 409         assert(kSecTrustSettingsDomainUser 
== 0); 
 410         for(unsigned domain
=0; domain
<TRUST_SETTINGS_NUM_DOMAINS
; domain
++) { 
 411                 if(!domainEnable
[domain
]) { 
 414                 TrustSettings 
*ts 
= tsGetGlobalTrustSettings((SecTrustSettingsDomain
)domain
); 
 418                 ts
->findQualifiedCerts(keychains
, 
 419                         false,          /* !findAll */ 
 421                         policyOID
, policyString
, keyUsage
, 
 425                 tsAddConditionalCerts(outArray
); 
 427         *certArray 
= outArray
; 
 428         CFRetainSafe(*certArray
); 
 429         trustSettingsDbg("tsCopyCertsCommon: %ld certs found", 
 430                 CFArrayGetCount(outArray
)); 
 431         return errSecSuccess
; 
 434 static void tsAddConditionalCerts(CFMutableArrayRef certArray
) 
 437         struct certmap_entry_s 
{ 
 438                 CFStringRef bundleId
; 
 440                 const CFIndex length
; 
 442         typedef struct certmap_entry_s certmap_entry_t
; 
 444         CFBundleRef bundle 
= CFBundleGetMainBundle(); 
 445         CFStringRef bundleIdentifier 
= (bundle
) ? CFBundleGetIdentifier(bundle
) : NULL
; 
 446         if (!bundleIdentifier 
|| !certArray
) { return; } 
 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 
 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
); 
 465         // this function is a no-op on iOS platforms 
 470 #pragma mark --- SPI functions --- 
 474  * Fundamental routine used by TP to ascertain status of one cert. 
 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. 
 484  * Returns the domain in which a setting was found in *foundDomain. 
 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. 
 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. 
 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. 
 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
, 
 521         StLock
<Mutex
>   _(sutCacheLock()); 
 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
) 
 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'; 
 541         /* initial condition - this can grow if we inspect multiple TrustSettings */ 
 542         *allowedErrors 
= NULL
; 
 543         *numAllowedErrors 
= 0; 
 546          * This loop relies on the ordering of the SecTrustSettingsDomain enum: 
 547          * search user first, then admin, then system. 
 549         assert(kSecTrustSettingsDomainAdmin 
== (kSecTrustSettingsDomainUser 
+ 1)); 
 550         assert(kSecTrustSettingsDomainSystem 
== (kSecTrustSettingsDomainAdmin 
+ 1)); 
 551         bool foundAny 
= false; 
 552         for(unsigned domain
=kSecTrustSettingsDomainUser
; 
 553                              domain
<=kSecTrustSettingsDomainSystem
; 
 555                 TrustSettings 
*ts 
= tsGetGlobalTrustSettings((SecTrustSettingsDomain
)domain
); 
 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
); 
 568                          * Note this, even though we may overwrite it later if this 
 569                          * is an Unspecified entry and we find a definitive entry 
 572                         *foundDomain 
= (SecTrustSettingsDomain
)domain
; 
 574                 if(found 
&& (*resultType 
!= kSecTrustSettingsResultUnspecified
)) { 
 575                         trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
); 
 576                         *foundAnyEntry 
= true; 
 577                         *foundMatchingEntry 
= true; 
 578                         return errSecSuccess
; 
 580                 foundAny 
|= foundAnyHere
; 
 582         trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND"); 
 583         *foundAnyEntry 
= foundAny
; 
 584         *foundMatchingEntry 
= false; 
 585         return errSecSuccess
; 
 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. 
 598 OSStatus 
SecTrustSettingsCopyQualifiedCerts( 
 599         const CSSM_OID                          
*policyOID
, 
 600         const char                                      *policyString
,          /* optional */ 
 601         uint32                                          policyStringLen
, 
 602         SecTrustSettingsKeyUsage        keyUsage
,                       /* optional */ 
 603         CFArrayRef                                      
*certArray
)                     /* RETURNED */ 
 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'; 
 617         return tsCopyCertsCommon(policyOID
, polStr
.get(), keyUsage
, 
 618                 false,                          /* !onlyRoots */ 
 619                 true, true, true,       /* all domains */ 
 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. 
 630 OSStatus 
SecTrustSettingsCopyUnrestrictedRoots( 
 634         CFArrayRef                              
*certArray
)             /* RETURNED */ 
 638         OSStatus status 
= tsCopyCertsCommon(NULL
, NULL
, NULL
,   /* no constraints */ 
 639                 true,                           /* onlyRoots */ 
 648 static const char hexChars
[16] = { 
 649         '0', '1', '2', '3', '4', '5', '6', '7', 
 650         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 
 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. 
 657 CFStringRef 
SecTrustSettingsCertHashStrFromCert( 
 658         SecCertificateRef certRef
) 
 660         if(certRef 
== NULL
) { 
 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
; 
 672         OSStatus ortn 
= SecCertificateGetData(certRef
, &certData
); 
 676         return SecTrustSettingsCertHashStrFromData(certData
.Data
, certData
.Length
); 
 679 CFStringRef 
SecTrustSettingsCertHashStrFromData( 
 683         unsigned char digest
[CC_SHA1_DIGEST_LENGTH
]; 
 684         char asciiDigest
[(2 * CC_SHA1_DIGEST_LENGTH
) + 1]; 
 686         char *outp 
= asciiDigest
; 
 687         unsigned char *inp 
= digest
; 
 693         CC_SHA1(cert
, (CC_LONG
)certLen
, digest
); 
 695         for(dex
=0; dex
<CC_SHA1_DIGEST_LENGTH
; dex
++) { 
 697                 outp
[1] = hexChars
[c 
& 0xf]; 
 699                 outp
[0] = hexChars
[c
]; 
 703         return CFStringCreateWithCString(NULL
, asciiDigest
, kCFStringEncodingASCII
); 
 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 
 711 OSStatus 
SecTrustSettingsSetTrustSettingsExternal( 
 712         CFDataRef                       settingsIn
,                                     /* optional */ 
 713         SecCertificateRef       certRef
,                                        /* optional */ 
 714         CFTypeRef                       trustSettingsDictOrArray
,       /* optional */ 
 715         CFDataRef                       
*settingsOut
)                           /* RETURNED */ 
 719         TS_REQUIRED(settingsOut
) 
 724         result 
= TrustSettings::CreateTrustSettings((SecTrustSettingsDomain
)kSecTrustSettingsDomainMemory
, settingsIn
, ts
); 
 725         if (result 
!= errSecSuccess
) { 
 729         auto_ptr
<TrustSettings
>_(ts
); 
 731         if(certRef 
!= NULL
) { 
 732                 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
); 
 734         *settingsOut 
= ts
->createExternal(); 
 735         return errSecSuccess
; 
 740 #pragma mark --- API functions --- 
 742 OSStatus 
SecTrustSettingsCopyTrustSettings( 
 743         SecCertificateRef certRef
, 
 744         SecTrustSettingsDomain domain
, 
 745         CFArrayRef 
*trustSettings
)                              /* RETURNED */ 
 748         TS_REQUIRED(trustSettings
) 
 750         OSStatus result 
= tsCopyTrustSettings(certRef
, domain
, trustSettings
, NULL
); 
 751         if (result 
== errSecSuccess 
&& *trustSettings 
== NULL
) { 
 752                 result 
= errSecItemNotFound
; /* documented result if no trust settings exist */ 
 757 OSStatus 
SecTrustSettingsCopyModificationDate( 
 758         SecCertificateRef               certRef
, 
 759         SecTrustSettingsDomain  domain
, 
 760         CFDateRef                               
*modificationDate
)      /* RETURNED */ 
 763         TS_REQUIRED(modificationDate
) 
 765         OSStatus result 
= tsCopyTrustSettings(certRef
, domain
, NULL
, modificationDate
); 
 766         if (result 
== errSecSuccess 
&& *modificationDate 
== NULL
) { 
 767                 result 
= errSecItemNotFound
; /* documented result if no trust settings exist */ 
 772 /* works with existing and with new cert */ 
 773 OSStatus 
SecTrustSettingsSetTrustSettings( 
 774         SecCertificateRef certRef
, 
 775         SecTrustSettingsDomain domain
, 
 776         CFTypeRef trustSettingsDictOrArray
) 
 782         if(domain 
== kSecTrustSettingsDomainSystem
) { 
 783                 return errSecDataNotModifiable
; 
 789         result 
= TrustSettings::CreateTrustSettings(domain
, CREATE_YES
, TRIM_NO
, ts
); 
 790         if (result 
!= errSecSuccess
) { 
 794         auto_ptr
<TrustSettings
>_(ts
); 
 796         ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
); 
 798         tsTrustSettingsChanged(); 
 799         return errSecSuccess
; 
 804 OSStatus 
SecTrustSettingsRemoveTrustSettings( 
 805         SecCertificateRef cert
, 
 806         SecTrustSettingsDomain domain
) 
 812         if(domain 
== kSecTrustSettingsDomainSystem
) { 
 813                 return errSecDataNotModifiable
; 
 819         result 
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
); 
 820         if (result 
!= errSecSuccess
) { 
 824         auto_ptr
<TrustSettings
>_(ts
); 
 826         /* deleteTrustSettings throws if record not found */ 
 827         trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d", 
 829         ts
->deleteTrustSettings(cert
); 
 831         tsTrustSettingsChanged(); 
 832         return errSecSuccess
; 
 837 /* get all certs listed in specified domain */ 
 838 OSStatus 
SecTrustSettingsCopyCertificates( 
 839         SecTrustSettingsDomain  domain
, 
 840         CFArrayRef                              
*certArray
) 
 844         TS_REQUIRED(certArray
) 
 848         CFMutableArrayRef trustedCertArray 
= NULL
; 
 849     SecTrustRef trust 
= NULL
; 
 851         status 
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
); 
 852         if (status 
!= errSecSuccess
) { 
 856         auto_ptr
<TrustSettings
>_(ts
); 
 858         CFMutableArrayRef outArray 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 861          * Keychains to search: user's search list, System.keychain, system root store 
 863         StorageManager::KeychainList keychains
; 
 867                 case kSecTrustSettingsDomainUser
: 
 868                         /* user search list */ 
 869                         globals().storageManager
.getSearchList(keychains
); 
 870                         /* drop thru to next case */ 
 871                 case kSecTrustSettingsDomainAdmin
: 
 872                         /* admin certs in system keychain */ 
 873                         adminKc 
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false); 
 874                         keychains
.push_back(adminKc
); 
 875                         /* drop thru to next case */ 
 876                 case kSecTrustSettingsDomainSystem
: 
 877                         /* and, for all cases, immutable system root store */ 
 878                         sysRootKc 
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false); 
 879                         keychains
.push_back(sysRootKc
); 
 881                         /* already validated when we created the TrustSettings */ 
 884         ts
->findCerts(keychains
, outArray
); 
 885     CFIndex count 
= outArray 
? CFArrayGetCount(outArray
) : 0; 
 887                 CFReleaseSafe(outArray
); 
 888                 return errSecNoTrustSettings
; 
 890  /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */ 
 891         if (kSecTrustSettingsDomainSystem 
== domain
) { 
 893         SecPolicyRef policy 
= SecPolicyCreateBasicX509(); 
 894             trustedCertArray 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 895         for (i 
= 0; i 
< count 
; i
++) { 
 896             SecTrustResultType result
; 
 897             SecCertificateRef certificate 
= (SecCertificateRef
) CFArrayGetValueAtIndex(outArray
, i
); 
 898             status 
= SecTrustCreateWithCertificates(certificate
, policy
, &trust
); 
 899                 if (status 
!= errSecSuccess
) { 
 900                CFReleaseSafe(policy
); 
 903             status 
= SecTrustEvaluate(trust
, &result
); 
 904             if (status 
!= errSecSuccess
) { 
 905                CFReleaseSafe(policy
); 
 908             if (result 
!= kSecTrustResultFatalTrustFailure
) { 
 909                 CFArrayAppendValue(trustedCertArray
, certificate
); 
 911             CFReleaseNull(trust
); 
 913                 tsAddConditionalCerts(trustedCertArray
); 
 914         if (CFArrayGetCount(trustedCertArray
) == 0) { 
 915                         status 
= errSecNoTrustSettings
; 
 917                         *certArray 
= trustedCertArray
; 
 918                         CFReleaseSafe(outArray
); 
 920                 CFReleaseSafe(policy
); 
 922                 *certArray 
= outArray
; 
 925     if (status 
!= errSecSuccess
) { 
 926         CFReleaseSafe(outArray
); 
 927                 CFReleaseSafe(trustedCertArray
); 
 929     CFReleaseNull(trust
); 
 934 static CFArrayRef gUserAdminCerts 
= NULL
; 
 935 static bool gUserAdminCertsCacheBuilt 
= false; 
 936 static ModuleNexus
<ReadWriteLock
> gUserAdminCertsLock
; 
 938 void SecTrustSettingsPurgeUserAdminCertsCache(void) { 
 939     StReadWriteLock 
_(gUserAdminCertsLock(), StReadWriteLock::Write
); 
 940     CFReleaseNull(gUserAdminCerts
); 
 941     gUserAdminCertsCacheBuilt 
= false; 
 944 OSStatus 
SecTrustSettingsCopyCertificatesForUserAdminDomains( 
 945                                                              CFArrayRef  
*certArray
) 
 947     TS_REQUIRED(certArray
); 
 948     OSStatus result 
= errSecSuccess
; 
 950     { /* Hold the read lock for the check */ 
 951         StReadWriteLock 
_(gUserAdminCertsLock(), StReadWriteLock::Read
); 
 952         if (gUserAdminCertsCacheBuilt
) { 
 953             if (gUserAdminCerts
) { 
 954                 *certArray 
= (CFArrayRef
)CFRetain(gUserAdminCerts
); 
 955                 return errSecSuccess
; 
 957                 return errSecNoTrustSettings
; 
 962     /* There were no cached results. We'll have to recreate them. */ 
 963     CFMutableArrayRef outArray 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 965         return errSecAllocate
; 
 968     CFArrayRef userTrusted 
= NULL
, adminTrusted 
= NULL
; 
 969     OSStatus userStatus 
= SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser
, &userTrusted
); 
 970     if ((userStatus 
== errSecSuccess
) && (userTrusted 
!= NULL
)) { 
 971         CFArrayAppendArray(outArray
, userTrusted
, CFRangeMake(0, CFArrayGetCount(userTrusted
))); 
 972         CFRelease(userTrusted
); 
 975     OSStatus adminStatus 
= SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin
, &adminTrusted
); 
 976     if ((adminStatus 
== errSecSuccess
) && (adminTrusted 
!= NULL
)) { 
 977         CFArrayAppendArray(outArray
, adminTrusted
, CFRangeMake(0, CFArrayGetCount(adminTrusted
))); 
 978         CFRelease(adminTrusted
); 
 981     /* Lack of trust settings for a domain results in an error above. Only fail 
 982      * if we weren't able to get trust settings for both domains. */ 
 983     if (userStatus 
!= errSecSuccess 
&& adminStatus 
!= errSecSuccess
) { 
 987     if (result 
!= errSecSuccess 
&& outArray
) { 
 992     *certArray 
= outArray
; 
 994     /* For valid results, update the global cache */ 
 995     if (result 
== errSecSuccess 
|| result 
== errSecNoTrustSettings
) { 
 996         StReadWriteLock 
_(gUserAdminCertsLock(), StReadWriteLock::Write
); 
 997         CFReleaseNull(gUserAdminCerts
); 
 998         gUserAdminCerts 
= (CFArrayRef
)CFRetainSafe(outArray
); 
 999         gUserAdminCertsCacheBuilt 
= true; 
1006  * Obtain an external, portable representation of the specified 
1007  * domain's TrustSettings. Caller must CFRelease the returned data. 
1009 OSStatus 
SecTrustSettingsCreateExternalRepresentation( 
1010         SecTrustSettingsDomain  domain
, 
1011         CFDataRef                               
*trustSettings
) 
1015         TS_REQUIRED(trustSettings
) 
1020         result 
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
); 
1021         if (result 
!= errSecSuccess
) { 
1025         auto_ptr
<TrustSettings
>_(ts
); 
1027         *trustSettings 
= ts
->createExternal(); 
1028         return errSecSuccess
; 
1034  * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation, 
1035  * into the specified domain. 
1037 OSStatus 
SecTrustSettingsImportExternalRepresentation( 
1038         SecTrustSettingsDomain  domain
, 
1039         CFDataRef                               trustSettings
)          /* optional - NULL means empty settings */ 
1043         if(domain 
== kSecTrustSettingsDomainSystem
) { 
1044                 return errSecDataNotModifiable
; 
1050         result 
= TrustSettings::CreateTrustSettings(domain
, trustSettings
, ts
); 
1051         if (result 
!= errSecSuccess
) { 
1055         auto_ptr
<TrustSettings
>_(ts
); 
1058         tsTrustSettingsChanged(); 
1059         return errSecSuccess
; 
1065  * SecTrustSettingsSetTrustSettings convenience wrapper function. 
1067 void SecTrustSettingsSetTrustedCertificateForSSLHost( 
1068     SecCertificateRef certificate
, 
1069     CFStringRef hostname
, 
1070     void (^result
)(SecTrustSettingsResult trustResult
, CFErrorRef error
)) 
1072         __block CFMutableArrayRef trustSettings 
= NULL
; 
1073         __block CFNumberRef trustSettingsResult 
= NULL
; 
1074         __block SecTrustSettingsDomain domain 
= kSecTrustSettingsDomainUser
; 
1076         CFDictionaryRef policyProperties 
= NULL
; 
1077         CFStringRef policyOid 
= NULL
; 
1078         SecPolicyRef policy 
= NULL
; 
1080         Boolean isSelfSigned 
= false; 
1081         Boolean hasPolicyConstraint 
= false; 
1082         Boolean hasPolicyValue 
= false; 
1083         Boolean policyConstraintChanged 
= false; 
1084         CFIndex indexOfEntryWithAllowedErrorForExpiredCert 
= kCFNotFound
; 
1085         CFIndex indexOfEntryWithAllowedErrorForHostnameMismatch 
= kCFNotFound
; 
1087         int32_t trustSettingsResultCode 
= kSecTrustSettingsResultTrustAsRoot
; 
1088         OSStatus status 
= errSecSuccess
; 
1090         CFRetainSafe(certificate
); 
1091         CFRetainSafe(hostname
); 
1092         if (!certificate 
|| !hostname
) { 
1093                 status 
= errSecParam
; 
1095                 status 
= SecCertificateIsSelfSigned(certificate
, &isSelfSigned
); 
1097         if (status 
!= errSecSuccess
) { 
1101                 trustSettingsResultCode 
= kSecTrustSettingsResultTrustRoot
; 
1103         trustSettingsResult 
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trustSettingsResultCode
); 
1105         /* start with the existing trust settings for this certificate, if any */ 
1107                 CFArrayRef curTrustSettings 
= NULL
; 
1108                 (void)SecTrustSettingsCopyTrustSettings(certificate
, domain
, &curTrustSettings
); 
1109                 if (curTrustSettings
) { 
1110                         trustSettings 
= CFArrayCreateMutableCopy(NULL
, 0, curTrustSettings
); 
1111                         CFReleaseNull(curTrustSettings
); 
1113                         trustSettings 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
1116         if (!trustSettings 
|| !trustSettingsResult
) { 
1117                 status 
= errSecAllocate
; 
1121         /* set up policy and value instances to trust the certificate for SSL for a given hostname */ 
1122         policy 
= SecPolicyCreateSSL(true, hostname
); 
1124                 status 
= errSecInternal
; 
1127         policyProperties 
= SecPolicyCopyProperties(policy
); 
1128         if (!policyProperties
) { 
1129                 status 
= errSecInternal
; 
1132         policyOid 
= (CFStringRef
)CFDictionaryGetValue(policyProperties
, kSecPolicyOid
); 
1133         CFRetainSafe(policyOid
); 
1135                 status 
= errSecInternal
; 
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
); 
1155                                 if (!allowedErr 
|| !CFNumberGetValue(allowedErr
, kCFNumberSInt32Type
, &eOld
)) { 
1158                                 CFNumberRef tsResult 
= (CFNumberRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsResult
); 
1160                                 if (!tsResult 
|| !CFNumberGetValue(allowedErr
, kCFNumberSInt32Type
, &rOld
)) { 
1161                                         rOld 
= kSecTrustSettingsResultTrustRoot
; 
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
; 
1170                                 if (trustSettingsResultCode 
!= rOld
) { 
1171                                         policyConstraintChanged 
= true;  // we are changing existing policy constraint's result 
1175                 CFReleaseSafe(properties
); 
1178         if (!hasPolicyConstraint
) { 
1179                 policyConstraintChanged 
= true; // we are adding a new policy constraint 
1180         } else if (hostname 
&& !hasPolicyValue
) { 
1181                 policyConstraintChanged 
= true; // we need to add the hostname to an existing policy constraint 
1182         } else if ((indexOfEntryWithAllowedErrorForExpiredCert 
== kCFNotFound
) || 
1183                            (indexOfEntryWithAllowedErrorForHostnameMismatch 
== kCFNotFound
)) { 
1184                 policyConstraintChanged 
= true; // we are missing one of the expected allowed-error entries for this policy 
1187         if (policyConstraintChanged
) { 
1188                 CFMutableDictionaryRef policyDict
[2] = { NULL
, NULL 
}; 
1189                 policyDict
[0] = CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1190                 policyDict
[1] = CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1191                 int32_t certExpiredCode 
= (int32_t)CSSMERR_TP_CERT_EXPIRED
; 
1192                 CFNumberRef certExpired 
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &certExpiredCode
); 
1193                 int32_t hostnameMismatchCode 
= (int32_t)CSSMERR_APPLETP_HOSTNAME_MISMATCH
; 
1194                 CFNumberRef hostnameMismatch 
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &hostnameMismatchCode
); 
1195                 if (!policyDict
[0] || !policyDict
[1] || !certExpired 
|| !hostnameMismatch
) { 
1196                         status 
= errSecInternal
; 
1198                         /* set up entry for policy, hostname, expired cert error, and result */ 
1199                         CFDictionarySetValue(policyDict
[0], kSecTrustSettingsPolicy
, policy
); 
1200                         CFDictionarySetValue(policyDict
[0], kSecTrustSettingsPolicyString
, hostname
); 
1201                         CFDictionarySetValue(policyDict
[0], kSecTrustSettingsAllowedError
, certExpired
); 
1202                         CFDictionarySetValue(policyDict
[0], kSecTrustSettingsResult
, trustSettingsResult
); 
1203                         if (indexOfEntryWithAllowedErrorForExpiredCert 
!= kCFNotFound
) { 
1204                                 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */ 
1205                                 CFArraySetValueAtIndex(trustSettings
, indexOfEntryWithAllowedErrorForExpiredCert
, policyDict
[0]); 
1206                         } else if (!(hasPolicyValue
)) { 
1207                                 /* add a new policy constraint */ 
1208                                 CFArrayAppendValue(trustSettings
, policyDict
[0]); 
1210                         /* set up additional entry for policy, hostname, hostname mismatch error, and result */ 
1211                         CFDictionarySetValue(policyDict
[1], kSecTrustSettingsPolicy
, policy
); 
1212                         CFDictionarySetValue(policyDict
[1], kSecTrustSettingsPolicyString
, hostname
); 
1213                         CFDictionarySetValue(policyDict
[1], kSecTrustSettingsAllowedError
, hostnameMismatch
); 
1214                         CFDictionarySetValue(policyDict
[1], kSecTrustSettingsResult
, trustSettingsResult
); 
1215                         if (indexOfEntryWithAllowedErrorForHostnameMismatch 
!= kCFNotFound
) { 
1216                                 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */ 
1217                                 CFArraySetValueAtIndex(trustSettings
, indexOfEntryWithAllowedErrorForHostnameMismatch
, policyDict
[1]); 
1218                         } else if (!(hasPolicyValue
)) { 
1219                                 /* add a new policy constraint */ 
1220                                 CFArrayAppendValue(trustSettings
, policyDict
[1]); 
1223                 CFReleaseSafe(policyDict
[0]); 
1224                 CFReleaseSafe(policyDict
[1]); 
1225                 CFReleaseSafe(certExpired
); 
1226                 CFReleaseSafe(hostnameMismatch
); 
1229         if (status 
!= errSecSuccess
) { 
1232         CFReleaseSafe(policyOid
); 
1233         CFReleaseSafe(policyProperties
); 
1234         CFReleaseSafe(policy
); 
1236         dispatch_async(dispatch_get_main_queue(), ^{ 
1237                 /* add certificate to keychain first */ 
1238                 OSStatus status 
= SecCertificateAddToKeychain(certificate
, NULL
); 
1239                 if (status 
== errSecSuccess 
|| status 
== errSecDuplicateItem
) { 
1240                         /* this will block on authorization UI... */ 
1241                         status 
= SecTrustSettingsSetTrustSettings(certificate
, 
1242                                 domain
, trustSettings
); 
1245                         CFErrorRef error 
= NULL
; 
1247                                 error 
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, status
, NULL
); 
1250                         if (!CFNumberGetValue(trustSettingsResult
, kCFNumberSInt32Type
, (int32_t*)&tsrc
)) { 
1251                                 tsrc 
= (int32_t)kSecTrustSettingsResultUnspecified
; 
1253                         result((SecTrustSettingsResult
)tsrc
, error
); 
1254                         CFReleaseSafe(error
); 
1256                 CFRelease(trustSettingsResult
); 
1257                 CFRelease(trustSettings
); 
1258                 CFRelease(certificate
); 
1259                 CFRelease(hostname
); 
1265         CFReleaseSafe(policyOid
); 
1266         CFReleaseSafe(policyProperties
); 
1267         CFReleaseSafe(policy
); 
1268         CFReleaseSafe(trustSettingsResult
); 
1269         CFReleaseSafe(trustSettings
); 
1270         CFReleaseSafe(certificate
); 
1271         CFReleaseSafe(hostname
); 
1273                 CFErrorRef error 
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, status
, NULL
); 
1274                 result(kSecTrustSettingsResultInvalid
, error
); 
1275                 CFReleaseSafe(error
);