+/*
+ * 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 <security_keychain/Trust.h>
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/simpleprefs.h>
+#include <CoreFoundation/CFData.h>
+#include "SecBridge.h"
+#include <Security/cssmapplePriv.h>
+#include <Security/oidsalg.h>
+
+/*
+ * 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<numPolicies; dex++) {
+ SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
+ SecPointer<Policy> 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<numPolicies; dex++) {
+ SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
+ SecPointer<Policy> 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<numPolicies; dex++) {
+ SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
+ SecPointer<Policy> 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<Policy> ocspPolicy;
+ SecPointer<Policy> crlPolicy;
+
+ // fetch policy value
+ CFOptionFlags policyFlags = kSecRevocationUseAnyAvailableMethod;
+ CSSM_DATA policyValue = { 0, NULL };
+ try {
+ policyValue = Policy::required(revPolicy)->value();
+ }
+ catch (...) {
+ policyValue.Data = NULL;
+ }
+ if (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) {
+ ocspFlags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT;
+ }
+ 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) {
+ crlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT;
+ }
+ 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(
+ bool ocspEnabledOnBestAttempt,
+ bool crlEnabledOnBestAttempt,
+ 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<Dictionary> prefsDict(pd);
+
+ bool doOcsp = false;
+ bool doCrl = false;
+ CFStringRef val;
+ SecRevocationPolicyStyle ocspStyle = kSecBestAttempt;
+ SecRevocationPolicyStyle crlStyle = kSecBestAttempt;
+ SecPointer<Policy> ocspPolicy;
+ SecPointer<Policy> crlPolicy;
+
+ /* Are any revocation policies enabled? */
+ val = prefsDict->getStringValue(kSecRevocationOcspStyle);
+ if(val != NULL) {
+ ocspStyle = parseRevStyle(val);
+ if(ocspStyle != kSecDisabled) {
+ doOcsp = true;
+ }
+ if(ocspStyle == kSecBestAttempt) {
+ doOcsp = ocspEnabledOnBestAttempt;
+ }
+ }
+ val = prefsDict->getStringValue(kSecRevocationCrlStyle);
+ if(val != NULL) {
+ crlStyle = parseRevStyle(val);
+ if(crlStyle != kSecDisabled) {
+ doCrl = true;
+ }
+ if(crlStyle == kSecBestAttempt) {
+ doCrl = crlEnabledOnBestAttempt;
+ }
+ }
+
+ 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 if(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<numPolicies; dex++) {
+ SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
+ //SecPointer<Policy> 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<Policy> pol1 = Policy::required(SecPolicyRef(policy1));
+ SecPointer<Policy> 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<Dictionary> 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<numPolicies; dex++) {
+ SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
+ SecPointer<Policy> 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(
+ bool ocspEnabled,
+ bool crlEnabled,
+ uint32 &numAdded,
+ Allocator &alloc,
+ bool requirePerCert)
+{
+ SecPointer<Policy> ocspPolicy;
+ SecPointer<Policy> 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<numPolicies; dex++) {
+ SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(mPolicies, dex);
+ SecPointer<Policy> 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 && ocspEnabled) {
+ /* 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 && crlEnabled) {
+ /* 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;
+}