2 * Copyright (c) 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 * tpCertAllowList.c - List of allowed certificates without a trusted root
32 #include "certGroupUtils.h"
33 #include "TPCertInfo.h"
34 #include "TPCrlInfo.h"
35 #include "tpPolicies.h"
36 #include "tpdebugging.h"
37 #include "tpCrlVerify.h"
39 #include <Security/oidsalg.h>
40 #include <Security/cssmapple.h>
41 #include "tpCertAllowList.h"
42 #include <Security/SecBase.h>
43 #include <CommonCrypto/CommonDigest.h>
44 #include <CommonCrypto/CommonDigestSPI.h>
45 #include <CoreFoundation/CFError.h>
46 #include <CoreFoundation/CFDictionary.h>
51 static CFStringRef kSecSystemTrustStoreBundlePath
= CFSTR("/System/Library/Security/Certificates.bundle");
53 static CFURLRef
SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName
,
54 CFStringRef resourceType
, CFStringRef subDirName
)
56 CFURLRef rsrcUrl
= NULL
;
57 CFBundleRef bundle
= NULL
;
59 CFURLRef bundleUrl
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, kSecSystemTrustStoreBundlePath
, kCFURLPOSIXPathStyle
, true);
61 bundle
= CFBundleCreate(kCFAllocatorDefault
, bundleUrl
);
66 rsrcUrl
= CFBundleCopyResourceURL(bundle
, resourceName
, resourceType
, subDirName
);
68 tpDebug("resource: not found");
76 static CFDataRef
SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName
,
77 CFStringRef resourceType
, CFStringRef subDirName
)
79 CFURLRef url
= SecSystemTrustStoreCopyResourceURL(resourceName
, resourceType
, subDirName
);
80 CFDataRef data
= NULL
;
83 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
,
84 url
, &data
, NULL
, NULL
, &error
)) {
85 tpDebug("Allow list read: %ld", (long) error
);
92 static CFDictionaryRef
InitializeAllowList()
94 CFDataRef xmlData
= NULL
;
95 // Use the file in the system trust store bundle
96 xmlData
= SecSystemTrustStoreCopyResourceContents(CFSTR("Allowed"), CFSTR("plist"), NULL
);
98 CFPropertyListRef allowList
= NULL
;
100 allowList
= CFPropertyListCreateWithData(kCFAllocatorDefault
, xmlData
, kCFPropertyListImmutable
, NULL
, NULL
);
104 if (allowList
&& (CFGetTypeID(allowList
) == CFDictionaryGetTypeID())) {
105 return (CFDictionaryRef
) allowList
;
108 CFRelease(allowList
);
113 /* helper functions borrowed from SecCFWrappers */
114 static inline CFComparisonResult
CFDataCompare(CFDataRef left
, CFDataRef right
)
116 const size_t left_size
= CFDataGetLength(left
);
117 const size_t right_size
= CFDataGetLength(right
);
118 const size_t shortest
= (left_size
<= right_size
) ? left_size
: right_size
;
120 int comparison
= memcmp(CFDataGetBytePtr(left
), CFDataGetBytePtr(right
), shortest
);
122 if (comparison
> 0 || (comparison
== 0 && left_size
> right_size
))
123 return kCFCompareGreaterThan
;
124 else if (comparison
< 0 || (comparison
== 0 && left_size
< right_size
))
125 return kCFCompareLessThan
;
127 return kCFCompareEqualTo
;
130 static inline void CFStringAppendHexData(CFMutableStringRef s
, CFDataRef data
) {
131 const uint8_t *bytes
= CFDataGetBytePtr(data
);
132 CFIndex len
= CFDataGetLength(data
);
133 for (CFIndex ix
= 0; ix
< len
; ++ix
) {
134 CFStringAppendFormat(s
, 0, CFSTR("%02X"), bytes
[ix
]);
138 static inline CF_RETURNS_RETAINED CFStringRef
CFDataCopyHexString(CFDataRef data
) {
139 CFMutableStringRef hexString
= CFStringCreateMutable(kCFAllocatorDefault
, 2 * CFDataGetLength(data
));
140 CFStringAppendHexData(hexString
, data
);
144 CSSM_RETURN
tpCheckCertificateAllowList(TPCertGroup
&certGroup
) {
145 CSSM_RETURN result
= CSSMERR_TP_NOT_TRUSTED
;
146 unsigned numCerts
= certGroup
.numCerts();
149 CFArrayRef allowedCerts
= NULL
;
151 TPCertInfo
*last
= certGroup
.lastCert();
156 /* parse authority key ID from certificate that would have been signed by a distrusted root */
157 const CSSM_DATA
*authKeyID
= last
->authorityKeyID();
158 if (!authKeyID
|| !authKeyID
->Data
) {
162 CSSM_X509_EXTENSION
*ake
= (CSSM_X509_EXTENSION
*)authKeyID
->Data
;
163 if (!ake
|| ake
->format
!= CSSM_X509_DATAFORMAT_PARSED
) {
167 const CE_AuthorityKeyID
*akid
= (CE_AuthorityKeyID
*)ake
->value
.parsedValue
;
168 if (!akid
|| !akid
->keyIdentifierPresent
) {
172 CFDataRef akData
= CFDataCreate(kCFAllocatorDefault
, akid
->keyIdentifier
.Data
, akid
->keyIdentifier
.Length
);
173 CFStringRef akString
= CFDataCopyHexString(akData
);
175 /* search allow list for allowed certs for this distrusted root */
176 CFDictionaryRef allowList
= InitializeAllowList();
177 if (NULL
== allowList
) {
181 allowedCerts
= (CFArrayRef
)CFDictionaryGetValue(allowList
, akString
);
182 if (!allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
186 /* found some allowed certificates: check whether a certificate in this chain is present */
187 range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
188 for (i
= 0; i
< numCerts
; i
++) {
189 TPCertInfo
*cert
= certGroup
.certAtIndex(i
);
190 UInt8 hashBytes
[CC_SHA256_DIGEST_LENGTH
] = {0};
192 const CSSM_DATA
*certData
= cert
->itemData();
193 if (!certData
|| !certData
->Data
|| (certData
->Length
<= 0)) {
197 int err
= CCDigest(kCCDigestSHA256
, certData
->Data
, certData
->Length
, hashBytes
);
202 CFDataRef hashData
= CFDataCreate(kCFAllocatorDefault
, hashBytes
, sizeof(hashBytes
));
204 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, hashData
, (CFComparatorFunction
)CFDataCompare
, NULL
);
205 if (position
< CFArrayGetCount(allowedCerts
)) {
206 CFDataRef possibleMatch
= (CFDataRef
) CFArrayGetValueAtIndex(allowedCerts
, position
);
207 if (!CFDataCompare(hashData
, possibleMatch
)) {
208 //this cert is in the allowlist
221 CFRelease(allowList
);
227 /* Legacy code path, only known to be used by IdentityCursorPolicyAndID::next. (rdar://28622060) */
229 CSSM_RETURN
tpCheckCertificateAllowList(TPCertGroup
&certGroup
) {
230 return CSSMERR_TP_NOT_TRUSTED
;
233 #endif /* !SECTRUST_OSX */