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