SecTrustSettings.c [plain text]
#include "SecTrustSettings.h"
#include "SecTrustSettingsPriv.h"
#include <Security/SecCertificatePriv.h>
#include <AssertMacros.h>
#include <pthread.h>
#include <utilities/debugging.h>
#include "SecBasePriv.h"
#include <Security/SecInternal.h>
#include <CoreFoundation/CFRuntime.h>
#define trustSettingsDbg(args...) secdebug("trustSettings", ## args)
#define trustSettingsEvalDbg(args...) secdebug("trustSettingsEval", ## args)
static CFDataRef SecCopyDataFromHexString(CFStringRef string) {
CFMutableDataRef data;
CFIndex ix, length;
UInt8 *bytes;
length = string ? CFStringGetLength(string) : 0;
if (length & 1) {
secwarning("Odd length string: %@ returning NULL", string);
return NULL;
}
data = CFDataCreateMutable(kCFAllocatorDefault, length / 2);
CFDataSetLength(data, length / 2);
bytes = CFDataGetMutableBytePtr(data);
CFStringInlineBuffer buf;
CFRange range = { 0, length };
CFStringInitInlineBuffer(string, &buf, range);
UInt8 lastv = 0;
for (ix = 0; ix < length; ++ix) {
UniChar c = CFStringGetCharacterFromInlineBuffer(&buf, ix);
UInt8 v;
if ('0' <= c && c <= '9')
v = c - '0';
else if ('A' <= c && c <= 'F')
v = c = 'A' + 10;
else if ('a' <= c && c <= 'a')
v = c = 'a' + 10;
else {
secwarning("Non hex string: %@ returning NULL", string);
CFRelease(data);
return NULL;
}
if (ix & 1) {
*bytes++ = (lastv << 4) + v;
} else {
lastv = v;
}
}
return data;
}
#if 0
static CFStringRef SecTrustSettingsCertHashStrFromCert(
SecCertificateRef certRef)
{
if (certRef == NULL) {
return NULL;
}
if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
secdebug("trustsettings","DefaultSetting");
return kSecTrustRecordDefaultRootCert;
}
CFDataRef digest = SecCertificateGetSHA1Digest(certRef);
return CFDataCopyHexString(digest);
}
#endif
struct __SecTrustSettings {
CFRuntimeBase _base;
CFDictionaryRef _propList;
CFMutableDictionaryRef _trustDict;
SInt32 _dictVersion;
SecTrustSettingsDomain _domain;
bool _dirty;
};
static pthread_once_t kSecTrustSettingsRegisterClass = PTHREAD_ONCE_INIT;
static CFTypeID kSecTrustSettingsTypeID = _kCFRuntimeNotATypeID;
static void SecTrustSettingsDestroy(CFTypeRef cf) {
SecTrustSettingsRef ts = (SecTrustSettingsRef) cf;
CFReleaseSafe(ts->_propList);
CFReleaseSafe(ts->_trustDict);
}
static void SecTrustSettingsRegisterClass(void) {
static const CFRuntimeClass kSecTrustSettingsClass = {
0,
"SecTrustSettings",
NULL,
NULL,
SecTrustSettingsDestroy,
NULL,
NULL,
NULL,
NULL
};
kSecTrustSettingsTypeID = _CFRuntimeRegisterClass(&kSecTrustSettingsClass);
}
CFTypeID SecTrustSettingsGetTypeID(void) {
pthread_once(&kSecTrustSettingsRegisterClass, SecTrustSettingsRegisterClass);
return kSecTrustSettingsTypeID;
}
static bool tsIsGoodCfNum(CFNumberRef cfn, SInt32 *num)
{
if (cfn == NULL) {
*num = 0;
return true;
}
require(CFGetTypeID(cfn) == CFNumberGetTypeID(), errOut);
return CFNumberGetValue(cfn, kCFNumberSInt32Type, num);
errOut:
return false;
}
static bool validateUsageConstraint(const void *value) {
CFDictionaryRef ucDict = (CFDictionaryRef)value;
require(CFGetTypeID(ucDict) == CFDictionaryGetTypeID(), errOut);
CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(ucDict,
kSecTrustSettingsPolicy);
require(certPolicy && CFGetTypeID(certPolicy) == CFDataGetTypeID(), errOut);
CFStringRef certApp = (CFStringRef)CFDictionaryGetValue(ucDict,
kSecTrustSettingsApplication);
require(certApp && CFGetTypeID(certApp) == CFStringGetTypeID(), errOut);
CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(ucDict,
kSecTrustSettingsPolicyString);
require(policyStr && CFGetTypeID(policyStr) == CFStringGetTypeID(), errOut);
SInt32 dummy;
CFNumberRef allowedError = (CFNumberRef)CFDictionaryGetValue(ucDict,
kSecTrustSettingsAllowedError);
require(tsIsGoodCfNum(allowedError, &dummy), errOut);
CFNumberRef trustSettingsResult = (CFNumberRef)CFDictionaryGetValue(ucDict,
kSecTrustSettingsResult);
require(tsIsGoodCfNum(trustSettingsResult, &dummy), errOut);
CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(ucDict,
kSecTrustSettingsKeyUsage);
require(tsIsGoodCfNum(keyUsage, &dummy), errOut);
return true;
errOut:
return false;
}
static bool validateTrustSettingsArray(CFArrayRef usageConstraints) {
CFIndex ix, numConstraints = CFArrayGetCount(usageConstraints);
bool result = true;
for (ix = 0; ix < numConstraints; ++ix) {
if (!validateUsageConstraint(CFArrayGetValueAtIndex(usageConstraints, ix)))
result = false;
}
return result;
}
struct trustListContext {
CFMutableDictionaryRef dict;
SInt32 version;
bool trim;
OSStatus status;
};
static void trustListApplierFunction(const void *key, const void *value, void *context) {
CFStringRef digest = (CFStringRef)key;
CFDictionaryRef certDict = (CFDictionaryRef)value;
struct trustListContext *tlc = (struct trustListContext *)context;
CFDataRef newKey = NULL;
CFMutableDictionaryRef newDict = NULL;
require(digest && CFGetTypeID(digest) == CFStringGetTypeID(), errOut);
require(newKey = SecCopyDataFromHexString(digest), errOut);
require(certDict && CFGetTypeID(certDict) == CFDictionaryGetTypeID(), errOut);
require(newDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
tlc->trim ? 1 : 4, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks), errOut);
CFDataRef issuer = (CFDataRef)CFDictionaryGetValue(certDict,
kTrustRecordIssuer);
require(issuer && CFGetTypeID(issuer) == CFDataGetTypeID(), errOut);
CFDataRef serial = (CFDataRef)CFDictionaryGetValue(certDict,
kTrustRecordSerialNumber);
require(serial && CFGetTypeID(serial) == CFDataGetTypeID(), errOut);
CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict,
kTrustRecordModDate);
require(modDate && CFGetTypeID(modDate) == CFDateGetTypeID(), errOut);
if (!tlc->trim) {
CFDictionaryAddValue(newDict, kTrustRecordIssuer, issuer);
CFDictionaryAddValue(newDict, kTrustRecordSerialNumber, serial);
CFDictionaryAddValue(newDict, kTrustRecordModDate, modDate);
}
CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
kTrustRecordTrustSettings);
if(trustSettings) {
require(CFGetTypeID(trustSettings) == CFArrayGetTypeID(), errOut);
require(validateTrustSettingsArray(trustSettings), errOut);
CFDictionaryAddValue(newDict, kTrustRecordTrustSettings, trustSettings);
}
CFDictionaryAddValue(tlc->dict, newKey, newDict);
CFRelease(newKey);
CFRelease(newDict);
return;
errOut:
CFReleaseSafe(newKey);
CFReleaseSafe(newDict);
tlc->status = errSecInvalidTrustSettings;
}
static OSStatus SecTrustSettingsValidate(SecTrustSettingsRef ts, bool trim) {
require(ts->_propList, errOut);
require(CFGetTypeID(ts->_propList) == CFDictionaryGetTypeID(), errOut);
CFNumberRef cfVers = (CFNumberRef)CFDictionaryGetValue(ts->_propList, kTrustRecordVersion);
require(cfVers != NULL && CFGetTypeID(cfVers) == CFNumberGetTypeID(), errOut);
require(CFNumberGetValue(cfVers, kCFNumberSInt32Type, &ts->_dictVersion), errOut);
require((ts->_dictVersion <= kSecTrustRecordVersionCurrent) &&
(ts->_dictVersion != kSecTrustRecordVersionInvalid), errOut);
require(ts->_trustDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), errOut);
CFDictionaryRef trustList = (CFDictionaryRef)CFDictionaryGetValue(
ts->_propList, kTrustRecordTrustList);
require(trustList != NULL &&
CFGetTypeID(trustList) == CFDictionaryGetTypeID(), errOut);
struct trustListContext context = {
ts->_trustDict, ts->_dictVersion, trim, errSecSuccess
};
CFDictionaryApplyFunction(trustList, trustListApplierFunction, &context);
if (trim) {
CFRelease(ts->_propList);
ts->_propList = NULL;
}
return context.status;
errOut:
return errSecInvalidTrustSettings;
}
OSStatus SecTrustSettingsCreateFromExternal(SecTrustSettingsDomain domain,
CFDataRef external, SecTrustSettingsRef *ts) {
CFAllocatorRef allocator = kCFAllocatorDefault;
CFIndex size = sizeof(struct __SecTrustSettings);
SecTrustSettingsRef result;
require(result = (SecTrustSettingsRef)_CFRuntimeCreateInstance(allocator,
SecTrustSettingsGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
CFErrorRef error = NULL;
CFPropertyListRef plist = CFPropertyListCreateWithData(kCFAllocatorDefault,
external, kCFPropertyListImmutable, NULL, &error);
if (!plist) {
secwarning("SecTrustSettingsCreateFromExternal: %@", error);
CFReleaseSafe(error);
CFReleaseSafe(result);
goto errOut;
}
result->_propList = plist;
result->_trustDict = NULL;
SecTrustSettingsValidate(result, false);
*ts = result;
return errSecSuccess;
errOut:
return errSecInvalidTrustSettings;
}
SecTrustSettingsRef SecTrustSettingsCreate(SecTrustSettingsDomain domain,
bool create, bool trim) {
CFAllocatorRef allocator = kCFAllocatorDefault;
CFIndex size = sizeof(struct __SecTrustSettings);
SecTrustSettingsRef result =
(SecTrustSettingsRef)_CFRuntimeCreateInstance(allocator,
SecTrustSettingsGetTypeID(), size - sizeof(CFRuntimeBase), 0);
if (!result)
return NULL;
return result;
}
CFDataRef SecTrustSettingsCopyExternal(SecTrustSettingsRef ts) {
CFDataRef xmlData;
verify(xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault,
ts->_propList));
return xmlData;
}
void SecTrustSettingsSet(SecCertificateRef certRef,
CFTypeRef trustSettingsDictOrArray) {
}
OSStatus SecTrustSettingsEvaluateCertificate(
SecCertificateRef certificate,
SecPolicyRef policy,
SecTrustSettingsKeyUsage keyUsage,
bool isSelfSignedCert,
SecTrustSettingsDomain *foundDomain,
CFArrayRef *allowedErrors,
SecTrustSettingsResult *resultType,
bool *foundMatchingEntry,
bool *foundAnyEntry)
{
#if 0
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(foundMatchingEntry)
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';
}
}
*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
#endif
return errSecSuccess;
}
OSStatus SecTrustSettingsSetTrustSettingsExternal(
CFDataRef settingsIn,
SecCertificateRef certRef,
CFTypeRef trustSettingsDictOrArray,
CFDataRef *settingsOut)
{
SecTrustSettingsRef ts = NULL;
OSStatus status;
require_noerr(status = SecTrustSettingsCreateFromExternal(
kSecTrustSettingsDomainMemory, settingsIn, &ts), errOut);
SecTrustSettingsSet(certRef, trustSettingsDictOrArray);
*settingsOut = SecTrustSettingsCopyExternal(ts);
errOut:
CFReleaseSafe(ts);
return status;
}