X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_keychain/Security/SecTrustSettings.cpp diff --git a/OSX/libsecurity_keychain/Security/SecTrustSettings.cpp b/OSX/libsecurity_keychain/Security/SecTrustSettings.cpp new file mode 100644 index 00000000..42dac0f1 --- /dev/null +++ b/OSX/libsecurity_keychain/Security/SecTrustSettings.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 2005,2011-2015 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * SecTrustSettings.cpp - Public interface for manipulation of Trust Settings. + * + */ + +#include "SecBridge.h" +#include "SecCertificatePriv.h" +#include "SecTrustSettings.h" +#include "SecTrustSettingsPriv.h" +#include "SecTrustSettingsCertificates.h" +#include "TrustSettingsUtils.h" +#include "TrustSettings.h" +#include "TrustSettingsSchema.h" +#include "TrustKeychains.h" +#include "Trust.h" +#include "SecKeychainPriv.h" +#include "Globals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for _CSCheckFix */ + +#define trustSettingsDbg(args...) secdebug("trustSettings", ## args) + +/* + * Ideally we'd like to implement our own lock to protect the state of the cert stores + * without grabbing the global Sec API lock, but we deal with SecCFObjects, so we'll have + * to bite the bullet and grab the big lock. We also have our own lock protecting the + * global trust settings cache which is also used by the keychain callback function + * (which does not grab the Sec API lock). + */ + +#define BEGIN_RCSAPI \ + OSStatus __secapiresult; \ + try { +#define END_RCSAPI \ + __secapiresult=errSecSuccess; \ + } \ + catch (const MacOSError &err) { __secapiresult=err.osStatus(); } \ + catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } \ + catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } \ + catch (...) { __secapiresult=errSecInternalComponent; } \ + return __secapiresult; + +#define END_RCSAPI0 \ + catch (...) {} \ + return; + + +#pragma mark --- TrustSettings preferences --- + +/* + * If Colonel Klink wants to disable user-level Trust Settings, he'll have + * to restart the apps which will be affected after he does so. We are not + * going to consult system prefs every time we do a cert evaluation. We + * consult it once per process and cache the results here. + */ +static bool tsUserTrustDisableValid = false; /* true once we consult prefs */ +static bool tsUserTrustDisable = false; /* the cached value */ + +/* + * Determine whether user-level Trust Settings disabled. + */ +static bool tsUserTrustSettingsDisabled() +{ + if(tsUserTrustDisableValid) { + return tsUserTrustDisable; + } + tsUserTrustDisable = false; + + Dictionary* dictionary = Dictionary::CreateDictionary(kSecTrustSettingsPrefsDomain, Dictionary::US_System); + if (dictionary) + { + auto_ptr prefsDict(dictionary); + /* this returns false if the pref isn't there, just like we want */ + tsUserTrustDisable = prefsDict->getBoolValue(kSecTrustSettingsDisableUserTrustSettings); + } + + tsUserTrustDisableValid = true; + return tsUserTrustDisable; +} + +#pragma mark --- TrustSettings global cache --- + +/*** + *** cache submodule - keeps per-app copy of zero or one TrustSettings + *** for each domain. Used only by SecTrustSettingsEvaluateCert() + *** and SecTrustSettingsCopyQualifiedCerts(); results of + *** manipulation by public API functions are not cached. + ***/ + +/* + * API/client code has to hold this lock when doing anything with any of + * the TrustSettings maintained here. + * It's recursive to accomodate CodeSigning's need to do cert verification + * (while we evaluate app equivalence). + */ +static ModuleNexus sutCacheLock; + +#define TRUST_SETTINGS_NUM_DOMAINS 3 + +/* + * The three global TrustSettings. + * We rely on the fact the the domain enums start with 0; we use + * the domain value as an index into the following two arrays. + */ +static TrustSettings *globalTrustSettings[TRUST_SETTINGS_NUM_DOMAINS] = + {NULL, NULL, NULL}; + +/* + * Indicates "the associated global here is currently valid; if there isn't a + * globalTrustSettings[domain], don't try to find one" + */ +static bool globalTrustSettingsValid[TRUST_SETTINGS_NUM_DOMAINS] = + {false, false, false}; + +/* remember the fact that we've registered our KC callback */ +static bool sutRegisteredCallback = false; + +static void tsRegisterCallback(); + +/* + * Assign global TrustSetting to new incoming value, which may be NULL. + * Caller holds sutCacheLock. + */ +static void tsSetGlobalTrustSettings( + TrustSettings *ts, + SecTrustSettingsDomain domain) +{ + assert(((int)domain >= 0) && ((int)domain < TRUST_SETTINGS_NUM_DOMAINS)); + + trustSettingsDbg("tsSetGlobalTrustSettings domain %d: caching TS %p old TS %p", + (int)domain, ts, globalTrustSettings[domain]); + delete globalTrustSettings[domain]; + globalTrustSettings[domain] = ts; + globalTrustSettingsValid[domain] = ts ? true : false; + tsRegisterCallback(); +} + +/* + * Obtain global TrustSettings for specified domain if it exists. + * Returns NULL if there is simply no TS for that domain. + * The TS, if returned, belongs to this cache module. + * Caller holds sutCacheLock. + */ +static TrustSettings *tsGetGlobalTrustSettings( + SecTrustSettingsDomain domain) +{ + assert(((int)domain >= 0) && ((int)domain < TRUST_SETTINGS_NUM_DOMAINS)); + + if((domain == kSecTrustSettingsDomainUser) && tsUserTrustSettingsDisabled()) { + trustSettingsDbg("tsGetGlobalTrustSettings: skipping DISABLED user domain"); + return NULL; + } + + if(globalTrustSettingsValid[domain]) { + // ready or not, use this + return globalTrustSettings[domain]; + } + assert(globalTrustSettings[domain] == NULL); + + /* try to find one */ + OSStatus result = errSecSuccess; + TrustSettings *ts = NULL; + /* don't create; trim if found */ + result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_YES, ts); + if ( (domain != kSecTrustSettingsDomainSystem) + && (result == errSecInternalComponent)) { + /* + * Could not connect to ocspd to get the user/admin domain trust settings + * This happens in single user mode for example. + * Valid flag is set to false and continue. + */ + trustSettingsDbg("tsGetGlobalTrustSettings: could not connect to ocspd for domain (%d)",(int)domain); + globalTrustSettingsValid[domain] = false; + tsRegisterCallback(); + return NULL; + } + else if (result == errSecNoTrustSettings) { + /* + * No TrustSettings for this domain, actually a fairly common case. + * Optimize: don't bother trying this again. + */ + trustSettingsDbg("tsGetGlobalTrustSettings: flagging known NULL"); + globalTrustSettingsValid[domain] = true; + tsRegisterCallback(); + return NULL; + } + else if(result != errSecSuccess) { + /* gross error */ + MacOSError::throwMe(result); + } + + tsSetGlobalTrustSettings(ts, domain); + return ts; +} + +/* + * Purge TrustSettings cache. + * Called by Keychain Event callback and by our API functions that + * modify trust settings. + * Caller can NOT hold sutCacheLock. + */ +static void tsPurgeCache() +{ + int domain; + + StLock _(sutCacheLock()); + trustSettingsDbg("tsPurgeCache"); + for(domain=0; domainpid == getpid()) { + /* + * Avoid dup cache invalidates: we already dealt with this event. + */ + trustSettingsDbg("cacheEventCallback: our pid, skipping"); + } + else { + tsPurgeCache(); + } + return errSecSuccess; +} + +/* + * Ensure that we've registered for kSecTrustSettingsChangedEvent callbacks + */ +static void tsRegisterCallback() +{ + if(sutRegisteredCallback) { + return; + } + trustSettingsDbg("tsRegisterCallback: registering callback"); + OSStatus ortn = SecKeychainAddCallback(tsTrustSettingsCallback, + kSecTrustSettingsChangedEventMask, NULL); + if(ortn) { + trustSettingsDbg("tsRegisterCallback: SecKeychainAddCallback returned %d", (int)ortn); + /* Not sure how this could ever happen - maybe if there is no run loop active? */ + } + sutRegisteredCallback = true; +} + +#pragma mark --- Static functions --- + + +/* + * Called by API code when a trust list has changed; we notify other processes + * and purge our own cache. + */ +static void tsTrustSettingsChanged() +{ + tsPurgeCache(); + + /* The only interesting data is our pid */ + NameValueDictionary nvd; + pid_t ourPid = getpid(); + nvd.Insert (new NameValuePair (PID_KEY, + CssmData (reinterpret_cast(&ourPid), sizeof (pid_t)))); + CssmData data; + nvd.Export (data); + + trustSettingsDbg("tsTrustSettingsChanged: posting notification"); + SecurityServer::ClientSession cs (Allocator::standard(), Allocator::standard()); + cs.postNotification (SecurityServer::kNotificationDomainDatabase, + kSecTrustSettingsChangedEvent, data); + free (data.data ()); +} + +/* + * Common code for SecTrustSettingsCopyTrustSettings(), + * SecTrustSettingsCopyModificationDate(). + */ +static OSStatus tsCopyTrustSettings( + SecCertificateRef cert, + SecTrustSettingsDomain domain, + CFArrayRef *trustSettings, /* optionally RETURNED */ + CFDateRef *modDate) /* optionally RETURNED */ +{ + BEGIN_RCSAPI + + TS_REQUIRED(cert) + + /* obtain fresh full copy from disk */ + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts); + + // rather than throw these results, just return them because we are at the top level + if (result == errSecNoTrustSettings) { + return errSecItemNotFound; + } + else if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); // make sure this gets deleted just in case something throws underneath + + if(trustSettings) { + *trustSettings = ts->copyTrustSettings(cert); + } + if(modDate) { + *modDate = ts->copyModDate(cert); + } + + END_RCSAPI +} + +static void tsAddConditionalCerts(CFMutableArrayRef certArray); + +/* + * Common code for SecTrustSettingsCopyQualifiedCerts() and + * SecTrustSettingsCopyUnrestrictedRoots(). + */ +static OSStatus tsCopyCertsCommon( + /* usage constraints, all optional */ + const CSSM_OID *policyOID, + const char *policyString, + SecTrustSettingsKeyUsage keyUsage, + /* constrain to only roots */ + bool onlyRoots, + /* per-domain enables */ + bool user, + bool admin, + bool system, + CFArrayRef *certArray) /* RETURNED */ +{ + StLock _TC(sutCacheLock()); + StLock _TK(SecTrustKeychainsGetMutex()); + + TS_REQUIRED(certArray) + + /* this relies on the domain enums being numbered 0..2, user..system */ + bool domainEnable[3] = {user, admin, system}; + + /* we'll retain it again before successful exit */ + CFRef outArray(CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks)); + + /* + * Search all keychains - user's, System.keychain, system root store, + * system intermdiates as appropriate + */ + StorageManager::KeychainList keychains; + Keychain adminKc; + if(user) { + globals().storageManager.getSearchList(keychains); + } + if(user || admin) { + adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false); + keychains.push_back(adminKc); + } + Keychain sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false); + keychains.push_back(sysRootKc); + Keychain sysCertKc = globals().storageManager.make(SYSTEM_CERT_STORE_PATH, false); + keychains.push_back(sysCertKc); + + assert(kSecTrustSettingsDomainUser == 0); + for(unsigned domain=0; domainfindQualifiedCerts(keychains, + false, /* !findAll */ + onlyRoots, + policyOID, policyString, keyUsage, + outArray); + } + if (system) { + tsAddConditionalCerts(outArray); + } + *certArray = outArray; + CFRetain(*certArray); + trustSettingsDbg("tsCopyCertsCommon: %ld certs found", + CFArrayGetCount(outArray)); + return errSecSuccess; +} + +#if TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE && !TARGET_OS_NANO +/* + * _CSCheckFix is implemented in CarbonCore and exported via CoreServices. + * To avoid a circular dependency with Security, load this symbol dynamically. + */ +typedef Boolean (*CSCheckFix_f)(CFStringRef str); + +static dispatch_once_t sTSInitializeOnce = 0; +static void * sCSCheckFixLibrary = NULL; +static CSCheckFix_f sCSCheckFix_f = NULL; + +static OSStatus _tsEnsuredInitialized(void); + +static OSStatus _tsEnsuredInitialized(void) +{ + __block OSStatus status = errSecNotAvailable; + + dispatch_once(&sTSInitializeOnce, ^{ + sCSCheckFixLibrary = dlopen("/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL); + assert(sCSCheckFixLibrary); + if (sCSCheckFixLibrary) { + sCSCheckFix_f = (CSCheckFix_f)(uintptr_t) dlsym(sCSCheckFixLibrary, "_CSCheckFix"); + } + }); + + if (sCSCheckFix_f) { + status = noErr; + } + return status; +} + +#define APPNAMEWORKAROUND_KEY CFSTR("WorkaroundAppNames") +#define APPNAMEWORKAROUND_DOMAIN CFSTR("com.apple.security") + +static bool tsCheckAppNameWorkaround(const char *name) +{ + bool result = false; + CFIndex idx, count; + CFStringRef str = + CFStringCreateWithCString (NULL, name, kCFStringEncodingUTF8); + CFArrayRef value = (CFArrayRef) + CFPreferencesCopyValue (APPNAMEWORKAROUND_KEY, + APPNAMEWORKAROUND_DOMAIN, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + if (!str || !value || + !(CFArrayGetTypeID() == CFGetTypeID(value))) { + goto cleanup; + } + count = CFArrayGetCount(value); + for (idx = 0; idx < count; idx++) { + CFStringRef appstr = (CFStringRef) CFArrayGetValueAtIndex(value, idx); + if (!(appstr) || !(CFStringGetTypeID() == CFGetTypeID(appstr))) { + continue; + } + if (!CFStringCompare(str, appstr, 0)) { + result = true; + break; + } + } + +cleanup: + if (str) { + CFRelease(str); + } + if (value) { + CFRelease(value); + } + return result; +} +#endif + +static void tsAddConditionalCerts(CFMutableArrayRef certArray) +{ +#if TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE && !TARGET_OS_NANO + struct certmap_entry_s { + const UInt8* data; + const CFIndex length; + }; + typedef struct certmap_entry_s certmap_entry_t; + + if (!certArray) { return; } + + pid_t pid = getpid(); + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + int ret = proc_name(pid, pathbuf, sizeof(pathbuf)); + if (ret <= 0) { return; } + + OSStatus status = _tsEnsuredInitialized(); + if ((status == 0 && sCSCheckFix_f(CFSTR("21946795"))) || + tsCheckAppNameWorkaround(pathbuf)) { + // conditionally include these 1024-bit roots + const certmap_entry_t certmap[] = { + { _GTECyberTrustGlobalRootCA, sizeof(_GTECyberTrustGlobalRootCA) }, + { _VeriSignClass3CA, sizeof(_VeriSignClass3CA) }, + }; + unsigned int i, certmaplen = sizeof(certmap) / sizeof(certmap_entry_t); + for (i=0; i _(sutCacheLock()); + + TS_REQUIRED(certHashStr) + TS_REQUIRED(foundDomain) + TS_REQUIRED(allowedErrors) + TS_REQUIRED(numAllowedErrors) + TS_REQUIRED(resultType) + TS_REQUIRED(foundMatchingEntry) + TS_REQUIRED(foundAnyEntry) + + /* ensure a NULL_terminated string */ + auto_array polStr; + if(policyString != NULL && policyStringLen > 0) { + polStr.allocate(policyStringLen + 1); + memmove(polStr.get(), policyString, policyStringLen); + if(policyString[policyStringLen - 1] != '\0') { + (polStr.get())[policyStringLen] = '\0'; + } + } + + /* initial condition - this can grow if we inspect multiple TrustSettings */ + *allowedErrors = NULL; + *numAllowedErrors = 0; + + /* + * This loop relies on the ordering of the SecTrustSettingsDomain enum: + * search user first, then admin, then system. + */ + assert(kSecTrustSettingsDomainAdmin == (kSecTrustSettingsDomainUser + 1)); + assert(kSecTrustSettingsDomainSystem == (kSecTrustSettingsDomainAdmin + 1)); + bool foundAny = false; + for(unsigned domain=kSecTrustSettingsDomainUser; + domain<=kSecTrustSettingsDomainSystem; + domain++) { + TrustSettings *ts = tsGetGlobalTrustSettings(domain); + if(ts == NULL) { + continue; + } + + /* validate cert returns true if matching entry was found */ + bool foundAnyHere = false; + bool found = ts->evaluateCert(certHashStr, policyOID, + polStr.get(), keyUsage, isRootCert, + allowedErrors, numAllowedErrors, resultType, &foundAnyHere); + + if(found) { + /* + * Note this, even though we may overwrite it later if this + * is an Unspecified entry and we find a definitive entry + * later + */ + *foundDomain = domain; + } + if(found && (*resultType != kSecTrustSettingsResultUnspecified)) { + trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain); + *foundAnyEntry = true; + *foundMatchingEntry = true; + return errSecSuccess; + } + foundAny |= foundAnyHere; + } + trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND"); + *foundAnyEntry = foundAny; + *foundMatchingEntry = false; + return errSecSuccess; + END_RCSAPI +} + +/* + * Obtain trusted certs which match specified usage. + * Only certs with a SecTrustSettingsResult of + * kSecTrustSettingsResultTrustRoot or + * or kSecTrustSettingsResultTrustAsRoot will be returned. + * To be used by SecureTransport for its SSLSetTrustedRoots() call; + * I hope nothing else has to use this... + * Caller must CFRelease the returned CFArrayRef. + */ +OSStatus SecTrustSettingsCopyQualifiedCerts( + const CSSM_OID *policyOID, + const char *policyString, /* optional */ + uint32 policyStringLen, + SecTrustSettingsKeyUsage keyUsage, /* optional */ + CFArrayRef *certArray) /* RETURNED */ +{ + BEGIN_RCSAPI + + /* ensure a NULL_terminated string */ + auto_array polStr; + if(policyString != NULL) { + polStr.allocate(policyStringLen + 1); + memmove(polStr.get(), policyString, policyStringLen); + if(policyString[policyStringLen - 1] != '\0') { + (polStr.get())[policyStringLen] = '\0'; + } + } + + return tsCopyCertsCommon(policyOID, polStr.get(), keyUsage, + false, /* !onlyRoots */ + true, true, true, /* all domains */ + certArray); + + END_RCSAPI +} + +/* + * Obtain unrestricted root certs from the specified domain(s). + * Only returns roots with no usage constraints. + * Caller must CFRelease the returned CFArrayRef. + */ +OSStatus SecTrustSettingsCopyUnrestrictedRoots( + Boolean user, + Boolean admin, + Boolean system, + CFArrayRef *certArray) /* RETURNED */ +{ + BEGIN_RCSAPI + + return tsCopyCertsCommon(NULL, NULL, NULL, /* no constraints */ + true, /* onlyRoots */ + user, admin, system, + certArray); + + END_RCSAPI +} + +static const char hexChars[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +/* + * Obtain a string representing a cert's SHA1 digest. This string is + * the key used to look up per-cert trust settings in a TrustSettings record. + */ +CFStringRef SecTrustSettingsCertHashStrFromCert( + SecCertificateRef certRef) +{ + if(certRef == NULL) { + return NULL; + } + + if(certRef == kSecTrustSettingsDefaultRootCertSetting) { + /* use this string instead of the cert hash as the dictionary key */ + trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting"); + return kSecTrustRecordDefaultRootCert; + } + + CSSM_DATA certData; + OSStatus ortn = SecCertificateGetData(certRef, &certData); + if(ortn) { + return NULL; + } + return SecTrustSettingsCertHashStrFromData(certData.Data, certData.Length); +} + +CFStringRef SecTrustSettingsCertHashStrFromData( + const void *cert, + size_t certLen) +{ + unsigned char digest[CC_SHA1_DIGEST_LENGTH]; + char asciiDigest[(2 * CC_SHA1_DIGEST_LENGTH) + 1]; + unsigned dex; + char *outp = asciiDigest; + unsigned char *inp = digest; + + if(cert == NULL) { + return NULL; + } + + CC_SHA1(cert, (CC_LONG)certLen, digest); + + for(dex=0; dex>= 4; + outp[0] = hexChars[c]; + outp += 2; + } + *outp = 0; + return CFStringCreateWithCString(NULL, asciiDigest, kCFStringEncodingASCII); +} + +/* + * Add a cert's TrustSettings to a non-persistent TrustSettings record. + * No locking or cache flushing here; it's all local to the TrustSettings + * we construct here. + */ +OSStatus SecTrustSettingsSetTrustSettingsExternal( + CFDataRef settingsIn, /* optional */ + SecCertificateRef certRef, /* optional */ + CFTypeRef trustSettingsDictOrArray, /* optional */ + CFDataRef *settingsOut) /* RETURNED */ +{ + BEGIN_RCSAPI + + TS_REQUIRED(settingsOut) + + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(kSecTrustSettingsDomainMemory, settingsIn, ts); + if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); + + if(certRef != NULL) { + ts->setTrustSettings(certRef, trustSettingsDictOrArray); + } + *settingsOut = ts->createExternal(); + return errSecSuccess; + + END_RCSAPI +} + +#pragma mark --- API functions --- + +OSStatus SecTrustSettingsCopyTrustSettings( + SecCertificateRef certRef, + SecTrustSettingsDomain domain, + CFArrayRef *trustSettings) /* RETURNED */ +{ + TS_REQUIRED(certRef) + TS_REQUIRED(trustSettings) + + OSStatus result = tsCopyTrustSettings(certRef, domain, trustSettings, NULL); + if (result == errSecSuccess && *trustSettings == NULL) { + result = errSecItemNotFound; /* documented result if no trust settings exist */ + } + return result; +} + +OSStatus SecTrustSettingsCopyModificationDate( + SecCertificateRef certRef, + SecTrustSettingsDomain domain, + CFDateRef *modificationDate) /* RETURNED */ +{ + TS_REQUIRED(certRef) + TS_REQUIRED(modificationDate) + + OSStatus result = tsCopyTrustSettings(certRef, domain, NULL, modificationDate); + if (result == errSecSuccess && *modificationDate == NULL) { + result = errSecItemNotFound; /* documented result if no trust settings exist */ + } + return result; +} + +/* works with existing and with new cert */ +OSStatus SecTrustSettingsSetTrustSettings( + SecCertificateRef certRef, + SecTrustSettingsDomain domain, + CFTypeRef trustSettingsDictOrArray) +{ + BEGIN_RCSAPI + + TS_REQUIRED(certRef) + + if(domain == kSecTrustSettingsDomainSystem) { + return errSecDataNotModifiable; + } + + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(domain, CREATE_YES, TRIM_NO, ts); + if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); + + ts->setTrustSettings(certRef, trustSettingsDictOrArray); + ts->flushToDisk(); + tsTrustSettingsChanged(); + return errSecSuccess; + + END_RCSAPI +} + +OSStatus SecTrustSettingsRemoveTrustSettings( + SecCertificateRef cert, + SecTrustSettingsDomain domain) +{ + BEGIN_RCSAPI + + TS_REQUIRED(cert) + + if(domain == kSecTrustSettingsDomainSystem) { + return errSecDataNotModifiable; + } + + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts); + if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); + + /* deleteTrustSettings throws if record not found */ + trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d", + (int)domain); + ts->deleteTrustSettings(cert); + ts->flushToDisk(); + tsTrustSettingsChanged(); + return errSecSuccess; + + END_RCSAPI +} + +/* get all certs listed in specified domain */ +OSStatus SecTrustSettingsCopyCertificates( + SecTrustSettingsDomain domain, + CFArrayRef *certArray) +{ + BEGIN_RCSAPI + + TS_REQUIRED(certArray) + + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts); + if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); + + CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + /* + * Keychains to search: user's search list, System.keychain, system root store, + * system intermediates, as appropriate + */ + StorageManager::KeychainList keychains; + Keychain adminKc; + Keychain sysCertKc; + Keychain sysRootKc; + switch(domain) { + case kSecTrustSettingsDomainUser: + /* user search list */ + globals().storageManager.getSearchList(keychains); + /* drop thru to next case */ + case kSecTrustSettingsDomainAdmin: + /* admin certs in system keychain */ + adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false); + keychains.push_back(adminKc); + /* system-wide intermediate certs */ + sysCertKc = globals().storageManager.make(SYSTEM_CERT_STORE_PATH, false); + keychains.push_back(sysCertKc); + /* drop thru to next case */ + case kSecTrustSettingsDomainSystem: + /* and, for all cases, immutable system root store */ + sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false); + keychains.push_back(sysRootKc); + default: + /* already validated when we created the TrustSettings */ + break; + } + ts->findCerts(keychains, outArray); + if(CFArrayGetCount(outArray) == 0) { + CFRelease(outArray); + return errSecNoTrustSettings; + } + tsAddConditionalCerts(outArray); + *certArray = outArray; + END_RCSAPI +} + +/* + * Obtain an external, portable representation of the specified + * domain's TrustSettings. Caller must CFRelease the returned data. + */ +OSStatus SecTrustSettingsCreateExternalRepresentation( + SecTrustSettingsDomain domain, + CFDataRef *trustSettings) +{ + BEGIN_RCSAPI + + TS_REQUIRED(trustSettings) + + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts); + if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); + + *trustSettings = ts->createExternal(); + return errSecSuccess; + + END_RCSAPI +} + +/* + * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation, + * into the specified domain. + */ +OSStatus SecTrustSettingsImportExternalRepresentation( + SecTrustSettingsDomain domain, + CFDataRef trustSettings) /* optional - NULL means empty settings */ +{ + BEGIN_RCSAPI + + if(domain == kSecTrustSettingsDomainSystem) { + return errSecDataNotModifiable; + } + + OSStatus result; + TrustSettings* ts; + + result = TrustSettings::CreateTrustSettings(domain, trustSettings, ts); + if (result != errSecSuccess) { + return result; + } + + auto_ptr_(ts); + + ts->flushToDisk(); + tsTrustSettingsChanged(); + return errSecSuccess; + + END_RCSAPI +} +