2 * Copyright (c) 2007-2008,2010,2012-2015 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>
37 #include <utilities/SecCFWrappers.h>
39 #define trustSettingsDbg(args...) secdebug("trustSettings", ## args)
40 #define trustSettingsEvalDbg(args...) secdebug("trustSettingsEval", ## args)
43 // MARK: Static Functions
45 /* Return a CFDataRef representation of a (hex)string. */
46 static CFDataRef
SecCopyDataFromHexString(CFStringRef string
) {
47 CFMutableDataRef data
;
51 length
= string
? CFStringGetLength(string
) : 0;
53 secwarning("Odd length string: %@ returning NULL", string
);
57 data
= CFDataCreateMutable(kCFAllocatorDefault
, length
/ 2);
58 CFDataSetLength(data
, length
/ 2);
59 bytes
= CFDataGetMutableBytePtr(data
);
61 CFStringInlineBuffer buf
;
62 CFRange range
= { 0, length
};
63 CFStringInitInlineBuffer(string
, &buf
, range
);
65 for (ix
= 0; ix
< length
; ++ix
) {
66 UniChar c
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
68 if ('0' <= c
&& c
<= '9')
70 else if ('A' <= c
&& c
<= 'F')
72 else if ('a' <= c
&& c
<= 'a')
75 secwarning("Non hex string: %@ returning NULL", string
);
80 *bytes
++ = (lastv
<< 4) + v
;
91 * Obtain a string representing a cert's SHA1 digest. This string is
92 * the key used to look up per-cert trust settings in a TrustSettings record.
94 static CFStringRef
SecTrustSettingsCertHashStrFromCert(
95 SecCertificateRef certRef
)
97 if (certRef
== NULL
) {
101 if(certRef
== kSecTrustSettingsDefaultRootCertSetting
) {
102 /* use this string instead of the cert hash as the dictionary key */
103 secdebug("trustsettings","DefaultSetting");
104 return kSecTrustRecordDefaultRootCert
;
107 CFDataRef digest
= SecCertificateGetSHA1Digest(certRef
);
108 return CFDataCopyHexString(digest
);
113 // MARK: SecTrustSettings
114 /********************************************************
115 ************** SecTrustSettings object *****************
116 ********************************************************/
118 struct __SecTrustSettings
{
121 /* the overall parsed TrustSettings - may be NULL if this is trimmed */
122 CFDictionaryRef _propList
;
124 /* and the main thing we work with, the dictionary of per-cert trust settings */
125 CFMutableDictionaryRef _trustDict
;
127 /* version number of mPropDict */
130 SecTrustSettingsDomain _domain
;
131 bool _dirty
; /* we've changed _trustDict since creation */
134 static CFStringRef
SecTrustSettingsCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
135 SecTrustSettingsRef ts
= (SecTrustSettingsRef
)cf
;
136 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
137 CFSTR("<SecTrustSettings: %p>"), ts
);
140 static void SecTrustSettingsDestroy(CFTypeRef cf
) {
141 SecTrustSettingsRef ts
= (SecTrustSettingsRef
) cf
;
142 CFReleaseSafe(ts
->_propList
);
143 CFReleaseSafe(ts
->_trustDict
);
146 /* SecTrustSettings API functions. */
147 CFGiblisFor(SecTrustSettings
)
149 /* Make sure a presumed CFNumber can be converted to a 32-bit number */
150 static bool tsIsGoodCfNum(CFNumberRef cfn
, SInt32
*num
)
157 require(CFGetTypeID(cfn
) == CFNumberGetTypeID(), errOut
);
158 return CFNumberGetValue(cfn
, kCFNumberSInt32Type
, num
);
163 static bool validateUsageConstraint(const void *value
) {
164 CFDictionaryRef ucDict
= (CFDictionaryRef
)value
;
165 require(CFGetTypeID(ucDict
) == CFDictionaryGetTypeID(), errOut
);
167 CFDataRef certPolicy
= (CFDataRef
)CFDictionaryGetValue(ucDict
,
168 kSecTrustSettingsPolicy
);
169 require(certPolicy
&& CFGetTypeID(certPolicy
) == CFDataGetTypeID(), errOut
);
171 CFStringRef certApp
= (CFStringRef
)CFDictionaryGetValue(ucDict
,
172 kSecTrustSettingsApplication
);
173 require(certApp
&& CFGetTypeID(certApp
) == CFStringGetTypeID(), errOut
);
175 CFStringRef policyStr
= (CFStringRef
)CFDictionaryGetValue(ucDict
,
176 kSecTrustSettingsPolicyString
);
177 require(policyStr
&& CFGetTypeID(policyStr
) == CFStringGetTypeID(), errOut
);
180 CFNumberRef allowedError
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
181 kSecTrustSettingsAllowedError
);
182 require(tsIsGoodCfNum(allowedError
, &dummy
), errOut
);
184 CFNumberRef trustSettingsResult
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
185 kSecTrustSettingsResult
);
186 require(tsIsGoodCfNum(trustSettingsResult
, &dummy
), errOut
);
188 CFNumberRef keyUsage
= (CFNumberRef
)CFDictionaryGetValue(ucDict
,
189 kSecTrustSettingsKeyUsage
);
190 require(tsIsGoodCfNum(keyUsage
, &dummy
), errOut
);
197 static bool validateTrustSettingsArray(CFArrayRef usageConstraints
) {
198 CFIndex ix
, numConstraints
= CFArrayGetCount(usageConstraints
);
200 for (ix
= 0; ix
< numConstraints
; ++ix
) {
201 if (!validateUsageConstraint(CFArrayGetValueAtIndex(usageConstraints
, ix
)))
207 struct trustListContext
{
208 CFMutableDictionaryRef dict
;
214 static void trustListApplierFunction(const void *key
, const void *value
, void *context
) {
215 CFStringRef digest
= (CFStringRef
)key
;
216 CFDictionaryRef certDict
= (CFDictionaryRef
)value
;
217 struct trustListContext
*tlc
= (struct trustListContext
*)context
;
218 CFDataRef newKey
= NULL
;
219 CFMutableDictionaryRef newDict
= NULL
;
222 require(digest
&& CFGetTypeID(digest
) == CFStringGetTypeID(), errOut
);
223 /* Convert it to a CFDataRef. */
224 require(newKey
= SecCopyDataFromHexString(digest
), errOut
);
226 /* get per-cert dictionary */
227 require(certDict
&& CFGetTypeID(certDict
) == CFDictionaryGetTypeID(), errOut
);
229 /* Create the to be inserted dictionary. */
230 require(newDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
231 tlc
->trim
? 1 : 4, &kCFTypeDictionaryKeyCallBacks
,
232 &kCFTypeDictionaryValueCallBacks
), errOut
);
234 /* The certDict dictionary should have exactly four entries.
235 If we're trimming, all we need is the actual trust settings. */
238 CFDataRef issuer
= (CFDataRef
)CFDictionaryGetValue(certDict
,
240 require(issuer
&& CFGetTypeID(issuer
) == CFDataGetTypeID(), errOut
);
243 CFDataRef serial
= (CFDataRef
)CFDictionaryGetValue(certDict
,
244 kTrustRecordSerialNumber
);
245 require(serial
&& CFGetTypeID(serial
) == CFDataGetTypeID(), errOut
);
247 /* modification date */
248 CFDateRef modDate
= (CFDateRef
)CFDictionaryGetValue(certDict
,
249 kTrustRecordModDate
);
250 require(modDate
&& CFGetTypeID(modDate
) == CFDateGetTypeID(), errOut
);
252 /* If we are not trimming we copy these extra values as well. */
254 CFDictionaryAddValue(newDict
, kTrustRecordIssuer
, issuer
);
255 CFDictionaryAddValue(newDict
, kTrustRecordSerialNumber
, serial
);
256 CFDictionaryAddValue(newDict
, kTrustRecordModDate
, modDate
);
259 /* The actual trust settings */
260 CFArrayRef trustSettings
= (CFArrayRef
)CFDictionaryGetValue(certDict
,
261 kTrustRecordTrustSettings
);
262 /* optional; this cert's entry is good */
264 require(CFGetTypeID(trustSettings
) == CFArrayGetTypeID(), errOut
);
266 /* Now validate the usageConstraint array contents */
267 require(validateTrustSettingsArray(trustSettings
), errOut
);
269 /* Add the trustSettings to the dict. */
270 CFDictionaryAddValue(newDict
, kTrustRecordTrustSettings
, trustSettings
);
273 CFDictionaryAddValue(tlc
->dict
, newKey
, newDict
);
279 CFReleaseSafe(newKey
);
280 CFReleaseSafe(newDict
);
281 tlc
->status
= errSecInvalidTrustSettings
;
284 static OSStatus
SecTrustSettingsValidate(SecTrustSettingsRef ts
, bool trim
) {
285 /* top level dictionary */
286 require(ts
->_propList
, errOut
);
287 require(CFGetTypeID(ts
->_propList
) == CFDictionaryGetTypeID(), errOut
);
289 /* That dictionary has two entries */
290 CFNumberRef cfVers
= (CFNumberRef
)CFDictionaryGetValue(ts
->_propList
, kTrustRecordVersion
);
291 require(cfVers
!= NULL
&& CFGetTypeID(cfVers
) == CFNumberGetTypeID(), errOut
);
292 require(CFNumberGetValue(cfVers
, kCFNumberSInt32Type
, &ts
->_dictVersion
), errOut
);
293 require((ts
->_dictVersion
<= kSecTrustRecordVersionCurrent
) &&
294 (ts
->_dictVersion
!= kSecTrustRecordVersionInvalid
), errOut
);
296 /* other backwards-compatibility handling done later, if needed, per _dictVersion */
298 require(ts
->_trustDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
299 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
301 CFDictionaryRef trustList
= (CFDictionaryRef
)CFDictionaryGetValue(
302 ts
->_propList
, kTrustRecordTrustList
);
303 require(trustList
!= NULL
&&
304 CFGetTypeID(trustList
) == CFDictionaryGetTypeID(), errOut
);
306 /* Convert the per-cert entries from on disk to in memory format. */
307 struct trustListContext context
= {
308 ts
->_trustDict
, ts
->_dictVersion
, trim
, errSecSuccess
310 CFDictionaryApplyFunction(trustList
, trustListApplierFunction
, &context
);
313 /* we don't need the top-level dictionary any more */
314 CFRelease(ts
->_propList
);
315 ts
->_propList
= NULL
;
318 return context
.status
;
321 return errSecInvalidTrustSettings
;
324 OSStatus
SecTrustSettingsCreateFromExternal(SecTrustSettingsDomain domain
,
325 CFDataRef external
, SecTrustSettingsRef
*ts
) {
326 CFAllocatorRef allocator
= kCFAllocatorDefault
;
327 CFIndex size
= sizeof(struct __SecTrustSettings
);
328 SecTrustSettingsRef result
;
330 require(result
= (SecTrustSettingsRef
)_CFRuntimeCreateInstance(allocator
,
331 SecTrustSettingsGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
333 CFErrorRef error
= NULL
;
334 CFPropertyListRef plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
335 external
, kCFPropertyListImmutable
, NULL
, &error
);
337 secwarning("SecTrustSettingsCreateFromExternal: %@", error
);
338 CFReleaseSafe(error
);
339 CFReleaseSafe(result
);
343 result
->_propList
= plist
;
344 result
->_trustDict
= NULL
;
345 SecTrustSettingsValidate(result
, false);
349 return errSecSuccess
;
352 return errSecInvalidTrustSettings
;
355 SecTrustSettingsRef
SecTrustSettingsCreate(SecTrustSettingsDomain domain
,
356 bool create
, bool trim
) {
357 CFAllocatorRef allocator
= kCFAllocatorDefault
;
358 CFIndex size
= sizeof(struct __SecTrustSettings
);
359 SecTrustSettingsRef result
=
360 (SecTrustSettingsRef
)_CFRuntimeCreateInstance(allocator
,
361 SecTrustSettingsGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
365 //result->_data = NULL;
370 CFDataRef
SecTrustSettingsCopyExternal(SecTrustSettingsRef ts
) {
371 /* Transform the internal represantation of the trustSettings to an
373 // @@@ SecTrustSettingsUpdatePropertyList(SecTrustSettingsRef ts);
375 verify(xmlData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
380 void SecTrustSettingsSet(SecCertificateRef certRef
,
381 CFTypeRef trustSettingsDictOrArray
) {
387 // MARK: SPI functions
391 * Fundamental routine used by TP to ascertain status of one cert.
393 * Returns true in *foundMatchingEntry if a trust setting matching
394 * specific constraints was found for the cert. Returns true in
395 * *foundAnyEntry if any entry was found for the cert, even if it
396 * did not match the specified constraints. The TP uses this to
397 * optimize for the case where a cert is being evaluated for
398 * one type of usage, and then later for another type. If
399 * foundAnyEntry is false, the second evaluation need not occur.
401 * Returns the domain in which a setting was found in *foundDomain.
403 * Allowed errors applying to the specified cert evaluation
404 * are returned in a mallocd array in *allowedErrors and must
405 * be freed by caller.
407 * The design of the entire TrustSettings module is centered around
408 * optimizing the performance of this routine (security concerns
409 * aside, that is). It's why the per-cert dictionaries are stored
410 * as a dictionary, keyed off of the cert hash. It's why TrustSettings
411 * are cached in memory by tsGetGlobalTrustSettings(), and why those
412 * cached TrustSettings objects are 'trimmed' of dictionary fields
413 * which are not needed to verify a cert.
415 * The API functions which are used to manipulate Trust Settings
416 * are called infrequently and need not be particularly fast since
417 * they result in user interaction for authentication. Thus they do
418 * not use cached TrustSettings as this function does.
420 OSStatus
SecTrustSettingsEvaluateCertificate(
421 SecCertificateRef certificate
,
423 SecTrustSettingsKeyUsage keyUsage
, /* optional */
424 bool isSelfSignedCert
, /* for checking default setting */
425 /* RETURNED values */
426 SecTrustSettingsDomain
*foundDomain
,
427 CFArrayRef
*allowedErrors
, /* RETURNED */
428 SecTrustSettingsResult
*resultType
, /* RETURNED */
429 bool *foundMatchingEntry
,/* RETURNED */
430 bool *foundAnyEntry
) /* RETURNED */
435 StLock
<Mutex
> _(sutCacheLock());
437 TS_REQUIRED(certHashStr
)
438 TS_REQUIRED(foundDomain
)
439 TS_REQUIRED(allowedErrors
)
440 TS_REQUIRED(numAllowedErrors
)
441 TS_REQUIRED(resultType
)
442 TS_REQUIRED(foundMatchingEntry
)
443 TS_REQUIRED(foundMatchingEntry
)
445 /* ensure a NULL_terminated string */
446 auto_array
<char> polStr
;
447 if(policyString
!= NULL
) {
448 polStr
.allocate(policyStringLen
+ 1);
449 memmove(polStr
.get(), policyString
, policyStringLen
);
450 if(policyString
[policyStringLen
- 1] != '\0') {
451 (polStr
.get())[policyStringLen
] = '\0';
455 /* initial condition - this can grow if we inspect multiple TrustSettings */
456 *allowedErrors
= NULL
;
457 *numAllowedErrors
= 0;
460 * This loop relies on the ordering of the SecTrustSettingsDomain enum:
461 * search user first, then admin, then system.
463 assert(kSecTrustSettingsDomainAdmin
== (kSecTrustSettingsDomainUser
+ 1));
464 assert(kSecTrustSettingsDomainSystem
== (kSecTrustSettingsDomainAdmin
+ 1));
465 bool foundAny
= false;
466 for(unsigned domain
=kSecTrustSettingsDomainUser
;
467 domain
<=kSecTrustSettingsDomainSystem
;
469 TrustSettings
*ts
= tsGetGlobalTrustSettings(domain
);
474 /* validate cert returns true if matching entry was found */
475 bool foundAnyHere
= false;
476 bool found
= ts
->evaluateCert(certHashStr
, policyOID
,
477 polStr
.get(), keyUsage
, isRootCert
,
478 allowedErrors
, numAllowedErrors
, resultType
, &foundAnyHere
);
482 * Note this, even though we may overwrite it later if this
483 * is an Unspecified entry and we find a definitive entry
486 *foundDomain
= domain
;
488 if(found
&& (*resultType
!= kSecTrustSettingsResultUnspecified
)) {
489 trustSettingsDbg("SecTrustSettingsEvaluateCert: found in domain %d", domain
);
490 *foundAnyEntry
= true;
491 *foundMatchingEntry
= true;
492 return errSecSuccess
;
494 foundAny
|= foundAnyHere
;
496 trustSettingsDbg("SecTrustSettingsEvaluateCert: NOT FOUND");
497 *foundAnyEntry
= foundAny
;
498 *foundMatchingEntry
= false;
499 return errSecSuccess
;
502 return errSecSuccess
;
506 * Add a cert's TrustSettings to a non-persistent TrustSettings record.
507 * No locking or cache flushing here; it's all local to the TrustSettings
511 // There is already a SecTrustSettingsSetTrustSettingsExternal API on OS X
512 // with different arguments, so give this one an _ios extension.
513 OSStatus
SecTrustSettingsSetTrustSettingsExternal_ios(
515 OSStatus
SecTrustSettingsSetTrustSettingsExternal(
517 CFDataRef settingsIn
, /* optional */
518 SecCertificateRef certRef
, /* optional */
519 CFTypeRef trustSettingsDictOrArray
, /* optional */
520 CFDataRef
*settingsOut
) /* RETURNED */
522 SecTrustSettingsRef ts
= NULL
;
525 require_noerr(status
= SecTrustSettingsCreateFromExternal(
526 kSecTrustSettingsDomainMemory
, settingsIn
, &ts
), errOut
);
527 SecTrustSettingsSet(certRef
, trustSettingsDictOrArray
);
528 *settingsOut
= SecTrustSettingsCopyExternal(ts
);