/* * 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); }