SecTrustSettings.cpp [plain text]
#include "SecBridge.h"
#include "SecTrustSettings.h"
#include "SecTrustSettingsPriv.h"
#include "TrustSettingsUtils.h"
#include "TrustSettings.h"
#include "TrustSettingsSchema.h"
#include "TrustKeychains.h"
#include "Trust.h"
#include "SecKeychainPriv.h"
#include "Globals.h"
#include <security_utilities/threading.h>
#include <security_utilities/globalizer.h>
#include <security_utilities/errors.h>
#include <security_cdsa_utilities/cssmerrors.h>
#include <security_utilities/logging.h>
#include <security_utilities/debugging.h>
#include <security_utilities/simpleprefs.h>
#include <securityd_client/dictionary.h>
#include <securityd_client/ssclient.h>
#include <assert.h>
#include <vector>
#include <CommonCrypto/CommonDigest.h>
#define trustSettingsDbg(args...) secdebug("trustSettings", ## args)
#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 ---
static bool tsUserTrustDisableValid = false;
static bool tsUserTrustDisable = false;
static bool tsUserTrustSettingsDisabled()
{
if(tsUserTrustDisableValid) {
return tsUserTrustDisable;
}
tsUserTrustDisable = false;
Dictionary* dictionary = Dictionary::CreateDictionary(kSecTrustSettingsPrefsDomain, Dictionary::US_System);
if (dictionary)
{
auto_ptr<Dictionary> prefsDict(dictionary);
tsUserTrustDisable = prefsDict->getBoolValue(kSecTrustSettingsDisableUserTrustSettings);
}
tsUserTrustDisableValid = true;
return tsUserTrustDisable;
}
#pragma mark --- TrustSettings global cache ---
static ModuleNexus<RecursiveMutex> sutCacheLock;
#define TRUST_SETTINGS_NUM_DOMAINS 3
static TrustSettings *globalTrustSettings[TRUST_SETTINGS_NUM_DOMAINS] =
{NULL, NULL, NULL};
static bool globalTrustSettingsValid[TRUST_SETTINGS_NUM_DOMAINS] =
{false, false, false};
static bool sutRegisteredCallback = false;
static void tsRegisterCallback();
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();
}
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]) {
return globalTrustSettings[domain];
}
assert(globalTrustSettings[domain] == NULL);
OSStatus result = errSecSuccess;
TrustSettings *ts = NULL;
result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_YES, ts);
if(result != errSecSuccess && result != errSecNoTrustSettings) {
MacOSError::throwMe(result);
}
else if (result != errSecSuccess) {
trustSettingsDbg("tsGetGlobalTrustSettings: flagging known NULL");
globalTrustSettingsValid[domain] = true;
tsRegisterCallback();
return NULL;
}
tsSetGlobalTrustSettings(ts, domain);
return ts;
}
static void tsPurgeCache()
{
int domain;
StLock<Mutex> _(sutCacheLock());
trustSettingsDbg("tsPurgeCache");
for(domain=0; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
tsSetGlobalTrustSettings(NULL, domain);
}
}
static OSStatus tsTrustSettingsCallback (
SecKeychainEvent keychainEvent,
SecKeychainCallbackInfo *info,
void *context)
{
trustSettingsDbg("tsTrustSettingsCallback, event %d", (int)keychainEvent);
if(keychainEvent != kSecTrustSettingsChangedEvent) {
return errSecSuccess;
}
if(info->pid == getpid()) {
trustSettingsDbg("cacheEventCallback: our pid, skipping");
}
else {
tsPurgeCache();
}
return errSecSuccess;
}
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);
}
sutRegisteredCallback = true;
}
#pragma mark --- Static functions ---
static void tsTrustSettingsChanged()
{
tsPurgeCache();
NameValueDictionary nvd;
pid_t ourPid = getpid();
nvd.Insert (new NameValuePair (PID_KEY,
CssmData (reinterpret_cast<void*>(&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 ());
}
static OSStatus tsCopyTrustSettings(
SecCertificateRef cert,
SecTrustSettingsDomain domain,
CFArrayRef *trustSettings,
CFDateRef *modDate)
{
BEGIN_RCSAPI
TS_REQUIRED(cert)
OSStatus result;
TrustSettings* ts;
result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
if (result == errSecNoTrustSettings) {
return errSecItemNotFound;
}
else if (result != errSecSuccess) {
return result;
}
auto_ptr<TrustSettings>_(ts);
if(trustSettings) {
*trustSettings = ts->copyTrustSettings(cert);
}
if(modDate) {
*modDate = ts->copyModDate(cert);
}
END_RCSAPI
}
static OSStatus tsCopyCertsCommon(
const CSSM_OID *policyOID,
const char *policyString,
SecTrustSettingsKeyUsage keyUsage,
bool onlyRoots,
bool user,
bool admin,
bool system,
CFArrayRef *certArray)
{
StLock<Mutex> _TC(sutCacheLock());
StLock<Mutex> _TK(SecTrustKeychainsGetMutex());
TS_REQUIRED(certArray)
bool domainEnable[3] = {user, admin, system};
CFRef<CFMutableArrayRef> outArray(CFArrayCreateMutable(NULL, 0,
&kCFTypeArrayCallBacks));
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; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
if(!domainEnable[domain]) {
continue;
}
TrustSettings *ts = tsGetGlobalTrustSettings(domain);
if(ts == NULL) {
continue;
}
ts->findQualifiedCerts(keychains,
false,
onlyRoots,
policyOID, policyString, keyUsage,
outArray);
}
*certArray = outArray;
CFRetain(*certArray);
trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
CFArrayGetCount(outArray));
return errSecSuccess;
}
#pragma mark --- SPI functions ---
OSStatus SecTrustSettingsEvaluateCert(
CFStringRef certHashStr,
const CSSM_OID *policyOID,
const char *policyString,
uint32 policyStringLen,
SecTrustSettingsKeyUsage keyUsage,
bool isRootCert,
SecTrustSettingsDomain *foundDomain,
CSSM_RETURN **allowedErrors,
uint32 *numAllowedErrors,
SecTrustSettingsResult *resultType,
bool *foundMatchingEntry,
bool *foundAnyEntry)
{
BEGIN_RCSAPI
StLock<Mutex> _(sutCacheLock());
TS_REQUIRED(certHashStr)
TS_REQUIRED(foundDomain)
TS_REQUIRED(allowedErrors)
TS_REQUIRED(numAllowedErrors)
TS_REQUIRED(resultType)
TS_REQUIRED(foundMatchingEntry)
TS_REQUIRED(foundAnyEntry)
auto_array<char> polStr;
if(policyString != NULL && policyStringLen > 0) {
polStr.allocate(policyStringLen + 1);
memmove(polStr.get(), policyString, policyStringLen);
if(policyString[policyStringLen - 1] != '\0') {
(polStr.get())[policyStringLen] = '\0';
}
}
*allowedErrors = NULL;
*numAllowedErrors = 0;
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;
}
bool foundAnyHere = false;
bool found = ts->evaluateCert(certHashStr, policyOID,
polStr.get(), keyUsage, isRootCert,
allowedErrors, numAllowedErrors, resultType, &foundAnyHere);
if(found) {
*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
}
OSStatus SecTrustSettingsCopyQualifiedCerts(
const CSSM_OID *policyOID,
const char *policyString,
uint32 policyStringLen,
SecTrustSettingsKeyUsage keyUsage,
CFArrayRef *certArray)
{
BEGIN_RCSAPI
auto_array<char> 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,
true, true, true,
certArray);
END_RCSAPI
}
OSStatus SecTrustSettingsCopyUnrestrictedRoots(
Boolean user,
Boolean admin,
Boolean system,
CFArrayRef *certArray)
{
BEGIN_RCSAPI
return tsCopyCertsCommon(NULL, NULL, NULL,
true,
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'
};
CFStringRef SecTrustSettingsCertHashStrFromCert(
SecCertificateRef certRef)
{
if(certRef == NULL) {
return NULL;
}
if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
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<CC_SHA1_DIGEST_LENGTH; dex++) {
unsigned c = *inp++;
outp[1] = hexChars[c & 0xf];
c >>= 4;
outp[0] = hexChars[c];
outp += 2;
}
*outp = 0;
return CFStringCreateWithCString(NULL, asciiDigest, kCFStringEncodingASCII);
}
OSStatus SecTrustSettingsSetTrustSettingsExternal(
CFDataRef settingsIn,
SecCertificateRef certRef,
CFTypeRef trustSettingsDictOrArray,
CFDataRef *settingsOut)
{
BEGIN_RCSAPI
TS_REQUIRED(settingsOut)
OSStatus result;
TrustSettings* ts;
result = TrustSettings::CreateTrustSettings(kSecTrustSettingsDomainMemory, settingsIn, ts);
if (result != errSecSuccess) {
return result;
}
auto_ptr<TrustSettings>_(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)
{
TS_REQUIRED(trustSettings)
OSStatus result = tsCopyTrustSettings(certRef, domain, trustSettings, NULL);
if (result == errSecSuccess && *trustSettings == NULL) {
result = errSecItemNotFound;
}
return result;
}
OSStatus SecTrustSettingsCopyModificationDate(
SecCertificateRef certRef,
SecTrustSettingsDomain domain,
CFDateRef *modificationDate)
{
TS_REQUIRED(modificationDate)
OSStatus result = tsCopyTrustSettings(certRef, domain, NULL, modificationDate);
if (result == errSecSuccess && *modificationDate == NULL) {
result = errSecItemNotFound;
}
return result;
}
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<TrustSettings>_(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<TrustSettings>_(ts);
trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
(int)domain);
ts->deleteTrustSettings(cert);
ts->flushToDisk();
tsTrustSettingsChanged();
return errSecSuccess;
END_RCSAPI
}
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<TrustSettings>_(ts);
CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
StorageManager::KeychainList keychains;
Keychain adminKc;
Keychain sysCertKc;
Keychain sysRootKc;
switch(domain) {
case kSecTrustSettingsDomainUser:
globals().storageManager.getSearchList(keychains);
case kSecTrustSettingsDomainAdmin:
adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
keychains.push_back(adminKc);
sysCertKc = globals().storageManager.make(SYSTEM_CERT_STORE_PATH, false);
keychains.push_back(sysCertKc);
case kSecTrustSettingsDomainSystem:
sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
keychains.push_back(sysRootKc);
default:
break;
}
ts->findCerts(keychains, outArray);
if(CFArrayGetCount(outArray) == 0) {
CFRelease(outArray);
return errSecNoTrustSettings;
}
*certArray = outArray;
END_RCSAPI
}
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<TrustSettings>_(ts);
*trustSettings = ts->createExternal();
return errSecSuccess;
END_RCSAPI
}
OSStatus SecTrustSettingsImportExternalRepresentation(
SecTrustSettingsDomain domain,
CFDataRef trustSettings)
{
BEGIN_RCSAPI
if(domain == kSecTrustSettingsDomainSystem) {
return errSecDataNotModifiable;
}
OSStatus result;
TrustSettings* ts;
result = TrustSettings::CreateTrustSettings(domain, trustSettings, ts);
if (result != errSecSuccess) {
return result;
}
auto_ptr<TrustSettings>_(ts);
ts->flushToDisk();
tsTrustSettingsChanged();
return errSecSuccess;
END_RCSAPI
}