]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_keychain/lib/TrustRevocation.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / 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 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
194 static 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
213 CFDictionaryRef 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
245 CFMutableArrayRef 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 */
438 void 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 */
477 static 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 */
518 void 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 */
583 CFMutableArrayRef 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 }