1/*
2 * Copyright (c) 2005 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 * SecTrustSettings.cpp - Public interface for manipulation of Trust Settings.
26 *
27 * Created May 10 2005 by dmitch.
28 */
29
30#include "SecBridge.h"
31#include "SecTrustSettings.h"
32#include "SecTrustSettingsPriv.h"
33#include "TrustSettingsUtils.h"
34#include "TrustSettings.h"
35#include "TrustSettingsSchema.h"
36#include "TrustKeychains.h"
37#include "Trust.h"
38#include "SecKeychainPriv.h"
39#include "Globals.h"
40#include <security_utilities/threading.h>
41#include <security_utilities/globalizer.h>
42#include <security_utilities/errors.h>
43#include <security_cdsa_utilities/cssmerrors.h>
44#include <security_utilities/logging.h>
45#include <security_utilities/debugging.h>
46#include <security_utilities/simpleprefs.h>
47#include <securityd_client/dictionary.h>
48#include <securityd_client/ssclient.h>
49#include <assert.h>
50#include <vector>
51#include <CommonCrypto/CommonDigest.h>
52
53#define trustSettingsDbg(args...)	secdebug("trustSettings", ## args)
54
55/*
56 * Ideally we'd like to implement our own lock to protect the state of the cert stores
57 * without grabbing the global Sec API lock, but we deal with SecCFObjects, so we'll have
58 * to bite the bullet and grab the big lock. We also have our own lock protecting the
59 * global trust settings cache which is also used by the keychain callback function
60 * (which does not grab the Sec API lock).
61 */
62
63#define BEGIN_RCSAPI	\
64	OSStatus __secapiresult; \
65	try {
66#define END_RCSAPI		\
67		__secapiresult=errSecSuccess; \
68	} \
69	catch (const MacOSError &err) { __secapiresult=err.osStatus(); } \
70	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } \
71	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } \
72	catch (...) { __secapiresult=errSecInternalComponent; } \
73	return __secapiresult;
74
75#define END_RCSAPI0		\
76	catch (...) {} \
77	return;
78
79
80#pragma mark --- TrustSettings preferences ---
81
82/*
83 * If Colonel Klink wants to disable user-level Trust Settings, he'll have
84 * to restart the apps which will be affected after he does so. We are not
85 * going to consult system prefs every time we do a cert evaluation. We
86 * consult it once per process and cache the results here.
87 */
88static bool tsUserTrustDisableValid = false;	/* true once we consult prefs */
89static bool tsUserTrustDisable = false;			/* the cached value */
90
91/*
92 * Determine whether user-level Trust Settings disabled.
93 */
94static bool tsUserTrustSettingsDisabled()
95{
96	if(tsUserTrustDisableValid) {
97		return tsUserTrustDisable;
98	}
99	tsUserTrustDisable = false;
100
101	Dictionary* dictionary = Dictionary::CreateDictionary(kSecTrustSettingsPrefsDomain, Dictionary::US_System);
102	if (dictionary)
103	{
104		auto_ptr<Dictionary> prefsDict(dictionary);
105		/* this returns false if the pref isn't there, just like we want */
106		tsUserTrustDisable = prefsDict->getBoolValue(kSecTrustSettingsDisableUserTrustSettings);
107	}
108
109	tsUserTrustDisableValid = true;
110	return tsUserTrustDisable;
111}
112
113#pragma mark --- TrustSettings global cache ---
114
115/***
116 *** cache submodule - keeps per-app copy of zero or one TrustSettings
117 ***  				   for each domain. Used only by SecTrustSettingsEvaluateCert()
118 ***				   and SecTrustSettingsCopyQualifiedCerts(); results of
119 ***				   manipulation by public API functions are not cached.
120 ***/
121
122/*
123 * API/client code has to hold this lock when doing anything with any of
124 * the TrustSettings maintained here.
125 * It's recursive to accomodate CodeSigning's need to do cert verification
126 * (while we evaluate app equivalence).
127 */
128static ModuleNexus<RecursiveMutex> sutCacheLock;
129
130#define TRUST_SETTINGS_NUM_DOMAINS		3
131
132/*
133 * The three global TrustSettings.
134 * We rely on the fact the the domain enums start with 0; we use
135 * the domain value as an index into the following two arrays.
136 */
137static TrustSettings *globalTrustSettings[TRUST_SETTINGS_NUM_DOMAINS] =
138		{NULL, NULL, NULL};
139
140/*
141 * Indicates "the associated global here is currently valid; if there isn't a
142 * globalTrustSettings[domain], don't try to find one"
143 */
144static bool globalTrustSettingsValid[TRUST_SETTINGS_NUM_DOMAINS] =
145		{false, false, false};
146
147/* remember the fact that we've registered our KC callback */
148static bool sutRegisteredCallback = false;
149
150static void tsRegisterCallback();
151
152/*
153 * Assign global TrustSetting to new incoming value, which may be NULL.
154 * Caller holds sutCacheLock.
155 */
156static void tsSetGlobalTrustSettings(
157	TrustSettings *ts,
158	SecTrustSettingsDomain domain)
159{
160	assert(((int)domain >= 0) && ((int)domain < TRUST_SETTINGS_NUM_DOMAINS));
161
162	trustSettingsDbg("tsSetGlobalTrustSettings domain %d: caching TS %p old TS %p",
163		(int)domain, ts, globalTrustSettings[domain]);
164	delete globalTrustSettings[domain];
165	globalTrustSettings[domain] = ts;
166	globalTrustSettingsValid[domain] = ts ? true : false;
167	tsRegisterCallback();
168}
169
170/*
171 * Obtain global TrustSettings for specified domain if it exists.
172 * Returns NULL if there is simply no TS for that domain.
173 * The TS, if returned, belongs to this cache module.
174 * Caller holds sutCacheLock.
175 */
176static TrustSettings *tsGetGlobalTrustSettings(
177	SecTrustSettingsDomain domain)
178{
179	assert(((int)domain >= 0) && ((int)domain < TRUST_SETTINGS_NUM_DOMAINS));
180
181	if((domain == kSecTrustSettingsDomainUser) && tsUserTrustSettingsDisabled()) {
182		trustSettingsDbg("tsGetGlobalTrustSettings: skipping DISABLED user domain");
183		return NULL;
184	}
185
186	if(globalTrustSettingsValid[domain]) {
187		// ready or not, use this
188		return globalTrustSettings[domain];
189	}
190	assert(globalTrustSettings[domain] == NULL);
191
192	/* try to find one */
193	OSStatus result = errSecSuccess;
194	TrustSettings *ts = NULL;
195	/* don't create; trim if found */
196	result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_YES, ts);
197	if(result != errSecSuccess && result != errSecNoTrustSettings) {
198		/* gross error */
199		MacOSError::throwMe(result);
200	}
201	else if (result != errSecSuccess) {
202		/*
203		 * No TrustSettings for this domain, actually a fairly common case.
204		 * Optimize: don't bother trying this again.
205		 */
206		trustSettingsDbg("tsGetGlobalTrustSettings: flagging known NULL");
207		globalTrustSettingsValid[domain] = true;
208		tsRegisterCallback();
209		return NULL;
210	}
211
212	tsSetGlobalTrustSettings(ts, domain);
213	return ts;
214}
215
216/*
217 * Purge TrustSettings cache.
218 * Called by Keychain Event callback and by our API functions that
219 * modify trust settings.
220 * Caller can NOT hold sutCacheLock.
221 */
222static void tsPurgeCache()
223{
224	int domain;
225
226	StLock<Mutex>	_(sutCacheLock());
227	trustSettingsDbg("tsPurgeCache");
228	for(domain=0; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
229		tsSetGlobalTrustSettings(NULL, domain);
230	}
231}
232
233/*
234 * Keychain event callback function, for notification by other processes that
235 * user trust list(s) has/have changed.
236 */
237static OSStatus tsTrustSettingsCallback (
238   SecKeychainEvent keychainEvent,
239   SecKeychainCallbackInfo *info,
240   void *context)
241{
242	trustSettingsDbg("tsTrustSettingsCallback, event %d", (int)keychainEvent);
243	if(keychainEvent != kSecTrustSettingsChangedEvent) {
244		/* should not happen, right? */
245		return errSecSuccess;
246	}
247	if(info->pid == getpid()) {
248		/*
249		 * Avoid dup cache invalidates: we already dealt with this event.
250		 */
251		trustSettingsDbg("cacheEventCallback: our pid, skipping");
252	}
253	else {
254		tsPurgeCache();
255	}
256	return errSecSuccess;
257}
258
259/*
260 * Ensure that we've registered for kSecTrustSettingsChangedEvent callbacks
261 */
262static void tsRegisterCallback()
263{
264	if(sutRegisteredCallback) {
265		return;
266	}
267	trustSettingsDbg("tsRegisterCallback: registering callback");
268	OSStatus ortn = SecKeychainAddCallback(tsTrustSettingsCallback,
269		kSecTrustSettingsChangedEventMask, NULL);
270	if(ortn) {
271		trustSettingsDbg("tsRegisterCallback: SecKeychainAddCallback returned %d", (int)ortn);
272		/* Not sure how this could ever happen - maybe if there is no run loop active? */
273	}
274	sutRegisteredCallback = true;
275}
276
277#pragma mark --- Static functions ---
278
279
280/*
281 * Called by API code when a trust list has changed; we notify other processes
282 * and purge our own cache.
283 */
284static void tsTrustSettingsChanged()
285{
286	tsPurgeCache();
287
288	/* The only interesting data is our pid */
289	NameValueDictionary nvd;
290	pid_t ourPid = getpid();
291	nvd.Insert (new NameValuePair (PID_KEY,
292		CssmData (reinterpret_cast<void*>(&ourPid), sizeof (pid_t))));
293	CssmData data;
294	nvd.Export (data);
295
296	trustSettingsDbg("tsTrustSettingsChanged: posting notification");
297	SecurityServer::ClientSession cs (Allocator::standard(), Allocator::standard());
298	cs.postNotification (SecurityServer::kNotificationDomainDatabase,
299		kSecTrustSettingsChangedEvent, data);
300	free (data.data ());
301}
302
303/*
304 * Common code for SecTrustSettingsCopyTrustSettings(),
305 * SecTrustSettingsCopyModificationDate().
306 */
307static OSStatus tsCopyTrustSettings(
308	SecCertificateRef cert,
309	SecTrustSettingsDomain domain,
310	CFArrayRef *trustSettings,		/* optionally RETURNED */
311	CFDateRef *modDate)				/* optionally RETURNED */
312{
313	BEGIN_RCSAPI
314
315	TS_REQUIRED(cert)
316
317	/* obtain fresh full copy from disk */
318	OSStatus result;
319	TrustSettings* ts;
320
321	result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
322
323	// rather than throw these results, just return them because we are at the top level
324	if (result == errSecNoTrustSettings) {
325		return errSecItemNotFound;
326	}
327	else if (result != errSecSuccess) {
328		return result;
329	}
330
331	auto_ptr<TrustSettings>_(ts); // make sure this gets deleted just in case something throws underneath
332
333	if(trustSettings) {
334		*trustSettings = ts->copyTrustSettings(cert);
335	}
336	if(modDate) {
337		*modDate = ts->copyModDate(cert);
338	}
339
340	END_RCSAPI
341}
342
343/*
344 * Common code for SecTrustSettingsCopyQualifiedCerts() and
345 * SecTrustSettingsCopyUnrestrictedRoots().
346 */
347static OSStatus tsCopyCertsCommon(
348	/* usage constraints, all optional */
349	const CSSM_OID			*policyOID,
350	const char				*policyString,
351	SecTrustSettingsKeyUsage keyUsage,
352	/* constrain to only roots */
353	bool					onlyRoots,
354	/* per-domain enables */
355	bool					user,
356	bool					admin,
357	bool					system,
358	CFArrayRef				*certArray)		/* RETURNED */
359{
360	StLock<Mutex> _TC(sutCacheLock());
361	StLock<Mutex> _TK(SecTrustKeychainsGetMutex());
362
363	TS_REQUIRED(certArray)
364
365	/* this relies on the domain enums being numbered 0..2, user..system */
366	bool domainEnable[3] = {user, admin, system};
367
368	/* we'll retain it again before successful exit */
369	CFRef<CFMutableArrayRef> outArray(CFArrayCreateMutable(NULL, 0,
370		&kCFTypeArrayCallBacks));
371
372	/*
373	 * Search all keychains - user's, System.keychain, system root store,
374	 * system intermdiates as appropriate
375  	 */
376	StorageManager::KeychainList keychains;
377	Keychain adminKc;
378	if(user) {
379		globals().storageManager.getSearchList(keychains);
380	}
381	if(user || admin) {
382		adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
383		keychains.push_back(adminKc);
384	}
385	Keychain sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
386	keychains.push_back(sysRootKc);
387	Keychain sysCertKc = globals().storageManager.make(SYSTEM_CERT_STORE_PATH, false);
388	keychains.push_back(sysCertKc);
389
390	assert(kSecTrustSettingsDomainUser == 0);
391	for(unsigned domain=0; domain<TRUST_SETTINGS_NUM_DOMAINS; domain++) {
392		if(!domainEnable[domain]) {
393			continue;
394		}
395		TrustSettings *ts = tsGetGlobalTrustSettings(domain);
396		if(ts == NULL) {
397			continue;
398		}
399		ts->findQualifiedCerts(keychains,
400			false, 		/* !findAll */
401			onlyRoots,
402			policyOID, policyString, keyUsage,
403			outArray);
404	}
405	*certArray = outArray;
406	CFRetain(*certArray);
407	trustSettingsDbg("tsCopyCertsCommon: %ld certs found",
408		CFArrayGetCount(outArray));
409	return errSecSuccess;
410}
411
412#pragma mark --- SPI functions ---
413
414
415/*
416 * Fundamental routine used by TP to ascertain status of one cert.
417 *
418 * Returns true in *foundMatchingEntry if a trust setting matching
419 * specific constraints was found for the cert. Returns true in
420 * *foundAnyEntry if any entry was found for the cert, even if it
421 * did not match the specified constraints. The TP uses this to
422 * optimize for the case where a cert is being evaluated for
423 * one type of usage, and then later for another type. If
424 * foundAnyEntry is false, the second evaluation need not occur.
425 *
426 * Returns the domain in which a setting was found in *foundDomain.
427 *
428 * Allowed errors applying to the specified cert evaluation
429 * are returned in a mallocd array in *allowedErrors and must
430 * be freed by caller.
431 *
432 * The design of the entire TrustSettings module is centered around
433 * optimizing the performance of this routine (security concerns
434 * aside, that is). It's why the per-cert dictionaries are stored
435 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
436 * are cached in memory by tsGetGlobalTrustSettings(), and why those
437 * cached TrustSettings objects are 'trimmed' of dictionary fields
438 * which are not needed to verify a cert.
439 *
440 * The API functions which are used to manipulate Trust Settings
441 * are called infrequently and need not be particularly fast since
442 * they result in user interaction for authentication. Thus they do
443 * not use cached TrustSettings as this function does.
444 */
445OSStatus SecTrustSettingsEvaluateCert(
446	CFStringRef				certHashStr,
447	/* parameters describing the current cert evalaution */
448	const CSSM_OID			*policyOID,
449	const char				*policyString,		/* optional */
450	uint32					policyStringLen,
451	SecTrustSettingsKeyUsage keyUsage,			/* optional */
452	bool					isRootCert,			/* for checking default setting */
453	/* RETURNED values */
454	SecTrustSettingsDomain	*foundDomain,
455	CSSM_RETURN				**allowedErrors,	/* mallocd */
456	uint32					*numAllowedErrors,
457	SecTrustSettingsResult	*resultType,
458	bool					*foundMatchingEntry,
459	bool					*foundAnyEntry)
460{
461	BEGIN_RCSAPI
462
463	StLock<Mutex>	_(sutCacheLock());
464
465	TS_REQUIRED(certHashStr)
466	TS_REQUIRED(foundDomain)
467	TS_REQUIRED(allowedErrors)
468	TS_REQUIRED(numAllowedErrors)
469	TS_REQUIRED(resultType)
470	TS_REQUIRED(foundMatchingEntry)
471	TS_REQUIRED(foundAnyEntry)
472
473	/* ensure a NULL_terminated string */
474	auto_array<char> polStr;
475	if(policyString != NULL && policyStringLen > 0) {
476		polStr.allocate(policyStringLen + 1);
477		memmove(polStr.get(), policyString, policyStringLen);
478		if(policyString[policyStringLen - 1] != '\0') {
479			(polStr.get())[policyStringLen] = '\0';
480		}
481	}
482
483	/* initial condition - this can grow if we inspect multiple TrustSettings */
484	*allowedErrors = NULL;
485	*numAllowedErrors = 0;
486
487	/*
488	 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
489	 * search user first, then admin, then system.
490	 */
491	assert(kSecTrustSettingsDomainAdmin == (kSecTrustSettingsDomainUser + 1));
492	assert(kSecTrustSettingsDomainSystem == (kSecTrustSettingsDomainAdmin + 1));
493	bool foundAny = false;
494	for(unsigned domain=kSecTrustSettingsDomainUser;
495			     domain<=kSecTrustSettingsDomainSystem;
496				 domain++) {
497		TrustSettings *ts = tsGetGlobalTrustSettings(domain);
498		if(ts == NULL) {
499			continue;
500		}
501
502		/* validate cert returns true if matching entry was found */
503		bool foundAnyHere = false;
504		bool found = ts->evaluateCert(certHashStr, policyOID,
505			polStr.get(), keyUsage, isRootCert,
506			allowedErrors, numAllowedErrors, resultType, &foundAnyHere);
507
508		if(found) {
509			/*
510			 * Note this, even though we may overwrite it later if this
511			 * is an Unspecified entry and we find a definitive entry
512			 * later
513			 */
514			*foundDomain = domain;
515		}
516		if(found && (*resultType != kSecTrustSettingsResultUnspecified)) {
517			trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain);
518			*foundAnyEntry = true;
519			*foundMatchingEntry = true;
520			return errSecSuccess;
521		}
522		foundAny |= foundAnyHere;
523	}
524	trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
525	*foundAnyEntry = foundAny;
526	*foundMatchingEntry = false;
527	return errSecSuccess;
528	END_RCSAPI
529}
530
531/*
532 * Obtain trusted certs which match specified usage.
533 * Only certs with a SecTrustSettingsResult of
534 * kSecTrustSettingsResultTrustRoot or
535 * or kSecTrustSettingsResultTrustAsRoot will be returned.
536 * To be used by SecureTransport for its SSLSetTrustedRoots() call;
537 * I hope nothing else has to use this...
538 * Caller must CFRelease the returned CFArrayRef.
539 */
540OSStatus SecTrustSettingsCopyQualifiedCerts(
541	const CSSM_OID				*policyOID,
542	const char					*policyString,		/* optional */
543	uint32						policyStringLen,
544	SecTrustSettingsKeyUsage	keyUsage,			/* optional */
545	CFArrayRef					*certArray)			/* RETURNED */
546{
547	BEGIN_RCSAPI
548
549	/* ensure a NULL_terminated string */
550	auto_array<char> polStr;
551	if(policyString != NULL) {
552		polStr.allocate(policyStringLen + 1);
553		memmove(polStr.get(), policyString, policyStringLen);
554		if(policyString[policyStringLen - 1] != '\0') {
555			(polStr.get())[policyStringLen] = '\0';
556		}
557	}
558
559	return tsCopyCertsCommon(policyOID, polStr.get(), keyUsage,
560		false,				/* !onlyRoots */
561		true, true, true,	/* all domains */
562		certArray);
563
564	END_RCSAPI
565}
566
567/*
568 * Obtain unrestricted root certs fromt eh specified domain(s).
569 * Only returns roots with no usage constraints.
570 * Caller must CFRelease the returned CFArrayRef.
571 */
572OSStatus SecTrustSettingsCopyUnrestrictedRoots(
573	Boolean					user,
574	Boolean					admin,
575	Boolean					system,
576	CFArrayRef				*certArray)		/* RETURNED */
577{
578	BEGIN_RCSAPI
579
580	return tsCopyCertsCommon(NULL, NULL, NULL,	/* no constraints */
581		true,				/* onlyRoots */
582		user, admin, system,
583		certArray);
584
585	END_RCSAPI
586}
587
588static const char hexChars[16] = {
589	'0', '1', '2', '3', '4', '5', '6', '7',
590	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
591};
592
593/*
594 * Obtain a string representing a cert's SHA1 digest. This string is
595 * the key used to look up per-cert trust settings in a TrustSettings record.
596 */
597CFStringRef SecTrustSettingsCertHashStrFromCert(
598	SecCertificateRef certRef)
599{
600	if(certRef == NULL) {
601		return NULL;
602	}
603
604	if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
605		/* use this string instead of the cert hash as the dictionary key */
606		trustSettingsDbg("SecTrustSettingsCertHashStrFromCert: DefaultSetting");
607		return kSecTrustRecordDefaultRootCert;
608	}
609
610	CSSM_DATA certData;
611	OSStatus ortn = SecCertificateGetData(certRef, &certData);
612	if(ortn) {
613		return NULL;
614	}
615	return SecTrustSettingsCertHashStrFromData(certData.Data, certData.Length);
616}
617
618CFStringRef SecTrustSettingsCertHashStrFromData(
619	const void *cert,
620	size_t certLen)
621{
622	unsigned char digest[CC_SHA1_DIGEST_LENGTH];
623	char asciiDigest[(2 * CC_SHA1_DIGEST_LENGTH) + 1];
624	unsigned dex;
625	char *outp = asciiDigest;
626	unsigned char *inp = digest;
627
628	if(cert == NULL) {
629		return NULL;
630	}
631
632	CC_SHA1(cert, (CC_LONG)certLen, digest);
633
634	for(dex=0; dex<CC_SHA1_DIGEST_LENGTH; dex++) {
635		unsigned c = *inp++;
636		outp[1] = hexChars[c & 0xf];
637		c >>= 4;
638		outp[0] = hexChars[c];
639		outp += 2;
640	}
641	*outp = 0;
642	return CFStringCreateWithCString(NULL, asciiDigest, kCFStringEncodingASCII);
643}
644
645/*
646 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
647 * No locking or cache flushing here; it's all local to the TrustSettings
648 * we construct here.
649 */
650OSStatus SecTrustSettingsSetTrustSettingsExternal(
651	CFDataRef			settingsIn,					/* optional */
652	SecCertificateRef	certRef,					/* optional */
653	CFTypeRef			trustSettingsDictOrArray,	/* optional */
654	CFDataRef			*settingsOut)				/* RETURNED */
655{
656	BEGIN_RCSAPI
657
658	TS_REQUIRED(settingsOut)
659
660	OSStatus result;
661	TrustSettings* ts;
662
663	result = TrustSettings::CreateTrustSettings(kSecTrustSettingsDomainMemory, settingsIn, ts);
664	if (result != errSecSuccess) {
665		return result;
666	}
667
668	auto_ptr<TrustSettings>_(ts);
669
670	if(certRef != NULL) {
671		ts->setTrustSettings(certRef, trustSettingsDictOrArray);
672	}
673	*settingsOut = ts->createExternal();
674	return errSecSuccess;
675
676	END_RCSAPI
677}
678
679#pragma mark --- API functions ---
680
681OSStatus SecTrustSettingsCopyTrustSettings(
682	SecCertificateRef certRef,
683	SecTrustSettingsDomain domain,
684	CFArrayRef *trustSettings)				/* RETURNED */
685{
686	TS_REQUIRED(trustSettings)
687
688	OSStatus result = tsCopyTrustSettings(certRef, domain, trustSettings, NULL);
689	if (result == errSecSuccess && *trustSettings == NULL) {
690		result = errSecItemNotFound; /* documented result if no trust settings exist */
691	}
692	return result;
693}
694
695OSStatus SecTrustSettingsCopyModificationDate(
696	SecCertificateRef		certRef,
697	SecTrustSettingsDomain	domain,
698	CFDateRef				*modificationDate)	/* RETURNED */
699{
700	TS_REQUIRED(modificationDate)
701
702	OSStatus result = tsCopyTrustSettings(certRef, domain, NULL, modificationDate);
703	if (result == errSecSuccess && *modificationDate == NULL) {
704		result = errSecItemNotFound; /* documented result if no trust settings exist */
705	}
706	return result;
707}
708
709/* works with existing and with new cert */
710OSStatus SecTrustSettingsSetTrustSettings(
711	SecCertificateRef certRef,
712	SecTrustSettingsDomain domain,
713	CFTypeRef trustSettingsDictOrArray)
714{
715	BEGIN_RCSAPI
716
717	TS_REQUIRED(certRef)
718
719	if(domain == kSecTrustSettingsDomainSystem) {
720		return errSecDataNotModifiable;
721	}
722
723	OSStatus result;
724	TrustSettings* ts;
725
726	result = TrustSettings::CreateTrustSettings(domain, CREATE_YES, TRIM_NO, ts);
727	if (result != errSecSuccess) {
728		return result;
729	}
730
731	auto_ptr<TrustSettings>_(ts);
732
733	ts->setTrustSettings(certRef, trustSettingsDictOrArray);
734	ts->flushToDisk();
735	tsTrustSettingsChanged();
736	return errSecSuccess;
737
738	END_RCSAPI
739}
740
741OSStatus SecTrustSettingsRemoveTrustSettings(
742	SecCertificateRef cert,
743	SecTrustSettingsDomain domain)
744{
745	BEGIN_RCSAPI
746
747	TS_REQUIRED(cert)
748
749	if(domain == kSecTrustSettingsDomainSystem) {
750		return errSecDataNotModifiable;
751	}
752
753	OSStatus result;
754	TrustSettings* ts;
755
756	result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
757	if (result != errSecSuccess) {
758		return result;
759	}
760
761	auto_ptr<TrustSettings>_(ts);
762
763	/* deleteTrustSettings throws if record not found */
764	trustSettingsDbg("SecTrustSettingsRemoveTrustSettings: deleting from domain %d",
765		(int)domain);
766	ts->deleteTrustSettings(cert);
767	ts->flushToDisk();
768	tsTrustSettingsChanged();
769	return errSecSuccess;
770
771	END_RCSAPI
772}
773
774/* get all certs listed in specified domain */
775OSStatus SecTrustSettingsCopyCertificates(
776	SecTrustSettingsDomain	domain,
777	CFArrayRef				*certArray)
778{
779	BEGIN_RCSAPI
780
781	TS_REQUIRED(certArray)
782
783	OSStatus result;
784	TrustSettings* ts;
785
786	result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
787	if (result != errSecSuccess) {
788		return result;
789	}
790
791	auto_ptr<TrustSettings>_(ts);
792
793	CFMutableArrayRef outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
794
795	/*
796 	 * Keychains to search: user's search list, System.keychain, system root store,
797	 * system intermdiates, as appropriate
798	 */
799	StorageManager::KeychainList keychains;
800	Keychain adminKc;
801	Keychain sysCertKc;
802	Keychain sysRootKc;
803	switch(domain) {
804		case kSecTrustSettingsDomainUser:
805			/* user search list */
806			globals().storageManager.getSearchList(keychains);
807			/* drop thru to next case */
808		case kSecTrustSettingsDomainAdmin:
809			/* admin certs in system keychain */
810			adminKc = globals().storageManager.make(ADMIN_CERT_STORE_PATH, false);
811			keychains.push_back(adminKc);
812			/* system-wide intermediate certs */
813			sysCertKc = globals().storageManager.make(SYSTEM_CERT_STORE_PATH, false);
814			keychains.push_back(sysCertKc);
815			/* drop thru to next case */
816		case kSecTrustSettingsDomainSystem:
817			/* and, for all cases, immutable system root store */
818			sysRootKc = globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false);
819			keychains.push_back(sysRootKc);
820		default:
821			/* already validated when we created the TrustSettings */
822			break;
823	}
824	ts->findCerts(keychains, outArray);
825	if(CFArrayGetCount(outArray) == 0) {
826		CFRelease(outArray);
827		return errSecNoTrustSettings;
828	}
829	*certArray = outArray;
830	END_RCSAPI
831}
832
833/*
834 * Obtain an external, portable representation of the specified
835 * domain's TrustSettings. Caller must CFRelease the returned data.
836 */
837OSStatus SecTrustSettingsCreateExternalRepresentation(
838	SecTrustSettingsDomain	domain,
839	CFDataRef				*trustSettings)
840{
841	BEGIN_RCSAPI
842
843	TS_REQUIRED(trustSettings)
844
845	OSStatus result;
846	TrustSettings* ts;
847
848	result = TrustSettings::CreateTrustSettings(domain, CREATE_NO, TRIM_NO, ts);
849	if (result != errSecSuccess) {
850		return result;
851	}
852
853	auto_ptr<TrustSettings>_(ts);
854
855	*trustSettings = ts->createExternal();
856	return errSecSuccess;
857
858	END_RCSAPI
859}
860
861/*
862 * Import trust settings, obtained via SecTrustSettingsCreateExternalRepresentation,
863 * into the specified domain.
864 */
865OSStatus SecTrustSettingsImportExternalRepresentation(
866	SecTrustSettingsDomain	domain,
867	CFDataRef				trustSettings)		/* optional - NULL means empty settings */
868{
869	BEGIN_RCSAPI
870
871	if(domain == kSecTrustSettingsDomainSystem) {
872		return errSecDataNotModifiable;
873	}
874
875	OSStatus result;
876	TrustSettings* ts;
877
878	result = TrustSettings::CreateTrustSettings(domain, trustSettings, ts);
879	if (result != errSecSuccess) {
880		return result;
881	}
882
883	auto_ptr<TrustSettings>_(ts);
884
885	ts->flushToDisk();
886	tsTrustSettingsChanged();
887	return errSecSuccess;
888
889	END_RCSAPI
890}
891
892