2 * Copyright (c) 2005,2011-2015 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 "TrustSettingsUtils.h"
35 #include "TrustSettings.h"
36 #include "TrustSettingsSchema.h"
37 #include "TrustKeychains.h"
39 #include "SecKeychainPriv.h"
41 #include <security_utilities/threading.h>
42 #include <security_utilities/globalizer.h>
43 #include <security_utilities/errors.h>
44 #include <security_cdsa_utilities/cssmerrors.h>
45 #include <security_utilities/logging.h>
46 #include <security_utilities/debugging.h>
47 #include <security_utilities/simpleprefs.h>
48 #include <securityd_client/dictionary.h>
49 #include <securityd_client/ssclient.h>
55 #include <CommonCrypto/CommonDigest.h>
56 #include <CoreFoundation/CFPreferences.h>
57 #include <CoreServices/CoreServicesPriv.h> /* for _CSCheckFix */
59 #define trustSettingsDbg(args...) secdebug("trustSettings", ## args)
62 * Ideally we'd like to implement our own lock to protect the state of the cert stores
63 * without grabbing the global Sec API lock, but we deal with SecCFObjects, so we'll have
64 * to bite the bullet and grab the big lock. We also have our own lock protecting the
65 * global trust settings cache which is also used by the keychain callback function
66 * (which does not grab the Sec API lock).
69 #define BEGIN_RCSAPI \
70 OSStatus __secapiresult; \
73 __secapiresult=errSecSuccess; \
75 catch (const MacOSError &err) { __secapiresult=err.osStatus(); } \
76 catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } \
77 catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } \
78 catch (...) { __secapiresult=errSecInternalComponent; } \
79 return __secapiresult;
86 #pragma mark --- TrustSettings preferences ---
89 * If Colonel Klink wants to disable user-level Trust Settings, he'll have
90 * to restart the apps which will be affected after he does so. We are not
91 * going to consult system prefs every time we do a cert evaluation. We
92 * consult it once per process and cache the results here.
94 static bool tsUserTrustDisableValid
= false; /* true once we consult prefs */
95 static bool tsUserTrustDisable
= false; /* the cached value */
98 * Determine whether user-level Trust Settings disabled.
100 static bool tsUserTrustSettingsDisabled()
102 if(tsUserTrustDisableValid
) {
103 return tsUserTrustDisable
;
105 tsUserTrustDisable
= false;
107 Dictionary
* dictionary
= Dictionary::CreateDictionary(kSecTrustSettingsPrefsDomain
, Dictionary::US_System
);
110 auto_ptr
<Dictionary
> prefsDict(dictionary
);
111 /* this returns false if the pref isn't there, just like we want */
112 tsUserTrustDisable
= prefsDict
->getBoolValue(kSecTrustSettingsDisableUserTrustSettings
);
115 tsUserTrustDisableValid
= true;
116 return tsUserTrustDisable
;
119 #pragma mark --- TrustSettings global cache ---
122 *** cache submodule - keeps per-app copy of zero or one TrustSettings
123 *** for each domain. Used only by SecTrustSettingsEvaluateCert()
124 *** and SecTrustSettingsCopyQualifiedCerts(); results of
125 *** manipulation by public API functions are not cached.
129 * API/client code has to hold this lock when doing anything with any of
130 * the TrustSettings maintained here.
131 * It's recursive to accomodate CodeSigning's need to do cert verification
132 * (while we evaluate app equivalence).
134 static ModuleNexus
<RecursiveMutex
> sutCacheLock
;
136 #define TRUST_SETTINGS_NUM_DOMAINS 3
139 * The three global TrustSettings.
140 * We rely on the fact the the domain enums start with 0; we use
141 * the domain value as an index into the following two arrays.
143 static TrustSettings
*globalTrustSettings
[TRUST_SETTINGS_NUM_DOMAINS
] =
147 * Indicates "the associated global here is currently valid; if there isn't a
148 * globalTrustSettings[domain], don't try to find one"
150 static bool globalTrustSettingsValid
[TRUST_SETTINGS_NUM_DOMAINS
] =
151 {false, false, false};
153 /* remember the fact that we've registered our KC callback */
154 static bool sutRegisteredCallback
= false;
156 static void tsRegisterCallback();
159 * Assign global TrustSetting to new incoming value, which may be NULL.
160 * Caller holds sutCacheLock.
162 static void tsSetGlobalTrustSettings(
164 SecTrustSettingsDomain domain
)
166 assert(((int)domain
>= 0) && ((int)domain
< TRUST_SETTINGS_NUM_DOMAINS
));
168 trustSettingsDbg("tsSetGlobalTrustSettings domain %d: caching TS %p old TS %p",
169 (int)domain
, ts
, globalTrustSettings
[domain
]);
170 delete globalTrustSettings
[domain
];
171 globalTrustSettings
[domain
] = ts
;
172 globalTrustSettingsValid
[domain
] = ts
? true : false;
173 tsRegisterCallback();
177 * Obtain global TrustSettings for specified domain if it exists.
178 * Returns NULL if there is simply no TS for that domain.
179 * The TS, if returned, belongs to this cache module.
180 * Caller holds sutCacheLock.
182 static TrustSettings
*tsGetGlobalTrustSettings(
183 SecTrustSettingsDomain domain
)
185 assert(((int)domain
>= 0) && ((int)domain
< TRUST_SETTINGS_NUM_DOMAINS
));
187 if((domain
== kSecTrustSettingsDomainUser
) && tsUserTrustSettingsDisabled()) {
188 trustSettingsDbg("tsGetGlobalTrustSettings: skipping DISABLED user domain");
192 if(globalTrustSettingsValid
[domain
]) {
193 // ready or not, use this
194 return globalTrustSettings
[domain
];
196 assert(globalTrustSettings
[domain
] == NULL
);
198 /* try to find one */
199 OSStatus result
= errSecSuccess
;
200 TrustSettings
*ts
= NULL
;
201 /* don't create; trim if found */
202 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_YES
, ts
);
203 if ( (domain
!= kSecTrustSettingsDomainSystem
)
204 && (result
== errSecInternalComponent
)) {
206 * Could not connect to ocspd to get the user/admin domain trust settings
207 * This happens in single user mode for example.
208 * Valid flag is set to false and continue.
210 trustSettingsDbg("tsGetGlobalTrustSettings: could not connect to ocspd for domain (%d)",(int)domain
);
211 globalTrustSettingsValid
[domain
] = false;
212 tsRegisterCallback();
215 else if (result
== errSecNoTrustSettings
) {
217 * No TrustSettings for this domain, actually a fairly common case.
218 * Optimize: don't bother trying this again.
220 trustSettingsDbg("tsGetGlobalTrustSettings: flagging known NULL");
221 globalTrustSettingsValid
[domain
] = true;
222 tsRegisterCallback();
225 else if(result
!= errSecSuccess
) {
227 MacOSError::throwMe(result
);
230 tsSetGlobalTrustSettings(ts
, domain
);
235 * Purge TrustSettings cache.
236 * Called by Keychain Event callback and by our API functions that
237 * modify trust settings.
238 * Caller can NOT hold sutCacheLock.
240 static void tsPurgeCache()
244 StLock
<Mutex
> _(sutCacheLock());
245 trustSettingsDbg("tsPurgeCache");
246 for(domain
=0; domain
<TRUST_SETTINGS_NUM_DOMAINS
; domain
++) {
247 tsSetGlobalTrustSettings(NULL
, domain
);
252 * Keychain event callback function, for notification by other processes that
253 * user trust list(s) has/have changed.
255 static OSStatus
tsTrustSettingsCallback (
256 SecKeychainEvent keychainEvent
,
257 SecKeychainCallbackInfo
*info
,
260 trustSettingsDbg("tsTrustSettingsCallback, event %d", (int)keychainEvent
);
261 if(keychainEvent
!= kSecTrustSettingsChangedEvent
) {
262 /* should not happen, right? */
263 return errSecSuccess
;
265 if(info
->pid
== getpid()) {
267 * Avoid dup cache invalidates: we already dealt with this event.
269 trustSettingsDbg("cacheEventCallback: our pid, skipping");
274 return errSecSuccess
;
278 * Ensure that we've registered for kSecTrustSettingsChangedEvent callbacks
280 static void tsRegisterCallback()
282 if(sutRegisteredCallback
) {
285 trustSettingsDbg("tsRegisterCallback: registering callback");
286 OSStatus ortn
= SecKeychainAddCallback(tsTrustSettingsCallback
,
287 kSecTrustSettingsChangedEventMask
, NULL
);
289 trustSettingsDbg("tsRegisterCallback: SecKeychainAddCallback returned %d", (int)ortn
);
290 /* Not sure how this could ever happen - maybe if there is no run loop active? */
292 sutRegisteredCallback
= true;
295 #pragma mark --- Static functions ---
299 * Called by API code when a trust list has changed; we notify other processes
300 * and purge our own cache.
302 static void tsTrustSettingsChanged()
306 /* The only interesting data is our pid */
307 NameValueDictionary nvd
;
308 pid_t ourPid
= getpid();
309 nvd
.Insert (new NameValuePair (PID_KEY
,
310 CssmData (reinterpret_cast<void*>(&ourPid
), sizeof (pid_t
))));
314 trustSettingsDbg("tsTrustSettingsChanged: posting notification");
315 SecurityServer::ClientSession
cs (Allocator::standard(), Allocator::standard());
316 cs
.postNotification (SecurityServer::kNotificationDomainDatabase
,
317 kSecTrustSettingsChangedEvent
, data
);
322 * Common code for SecTrustSettingsCopyTrustSettings(),
323 * SecTrustSettingsCopyModificationDate().
325 static OSStatus
tsCopyTrustSettings(
326 SecCertificateRef cert
,
327 SecTrustSettingsDomain domain
,
328 CFArrayRef
*trustSettings
, /* optionally RETURNED */
329 CFDateRef
*modDate
) /* optionally RETURNED */
335 /* obtain fresh full copy from disk */
339 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
341 // rather than throw these results, just return them because we are at the top level
342 if (result
== errSecNoTrustSettings
) {
343 return errSecItemNotFound
;
345 else if (result
!= errSecSuccess
) {
349 auto_ptr
<TrustSettings
>_(ts
); // make sure this gets deleted just in case something throws underneath
352 *trustSettings
= ts
->copyTrustSettings(cert
);
355 *modDate
= ts
->copyModDate(cert
);
361 static void tsAddConditionalCerts(CFMutableArrayRef certArray
);
364 * Common code for SecTrustSettingsCopyQualifiedCerts() and
365 * SecTrustSettingsCopyUnrestrictedRoots().
367 static OSStatus
tsCopyCertsCommon(
368 /* usage constraints, all optional */
369 const CSSM_OID
*policyOID
,
370 const char *policyString
,
371 SecTrustSettingsKeyUsage keyUsage
,
372 /* constrain to only roots */
374 /* per-domain enables */
378 CFArrayRef
*certArray
) /* RETURNED */
380 StLock
<Mutex
> _TC(sutCacheLock());
381 StLock
<Mutex
> _TK(SecTrustKeychainsGetMutex());
383 TS_REQUIRED(certArray
)
385 /* this relies on the domain enums being numbered 0..2, user..system */
386 bool domainEnable
[3] = {user
, admin
, system
};
388 /* we'll retain it again before successful exit */
389 CFRef
<CFMutableArrayRef
> outArray(CFArrayCreateMutable(NULL
, 0,
390 &kCFTypeArrayCallBacks
));
393 * Search all keychains - user's, System.keychain, system root store,
394 * system intermdiates as appropriate
396 StorageManager::KeychainList keychains
;
399 globals().storageManager
.getSearchList(keychains
);
402 adminKc
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false);
403 keychains
.push_back(adminKc
);
405 Keychain sysRootKc
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false);
406 keychains
.push_back(sysRootKc
);
407 Keychain sysCertKc
= globals().storageManager
.make(SYSTEM_CERT_STORE_PATH
, false);
408 keychains
.push_back(sysCertKc
);
410 assert(kSecTrustSettingsDomainUser
== 0);
411 for(unsigned domain
=0; domain
<TRUST_SETTINGS_NUM_DOMAINS
; domain
++) {
412 if(!domainEnable
[domain
]) {
415 TrustSettings
*ts
= tsGetGlobalTrustSettings(domain
);
419 ts
->findQualifiedCerts(keychains
,
420 false, /* !findAll */
422 policyOID
, policyString
, keyUsage
,
426 tsAddConditionalCerts(outArray
);
428 *certArray
= outArray
;
429 CFRetain(*certArray
);
430 trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
431 CFArrayGetCount(outArray
));
432 return errSecSuccess
;
435 #if TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE && !TARGET_OS_NANO
437 * _CSCheckFix is implemented in CarbonCore and exported via CoreServices.
438 * To avoid a circular dependency with Security, load this symbol dynamically.
440 typedef Boolean (*CSCheckFix_f
)(CFStringRef str
);
442 static dispatch_once_t sTSInitializeOnce
= 0;
443 static void * sCSCheckFixLibrary
= NULL
;
444 static CSCheckFix_f sCSCheckFix_f
= NULL
;
446 static OSStatus
_tsEnsuredInitialized(void);
448 static OSStatus
_tsEnsuredInitialized(void)
450 __block OSStatus status
= errSecNotAvailable
;
452 dispatch_once(&sTSInitializeOnce
, ^{
453 sCSCheckFixLibrary
= dlopen("/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY
| RTLD_LOCAL
);
454 assert(sCSCheckFixLibrary
);
455 if (sCSCheckFixLibrary
) {
456 sCSCheckFix_f
= (CSCheckFix_f
)(uintptr_t) dlsym(sCSCheckFixLibrary
, "_CSCheckFix");
466 #define APPNAMEWORKAROUND_KEY CFSTR("WorkaroundAppNames")
467 #define APPNAMEWORKAROUND_DOMAIN CFSTR("com.apple.security")
469 static bool tsCheckAppNameWorkaround(const char *name
)
474 CFStringCreateWithCString (NULL
, name
, kCFStringEncodingUTF8
);
475 CFArrayRef value
= (CFArrayRef
)
476 CFPreferencesCopyValue (APPNAMEWORKAROUND_KEY
,
477 APPNAMEWORKAROUND_DOMAIN
,
478 kCFPreferencesCurrentUser
,
479 kCFPreferencesAnyHost
);
480 if (!str
|| !value
||
481 !(CFArrayGetTypeID() == CFGetTypeID(value
))) {
484 count
= CFArrayGetCount(value
);
485 for (idx
= 0; idx
< count
; idx
++) {
486 CFStringRef appstr
= (CFStringRef
) CFArrayGetValueAtIndex(value
, idx
);
487 if (!(appstr
) || !(CFStringGetTypeID() == CFGetTypeID(appstr
))) {
490 if (!CFStringCompare(str
, appstr
, 0)) {
507 static void tsAddConditionalCerts(CFMutableArrayRef certArray
)
509 #if TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE && !TARGET_OS_NANO
510 struct certmap_entry_s
{
512 const CFIndex length
;
514 typedef struct certmap_entry_s certmap_entry_t
;
516 if (!certArray
) { return; }
518 pid_t pid
= getpid();
519 char pathbuf
[PROC_PIDPATHINFO_MAXSIZE
];
520 int ret
= proc_name(pid
, pathbuf
, sizeof(pathbuf
));
521 if (ret
<= 0) { return; }
523 OSStatus status
= _tsEnsuredInitialized();
524 if ((status
== 0 && sCSCheckFix_f(CFSTR("21946795"))) ||
525 tsCheckAppNameWorkaround(pathbuf
)) {
526 // conditionally include these 1024-bit roots
527 const certmap_entry_t certmap
[] = {
528 { _EquifaxSecureCA
, sizeof(_EquifaxSecureCA
) },
529 { _GTECyberTrustGlobalRootCA
, sizeof(_GTECyberTrustGlobalRootCA
) },
530 { _ThawtePremiumServerCA
, sizeof(_ThawtePremiumServerCA
) },
531 { _ThawteServerCA
, sizeof(_ThawteServerCA
) },
532 { _VeriSignClass3CA
, sizeof(_VeriSignClass3CA
) },
534 unsigned int i
, certmaplen
= sizeof(certmap
) / sizeof(certmap_entry_t
);
535 for (i
=0; i
<certmaplen
; i
++) {
536 SecCertificateRef cert
= SecCertificateCreateWithBytes(NULL
,
537 certmap
[i
].data
, certmap
[i
].length
);
539 CFArrayAppendValue(certArray
, cert
);
546 // this function is a no-op on iOS platforms
551 #pragma mark --- SPI functions ---
555 * Fundamental routine used by TP to ascertain status of one cert.
557 * Returns true in *foundMatchingEntry if a trust setting matching
558 * specific constraints was found for the cert. Returns true in
559 * *foundAnyEntry if any entry was found for the cert, even if it
560 * did not match the specified constraints. The TP uses this to
561 * optimize for the case where a cert is being evaluated for
562 * one type of usage, and then later for another type. If
563 * foundAnyEntry is false, the second evaluation need not occur.
565 * Returns the domain in which a setting was found in *foundDomain.
567 * Allowed errors applying to the specified cert evaluation
568 * are returned in a mallocd array in *allowedErrors and must
569 * be freed by caller.
571 * The design of the entire TrustSettings module is centered around
572 * optimizing the performance of this routine (security concerns
573 * aside, that is). It's why the per-cert dictionaries are stored
574 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
575 * are cached in memory by tsGetGlobalTrustSettings(), and why those
576 * cached TrustSettings objects are 'trimmed' of dictionary fields
577 * which are not needed to verify a cert.
579 * The API functions which are used to manipulate Trust Settings
580 * are called infrequently and need not be particularly fast since
581 * they result in user interaction for authentication. Thus they do
582 * not use cached TrustSettings as this function does.
584 OSStatus
SecTrustSettingsEvaluateCert(
585 CFStringRef certHashStr
,
586 /* parameters describing the current cert evalaution */
587 const CSSM_OID
*policyOID
,
588 const char *policyString
, /* optional */
589 uint32 policyStringLen
,
590 SecTrustSettingsKeyUsage keyUsage
, /* optional */
591 bool isRootCert
, /* for checking default setting */
592 /* RETURNED values */
593 SecTrustSettingsDomain
*foundDomain
,
594 CSSM_RETURN
**allowedErrors
, /* mallocd */
595 uint32
*numAllowedErrors
,
596 SecTrustSettingsResult
*resultType
,
597 bool *foundMatchingEntry
,
602 StLock
<Mutex
> _(sutCacheLock());
604 TS_REQUIRED(certHashStr
)
605 TS_REQUIRED(foundDomain
)
606 TS_REQUIRED(allowedErrors
)
607 TS_REQUIRED(numAllowedErrors
)
608 TS_REQUIRED(resultType
)
609 TS_REQUIRED(foundMatchingEntry
)
610 TS_REQUIRED(foundAnyEntry
)
612 /* ensure a NULL_terminated string */
613 auto_array
<char> polStr
;
614 if(policyString
!= NULL
&& policyStringLen
> 0) {
615 polStr
.allocate(policyStringLen
+ 1);
616 memmove(polStr
.get(), policyString
, policyStringLen
);
617 if(policyString
[policyStringLen
- 1] != '\0') {
618 (polStr
.get())[policyStringLen
] = '\0';
622 /* initial condition - this can grow if we inspect multiple TrustSettings */
623 *allowedErrors
= NULL
;
624 *numAllowedErrors
= 0;
627 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
628 * search user first, then admin, then system.
630 assert(kSecTrustSettingsDomainAdmin
== (kSecTrustSettingsDomainUser
+ 1));
631 assert(kSecTrustSettingsDomainSystem
== (kSecTrustSettingsDomainAdmin
+ 1));
632 bool foundAny
= false;
633 for(unsigned domain
=kSecTrustSettingsDomainUser
;
634 domain
<=kSecTrustSettingsDomainSystem
;
636 TrustSettings
*ts
= tsGetGlobalTrustSettings(domain
);
641 /* validate cert returns true if matching entry was found */
642 bool foundAnyHere
= false;
643 bool found
= ts
->evaluateCert(certHashStr
, policyOID
,
644 polStr
.get(), keyUsage
, isRootCert
,
645 allowedErrors
, numAllowedErrors
, resultType
, &foundAnyHere
);
649 * Note this, even though we may overwrite it later if this
650 * is an Unspecified entry and we find a definitive entry
653 *foundDomain
= domain
;
655 if(found
&& (*resultType
!= kSecTrustSettingsResultUnspecified
)) {
656 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
);
657 *foundAnyEntry
= true;
658 *foundMatchingEntry
= true;
659 return errSecSuccess
;
661 foundAny
|= foundAnyHere
;
663 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
664 *foundAnyEntry
= foundAny
;
665 *foundMatchingEntry
= false;
666 return errSecSuccess
;
671 * Obtain trusted certs which match specified usage.
672 * Only certs with a SecTrustSettingsResult of
673 * kSecTrustSettingsResultTrustRoot or
674 * or kSecTrustSettingsResultTrustAsRoot will be returned.
675 * To be used by SecureTransport for its SSLSetTrustedRoots() call;
676 * I hope nothing else has to use this...
677 * Caller must CFRelease the returned CFArrayRef.
679 OSStatus
SecTrustSettingsCopyQualifiedCerts(
680 const CSSM_OID
*policyOID
,
681 const char *policyString
, /* optional */
682 uint32 policyStringLen
,
683 SecTrustSettingsKeyUsage keyUsage
, /* optional */
684 CFArrayRef
*certArray
) /* RETURNED */
688 /* ensure a NULL_terminated string */
689 auto_array
<char> polStr
;
690 if(policyString
!= NULL
) {
691 polStr
.allocate(policyStringLen
+ 1);
692 memmove(polStr
.get(), policyString
, policyStringLen
);
693 if(policyString
[policyStringLen
- 1] != '\0') {
694 (polStr
.get())[policyStringLen
] = '\0';
698 return tsCopyCertsCommon(policyOID
, polStr
.get(), keyUsage
,
699 false, /* !onlyRoots */
700 true, true, true, /* all domains */
707 * Obtain unrestricted root certs from the specified domain(s).
708 * Only returns roots with no usage constraints.
709 * Caller must CFRelease the returned CFArrayRef.
711 OSStatus
SecTrustSettingsCopyUnrestrictedRoots(
715 CFArrayRef
*certArray
) /* RETURNED */
719 return tsCopyCertsCommon(NULL
, NULL
, NULL
, /* no constraints */
720 true, /* onlyRoots */
727 static const char hexChars
[16] = {
728 '0', '1', '2', '3', '4', '5', '6', '7',
729 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
733 * Obtain a string representing a cert's SHA1 digest. This string is
734 * the key used to look up per-cert trust settings in a TrustSettings record.
736 CFStringRef
SecTrustSettingsCertHashStrFromCert(
737 SecCertificateRef certRef
)
739 if(certRef
== NULL
) {
743 if(certRef
== kSecTrustSettingsDefaultRootCertSetting
) {
744 /* use this string instead of the cert hash as the dictionary key */
745 trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting");
746 return kSecTrustRecordDefaultRootCert
;
750 OSStatus ortn
= SecCertificateGetData(certRef
, &certData
);
754 return SecTrustSettingsCertHashStrFromData(certData
.Data
, certData
.Length
);
757 CFStringRef
SecTrustSettingsCertHashStrFromData(
761 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
762 char asciiDigest
[(2 * CC_SHA1_DIGEST_LENGTH
) + 1];
764 char *outp
= asciiDigest
;
765 unsigned char *inp
= digest
;
771 CC_SHA1(cert
, (CC_LONG
)certLen
, digest
);
773 for(dex
=0; dex
<CC_SHA1_DIGEST_LENGTH
; dex
++) {
775 outp
[1] = hexChars
[c
& 0xf];
777 outp
[0] = hexChars
[c
];
781 return CFStringCreateWithCString(NULL
, asciiDigest
, kCFStringEncodingASCII
);
785 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
786 * No locking or cache flushing here; it's all local to the TrustSettings
789 OSStatus
SecTrustSettingsSetTrustSettingsExternal(
790 CFDataRef settingsIn
, /* optional */
791 SecCertificateRef certRef
, /* optional */
792 CFTypeRef trustSettingsDictOrArray
, /* optional */
793 CFDataRef
*settingsOut
) /* RETURNED */
797 TS_REQUIRED(settingsOut
)
802 result
= TrustSettings::CreateTrustSettings(kSecTrustSettingsDomainMemory
, settingsIn
, ts
);
803 if (result
!= errSecSuccess
) {
807 auto_ptr
<TrustSettings
>_(ts
);
809 if(certRef
!= NULL
) {
810 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
);
812 *settingsOut
= ts
->createExternal();
813 return errSecSuccess
;
818 #pragma mark --- API functions ---
820 OSStatus
SecTrustSettingsCopyTrustSettings(
821 SecCertificateRef certRef
,
822 SecTrustSettingsDomain domain
,
823 CFArrayRef
*trustSettings
) /* RETURNED */
826 TS_REQUIRED(trustSettings
)
828 OSStatus result
= tsCopyTrustSettings(certRef
, domain
, trustSettings
, NULL
);
829 if (result
== errSecSuccess
&& *trustSettings
== NULL
) {
830 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
835 OSStatus
SecTrustSettingsCopyModificationDate(
836 SecCertificateRef certRef
,
837 SecTrustSettingsDomain domain
,
838 CFDateRef
*modificationDate
) /* RETURNED */
841 TS_REQUIRED(modificationDate
)
843 OSStatus result
= tsCopyTrustSettings(certRef
, domain
, NULL
, modificationDate
);
844 if (result
== errSecSuccess
&& *modificationDate
== NULL
) {
845 result
= errSecItemNotFound
; /* documented result if no trust settings exist */
850 /* works with existing and with new cert */
851 OSStatus
SecTrustSettingsSetTrustSettings(
852 SecCertificateRef certRef
,
853 SecTrustSettingsDomain domain
,
854 CFTypeRef trustSettingsDictOrArray
)
860 if(domain
== kSecTrustSettingsDomainSystem
) {
861 return errSecDataNotModifiable
;
867 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_YES
, TRIM_NO
, ts
);
868 if (result
!= errSecSuccess
) {
872 auto_ptr
<TrustSettings
>_(ts
);
874 ts
->setTrustSettings(certRef
, trustSettingsDictOrArray
);
876 tsTrustSettingsChanged();
877 return errSecSuccess
;
882 OSStatus
SecTrustSettingsRemoveTrustSettings(
883 SecCertificateRef cert
,
884 SecTrustSettingsDomain domain
)
890 if(domain
== kSecTrustSettingsDomainSystem
) {
891 return errSecDataNotModifiable
;
897 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
898 if (result
!= errSecSuccess
) {
902 auto_ptr
<TrustSettings
>_(ts
);
904 /* deleteTrustSettings throws if record not found */
905 trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
907 ts
->deleteTrustSettings(cert
);
909 tsTrustSettingsChanged();
910 return errSecSuccess
;
915 /* get all certs listed in specified domain */
916 OSStatus
SecTrustSettingsCopyCertificates(
917 SecTrustSettingsDomain domain
,
918 CFArrayRef
*certArray
)
922 TS_REQUIRED(certArray
)
927 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
928 if (result
!= errSecSuccess
) {
932 auto_ptr
<TrustSettings
>_(ts
);
934 CFMutableArrayRef outArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
937 * Keychains to search: user's search list, System.keychain, system root store,
938 * system intermediates, as appropriate
940 StorageManager::KeychainList keychains
;
945 case kSecTrustSettingsDomainUser
:
946 /* user search list */
947 globals().storageManager
.getSearchList(keychains
);
948 /* drop thru to next case */
949 case kSecTrustSettingsDomainAdmin
:
950 /* admin certs in system keychain */
951 adminKc
= globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false);
952 keychains
.push_back(adminKc
);
953 /* system-wide intermediate certs */
954 sysCertKc
= globals().storageManager
.make(SYSTEM_CERT_STORE_PATH
, false);
955 keychains
.push_back(sysCertKc
);
956 /* drop thru to next case */
957 case kSecTrustSettingsDomainSystem
:
958 /* and, for all cases, immutable system root store */
959 sysRootKc
= globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false);
960 keychains
.push_back(sysRootKc
);
962 /* already validated when we created the TrustSettings */
965 ts
->findCerts(keychains
, outArray
);
966 if(CFArrayGetCount(outArray
) == 0) {
968 return errSecNoTrustSettings
;
970 tsAddConditionalCerts(outArray
);
971 *certArray
= outArray
;
976 * Obtain an external, portable representation of the specified
977 * domain's TrustSettings. Caller must CFRelease the returned data.
979 OSStatus
SecTrustSettingsCreateExternalRepresentation(
980 SecTrustSettingsDomain domain
,
981 CFDataRef
*trustSettings
)
985 TS_REQUIRED(trustSettings
)
990 result
= TrustSettings::CreateTrustSettings(domain
, CREATE_NO
, TRIM_NO
, ts
);
991 if (result
!= errSecSuccess
) {
995 auto_ptr
<TrustSettings
>_(ts
);
997 *trustSettings
= ts
->createExternal();
998 return errSecSuccess
;
1004 * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation,
1005 * into the specified domain.
1007 OSStatus
SecTrustSettingsImportExternalRepresentation(
1008 SecTrustSettingsDomain domain
,
1009 CFDataRef trustSettings
) /* optional - NULL means empty settings */
1013 if(domain
== kSecTrustSettingsDomainSystem
) {
1014 return errSecDataNotModifiable
;
1020 result
= TrustSettings::CreateTrustSettings(domain
, trustSettings
, ts
);
1021 if (result
!= errSecSuccess
) {
1025 auto_ptr
<TrustSettings
>_(ts
);
1028 tsTrustSettingsChanged();
1029 return errSecSuccess
;