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