TrustRevocation.cpp [plain text]
#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>
typedef enum {
kSecDisabled,
kSecBestAttempt,
kSecRequireIfPresentInCertificate,
kSecRequireForAllCertificates
} SecRevocationPolicyStyle;
using namespace KeychainCore;
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;
}
CFMutableArrayRef Trust::addSpecifiedRevocationPolicies(
uint32 &numAdded,
Allocator &alloc)
{
return NULL;
}
void Trust::freeSpecifiedRevocationPolicies(
CFArrayRef policies,
uint32 numAdded,
Allocator &alloc)
{
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;
try {
prefsDict = new Dictionary(kSecRevocationDomain, Dictionary::US_User, true);
if (!prefsDict->dict()) {
delete prefsDict;
prefsDict = NULL;
}
}
catch(...) {}
if(prefsDict == NULL) {
try {
prefsDict = new Dictionary(kSecRevocationDomain, Dictionary::US_System, true);
if (!prefsDict->dict()) {
delete prefsDict;
return NULL;
}
}
catch(...) {
return NULL;
}
}
bool doOcsp = false;
bool doCrl = false;
CFStringRef val;
SecRevocationPolicyStyle ocspStyle = kSecDisabled;
SecRevocationPolicyStyle crlStyle = kSecDisabled;
SecPointer<Policy> ocspPolicy;
SecPointer<Policy> crlPolicy;
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;
}
bool ocspFirst = true; if(doCrl && doOcsp) {
val = prefsDict->getStringValue(kSecRevocationWhichFirst);
if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) {
ocspFirst = false;
}
}
CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies);
if(policies == NULL) {
delete prefsDict;
throw std::bad_alloc();
}
if(doOcsp) {
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;
switch(ocspStyle) {
case kSecDisabled:
assert(0);
break;
case kSecBestAttempt:
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);
}
CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
ocspPolicy->value() = optData;
numAdded++;
}
if(doCrl) {
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 = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; switch(crlStyle) {
case kSecDisabled:
assert(0);
break;
case kSecBestAttempt:
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;
}
CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
crlPolicy->value() = optData;
numAdded++;
}
if(doOcsp) {
if(doCrl) {
if(ocspFirst) {
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;
}
void Trust::freePreferenceRevocationPolicies(
CFArrayRef policies,
uint32 numAdded,
Allocator &alloc)
{
uint32 numPolicies = (uint32)CFArrayGetCount(policies);
if(numPolicies < numAdded) {
assert(0);
return;
}
for(unsigned dex=numPolicies-numAdded; dex<numPolicies; dex++) {
SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
Policy *pol = Policy::required(secPol);
const CssmOid &oid = pol->oid(); const CssmData &optData = pol->value();
if(optData.Data) {
if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) {
}
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);
}
}
}
}
CFRelease(policies);
}