2 * Copyright (c) 2007-2008,2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecTrustSettings.c - Implement trust settings for certificates
28 #include "SecTrustSettings.h"
29 #include "SecTrustSettingsPriv.h"
30 #include <Security/SecCertificatePriv.h>
31 #include <AssertMacros.h>
33 #include <security_utilities/debugging.h>
34 #include "SecBasePriv.h"
35 #include <Security/SecInternal.h>
36 #include <CoreFoundation/CFRuntime.h>
38 #define trustSettingsDbg(args...) secdebug("trustSettings", ## args)
39 #define trustSettingsEvalDbg(args...) secdebug("trustSettingsEval", ## args)
42 #pragma mark Static Functions
45 /* Return a (hex)string representation of a CFDataRef. */
46 static CFStringRef
SecCopyHexStringFromData(CFDataRef data
)
50 CFMutableStringRef string
;
53 length
= CFDataGetLength(data
);
54 bytes
= CFDataGetBytePtr(data
);
59 string
= CFStringCreateMutable(kCFAllocatorDefault
, length
* 2);
60 for (ix
= 0; ix
< length
; ++ix
)
61 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), bytes
[ix
]);
67 /* Return a CFDataRef representation of a (hex)string. */
68 static CFDataRef
SecCopyDataFromHexString(CFStringRef string
) {
69 CFMutableDataRef data
;
73 length
= string
? CFStringGetLength(string
) : 0;
75 secwarning("Odd length string: %@ returning NULL", string
);
79 data
= CFDataCreateMutable(kCFAllocatorDefault
, length
/ 2);
80 CFDataSetLength(data
, length
/ 2);
81 bytes
= CFDataGetMutableBytePtr(data
);
83 CFStringInlineBuffer buf
;
84 CFRange range
= { 0, length
};
85 CFStringInitInlineBuffer(string
, &buf
, range
);
87 for (ix
= 0; ix
< length
; ++ix
) {
88 UniChar c
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
90 if ('0' <= c
&& c
<= '9')
92 else if ('A' <= c
&& c
<= 'F')
94 else if ('a' <= c
&& c
<= 'a')
97 secwarning("Non hex string: %@ returning NULL", string
);
102 *bytes
++ = (lastv
<< 4) + v
;
113 * Obtain a string representing a cert's SHA1 digest. This string is
114 * the key used to look up per-cert trust settings in a TrustSettings record.
116 static CFStringRef
SecTrustSettingsCertHashStrFromCert(
117 SecCertificateRef certRef
)
119 if (certRef
== NULL
) {
123 if(certRef
== kSecTrustSettingsDefaultRootCertSetting
) {
124 /* use this string instead of the cert hash as the dictionary key */
125 secdebug("trustsettings","DefaultSetting");
126 return kSecTrustRecordDefaultRootCert
;
129 CFDataRef digest
= SecCertificateGetSHA1Digest(certRef
);
130 return SecCopyHexStringFromData(digest
);
135 #pragma mark SecTrustSettings
136 /********************************************************
137 ************** SecTrustSettings object *****************
138 ********************************************************/
140 struct __SecTrustSettings
{
143 /* the overall parsed TrustSettings - may be NULL if this is trimmed */
144 CFDictionaryRef _propList
;
146 /* and the main thing we work with, the dictionary of per-cert trust settings */
147 CFMutableDictionaryRef _trustDict
;
149 /* version number of mPropDict */
152 SecTrustSettingsDomain _domain
;
153 bool _dirty
; /* we've changed _trustDict since creation */
156 /* CFRuntime regsitration data. */
157 static pthread_once_t kSecTrustSettingsRegisterClass
= PTHREAD_ONCE_INIT
;
158 static CFTypeID kSecTrustSettingsTypeID
= _kCFRuntimeNotATypeID
;
160 static void SecTrustSettingsDestroy(CFTypeRef cf
) {
161 SecTrustSettingsRef ts
= (SecTrustSettingsRef
) cf
;
162 CFReleaseSafe(ts
->_propList
);
163 CFReleaseSafe(ts
->_trustDict
);
166 static void SecTrustSettingsRegisterClass(void) {
167 static const CFRuntimeClass kSecTrustSettingsClass
= {
169 "SecTrustSettings", /* class name */
172 SecTrustSettingsDestroy
, /* dealloc */
175 NULL
, /* copyFormattingDesc */
176 NULL
/* copyDebugDesc */
179 kSecTrustSettingsTypeID
= _CFRuntimeRegisterClass(&kSecTrustSettingsClass
);
182 /* SecTrustSettings API functions. */
183 CFTypeID
SecTrustSettingsGetTypeID(void) {
184 pthread_once(&kSecTrustSettingsRegisterClass
, SecTrustSettingsRegisterClass
);
185 return kSecTrustSettingsTypeID
;
188 /* Make sure a presumed CFNumber can be converted to a 32-bit number */
189 static bool tsIsGoodCfNum(CFNumberRef cfn
, SInt32
*num
)
196 require(CFGetTypeID(cfn
) == CFNumberGetTypeID(), errOut
);
197 return CFNumberGetValue(cfn
, kCFNumberSInt32Type
, num
);
202 static bool validateUsageConstraint(const void *value
) {
203 CFDictionaryRef ucDict
= (CFDictionaryRef
)value
;
204 require(CFGetTypeID(ucDict
) == CFDictionaryGetTypeID(), errOut
);
206 CFDataRef certPolicy
= (CFDataRef
)CFDictionaryGetValue(ucDict
,
207 kSecTrustSettingsPolicy
);
208 require(certPolicy
&& CFGetTypeID(certPolicy
) == CFDataGetTypeID(), errOut
);
210 CFStringRef certApp
= (CFStringRef
)CFDictionaryGetValue(ucDict
,
211 kSecTrustSettingsApplication
);
212 require(certApp
&& CFGetTypeID(certApp
) == CFStringGetTypeID(), errOut
);
214 CFStringRef policyStr
= (CFStringRef
)CFDictionaryGetValue(ucDict
,
215 kSecTrustSettingsPolicyString
);
216 require(policyStr
&& CFGetTypeID(policyStr
) == CFStringGetTypeID(), errOut
);
219 CFNumberRef allowedError
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
220 kSecTrustSettingsAllowedError
);
221 require(tsIsGoodCfNum(allowedError
, &dummy
), errOut
);
223 CFNumberRef trustSettingsResult
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
224 kSecTrustSettingsResult
);
225 require(tsIsGoodCfNum(trustSettingsResult
, &dummy
), errOut
);
227 CFNumberRef keyUsage
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
228 kSecTrustSettingsKeyUsage
);
229 require(tsIsGoodCfNum(keyUsage
, &dummy
), errOut
);
236 static bool validateTrustSettingsArray(CFArrayRef usageConstraints
) {
237 CFIndex ix
, numConstraints
= CFArrayGetCount(usageConstraints
);
239 for (ix
= 0; ix
< numConstraints
; ++ix
) {
240 if (!validateUsageConstraint(CFArrayGetValueAtIndex(usageConstraints
, ix
)))
246 struct trustListContext
{
247 CFMutableDictionaryRef dict
;
253 static void trustListApplierFunction(const void *key
, const void *value
, void *context
) {
254 CFStringRef digest
= (CFStringRef
)key
;
255 CFDictionaryRef certDict
= (CFDictionaryRef
)value
;
256 struct trustListContext
*tlc
= (struct trustListContext
*)context
;
257 CFDataRef newKey
= NULL
;
258 CFMutableDictionaryRef newDict
= NULL
;
261 require(digest
&& CFGetTypeID(digest
) == CFStringGetTypeID(), errOut
);
262 /* Convert it to a CFDataRef. */
263 require(newKey
= SecCopyDataFromHexString(digest
), errOut
);
265 /* get per-cert dictionary */
266 require(certDict
&& CFGetTypeID(certDict
) == CFDictionaryGetTypeID(), errOut
);
268 /* Create the to be inserted dictionary. */
269 require(newDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
270 tlc
->trim
? 1 : 4, &kCFTypeDictionaryKeyCallBacks
,
271 &kCFTypeDictionaryValueCallBacks
), errOut
);
273 /* The certDict dictionary should have exactly four entries.
274 If we're trimming, all we need is the actual trust settings. */
277 CFDataRef issuer
= (CFDataRef
)CFDictionaryGetValue(certDict
,
279 require(issuer
&& CFGetTypeID(issuer
) == CFDataGetTypeID(), errOut
);
282 CFDataRef serial
= (CFDataRef
)CFDictionaryGetValue(certDict
,
283 kTrustRecordSerialNumber
);
284 require(serial
&& CFGetTypeID(serial
) == CFDataGetTypeID(), errOut
);
286 /* modification date */
287 CFDateRef modDate
= (CFDateRef
)CFDictionaryGetValue(certDict
,
288 kTrustRecordModDate
);
289 require(modDate
&& CFGetTypeID(modDate
) == CFDateGetTypeID(), errOut
);
291 /* If we are not trimming we copy these extra values as well. */
293 CFDictionaryAddValue(newDict
, kTrustRecordIssuer
, issuer
);
294 CFDictionaryAddValue(newDict
, kTrustRecordSerialNumber
, serial
);
295 CFDictionaryAddValue(newDict
, kTrustRecordModDate
, modDate
);
298 /* The actual trust settings */
299 CFArrayRef trustSettings
= (CFArrayRef
)CFDictionaryGetValue(certDict
,
300 kTrustRecordTrustSettings
);
301 /* optional; this cert's entry is good */
303 require(CFGetTypeID(trustSettings
) == CFArrayGetTypeID(), errOut
);
305 /* Now validate the usageConstraint array contents */
306 require(validateTrustSettingsArray(trustSettings
), errOut
);
308 /* Add the trustSettings to the dict. */
309 CFDictionaryAddValue(newDict
, kTrustRecordTrustSettings
, trustSettings
);
312 CFDictionaryAddValue(tlc
->dict
, newKey
, newDict
);
318 CFReleaseSafe(newKey
);
319 CFReleaseSafe(newDict
);
320 tlc
->status
= errSecInvalidTrustSettings
;
323 static OSStatus
SecTrustSettingsValidate(SecTrustSettingsRef ts
, bool trim
) {
324 /* top level dictionary */
325 require(ts
->_propList
, errOut
);
326 require(CFGetTypeID(ts
->_propList
) == CFDictionaryGetTypeID(), errOut
);
328 /* That dictionary has two entries */
329 CFNumberRef cfVers
= (CFNumberRef
)CFDictionaryGetValue(ts
->_propList
, kTrustRecordVersion
);
330 require(cfVers
!= NULL
&& CFGetTypeID(cfVers
) == CFNumberGetTypeID(), errOut
);
331 require(CFNumberGetValue(cfVers
, kCFNumberSInt32Type
, &ts
->_dictVersion
), errOut
);
332 require((ts
->_dictVersion
<= kSecTrustRecordVersionCurrent
) &&
333 (ts
->_dictVersion
!= kSecTrustRecordVersionInvalid
), errOut
);
335 /* other backwards-compatibility handling done later, if needed, per _dictVersion */
337 require(ts
->_trustDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
338 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
340 CFDictionaryRef trustList
= (CFDictionaryRef
)CFDictionaryGetValue(
341 ts
->_propList
, kTrustRecordTrustList
);
342 require(trustList
!= NULL
&&
343 CFGetTypeID(trustList
) == CFDictionaryGetTypeID(), errOut
);
345 /* Convert the per-cert entries from on disk to in memory format. */
346 struct trustListContext context
= {
347 ts
->_trustDict
, ts
->_dictVersion
, trim
, noErr
349 CFDictionaryApplyFunction(trustList
, trustListApplierFunction
, &context
);
352 /* we don't need the top-level dictionary any more */
353 CFRelease(ts
->_propList
);
354 ts
->_propList
= NULL
;
357 return context
.status
;
360 return errSecInvalidTrustSettings
;
363 OSStatus
SecTrustSettingsCreateFromExternal(SecTrustSettingsDomain domain
,
364 CFDataRef external
, SecTrustSettingsRef
*ts
) {
365 CFAllocatorRef allocator
= kCFAllocatorDefault
;
366 CFIndex size
= sizeof(struct __SecTrustSettings
);
367 SecTrustSettingsRef result
;
369 require(result
= (SecTrustSettingsRef
)_CFRuntimeCreateInstance(allocator
,
370 SecTrustSettingsGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
372 CFStringRef errorString
= NULL
;
373 CFPropertyListRef plist
= CFPropertyListCreateFromXMLData(
374 kCFAllocatorDefault
, external
, kCFPropertyListImmutable
, &errorString
);
376 secwarning("CFPropertyListCreateFromXMLData: %@", errorString
);
377 CFReleaseSafe(errorString
);
381 result
->_propList
= plist
;
382 result
->_trustDict
= NULL
;
383 SecTrustSettingsValidate(result
, false);
390 return errSecInvalidTrustSettings
;
393 SecTrustSettingsRef
SecTrustSettingsCreate(SecTrustSettingsDomain domain
,
394 bool create
, bool trim
) {
395 CFAllocatorRef allocator
= kCFAllocatorDefault
;
396 CFIndex size
= sizeof(struct __SecTrustSettings
);
397 SecTrustSettingsRef result
=
398 (SecTrustSettingsRef
)_CFRuntimeCreateInstance(allocator
,
399 SecTrustSettingsGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
403 //result->_data = NULL;
408 CFDataRef
SecTrustSettingsCopyExternal(SecTrustSettingsRef ts
) {
409 /* Transform the internal represantation of the trustSettings to an
411 // @@@ SecTrustSettingsUpdatePropertyList(SecTrustSettingsRef ts);
413 verify(xmlData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
418 void SecTrustSettingsSet(SecCertificateRef certRef
,
419 CFTypeRef trustSettingsDictOrArray
) {
425 #pragma mark SPI functions
429 * Fundamental routine used by TP to ascertain status of one cert.
431 * Returns true in *foundMatchingEntry if a trust setting matching
432 * specific constraints was found for the cert. Returns true in
433 * *foundAnyEntry if any entry was found for the cert, even if it
434 * did not match the specified constraints. The TP uses this to
435 * optimize for the case where a cert is being evaluated for
436 * one type of usage, and then later for another type. If
437 * foundAnyEntry is false, the second evaluation need not occur.
439 * Returns the domain in which a setting was found in *foundDomain.
441 * Allowed errors applying to the specified cert evaluation
442 * are returned in a mallocd array in *allowedErrors and must
443 * be freed by caller.
445 * The design of the entire TrustSettings module is centered around
446 * optimizing the performance of this routine (security concerns
447 * aside, that is). It's why the per-cert dictionaries are stored
448 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
449 * are cached in memory by tsGetGlobalTrustSettings(), and why those
450 * cached TrustSettings objects are 'trimmed' of dictionary fields
451 * which are not needed to verify a cert.
453 * The API functions which are used to manipulate Trust Settings
454 * are called infrequently and need not be particularly fast since
455 * they result in user interaction for authentication. Thus they do
456 * not use cached TrustSettings as this function does.
458 OSStatus
SecTrustSettingsEvaluateCertificate(
459 SecCertificateRef certificate
,
461 SecTrustSettingsKeyUsage keyUsage
, /* optional */
462 bool isSelfSignedCert
, /* for checking default setting */
463 /* RETURNED values */
464 SecTrustSettingsDomain
*foundDomain
,
465 CFArrayRef
*allowedErrors
, /* RETURNED */
466 SecTrustSettingsResult
*resultType
, /* RETURNED */
467 bool *foundMatchingEntry
,/* RETURNED */
468 bool *foundAnyEntry
) /* RETURNED */
473 StLock
<Mutex
> _(sutCacheLock());
475 TS_REQUIRED(certHashStr
)
476 TS_REQUIRED(foundDomain
)
477 TS_REQUIRED(allowedErrors
)
478 TS_REQUIRED(numAllowedErrors
)
479 TS_REQUIRED(resultType
)
480 TS_REQUIRED(foundMatchingEntry
)
481 TS_REQUIRED(foundMatchingEntry
)
483 /* ensure a NULL_terminated string */
484 auto_array
<char> polStr
;
485 if(policyString
!= NULL
) {
486 polStr
.allocate(policyStringLen
+ 1);
487 memmove(polStr
.get(), policyString
, policyStringLen
);
488 if(policyString
[policyStringLen
- 1] != '\0') {
489 (polStr
.get())[policyStringLen
] = '\0';
493 /* initial condition - this can grow if we inspect multiple TrustSettings */
494 *allowedErrors
= NULL
;
495 *numAllowedErrors
= 0;
498 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
499 * search user first, then admin, then system.
501 assert(kSecTrustSettingsDomainAdmin
== (kSecTrustSettingsDomainUser
+ 1));
502 assert(kSecTrustSettingsDomainSystem
== (kSecTrustSettingsDomainAdmin
+ 1));
503 bool foundAny
= false;
504 for(unsigned domain
=kSecTrustSettingsDomainUser
;
505 domain
<=kSecTrustSettingsDomainSystem
;
507 TrustSettings
*ts
= tsGetGlobalTrustSettings(domain
);
512 /* validate cert returns true if matching entry was found */
513 bool foundAnyHere
= false;
514 bool found
= ts
->evaluateCert(certHashStr
, policyOID
,
515 polStr
.get(), keyUsage
, isRootCert
,
516 allowedErrors
, numAllowedErrors
, resultType
, &foundAnyHere
);
520 * Note this, even though we may overwrite it later if this
521 * is an Unspecified entry and we find a definitive entry
524 *foundDomain
= domain
;
526 if(found
&& (*resultType
!= kSecTrustSettingsResultUnspecified
)) {
527 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
);
528 *foundAnyEntry
= true;
529 *foundMatchingEntry
= true;
532 foundAny
|= foundAnyHere
;
534 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
535 *foundAnyEntry
= foundAny
;
536 *foundMatchingEntry
= false;
544 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
545 * No locking or cache flushing here; it's all local to the TrustSettings
548 OSStatus
SecTrustSettingsSetTrustSettingsExternal(
549 CFDataRef settingsIn
, /* optional */
550 SecCertificateRef certRef
, /* optional */
551 CFTypeRef trustSettingsDictOrArray
, /* optional */
552 CFDataRef
*settingsOut
) /* RETURNED */
554 SecTrustSettingsRef ts
= NULL
;
557 require_noerr(status
= SecTrustSettingsCreateFromExternal(
558 kSecTrustSettingsDomainMemory
, settingsIn
, &ts
), errOut
);
559 SecTrustSettingsSet(certRef
, trustSettingsDictOrArray
);
560 *settingsOut
= SecTrustSettingsCopyExternal(ts
);