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 <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"
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>
51 #include <security_utilities/simulatecrash_assert.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 unique_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()
306 SecTrustSettingsPurgeUserAdminCertsCache();
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
))));
316 trustSettingsDbg("tsTrustSettingsChanged: posting notification");
317 SecurityServer::ClientSession
cs (Allocator::standard(), Allocator::standard());
318 cs
.postNotification (SecurityServer::kNotificationDomainDatabase
,
319 kSecTrustSettingsChangedEvent
, data
);
324 * Common code for SecTrustSettingsCopyTrustSettings(),
325 * SecTrustSettingsCopyModificationDate().
327 static OSStatus
tsCopyTrustSettings(
328 SecCertificateRef cert
,
329 SecTrustSettingsDomain domain
,
330 CFArrayRef
*trustSettings
, /* optionally RETURNED */
331 CFDateRef
*modDate
) /* optionally RETURNED */
337 /* obtain fresh full copy from disk */
341 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
343 // rather than throw these results, just return them because we are at the top level
344 if (result
== errSecNoTrustSettings
) {
345 return errSecItemNotFound
;
347 else if (result
!= errSecSuccess
) {
351 unique_ptr
<TrustSettings
>_(ts
); // make sure this gets deleted just in case something throws underneath
354 *trustSettings
= ts
->copyTrustSettings(cert
);
357 *modDate
= ts
->copyModDate(cert
);
364 * Common code for SecTrustSettingsCopyTrustSettings(),
365 * SecTrustSettingsCopyModificationDate().
367 static OSStatus
tsCopyTrustSettings_cached(
368 SecCertificateRef cert
,
369 SecTrustSettingsDomain domain
,
370 CFArrayRef CF_RETURNS_RETAINED
*trustSettings
)
376 StLock
<Mutex
> _(sutCacheLock());
377 TrustSettings
* ts
= tsGetGlobalTrustSettings(domain
);
379 // rather than throw these results, just return them because we are at the top level
381 return errSecItemNotFound
;
385 *trustSettings
= ts
->copyTrustSettings(cert
);
391 static OSStatus
tsContains(
392 SecCertificateRef cert
,
393 SecTrustSettingsDomain domain
)
399 StLock
<Mutex
> _(sutCacheLock());
400 TrustSettings
* ts
= tsGetGlobalTrustSettings(domain
);
402 // rather than throw these results, just return them because we are at the top level
404 return errSecItemNotFound
;
407 if (ts
->contains(cert
)) {
408 return errSecSuccess
;
410 return errSecItemNotFound
;
416 static void tsAddConditionalCerts(CFMutableArrayRef certArray
);
419 * Common code for SecTrustSettingsCopyQualifiedCerts() and
420 * SecTrustSettingsCopyUnrestrictedRoots().
422 static OSStatus
tsCopyCertsCommon(
423 /* usage constraints, all optional */
424 const CSSM_OID
*policyOID
,
425 const char *policyString
,
426 SecTrustSettingsKeyUsage keyUsage
,
427 /* constrain to only roots */
429 /* per-domain enables */
433 CFArrayRef
*certArray
) /* RETURNED */
435 StLock
<Mutex
> _TC(sutCacheLock());
436 StLock
<Mutex
> _TK(SecTrustKeychainsGetMutex());
438 TS_REQUIRED(certArray
)
440 /* this relies on the domain enums being numbered 0..2, user..system */
441 bool domainEnable
[3] = {user
, admin
, system
};
443 /* we'll retain it again before successful exit */
444 CFRef
<CFMutableArrayRef
> outArray(CFArrayCreateMutable(NULL
, 0,
445 &kCFTypeArrayCallBacks
));
448 * Search all keychains - user's keychain list, System.keychain,
449 * and system root store
451 StorageManager::KeychainList keychains
;
454 globals().storageManager
.getSearchList(keychains
);
457 adminKc
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false);
458 keychains
.push_back(adminKc
);
460 Keychain sysRootKc
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false);
461 keychains
.push_back(sysRootKc
);
463 assert(kSecTrustSettingsDomainUser
== 0);
464 for(unsigned domain
=0; domain
<TRUST_SETTINGS_NUM_DOMAINS
; domain
++) {
465 if(!domainEnable
[domain
]) {
468 TrustSettings
*ts
= tsGetGlobalTrustSettings((SecTrustSettingsDomain
)domain
);
472 ts
->findQualifiedCerts(keychains
,
473 false, /* !findAll */
475 policyOID
, policyString
, keyUsage
,
479 tsAddConditionalCerts(outArray
);
481 *certArray
= outArray
;
482 CFRetainSafe(*certArray
);
483 trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
484 CFArrayGetCount(outArray
));
485 return errSecSuccess
;
488 static void tsAddConditionalCerts(CFMutableArrayRef certArray
)
491 struct certmap_entry_s
{
492 CFStringRef bundleId
;
494 const CFIndex length
;
496 typedef struct certmap_entry_s certmap_entry_t
;
498 CFBundleRef bundle
= CFBundleGetMainBundle();
499 CFStringRef bundleIdentifier
= (bundle
) ? CFBundleGetIdentifier(bundle
) : NULL
;
500 if (!bundleIdentifier
|| !certArray
) { return; }
502 // conditionally include 1024-bit compatibility roots for specific apps
503 const certmap_entry_t certmap
[] = {
504 { CFSTR("com.autodesk.AdSSO"), _GTECyberTrustGlobalRootCA
, sizeof(_GTECyberTrustGlobalRootCA
) }, // rdar://25916338
505 { CFSTR("com.clo3d.MD5"), _ThawtePremiumServerCA
, sizeof(_ThawtePremiumServerCA
) }, // rdar://26281864
508 unsigned int i
, certmaplen
= sizeof(certmap
) / sizeof(certmap_entry_t
);
509 for (i
=0; i
<certmaplen
; i
++) {
510 if (CFStringCompare(bundleIdentifier
, certmap
[i
].bundleId
, 0) == kCFCompareEqualTo
) {
511 SecCertificateRef cert
= SecCertificateCreateWithBytes(NULL
, certmap
[i
].data
, certmap
[i
].length
);
512 if (!cert
) { continue; }
513 CFArrayAppendValue(certArray
, cert
);
519 // this function is a no-op on iOS platforms
524 #pragma mark --- SPI functions ---
528 * Fundamental routine used by TP to ascertain status of one cert.
530 * Returns true in *foundMatchingEntry if a trust setting matching
531 * specific constraints was found for the cert. Returns true in
532 * *foundAnyEntry if any entry was found for the cert, even if it
533 * did not match the specified constraints. The TP uses this to
534 * optimize for the case where a cert is being evaluated for
535 * one type of usage, and then later for another type. If
536 * foundAnyEntry is false, the second evaluation need not occur.
538 * Returns the domain in which a setting was found in *foundDomain.
540 * Allowed errors applying to the specified cert evaluation
541 * are returned in a mallocd array in *allowedErrors and must
542 * be freed by caller.
544 * The design of the entire TrustSettings module is centered around
545 * optimizing the performance of this routine (security concerns
546 * aside, that is). It's why the per-cert dictionaries are stored
547 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
548 * are cached in memory by tsGetGlobalTrustSettings(), and why those
549 * cached TrustSettings objects are 'trimmed' of dictionary fields
550 * which are not needed to verify a cert.
552 * The API functions which are used to manipulate Trust Settings
553 * are called infrequently and need not be particularly fast since
554 * they result in user interaction for authentication. Thus they do
555 * not use cached TrustSettings as this function does.
557 OSStatus
SecTrustSettingsEvaluateCert(
558 CFStringRef certHashStr
,
559 /* parameters describing the current cert evalaution */
560 const CSSM_OID
*policyOID
,
561 const char *policyString
, /* optional */
562 uint32 policyStringLen
,
563 SecTrustSettingsKeyUsage keyUsage
, /* optional */
564 bool isRootCert
, /* for checking default setting */
565 /* RETURNED values */
566 SecTrustSettingsDomain
*foundDomain
,
567 CSSM_RETURN
**allowedErrors
, /* mallocd */
568 uint32
*numAllowedErrors
,
569 SecTrustSettingsResult
*resultType
,
570 bool *foundMatchingEntry
,
575 StLock
<Mutex
> _(sutCacheLock());
577 TS_REQUIRED(certHashStr
)
578 TS_REQUIRED(foundDomain
)
579 TS_REQUIRED(allowedErrors
)
580 TS_REQUIRED(numAllowedErrors
)
581 TS_REQUIRED(resultType
)
582 TS_REQUIRED(foundMatchingEntry
)
583 TS_REQUIRED(foundAnyEntry
)
585 /* ensure a NULL_terminated string */
586 auto_array
<char> polStr
;
587 if(policyString
!= NULL
&& policyStringLen
> 0) {
588 polStr
.allocate(policyStringLen
+ 1);
589 memmove(polStr
.get(), policyString
, policyStringLen
);
590 if(policyString
[policyStringLen
- 1] != '\0') {
591 (polStr
.get())[policyStringLen
] = '\0';
595 /* initial condition - this can grow if we inspect multiple TrustSettings */
596 *allowedErrors
= NULL
;
597 *numAllowedErrors
= 0;
600 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
601 * search user first, then admin, then system.
603 assert(kSecTrustSettingsDomainAdmin
== (kSecTrustSettingsDomainUser
+ 1));
604 assert(kSecTrustSettingsDomainSystem
== (kSecTrustSettingsDomainAdmin
+ 1));
605 bool foundAny
= false;
606 for(unsigned domain
=kSecTrustSettingsDomainUser
;
607 domain
<=kSecTrustSettingsDomainSystem
;
609 TrustSettings
*ts
= tsGetGlobalTrustSettings((SecTrustSettingsDomain
)domain
);
614 /* validate cert returns true if matching entry was found */
615 bool foundAnyHere
= false;
616 bool found
= ts
->evaluateCert(certHashStr
, policyOID
,
617 polStr
.get(), keyUsage
, isRootCert
,
618 allowedErrors
, numAllowedErrors
, resultType
, &foundAnyHere
);
622 * Note this, even though we may overwrite it later if this
623 * is an Unspecified entry and we find a definitive entry
626 *foundDomain
= (SecTrustSettingsDomain
)domain
;
628 if(found
&& (*resultType
!= kSecTrustSettingsResultUnspecified
)) {
629 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
);
630 *foundAnyEntry
= true;
631 *foundMatchingEntry
= true;
632 return errSecSuccess
;
634 foundAny
|= foundAnyHere
;
636 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
637 *foundAnyEntry
= foundAny
;
638 *foundMatchingEntry
= false;
639 return errSecSuccess
;
644 * Obtain trusted certs which match specified usage.
645 * Only certs with a SecTrustSettingsResult of
646 * kSecTrustSettingsResultTrustRoot or
647 * or kSecTrustSettingsResultTrustAsRoot will be returned.
648 * To be used by SecureTransport for its SSLSetTrustedRoots() call;
649 * I hope nothing else has to use this...
650 * Caller must CFRelease the returned CFArrayRef.
652 OSStatus
SecTrustSettingsCopyQualifiedCerts(
653 const CSSM_OID
*policyOID
,
654 const char *policyString
, /* optional */
655 uint32 policyStringLen
,
656 SecTrustSettingsKeyUsage keyUsage
, /* optional */
657 CFArrayRef
*certArray
) /* RETURNED */
661 /* ensure a NULL_terminated string */
662 auto_array
<char> polStr
;
663 if(policyString
!= NULL
) {
664 polStr
.allocate(policyStringLen
+ 1);
665 memmove(polStr
.get(), policyString
, policyStringLen
);
666 if(policyString
[policyStringLen
- 1] != '\0') {
667 (polStr
.get())[policyStringLen
] = '\0';
671 return tsCopyCertsCommon(policyOID
, polStr
.get(), keyUsage
,
672 false, /* !onlyRoots */
673 true, true, true, /* all domains */
680 * Obtain unrestricted root certs from the specified domain(s).
681 * Only returns roots with no usage constraints.
682 * Caller must CFRelease the returned CFArrayRef.
684 OSStatus
SecTrustSettingsCopyUnrestrictedRoots(
688 CFArrayRef
*certArray
) /* RETURNED */
692 OSStatus status
= tsCopyCertsCommon(NULL
, NULL
, NULL
, /* no constraints */
693 true, /* onlyRoots */
702 static const char hexChars
[16] = {
703 '0', '1', '2', '3', '4', '5', '6', '7',
704 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
708 * Obtain a string representing a cert's SHA1 digest. This string is
709 * the key used to look up per-cert trust settings in a TrustSettings record.
711 CFStringRef
SecTrustSettingsCertHashStrFromCert(
712 SecCertificateRef certRef
)
714 if(certRef
== NULL
) {
718 if(certRef
== kSecTrustSettingsDefaultRootCertSetting
) {
719 /* use this string instead of the cert hash as the dictionary key */
720 trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting");
721 secerror("Caller passed kSecTrustSettingsDefaultRootCertSetting. This constant is deprecated and no longer affects the behavior of the system.");
722 return kSecTrustRecordDefaultRootCert
;
726 OSStatus ortn
= SecCertificateGetData(certRef
, &certData
);
730 return SecTrustSettingsCertHashStrFromData(certData
.Data
, certData
.Length
);
733 CFStringRef
SecTrustSettingsCertHashStrFromData(
737 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
738 char asciiDigest
[(2 * CC_SHA1_DIGEST_LENGTH
) + 1];
740 char *outp
= asciiDigest
;
741 unsigned char *inp
= digest
;
747 CC_SHA1(cert
, (CC_LONG
)certLen
, digest
);
749 for(dex
=0; dex
<CC_SHA1_DIGEST_LENGTH
; dex
++) {
751 outp
[1] = hexChars
[c
& 0xf];
753 outp
[0] = hexChars
[c
];
757 return CFStringCreateWithCString(NULL
, asciiDigest
, kCFStringEncodingASCII
);
761 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
762 * No locking or cache flushing here; it's all local to the TrustSettings
765 OSStatus
SecTrustSettingsSetTrustSettingsExternal(
766 CFDataRef settingsIn
, /* optional */
767 SecCertificateRef certRef
, /* optional */
768 CFTypeRef trustSettingsDictOrArray
, /* optional */
769 CFDataRef
*settingsOut
) /* RETURNED */
773 TS_REQUIRED(settingsOut
)
778 result
= TrustSettings::CreateTrustSettings((SecTrustSettingsDomain
)kSecTrustSettingsDomainMemory
, settingsIn
, ts
);
779 if (result
!= errSecSuccess
) {
783 unique_ptr
<TrustSettings
>_(ts
);
785 if(certRef
!= NULL
) {
786 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
);
788 *settingsOut
= ts
->createExternal();
789 return errSecSuccess
;
794 void SecTrustSettingsPurgeCache(void) {
798 OSStatus
SecTrustSettingsCopyTrustSettings_Cached(
799 SecCertificateRef certRef
,
800 SecTrustSettingsDomain domain
,
801 CFArrayRef CF_RETURNS_RETAINED
*trustSettings
) /* RETURNED */
804 TS_REQUIRED(trustSettings
)
806 OSStatus result
= tsCopyTrustSettings_cached(certRef
, domain
, trustSettings
);
807 if (result
== errSecSuccess
&& *trustSettings
== NULL
) {
808 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
813 #pragma mark --- API functions ---
815 OSStatus
SecTrustSettingsCopyTrustSettings(
816 SecCertificateRef certRef
,
817 SecTrustSettingsDomain domain
,
818 CFArrayRef
*trustSettings
) /* RETURNED */
821 TS_REQUIRED(trustSettings
)
823 OSStatus result
= tsCopyTrustSettings(certRef
, domain
, trustSettings
, NULL
);
824 if (result
== errSecSuccess
&& *trustSettings
== NULL
) {
825 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
830 OSStatus
SecTrustSettingsCopyModificationDate(
831 SecCertificateRef certRef
,
832 SecTrustSettingsDomain domain
,
833 CFDateRef
*modificationDate
) /* RETURNED */
836 TS_REQUIRED(modificationDate
)
838 OSStatus result
= tsCopyTrustSettings(certRef
, domain
, NULL
, modificationDate
);
839 if (result
== errSecSuccess
&& *modificationDate
== NULL
) {
840 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
845 /* works with existing and with new cert */
846 OSStatus
SecTrustSettingsSetTrustSettings(
847 SecCertificateRef certRef
,
848 SecTrustSettingsDomain domain
,
849 CFTypeRef trustSettingsDictOrArray
)
855 if(domain
== kSecTrustSettingsDomainSystem
) {
856 return errSecDataNotModifiable
;
862 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_YES
, TRIM_NO
, ts
);
863 if (result
!= errSecSuccess
) {
867 unique_ptr
<TrustSettings
>_(ts
);
869 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
);
871 tsTrustSettingsChanged();
872 return errSecSuccess
;
877 OSStatus
SecTrustSettingsRemoveTrustSettings(
878 SecCertificateRef cert
,
879 SecTrustSettingsDomain domain
)
885 if(domain
== kSecTrustSettingsDomainSystem
) {
886 return errSecDataNotModifiable
;
892 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
893 if (result
!= errSecSuccess
) {
897 unique_ptr
<TrustSettings
>_(ts
);
899 /* deleteTrustSettings throws if record not found */
900 trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
902 ts
->deleteTrustSettings(cert
);
904 tsTrustSettingsChanged();
905 return errSecSuccess
;
910 /* get all certs listed in specified domain */
911 OSStatus
SecTrustSettingsCopyCertificates(
912 SecTrustSettingsDomain domain
,
913 CFArrayRef
*certArray
)
917 TS_REQUIRED(certArray
)
921 CFMutableArrayRef trustedCertArray
= NULL
;
922 SecTrustRef trust
= NULL
;
924 status
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
925 if (status
!= errSecSuccess
) {
929 unique_ptr
<TrustSettings
>_(ts
);
931 CFMutableArrayRef outArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
934 * Keychains to search: user's search list, System.keychain, system root store
936 StorageManager::KeychainList keychains
;
940 case kSecTrustSettingsDomainUser
:
941 /* user search list */
942 globals().storageManager
.getSearchList(keychains
);
943 /* drop thru to next case */
944 case kSecTrustSettingsDomainAdmin
:
945 /* admin certs in system keychain */
946 adminKc
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false);
947 keychains
.push_back(adminKc
);
948 /* drop thru to next case */
949 case kSecTrustSettingsDomainSystem
:
950 /* and, for all cases, immutable system root store */
951 sysRootKc
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false);
952 keychains
.push_back(sysRootKc
);
954 /* already validated when we created the TrustSettings */
957 ts
->findCerts(keychains
, outArray
);
958 CFIndex count
= outArray
? CFArrayGetCount(outArray
) : 0;
960 CFReleaseSafe(outArray
);
961 return errSecNoTrustSettings
;
963 /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */
964 if (kSecTrustSettingsDomainSystem
== domain
) {
966 SecPolicyRef policy
= SecPolicyCreateBasicX509();
967 trustedCertArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
968 for (i
= 0; i
< count
; i
++) {
969 SecTrustResultType result
;
970 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(outArray
, i
);
971 status
= SecTrustCreateWithCertificates(certificate
, policy
, &trust
);
972 if (status
!= errSecSuccess
) {
973 CFReleaseSafe(policy
);
976 status
= SecTrustEvaluate(trust
, &result
);
977 if (status
!= errSecSuccess
) {
978 CFReleaseSafe(policy
);
981 if (result
!= kSecTrustResultFatalTrustFailure
) {
982 CFArrayAppendValue(trustedCertArray
, certificate
);
984 CFReleaseNull(trust
);
986 tsAddConditionalCerts(trustedCertArray
);
987 if (CFArrayGetCount(trustedCertArray
) == 0) {
988 status
= errSecNoTrustSettings
;
990 *certArray
= trustedCertArray
;
991 CFReleaseSafe(outArray
);
993 CFReleaseSafe(policy
);
995 *certArray
= outArray
;
998 if (status
!= errSecSuccess
) {
999 CFReleaseSafe(outArray
);
1000 CFReleaseSafe(trustedCertArray
);
1002 CFReleaseNull(trust
);
1007 static CFArrayRef gUserAdminCerts
= NULL
;
1008 static bool gUserAdminCertsCacheBuilt
= false;
1009 static ModuleNexus
<ReadWriteLock
> gUserAdminCertsLock
;
1011 void SecTrustSettingsPurgeUserAdminCertsCache(void) {
1012 StReadWriteLock
_(gUserAdminCertsLock(), StReadWriteLock::Write
);
1013 CFReleaseNull(gUserAdminCerts
);
1014 gUserAdminCertsCacheBuilt
= false;
1017 OSStatus
SecTrustSettingsCopyCertificatesForUserAdminDomains(
1018 CFArrayRef
*certArray
)
1020 TS_REQUIRED(certArray
);
1021 OSStatus result
= errSecSuccess
;
1023 { /* Hold the read lock for the check */
1024 StReadWriteLock
_(gUserAdminCertsLock(), StReadWriteLock::Read
);
1025 if (gUserAdminCertsCacheBuilt
) {
1026 if (gUserAdminCerts
) {
1027 *certArray
= (CFArrayRef
)CFRetain(gUserAdminCerts
);
1028 return errSecSuccess
;
1030 return errSecNoTrustSettings
;
1035 /* There were no cached results. We'll have to recreate them. */
1036 CFMutableArrayRef outArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1038 return errSecAllocate
;
1041 CFArrayRef userTrusted
= NULL
, adminTrusted
= NULL
;
1042 OSStatus userStatus
= SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser
, &userTrusted
);
1043 if ((userStatus
== errSecSuccess
) && (userTrusted
!= NULL
)) {
1044 CFArrayAppendArray(outArray
, userTrusted
, CFRangeMake(0, CFArrayGetCount(userTrusted
)));
1045 CFRelease(userTrusted
);
1048 OSStatus adminStatus
= SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin
, &adminTrusted
);
1049 if ((adminStatus
== errSecSuccess
) && (adminTrusted
!= NULL
)) {
1050 CFArrayAppendArray(outArray
, adminTrusted
, CFRangeMake(0, CFArrayGetCount(adminTrusted
)));
1051 CFRelease(adminTrusted
);
1054 /* Lack of trust settings for a domain results in an error above. Only fail
1055 * if we weren't able to get trust settings for both domains. */
1056 if (userStatus
!= errSecSuccess
&& adminStatus
!= errSecSuccess
) {
1057 result
= userStatus
;
1060 if (result
!= errSecSuccess
&& outArray
) {
1061 CFRelease(outArray
);
1065 *certArray
= outArray
;
1067 /* For valid results, update the global cache */
1068 if (result
== errSecSuccess
|| result
== errSecNoTrustSettings
) {
1069 StReadWriteLock
_(gUserAdminCertsLock(), StReadWriteLock::Write
);
1070 CFReleaseNull(gUserAdminCerts
);
1071 gUserAdminCerts
= (CFArrayRef
)CFRetainSafe(outArray
);
1072 gUserAdminCertsCacheBuilt
= true;
1078 bool SecTrustSettingsUserAdminDomainsContain(SecCertificateRef certRef
)
1080 TS_REQUIRED(certRef
)
1081 if (tsContains(certRef
, kSecTrustSettingsDomainAdmin
) == errSecSuccess
||
1082 tsContains(certRef
, kSecTrustSettingsDomainUser
) == errSecSuccess
) {
1089 * Obtain an external, portable representation of the specified
1090 * domain's TrustSettings. Caller must CFRelease the returned data.
1092 OSStatus
SecTrustSettingsCreateExternalRepresentation(
1093 SecTrustSettingsDomain domain
,
1094 CFDataRef
*trustSettings
)
1098 TS_REQUIRED(trustSettings
)
1103 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
1104 if (result
!= errSecSuccess
) {
1108 unique_ptr
<TrustSettings
>_(ts
);
1110 *trustSettings
= ts
->createExternal();
1111 return errSecSuccess
;
1117 * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation,
1118 * into the specified domain.
1120 OSStatus
SecTrustSettingsImportExternalRepresentation(
1121 SecTrustSettingsDomain domain
,
1122 CFDataRef trustSettings
) /* optional - NULL means empty settings */
1126 if(domain
== kSecTrustSettingsDomainSystem
) {
1127 return errSecDataNotModifiable
;
1133 result
= TrustSettings::CreateTrustSettings(domain
, trustSettings
, ts
);
1134 if (result
!= errSecSuccess
) {
1138 unique_ptr
<TrustSettings
>_(ts
);
1141 tsTrustSettingsChanged();
1142 return errSecSuccess
;
1148 * SecTrustSettingsSetTrustSettings convenience wrapper function.
1150 void SecTrustSettingsSetTrustedCertificateForSSLHost(
1151 SecCertificateRef certificate
,
1152 CFStringRef hostname
,
1153 void (^result
)(SecTrustSettingsResult trustResult
, CFErrorRef error
))
1155 __block CFMutableArrayRef trustSettings
= NULL
;
1156 __block CFNumberRef trustSettingsResult
= NULL
;
1157 __block SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
1159 CFDictionaryRef policyProperties
= NULL
;
1160 CFStringRef policyOid
= NULL
;
1161 SecPolicyRef policy
= NULL
;
1163 Boolean isSelfSigned
= false;
1164 Boolean hasPolicyConstraint
= false;
1165 Boolean hasPolicyValue
= false;
1166 Boolean policyConstraintChanged
= false;
1167 CFIndex indexOfEntryWithAllowedErrorForExpiredCert
= kCFNotFound
;
1168 CFIndex indexOfEntryWithAllowedErrorForHostnameMismatch
= kCFNotFound
;
1170 int32_t trustSettingsResultCode
= kSecTrustSettingsResultTrustAsRoot
;
1171 OSStatus status
= errSecSuccess
;
1173 CFRetainSafe(certificate
);
1174 CFRetainSafe(hostname
);
1175 if (!certificate
|| !hostname
) {
1176 status
= errSecParam
;
1178 status
= SecCertificateIsSelfSigned(certificate
, &isSelfSigned
);
1180 if (status
!= errSecSuccess
) {
1184 trustSettingsResultCode
= kSecTrustSettingsResultTrustRoot
;
1186 trustSettingsResult
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trustSettingsResultCode
);
1188 /* start with the existing trust settings for this certificate, if any */
1190 CFArrayRef curTrustSettings
= NULL
;
1191 (void)SecTrustSettingsCopyTrustSettings(certificate
, domain
, &curTrustSettings
);
1192 if (curTrustSettings
) {
1193 trustSettings
= CFArrayCreateMutableCopy(NULL
, 0, curTrustSettings
);
1194 CFReleaseNull(curTrustSettings
);
1196 trustSettings
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1199 if (!trustSettings
|| !trustSettingsResult
) {
1200 status
= errSecAllocate
;
1204 /* set up policy and value instances to trust the certificate for SSL for a given hostname */
1205 policy
= SecPolicyCreateSSL(true, hostname
);
1207 status
= errSecInternal
;
1210 policyProperties
= SecPolicyCopyProperties(policy
);
1211 if (!policyProperties
) {
1212 status
= errSecInternal
;
1215 policyOid
= (CFStringRef
)CFDictionaryGetValue(policyProperties
, kSecPolicyOid
);
1216 CFRetainSafe(policyOid
);
1218 status
= errSecInternal
;
1222 /* look for dictionaries in the trust settings array for this policy and value */
1223 count
= CFArrayGetCount(trustSettings
);
1224 for (i
=0; i
< count
; i
++) {
1225 CFDictionaryRef constraints
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trustSettings
, i
);
1226 if (!constraints
) { continue; }
1227 SecPolicyRef aPolicy
= (SecPolicyRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsPolicy
);
1228 if (!aPolicy
) { continue; }
1229 CFDictionaryRef properties
= SecPolicyCopyProperties(aPolicy
);
1230 if (!properties
) { continue; }
1231 CFStringRef aPolicyOid
= (CFStringRef
)CFDictionaryGetValue(properties
, kSecPolicyOid
);
1232 if (aPolicyOid
&& kCFCompareEqualTo
== CFStringCompare(aPolicyOid
, policyOid
, 0)) {
1233 CFStringRef aPolicyString
= (CFStringRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsPolicyString
);
1234 if (aPolicyString
&& kCFCompareEqualTo
== CFStringCompare(aPolicyString
, hostname
, kCFCompareCaseInsensitive
)) {
1235 /* found existing entry */
1236 CFNumberRef allowedErr
= (CFNumberRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsAllowedError
);
1238 if (!allowedErr
|| !CFNumberGetValue(allowedErr
, kCFNumberSInt32Type
, &eOld
)) {
1241 CFNumberRef tsResult
= (CFNumberRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsResult
);
1243 if (!tsResult
|| !CFNumberGetValue(allowedErr
, kCFNumberSInt32Type
, &rOld
)) {
1244 rOld
= kSecTrustSettingsResultTrustRoot
;
1246 if (!hasPolicyValue
) { hasPolicyValue
= (aPolicyString
!= NULL
); }
1247 if (!hasPolicyConstraint
) { hasPolicyConstraint
= true; }
1248 if (eOld
== CSSMERR_TP_CERT_EXPIRED
) {
1249 indexOfEntryWithAllowedErrorForExpiredCert
= i
;
1250 } else if (eOld
== CSSMERR_APPLETP_HOSTNAME_MISMATCH
) {
1251 indexOfEntryWithAllowedErrorForHostnameMismatch
= i
;
1253 if (trustSettingsResultCode
!= rOld
) {
1254 policyConstraintChanged
= true; // we are changing existing policy constraint's result
1258 CFReleaseSafe(properties
);
1261 if (!hasPolicyConstraint
) {
1262 policyConstraintChanged
= true; // we are adding a new policy constraint
1263 } else if (hostname
&& !hasPolicyValue
) {
1264 policyConstraintChanged
= true; // we need to add the hostname to an existing policy constraint
1265 } else if ((indexOfEntryWithAllowedErrorForExpiredCert
== kCFNotFound
) ||
1266 (indexOfEntryWithAllowedErrorForHostnameMismatch
== kCFNotFound
)) {
1267 policyConstraintChanged
= true; // we are missing one of the expected allowed-error entries for this policy
1270 if (policyConstraintChanged
) {
1271 CFMutableDictionaryRef policyDict
[2] = { NULL
, NULL
};
1272 policyDict
[0] = CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1273 policyDict
[1] = CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1274 int32_t certExpiredCode
= (int32_t)CSSMERR_TP_CERT_EXPIRED
;
1275 CFNumberRef certExpired
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &certExpiredCode
);
1276 int32_t hostnameMismatchCode
= (int32_t)CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
1277 CFNumberRef hostnameMismatch
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &hostnameMismatchCode
);
1278 if (!policyDict
[0] || !policyDict
[1] || !certExpired
|| !hostnameMismatch
) {
1279 status
= errSecInternal
;
1281 /* set up entry for policy, hostname, expired cert error, and result */
1282 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsPolicy
, policy
);
1283 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsPolicyString
, hostname
);
1284 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsAllowedError
, certExpired
);
1285 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsResult
, trustSettingsResult
);
1286 if (indexOfEntryWithAllowedErrorForExpiredCert
!= kCFNotFound
) {
1287 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1288 CFArraySetValueAtIndex(trustSettings
, indexOfEntryWithAllowedErrorForExpiredCert
, policyDict
[0]);
1289 } else if (!(hasPolicyValue
)) {
1290 /* add a new policy constraint */
1291 CFArrayAppendValue(trustSettings
, policyDict
[0]);
1293 /* set up additional entry for policy, hostname, hostname mismatch error, and result */
1294 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsPolicy
, policy
);
1295 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsPolicyString
, hostname
);
1296 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsAllowedError
, hostnameMismatch
);
1297 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsResult
, trustSettingsResult
);
1298 if (indexOfEntryWithAllowedErrorForHostnameMismatch
!= kCFNotFound
) {
1299 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1300 CFArraySetValueAtIndex(trustSettings
, indexOfEntryWithAllowedErrorForHostnameMismatch
, policyDict
[1]);
1301 } else if (!(hasPolicyValue
)) {
1302 /* add a new policy constraint */
1303 CFArrayAppendValue(trustSettings
, policyDict
[1]);
1306 CFReleaseSafe(policyDict
[0]);
1307 CFReleaseSafe(policyDict
[1]);
1308 CFReleaseSafe(certExpired
);
1309 CFReleaseSafe(hostnameMismatch
);
1312 if (status
!= errSecSuccess
) {
1315 CFReleaseSafe(policyOid
);
1316 CFReleaseSafe(policyProperties
);
1317 CFReleaseSafe(policy
);
1319 dispatch_async(dispatch_get_main_queue(), ^{
1320 /* add certificate to keychain first */
1321 OSStatus status
= SecCertificateAddToKeychain(certificate
, NULL
);
1322 if (status
== errSecSuccess
|| status
== errSecDuplicateItem
) {
1323 /* this will block on authorization UI... */
1324 status
= SecTrustSettingsSetTrustSettings(certificate
,
1325 domain
, trustSettings
);
1328 CFErrorRef error
= NULL
;
1330 error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, status
, NULL
);
1333 if (!CFNumberGetValue(trustSettingsResult
, kCFNumberSInt32Type
, (int32_t*)&tsrc
)) {
1334 tsrc
= (int32_t)kSecTrustSettingsResultUnspecified
;
1336 result((SecTrustSettingsResult
)tsrc
, error
);
1337 CFReleaseSafe(error
);
1339 CFRelease(trustSettingsResult
);
1340 CFRelease(trustSettings
);
1341 CFRelease(certificate
);
1342 CFRelease(hostname
);
1348 CFReleaseSafe(policyOid
);
1349 CFReleaseSafe(policyProperties
);
1350 CFReleaseSafe(policy
);
1351 CFReleaseSafe(trustSettingsResult
);
1352 CFReleaseSafe(trustSettings
);
1353 CFReleaseSafe(certificate
);
1354 CFReleaseSafe(hostname
);
1356 CFErrorRef error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, status
, NULL
);
1357 result(kSecTrustSettingsResultInvalid
, error
);
1358 CFReleaseSafe(error
);