/* * Copyright (c) 2002-2004 Apple Computer, 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 * 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; } CFMutableArrayRef Trust::addSpecifiedRevocationPolicies( uint32 &numAdded, Allocator &alloc) { /* policies specified by SPI not implemented */ return NULL; } void Trust::freeSpecifiedRevocationPolicies( CFArrayRef policies, uint32 numAdded, Allocator &alloc) { /* shouldn't be called */ MacOSError::throwMe(unimpErr); } 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; } } CFMutableArrayRef Trust::addPreferenceRevocationPolicies( uint32 &numAdded, Allocator &alloc) { Dictionary *prefsDict = NULL; numAdded = 0; /* any per-user prefs? */ try { prefsDict = new Dictionary(kSecRevocationDomain, Dictionary::US_User); } catch(...) {} if(prefsDict == NULL) { /* try system prefs */ try { prefsDict = new Dictionary(kSecRevocationDomain, Dictionary::US_System); } catch(...) { return NULL; } } bool doOcsp = false; bool doCrl = false; CFStringRef val; SecRevocationPolicyStyle ocspStyle = kSecDisabled; SecRevocationPolicyStyle crlStyle = kSecDisabled; 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) { delete prefsDict; 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; } } /* We're adding something to mPolicies, so make a copy we can work with */ CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies); if(policies == NULL) { delete prefsDict; 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)); } delete prefsDict; return policies; } /* * Called when we created the last numAdded Policies in the specified Policy array */ void Trust::freePreferenceRevocationPolicies( 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); } } CFRelease(policies); }