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>
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()
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 auto_ptr
<TrustSettings
>_(ts
); // make sure this gets deleted just in case something throws underneath
354 *trustSettings
= ts
->copyTrustSettings(cert
);
357 *modDate
= ts
->copyModDate(cert
);
363 static void tsAddConditionalCerts(CFMutableArrayRef certArray
);
366 * Common code for SecTrustSettingsCopyQualifiedCerts() and
367 * SecTrustSettingsCopyUnrestrictedRoots().
369 static OSStatus
tsCopyCertsCommon(
370 /* usage constraints, all optional */
371 const CSSM_OID
*policyOID
,
372 const char *policyString
,
373 SecTrustSettingsKeyUsage keyUsage
,
374 /* constrain to only roots */
376 /* per-domain enables */
380 CFArrayRef
*certArray
) /* RETURNED */
382 StLock
<Mutex
> _TC(sutCacheLock());
383 StLock
<Mutex
> _TK(SecTrustKeychainsGetMutex());
385 TS_REQUIRED(certArray
)
387 /* this relies on the domain enums being numbered 0..2, user..system */
388 bool domainEnable
[3] = {user
, admin
, system
};
390 /* we'll retain it again before successful exit */
391 CFRef
<CFMutableArrayRef
> outArray(CFArrayCreateMutable(NULL
, 0,
392 &kCFTypeArrayCallBacks
));
395 * Search all keychains - user's keychain list, System.keychain,
396 * and system root store
398 StorageManager::KeychainList keychains
;
401 globals().storageManager
.getSearchList(keychains
);
404 adminKc
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false);
405 keychains
.push_back(adminKc
);
407 Keychain sysRootKc
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false);
408 keychains
.push_back(sysRootKc
);
410 assert(kSecTrustSettingsDomainUser
== 0);
411 for(unsigned domain
=0; domain
<TRUST_SETTINGS_NUM_DOMAINS
; domain
++) {
412 if(!domainEnable
[domain
]) {
415 TrustSettings
*ts
= tsGetGlobalTrustSettings((SecTrustSettingsDomain
)domain
);
419 ts
->findQualifiedCerts(keychains
,
420 false, /* !findAll */
422 policyOID
, policyString
, keyUsage
,
426 tsAddConditionalCerts(outArray
);
428 *certArray
= outArray
;
429 CFRetainSafe(*certArray
);
430 trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
431 CFArrayGetCount(outArray
));
432 return errSecSuccess
;
435 static void tsAddConditionalCerts(CFMutableArrayRef certArray
)
438 struct certmap_entry_s
{
439 CFStringRef bundleId
;
441 const CFIndex length
;
443 typedef struct certmap_entry_s certmap_entry_t
;
445 CFBundleRef bundle
= CFBundleGetMainBundle();
446 CFStringRef bundleIdentifier
= (bundle
) ? CFBundleGetIdentifier(bundle
) : NULL
;
447 if (!bundleIdentifier
|| !certArray
) { return; }
449 // conditionally include 1024-bit compatibility roots for specific apps
450 const certmap_entry_t certmap
[] = {
451 { CFSTR("com.autodesk.AdSSO"), _GTECyberTrustGlobalRootCA
, sizeof(_GTECyberTrustGlobalRootCA
) }, // rdar://25916338
452 { CFSTR("com.clo3d.MD5"), _ThawtePremiumServerCA
, sizeof(_ThawtePremiumServerCA
) }, // rdar://26281864
455 unsigned int i
, certmaplen
= sizeof(certmap
) / sizeof(certmap_entry_t
);
456 for (i
=0; i
<certmaplen
; i
++) {
457 if (CFStringCompare(bundleIdentifier
, certmap
[i
].bundleId
, 0) == kCFCompareEqualTo
) {
458 SecCertificateRef cert
= SecCertificateCreateWithBytes(NULL
, certmap
[i
].data
, certmap
[i
].length
);
459 if (!cert
) { continue; }
460 CFArrayAppendValue(certArray
, cert
);
466 // this function is a no-op on iOS platforms
471 #pragma mark --- SPI functions ---
475 * Fundamental routine used by TP to ascertain status of one cert.
477 * Returns true in *foundMatchingEntry if a trust setting matching
478 * specific constraints was found for the cert. Returns true in
479 * *foundAnyEntry if any entry was found for the cert, even if it
480 * did not match the specified constraints. The TP uses this to
481 * optimize for the case where a cert is being evaluated for
482 * one type of usage, and then later for another type. If
483 * foundAnyEntry is false, the second evaluation need not occur.
485 * Returns the domain in which a setting was found in *foundDomain.
487 * Allowed errors applying to the specified cert evaluation
488 * are returned in a mallocd array in *allowedErrors and must
489 * be freed by caller.
491 * The design of the entire TrustSettings module is centered around
492 * optimizing the performance of this routine (security concerns
493 * aside, that is). It's why the per-cert dictionaries are stored
494 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
495 * are cached in memory by tsGetGlobalTrustSettings(), and why those
496 * cached TrustSettings objects are 'trimmed' of dictionary fields
497 * which are not needed to verify a cert.
499 * The API functions which are used to manipulate Trust Settings
500 * are called infrequently and need not be particularly fast since
501 * they result in user interaction for authentication. Thus they do
502 * not use cached TrustSettings as this function does.
504 OSStatus
SecTrustSettingsEvaluateCert(
505 CFStringRef certHashStr
,
506 /* parameters describing the current cert evalaution */
507 const CSSM_OID
*policyOID
,
508 const char *policyString
, /* optional */
509 uint32 policyStringLen
,
510 SecTrustSettingsKeyUsage keyUsage
, /* optional */
511 bool isRootCert
, /* for checking default setting */
512 /* RETURNED values */
513 SecTrustSettingsDomain
*foundDomain
,
514 CSSM_RETURN
**allowedErrors
, /* mallocd */
515 uint32
*numAllowedErrors
,
516 SecTrustSettingsResult
*resultType
,
517 bool *foundMatchingEntry
,
522 StLock
<Mutex
> _(sutCacheLock());
524 TS_REQUIRED(certHashStr
)
525 TS_REQUIRED(foundDomain
)
526 TS_REQUIRED(allowedErrors
)
527 TS_REQUIRED(numAllowedErrors
)
528 TS_REQUIRED(resultType
)
529 TS_REQUIRED(foundMatchingEntry
)
530 TS_REQUIRED(foundAnyEntry
)
532 /* ensure a NULL_terminated string */
533 auto_array
<char> polStr
;
534 if(policyString
!= NULL
&& policyStringLen
> 0) {
535 polStr
.allocate(policyStringLen
+ 1);
536 memmove(polStr
.get(), policyString
, policyStringLen
);
537 if(policyString
[policyStringLen
- 1] != '\0') {
538 (polStr
.get())[policyStringLen
] = '\0';
542 /* initial condition - this can grow if we inspect multiple TrustSettings */
543 *allowedErrors
= NULL
;
544 *numAllowedErrors
= 0;
547 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
548 * search user first, then admin, then system.
550 assert(kSecTrustSettingsDomainAdmin
== (kSecTrustSettingsDomainUser
+ 1));
551 assert(kSecTrustSettingsDomainSystem
== (kSecTrustSettingsDomainAdmin
+ 1));
552 bool foundAny
= false;
553 for(unsigned domain
=kSecTrustSettingsDomainUser
;
554 domain
<=kSecTrustSettingsDomainSystem
;
556 TrustSettings
*ts
= tsGetGlobalTrustSettings((SecTrustSettingsDomain
)domain
);
561 /* validate cert returns true if matching entry was found */
562 bool foundAnyHere
= false;
563 bool found
= ts
->evaluateCert(certHashStr
, policyOID
,
564 polStr
.get(), keyUsage
, isRootCert
,
565 allowedErrors
, numAllowedErrors
, resultType
, &foundAnyHere
);
569 * Note this, even though we may overwrite it later if this
570 * is an Unspecified entry and we find a definitive entry
573 *foundDomain
= (SecTrustSettingsDomain
)domain
;
575 if(found
&& (*resultType
!= kSecTrustSettingsResultUnspecified
)) {
576 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
);
577 *foundAnyEntry
= true;
578 *foundMatchingEntry
= true;
579 return errSecSuccess
;
581 foundAny
|= foundAnyHere
;
583 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
584 *foundAnyEntry
= foundAny
;
585 *foundMatchingEntry
= false;
586 return errSecSuccess
;
591 * Obtain trusted certs which match specified usage.
592 * Only certs with a SecTrustSettingsResult of
593 * kSecTrustSettingsResultTrustRoot or
594 * or kSecTrustSettingsResultTrustAsRoot will be returned.
595 * To be used by SecureTransport for its SSLSetTrustedRoots() call;
596 * I hope nothing else has to use this...
597 * Caller must CFRelease the returned CFArrayRef.
599 OSStatus
SecTrustSettingsCopyQualifiedCerts(
600 const CSSM_OID
*policyOID
,
601 const char *policyString
, /* optional */
602 uint32 policyStringLen
,
603 SecTrustSettingsKeyUsage keyUsage
, /* optional */
604 CFArrayRef
*certArray
) /* RETURNED */
608 /* ensure a NULL_terminated string */
609 auto_array
<char> polStr
;
610 if(policyString
!= NULL
) {
611 polStr
.allocate(policyStringLen
+ 1);
612 memmove(polStr
.get(), policyString
, policyStringLen
);
613 if(policyString
[policyStringLen
- 1] != '\0') {
614 (polStr
.get())[policyStringLen
] = '\0';
618 return tsCopyCertsCommon(policyOID
, polStr
.get(), keyUsage
,
619 false, /* !onlyRoots */
620 true, true, true, /* all domains */
627 * Obtain unrestricted root certs from the specified domain(s).
628 * Only returns roots with no usage constraints.
629 * Caller must CFRelease the returned CFArrayRef.
631 OSStatus
SecTrustSettingsCopyUnrestrictedRoots(
635 CFArrayRef
*certArray
) /* RETURNED */
639 OSStatus status
= tsCopyCertsCommon(NULL
, NULL
, NULL
, /* no constraints */
640 true, /* onlyRoots */
649 static const char hexChars
[16] = {
650 '0', '1', '2', '3', '4', '5', '6', '7',
651 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
655 * Obtain a string representing a cert's SHA1 digest. This string is
656 * the key used to look up per-cert trust settings in a TrustSettings record.
658 CFStringRef
SecTrustSettingsCertHashStrFromCert(
659 SecCertificateRef certRef
)
661 if(certRef
== NULL
) {
665 if(certRef
== kSecTrustSettingsDefaultRootCertSetting
) {
666 /* use this string instead of the cert hash as the dictionary key */
667 trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting");
668 secerror("Caller passed kSecTrustSettingsDefaultRootCertSetting. This constant is deprecated and no longer affects the behavior of the system.");
669 return kSecTrustRecordDefaultRootCert
;
673 OSStatus ortn
= SecCertificateGetData(certRef
, &certData
);
677 return SecTrustSettingsCertHashStrFromData(certData
.Data
, certData
.Length
);
680 CFStringRef
SecTrustSettingsCertHashStrFromData(
684 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
685 char asciiDigest
[(2 * CC_SHA1_DIGEST_LENGTH
) + 1];
687 char *outp
= asciiDigest
;
688 unsigned char *inp
= digest
;
694 CC_SHA1(cert
, (CC_LONG
)certLen
, digest
);
696 for(dex
=0; dex
<CC_SHA1_DIGEST_LENGTH
; dex
++) {
698 outp
[1] = hexChars
[c
& 0xf];
700 outp
[0] = hexChars
[c
];
704 return CFStringCreateWithCString(NULL
, asciiDigest
, kCFStringEncodingASCII
);
708 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
709 * No locking or cache flushing here; it's all local to the TrustSettings
712 OSStatus
SecTrustSettingsSetTrustSettingsExternal(
713 CFDataRef settingsIn
, /* optional */
714 SecCertificateRef certRef
, /* optional */
715 CFTypeRef trustSettingsDictOrArray
, /* optional */
716 CFDataRef
*settingsOut
) /* RETURNED */
720 TS_REQUIRED(settingsOut
)
725 result
= TrustSettings::CreateTrustSettings((SecTrustSettingsDomain
)kSecTrustSettingsDomainMemory
, settingsIn
, ts
);
726 if (result
!= errSecSuccess
) {
730 auto_ptr
<TrustSettings
>_(ts
);
732 if(certRef
!= NULL
) {
733 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
);
735 *settingsOut
= ts
->createExternal();
736 return errSecSuccess
;
741 #pragma mark --- API functions ---
743 OSStatus
SecTrustSettingsCopyTrustSettings(
744 SecCertificateRef certRef
,
745 SecTrustSettingsDomain domain
,
746 CFArrayRef
*trustSettings
) /* RETURNED */
749 TS_REQUIRED(trustSettings
)
751 OSStatus result
= tsCopyTrustSettings(certRef
, domain
, trustSettings
, NULL
);
752 if (result
== errSecSuccess
&& *trustSettings
== NULL
) {
753 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
758 OSStatus
SecTrustSettingsCopyModificationDate(
759 SecCertificateRef certRef
,
760 SecTrustSettingsDomain domain
,
761 CFDateRef
*modificationDate
) /* RETURNED */
764 TS_REQUIRED(modificationDate
)
766 OSStatus result
= tsCopyTrustSettings(certRef
, domain
, NULL
, modificationDate
);
767 if (result
== errSecSuccess
&& *modificationDate
== NULL
) {
768 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
773 /* works with existing and with new cert */
774 OSStatus
SecTrustSettingsSetTrustSettings(
775 SecCertificateRef certRef
,
776 SecTrustSettingsDomain domain
,
777 CFTypeRef trustSettingsDictOrArray
)
783 if(domain
== kSecTrustSettingsDomainSystem
) {
784 return errSecDataNotModifiable
;
790 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_YES
, TRIM_NO
, ts
);
791 if (result
!= errSecSuccess
) {
795 auto_ptr
<TrustSettings
>_(ts
);
797 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
);
799 tsTrustSettingsChanged();
800 return errSecSuccess
;
805 OSStatus
SecTrustSettingsRemoveTrustSettings(
806 SecCertificateRef cert
,
807 SecTrustSettingsDomain domain
)
813 if(domain
== kSecTrustSettingsDomainSystem
) {
814 return errSecDataNotModifiable
;
820 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
821 if (result
!= errSecSuccess
) {
825 auto_ptr
<TrustSettings
>_(ts
);
827 /* deleteTrustSettings throws if record not found */
828 trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
830 ts
->deleteTrustSettings(cert
);
832 tsTrustSettingsChanged();
833 return errSecSuccess
;
838 /* get all certs listed in specified domain */
839 OSStatus
SecTrustSettingsCopyCertificates(
840 SecTrustSettingsDomain domain
,
841 CFArrayRef
*certArray
)
845 TS_REQUIRED(certArray
)
849 CFMutableArrayRef trustedCertArray
= NULL
;
850 SecTrustRef trust
= NULL
;
852 status
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
853 if (status
!= errSecSuccess
) {
857 auto_ptr
<TrustSettings
>_(ts
);
859 CFMutableArrayRef outArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
862 * Keychains to search: user's search list, System.keychain, system root store
864 StorageManager::KeychainList keychains
;
868 case kSecTrustSettingsDomainUser
:
869 /* user search list */
870 globals().storageManager
.getSearchList(keychains
);
871 /* drop thru to next case */
872 case kSecTrustSettingsDomainAdmin
:
873 /* admin certs in system keychain */
874 adminKc
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false);
875 keychains
.push_back(adminKc
);
876 /* drop thru to next case */
877 case kSecTrustSettingsDomainSystem
:
878 /* and, for all cases, immutable system root store */
879 sysRootKc
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false);
880 keychains
.push_back(sysRootKc
);
882 /* already validated when we created the TrustSettings */
885 ts
->findCerts(keychains
, outArray
);
886 CFIndex count
= outArray
? CFArrayGetCount(outArray
) : 0;
888 CFReleaseSafe(outArray
);
889 return errSecNoTrustSettings
;
891 /* Go through outArray and do a SecTrustEvaluate only for DomainSystem */
892 if (kSecTrustSettingsDomainSystem
== domain
) {
894 SecPolicyRef policy
= SecPolicyCreateBasicX509();
895 trustedCertArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
896 for (i
= 0; i
< count
; i
++) {
897 SecTrustResultType result
;
898 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(outArray
, i
);
899 status
= SecTrustCreateWithCertificates(certificate
, policy
, &trust
);
900 if (status
!= errSecSuccess
) {
901 CFReleaseSafe(policy
);
904 status
= SecTrustEvaluate(trust
, &result
);
905 if (status
!= errSecSuccess
) {
906 CFReleaseSafe(policy
);
909 if (result
!= kSecTrustResultFatalTrustFailure
) {
910 CFArrayAppendValue(trustedCertArray
, certificate
);
912 CFReleaseNull(trust
);
914 tsAddConditionalCerts(trustedCertArray
);
915 if (CFArrayGetCount(trustedCertArray
) == 0) {
916 status
= errSecNoTrustSettings
;
918 *certArray
= trustedCertArray
;
919 CFReleaseSafe(outArray
);
921 CFReleaseSafe(policy
);
923 *certArray
= outArray
;
926 if (status
!= errSecSuccess
) {
927 CFReleaseSafe(outArray
);
928 CFReleaseSafe(trustedCertArray
);
930 CFReleaseNull(trust
);
935 static CFArrayRef gUserAdminCerts
= NULL
;
936 static bool gUserAdminCertsCacheBuilt
= false;
937 static ModuleNexus
<ReadWriteLock
> gUserAdminCertsLock
;
939 void SecTrustSettingsPurgeUserAdminCertsCache(void) {
940 StReadWriteLock
_(gUserAdminCertsLock(), StReadWriteLock::Write
);
941 CFReleaseNull(gUserAdminCerts
);
942 gUserAdminCertsCacheBuilt
= false;
945 OSStatus
SecTrustSettingsCopyCertificatesForUserAdminDomains(
946 CFArrayRef
*certArray
)
948 TS_REQUIRED(certArray
);
949 OSStatus result
= errSecSuccess
;
951 { /* Hold the read lock for the check */
952 StReadWriteLock
_(gUserAdminCertsLock(), StReadWriteLock::Read
);
953 if (gUserAdminCertsCacheBuilt
) {
954 if (gUserAdminCerts
) {
955 *certArray
= (CFArrayRef
)CFRetain(gUserAdminCerts
);
956 return errSecSuccess
;
958 return errSecNoTrustSettings
;
963 /* There were no cached results. We'll have to recreate them. */
964 CFMutableArrayRef outArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
966 return errSecAllocate
;
969 CFArrayRef userTrusted
= NULL
, adminTrusted
= NULL
;
970 OSStatus userStatus
= SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser
, &userTrusted
);
971 if ((userStatus
== errSecSuccess
) && (userTrusted
!= NULL
)) {
972 CFArrayAppendArray(outArray
, userTrusted
, CFRangeMake(0, CFArrayGetCount(userTrusted
)));
973 CFRelease(userTrusted
);
976 OSStatus adminStatus
= SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin
, &adminTrusted
);
977 if ((adminStatus
== errSecSuccess
) && (adminTrusted
!= NULL
)) {
978 CFArrayAppendArray(outArray
, adminTrusted
, CFRangeMake(0, CFArrayGetCount(adminTrusted
)));
979 CFRelease(adminTrusted
);
982 /* Lack of trust settings for a domain results in an error above. Only fail
983 * if we weren't able to get trust settings for both domains. */
984 if (userStatus
!= errSecSuccess
&& adminStatus
!= errSecSuccess
) {
988 if (result
!= errSecSuccess
&& outArray
) {
993 *certArray
= outArray
;
995 /* For valid results, update the global cache */
996 if (result
== errSecSuccess
|| result
== errSecNoTrustSettings
) {
997 StReadWriteLock
_(gUserAdminCertsLock(), StReadWriteLock::Write
);
998 CFReleaseNull(gUserAdminCerts
);
999 gUserAdminCerts
= (CFArrayRef
)CFRetainSafe(outArray
);
1000 gUserAdminCertsCacheBuilt
= true;
1007 * Obtain an external, portable representation of the specified
1008 * domain's TrustSettings. Caller must CFRelease the returned data.
1010 OSStatus
SecTrustSettingsCreateExternalRepresentation(
1011 SecTrustSettingsDomain domain
,
1012 CFDataRef
*trustSettings
)
1016 TS_REQUIRED(trustSettings
)
1021 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
1022 if (result
!= errSecSuccess
) {
1026 auto_ptr
<TrustSettings
>_(ts
);
1028 *trustSettings
= ts
->createExternal();
1029 return errSecSuccess
;
1035 * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation,
1036 * into the specified domain.
1038 OSStatus
SecTrustSettingsImportExternalRepresentation(
1039 SecTrustSettingsDomain domain
,
1040 CFDataRef trustSettings
) /* optional - NULL means empty settings */
1044 if(domain
== kSecTrustSettingsDomainSystem
) {
1045 return errSecDataNotModifiable
;
1051 result
= TrustSettings::CreateTrustSettings(domain
, trustSettings
, ts
);
1052 if (result
!= errSecSuccess
) {
1056 auto_ptr
<TrustSettings
>_(ts
);
1059 tsTrustSettingsChanged();
1060 return errSecSuccess
;
1066 * SecTrustSettingsSetTrustSettings convenience wrapper function.
1068 void SecTrustSettingsSetTrustedCertificateForSSLHost(
1069 SecCertificateRef certificate
,
1070 CFStringRef hostname
,
1071 void (^result
)(SecTrustSettingsResult trustResult
, CFErrorRef error
))
1073 __block CFMutableArrayRef trustSettings
= NULL
;
1074 __block CFNumberRef trustSettingsResult
= NULL
;
1075 __block SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
1077 CFDictionaryRef policyProperties
= NULL
;
1078 CFStringRef policyOid
= NULL
;
1079 SecPolicyRef policy
= NULL
;
1081 Boolean isSelfSigned
= false;
1082 Boolean hasPolicyConstraint
= false;
1083 Boolean hasPolicyValue
= false;
1084 Boolean policyConstraintChanged
= false;
1085 CFIndex indexOfEntryWithAllowedErrorForExpiredCert
= kCFNotFound
;
1086 CFIndex indexOfEntryWithAllowedErrorForHostnameMismatch
= kCFNotFound
;
1088 int32_t trustSettingsResultCode
= kSecTrustSettingsResultTrustAsRoot
;
1089 OSStatus status
= errSecSuccess
;
1091 CFRetainSafe(certificate
);
1092 CFRetainSafe(hostname
);
1093 if (!certificate
|| !hostname
) {
1094 status
= errSecParam
;
1096 status
= SecCertificateIsSelfSigned(certificate
, &isSelfSigned
);
1098 if (status
!= errSecSuccess
) {
1102 trustSettingsResultCode
= kSecTrustSettingsResultTrustRoot
;
1104 trustSettingsResult
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trustSettingsResultCode
);
1106 /* start with the existing trust settings for this certificate, if any */
1108 CFArrayRef curTrustSettings
= NULL
;
1109 (void)SecTrustSettingsCopyTrustSettings(certificate
, domain
, &curTrustSettings
);
1110 if (curTrustSettings
) {
1111 trustSettings
= CFArrayCreateMutableCopy(NULL
, 0, curTrustSettings
);
1112 CFReleaseNull(curTrustSettings
);
1114 trustSettings
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1117 if (!trustSettings
|| !trustSettingsResult
) {
1118 status
= errSecAllocate
;
1122 /* set up policy and value instances to trust the certificate for SSL for a given hostname */
1123 policy
= SecPolicyCreateSSL(true, hostname
);
1125 status
= errSecInternal
;
1128 policyProperties
= SecPolicyCopyProperties(policy
);
1129 if (!policyProperties
) {
1130 status
= errSecInternal
;
1133 policyOid
= (CFStringRef
)CFDictionaryGetValue(policyProperties
, kSecPolicyOid
);
1134 CFRetainSafe(policyOid
);
1136 status
= errSecInternal
;
1140 /* look for dictionaries in the trust settings array for this policy and value */
1141 count
= CFArrayGetCount(trustSettings
);
1142 for (i
=0; i
< count
; i
++) {
1143 CFDictionaryRef constraints
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trustSettings
, i
);
1144 if (!constraints
) { continue; }
1145 SecPolicyRef aPolicy
= (SecPolicyRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsPolicy
);
1146 if (!aPolicy
) { continue; }
1147 CFDictionaryRef properties
= SecPolicyCopyProperties(aPolicy
);
1148 if (!properties
) { continue; }
1149 CFStringRef aPolicyOid
= (CFStringRef
)CFDictionaryGetValue(properties
, kSecPolicyOid
);
1150 if (aPolicyOid
&& kCFCompareEqualTo
== CFStringCompare(aPolicyOid
, policyOid
, 0)) {
1151 CFStringRef aPolicyString
= (CFStringRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsPolicyString
);
1152 if (aPolicyString
&& kCFCompareEqualTo
== CFStringCompare(aPolicyString
, hostname
, kCFCompareCaseInsensitive
)) {
1153 /* found existing entry */
1154 CFNumberRef allowedErr
= (CFNumberRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsAllowedError
);
1156 if (!allowedErr
|| !CFNumberGetValue(allowedErr
, kCFNumberSInt32Type
, &eOld
)) {
1159 CFNumberRef tsResult
= (CFNumberRef
)CFDictionaryGetValue(constraints
, kSecTrustSettingsResult
);
1161 if (!tsResult
|| !CFNumberGetValue(allowedErr
, kCFNumberSInt32Type
, &rOld
)) {
1162 rOld
= kSecTrustSettingsResultTrustRoot
;
1164 if (!hasPolicyValue
) { hasPolicyValue
= (aPolicyString
!= NULL
); }
1165 if (!hasPolicyConstraint
) { hasPolicyConstraint
= true; }
1166 if (eOld
== CSSMERR_TP_CERT_EXPIRED
) {
1167 indexOfEntryWithAllowedErrorForExpiredCert
= i
;
1168 } else if (eOld
== CSSMERR_APPLETP_HOSTNAME_MISMATCH
) {
1169 indexOfEntryWithAllowedErrorForHostnameMismatch
= i
;
1171 if (trustSettingsResultCode
!= rOld
) {
1172 policyConstraintChanged
= true; // we are changing existing policy constraint's result
1176 CFReleaseSafe(properties
);
1179 if (!hasPolicyConstraint
) {
1180 policyConstraintChanged
= true; // we are adding a new policy constraint
1181 } else if (hostname
&& !hasPolicyValue
) {
1182 policyConstraintChanged
= true; // we need to add the hostname to an existing policy constraint
1183 } else if ((indexOfEntryWithAllowedErrorForExpiredCert
== kCFNotFound
) ||
1184 (indexOfEntryWithAllowedErrorForHostnameMismatch
== kCFNotFound
)) {
1185 policyConstraintChanged
= true; // we are missing one of the expected allowed-error entries for this policy
1188 if (policyConstraintChanged
) {
1189 CFMutableDictionaryRef policyDict
[2] = { NULL
, NULL
};
1190 policyDict
[0] = CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1191 policyDict
[1] = CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1192 int32_t certExpiredCode
= (int32_t)CSSMERR_TP_CERT_EXPIRED
;
1193 CFNumberRef certExpired
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &certExpiredCode
);
1194 int32_t hostnameMismatchCode
= (int32_t)CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
1195 CFNumberRef hostnameMismatch
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &hostnameMismatchCode
);
1196 if (!policyDict
[0] || !policyDict
[1] || !certExpired
|| !hostnameMismatch
) {
1197 status
= errSecInternal
;
1199 /* set up entry for policy, hostname, expired cert error, and result */
1200 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsPolicy
, policy
);
1201 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsPolicyString
, hostname
);
1202 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsAllowedError
, certExpired
);
1203 CFDictionarySetValue(policyDict
[0], kSecTrustSettingsResult
, trustSettingsResult
);
1204 if (indexOfEntryWithAllowedErrorForExpiredCert
!= kCFNotFound
) {
1205 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1206 CFArraySetValueAtIndex(trustSettings
, indexOfEntryWithAllowedErrorForExpiredCert
, policyDict
[0]);
1207 } else if (!(hasPolicyValue
)) {
1208 /* add a new policy constraint */
1209 CFArrayAppendValue(trustSettings
, policyDict
[0]);
1211 /* set up additional entry for policy, hostname, hostname mismatch error, and result */
1212 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsPolicy
, policy
);
1213 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsPolicyString
, hostname
);
1214 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsAllowedError
, hostnameMismatch
);
1215 CFDictionarySetValue(policyDict
[1], kSecTrustSettingsResult
, trustSettingsResult
);
1216 if (indexOfEntryWithAllowedErrorForHostnameMismatch
!= kCFNotFound
) {
1217 /* if we found an existing constraint for this policy, hostname, and allowed error, replace it */
1218 CFArraySetValueAtIndex(trustSettings
, indexOfEntryWithAllowedErrorForHostnameMismatch
, policyDict
[1]);
1219 } else if (!(hasPolicyValue
)) {
1220 /* add a new policy constraint */
1221 CFArrayAppendValue(trustSettings
, policyDict
[1]);
1224 CFReleaseSafe(policyDict
[0]);
1225 CFReleaseSafe(policyDict
[1]);
1226 CFReleaseSafe(certExpired
);
1227 CFReleaseSafe(hostnameMismatch
);
1230 if (status
!= errSecSuccess
) {
1233 CFReleaseSafe(policyOid
);
1234 CFReleaseSafe(policyProperties
);
1235 CFReleaseSafe(policy
);
1237 dispatch_async(dispatch_get_main_queue(), ^{
1238 /* add certificate to keychain first */
1239 OSStatus status
= SecCertificateAddToKeychain(certificate
, NULL
);
1240 if (status
== errSecSuccess
|| status
== errSecDuplicateItem
) {
1241 /* this will block on authorization UI... */
1242 status
= SecTrustSettingsSetTrustSettings(certificate
,
1243 domain
, trustSettings
);
1246 CFErrorRef error
= NULL
;
1248 error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, status
, NULL
);
1251 if (!CFNumberGetValue(trustSettingsResult
, kCFNumberSInt32Type
, (int32_t*)&tsrc
)) {
1252 tsrc
= (int32_t)kSecTrustSettingsResultUnspecified
;
1254 result((SecTrustSettingsResult
)tsrc
, error
);
1255 CFReleaseSafe(error
);
1257 CFRelease(trustSettingsResult
);
1258 CFRelease(trustSettings
);
1259 CFRelease(certificate
);
1260 CFRelease(hostname
);
1266 CFReleaseSafe(policyOid
);
1267 CFReleaseSafe(policyProperties
);
1268 CFReleaseSafe(policy
);
1269 CFReleaseSafe(trustSettingsResult
);
1270 CFReleaseSafe(trustSettings
);
1271 CFReleaseSafe(certificate
);
1272 CFReleaseSafe(hostname
);
1274 CFErrorRef error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, status
, NULL
);
1275 result(kSecTrustSettingsResultInvalid
, error
);
1276 CFReleaseSafe(error
);