X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/TrustRevocation.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/TrustRevocation.cpp b/Security/libsecurity_keychain/lib/TrustRevocation.cpp new file mode 100644 index 00000000..591ff209 --- /dev/null +++ b/Security/libsecurity_keychain/lib/TrustRevocation.cpp @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2002-2004,2011-2012,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@ + */ + +/* +* TrustRevocation.cpp - private revocation policy manipulation +*/ + +#include +#include +#include +#include +#include "SecBridge.h" +#include +#include + +/* + * These may go into an SPI header for the SecTrust object. + */ +typedef enum { + /* this revocation policy disabled */ + kSecDisabled, + /* try, but tolerate inability to complete */ + kSecBestAttempt, + /* require successful revocation check if certificate indicates + * the policy is supported */ + kSecRequireIfPresentInCertificate, + /* require for every cert */ + kSecRequireForAllCertificates +} SecRevocationPolicyStyle; + +using namespace KeychainCore; + +/* + * Given an app-specified array of Policies, determine if at least one of them + * matches the given policy OID. + */ +bool Trust::policySpecified(CFArrayRef policies, const CSSM_OID &inOid) +{ + if(policies == NULL) { + return false; + } + CFIndex numPolicies = CFArrayGetCount(policies); + for(CFIndex dex=0; dex pol = Policy::required(SecPolicyRef(secPol)); + const CssmOid &oid = pol->oid(); + if(oid == CssmOid::overlay(inOid)) { + return true; + } + } + return false; +} + +/* + * Given an app-specified array of Policies, determine if at least one of them + * is an explicit revocation policy. + */ +bool Trust::revocationPolicySpecified(CFArrayRef policies) +{ + if(policies == NULL) { + return false; + } + CFIndex numPolicies = CFArrayGetCount(policies); + for(CFIndex dex=0; dex pol = Policy::required(SecPolicyRef(secPol)); + const CssmOid &oid = pol->oid(); + if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { + return true; + } + if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { + return true; + } + } + return false; +} + +/* + * Replace a unified revocation policy instance in the mPolicies array + * with specific instances of the OCSP and/or CRL policies which the TP + * module understands. Returns a (possibly) modified copy of the mPolicies + * array, which the caller is responsible for releasing. + */ +CFMutableArrayRef Trust::convertRevocationPolicy( + uint32 &numAdded, + Allocator &alloc) +{ + numAdded = 0; + if (!mPolicies) { + return NULL; + } + CFIndex numPolicies = CFArrayGetCount(mPolicies); + CFAllocatorRef allocator = CFGetAllocator(mPolicies); + CFMutableArrayRef policies = CFArrayCreateMutableCopy(allocator, numPolicies, mPolicies); + SecPolicyRef revPolicy = NULL; + for(CFIndex dex=0; dex pol = Policy::required(SecPolicyRef(secPol)); + const CssmOid &oid = pol->oid(); + if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION)) { + CFRetain(secPol); + if (revPolicy) + CFRelease(revPolicy); + revPolicy = secPol; + CFArrayRemoveValueAtIndex(policies, dex--); + numPolicies--; + } + } + if(!revPolicy) { + CFRelease(policies); + return NULL; + } + + SecPointer ocspPolicy; + SecPointer crlPolicy; + + // fetch policy value + CFOptionFlags policyFlags = kSecRevocationUseAnyAvailableMethod; + CSSM_DATA policyValue = { 0, NULL }; + OSStatus status = SecPolicyGetValue(revPolicy, &policyValue); + if (!status && policyValue.Data) { + policyFlags = (CFOptionFlags) *((CFOptionFlags*)policyValue.Data); + } + if (mNetworkPolicy == useNetworkDisabled) { + policyFlags = 0 | kSecRevocationNetworkAccessDisabled; + } + CFRelease(revPolicy); // all done with this policy reference + + if (policyFlags & kSecRevocationOCSPMethod) { + /* cook up a new Policy object */ + ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)); + CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags = CSSM_TP_ACTION_OCSP_SUFFICIENT; + if (policyFlags & kSecRevocationRequirePositiveResponse) { + // FIXME: possibly set CSSM_TP_ACTION_REQUIRE_REV_PER_CERT in actionFlags, + // but verify whether that only applies to certs which specify a revocation method + } + CSSM_APPLE_TP_OCSP_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; + opts.Flags = ocspFlags; + + /* Policy manages its own copy of this data */ + CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; + ocspPolicy->value() = optData; + + /* Policies array retains the Policy object */ + CFArrayAppendValue(policies, ocspPolicy->handle(false)); + numAdded++; + } + if (policyFlags & kSecRevocationCRLMethod) { + /* cook up a new Policy object */ + crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)); + CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags = 0; + if (policyFlags & kSecRevocationRequirePositiveResponse) { + // FIXME: possibly set CSSM_TP_ACTION_REQUIRE_REV_PER_CERT in actionFlags, + // but verify whether that only applies to certs which specify a revocation method + } + CSSM_APPLE_TP_CRL_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; + opts.CrlFlags = crlFlags; + + /* Policy manages its own copy of this data */ + CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; + crlPolicy->value() = optData; + + /* Policies array retains the Policy object */ + CFArrayAppendValue(policies, crlPolicy->handle(false)); + numAdded++; + } + return policies; +} + +static SecRevocationPolicyStyle parseRevStyle(CFStringRef val) +{ + if(CFEqual(val, kSecRevocationOff)) { + return kSecDisabled; + } + else if(CFEqual(val, kSecRevocationBestAttempt)) { + return kSecBestAttempt; + } + else if(CFEqual(val, kSecRevocationRequireIfPresent)) { + return kSecRequireIfPresentInCertificate; + } + else if(CFEqual(val, kSecRevocationRequireForAll)) { + return kSecRequireForAllCertificates; + } + else { + return kSecDisabled; + } +} + +CFDictionaryRef Trust::defaultRevocationSettings() +{ + /* + defaults read ~/Library/Preferences/com.apple.security.revocation + { + CRLStyle = BestAttempt; + CRLSufficientPerCert = 1; + OCSPStyle = BestAttempt; + OCSPSufficientPerCert = 1; + RevocationFirst = OCSP; + } + */ + const void *keys[] = { + kSecRevocationCrlStyle, + kSecRevocationCRLSufficientPerCert, + kSecRevocationOcspStyle, + kSecRevocationOCSPSufficientPerCert, + kSecRevocationWhichFirst + }; + const void *values[] = { + kSecRevocationBestAttempt, + kCFBooleanTrue, + kSecRevocationBestAttempt, + kCFBooleanTrue, + kSecRevocationOcspFirst + }; + + return CFDictionaryCreate(kCFAllocatorDefault, keys, + values, sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +} + +CFMutableArrayRef Trust::addPreferenceRevocationPolicies( + uint32 &numAdded, + Allocator &alloc) +{ + numAdded = 0; + + /* any per-user prefs? */ + Dictionary* pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true); + if (pd) + { + if (!pd->dict()) { + delete pd; + pd = NULL; + } + } + + if(pd == NULL) + { + pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_System, true); + if (!pd->dict()) { + delete pd; + pd = NULL; + } + } + + if(pd == NULL) + { + CFDictionaryRef tempDict = defaultRevocationSettings(); + if (tempDict == NULL) + return NULL; + + pd = new Dictionary(tempDict); + CFRelease(tempDict); + } + + auto_ptr prefsDict(pd); + + bool doOcsp = false; + bool doCrl = false; + CFStringRef val; + SecRevocationPolicyStyle ocspStyle = kSecBestAttempt; + SecRevocationPolicyStyle crlStyle = kSecBestAttempt; + SecPointer ocspPolicy; + SecPointer crlPolicy; + + /* Are any revocation policies enabled? */ + val = prefsDict->getStringValue(kSecRevocationOcspStyle); + if(val != NULL) { + ocspStyle = parseRevStyle(val); + if(ocspStyle != kSecDisabled) { + doOcsp = true; + } + } + val = prefsDict->getStringValue(kSecRevocationCrlStyle); + if(val != NULL) { + crlStyle = parseRevStyle(val); + if(crlStyle != kSecDisabled) { + doCrl = true; + } + } + if(!doCrl && !doOcsp) { + return NULL; + } + + /* which policy first? */ + bool ocspFirst = true; // default if both present + if(doCrl && doOcsp) { + val = prefsDict->getStringValue(kSecRevocationWhichFirst); + if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) { + ocspFirst = false; + } + } + + /* Must have at least one caller-specified policy + * (if they didn't specify any, it's a no-op evaluation, and if they wanted + * revocation checking only, that policy would already be in mPolicies) */ + if (!mPolicies || !CFArrayGetCount(mPolicies)) + return NULL; + + /* We're adding something to mPolicies, so make a copy we can work with */ + CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies); + if(policies == NULL) { + throw std::bad_alloc(); + } + + if(doOcsp) { + /* Cook up a new Policy object */ + ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)); + CSSM_APPLE_TP_OCSP_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; + + /* Now fill in the OCSP-related blanks */ + switch(ocspStyle) { + case kSecDisabled: + assert(0); + break; + case kSecBestAttempt: + /* default, nothing to set */ + break; + case kSecRequireIfPresentInCertificate: + opts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT; + break; + case kSecRequireForAllCertificates: + opts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT; + break; + } + + if(prefsDict->getBoolValue(kSecRevocationOCSPSufficientPerCert)) { + opts.Flags |= CSSM_TP_ACTION_OCSP_SUFFICIENT; + } + + val = prefsDict->getStringValue(kSecOCSPLocalResponder); + if(val != NULL) { + CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, + val, kCFStringEncodingUTF8, 0); + CFIndex len = CFDataGetLength(cfData); + opts.LocalResponder = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); + opts.LocalResponder->Data = (uint8 *)alloc.malloc(len); + opts.LocalResponder->Length = len; + memmove(opts.LocalResponder->Data, CFDataGetBytePtr(cfData), len); + CFRelease(cfData); + } + + /* Policy manages its own copy of this data */ + CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; + ocspPolicy->value() = optData; + numAdded++; + } + + if(doCrl) { + /* Cook up a new Policy object */ + crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)); + CSSM_APPLE_TP_CRL_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; + + /* Now fill in the CRL-related blanks */ + opts.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; // default true + switch(crlStyle) { + case kSecDisabled: + assert(0); + break; + case kSecBestAttempt: + /* default, nothing to set */ + break; + case kSecRequireIfPresentInCertificate: + opts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT; + break; + case kSecRequireForAllCertificates: + opts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT; + break; + } + if(prefsDict->getBoolValue(kSecRevocationCRLSufficientPerCert)) { + opts.CrlFlags |= CSSM_TP_ACTION_CRL_SUFFICIENT; + } + + /* Policy manages its own copy of this data */ + CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; + crlPolicy->value() = optData; + numAdded++; + } + + /* append in order */ + if(doOcsp) { + if(doCrl) { + if(ocspFirst) { + /* these SecCFObject go away when the policies array does */ + CFArrayAppendValue(policies, ocspPolicy->handle(false)); + CFArrayAppendValue(policies, crlPolicy->handle(false)); + } + else { + CFArrayAppendValue(policies, crlPolicy->handle(false)); + CFArrayAppendValue(policies, ocspPolicy->handle(false)); + } + } + else { + CFArrayAppendValue(policies, ocspPolicy->handle(false)); + } + + } + else { + assert(doCrl); + CFArrayAppendValue(policies, crlPolicy->handle(false)); + } + return policies; +} + +/* + * Called when we created the last numAdded Policies in the specified Policy array + * (only frees the policy data associated with the extra policies that we inserted; + * this does not free the policies array itself.) + */ +void Trust::freeAddedRevocationPolicyData( + CFArrayRef policies, + uint32 numAdded, + Allocator &alloc) +{ + uint32 numPolicies = (uint32)CFArrayGetCount(policies); + if(numPolicies < numAdded) { + /* should never happen - throw? */ + assert(0); + return; + } + for(unsigned dex=numPolicies-numAdded; dex pol = Policy::required(SecPolicyRef(secPol)); + Policy *pol = Policy::required(secPol); + const CssmOid &oid = pol->oid(); // required + const CssmData &optData = pol->value(); // optional + + if(optData.Data) { + if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { + /* currently no CRL-specific policy data */ + } + else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { + CSSM_APPLE_TP_OCSP_OPTIONS *opts = (CSSM_APPLE_TP_OCSP_OPTIONS *)optData.Data; + if(opts->LocalResponder != NULL) { + if(opts->LocalResponder->Data != NULL) { + alloc.free(opts->LocalResponder->Data); + } + alloc.free(opts->LocalResponder); + } + } + // managed by Policy alloc.free(optData.Data); + } + } +} + +/* + * Comparator function to correctly order revocation policies. + */ +static CFComparisonResult compareRevocationPolicies( + const void *policy1, + const void *policy2, + void *context) +{ + SecPointer pol1 = Policy::required(SecPolicyRef(policy1)); + SecPointer pol2 = Policy::required(SecPolicyRef(policy2)); + const CssmOid &oid1 = pol1->oid(); + const CssmOid &oid2 = pol2->oid(); + if(oid1 == oid2) { + return kCFCompareEqualTo; + } + bool ocspFirst = true; + if(context != NULL && CFEqual((CFBooleanRef)context, kCFBooleanFalse)) { + ocspFirst = false; + } + const CssmOid lastRevocationOid = (ocspFirst) ? + CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL) : + CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP); + const CssmOid firstRevocationOid = (ocspFirst) ? + CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP) : + CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL); + if(oid1 == lastRevocationOid) { + /* should be ordered last, after all other policies */ + return kCFCompareGreaterThan; + } + if(oid1 == firstRevocationOid) { + /* should be ordered after any policy except lastRevocationOid */ + if(oid2 == lastRevocationOid) { + return kCFCompareLessThan; + } + return kCFCompareGreaterThan; + } + /* normal policy in first position, anything else in second position */ + return kCFCompareLessThan; +} + +/* + * This method reorders any revocation policies which may be present + * in the provided array so they are at the end and evaluated last. + */ +void Trust::orderRevocationPolicies( + CFMutableArrayRef policies) +{ + if(!policies || CFGetTypeID(policies) != CFArrayGetTypeID()) { + return; + } + /* check revocation prefs to determine which policy goes first */ + CFBooleanRef ocspFirst = kCFBooleanTrue; + Dictionary* pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true); + if (pd) { + if (!pd->dict()) { + delete pd; + } else { + auto_ptr prefsDict(pd); + CFStringRef val = prefsDict->getStringValue(kSecRevocationWhichFirst); + if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) { + ocspFirst = kCFBooleanFalse; + } + } + } +#if POLICIES_DEBUG + CFShow(policies); // before sort + CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst); + CFShow(policies); // after sort, to see what changed + // check that policy order is what we expect + CFIndex numPolicies = CFArrayGetCount(policies); + for(CFIndex dex=0; dex pol = Policy::required(SecPolicyRef(secPol)); + const CssmOid &oid = pol->oid(); + if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { + CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = OCSP"), dex); + CFShow(s); + CFRelease(s); + } + else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { + CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = CRL"), dex); + CFShow(s); + CFRelease(s); + } + else { + CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = normal"), dex); + CFShow(s); + CFRelease(s); + } + } +#else + CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst); +#endif +} + +/* + * This method returns a copy of the mPolicies array which ensures that + * revocation checking (preferably OCSP, otherwise CRL) will be attempted. + * + * If OCSP is already in the mPolicies array, this makes sure the + * CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT and CSSM_TP_ACTION_OCSP_SUFFICIENT + * flags are set. If it's not already in the array, a new policy object is added. + * + * If CRL is already in the mPolicies array, this makes sure the + * CSSM_TP_ACTION_FETCH_CRL_FROM_NET and CSSM_TP_ACTION_CRL_SUFFICIENT flags are + * set. If it's not already in the array, a new policy object is added. + * + * Caller is responsible for releasing the returned policies array. + */ +CFMutableArrayRef Trust::forceRevocationPolicies( + uint32 &numAdded, + Allocator &alloc, + bool requirePerCert) +{ + SecPointer ocspPolicy; + SecPointer crlPolicy; + CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags; + CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags; + bool hasOcspPolicy = false; + bool hasCrlPolicy = false; + numAdded = 0; + + ocspFlags = CSSM_TP_ACTION_OCSP_SUFFICIENT; + crlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT; + if (requirePerCert) { + ocspFlags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT; + crlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT; + } + + CFIndex numPolicies = (mPolicies) ? CFArrayGetCount(mPolicies) : 0; + for(CFIndex dex=0; dex pol = Policy::required(SecPolicyRef(secPol)); + const CssmOid &oid = pol->oid(); + const CssmData &optData = pol->value(); + if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { + // make sure OCSP options are set correctly + CSSM_APPLE_TP_OCSP_OPTIONS *opts = (CSSM_APPLE_TP_OCSP_OPTIONS *)optData.Data; + if (opts) { + opts->Flags |= ocspFlags; + } else { + CSSM_APPLE_TP_OCSP_OPTIONS newOpts; + memset(&newOpts, 0, sizeof(newOpts)); + newOpts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; + newOpts.Flags = ocspFlags; + CSSM_DATA optData = {sizeof(newOpts), (uint8 *)&newOpts}; + pol->value() = optData; + } + hasOcspPolicy = true; + } + else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { + // make sure CRL options are set correctly + CSSM_APPLE_TP_CRL_OPTIONS *opts = (CSSM_APPLE_TP_CRL_OPTIONS *)optData.Data; + if (opts) { + opts->CrlFlags |= crlFlags; + } else { + CSSM_APPLE_TP_CRL_OPTIONS newOpts; + memset(&newOpts, 0, sizeof(newOpts)); + newOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; + newOpts.CrlFlags = crlFlags; + CSSM_DATA optData = {sizeof(newOpts), (uint8 *)&newOpts}; + pol->value() = optData; + } + hasCrlPolicy = true; + } + } + + /* We're potentially adding something to mPolicies, so make a copy we can work with */ + CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies); + if(policies == NULL) { + throw std::bad_alloc(); + } + + if(!hasOcspPolicy) { + /* Cook up a new Policy object */ + ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)); + CSSM_APPLE_TP_OCSP_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; + opts.Flags = ocspFlags; + + /* Check prefs dict for local responder info */ + Dictionary *prefsDict = NULL; + try { /* per-user prefs */ + prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true); + if (!prefsDict->dict()) { + delete prefsDict; + prefsDict = NULL; + } + } + catch(...) {} + if(prefsDict == NULL) { + try { /* system prefs */ + prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_System, true); + if (!prefsDict->dict()) { + delete prefsDict; + prefsDict = NULL; + } + } + catch(...) {} + } + if(prefsDict != NULL) { + CFStringRef val = prefsDict->getStringValue(kSecOCSPLocalResponder); + if(val != NULL) { + CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, + val, kCFStringEncodingUTF8, 0); + CFIndex len = CFDataGetLength(cfData); + opts.LocalResponder = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); + opts.LocalResponder->Data = (uint8 *)alloc.malloc(len); + opts.LocalResponder->Length = len; + memmove(opts.LocalResponder->Data, CFDataGetBytePtr(cfData), len); + CFRelease(cfData); + } + } + + /* Policy manages its own copy of the options data */ + CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; + ocspPolicy->value() = optData; + + /* Policies array retains the Policy object */ + CFArrayAppendValue(policies, ocspPolicy->handle(false)); + numAdded++; + + if(prefsDict != NULL) + delete prefsDict; + } + + if(!hasCrlPolicy) { + /* Cook up a new Policy object */ + crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)); + CSSM_APPLE_TP_CRL_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; + opts.CrlFlags = crlFlags; + + /* Policy manages its own copy of this data */ + CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; + crlPolicy->value() = optData; + + /* Policies array retains the Policy object */ + CFArrayAppendValue(policies, crlPolicy->handle(false)); + numAdded++; + } + + return policies; +}