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::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;
}
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::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;
CFIndex policyValue = kSecRevocationUseAnyAvailableMethod; CFRelease(revPolicy); if (policyValue & kSecRevocationOCSPMethod) {
ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP));
CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
CSSM_APPLE_TP_OCSP_OPTIONS opts;
memset(&opts, 0, sizeof(opts));
opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
opts.Flags = ocspFlags;
CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
ocspPolicy->value() = optData;
CFArrayAppendValue(policies, ocspPolicy->handle(false));
numAdded++;
}
if (policyValue & kSecRevocationCRLMethod) {
crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL));
CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags = 0;
CSSM_APPLE_TP_CRL_OPTIONS opts;
memset(&opts, 0, sizeof(opts));
opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
opts.CrlFlags = crlFlags;
CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
crlPolicy->value() = optData;
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()
{
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;
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;
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;
}
bool ocspFirst = true; if(doCrl && doOcsp) {
val = prefsDict->getStringValue(kSecRevocationWhichFirst);
if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) {
ocspFirst = false;
}
}
if (!mPolicies || !CFArrayGetCount(mPolicies))
return NULL;
CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies);
if(policies == NULL) {
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));
}
return policies;
}
void Trust::freeAddedRevocationPolicyData(
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);
}
}
}
}
}
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) {
return kCFCompareGreaterThan;
}
if(oid1 == firstRevocationOid) {
if(oid2 == lastRevocationOid) {
return kCFCompareLessThan;
}
return kCFCompareGreaterThan;
}
return kCFCompareLessThan;
}
void Trust::orderRevocationPolicies(
CFMutableArrayRef policies)
{
if(!policies || CFGetTypeID(policies) != CFArrayGetTypeID()) {
return;
}
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); CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst);
CFShow(policies); 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
}
CFMutableArrayRef Trust::forceRevocationPolicies(
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)) {
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)) {
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;
}
}
CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies);
if(policies == NULL) {
throw std::bad_alloc();
}
if(!hasOcspPolicy) {
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;
Dictionary *prefsDict = NULL;
try {
prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true);
if (!prefsDict->dict()) {
delete prefsDict;
prefsDict = NULL;
}
}
catch(...) {}
if(prefsDict == NULL) {
try {
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);
}
}
CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
ocspPolicy->value() = optData;
CFArrayAppendValue(policies, ocspPolicy->handle(false));
numAdded++;
if(prefsDict != NULL)
delete prefsDict;
}
if(!hasCrlPolicy) {
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;
CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
crlPolicy->value() = optData;
CFArrayAppendValue(policies, crlPolicy->handle(false));
numAdded++;
}
return policies;
}