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 <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 // MARK: Static Functions
44 /* Return a CFDataRef representation of a (hex)string. */
45 static CFDataRef
SecCopyDataFromHexString(CFStringRef string
) {
46 CFMutableDataRef data
;
50 length
= string
? CFStringGetLength(string
) : 0;
52 secwarning("Odd length string: %@ returning NULL", string
);
56 data
= CFDataCreateMutable(kCFAllocatorDefault
, length
/ 2);
57 CFDataSetLength(data
, length
/ 2);
58 bytes
= CFDataGetMutableBytePtr(data
);
60 CFStringInlineBuffer buf
;
61 CFRange range
= { 0, length
};
62 CFStringInitInlineBuffer(string
, &buf
, range
);
64 for (ix
= 0; ix
< length
; ++ix
) {
65 UniChar c
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
67 if ('0' <= c
&& c
<= '9')
69 else if ('A' <= c
&& c
<= 'F')
71 else if ('a' <= c
&& c
<= 'a')
74 secwarning("Non hex string: %@ returning NULL", string
);
79 *bytes
++ = (lastv
<< 4) + v
;
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.
93 static CFStringRef
SecTrustSettingsCertHashStrFromCert(
94 SecCertificateRef certRef
)
96 if (certRef
== NULL
) {
100 if(certRef
== kSecTrustSettingsDefaultRootCertSetting
) {
101 /* use this string instead of the cert hash as the dictionary key */
102 secdebug("trustsettings","DefaultSetting");
103 return kSecTrustRecordDefaultRootCert
;
106 CFDataRef digest
= SecCertificateGetSHA1Digest(certRef
);
107 return CFDataCopyHexString(digest
);
112 // MARK: SecTrustSettings
113 /********************************************************
114 ************** SecTrustSettings object *****************
115 ********************************************************/
117 struct __SecTrustSettings
{
120 /* the overall parsed TrustSettings - may be NULL if this is trimmed */
121 CFDictionaryRef _propList
;
123 /* and the main thing we work with, the dictionary of per-cert trust settings */
124 CFMutableDictionaryRef _trustDict
;
126 /* version number of mPropDict */
129 SecTrustSettingsDomain _domain
;
130 bool _dirty
; /* we've changed _trustDict since creation */
133 /* CFRuntime regsitration data. */
134 static pthread_once_t kSecTrustSettingsRegisterClass
= PTHREAD_ONCE_INIT
;
135 static CFTypeID kSecTrustSettingsTypeID
= _kCFRuntimeNotATypeID
;
137 static void SecTrustSettingsDestroy(CFTypeRef cf
) {
138 SecTrustSettingsRef ts
= (SecTrustSettingsRef
) cf
;
139 CFReleaseSafe(ts
->_propList
);
140 CFReleaseSafe(ts
->_trustDict
);
143 static void SecTrustSettingsRegisterClass(void) {
144 static const CFRuntimeClass kSecTrustSettingsClass
= {
146 "SecTrustSettings", /* class name */
149 SecTrustSettingsDestroy
, /* dealloc */
152 NULL
, /* copyFormattingDesc */
153 NULL
/* copyDebugDesc */
156 kSecTrustSettingsTypeID
= _CFRuntimeRegisterClass(&kSecTrustSettingsClass
);
159 /* SecTrustSettings API functions. */
160 CFTypeID
SecTrustSettingsGetTypeID(void) {
161 pthread_once(&kSecTrustSettingsRegisterClass
, SecTrustSettingsRegisterClass
);
162 return kSecTrustSettingsTypeID
;
165 /* Make sure a presumed CFNumber can be converted to a 32-bit number */
166 static bool tsIsGoodCfNum(CFNumberRef cfn
, SInt32
*num
)
173 require(CFGetTypeID(cfn
) == CFNumberGetTypeID(), errOut
);
174 return CFNumberGetValue(cfn
, kCFNumberSInt32Type
, num
);
179 static bool validateUsageConstraint(const void *value
) {
180 CFDictionaryRef ucDict
= (CFDictionaryRef
)value
;
181 require(CFGetTypeID(ucDict
) == CFDictionaryGetTypeID(), errOut
);
183 CFDataRef certPolicy
= (CFDataRef
)CFDictionaryGetValue(ucDict
,
184 kSecTrustSettingsPolicy
);
185 require(certPolicy
&& CFGetTypeID(certPolicy
) == CFDataGetTypeID(), errOut
);
187 CFStringRef certApp
= (CFStringRef
)CFDictionaryGetValue(ucDict
,
188 kSecTrustSettingsApplication
);
189 require(certApp
&& CFGetTypeID(certApp
) == CFStringGetTypeID(), errOut
);
191 CFStringRef policyStr
= (CFStringRef
)CFDictionaryGetValue(ucDict
,
192 kSecTrustSettingsPolicyString
);
193 require(policyStr
&& CFGetTypeID(policyStr
) == CFStringGetTypeID(), errOut
);
196 CFNumberRef allowedError
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
197 kSecTrustSettingsAllowedError
);
198 require(tsIsGoodCfNum(allowedError
, &dummy
), errOut
);
200 CFNumberRef trustSettingsResult
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
201 kSecTrustSettingsResult
);
202 require(tsIsGoodCfNum(trustSettingsResult
, &dummy
), errOut
);
204 CFNumberRef keyUsage
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
205 kSecTrustSettingsKeyUsage
);
206 require(tsIsGoodCfNum(keyUsage
, &dummy
), errOut
);
213 static bool validateTrustSettingsArray(CFArrayRef usageConstraints
) {
214 CFIndex ix
, numConstraints
= CFArrayGetCount(usageConstraints
);
216 for (ix
= 0; ix
< numConstraints
; ++ix
) {
217 if (!validateUsageConstraint(CFArrayGetValueAtIndex(usageConstraints
, ix
)))
223 struct trustListContext
{
224 CFMutableDictionaryRef dict
;
230 static 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
;
238 require(digest
&& CFGetTypeID(digest
) == CFStringGetTypeID(), errOut
);
239 /* Convert it to a CFDataRef. */
240 require(newKey
= SecCopyDataFromHexString(digest
), errOut
);
242 /* get per-cert dictionary */
243 require(certDict
&& CFGetTypeID(certDict
) == CFDictionaryGetTypeID(), errOut
);
245 /* Create the to be inserted dictionary. */
246 require(newDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
247 tlc
->trim
? 1 : 4, &kCFTypeDictionaryKeyCallBacks
,
248 &kCFTypeDictionaryValueCallBacks
), errOut
);
250 /* The certDict dictionary should have exactly four entries.
251 If we're trimming, all we need is the actual trust settings. */
254 CFDataRef issuer
= (CFDataRef
)CFDictionaryGetValue(certDict
,
256 require(issuer
&& CFGetTypeID(issuer
) == CFDataGetTypeID(), errOut
);
259 CFDataRef serial
= (CFDataRef
)CFDictionaryGetValue(certDict
,
260 kTrustRecordSerialNumber
);
261 require(serial
&& CFGetTypeID(serial
) == CFDataGetTypeID(), errOut
);
263 /* modification date */
264 CFDateRef modDate
= (CFDateRef
)CFDictionaryGetValue(certDict
,
265 kTrustRecordModDate
);
266 require(modDate
&& CFGetTypeID(modDate
) == CFDateGetTypeID(), errOut
);
268 /* If we are not trimming we copy these extra values as well. */
270 CFDictionaryAddValue(newDict
, kTrustRecordIssuer
, issuer
);
271 CFDictionaryAddValue(newDict
, kTrustRecordSerialNumber
, serial
);
272 CFDictionaryAddValue(newDict
, kTrustRecordModDate
, modDate
);
275 /* The actual trust settings */
276 CFArrayRef trustSettings
= (CFArrayRef
)CFDictionaryGetValue(certDict
,
277 kTrustRecordTrustSettings
);
278 /* optional; this cert's entry is good */
280 require(CFGetTypeID(trustSettings
) == CFArrayGetTypeID(), errOut
);
282 /* Now validate the usageConstraint array contents */
283 require(validateTrustSettingsArray(trustSettings
), errOut
);
285 /* Add the trustSettings to the dict. */
286 CFDictionaryAddValue(newDict
, kTrustRecordTrustSettings
, trustSettings
);
289 CFDictionaryAddValue(tlc
->dict
, newKey
, newDict
);
295 CFReleaseSafe(newKey
);
296 CFReleaseSafe(newDict
);
297 tlc
->status
= errSecInvalidTrustSettings
;
300 static OSStatus
SecTrustSettingsValidate(SecTrustSettingsRef ts
, bool trim
) {
301 /* top level dictionary */
302 require(ts
->_propList
, errOut
);
303 require(CFGetTypeID(ts
->_propList
) == CFDictionaryGetTypeID(), errOut
);
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
);
312 /* other backwards-compatibility handling done later, if needed, per _dictVersion */
314 require(ts
->_trustDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
315 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
317 CFDictionaryRef trustList
= (CFDictionaryRef
)CFDictionaryGetValue(
318 ts
->_propList
, kTrustRecordTrustList
);
319 require(trustList
!= NULL
&&
320 CFGetTypeID(trustList
) == CFDictionaryGetTypeID(), errOut
);
322 /* Convert the per-cert entries from on disk to in memory format. */
323 struct trustListContext context
= {
324 ts
->_trustDict
, ts
->_dictVersion
, trim
, errSecSuccess
326 CFDictionaryApplyFunction(trustList
, trustListApplierFunction
, &context
);
329 /* we don't need the top-level dictionary any more */
330 CFRelease(ts
->_propList
);
331 ts
->_propList
= NULL
;
334 return context
.status
;
337 return errSecInvalidTrustSettings
;
340 OSStatus
SecTrustSettingsCreateFromExternal(SecTrustSettingsDomain domain
,
341 CFDataRef external
, SecTrustSettingsRef
*ts
) {
342 CFAllocatorRef allocator
= kCFAllocatorDefault
;
343 CFIndex size
= sizeof(struct __SecTrustSettings
);
344 SecTrustSettingsRef result
;
346 require(result
= (SecTrustSettingsRef
)_CFRuntimeCreateInstance(allocator
,
347 SecTrustSettingsGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
349 CFErrorRef error
= NULL
;
350 CFPropertyListRef plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
351 external
, kCFPropertyListImmutable
, NULL
, &error
);
353 secwarning("SecTrustSettingsCreateFromExternal: %@", error
);
354 CFReleaseSafe(error
);
355 CFReleaseSafe(result
);
359 result
->_propList
= plist
;
360 result
->_trustDict
= NULL
;
361 SecTrustSettingsValidate(result
, false);
365 return errSecSuccess
;
368 return errSecInvalidTrustSettings
;
371 SecTrustSettingsRef
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);
381 //result->_data = NULL;
386 CFDataRef
SecTrustSettingsCopyExternal(SecTrustSettingsRef ts
) {
387 /* Transform the internal represantation of the trustSettings to an
389 // @@@ SecTrustSettingsUpdatePropertyList(SecTrustSettingsRef ts);
391 verify(xmlData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
396 void SecTrustSettingsSet(SecCertificateRef certRef
,
397 CFTypeRef trustSettingsDictOrArray
) {
403 // MARK: SPI functions
407 * Fundamental routine used by TP to ascertain status of one cert.
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.
417 * Returns the domain in which a setting was found in *foundDomain.
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.
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.
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.
436 OSStatus
SecTrustSettingsEvaluateCertificate(
437 SecCertificateRef certificate
,
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 */
451 StLock
<Mutex
> _(sutCacheLock());
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
)
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';
471 /* initial condition - this can grow if we inspect multiple TrustSettings */
472 *allowedErrors
= NULL
;
473 *numAllowedErrors
= 0;
476 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
477 * search user first, then admin, then system.
479 assert(kSecTrustSettingsDomainAdmin
== (kSecTrustSettingsDomainUser
+ 1));
480 assert(kSecTrustSettingsDomainSystem
== (kSecTrustSettingsDomainAdmin
+ 1));
481 bool foundAny
= false;
482 for(unsigned domain
=kSecTrustSettingsDomainUser
;
483 domain
<=kSecTrustSettingsDomainSystem
;
485 TrustSettings
*ts
= tsGetGlobalTrustSettings(domain
);
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
);
498 * Note this, even though we may overwrite it later if this
499 * is an Unspecified entry and we find a definitive entry
502 *foundDomain
= domain
;
504 if(found
&& (*resultType
!= kSecTrustSettingsResultUnspecified
)) {
505 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
);
506 *foundAnyEntry
= true;
507 *foundMatchingEntry
= true;
508 return errSecSuccess
;
510 foundAny
|= foundAnyHere
;
512 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
513 *foundAnyEntry
= foundAny
;
514 *foundMatchingEntry
= false;
515 return errSecSuccess
;
518 return errSecSuccess
;
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
526 OSStatus
SecTrustSettingsSetTrustSettingsExternal(
527 CFDataRef settingsIn
, /* optional */
528 SecCertificateRef certRef
, /* optional */
529 CFTypeRef trustSettingsDictOrArray
, /* optional */
530 CFDataRef
*settingsOut
) /* RETURNED */
532 SecTrustSettingsRef ts
= NULL
;
535 require_noerr(status
= SecTrustSettingsCreateFromExternal(
536 kSecTrustSettingsDomainMemory
, settingsIn
, &ts
), errOut
);
537 SecTrustSettingsSet(certRef
, trustSettingsDictOrArray
);
538 *settingsOut
= SecTrustSettingsCopyExternal(ts
);