X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/TrustSettings.cpp diff --git a/Security/libsecurity_keychain/lib/TrustSettings.cpp b/Security/libsecurity_keychain/lib/TrustSettings.cpp deleted file mode 100644 index 12a9c44e..00000000 --- a/Security/libsecurity_keychain/lib/TrustSettings.cpp +++ /dev/null @@ -1,1559 +0,0 @@ -/* - * Copyright (c) 2005,2011-2014 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@ - */ - -/* - * TrustSettings.h - class to manage cert trust settings. - * - */ - -#include "TrustSettings.h" -#include "TrustSettingsSchema.h" -#include "SecTrustSettings.h" -#include "TrustSettingsUtils.h" -#include "TrustKeychains.h" -#include "SecCertificatePriv.h" -#include "SecPolicyPriv.h" -#include "Certificate.h" -#include "cssmdatetime.h" -#include -#include "SecTrustedApplicationPriv.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define trustSettingsDbg(args...) secdebug("trustSettings", ## args) -#define trustSettingsEvalDbg(args...) secdebug("trustSettingsEval", ## args) - -/* - * Common error return for "malformed TrustSettings record" - */ -#define errSecInvalidTrustedRootRecord errSecInvalidTrustSettings - -using namespace KeychainCore; - -#pragma mark --- Static functions --- - -/* - * Comparator atoms to determine if an app's specified usage - * matches an individual trust setting. Each returns true on a match, false - * if the trust setting does not match the app's spec. - * - * A match fails iff: - * - * -- the app has specified a field, and the cert has a spec for that - * field, and the two specs do not match; - * - * OR - * - * -- the cert has a spec for the field and the app hasn't specified the field - */ -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; -} - -/* - * This one's slightly different: the match is for *this* app, not one - * specified by the app. - */ -static bool tsCheckApp( - CFDataRef certApp) -{ - if(certApp != NULL) { - SecTrustedApplicationRef appRef; - OSStatus ortn; - ortn = SecTrustedApplicationCreateWithExternalRepresentation(certApp, &appRef); - if(ortn) { - trustSettingsDbg("tsCheckApp: bad trustedApp data\n"); - return false; - } - ortn = SecTrustedApplicationValidateWithPath(appRef, NULL); - if(ortn) { - /* Not this app */ - 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) { - /* explicitly allows anything */ - return true; - } - /* cert specification must be a superset of app's intended use */ - 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; - } - /* Let CF do the string compare */ - CFStringRef cfPolicyStr = CFStringCreateWithCString(NULL, appPolicyStr, - kCFStringEncodingUTF8); - if(cfPolicyStr == NULL) { - /* I really don't see how this can happen */ - trustSettingsEvalDbg("tsCheckPolicyStr: policyStr string conversion error"); - return false; - } - - // Some trust setting strings were created with a NULL character at the - // end, which was included in the length. Strip those off before compare - - CFMutableStringRef certPolicyStrNoNULL = CFStringCreateMutableCopy(NULL, 0, certPolicyStr); - if (certPolicyStrNoNULL == NULL) { - /* I really don't see how this can happen either */ - trustSettingsEvalDbg("tsCheckPolicyStr: policyStr string conversion error 2"); - 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; -} - -/* - * Determine if a cert's trust settings dictionary satisfies the specified - * usage constraints. Returns true if so. - * Only certs with a SecTrustSettingsResult of kSecTrustSettingsResultTrustRoot - * or kSecTrustSettingsResultTrustAsRoot will match. - */ -static bool qualifyUsageWithCertDict( - CFDictionaryRef certDict, - const CSSM_OID *policyOID, /* optional */ - const char *policyStr, /* optional */ - SecTrustSettingsKeyUsage keyUsage, /* optional; default = any (actually "all" here) */ - bool onlyRoots) -{ - /* this array is optional */ - CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, - kTrustRecordTrustSettings); - CFIndex numSpecs = 0; - if(trustSettings != NULL) { - numSpecs = CFArrayGetCount(trustSettings); - } - if(numSpecs == 0) { - /* - * Trivial case: cert has no trust settings, indicating that - * it's used for everything. - */ - trustSettingsEvalDbg("qualifyUsageWithCertDict: no trust settings"); - return true; - } - for(CFIndex addDex=0; addDexmPropList = tsInitialDict(); - t->mDirty = true; - } - else { - trustSettingsDbg("TrustSettings: record not found for domain %d", - (int)domain); - delete t; - return ortn; - } - } - else { - CFRef propList(CFDataCreate(NULL, fileData.Data, fileData.Length)); - t->initFromData(propList); - alloc.free(fileData.Data); - } - t->validatePropList(trim); - - ts = t; - return errSecSuccess; -} - -/* - * Create from external data, obtained by createExternal(). - * If externalData is NULL, we'll create an empty mTrustDict. - */ -OSStatus TrustSettings::CreateTrustSettings( - SecTrustSettingsDomain domain, - CFDataRef externalData, - TrustSettings*& ts) -{ - switch(domain) { - case kSecTrustSettingsDomainUser: - case kSecTrustSettingsDomainAdmin: - case kSecTrustSettingsDomainMemory: - break; - case kSecTrustSettingsDomainSystem: /* no can do, that implies writing to it */ - default: - return errSecParam; - } - - TrustSettings* t = new TrustSettings(domain); - - if(externalData != NULL) { - t->initFromData(externalData); - } - else { - t->mPropList = tsInitialDict(); - } - t->validatePropList(TRIM_NO); /* never trim this */ - t->mDirty = true; - - ts = t; - return errSecSuccess; -} - - -TrustSettings::~TrustSettings() -{ - trustSettingsDbg("TrustSettings(domain %d) destructor", (int)mDomain); - CFRELEASE(mPropList); /* may be null if trimmed */ - CFRELEASE(mTrustDict); /* normally always non-NULL */ - -} - -/* common code to init mPropList from raw data */ -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) : ""); - if(errStr != NULL) { - CFRelease(errStr); - } - MacOSError::throwMe(errSecInvalidTrustSettings); - } -} - -/* - * Flush property list data out to disk if dirty. - */ -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: - /* caller shouldn't even try this */ - default: - trustSettingsDbg("flushToDisk, bad domain (%d)", (int)mDomain); - MacOSError::throwMe(errSecInternalComponent); - case kSecTrustSettingsDomainUser: - case kSecTrustSettingsDomainAdmin: - break; - } - - /* - * Optimization: if there are no certs in the mTrustDict dictionary, - * we tell ocspd to *remove* the settings for the specified domain. - * Having *no* settings uses less memory and is faster than having - * an empty settings file, especially for the admin domain, where we - * can avoid - * an RPC if the settings file is simply not there. - */ - CFRef xmlData; - CSSM_DATA cssmXmlData = {0, NULL}; - CFIndex numCerts = CFDictionaryGetCount(mTrustDict); - if(numCerts) { - xmlData.take(CFPropertyListCreateXMLData(NULL, mPropList)); - if(!xmlData) { - /* we've been very careful; this should never happen */ - 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); - } - - /* cook up auth stuff so ocspd can act on our behalf */ - 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); - } -} - -/* - * Obtain external representation of TrustSettings data. - */ -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; -} - -/* - * Evaluate specified cert. Returns true if we found a record for the cert - * matching specified constraints. - * Note that a true return with a value of kSecTrustSettingsResultUnspecified for - * the resultType means that a cert isn't to be trusted or untrusted - * per se; it just means that we only found allowedErrors entries. - * - * Found "allows errors" values are added to the incoming allowedErrors - * array which is reallocd as needed (and which may be NULL or non-NULL on - * entry). - */ -bool TrustSettings::evaluateCert( - CFStringRef certHashStr, - const CSSM_OID *policyOID, /* optional */ - const char *policyStr, /* optional */ - SecTrustSettingsKeyUsage keyUsage, /* optional */ - bool isRootCert, /* for checking default setting */ - CSSM_RETURN **allowedErrors, /* IN/OUT; reallocd as needed */ - uint32 *numAllowedErrors, /* IN/OUT */ - SecTrustSettingsResult *resultType, /* RETURNED */ - bool *foundAnyEntry) /* RETURNED */ -{ - assert(mTrustDict != NULL); - - /* get trust settings dictionary for this cert */ - CFDictionaryRef certDict = findDictionaryForCertHash(certHashStr); - if((certDict == NULL) && isRootCert) { - /* No? How about default root setting for this domain? */ - certDict = findDictionaryForCertHash(kSecTrustRecordDefaultRootCert); - } -#if CERT_HASH_DEBUG - /* @@@ debug only @@@ */ - /* print certificate hash and found dictionary reference */ - 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; - - /* to-be-returned array of allowed errors */ - CSSM_RETURN *allowedErrs = *allowedErrors; - uint32 numAllowedErrs = *numAllowedErrors; - - /* this means "we found something other than allowedErrors" if true */ - bool foundSettings = false; - - /* to be returned in *resultType if it ends up something other than Invalid */ - SecTrustSettingsResult returnedResult = kSecTrustSettingsResultInvalid; - - /* - * Note since we validated the entire mPropList in our constructor, and we're careful - * about what we put into it, we don't bother typechecking its contents here. - * Also note that the kTrustRecordTrustSettings entry is optional. - */ - CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict, - kTrustRecordTrustSettings); - CFIndex numSpecs = 0; - if(trustSettings != NULL) { - numSpecs = CFArrayGetCount(trustSettings); - } - if(numSpecs == 0) { - /* - * Trivial case: cert has no trust settings, indicating that - * it's used for everything. - */ - trustSettingsEvalDbg("evaluateCert: no trust settings"); - /* the default... */ - *resultType = kSecTrustSettingsResultTrustRoot; - return true; - } - - /* - * The decidedly nontrivial part: grind thru all of the cert's trust - * settings, see if the cert matches the caller's specified usage. - */ - for(CFIndex addDex=0; addDex _(SecTrustKeychainsGetMutex()); - - /* - * a set, hopefully with a good hash function for CFData, to keep track of what's - * been added to the outgoing array. - */ - CFRef certSet(CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks)); - - /* search: all certs, no attributes */ - KCCursor cursor(keychains, CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL); - Item certItem; - bool found; - do { - found = cursor->next(certItem); - if(!found) { - break; - } - CFRef certRef((SecCertificateRef)certItem->handle()); - - /* do we have an entry for this cert? */ - CFDictionaryRef certDict = findDictionaryForCert(certRef); - if(certDict == NULL) { - continue; - } - - if(!findAll) { - /* qualify */ - if(!qualifyUsageWithCertDict(certDict, policyOID, - policyString, keyUsage, onlyRoots)) { - continue; - } - } - - /* see if we already have this one - get in CFData form */ - CSSM_DATA certData; - OSStatus ortn = SecCertificateGetData(certRef, &certData); - if(ortn) { - trustSettingsEvalDbg("findQualifiedCerts: SecCertificateGetData error"); - continue; - } - CFRef cfData(CFDataCreate(NULL, certData.Data, certData.Length)); - CFDataRef cfd = cfData; - if(CFSetContainsValue(certSet, cfd)) { - trustSettingsEvalDbg("findQualifiedCerts: dup cert"); - continue; - } - else { - /* add to the tracking set, which owns the CFData now */ - CFSetAddValue(certSet, cfd); - /* and add the SecCert to caller's array, which owns that now */ - CFArrayAppendValue(certArray, certRef); - } - } while(found); -} - -/* - * Obtain trust settings for the specified cert. Returned settings array - * is in the public API form; caller must release. Returns NULL - * (does not throw) if the cert is not present in this TrustRecord. - */ -CFArrayRef TrustSettings::copyTrustSettings( - SecCertificateRef certRef) -{ - CFDictionaryRef certDict = NULL; - - /* find the on-disk usage constraints for this cert */ - 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) { - /* this field is optional */ - numSpecs = CFArrayGetCount(diskTrustSettings); - } - - /* - * Convert to API-style array of dictionaries. - * We give the caller an array even if it's empty. - */ - CFRef outArray(CFArrayCreateMutable(NULL, numSpecs, - &kCFTypeArrayCallBacks)); - for(CFIndex dex=0; dex outTsDict(CFDictionaryCreateMutable(NULL, - 0, // capacity - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - - if(certPolicy != NULL) { - /* convert OID as CFDataRef to SecPolicyRef */ - SecPolicyRef policyRef = NULL; - CSSM_OID policyOid = { CFDataGetLength(certPolicy), - (uint8 *)CFDataGetBytePtr(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); - } - CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicy, policyRef); - CFRelease(policyRef); // owned by dictionary - } - - if(certApp != NULL) { - /* convert app as CFDataRef to SecTrustedApplicationRef */ - 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); // owned by dictionary - } - - /* remaining 4 are trivial */ - if(policyStr != NULL) { - /* - * copy, since policyStr is in our mutable dictionary and could change out from - * under the caller - */ - CFStringRef str = CFStringCreateCopy(NULL, policyStr); - CFDictionaryAddValue(outTsDict, kSecTrustSettingsPolicyString, str); - CFRelease(str); // owned by dictionary - } - if(allowedErr != NULL) { - /* there is no mutable CFNumber, so.... */ - CFDictionaryAddValue(outTsDict, kSecTrustSettingsAllowedError, allowedErr); - } - if(resultType != NULL) { - CFDictionaryAddValue(outTsDict, kSecTrustSettingsResult, resultType); - } - if(keyUsage != NULL) { - CFDictionaryAddValue(outTsDict, kSecTrustSettingsKeyUsage, keyUsage); - } - CFArrayAppendValue(outArray, outTsDict); - /* outTsDict autoreleases; owned by outArray now */ - } - CFRetain(outArray); // now that it's good to go.... - return outArray; -} - -CFDateRef TrustSettings::copyModDate( - SecCertificateRef certRef) -{ - CFDictionaryRef certDict = NULL; - - /* find the on-disk usage constraints dictionary for this cert */ - certDict = findDictionaryForCert(certRef); - if(certDict == NULL) { - trustSettingsDbg("copyModDate: dictionary not found"); - return NULL; - } - CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict, kTrustRecordModDate); - if(modDate == NULL) { - return NULL; - } - - /* this only works becuase there is no mutable CFDateRef */ - CFRetain(modDate); - return modDate; -} - -/* - * Modify cert's trust settings, or add a new cert to the record. - */ -void TrustSettings::setTrustSettings( - SecCertificateRef certRef, - CFTypeRef trustSettingsDictOrArray) -{ - /* to validate, we need to know if the cert is self-signed */ - OSStatus ortn; - Boolean isSelfSigned = false; - - if(certRef == kSecTrustSettingsDefaultRootCertSetting) { - /* - * Validate settings as if this were root, specifically, - * kSecTrustSettingsResultTrustRoot (explicitly or by - * default) is OK. - */ - isSelfSigned = true; - } - else { - ortn = SecCertificateIsSelfSigned(certRef, &isSelfSigned); - if(ortn) { - MacOSError::throwMe(ortn); - } - } - - /* caller's app/policy spec OK? */ - CFRef trustSettings(validateApiTrustSettings( - trustSettingsDictOrArray, isSelfSigned)); - - /* caller is responsible for ensuring these */ - assert(mPropList != NULL); - assert(mDomain != kSecTrustSettingsDomainSystem); - - /* extract issuer and serial number from the cert, if it's a cert */ - CFRef issuer; - CFRef serial; - if(certRef != kSecTrustSettingsDefaultRootCertSetting) { - copyIssuerAndSerial(certRef, issuer.take(), serial.take()); - } - else { - UInt8 dummy; - issuer = CFDataCreate(NULL, &dummy, 0); - serial = CFDataCreate(NULL, &dummy, 0); - } - - /* SHA1 digest as string */ - CFRef certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); - if(!certHashStr) { - trustSettingsDbg("TrustSettings::setTrustSettings: CertHashStrFromCert error"); - MacOSError::throwMe(errSecItemNotFound); - } - - /* - * Find entry for this cert, if present. - */ - CFMutableDictionaryRef certDict = - (CFMutableDictionaryRef)findDictionaryForCertHash(certHashStr); - if(certDict == NULL) { - /* create new dictionary */ - certDict = CFDictionaryCreateMutable(NULL, kSecTrustRecordNumCertDictKeys, - &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if(certDict == NULL) { - MacOSError::throwMe(errSecAllocate); - } - CFDictionaryAddValue(certDict, kTrustRecordIssuer, issuer); - CFDictionaryAddValue(certDict, kTrustRecordSerialNumber, serial); - if(CFArrayGetCount(trustSettings) != 0) { - /* skip this if the settings array is empty */ - CFDictionaryAddValue(certDict, kTrustRecordTrustSettings, trustSettings); - } - tsSetModDate(certDict); - - /* add this new cert dictionary to top-level mTrustDict */ - CFDictionaryAddValue(mTrustDict, static_cast(certHashStr), certDict); - - /* mTrustDict owns the dictionary now */ - CFRelease(certDict); - } - else { - /* update */ - tsSetModDate(certDict); - if(CFArrayGetCount(trustSettings) != 0) { - CFDictionarySetValue(certDict, kTrustRecordTrustSettings, trustSettings); - } - else { - /* empty settings array: remove from dictionary */ - CFDictionaryRemoveValue(certDict, kTrustRecordTrustSettings); - } - } - mDirty = true; -} - -/* - * Delete a certificate's trust settings. - */ -void TrustSettings::deleteTrustSettings( - SecCertificateRef certRef) -{ - CFDictionaryRef certDict = NULL; - - /* caller is responsible for ensuring these */ - assert(mPropList != NULL); - assert(mDomain != kSecTrustSettingsDomainSystem); - - /* SHA1 digest as string */ - CFRef certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); - if(!certHashStr) { - MacOSError::throwMe(errSecItemNotFound); - } - - /* present in top-level mTrustDict? */ - certDict = findDictionaryForCertHash(certHashStr); - if(certDict != NULL) { - CFDictionaryRemoveValue(mTrustDict, static_cast(certHashStr)); - mDirty = true; - } - else { - /* - * Throwing this error is the only reason we don't blindly do - * a CFDictionaryRemoveValue() without first doing - * findDictionaryForCertHash(). - */ - trustSettingsDbg("TrustSettings::deleteRoot: cert dictionary not found"); - MacOSError::throwMe(errSecItemNotFound); - } -} - -#pragma mark --- Private methods --- - -/* - * Find a given cert's entry in the top-level mTrustDict. Return the - * entry as a dictionary. Returned dictionary is not refcounted. - * The mutability of the returned dictionary is the same as the mutability - * of the underlying StickRecord::mPropList, which the caller is just - * going to have to know (and cast accordingly if a mutable dictionary - * is needed). - */ -CFDictionaryRef TrustSettings::findDictionaryForCert( - SecCertificateRef certRef) -{ - CFRef certHashStr(SecTrustSettingsCertHashStrFromCert(certRef)); - if (certHashStr.get() == NULL) - { - return NULL; - } - - return findDictionaryForCertHash(static_cast(certHashStr)); -} - -/* - * Find entry in mTrustDict given cert hash string. - */ -CFDictionaryRef TrustSettings::findDictionaryForCertHash( - CFStringRef certHashStr) -{ - assert(mTrustDict != NULL); - return (CFDictionaryRef)CFDictionaryGetValue(mTrustDict, certHashStr); -} - -/* - * Validate incoming trust settings, which may be NULL, a dictionary, or - * an array of dictionaries. Convert from the API-style dictionaries - * to the internal style suitable for writing to disk as part of - * mPropList. - * - * We return a refcounted CFArray in any case if the incoming parameter is good. - */ -CFArrayRef TrustSettings::validateApiTrustSettings( - CFTypeRef trustSettingsDictOrArray, - Boolean isSelfSigned) -{ - CFArrayRef tmpInArray = NULL; - - if(trustSettingsDictOrArray == NULL) { - /* trivial case, only valid for roots */ - if(!isSelfSigned) { - trustSettingsDbg("validateApiUsageConstraints: !isSelfSigned, no settings"); - MacOSError::throwMe(errSecParam); - } - return CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); - } - else if(CFGetTypeID(trustSettingsDictOrArray) == CFDictionaryGetTypeID()) { - /* array-ize it */ - tmpInArray = CFArrayCreate(NULL, &trustSettingsDictOrArray, 1, - &kCFTypeArrayCallBacks); - } - else if(CFGetTypeID(trustSettingsDictOrArray) == CFArrayGetTypeID()) { - /* as is, refcount - we'll release later */ - 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; - - /* convert */ - for(CFIndex dex=0; dex kSecTrustRecordVersionCurrent) || - (mDictVersion == kSecTrustRecordVersionInvalid)) { - trustSettingsDbg("TrustSettings::validatePropList: incompatible version"); - abort("incompatible version", errSecInvalidTrustedRootRecord); - } - /* other backwards-compatibility handling done later, if needed, per mDictVersion */ - - 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); - } - - /* grind through the per-cert entries */ - CFIndex numCerts = CFDictionaryGetCount(mTrustDict); - const void *dictKeys[numCerts]; - const void *dictValues[numCerts]; - CFDictionaryGetKeysAndValues(mTrustDict, dictKeys, dictValues); - - for(CFIndex dex=0; dex cert = Certificate::required(certRef); - 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); -} -