1/* 2 * Copyright (c) 2002-2004,2011-2012,2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25* TrustRevocation.cpp - private revocation policy manipulation 26*/ 27 28#include <security_keychain/Trust.h> 29#include <security_utilities/cfutilities.h> 30#include <security_utilities/simpleprefs.h> 31#include <CoreFoundation/CFData.h> 32#include "SecBridge.h" 33#include <Security/cssmapplePriv.h> 34#include <Security/oidsalg.h> 35 36/* 37 * These may go into an SPI header for the SecTrust object. 38 */ 39typedef enum { 40 /* this revocation policy disabled */ 41 kSecDisabled, 42 /* try, but tolerate inability to complete */ 43 kSecBestAttempt, 44 /* require successful revocation check if certificate indicates 45 * the policy is supported */ 46 kSecRequireIfPresentInCertificate, 47 /* require for every cert */ 48 kSecRequireForAllCertificates 49} SecRevocationPolicyStyle; 50 51using namespace KeychainCore; 52 53/* 54 * Given an app-specified array of Policies, determine if at least one of them 55 * matches the given policy OID. 56 */ 57bool Trust::policySpecified(CFArrayRef policies, const CSSM_OID &inOid) 58{ 59 if(policies == NULL) { 60 return false; 61 } 62 CFIndex numPolicies = CFArrayGetCount(policies); 63 for(CFIndex dex=0; dex<numPolicies; dex++) { 64 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex); 65 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol)); 66 const CssmOid &oid = pol->oid(); 67 if(oid == CssmOid::overlay(inOid)) { 68 return true; 69 } 70 } 71 return false; 72} 73 74/* 75 * Given an app-specified array of Policies, determine if at least one of them 76 * is an explicit revocation policy. 77 */ 78bool Trust::revocationPolicySpecified(CFArrayRef policies) 79{ 80 if(policies == NULL) { 81 return false; 82 } 83 CFIndex numPolicies = CFArrayGetCount(policies); 84 for(CFIndex dex=0; dex<numPolicies; dex++) { 85 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex); 86 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol)); 87 const CssmOid &oid = pol->oid(); 88 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { 89 return true; 90 } 91 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { 92 return true; 93 } 94 } 95 return false; 96} 97 98/* 99 * Replace a unified revocation policy instance in the mPolicies array 100 * with specific instances of the OCSP and/or CRL policies which the TP 101 * module understands. Returns a (possibly) modified copy of the mPolicies 102 * array, which the caller is responsible for releasing. 103 */ 104CFMutableArrayRef Trust::convertRevocationPolicy( 105 uint32 &numAdded, 106 Allocator &alloc) 107{ 108 numAdded = 0; 109 if (!mPolicies) { 110 return NULL; 111 } 112 CFIndex numPolicies = CFArrayGetCount(mPolicies); 113 CFAllocatorRef allocator = CFGetAllocator(mPolicies); 114 CFMutableArrayRef policies = CFArrayCreateMutableCopy(allocator, numPolicies, mPolicies); 115 SecPolicyRef revPolicy = NULL; 116 for(CFIndex dex=0; dex<numPolicies; dex++) { 117 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex); 118 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol)); 119 const CssmOid &oid = pol->oid(); 120 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION)) { 121 CFRetain(secPol); 122 if (revPolicy) 123 CFRelease(revPolicy); 124 revPolicy = secPol; 125 CFArrayRemoveValueAtIndex(policies, dex--); 126 numPolicies--; 127 } 128 } 129 if(!revPolicy) { 130 CFRelease(policies); 131 return NULL; 132 } 133 134 SecPointer<Policy> ocspPolicy; 135 SecPointer<Policy> crlPolicy; 136 137 // fetch policy value 138 CFOptionFlags policyFlags = kSecRevocationUseAnyAvailableMethod; 139 CSSM_DATA policyValue = { 0, NULL }; 140 OSStatus status = SecPolicyGetValue(revPolicy, &policyValue); 141 if (!status && policyValue.Data) { 142 policyFlags = (CFOptionFlags) *((CFOptionFlags*)policyValue.Data); 143 } 144 if (mNetworkPolicy == useNetworkDisabled) { 145 policyFlags = 0 | kSecRevocationNetworkAccessDisabled; 146 } 147 CFRelease(revPolicy); // all done with this policy reference 148 149 if (policyFlags & kSecRevocationOCSPMethod) { 150 /* cook up a new Policy object */ 151 ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)); 152 CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags = CSSM_TP_ACTION_OCSP_SUFFICIENT; 153 if (policyFlags & kSecRevocationRequirePositiveResponse) { 154 // FIXME: possibly set CSSM_TP_ACTION_REQUIRE_REV_PER_CERT in actionFlags, 155 // but verify whether that only applies to certs which specify a revocation method 156 } 157 CSSM_APPLE_TP_OCSP_OPTIONS opts; 158 memset(&opts, 0, sizeof(opts)); 159 opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; 160 opts.Flags = ocspFlags; 161 162 /* Policy manages its own copy of this data */ 163 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; 164 ocspPolicy->value() = optData; 165 166 /* Policies array retains the Policy object */ 167 CFArrayAppendValue(policies, ocspPolicy->handle(false)); 168 numAdded++; 169 } 170 if (policyFlags & kSecRevocationCRLMethod) { 171 /* cook up a new Policy object */ 172 crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)); 173 CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags = 0; 174 if (policyFlags & kSecRevocationRequirePositiveResponse) { 175 // FIXME: possibly set CSSM_TP_ACTION_REQUIRE_REV_PER_CERT in actionFlags, 176 // but verify whether that only applies to certs which specify a revocation method 177 } 178 CSSM_APPLE_TP_CRL_OPTIONS opts; 179 memset(&opts, 0, sizeof(opts)); 180 opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; 181 opts.CrlFlags = crlFlags; 182 183 /* Policy manages its own copy of this data */ 184 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; 185 crlPolicy->value() = optData; 186 187 /* Policies array retains the Policy object */ 188 CFArrayAppendValue(policies, crlPolicy->handle(false)); 189 numAdded++; 190 } 191 return policies; 192} 193 194static SecRevocationPolicyStyle parseRevStyle(CFStringRef val) 195{ 196 if(CFEqual(val, kSecRevocationOff)) { 197 return kSecDisabled; 198 } 199 else if(CFEqual(val, kSecRevocationBestAttempt)) { 200 return kSecBestAttempt; 201 } 202 else if(CFEqual(val, kSecRevocationRequireIfPresent)) { 203 return kSecRequireIfPresentInCertificate; 204 } 205 else if(CFEqual(val, kSecRevocationRequireForAll)) { 206 return kSecRequireForAllCertificates; 207 } 208 else { 209 return kSecDisabled; 210 } 211} 212 213CFDictionaryRef Trust::defaultRevocationSettings() 214{ 215 /* 216 defaults read ~/Library/Preferences/com.apple.security.revocation 217 { 218 CRLStyle = BestAttempt; 219 CRLSufficientPerCert = 1; 220 OCSPStyle = BestAttempt; 221 OCSPSufficientPerCert = 1; 222 RevocationFirst = OCSP; 223 } 224 */ 225 const void *keys[] = { 226 kSecRevocationCrlStyle, 227 kSecRevocationCRLSufficientPerCert, 228 kSecRevocationOcspStyle, 229 kSecRevocationOCSPSufficientPerCert, 230 kSecRevocationWhichFirst 231 }; 232 const void *values[] = { 233 kSecRevocationBestAttempt, 234 kCFBooleanTrue, 235 kSecRevocationBestAttempt, 236 kCFBooleanTrue, 237 kSecRevocationOcspFirst 238 }; 239 240 return CFDictionaryCreate(kCFAllocatorDefault, keys, 241 values, sizeof(keys) / sizeof(*keys), 242 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 243} 244 245CFMutableArrayRef Trust::addPreferenceRevocationPolicies( 246 uint32 &numAdded, 247 Allocator &alloc) 248{ 249 numAdded = 0; 250 251 /* any per-user prefs? */ 252 Dictionary* pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true); 253 if (pd) 254 { 255 if (!pd->dict()) { 256 delete pd; 257 pd = NULL; 258 } 259 } 260 261 if(pd == NULL) 262 { 263 pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_System, true); 264 if (!pd->dict()) { 265 delete pd; 266 pd = NULL; 267 } 268 } 269 270 if(pd == NULL) 271 { 272 CFDictionaryRef tempDict = defaultRevocationSettings(); 273 if (tempDict == NULL) 274 return NULL; 275 276 pd = new Dictionary(tempDict); 277 CFRelease(tempDict); 278 } 279 280 auto_ptr<Dictionary> prefsDict(pd); 281 282 bool doOcsp = false; 283 bool doCrl = false; 284 CFStringRef val; 285 SecRevocationPolicyStyle ocspStyle = kSecBestAttempt; 286 SecRevocationPolicyStyle crlStyle = kSecBestAttempt; 287 SecPointer<Policy> ocspPolicy; 288 SecPointer<Policy> crlPolicy; 289 290 /* Are any revocation policies enabled? */ 291 val = prefsDict->getStringValue(kSecRevocationOcspStyle); 292 if(val != NULL) { 293 ocspStyle = parseRevStyle(val); 294 if(ocspStyle != kSecDisabled) { 295 doOcsp = true; 296 } 297 } 298 val = prefsDict->getStringValue(kSecRevocationCrlStyle); 299 if(val != NULL) { 300 crlStyle = parseRevStyle(val); 301 if(crlStyle != kSecDisabled) { 302 doCrl = true; 303 } 304 } 305 if(!doCrl && !doOcsp) { 306 return NULL; 307 } 308 309 /* which policy first? */ 310 bool ocspFirst = true; // default if both present 311 if(doCrl && doOcsp) { 312 val = prefsDict->getStringValue(kSecRevocationWhichFirst); 313 if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) { 314 ocspFirst = false; 315 } 316 } 317 318 /* Must have at least one caller-specified policy 319 * (if they didn't specify any, it's a no-op evaluation, and if they wanted 320 * revocation checking only, that policy would already be in mPolicies) */ 321 if (!mPolicies || !CFArrayGetCount(mPolicies)) 322 return NULL; 323 324 /* We're adding something to mPolicies, so make a copy we can work with */ 325 CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies); 326 if(policies == NULL) { 327 throw std::bad_alloc(); 328 } 329 330 if(doOcsp) { 331 /* Cook up a new Policy object */ 332 ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)); 333 CSSM_APPLE_TP_OCSP_OPTIONS opts; 334 memset(&opts, 0, sizeof(opts)); 335 opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; 336 337 /* Now fill in the OCSP-related blanks */ 338 switch(ocspStyle) { 339 case kSecDisabled: 340 assert(0); 341 break; 342 case kSecBestAttempt: 343 /* default, nothing to set */ 344 break; 345 case kSecRequireIfPresentInCertificate: 346 opts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT; 347 break; 348 case kSecRequireForAllCertificates: 349 opts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT; 350 break; 351 } 352 353 if(prefsDict->getBoolValue(kSecRevocationOCSPSufficientPerCert)) { 354 opts.Flags |= CSSM_TP_ACTION_OCSP_SUFFICIENT; 355 } 356 357 val = prefsDict->getStringValue(kSecOCSPLocalResponder); 358 if(val != NULL) { 359 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, 360 val, kCFStringEncodingUTF8, 0); 361 CFIndex len = CFDataGetLength(cfData); 362 opts.LocalResponder = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); 363 opts.LocalResponder->Data = (uint8 *)alloc.malloc(len); 364 opts.LocalResponder->Length = len; 365 memmove(opts.LocalResponder->Data, CFDataGetBytePtr(cfData), len); 366 CFRelease(cfData); 367 } 368 369 /* Policy manages its own copy of this data */ 370 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; 371 ocspPolicy->value() = optData; 372 numAdded++; 373 } 374 375 if(doCrl) { 376 /* Cook up a new Policy object */ 377 crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)); 378 CSSM_APPLE_TP_CRL_OPTIONS opts; 379 memset(&opts, 0, sizeof(opts)); 380 opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; 381 382 /* Now fill in the CRL-related blanks */ 383 opts.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; // default true 384 switch(crlStyle) { 385 case kSecDisabled: 386 assert(0); 387 break; 388 case kSecBestAttempt: 389 /* default, nothing to set */ 390 break; 391 case kSecRequireIfPresentInCertificate: 392 opts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT; 393 break; 394 case kSecRequireForAllCertificates: 395 opts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT; 396 break; 397 } 398 if(prefsDict->getBoolValue(kSecRevocationCRLSufficientPerCert)) { 399 opts.CrlFlags |= CSSM_TP_ACTION_CRL_SUFFICIENT; 400 } 401 402 /* Policy manages its own copy of this data */ 403 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; 404 crlPolicy->value() = optData; 405 numAdded++; 406 } 407 408 /* append in order */ 409 if(doOcsp) { 410 if(doCrl) { 411 if(ocspFirst) { 412 /* these SecCFObject go away when the policies array does */ 413 CFArrayAppendValue(policies, ocspPolicy->handle(false)); 414 CFArrayAppendValue(policies, crlPolicy->handle(false)); 415 } 416 else { 417 CFArrayAppendValue(policies, crlPolicy->handle(false)); 418 CFArrayAppendValue(policies, ocspPolicy->handle(false)); 419 } 420 } 421 else { 422 CFArrayAppendValue(policies, ocspPolicy->handle(false)); 423 } 424 425 } 426 else { 427 assert(doCrl); 428 CFArrayAppendValue(policies, crlPolicy->handle(false)); 429 } 430 return policies; 431} 432 433/* 434 * Called when we created the last numAdded Policies in the specified Policy array 435 * (only frees the policy data associated with the extra policies that we inserted; 436 * this does not free the policies array itself.) 437 */ 438void Trust::freeAddedRevocationPolicyData( 439 CFArrayRef policies, 440 uint32 numAdded, 441 Allocator &alloc) 442{ 443 uint32 numPolicies = (uint32)CFArrayGetCount(policies); 444 if(numPolicies < numAdded) { 445 /* should never happen - throw? */ 446 assert(0); 447 return; 448 } 449 for(unsigned dex=numPolicies-numAdded; dex<numPolicies; dex++) { 450 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex); 451 //SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol)); 452 Policy *pol = Policy::required(secPol); 453 const CssmOid &oid = pol->oid(); // required 454 const CssmData &optData = pol->value(); // optional 455 456 if(optData.Data) { 457 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { 458 /* currently no CRL-specific policy data */ 459 } 460 else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { 461 CSSM_APPLE_TP_OCSP_OPTIONS *opts = (CSSM_APPLE_TP_OCSP_OPTIONS *)optData.Data; 462 if(opts->LocalResponder != NULL) { 463 if(opts->LocalResponder->Data != NULL) { 464 alloc.free(opts->LocalResponder->Data); 465 } 466 alloc.free(opts->LocalResponder); 467 } 468 } 469 // managed by Policy alloc.free(optData.Data); 470 } 471 } 472} 473 474/* 475 * Comparator function to correctly order revocation policies. 476 */ 477static CFComparisonResult compareRevocationPolicies( 478 const void *policy1, 479 const void *policy2, 480 void *context) 481{ 482 SecPointer<Policy> pol1 = Policy::required(SecPolicyRef(policy1)); 483 SecPointer<Policy> pol2 = Policy::required(SecPolicyRef(policy2)); 484 const CssmOid &oid1 = pol1->oid(); 485 const CssmOid &oid2 = pol2->oid(); 486 if(oid1 == oid2) { 487 return kCFCompareEqualTo; 488 } 489 bool ocspFirst = true; 490 if(context != NULL && CFEqual((CFBooleanRef)context, kCFBooleanFalse)) { 491 ocspFirst = false; 492 } 493 const CssmOid lastRevocationOid = (ocspFirst) ? 494 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL) : 495 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP); 496 const CssmOid firstRevocationOid = (ocspFirst) ? 497 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP) : 498 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL); 499 if(oid1 == lastRevocationOid) { 500 /* should be ordered last, after all other policies */ 501 return kCFCompareGreaterThan; 502 } 503 if(oid1 == firstRevocationOid) { 504 /* should be ordered after any policy except lastRevocationOid */ 505 if(oid2 == lastRevocationOid) { 506 return kCFCompareLessThan; 507 } 508 return kCFCompareGreaterThan; 509 } 510 /* normal policy in first position, anything else in second position */ 511 return kCFCompareLessThan; 512} 513 514/* 515 * This method reorders any revocation policies which may be present 516 * in the provided array so they are at the end and evaluated last. 517 */ 518void Trust::orderRevocationPolicies( 519 CFMutableArrayRef policies) 520{ 521 if(!policies || CFGetTypeID(policies) != CFArrayGetTypeID()) { 522 return; 523 } 524 /* check revocation prefs to determine which policy goes first */ 525 CFBooleanRef ocspFirst = kCFBooleanTrue; 526 Dictionary* pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true); 527 if (pd) { 528 if (!pd->dict()) { 529 delete pd; 530 } else { 531 auto_ptr<Dictionary> prefsDict(pd); 532 CFStringRef val = prefsDict->getStringValue(kSecRevocationWhichFirst); 533 if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) { 534 ocspFirst = kCFBooleanFalse; 535 } 536 } 537 } 538#if POLICIES_DEBUG 539 CFShow(policies); // before sort 540 CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst); 541 CFShow(policies); // after sort, to see what changed 542 // check that policy order is what we expect 543 CFIndex numPolicies = CFArrayGetCount(policies); 544 for(CFIndex dex=0; dex<numPolicies; dex++) { 545 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex); 546 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol)); 547 const CssmOid &oid = pol->oid(); 548 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { 549 CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = OCSP"), dex); 550 CFShow(s); 551 CFRelease(s); 552 } 553 else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { 554 CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = CRL"), dex); 555 CFShow(s); 556 CFRelease(s); 557 } 558 else { 559 CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = normal"), dex); 560 CFShow(s); 561 CFRelease(s); 562 } 563 } 564#else 565 CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst); 566#endif 567} 568 569/* 570 * This method returns a copy of the mPolicies array which ensures that 571 * revocation checking (preferably OCSP, otherwise CRL) will be attempted. 572 * 573 * If OCSP is already in the mPolicies array, this makes sure the 574 * CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT and CSSM_TP_ACTION_OCSP_SUFFICIENT 575 * flags are set. If it's not already in the array, a new policy object is added. 576 * 577 * If CRL is already in the mPolicies array, this makes sure the 578 * CSSM_TP_ACTION_FETCH_CRL_FROM_NET and CSSM_TP_ACTION_CRL_SUFFICIENT flags are 579 * set. If it's not already in the array, a new policy object is added. 580 * 581 * Caller is responsible for releasing the returned policies array. 582 */ 583CFMutableArrayRef Trust::forceRevocationPolicies( 584 uint32 &numAdded, 585 Allocator &alloc, 586 bool requirePerCert) 587{ 588 SecPointer<Policy> ocspPolicy; 589 SecPointer<Policy> crlPolicy; 590 CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags; 591 CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags; 592 bool hasOcspPolicy = false; 593 bool hasCrlPolicy = false; 594 numAdded = 0; 595 596 ocspFlags = CSSM_TP_ACTION_OCSP_SUFFICIENT; 597 crlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT; 598 if (requirePerCert) { 599 ocspFlags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT; 600 crlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT; 601 } 602 603 CFIndex numPolicies = (mPolicies) ? CFArrayGetCount(mPolicies) : 0; 604 for(CFIndex dex=0; dex<numPolicies; dex++) { 605 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(mPolicies, dex); 606 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol)); 607 const CssmOid &oid = pol->oid(); 608 const CssmData &optData = pol->value(); 609 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) { 610 // make sure OCSP options are set correctly 611 CSSM_APPLE_TP_OCSP_OPTIONS *opts = (CSSM_APPLE_TP_OCSP_OPTIONS *)optData.Data; 612 if (opts) { 613 opts->Flags |= ocspFlags; 614 } else { 615 CSSM_APPLE_TP_OCSP_OPTIONS newOpts; 616 memset(&newOpts, 0, sizeof(newOpts)); 617 newOpts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; 618 newOpts.Flags = ocspFlags; 619 CSSM_DATA optData = {sizeof(newOpts), (uint8 *)&newOpts}; 620 pol->value() = optData; 621 } 622 hasOcspPolicy = true; 623 } 624 else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) { 625 // make sure CRL options are set correctly 626 CSSM_APPLE_TP_CRL_OPTIONS *opts = (CSSM_APPLE_TP_CRL_OPTIONS *)optData.Data; 627 if (opts) { 628 opts->CrlFlags |= crlFlags; 629 } else { 630 CSSM_APPLE_TP_CRL_OPTIONS newOpts; 631 memset(&newOpts, 0, sizeof(newOpts)); 632 newOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; 633 newOpts.CrlFlags = crlFlags; 634 CSSM_DATA optData = {sizeof(newOpts), (uint8 *)&newOpts}; 635 pol->value() = optData; 636 } 637 hasCrlPolicy = true; 638 } 639 } 640 641 /* We're potentially adding something to mPolicies, so make a copy we can work with */ 642 CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies); 643 if(policies == NULL) { 644 throw std::bad_alloc(); 645 } 646 647 if(!hasOcspPolicy) { 648 /* Cook up a new Policy object */ 649 ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)); 650 CSSM_APPLE_TP_OCSP_OPTIONS opts; 651 memset(&opts, 0, sizeof(opts)); 652 opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; 653 opts.Flags = ocspFlags; 654 655 /* Check prefs dict for local responder info */ 656 Dictionary *prefsDict = NULL; 657 try { /* per-user prefs */ 658 prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true); 659 if (!prefsDict->dict()) { 660 delete prefsDict; 661 prefsDict = NULL; 662 } 663 } 664 catch(...) {} 665 if(prefsDict == NULL) { 666 try { /* system prefs */ 667 prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_System, true); 668 if (!prefsDict->dict()) { 669 delete prefsDict; 670 prefsDict = NULL; 671 } 672 } 673 catch(...) {} 674 } 675 if(prefsDict != NULL) { 676 CFStringRef val = prefsDict->getStringValue(kSecOCSPLocalResponder); 677 if(val != NULL) { 678 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, 679 val, kCFStringEncodingUTF8, 0); 680 CFIndex len = CFDataGetLength(cfData); 681 opts.LocalResponder = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); 682 opts.LocalResponder->Data = (uint8 *)alloc.malloc(len); 683 opts.LocalResponder->Length = len; 684 memmove(opts.LocalResponder->Data, CFDataGetBytePtr(cfData), len); 685 CFRelease(cfData); 686 } 687 } 688 689 /* Policy manages its own copy of the options data */ 690 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; 691 ocspPolicy->value() = optData; 692 693 /* Policies array retains the Policy object */ 694 CFArrayAppendValue(policies, ocspPolicy->handle(false)); 695 numAdded++; 696 697 if(prefsDict != NULL) 698 delete prefsDict; 699 } 700 701 if(!hasCrlPolicy) { 702 /* Cook up a new Policy object */ 703 crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)); 704 CSSM_APPLE_TP_CRL_OPTIONS opts; 705 memset(&opts, 0, sizeof(opts)); 706 opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; 707 opts.CrlFlags = crlFlags; 708 709 /* Policy manages its own copy of this data */ 710 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts}; 711 crlPolicy->value() = optData; 712 713 /* Policies array retains the Policy object */ 714 CFArrayAppendValue(policies, crlPolicy->handle(false)); 715 numAdded++; 716 } 717 718 return policies; 719} 720