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 */
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	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
194static SecRevocationPolicyStyle parseRevStyle(CFStringRef val)
195{
196	if(CFEqual(val, kSecRevocationOff)) {
197		return kSecDisabled;
198	}
199	else if(CFEqual(val, kSecRevocationBestAttempt)) {
200		return kSecBestAttempt;
201	}
202	else if(CFEqual(val, kSecRevocationRequireIfPresent)) {
203		return kSecRequireIfPresentInCertificate;
204	}
205	else if(CFEqual(val, kSecRevocationRequireForAll)) {
206		return kSecRequireForAllCertificates;
207	}
208	else {
209		return kSecDisabled;
210	}
211}
212
213CFDictionaryRef Trust::defaultRevocationSettings()
214{
215    /*
216        defaults read ~/Library/Preferences/com.apple.security.revocation
217        {
218            CRLStyle = BestAttempt;
219            CRLSufficientPerCert = 1;
220            OCSPStyle = BestAttempt;
221            OCSPSufficientPerCert = 1;
222            RevocationFirst = OCSP;
223        }
224    */
225    const void *keys[] = {
226		kSecRevocationCrlStyle,
227        kSecRevocationCRLSufficientPerCert,
228        kSecRevocationOcspStyle,
229        kSecRevocationOCSPSufficientPerCert,
230        kSecRevocationWhichFirst
231	};
232	const void *values[] = {
233		kSecRevocationBestAttempt,
234		kCFBooleanTrue,
235		kSecRevocationBestAttempt,
236		kCFBooleanTrue,
237		kSecRevocationOcspFirst
238	};
239
240    return CFDictionaryCreate(kCFAllocatorDefault, keys,
241		values, sizeof(keys) / sizeof(*keys),
242		&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
243}
244
245CFMutableArrayRef 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 */
438void 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 */
477static 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 */
518void 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 */
583CFMutableArrayRef 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}
720