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