]> git.saurik.com Git - apple/security.git/blame - Security/libsecurity_keychain/lib/TrustRevocation.cpp
Security-57031.30.12.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / TrustRevocation.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2002-2004,2011-2012,2014 Apple Inc. All Rights Reserved.
427c49bc 3 *
b1ab9ed8 4 * @APPLE_LICENSE_HEADER_START@
d8f41ccd 5 *
b1ab9ed8
A
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.
d8f41ccd 12 *
b1ab9ed8
A
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.
d8f41ccd 20 *
b1ab9ed8
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
427c49bc 24/*
b1ab9ed8
A
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
427c49bc 36/*
b1ab9ed8
A
37 * These may go into an SPI header for the SecTrust object.
38 */
39typedef enum {
40 /* this revocation policy disabled */
427c49bc 41 kSecDisabled,
b1ab9ed8 42 /* try, but tolerate inability to complete */
427c49bc 43 kSecBestAttempt,
b1ab9ed8
A
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
427c49bc 53/*
b1ab9ed8
A
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
427c49bc 74/*
b1ab9ed8
A
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
427c49bc
A
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,
b1ab9ed8
A
106 Allocator &alloc)
107{
427c49bc
A
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 }
b1ab9ed8 133
427c49bc
A
134 SecPointer<Policy> ocspPolicy;
135 SecPointer<Policy> crlPolicy;
136
137 // fetch policy value
d8f41ccd
A
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 }
427c49bc 147 CFRelease(revPolicy); // all done with this policy reference
d8f41ccd
A
148
149 if (policyFlags & kSecRevocationOCSPMethod) {
427c49bc
A
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;
d8f41ccd
A
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 }
427c49bc
A
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 }
d8f41ccd 170 if (policyFlags & kSecRevocationCRLMethod) {
427c49bc
A
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;
d8f41ccd
A
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 }
427c49bc
A
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;
b1ab9ed8
A
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
427c49bc 245CFMutableArrayRef Trust::addPreferenceRevocationPolicies(
d87e1158
A
246 bool ocspEnabledOnBestAttempt,
247 bool crlEnabledOnBestAttempt,
427c49bc 248 uint32 &numAdded,
b1ab9ed8
A
249 Allocator &alloc)
250{
251 numAdded = 0;
427c49bc 252
b1ab9ed8
A
253 /* any per-user prefs? */
254 Dictionary* pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true);
255 if (pd)
256 {
257 if (!pd->dict()) {
258 delete pd;
259 pd = NULL;
260 }
261 }
262
263 if(pd == NULL)
264 {
265 pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_System, true);
266 if (!pd->dict()) {
267 delete pd;
268 pd = NULL;
269 }
270 }
427c49bc 271
b1ab9ed8
A
272 if(pd == NULL)
273 {
274 CFDictionaryRef tempDict = defaultRevocationSettings();
275 if (tempDict == NULL)
276 return NULL;
427c49bc 277
b1ab9ed8
A
278 pd = new Dictionary(tempDict);
279 CFRelease(tempDict);
280 }
427c49bc 281
b1ab9ed8 282 auto_ptr<Dictionary> prefsDict(pd);
427c49bc 283
b1ab9ed8
A
284 bool doOcsp = false;
285 bool doCrl = false;
286 CFStringRef val;
287 SecRevocationPolicyStyle ocspStyle = kSecBestAttempt;
288 SecRevocationPolicyStyle crlStyle = kSecBestAttempt;
289 SecPointer<Policy> ocspPolicy;
290 SecPointer<Policy> crlPolicy;
427c49bc 291
b1ab9ed8
A
292 /* Are any revocation policies enabled? */
293 val = prefsDict->getStringValue(kSecRevocationOcspStyle);
294 if(val != NULL) {
295 ocspStyle = parseRevStyle(val);
296 if(ocspStyle != kSecDisabled) {
297 doOcsp = true;
298 }
d87e1158
A
299 if(ocspStyle == kSecBestAttempt) {
300 doOcsp = ocspEnabledOnBestAttempt;
301 }
b1ab9ed8
A
302 }
303 val = prefsDict->getStringValue(kSecRevocationCrlStyle);
304 if(val != NULL) {
305 crlStyle = parseRevStyle(val);
306 if(crlStyle != kSecDisabled) {
307 doCrl = true;
308 }
d87e1158
A
309 if(crlStyle == kSecBestAttempt) {
310 doCrl = crlEnabledOnBestAttempt;
311 }
b1ab9ed8 312 }
d87e1158 313
b1ab9ed8
A
314 if(!doCrl && !doOcsp) {
315 return NULL;
316 }
427c49bc 317
b1ab9ed8
A
318 /* which policy first? */
319 bool ocspFirst = true; // default if both present
320 if(doCrl && doOcsp) {
321 val = prefsDict->getStringValue(kSecRevocationWhichFirst);
322 if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) {
323 ocspFirst = false;
324 }
325 }
326
427c49bc
A
327 /* Must have at least one caller-specified policy
328 * (if they didn't specify any, it's a no-op evaluation, and if they wanted
329 * revocation checking only, that policy would already be in mPolicies) */
330 if (!mPolicies || !CFArrayGetCount(mPolicies))
331 return NULL;
332
b1ab9ed8
A
333 /* We're adding something to mPolicies, so make a copy we can work with */
334 CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies);
335 if(policies == NULL) {
336 throw std::bad_alloc();
337 }
427c49bc 338
b1ab9ed8
A
339 if(doOcsp) {
340 /* Cook up a new Policy object */
341 ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP));
342 CSSM_APPLE_TP_OCSP_OPTIONS opts;
343 memset(&opts, 0, sizeof(opts));
344 opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
427c49bc 345
b1ab9ed8
A
346 /* Now fill in the OCSP-related blanks */
347 switch(ocspStyle) {
348 case kSecDisabled:
349 assert(0);
350 break;
351 case kSecBestAttempt:
352 /* default, nothing to set */
353 break;
354 case kSecRequireIfPresentInCertificate:
355 opts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT;
356 break;
357 case kSecRequireForAllCertificates:
358 opts.Flags |= CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT;
359 break;
360 }
427c49bc 361
b1ab9ed8
A
362 if(prefsDict->getBoolValue(kSecRevocationOCSPSufficientPerCert)) {
363 opts.Flags |= CSSM_TP_ACTION_OCSP_SUFFICIENT;
364 }
427c49bc 365
b1ab9ed8
A
366 val = prefsDict->getStringValue(kSecOCSPLocalResponder);
367 if(val != NULL) {
368 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
369 val, kCFStringEncodingUTF8, 0);
370 CFIndex len = CFDataGetLength(cfData);
371 opts.LocalResponder = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA));
372 opts.LocalResponder->Data = (uint8 *)alloc.malloc(len);
373 opts.LocalResponder->Length = len;
374 memmove(opts.LocalResponder->Data, CFDataGetBytePtr(cfData), len);
375 CFRelease(cfData);
376 }
427c49bc 377
b1ab9ed8
A
378 /* Policy manages its own copy of this data */
379 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
380 ocspPolicy->value() = optData;
381 numAdded++;
382 }
427c49bc 383
b1ab9ed8
A
384 if(doCrl) {
385 /* Cook up a new Policy object */
386 crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL));
387 CSSM_APPLE_TP_CRL_OPTIONS opts;
388 memset(&opts, 0, sizeof(opts));
389 opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
390
391 /* Now fill in the CRL-related blanks */
392 opts.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; // default true
393 switch(crlStyle) {
394 case kSecDisabled:
395 assert(0);
396 break;
397 case kSecBestAttempt:
398 /* default, nothing to set */
399 break;
400 case kSecRequireIfPresentInCertificate:
401 opts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT;
402 break;
403 case kSecRequireForAllCertificates:
404 opts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT;
405 break;
406 }
407 if(prefsDict->getBoolValue(kSecRevocationCRLSufficientPerCert)) {
408 opts.CrlFlags |= CSSM_TP_ACTION_CRL_SUFFICIENT;
409 }
410
411 /* Policy manages its own copy of this data */
412 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
413 crlPolicy->value() = optData;
414 numAdded++;
415 }
427c49bc 416
b1ab9ed8
A
417 /* append in order */
418 if(doOcsp) {
419 if(doCrl) {
420 if(ocspFirst) {
421 /* these SecCFObject go away when the policies array does */
422 CFArrayAppendValue(policies, ocspPolicy->handle(false));
423 CFArrayAppendValue(policies, crlPolicy->handle(false));
424 }
425 else {
426 CFArrayAppendValue(policies, crlPolicy->handle(false));
427 CFArrayAppendValue(policies, ocspPolicy->handle(false));
428 }
429 }
430 else {
431 CFArrayAppendValue(policies, ocspPolicy->handle(false));
432 }
433
434 }
d87e1158 435 else if(doCrl) {
b1ab9ed8
A
436 CFArrayAppendValue(policies, crlPolicy->handle(false));
437 }
438 return policies;
439}
440
441/*
427c49bc
A
442 * Called when we created the last numAdded Policies in the specified Policy array
443 * (only frees the policy data associated with the extra policies that we inserted;
444 * this does not free the policies array itself.)
b1ab9ed8 445 */
427c49bc 446void Trust::freeAddedRevocationPolicyData(
b1ab9ed8 447 CFArrayRef policies,
427c49bc 448 uint32 numAdded,
b1ab9ed8
A
449 Allocator &alloc)
450{
451 uint32 numPolicies = (uint32)CFArrayGetCount(policies);
452 if(numPolicies < numAdded) {
453 /* should never happen - throw? */
454 assert(0);
455 return;
456 }
457 for(unsigned dex=numPolicies-numAdded; dex<numPolicies; dex++) {
458 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
459 //SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
460 Policy *pol = Policy::required(secPol);
461 const CssmOid &oid = pol->oid(); // required
462 const CssmData &optData = pol->value(); // optional
427c49bc 463
b1ab9ed8
A
464 if(optData.Data) {
465 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) {
466 /* currently no CRL-specific policy data */
467 }
468 else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) {
469 CSSM_APPLE_TP_OCSP_OPTIONS *opts = (CSSM_APPLE_TP_OCSP_OPTIONS *)optData.Data;
470 if(opts->LocalResponder != NULL) {
471 if(opts->LocalResponder->Data != NULL) {
472 alloc.free(opts->LocalResponder->Data);
473 }
474 alloc.free(opts->LocalResponder);
475 }
476 }
477 // managed by Policy alloc.free(optData.Data);
478 }
479 }
b1ab9ed8
A
480}
481
482/*
483 * Comparator function to correctly order revocation policies.
484 */
485static CFComparisonResult compareRevocationPolicies(
486 const void *policy1,
487 const void *policy2,
488 void *context)
489{
490 SecPointer<Policy> pol1 = Policy::required(SecPolicyRef(policy1));
491 SecPointer<Policy> pol2 = Policy::required(SecPolicyRef(policy2));
492 const CssmOid &oid1 = pol1->oid();
493 const CssmOid &oid2 = pol2->oid();
494 if(oid1 == oid2) {
495 return kCFCompareEqualTo;
496 }
497 bool ocspFirst = true;
498 if(context != NULL && CFEqual((CFBooleanRef)context, kCFBooleanFalse)) {
499 ocspFirst = false;
500 }
501 const CssmOid lastRevocationOid = (ocspFirst) ?
502 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL) :
503 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP);
504 const CssmOid firstRevocationOid = (ocspFirst) ?
505 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP) :
506 CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL);
507 if(oid1 == lastRevocationOid) {
508 /* should be ordered last, after all other policies */
509 return kCFCompareGreaterThan;
510 }
511 if(oid1 == firstRevocationOid) {
512 /* should be ordered after any policy except lastRevocationOid */
513 if(oid2 == lastRevocationOid) {
514 return kCFCompareLessThan;
515 }
516 return kCFCompareGreaterThan;
517 }
518 /* normal policy in first position, anything else in second position */
519 return kCFCompareLessThan;
520}
521
522/*
523 * This method reorders any revocation policies which may be present
524 * in the provided array so they are at the end and evaluated last.
525 */
526void Trust::orderRevocationPolicies(
527 CFMutableArrayRef policies)
528{
529 if(!policies || CFGetTypeID(policies) != CFArrayGetTypeID()) {
530 return;
531 }
532 /* check revocation prefs to determine which policy goes first */
533 CFBooleanRef ocspFirst = kCFBooleanTrue;
534 Dictionary* pd = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true);
535 if (pd) {
536 if (!pd->dict()) {
537 delete pd;
538 } else {
539 auto_ptr<Dictionary> prefsDict(pd);
540 CFStringRef val = prefsDict->getStringValue(kSecRevocationWhichFirst);
541 if((val != NULL) && CFEqual(val, kSecRevocationCrlFirst)) {
542 ocspFirst = kCFBooleanFalse;
543 }
544 }
545 }
546#if POLICIES_DEBUG
547 CFShow(policies); // before sort
548 CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst);
549 CFShow(policies); // after sort, to see what changed
550 // check that policy order is what we expect
551 CFIndex numPolicies = CFArrayGetCount(policies);
552 for(CFIndex dex=0; dex<numPolicies; dex++) {
553 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
554 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
555 const CssmOid &oid = pol->oid();
556 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) {
557 CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = OCSP"), dex);
558 CFShow(s);
559 CFRelease(s);
560 }
561 else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) {
562 CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = CRL"), dex);
563 CFShow(s);
564 CFRelease(s);
565 }
566 else {
567 CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("idx %d = normal"), dex);
568 CFShow(s);
569 CFRelease(s);
570 }
571 }
572#else
573 CFArraySortValues(policies, CFRangeMake(0, CFArrayGetCount(policies)), compareRevocationPolicies, (void*)ocspFirst);
574#endif
575}
576
577/*
578 * This method returns a copy of the mPolicies array which ensures that
579 * revocation checking (preferably OCSP, otherwise CRL) will be attempted.
580 *
581 * If OCSP is already in the mPolicies array, this makes sure the
582 * CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT and CSSM_TP_ACTION_OCSP_SUFFICIENT
583 * flags are set. If it's not already in the array, a new policy object is added.
584 *
585 * If CRL is already in the mPolicies array, this makes sure the
586 * CSSM_TP_ACTION_FETCH_CRL_FROM_NET and CSSM_TP_ACTION_CRL_SUFFICIENT flags are
587 * set. If it's not already in the array, a new policy object is added.
588 *
589 * Caller is responsible for releasing the returned policies array.
590 */
427c49bc 591CFMutableArrayRef Trust::forceRevocationPolicies(
d87e1158
A
592 bool ocspEnabled,
593 bool crlEnabled,
427c49bc 594 uint32 &numAdded,
b1ab9ed8
A
595 Allocator &alloc,
596 bool requirePerCert)
597{
598 SecPointer<Policy> ocspPolicy;
599 SecPointer<Policy> crlPolicy;
600 CSSM_APPLE_TP_OCSP_OPT_FLAGS ocspFlags;
601 CSSM_APPLE_TP_CRL_OPT_FLAGS crlFlags;
602 bool hasOcspPolicy = false;
603 bool hasCrlPolicy = false;
604 numAdded = 0;
427c49bc 605
b1ab9ed8
A
606 ocspFlags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
607 crlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
608 if (requirePerCert) {
609 ocspFlags |= CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT;
610 crlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT;
611 }
612
613 CFIndex numPolicies = (mPolicies) ? CFArrayGetCount(mPolicies) : 0;
614 for(CFIndex dex=0; dex<numPolicies; dex++) {
615 SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(mPolicies, dex);
616 SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
617 const CssmOid &oid = pol->oid();
618 const CssmData &optData = pol->value();
619 if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) {
620 // make sure OCSP options are set correctly
621 CSSM_APPLE_TP_OCSP_OPTIONS *opts = (CSSM_APPLE_TP_OCSP_OPTIONS *)optData.Data;
622 if (opts) {
623 opts->Flags |= ocspFlags;
624 } else {
625 CSSM_APPLE_TP_OCSP_OPTIONS newOpts;
626 memset(&newOpts, 0, sizeof(newOpts));
627 newOpts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
628 newOpts.Flags = ocspFlags;
629 CSSM_DATA optData = {sizeof(newOpts), (uint8 *)&newOpts};
630 pol->value() = optData;
631 }
632 hasOcspPolicy = true;
633 }
634 else if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) {
635 // make sure CRL options are set correctly
636 CSSM_APPLE_TP_CRL_OPTIONS *opts = (CSSM_APPLE_TP_CRL_OPTIONS *)optData.Data;
637 if (opts) {
638 opts->CrlFlags |= crlFlags;
639 } else {
640 CSSM_APPLE_TP_CRL_OPTIONS newOpts;
641 memset(&newOpts, 0, sizeof(newOpts));
642 newOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
643 newOpts.CrlFlags = crlFlags;
644 CSSM_DATA optData = {sizeof(newOpts), (uint8 *)&newOpts};
645 pol->value() = optData;
646 }
647 hasCrlPolicy = true;
648 }
427c49bc 649 }
b1ab9ed8
A
650
651 /* We're potentially adding something to mPolicies, so make a copy we can work with */
652 CFMutableArrayRef policies = CFArrayCreateMutableCopy(NULL, 0, mPolicies);
653 if(policies == NULL) {
654 throw std::bad_alloc();
655 }
656
d87e1158 657 if(!hasOcspPolicy && ocspEnabled) {
b1ab9ed8
A
658 /* Cook up a new Policy object */
659 ocspPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP));
660 CSSM_APPLE_TP_OCSP_OPTIONS opts;
661 memset(&opts, 0, sizeof(opts));
662 opts.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
663 opts.Flags = ocspFlags;
427c49bc 664
b1ab9ed8
A
665 /* Check prefs dict for local responder info */
666 Dictionary *prefsDict = NULL;
667 try { /* per-user prefs */
668 prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_User, true);
669 if (!prefsDict->dict()) {
670 delete prefsDict;
671 prefsDict = NULL;
672 }
673 }
674 catch(...) {}
675 if(prefsDict == NULL) {
676 try { /* system prefs */
677 prefsDict = Dictionary::CreateDictionary(kSecRevocationDomain, Dictionary::US_System, true);
678 if (!prefsDict->dict()) {
679 delete prefsDict;
680 prefsDict = NULL;
681 }
682 }
683 catch(...) {}
684 }
685 if(prefsDict != NULL) {
686 CFStringRef val = prefsDict->getStringValue(kSecOCSPLocalResponder);
687 if(val != NULL) {
688 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
689 val, kCFStringEncodingUTF8, 0);
690 CFIndex len = CFDataGetLength(cfData);
691 opts.LocalResponder = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA));
692 opts.LocalResponder->Data = (uint8 *)alloc.malloc(len);
693 opts.LocalResponder->Length = len;
694 memmove(opts.LocalResponder->Data, CFDataGetBytePtr(cfData), len);
695 CFRelease(cfData);
696 }
697 }
698
699 /* Policy manages its own copy of the options data */
700 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
701 ocspPolicy->value() = optData;
427c49bc 702
b1ab9ed8
A
703 /* Policies array retains the Policy object */
704 CFArrayAppendValue(policies, ocspPolicy->handle(false));
705 numAdded++;
427c49bc 706
b1ab9ed8
A
707 if(prefsDict != NULL)
708 delete prefsDict;
709 }
427c49bc 710
d87e1158 711 if(!hasCrlPolicy && crlEnabled) {
b1ab9ed8
A
712 /* Cook up a new Policy object */
713 crlPolicy = new Policy(mTP, CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL));
714 CSSM_APPLE_TP_CRL_OPTIONS opts;
715 memset(&opts, 0, sizeof(opts));
716 opts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
717 opts.CrlFlags = crlFlags;
718
719 /* Policy manages its own copy of this data */
720 CSSM_DATA optData = {sizeof(opts), (uint8 *)&opts};
721 crlPolicy->value() = optData;
427c49bc 722
b1ab9ed8
A
723 /* Policies array retains the Policy object */
724 CFArrayAppendValue(policies, crlPolicy->handle(false));
725 numAdded++;
726 }
727
728 return policies;
729}