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
);