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>
49 static CFStringRef kSecSystemTrustStoreBundlePath
= CFSTR("/System/Library/Security/Certificates.bundle");
51 static CFURLRef
SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName
,
52 CFStringRef resourceType
, CFStringRef subDirName
)
54 CFURLRef rsrcUrl
= NULL
;
55 CFBundleRef bundle
= NULL
;
57 CFURLRef bundleUrl
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, kSecSystemTrustStoreBundlePath
, kCFURLPOSIXPathStyle
, true);
59 bundle
= CFBundleCreate(kCFAllocatorDefault
, bundleUrl
);
64 rsrcUrl
= CFBundleCopyResourceURL(bundle
, resourceName
, resourceType
, subDirName
);
66 tpDebug("resource: not found");
74 static CFDataRef
SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName
,
75 CFStringRef resourceType
, CFStringRef subDirName
)
77 CFURLRef url
= SecSystemTrustStoreCopyResourceURL(resourceName
, resourceType
, subDirName
);
78 CFDataRef data
= NULL
;
81 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
,
82 url
, &data
, NULL
, NULL
, &error
)) {
83 tpDebug("Allow list read: %ld", (long) error
);
90 static CFDictionaryRef
InitializeAllowList()
92 CFDataRef xmlData
= NULL
;
93 // Use the file in the system trust store bundle
94 xmlData
= SecSystemTrustStoreCopyResourceContents(CFSTR("Allowed"), CFSTR("plist"), NULL
);
96 CFPropertyListRef allowList
= NULL
;
98 allowList
= CFPropertyListCreateWithData(kCFAllocatorDefault
, xmlData
, kCFPropertyListImmutable
, NULL
, NULL
);
102 if (allowList
&& (CFGetTypeID(allowList
) == CFDictionaryGetTypeID())) {
103 return (CFDictionaryRef
) allowList
;
106 CFRelease(allowList
);
111 /* helper functions borrowed from SecCFWrappers */
112 static inline CFComparisonResult
CFDataCompare(CFDataRef left
, CFDataRef right
)
114 const size_t left_size
= CFDataGetLength(left
);
115 const size_t right_size
= CFDataGetLength(right
);
116 const size_t shortest
= (left_size
<= right_size
) ? left_size
: right_size
;
118 int comparison
= memcmp(CFDataGetBytePtr(left
), CFDataGetBytePtr(right
), shortest
);
120 if (comparison
> 0 || (comparison
== 0 && left_size
> right_size
))
121 return kCFCompareGreaterThan
;
122 else if (comparison
< 0 || (comparison
== 0 && left_size
< right_size
))
123 return kCFCompareLessThan
;
125 return kCFCompareEqualTo
;
128 static inline void CFStringAppendHexData(CFMutableStringRef s
, CFDataRef data
) {
129 const uint8_t *bytes
= CFDataGetBytePtr(data
);
130 CFIndex len
= CFDataGetLength(data
);
131 for (CFIndex ix
= 0; ix
< len
; ++ix
) {
132 CFStringAppendFormat(s
, 0, CFSTR("%02X"), bytes
[ix
]);
136 static inline CF_RETURNS_RETAINED CFStringRef
CFDataCopyHexString(CFDataRef data
) {
137 CFMutableStringRef hexString
= CFStringCreateMutable(kCFAllocatorDefault
, 2 * CFDataGetLength(data
));
138 CFStringAppendHexData(hexString
, data
);
142 CSSM_RETURN
tpCheckCertificateAllowList(TPCertGroup
&certGroup
) {
143 CSSM_RETURN result
= CSSMERR_TP_NOT_TRUSTED
;
144 unsigned numCerts
= certGroup
.numCerts();
147 CFArrayRef allowedCerts
= NULL
;
149 TPCertInfo
*last
= certGroup
.lastCert();
154 /* parse authority key ID from certificate that would have been signed by a distrusted root */
155 const CSSM_DATA
*authKeyID
= last
->authorityKeyID();
156 if (!authKeyID
|| !authKeyID
->Data
) {
160 CSSM_X509_EXTENSION
*ake
= (CSSM_X509_EXTENSION
*)authKeyID
->Data
;
161 if (!ake
|| ake
->format
!= CSSM_X509_DATAFORMAT_PARSED
) {
165 const CE_AuthorityKeyID
*akid
= (CE_AuthorityKeyID
*)ake
->value
.parsedValue
;
166 if (!akid
|| !akid
->keyIdentifierPresent
) {
170 CFDataRef akData
= CFDataCreate(kCFAllocatorDefault
, akid
->keyIdentifier
.Data
, akid
->keyIdentifier
.Length
);
171 CFStringRef akString
= CFDataCopyHexString(akData
);
173 /* search allow list for allowed certs for this distrusted root */
174 CFDictionaryRef allowList
= InitializeAllowList();
175 if (NULL
== allowList
) {
179 allowedCerts
= (CFArrayRef
)CFDictionaryGetValue(allowList
, akString
);
180 if (!allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
184 /* found some allowed certificates: check whether a certificate in this chain is present */
185 range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
186 for (i
= 0; i
< numCerts
; i
++) {
187 TPCertInfo
*cert
= certGroup
.certAtIndex(i
);
188 UInt8 hashBytes
[CC_SHA256_DIGEST_LENGTH
] = {0};
190 const CSSM_DATA
*certData
= cert
->itemData();
191 if (!certData
|| !certData
->Data
|| (certData
->Length
<= 0)) {
195 int err
= CCDigest(kCCDigestSHA256
, certData
->Data
, certData
->Length
, hashBytes
);
200 CFDataRef hashData
= CFDataCreate(kCFAllocatorDefault
, hashBytes
, sizeof(hashBytes
));
202 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, hashData
, (CFComparatorFunction
)CFDataCompare
, NULL
);
203 if (position
< CFArrayGetCount(allowedCerts
)) {
204 CFDataRef possibleMatch
= (CFDataRef
) CFArrayGetValueAtIndex(allowedCerts
, position
);
205 if (!CFDataCompare(hashData
, possibleMatch
)) {
206 //this cert is in the allowlist
219 CFRelease(allowList
);