1/*
2 * Copyright (c) 2007-2008,2010 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 *  SecTrustSettings.c - Implement trust settings for certificates
26 */
27
28#include "SecTrustSettings.h"
29#include "SecTrustSettingsPriv.h"
30#include <Security/SecCertificatePriv.h>
31#include <AssertMacros.h>
32#include <pthread.h>
33#include <utilities/debugging.h>
34#include "SecBasePriv.h"
35#include <Security/SecInternal.h>
36#include <CoreFoundation/CFRuntime.h>
37
38#define trustSettingsDbg(args...)		secdebug("trustSettings", ## args)
39#define trustSettingsEvalDbg(args...)	secdebug("trustSettingsEval", ## args)
40
41// MARK: -
42// MARK: Static Functions
43
44/* Return a CFDataRef representation of a (hex)string. */
45static CFDataRef SecCopyDataFromHexString(CFStringRef string) {
46    CFMutableDataRef data;
47    CFIndex ix, length;
48	UInt8 *bytes;
49
50	length = string ? CFStringGetLength(string) : 0;
51	if (length & 1) {
52		secwarning("Odd length string: %@ returning NULL", string);
53		return NULL;
54	}
55
56	data = CFDataCreateMutable(kCFAllocatorDefault, length / 2);
57	CFDataSetLength(data, length / 2);
58	bytes = CFDataGetMutableBytePtr(data);
59
60	CFStringInlineBuffer buf;
61	CFRange range = { 0, length };
62	CFStringInitInlineBuffer(string, &buf, range);
63	UInt8 lastv = 0;
64	for (ix = 0; ix < length; ++ix) {
65		UniChar c = CFStringGetCharacterFromInlineBuffer(&buf, ix);
66		UInt8 v;
67		if ('0' <= c && c <= '9')
68			v = c - '0';
69		else if ('A' <= c && c <= 'F')
70			v = c = 'A' + 10;
71		else if ('a' <= c && c <= 'a')
72			v = c = 'a' + 10;
73		else {
74			secwarning("Non hex string: %@ returning NULL", string);
75			CFRelease(data);
76			return NULL;
77		}
78		if (ix & 1) {
79			*bytes++ = (lastv << 4) + v;
80		} else {
81			lastv = v;
82		}
83	}
84
85    return data;
86}
87
88#if 0
89/*
90 * Obtain a string representing a cert's SHA1 digest. This string is
91 * the key used to look up per-cert trust settings in a TrustSettings record.
92 */
93static CFStringRef SecTrustSettingsCertHashStrFromCert(
94	SecCertificateRef certRef)
95{
96	if (certRef == NULL) {
97		return NULL;
98	}
99
100	if(certRef == kSecTrustSettingsDefaultRootCertSetting) {
101		/* use this string instead of the cert hash as the dictionary key */
102		secdebug("trustsettings","DefaultSetting");
103		return kSecTrustRecordDefaultRootCert;
104	}
105
106	CFDataRef digest = SecCertificateGetSHA1Digest(certRef);
107	return CFDataCopyHexString(digest);
108}
109#endif
110
111// MARK: -
112// MARK: SecTrustSettings
113/********************************************************
114 ************** SecTrustSettings object *****************
115 ********************************************************/
116
117struct __SecTrustSettings {
118    CFRuntimeBase		_base;
119
120	/* the overall parsed TrustSettings - may be NULL if this is trimmed */
121	CFDictionaryRef					_propList;
122
123	/* and the main thing we work with, the dictionary of per-cert trust settings */
124	CFMutableDictionaryRef			_trustDict;
125
126	/* version number of mPropDict */
127	SInt32							_dictVersion;
128
129	SecTrustSettingsDomain			_domain;
130	bool							_dirty;		/* we've changed _trustDict since creation */
131};
132
133/* CFRuntime regsitration data. */
134static pthread_once_t kSecTrustSettingsRegisterClass = PTHREAD_ONCE_INIT;
135static CFTypeID kSecTrustSettingsTypeID = _kCFRuntimeNotATypeID;
136
137static void SecTrustSettingsDestroy(CFTypeRef cf) {
138	SecTrustSettingsRef ts = (SecTrustSettingsRef) cf;
139	CFReleaseSafe(ts->_propList);
140	CFReleaseSafe(ts->_trustDict);
141}
142
143static void SecTrustSettingsRegisterClass(void) {
144	static const CFRuntimeClass kSecTrustSettingsClass = {
145		0,										/* version */
146        "SecTrustSettings",						/* class name */
147		NULL,									/* init */
148		NULL,									/* copy */
149		SecTrustSettingsDestroy,                /* dealloc */
150		NULL,                                   /* equal */
151		NULL,                                   /* hash */
152		NULL,									/* copyFormattingDesc */
153		NULL                                    /* copyDebugDesc */
154	};
155
156    kSecTrustSettingsTypeID = _CFRuntimeRegisterClass(&kSecTrustSettingsClass);
157}
158
159/* SecTrustSettings API functions. */
160CFTypeID SecTrustSettingsGetTypeID(void) {
161    pthread_once(&kSecTrustSettingsRegisterClass, SecTrustSettingsRegisterClass);
162    return kSecTrustSettingsTypeID;
163}
164
165/* Make sure a presumed CFNumber can be converted to a 32-bit number */
166static bool tsIsGoodCfNum(CFNumberRef cfn, SInt32 *num)
167{
168	/* by convention */
169	if (cfn == NULL) {
170		*num = 0;
171		return true;
172	}
173	require(CFGetTypeID(cfn) == CFNumberGetTypeID(), errOut);
174	return CFNumberGetValue(cfn, kCFNumberSInt32Type, num);
175errOut:
176	return false;
177}
178
179static bool validateUsageConstraint(const void *value) {
180	CFDictionaryRef ucDict = (CFDictionaryRef)value;
181	require(CFGetTypeID(ucDict) == CFDictionaryGetTypeID(), errOut);
182
183	CFDataRef certPolicy = (CFDataRef)CFDictionaryGetValue(ucDict,
184		kSecTrustSettingsPolicy);
185	require(certPolicy && CFGetTypeID(certPolicy) == CFDataGetTypeID(), errOut);
186
187	CFStringRef certApp = (CFStringRef)CFDictionaryGetValue(ucDict,
188		kSecTrustSettingsApplication);
189	require(certApp && CFGetTypeID(certApp) == CFStringGetTypeID(), errOut);
190
191	CFStringRef policyStr = (CFStringRef)CFDictionaryGetValue(ucDict,
192		kSecTrustSettingsPolicyString);
193	require(policyStr && CFGetTypeID(policyStr) == CFStringGetTypeID(), errOut);
194
195	SInt32 dummy;
196	CFNumberRef allowedError = (CFNumberRef)CFDictionaryGetValue(ucDict,
197		kSecTrustSettingsAllowedError);
198	require(tsIsGoodCfNum(allowedError, &dummy), errOut);
199
200	CFNumberRef trustSettingsResult = (CFNumberRef)CFDictionaryGetValue(ucDict,
201		kSecTrustSettingsResult);
202	require(tsIsGoodCfNum(trustSettingsResult, &dummy), errOut);
203
204	CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(ucDict,
205		kSecTrustSettingsKeyUsage);
206	require(tsIsGoodCfNum(keyUsage, &dummy), errOut);
207
208	return true;
209errOut:
210	return false;
211}
212
213static bool validateTrustSettingsArray(CFArrayRef usageConstraints) {
214	CFIndex ix, numConstraints = CFArrayGetCount(usageConstraints);
215	bool result = true;
216	for (ix = 0; ix < numConstraints; ++ix) {
217		if (!validateUsageConstraint(CFArrayGetValueAtIndex(usageConstraints, ix)))
218			result = false;
219	}
220	return result;
221}
222
223struct trustListContext {
224	CFMutableDictionaryRef dict;
225	SInt32 version;
226	bool trim;
227	OSStatus status;
228};
229
230static void trustListApplierFunction(const void *key, const void *value, void *context) {
231	CFStringRef digest = (CFStringRef)key;
232	CFDictionaryRef certDict = (CFDictionaryRef)value;
233	struct trustListContext *tlc = (struct trustListContext *)context;
234	CFDataRef newKey = NULL;
235	CFMutableDictionaryRef newDict = NULL;
236
237	/* Get the key. */
238	require(digest && CFGetTypeID(digest) == CFStringGetTypeID(), errOut);
239	/* Convert it to a CFDataRef. */
240	require(newKey = SecCopyDataFromHexString(digest), errOut);
241
242	/* get per-cert dictionary */
243	require(certDict && CFGetTypeID(certDict) == CFDictionaryGetTypeID(), errOut);
244
245	/* Create the to be inserted dictionary. */
246	require(newDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
247		tlc->trim ? 1 : 4, &kCFTypeDictionaryKeyCallBacks,
248		&kCFTypeDictionaryValueCallBacks), errOut);
249
250	/* The certDict dictionary should have exactly four entries.
251	   If we're trimming, all we need is the actual trust settings. */
252
253	/* issuer */
254	CFDataRef issuer = (CFDataRef)CFDictionaryGetValue(certDict,
255		kTrustRecordIssuer);
256	require(issuer && CFGetTypeID(issuer) == CFDataGetTypeID(), errOut);
257
258	/* serial number */
259	CFDataRef serial = (CFDataRef)CFDictionaryGetValue(certDict,
260		kTrustRecordSerialNumber);
261	require(serial && CFGetTypeID(serial) == CFDataGetTypeID(), errOut);
262
263	/* modification date */
264	CFDateRef modDate = (CFDateRef)CFDictionaryGetValue(certDict,
265		kTrustRecordModDate);
266	require(modDate && CFGetTypeID(modDate) == CFDateGetTypeID(), errOut);
267
268	/* If we are not trimming we copy these extra values as well. */
269	if (!tlc->trim) {
270		CFDictionaryAddValue(newDict, kTrustRecordIssuer, issuer);
271		CFDictionaryAddValue(newDict, kTrustRecordSerialNumber, serial);
272		CFDictionaryAddValue(newDict, kTrustRecordModDate, modDate);
273	}
274
275	/* The actual trust settings */
276	CFArrayRef trustSettings = (CFArrayRef)CFDictionaryGetValue(certDict,
277			kTrustRecordTrustSettings);
278	/* optional; this cert's entry is good */
279	if(trustSettings) {
280		require(CFGetTypeID(trustSettings) == CFArrayGetTypeID(), errOut);
281
282		/* Now validate the usageConstraint array contents */
283		require(validateTrustSettingsArray(trustSettings), errOut);
284
285		/* Add the trustSettings to the dict. */
286		CFDictionaryAddValue(newDict, kTrustRecordTrustSettings, trustSettings);
287	}
288
289	CFDictionaryAddValue(tlc->dict, newKey, newDict);
290	CFRelease(newKey);
291	CFRelease(newDict);
292
293	return;
294errOut:
295	CFReleaseSafe(newKey);
296	CFReleaseSafe(newDict);
297	tlc->status = errSecInvalidTrustSettings;
298}
299
300static OSStatus SecTrustSettingsValidate(SecTrustSettingsRef ts, bool trim) {
301	/* top level dictionary */
302	require(ts->_propList, errOut);
303	require(CFGetTypeID(ts->_propList) == CFDictionaryGetTypeID(), errOut);
304
305	/* That dictionary has two entries */
306	CFNumberRef cfVers = (CFNumberRef)CFDictionaryGetValue(ts->_propList, kTrustRecordVersion);
307	require(cfVers != NULL && CFGetTypeID(cfVers) == CFNumberGetTypeID(), errOut);
308	require(CFNumberGetValue(cfVers, kCFNumberSInt32Type, &ts->_dictVersion), errOut);
309	require((ts->_dictVersion <= kSecTrustRecordVersionCurrent) &&
310		(ts->_dictVersion != kSecTrustRecordVersionInvalid), errOut);
311
312	/* other backwards-compatibility handling done later, if needed, per _dictVersion */
313
314	require(ts->_trustDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
315		&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), errOut);
316
317	CFDictionaryRef trustList = (CFDictionaryRef)CFDictionaryGetValue(
318		ts->_propList, kTrustRecordTrustList);
319	require(trustList != NULL &&
320		CFGetTypeID(trustList) == CFDictionaryGetTypeID(), errOut);
321
322	/* Convert the per-cert entries from on disk to in memory format. */
323	struct trustListContext context = {
324		ts->_trustDict, ts->_dictVersion, trim, errSecSuccess
325	};
326	CFDictionaryApplyFunction(trustList, trustListApplierFunction, &context);
327
328	if (trim) {
329		/* we don't need the top-level dictionary any more */
330		CFRelease(ts->_propList);
331		ts->_propList = NULL;
332	}
333
334	return context.status;
335
336errOut:
337	return errSecInvalidTrustSettings;
338}
339
340OSStatus SecTrustSettingsCreateFromExternal(SecTrustSettingsDomain domain,
341    CFDataRef external, SecTrustSettingsRef *ts) {
342    CFAllocatorRef allocator = kCFAllocatorDefault;
343    CFIndex size = sizeof(struct __SecTrustSettings);
344    SecTrustSettingsRef result;
345
346	require(result = (SecTrustSettingsRef)_CFRuntimeCreateInstance(allocator,
347		SecTrustSettingsGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
348
349	CFErrorRef error = NULL;
350	CFPropertyListRef plist = CFPropertyListCreateWithData(kCFAllocatorDefault,
351        external, kCFPropertyListImmutable, NULL, &error);
352	if (!plist) {
353		secwarning("SecTrustSettingsCreateFromExternal: %@", error);
354		CFReleaseSafe(error);
355        CFReleaseSafe(result);
356		goto errOut;
357	}
358
359	result->_propList = plist;
360	result->_trustDict = NULL;
361	SecTrustSettingsValidate(result, false);
362
363	*ts = result;
364
365	return errSecSuccess;
366
367errOut:
368    return errSecInvalidTrustSettings;
369}
370
371SecTrustSettingsRef SecTrustSettingsCreate(SecTrustSettingsDomain domain,
372    bool create, bool trim) {
373    CFAllocatorRef allocator = kCFAllocatorDefault;
374    CFIndex size = sizeof(struct __SecTrustSettings);
375    SecTrustSettingsRef result =
376		(SecTrustSettingsRef)_CFRuntimeCreateInstance(allocator,
377		SecTrustSettingsGetTypeID(), size - sizeof(CFRuntimeBase), 0);
378	if (!result)
379        return NULL;
380
381	//result->_data = NULL;
382
383    return result;
384}
385
386CFDataRef SecTrustSettingsCopyExternal(SecTrustSettingsRef ts) {
387	/* Transform the internal represantation of the trustSettings to an
388	   external form. */
389	// @@@ SecTrustSettingsUpdatePropertyList(SecTrustSettingsRef ts);
390	CFDataRef xmlData;
391	verify(xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault,
392		ts->_propList));
393	return xmlData;
394}
395
396void SecTrustSettingsSet(SecCertificateRef certRef,
397    CFTypeRef trustSettingsDictOrArray) {
398}
399
400
401
402// MARK: -
403// MARK: SPI functions
404
405
406/*
407 * Fundamental routine used by TP to ascertain status of one cert.
408 *
409 * Returns true in *foundMatchingEntry if a trust setting matching
410 * specific constraints was found for the cert. Returns true in
411 * *foundAnyEntry if any entry was found for the cert, even if it
412 * did not match the specified constraints. The TP uses this to
413 * optimize for the case where a cert is being evaluated for
414 * one type of usage, and then later for another type. If
415 * foundAnyEntry is false, the second evaluation need not occur.
416 *
417 * Returns the domain in which a setting was found in *foundDomain.
418 *
419 * Allowed errors applying to the specified cert evaluation
420 * are returned in a mallocd array in *allowedErrors and must
421 * be freed by caller.
422 *
423 * The design of the entire TrustSettings module is centered around
424 * optimizing the performance of this routine (security concerns
425 * aside, that is). It's why the per-cert dictionaries are stored
426 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
427 * are cached in memory by tsGetGlobalTrustSettings(), and why those
428 * cached TrustSettings objects are 'trimmed' of dictionary fields
429 * which are not needed to verify a cert.
430 *
431 * The API functions which are used to manipulate Trust Settings
432 * are called infrequently and need not be particularly fast since
433 * they result in user interaction for authentication. Thus they do
434 * not use cached TrustSettings as this function does.
435 */
436OSStatus SecTrustSettingsEvaluateCertificate(
437	SecCertificateRef certificate,
438	SecPolicyRef policy,
439	SecTrustSettingsKeyUsage	keyUsage,			/* optional */
440	bool						isSelfSignedCert,	/* for checking default setting */
441	/* RETURNED values */
442	SecTrustSettingsDomain		*foundDomain,
443	CFArrayRef					*allowedErrors,		/* RETURNED */
444	SecTrustSettingsResult		*resultType,		/* RETURNED */
445	bool						*foundMatchingEntry,/* RETURNED */
446	bool						*foundAnyEntry)		/* RETURNED */
447{
448#if 0
449	BEGIN_RCSAPI
450
451	StLock<Mutex>	_(sutCacheLock());
452
453	TS_REQUIRED(certHashStr)
454	TS_REQUIRED(foundDomain)
455	TS_REQUIRED(allowedErrors)
456	TS_REQUIRED(numAllowedErrors)
457	TS_REQUIRED(resultType)
458	TS_REQUIRED(foundMatchingEntry)
459	TS_REQUIRED(foundMatchingEntry)
460
461	/* ensure a NULL_terminated string */
462	auto_array<char> polStr;
463	if(policyString != NULL) {
464		polStr.allocate(policyStringLen + 1);
465		memmove(polStr.get(), policyString, policyStringLen);
466		if(policyString[policyStringLen - 1] != '\0') {
467			(polStr.get())[policyStringLen] = '\0';
468		}
469	}
470
471	/* initial condition - this can grow if we inspect multiple TrustSettings */
472	*allowedErrors = NULL;
473	*numAllowedErrors = 0;
474
475	/*
476	 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
477	 * search user first, then admin, then system.
478	 */
479	assert(kSecTrustSettingsDomainAdmin == (kSecTrustSettingsDomainUser + 1));
480	assert(kSecTrustSettingsDomainSystem == (kSecTrustSettingsDomainAdmin + 1));
481	bool foundAny = false;
482	for(unsigned domain=kSecTrustSettingsDomainUser;
483			     domain<=kSecTrustSettingsDomainSystem;
484				 domain++) {
485		TrustSettings *ts = tsGetGlobalTrustSettings(domain);
486		if(ts == NULL) {
487			continue;
488		}
489
490		/* validate cert returns true if matching entry was found */
491		bool foundAnyHere = false;
492		bool found = ts->evaluateCert(certHashStr, policyOID,
493			polStr.get(), keyUsage, isRootCert,
494			allowedErrors, numAllowedErrors, resultType, &foundAnyHere);
495
496		if(found) {
497			/*
498			 * Note this, even though we may overwrite it later if this
499			 * is an Unspecified entry and we find a definitive entry
500			 * later
501			 */
502			*foundDomain = domain;
503		}
504		if(found && (*resultType != kSecTrustSettingsResultUnspecified)) {
505			trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain);
506			*foundAnyEntry = true;
507			*foundMatchingEntry = true;
508			return errSecSuccess;
509		}
510		foundAny |= foundAnyHere;
511	}
512	trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
513	*foundAnyEntry = foundAny;
514	*foundMatchingEntry = false;
515	return errSecSuccess;
516	END_RCSAPI
517#endif
518	return errSecSuccess;
519}
520
521/*
522 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
523 * No locking or cache flushing here; it's all local to the TrustSettings
524 * we construct here.
525 */
526OSStatus SecTrustSettingsSetTrustSettingsExternal(
527	CFDataRef			settingsIn,					/* optional */
528	SecCertificateRef	certRef,					/* optional */
529	CFTypeRef			trustSettingsDictOrArray,	/* optional */
530	CFDataRef			*settingsOut)				/* RETURNED */
531{
532    SecTrustSettingsRef ts = NULL;
533    OSStatus status;
534
535    require_noerr(status = SecTrustSettingsCreateFromExternal(
536        kSecTrustSettingsDomainMemory, settingsIn, &ts), errOut);
537    SecTrustSettingsSet(certRef, trustSettingsDictOrArray);
538    *settingsOut = SecTrustSettingsCopyExternal(ts);
539
540errOut:
541    CFReleaseSafe(ts);
542	return status;
543}
544