#include "TrustSettings.h"
#include "TrustSettingsSchema.h"
#include <Security/SecTrustSettings.h>
#include "TrustSettingsUtils.h"
#include "TrustKeychains.h"
#include "Certificate.h"
#include "cssmdatetime.h"
#include <Security/SecBase.h>
#include "SecTrustedApplicationPriv.h"
#include <security_utilities/errors.h>
#include <security_utilities/debugging.h>
#include <security_utilities/logging.h>
#include <security_utilities/cfutilities.h>
#include <security_utilities/alloc.h>
#include <security_utilities/casts.h>
#include <utilities/SecCFRelease.h>
#include <Security/Authorization.h>
#include <Security/cssmapplePriv.h>
#include <Security/oidscert.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecPolicyPriv.h>
#include <security_keychain/KCCursor.h>
#include <security_ocspd/ocspdClient.h>
#include <CoreFoundation/CoreFoundation.h>
#include <security_utilities/simulatecrash_assert.h>
#include <dispatch/dispatch.h>
#include <sys/stat.h>
#include <syslog.h>
#if 0
#define trustSettingsDbg(args...) syslog(LOG_ERR, ## args)
#define trustSettingsEvalDbg(args...) syslog(LOG_ERR, ## args)
#else
#define trustSettingsDbg(args...) secinfo("trustSettings", ## args)
#define trustSettingsEvalDbg(args...) secinfo("trustSettingsEval", ## args)
#endif
#define errSecInvalidTrustedRootRecord errSecInvalidTrustSettings
using namespace KeychainCore;
#pragma mark --- Static functions ---
static bool tsCheckPolicy(
const CSSM_OID *appPolicy,
CFDataRef certPolicy)
{
if(certPolicy != NULL) {
if(appPolicy == NULL) {
trustSettingsEvalDbg("tsCheckPolicy: certPolicy, !appPolicy");
return false;
}
unsigned cLen = (unsigned)CFDataGetLength(certPolicy);
const UInt8 *cData = CFDataGetBytePtr(certPolicy);
if((cLen != appPolicy->Length) || memcmp(appPolicy->Data, cData, cLen)) {
trustSettingsEvalDbg("tsCheckPolicy: policy mismatch");
return false;
}
}
return true;
}
static bool tsCheckApp(
CFDataRef certApp)
{
if(certApp != NULL) {
SecTrustedApplicationRef appRef;
OSStatus ortn;
ortn = SecTrustedApplicationCreateWithExternalRepresentation(certApp, &appRef);
if(ortn) {
trustSettingsDbg("tsCheckApp: bad trustedApp data");
return false;
}
ortn = SecTrustedApplicationValidateWithPath(appRef, NULL);
if(ortn) {
return false;
}
}
return true;
}
static bool tsCheckKeyUse(
SecTrustSettingsKeyUsage appKeyUse,
CFNumberRef certKeyUse)
{
if(certKeyUse != NULL) {
SInt32 certUse;
CFNumberGetValue(certKeyUse, kCFNumberSInt32Type, &certUse);
SecTrustSettingsKeyUsage cku = (SecTrustSettingsKeyUsage)certUse;
if(cku == kSecTrustSettingsKeyUseAny) {
return true;
}
if(appKeyUse == 0) {
trustSettingsEvalDbg("tsCheckKeyUse: certKeyUsage, !appKeyUsage");
return false;
}
if((cku & appKeyUse) != appKeyUse) {
trustSettingsEvalDbg("tsCheckKeyUse: keyUse mismatch");
return false;
}
}
return true;
}
static bool tsCheckPolicyStr(
const char *appPolicyStr,
CFStringRef certPolicyStr)
{
if(certPolicyStr != NULL) {
if(appPolicyStr == NULL) {
trustSettingsEvalDbg("tsCheckPolicyStr: certPolicyStr, !appPolicyStr");
return false;
}
CFStringRef cfPolicyStr = CFStringCreateWithCString(NULL, appPolicyStr,
kCFStringEncodingUTF8);
if(cfPolicyStr == NULL) {
trustSettingsEvalDbg("tsCheckPolicyStr: policyStr string conversion error");
return false;
}
CFMutableStringRef certPolicyStrNoNULL = CFStringCreateMutableCopy(NULL, 0, certPolicyStr);
if (certPolicyStrNoNULL == NULL) {
trustSettingsEvalDbg("tsCheckPolicyStr: policyStr string conversion error 2");
CFReleaseNull(cfPolicyStr);
return false;
}
CFStringFindAndReplace(certPolicyStrNoNULL, CFSTR("\00"),
CFSTR(""), CFRangeMake(0, CFStringGetLength(certPolicyStrNoNULL)), kCFCompareBackwards);
CFComparisonResult res = CFStringCompare(cfPolicyStr, certPolicyStrNoNULL, 0);
CFRelease(cfPolicyStr);
CFRelease(certPolicyStrNoNULL);
if(res != kCFCompareEqualTo) {
trustSettingsEvalDbg("tsCheckPolicyStr: policyStr mismatch");
return false;
}
}
return true;
}
static bool qualifyUsageWithCertDict(
CFDictionaryRef certDict,
const CSSM_OID *policyOID,
const char *policyStr,
SecTrustSettingsKeyUsage keyUsage,
bool onlyRoots)
{
CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
kTrustRecordTrustSettings);
CFIndex numSpecs = 0;
if(trustSettings != NULL) {
numSpecs = CFArrayGetCount(trustSettings);
}
if(numSpecs == 0) {
trustSettingsEvalDbg("qualifyUsageWithCertDict: no trust settings");
return true;
}
for(CFIndex addDex=0; addDex<numSpecs; addDex++) {
CFDictionaryRef tsDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings,
addDex);
CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsPolicy);
CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsApplication);
CFStringRef certPolicyStr = (CFStringRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsPolicyString);
CFNumberRef certKeyUsage = (CFNumberRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsKeyUsage);
CFNumberRef certResultType = (CFNumberRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsResult);
if(!tsCheckPolicy(policyOID, certPolicy)) {
continue;
}
if(!tsCheckApp(certApp)) {
continue;
}
if(!tsCheckKeyUse(keyUsage, certKeyUsage)) {
continue;
}
if(!tsCheckPolicyStr(policyStr, certPolicyStr)) {
continue;
}
SecTrustSettingsResult resultType = kSecTrustSettingsResultTrustRoot;
if(certResultType) {
SInt32 s;
CFNumberGetValue(certResultType, kCFNumberSInt32Type, &s);
resultType = (SecTrustSettingsResult)s;
}
switch(resultType) {
case kSecTrustSettingsResultTrustRoot:
trustSettingsEvalDbg("qualifyUsageWithCertDict: TrustRoot MATCH");
return true;
case kSecTrustSettingsResultTrustAsRoot:
if(onlyRoots) {
trustSettingsEvalDbg("qualifyUsageWithCertDict: TrustAsRoot but not root");
return false;
}
trustSettingsEvalDbg("qualifyUsageWithCertDict: TrustAsRoot MATCH");
return true;
default:
trustSettingsEvalDbg("qualifyUsageWithCertDict: bad resultType "
"(%lu)", (unsigned long)resultType);
return false;
}
}
trustSettingsEvalDbg("qualifyUsageWithCertDict: NO MATCH");
return false;
}
static CFMutableDictionaryRef tsInitialDict()
{
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL,
kSecTrustRecordNumTopDictKeys,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef trustDict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(dict, kTrustRecordTrustList, trustDict);
CFRelease(trustDict);
SInt32 vers = kSecTrustRecordVersionCurrent;
CFNumberRef cfVers = CFNumberCreate(NULL, kCFNumberSInt32Type, &vers);
CFDictionaryAddValue(dict, kTrustRecordVersion, cfVers);
CFRelease(cfVers);
return dict;
}
static void tsSetModDate(
CFMutableDictionaryRef dict)
{
CFDateRef modDate;
modDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
CFDictionarySetValue(dict, kTrustRecordModDate, modDate);
CFRelease(modDate);
}
static
bool tsIsGoodCfNum(CFNumberRef cfn, SInt32 *num = NULL)
{
if(cfn == NULL) {
if(num) {
*num = 0;
}
return true;
}
if(CFGetTypeID(cfn) != CFNumberGetTypeID()) {
return false;
}
SInt32 s;
if(!CFNumberGetValue(cfn, kCFNumberSInt32Type, &s)) {
return false;
}
else {
if(num) {
*num = s;
}
return true;
}
}
TrustSettings::TrustSettings(SecTrustSettingsDomain domain)
: mPropList(NULL),
mTrustDict(NULL),
mDictVersion(0),
mDomain(domain),
mDirty(false)
{
}
#pragma mark --- Public methods ---
OSStatus TrustSettings::CreateTrustSettings(
SecTrustSettingsDomain domain,
bool create,
bool trim,
TrustSettings*& ts)
{
TrustSettings* t = new TrustSettings(domain);
Allocator &alloc = Allocator::standard();
CSSM_DATA fileData = {0, NULL};
OSStatus ortn = errSecSuccess;
struct stat sb;
const char *path;
switch(domain) {
case kSecTrustSettingsDomainAdmin:
path = TRUST_SETTINGS_PATH "/" ADMIN_TRUST_SETTINGS;
if(stat(path, &sb)) {
trustSettingsDbg("TrustSettings: no admin record; skipping");
ortn = errSecNoTrustSettings;
break;
}
case kSecTrustSettingsDomainUser:
ortn = ocspdTrustSettingsRead(alloc, domain, fileData);
break;
case kSecTrustSettingsDomainSystem:
if(tsReadFile(SYSTEM_TRUST_SETTINGS_PATH, alloc, fileData)) {
ortn = errSecNoTrustSettings;
}
break;
default:
delete t;
return errSecParam;
}
if(ortn) {
if(create) {
trustSettingsDbg("TrustSettings: creating new record for domain %d",
(int)domain);
t->mPropList = tsInitialDict();
t->mDirty = true;
}
else {
trustSettingsDbg("TrustSettings: record not found for domain %d",
(int)domain);
delete t;
return ortn;
}
}
else {
CFRef<CFDataRef> propList(CFDataCreate(NULL, fileData.Data, fileData.Length));
t->initFromData(propList);
alloc.free(fileData.Data);
}
t->validatePropList(trim);
ts = t;
return errSecSuccess;
}
OSStatus TrustSettings::CreateTrustSettings(
SecTrustSettingsDomain domain,
CFDataRef externalData,
TrustSettings*& ts)
{
switch(domain) {
case kSecTrustSettingsDomainUser:
case kSecTrustSettingsDomainAdmin:
case kSecTrustSettingsDomainMemory:
break;
case kSecTrustSettingsDomainSystem:
default:
return errSecParam;
}
TrustSettings* t = new TrustSettings(domain);
if(externalData != NULL) {
t->initFromData(externalData);
}
else {
t->mPropList = tsInitialDict();
}
t->validatePropList(TRIM_NO);
t->mDirty = true;
ts = t;
return errSecSuccess;
}
TrustSettings::~TrustSettings()
{
trustSettingsDbg("TrustSettings(domain %d) destructor", (int)mDomain);
CFRELEASE(mPropList);
CFRELEASE(mTrustDict);
}
void TrustSettings::initFromData(
CFDataRef trustSettingsData)
{
CFStringRef errStr = NULL;
mPropList = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData(
NULL,
trustSettingsData,
kCFPropertyListMutableContainersAndLeaves,
&errStr);
if(mPropList == NULL) {
trustSettingsDbg("TrustSettings::initFromData decode err (%s)",
errStr ? CFStringGetCStringPtr(errStr, kCFStringEncodingUTF8) : "<no err>");
if(errStr != NULL) {
CFRelease(errStr);
}
MacOSError::throwMe(errSecInvalidTrustSettings);
}
}
void TrustSettings::flushToDisk()
{
if(!mDirty) {
trustSettingsDbg("flushToDisk, domain %d, !dirty!", (int)mDomain);
return;
}
if(mPropList == NULL) {
trustSettingsDbg("flushToDisk, domain %d, trimmed!", (int)mDomain);
assert(0);
MacOSError::throwMe(errSecInternalComponent);
}
switch(mDomain) {
case kSecTrustSettingsDomainSystem:
case kSecTrustSettingsDomainMemory:
default:
trustSettingsDbg("flushToDisk, bad domain (%d)", (int)mDomain);
MacOSError::throwMe(errSecInternalComponent);
case kSecTrustSettingsDomainUser:
case kSecTrustSettingsDomainAdmin:
break;
}
CFRef<CFDataRef> xmlData;
CSSM_DATA cssmXmlData = {0, NULL};
CFIndex numCerts = CFDictionaryGetCount(mTrustDict);
if(numCerts) {
xmlData.take(CFPropertyListCreateXMLData(NULL, mPropList));
if(!xmlData) {
trustSettingsDbg("flushToDisk, domain %d: error converting to XML", (int)mDomain);
MacOSError::throwMe(errSecInternalComponent);
}
cssmXmlData.Data = (uint8 *)CFDataGetBytePtr(xmlData);
cssmXmlData.Length = CFDataGetLength(xmlData);
}
else {
trustSettingsDbg("flushToDisk, domain %d: DELETING trust settings", (int)mDomain);
}
AuthorizationRef authRef;
OSStatus ortn;
ortn = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
0, &authRef);
if(ortn) {
trustSettingsDbg("flushToDisk, domain %d: AuthorizationCreate returned %ld",
(int)mDomain, (long)ortn);
MacOSError::throwMe(errSecInternalComponent);
}
AuthorizationExternalForm authExt;
CSSM_DATA authBlob = {sizeof(authExt), (uint8 *)&authExt};
ortn = AuthorizationMakeExternalForm(authRef, &authExt);
if(ortn) {
trustSettingsDbg("flushToDisk, domain %d: AuthorizationMakeExternalForm returned %ld",
(int)mDomain, (long)ortn);
ortn = errSecInternalComponent;
goto errOut;
}
ortn = ocspdTrustSettingsWrite(mDomain, authBlob, cssmXmlData);
if(ortn) {
trustSettingsDbg("flushToDisk, domain %d: ocspdTrustSettingsWrite returned %ld",
(int)mDomain, (long)ortn);
goto errOut;
}
trustSettingsDbg("flushToDisk, domain %d: wrote to disk", (int)mDomain);
mDirty = false;
errOut:
AuthorizationFree(authRef, 0);
if(ortn) {
MacOSError::throwMe(ortn);
}
}
CFDataRef TrustSettings::createExternal()
{
assert(mPropList);
CFDataRef xmlData = CFPropertyListCreateXMLData(NULL, mPropList);
if(xmlData == NULL) {
trustSettingsDbg("createExternal, domain %d: error converting to XML",
(int)mDomain);
MacOSError::throwMe(errSecInternalComponent);
}
return xmlData;
}
bool TrustSettings::evaluateCert(
CFStringRef certHashStr,
const CSSM_OID *policyOID,
const char *policyStr,
SecTrustSettingsKeyUsage keyUsage,
bool isRootCert,
CSSM_RETURN **allowedErrors,
uint32 *numAllowedErrors,
SecTrustSettingsResult *resultType,
bool *foundAnyEntry)
{
assert(mTrustDict != NULL);
CFDictionaryRef certDict = findDictionaryForCertHash(certHashStr);
#if CERT_HASH_DEBUG
const size_t maxHashStrLen = 512;
char *buf = (char*)malloc(maxHashStrLen);
if (buf) {
if (!CFStringGetCString(certHashStr, buf, (CFIndex)maxHashStrLen, kCFStringEncodingUTF8)) {
buf[0]='\0';
}
trustSettingsEvalDbg("evaluateCert for \"%s\", found dict %p", buf, certDict);
free(buf);
}
#endif
if(certDict == NULL) {
*foundAnyEntry = false;
return false;
}
*foundAnyEntry = true;
CSSM_RETURN *allowedErrs = *allowedErrors;
uint32 numAllowedErrs = *numAllowedErrors;
bool foundSettings = false;
SecTrustSettingsResult returnedResult = kSecTrustSettingsResultInvalid;
CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
kTrustRecordTrustSettings);
CFIndex numSpecs = 0;
if(trustSettings != NULL) {
numSpecs = CFArrayGetCount(trustSettings);
}
if(numSpecs == 0) {
trustSettingsEvalDbg("evaluateCert: no trust settings");
*resultType = kSecTrustSettingsResultTrustRoot;
return true;
}
for(CFIndex addDex=0; addDex<numSpecs; addDex++) {
CFDictionaryRef tsDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings,
addDex);
CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsPolicy);
CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsApplication);
CFStringRef certPolicyStr = (CFStringRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsPolicyString);
CFNumberRef certKeyUsage = (CFNumberRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsKeyUsage);
CFNumberRef certResultType = (CFNumberRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsResult);
CFNumberRef certAllowedErr = (CFNumberRef)CFDictionaryGetValue(tsDict,
kSecTrustSettingsAllowedError);
if(!tsCheckPolicy(policyOID, certPolicy)) {
continue;
}
if(!tsCheckApp(certApp)) {
continue;
}
if(!tsCheckKeyUse(keyUsage, certKeyUsage)) {
continue;
}
if(!tsCheckPolicyStr(policyStr, certPolicyStr)) {
continue;
}
trustSettingsEvalDbg("evaluateCert: MATCH");
foundSettings = true;
if(certAllowedErr) {
SInt32 s;
CFNumberGetValue(certAllowedErr, kCFNumberSInt32Type, &s);
allowedErrs = (CSSM_RETURN *)::realloc(allowedErrs,
++numAllowedErrs * sizeof(CSSM_RETURN));
allowedErrs[numAllowedErrs-1] = (CSSM_RETURN) s;
}
switch(returnedResult) {
case kSecTrustSettingsResultUnspecified:
case kSecTrustSettingsResultInvalid:
if(certResultType) {
SInt32 s;
CFNumberGetValue(certResultType, kCFNumberSInt32Type, &s);
returnedResult = (SecTrustSettingsResult)s;
}
else {
returnedResult = kSecTrustSettingsResultTrustRoot;
}
break;
default:
break;
}
}
*allowedErrors = allowedErrs;
*numAllowedErrors = numAllowedErrs;
if(returnedResult != kSecTrustSettingsResultInvalid) {
*resultType = returnedResult;
}
return foundSettings;
}
void TrustSettings::findCerts(
StorageManager::KeychainList &keychains,
CFMutableArrayRef certArray)
{
findQualifiedCerts(keychains,
true,
false,
NULL, NULL, kSecTrustSettingsKeyUseAny,
certArray);
}
void TrustSettings::findQualifiedCerts(
StorageManager::KeychainList &keychains,
bool findAll,
bool onlyRoots,
const CSSM_OID *policyOID,
const char *policyString,
SecTrustSettingsKeyUsage keyUsage,
CFMutableArrayRef certArray)
{
StLock<Mutex> _(SecTrustKeychainsGetMutex());
CFRef<CFMutableSetRef> certSet(CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks));
KCCursor cursor(keychains, (SecItemClass) CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL);
Item certItem;
bool found;
unsigned int total=0, entries=0, qualified=0;
do {
found = cursor->next(certItem);
if(!found) {
break;
}
++total;
SecPointer<Certificate> certificate(static_cast<Certificate *>(&*certItem));
CssmData certCssmData;
try {
certCssmData = certificate->data();
}
catch (...) {}
if (!(certCssmData.Data && certCssmData.Length)) {
continue;
}
CFRef<CFDataRef> cfDataRef(CFDataCreate(NULL, certCssmData.Data, certCssmData.Length));
CFRef<SecCertificateRef> certRef(SecCertificateCreateWithData(NULL, cfDataRef));
CFDictionaryRef certDict = findDictionaryForCert(certRef);
if(certDict == NULL) {
continue;
}
++entries;
if(!findAll) {
if(!qualifyUsageWithCertDict(certDict, policyOID,
policyString, keyUsage, onlyRoots)) {
continue;
}
}
++qualified;
CSSM_DATA certData;
OSStatus ortn = SecCertificateGetData(certRef, &certData);
if(ortn) {
trustSettingsEvalDbg("findQualifiedCerts: SecCertificateGetData error");
continue;
}
CFRef<CFDataRef> cfData(CFDataCreate(NULL, certData.Data, certData.Length));
CFDataRef cfd = cfData.get();
if(CFSetContainsValue(certSet, cfd)) {
trustSettingsEvalDbg("findQualifiedCerts: dup cert");
continue;
}
else {
CFSetAddValue(certSet, cfd);
CFArrayAppendValue(certArray, certRef);
}
} while(found);
trustSettingsEvalDbg("findQualifiedCerts: examined %d certs, qualified %d of %d",
total, qualified, entries);
}
CFArrayRef TrustSettings::copyTrustSettings(
SecCertificateRef certRef)
{
CFDictionaryRef certDict = NULL;
certDict = findDictionaryForCert(certRef);
if(certDict == NULL) {
trustSettingsDbg("copyTrustSettings: dictionary not found");
return NULL;
}
CFArrayRef diskTrustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
kTrustRecordTrustSettings);
CFIndex numSpecs = 0;
if(diskTrustSettings != NULL) {
numSpecs = CFArrayGetCount(diskTrustSettings);
}
CFRef<CFMutableArrayRef> outArray(CFArrayCreateMutable(NULL, numSpecs,
&kCFTypeArrayCallBacks));
for(CFIndex dex=0; dex<numSpecs; dex++) {
CFDictionaryRef diskTsDict =
(CFDictionaryRef)CFArrayGetValueAtIndex(diskTrustSettings, dex);
assert(CFGetTypeID(diskTsDict) == CFDictionaryGetTypeID());
CFTypeRef certPolicy = (CFTypeRef) CFDictionaryGetValue(diskTsDict, kSecTrustSettingsPolicy);
CFStringRef policyName = (CFStringRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsPolicyName);
CFDataRef certApp = (CFDataRef) CFDictionaryGetValue(diskTsDict, kSecTrustSettingsApplication);
CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsPolicyString);
CFNumberRef allowedErr = (CFNumberRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsAllowedError);
CFNumberRef resultType = (CFNumberRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsResult);
CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(diskTsDict, kSecTrustSettingsKeyUsage);
if((certPolicy == NULL) &&
(certApp == NULL) &&
(policyStr == NULL) &&
(allowedErr == NULL) &&
(resultType == NULL) &&
(keyUsage == NULL)) {
continue;
}
CFRef<CFMutableDictionaryRef> outTsDict(CFDictionaryCreateMutable(NULL,
0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
if(certPolicy != NULL) {
SecPolicyRef policyRef = NULL;
if (CFDataGetTypeID() == CFGetTypeID(certPolicy)) {
CSSM_OID policyOid = { int_cast<CFIndex, CSSM_SIZE>(CFDataGetLength((CFDataRef)certPolicy)),
(uint8 *)CFDataGetBytePtr((CFDataRef)certPolicy) };
OSStatus ortn = SecPolicyCopy(CSSM_CERT_X_509v3, &policyOid, &policyRef);
if(ortn) {
trustSettingsDbg("copyTrustSettings: OID conversion error");
abort("Bad Policy OID in trusted root list", errSecInvalidTrustedRootRecord);
}
} else if (CFStringGetTypeID() == CFGetTypeID(certPolicy)) {
policyRef = SecPolicyCreateWithProperties(certPolicy, NULL);
}
if (policyRef) {
CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicy, policyRef);
CFRelease(policyRef); }
}
if (policyName != NULL) {
CFStringRef str = CFStringCreateCopy(NULL, policyName);
CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicyName, str);
CFRelease(str); }
if(certApp != NULL) {
SecTrustedApplicationRef appRef;
OSStatus ortn = SecTrustedApplicationCreateWithExternalRepresentation(certApp, &appRef);
if(ortn) {
trustSettingsDbg("copyTrustSettings: App conversion error");
abort("Bad application data in trusted root list", errSecInvalidTrustedRootRecord);
}
CFDictionaryAddValue(outTsDict, kSecTrustSettingsApplication, appRef);
CFRelease(appRef); }
if(policyStr != NULL) {
CFStringRef str = CFStringCreateCopy(NULL, policyStr);
CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicyString, str);
CFRelease(str); }
if(allowedErr != NULL) {
CFDictionaryAddValue(outTsDict, kSecTrustSettingsAllowedError, allowedErr);
}
if(resultType != NULL) {
CFDictionaryAddValue(outTsDict, kSecTrustSettingsResult, resultType);
}
if(keyUsage != NULL) {
CFDictionaryAddValue(outTsDict, kSecTrustSettingsKeyUsage, keyUsage);
}
CFArrayAppendValue(outArray, outTsDict);
}
CFRetain(outArray); return outArray;
}
CFDateRef TrustSettings::copyModDate(
SecCertificateRef certRef)
{
CFDictionaryRef certDict = NULL;
certDict = findDictionaryForCert(certRef);
if(certDict == NULL) {
trustSettingsDbg("copyModDate: dictionary not found");
return NULL;
}
CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict, kTrustRecordModDate);
if(modDate == NULL) {
return NULL;
}
CFRetain(modDate);
return modDate;
}
bool TrustSettings::contains(SecCertificateRef certRef)
{
if(findDictionaryForCert(certRef) != NULL) {
return true;
}
return false;
}
void TrustSettings::setTrustSettings(
SecCertificateRef certRef,
CFTypeRef trustSettingsDictOrArray)
{
OSStatus ortn;
Boolean isSelfSigned = false;
if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
isSelfSigned = true;
}
else {
ortn = SecCertificateIsSelfSigned(certRef, &isSelfSigned);
if(ortn) {
MacOSError::throwMe(ortn);
}
}
CFRef<CFArrayRef> trustSettings(validateApiTrustSettings(
trustSettingsDictOrArray, isSelfSigned));
assert(mPropList != NULL);
assert(mDomain != kSecTrustSettingsDomainSystem);
CFRef<CFDataRef> issuer;
CFRef<CFDataRef> serial;
if(certRef != kSecTrustSettingsDefaultRootCertSetting) {
copyIssuerAndSerial(certRef, issuer.take(), serial.take());
}
else {
UInt8 dummy;
issuer = CFDataCreate(NULL, &dummy, 0);
serial = CFDataCreate(NULL, &dummy, 0);
}
CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef));
if(!certHashStr) {
trustSettingsDbg("TrustSettings::setTrustSettings: CertHashStrFromCert error");
MacOSError::throwMe(errSecItemNotFound);
}
CFMutableDictionaryRef certDict =
(CFMutableDictionaryRef)findDictionaryForCertHash(certHashStr);
if(certDict == NULL) {
certDict = CFDictionaryCreateMutable(NULL, kSecTrustRecordNumCertDictKeys,
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(certDict == NULL) {
MacOSError::throwMe(errSecAllocate);
}
CFDictionaryAddValue(certDict, kTrustRecordIssuer, issuer);
CFDictionaryAddValue(certDict, kTrustRecordSerialNumber, serial);
if(CFArrayGetCount(trustSettings) != 0) {
CFDictionaryAddValue(certDict, kTrustRecordTrustSettings, trustSettings);
}
tsSetModDate(certDict);
CFDictionaryAddValue(mTrustDict, static_cast<CFStringRef>(certHashStr), certDict);
CFRelease(certDict);
}
else {
tsSetModDate(certDict);
if(CFArrayGetCount(trustSettings) != 0) {
CFDictionarySetValue(certDict, kTrustRecordTrustSettings, trustSettings);
}
else {
CFDictionaryRemoveValue(certDict, kTrustRecordTrustSettings);
}
}
mDirty = true;
}
void TrustSettings::deleteTrustSettings(
SecCertificateRef certRef)
{
CFDictionaryRef certDict = NULL;
assert(mPropList != NULL);
assert(mDomain != kSecTrustSettingsDomainSystem);
CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef));
if(!certHashStr) {
MacOSError::throwMe(errSecItemNotFound);
}
certDict = findDictionaryForCertHash(certHashStr);
if(certDict != NULL) {
CFDictionaryRemoveValue(mTrustDict, static_cast<CFStringRef>(certHashStr));
mDirty = true;
}
else {
trustSettingsDbg("TrustSettings::deleteRoot: cert dictionary not found");
MacOSError::throwMe(errSecItemNotFound);
}
}
#pragma mark --- Private methods ---
CFDictionaryRef TrustSettings::findDictionaryForCert(
SecCertificateRef certRef)
{
CFRef<CFStringRef> certHashStr(SecTrustSettingsCertHashStrFromCert(certRef));
if (certHashStr.get() == NULL)
{
return NULL;
}
return findDictionaryForCertHash(static_cast<CFStringRef>(certHashStr.get()));
}
CFDictionaryRef TrustSettings::findDictionaryForCertHash(
CFStringRef certHashStr)
{
assert(mTrustDict != NULL);
return (CFDictionaryRef)CFDictionaryGetValue(mTrustDict, certHashStr);
}
CFArrayRef TrustSettings::validateApiTrustSettings(
CFTypeRef trustSettingsDictOrArray,
Boolean isSelfSigned)
{
CFArrayRef tmpInArray = NULL;
if(trustSettingsDictOrArray == NULL) {
if(!isSelfSigned) {
trustSettingsDbg("validateApiUsageConstraints: !isSelfSigned, no settings");
MacOSError::throwMe(errSecParam);
}
return CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
}
else if(CFGetTypeID(trustSettingsDictOrArray) == CFDictionaryGetTypeID()) {
tmpInArray = CFArrayCreate(NULL, &trustSettingsDictOrArray, 1,
&kCFTypeArrayCallBacks);
}
else if(CFGetTypeID(trustSettingsDictOrArray) == CFArrayGetTypeID()) {
tmpInArray = (CFArrayRef)trustSettingsDictOrArray;
CFRetain(tmpInArray);
}
else {
trustSettingsDbg("validateApiUsageConstraints: bad trustSettingsDictOrArray");
MacOSError::throwMe(errSecParam);
}
CFIndex numSpecs = CFArrayGetCount(tmpInArray);
CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, numSpecs, &kCFTypeArrayCallBacks);
CSSM_OID oid;
OSStatus ortn = errSecSuccess;
SecPolicyRef certPolicy;
SecTrustedApplicationRef certApp;
CFTypeRef oidData = NULL;
for(CFIndex dex=0; dex<numSpecs; dex++) {
CFStringRef policyName = NULL;
CFDataRef appData = NULL;
CFStringRef policyStr = NULL;
CFNumberRef allowedErr = NULL;
CFNumberRef resultType = NULL;
CFNumberRef keyUsage = NULL;
SInt32 resultNum;
SecTrustSettingsResult result;
CFDictionaryRef ucDict = (CFDictionaryRef)CFArrayGetValueAtIndex(tmpInArray, dex);
if(CFGetTypeID(ucDict) != CFDictionaryGetTypeID()) {
trustSettingsDbg("validateAppPolicyArray: malformed usageConstraint dictionary");
ortn = errSecParam;
break;
}
certPolicy = (SecPolicyRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicy);
if(certPolicy != NULL) {
if(CFGetTypeID(certPolicy) != SecPolicyGetTypeID()) {
trustSettingsDbg("validateAppPolicyArray: malformed certPolicy");
ortn = errSecParam;
break;
}
ortn = SecPolicyGetOID(certPolicy, &oid);
if (ortn) {
oidData = CFRetain(SecPolicyGetOidString(certPolicy));
} else {
oidData = CFDataCreate(NULL, oid.Data, oid.Length);
}
if (!oidData) {
trustSettingsDbg("validateAppPolicyArray: SecPolicyGetOID error");
break;
}
policyName = SecPolicyGetName(certPolicy);
}
certApp = (SecTrustedApplicationRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsApplication);
if(certApp != NULL) {
if(CFGetTypeID(certApp) != SecTrustedApplicationGetTypeID()) {
trustSettingsDbg("validateAppPolicyArray: malformed certApp");
ortn = errSecParam;
break;
}
ortn = SecTrustedApplicationCopyExternalRepresentation(certApp, &appData);
if(ortn) {
trustSettingsDbg("validateAppPolicyArray: "
"SecTrustedApplicationCopyExternalRepresentation error");
break;
}
}
policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString);
if(policyStr != NULL) {
if(CFGetTypeID(policyStr) != CFStringGetTypeID()) {
trustSettingsDbg("validateAppPolicyArray: malformed policyStr");
ortn = errSecParam;
break;
}
}
allowedErr = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError);
if(!tsIsGoodCfNum(allowedErr)) {
trustSettingsDbg("validateAppPolicyArray: malformed allowedErr");
ortn = errSecParam;
break;
}
resultType = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult);
if(!tsIsGoodCfNum(resultType, &resultNum)) {
trustSettingsDbg("validateAppPolicyArray: malformed resultType");
ortn = errSecParam;
break;
}
result = (SecTrustSettingsResult) resultNum;
keyUsage = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage);
if(!tsIsGoodCfNum(keyUsage)) {
trustSettingsDbg("validateAppPolicyArray: malformed keyUsage");
ortn = errSecParam;
break;
}
if(!oidData && !appData && !policyStr &&
!allowedErr && !resultType && !keyUsage) {
continue;
}
CFMutableDictionaryRef outDict = CFDictionaryCreateMutable(NULL,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if(oidData) {
CFDictionaryAddValue(outDict, kSecTrustSettingsPolicy, oidData);
CFReleaseNull(oidData); }
if(policyName) {
CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyName, policyName);
}
if(appData) {
CFDictionaryAddValue(outDict, kSecTrustSettingsApplication, appData);
CFRelease(appData); }
if(policyStr) {
CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyString, policyStr);
}
if(allowedErr) {
CFDictionaryAddValue(outDict, kSecTrustSettingsAllowedError, allowedErr);
}
ortn = errSecSuccess;
if(resultType) {
switch(result) {
case kSecTrustSettingsResultInvalid:
ortn = errSecParam;
break;
case kSecTrustSettingsResultTrustRoot:
if(!isSelfSigned) {
trustSettingsDbg("validateAppPolicyArray: TrustRoot, !isSelfSigned");
ortn = errSecParam;
}
break;
case kSecTrustSettingsResultTrustAsRoot:
if(isSelfSigned) {
trustSettingsDbg("validateAppPolicyArray: TrustAsRoot, isSelfSigned");
ortn = errSecParam;
}
break;
case kSecTrustSettingsResultDeny:
case kSecTrustSettingsResultUnspecified:
break;
default:
trustSettingsDbg("validateAppPolicyArray: bogus resultType");
ortn = errSecParam;
break;
}
if(ortn) {
break;
}
CFDictionaryAddValue(outDict, kSecTrustSettingsResult, resultType);
}
else {
if(!isSelfSigned) {
trustSettingsDbg("validateAppPolicyArray: default result, !isSelfSigned");
ortn = errSecParam;
break;
}
}
if(keyUsage) {
CFDictionaryAddValue(outDict, kSecTrustSettingsKeyUsage, keyUsage);
}
CFArrayAppendValue(outArray, outDict);
CFRelease(outDict);
}
CFReleaseNull(oidData);
CFRelease(tmpInArray);
if(ortn) {
CFRelease(outArray);
MacOSError::throwMe(ortn);
}
return outArray;
}
bool TrustSettings::validateTrustSettingsArray(
CFArrayRef trustSettings)
{
CFIndex numSpecs = CFArrayGetCount(trustSettings);
for(CFIndex dex=0; dex<numSpecs; dex++) {
CFDictionaryRef ucDict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings,
dex);
if(CFGetTypeID(ucDict) != CFDictionaryGetTypeID()) {
trustSettingsDbg("validateAppPolicyArray: malformed app/policy dictionary");
return false;
}
CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicy);
if((certPolicy != NULL) && (CFGetTypeID(certPolicy) != CFDataGetTypeID())) {
trustSettingsDbg("validateAppPolicyArray: malformed certPolicy");
return false;
}
CFDataRef certApp = (CFDataRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsApplication);
if((certApp != NULL) && (CFGetTypeID(certApp) != CFDataGetTypeID())) {
trustSettingsDbg("validateAppPolicyArray: malformed certApp");
return false;
}
CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString);
if((policyStr != NULL) && (CFGetTypeID(policyStr) != CFStringGetTypeID())) {
trustSettingsDbg("validateAppPolicyArray: malformed policyStr");
return false;
}
CFNumberRef cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError);
if(!tsIsGoodCfNum(cfNum)) {
trustSettingsDbg("validateAppPolicyArray: malformed allowedErr");
return false;
}
cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult);
if(!tsIsGoodCfNum(cfNum)) {
trustSettingsDbg("validateAppPolicyArray: malformed resultType");
return false;
}
cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage);
if(!tsIsGoodCfNum(cfNum)) {
trustSettingsDbg("validateAppPolicyArray: malformed keyUsage");
return false;
}
}
return true;
}
void TrustSettings::validatePropList(bool trim)
{
if(!mPropList) {
trustSettingsDbg("TrustSettings::validatePropList missing mPropList");
abort("missing propList", errSecInvalidTrustedRootRecord);
}
if(CFGetTypeID(mPropList) != CFDictionaryGetTypeID()) {
trustSettingsDbg("TrustSettings::validatePropList: malformed mPropList");
abort("malformed propList", errSecInvalidTrustedRootRecord);
}
CFNumberRef cfVers = (CFNumberRef)CFDictionaryGetValue(mPropList, kTrustRecordVersion);
if((cfVers == NULL) || (CFGetTypeID(cfVers) != CFNumberGetTypeID())) {
trustSettingsDbg("TrustSettings::validatePropList: malformed version");
abort("malformed version", errSecInvalidTrustedRootRecord);
}
if(!CFNumberGetValue(cfVers, kCFNumberSInt32Type, &mDictVersion)) {
trustSettingsDbg("TrustSettings::validatePropList: malformed version");
abort("malformed version", errSecInvalidTrustedRootRecord);
}
if((mDictVersion > kSecTrustRecordVersionCurrent) ||
(mDictVersion == kSecTrustRecordVersionInvalid)) {
trustSettingsDbg("TrustSettings::validatePropList: incompatible version");
abort("incompatible version", errSecInvalidTrustedRootRecord);
}
mTrustDict = (CFMutableDictionaryRef)CFDictionaryGetValue(mPropList, kTrustRecordTrustList);
if(mTrustDict != NULL) {
CFRetain(mTrustDict);
}
if((mTrustDict == NULL) || (CFGetTypeID(mTrustDict) != CFDictionaryGetTypeID())) {
trustSettingsDbg("TrustSettings::validatePropList: malformed mTrustDict");
abort("malformed TrustArray", errSecInvalidTrustedRootRecord);
}
CFIndex numCerts = CFDictionaryGetCount(mTrustDict);
const void *dictKeys[numCerts];
const void *dictValues[numCerts];
CFDictionaryGetKeysAndValues(mTrustDict, dictKeys, dictValues);
for(CFIndex dex=0; dex<numCerts; dex++) {
CFMutableDictionaryRef certDict = (CFMutableDictionaryRef)dictValues[dex];
if((certDict == NULL) || (CFGetTypeID(certDict) != CFDictionaryGetTypeID())) {
trustSettingsDbg("TrustSettings::validatePropList: malformed certDict");
abort("malformed certDict", errSecInvalidTrustedRootRecord);
}
CFDataRef cfd = (CFDataRef)CFDictionaryGetValue(certDict, kTrustRecordIssuer);
if(cfd == NULL) {
trustSettingsDbg("TrustSettings::validatePropList: missing issuer");
abort("missing issuer", errSecInvalidTrustedRootRecord);
}
if(CFGetTypeID(cfd) != CFDataGetTypeID()) {
trustSettingsDbg("TrustSettings::validatePropList: malformed issuer");
abort("malformed issuer", errSecInvalidTrustedRootRecord);
}
if(trim) {
CFDictionaryRemoveValue(certDict, kTrustRecordIssuer);
}
cfd = (CFDataRef)CFDictionaryGetValue(certDict, kTrustRecordSerialNumber);
if(cfd == NULL) {
trustSettingsDbg("TrustSettings::validatePropList: missing serial number");
abort("missing serial number", errSecInvalidTrustedRootRecord);
}
if(CFGetTypeID(cfd) != CFDataGetTypeID()) {
trustSettingsDbg("TrustSettings::validatePropList: malformed serial number");
abort("malformed serial number", errSecInvalidTrustedRootRecord);
}
if(trim) {
CFDictionaryRemoveValue(certDict, kTrustRecordSerialNumber);
}
CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict, kTrustRecordModDate);
if(modDate == NULL) {
trustSettingsDbg("TrustSettings::validatePropList: missing modDate");
abort("missing modDate", errSecInvalidTrustedRootRecord);
}
if(CFGetTypeID(modDate) != CFDateGetTypeID()) {
trustSettingsDbg("TrustSettings::validatePropList: malformed modDate");
abort("malformed modDate", errSecInvalidTrustedRootRecord);
}
if(trim) {
CFDictionaryRemoveValue(certDict, kTrustRecordModDate);
}
CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
kTrustRecordTrustSettings);
if(trustSettings == NULL) {
continue;
}
if(CFGetTypeID(trustSettings) != CFArrayGetTypeID()) {
trustSettingsDbg("TrustSettings::validatePropList: malformed useConstraint"
"array");
abort("malformed useConstraint array", errSecInvalidTrustedRootRecord);
}
if(!validateTrustSettingsArray(trustSettings)) {
abort("malformed useConstraint array", errSecInvalidTrustedRootRecord);
}
}
if(trim) {
CFRelease(mPropList);
mPropList = NULL;
}
}
void TrustSettings::copyIssuerAndSerial(
SecCertificateRef certRef,
CFDataRef *issuer,
CFDataRef *serial)
{
CFRef<SecCertificateRef> certificate = SecCertificateCreateItemImplInstance(certRef);
SecPointer<Certificate> cert = Certificate::required(certificate);
CSSM_DATA_PTR fieldVal;
if(issuer != NULL) {
fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd);
*issuer = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length);
cert->releaseFieldValue(CSSMOID_X509V1IssuerNameStd, fieldVal);
}
fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1SerialNumber);
*serial = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length);
cert->releaseFieldValue(CSSMOID_X509V1SerialNumber, fieldVal);
}
void TrustSettings::abort(
const char *why,
OSStatus err)
{
Syslog::error("TrustSettings: %s", why);
MacOSError::throwMe(err);
}