]>
Commit | Line | Data |
---|---|---|
5c19dc3a A |
1 | /* |
2 | * Copyright (c) 2015 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * tpCertAllowList.c - List of allowed certificates without a trusted root | |
26 | */ | |
27 | ||
28 | ||
29 | #include <stdlib.h> | |
30 | #include <strings.h> | |
31 | ||
32 | #include "certGroupUtils.h" | |
33 | #include "TPCertInfo.h" | |
34 | #include "TPCrlInfo.h" | |
35 | #include "tpPolicies.h" | |
36 | #include "tpdebugging.h" | |
37 | #include "tpCrlVerify.h" | |
38 | ||
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> | |
47 | ||
48 | ||
49 | static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle"); | |
50 | ||
51 | static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName, | |
52 | CFStringRef resourceType, CFStringRef subDirName) | |
53 | { | |
54 | CFURLRef rsrcUrl = NULL; | |
55 | CFBundleRef bundle = NULL; | |
56 | ||
57 | CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kSecSystemTrustStoreBundlePath, kCFURLPOSIXPathStyle, true); | |
58 | if (bundleUrl) { | |
59 | bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl); | |
60 | CFRelease(bundleUrl); | |
61 | } | |
62 | ||
63 | if (bundle) { | |
64 | rsrcUrl = CFBundleCopyResourceURL(bundle, resourceName, resourceType, subDirName); | |
65 | if (!rsrcUrl) { | |
66 | tpDebug("resource: not found"); | |
67 | } | |
68 | CFRelease(bundle); | |
69 | } | |
70 | ||
71 | return rsrcUrl; | |
72 | } | |
73 | ||
74 | static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName, | |
75 | CFStringRef resourceType, CFStringRef subDirName) | |
76 | { | |
77 | CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName); | |
78 | CFDataRef data = NULL; | |
79 | if (url) { | |
80 | SInt32 error; | |
81 | if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, | |
82 | url, &data, NULL, NULL, &error)) { | |
83 | tpDebug("Allow list read: %ld", (long) error); | |
84 | } | |
85 | CFRelease(url); | |
86 | } | |
87 | return data; | |
88 | } | |
89 | ||
90 | static CFDictionaryRef InitializeAllowList() | |
91 | { | |
92 | CFDataRef xmlData = NULL; | |
93 | // Use the file in the system trust store bundle | |
94 | xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("Allowed"), CFSTR("plist"), NULL); | |
95 | ||
96 | CFPropertyListRef allowList = NULL; | |
97 | if (xmlData) { | |
98 | allowList = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL); | |
99 | CFRelease(xmlData); | |
100 | } | |
101 | ||
102 | if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) { | |
103 | return (CFDictionaryRef) allowList; | |
104 | } else { | |
105 | if (allowList) | |
106 | CFRelease(allowList); | |
107 | return NULL; | |
108 | } | |
109 | } | |
110 | ||
111 | /* helper functions borrowed from SecCFWrappers */ | |
112 | static inline CFComparisonResult CFDataCompare(CFDataRef left, CFDataRef right) | |
113 | { | |
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; | |
117 | ||
118 | int comparison = memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), shortest); | |
119 | ||
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; | |
124 | else | |
125 | return kCFCompareEqualTo; | |
126 | } | |
127 | ||
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]); | |
133 | } | |
134 | } | |
135 | ||
136 | static inline CF_RETURNS_RETAINED CFStringRef CFDataCopyHexString(CFDataRef data) { | |
137 | CFMutableStringRef hexString = CFStringCreateMutable(kCFAllocatorDefault, 2 * CFDataGetLength(data)); | |
138 | CFStringAppendHexData(hexString, data); | |
139 | return hexString; | |
140 | } | |
141 | ||
142 | CSSM_RETURN tpCheckCertificateAllowList(TPCertGroup &certGroup) { | |
143 | CSSM_RETURN result = CSSMERR_TP_NOT_TRUSTED; | |
144 | unsigned numCerts = certGroup.numCerts(); | |
145 | int i; | |
146 | CFRange range; | |
147 | CFArrayRef allowedCerts = NULL; | |
148 | ||
149 | TPCertInfo *last = certGroup.lastCert(); | |
150 | if (!last) { | |
151 | return result; | |
152 | } | |
153 | ||
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) { | |
157 | return result; | |
158 | } | |
159 | ||
160 | CSSM_X509_EXTENSION *ake = (CSSM_X509_EXTENSION *)authKeyID->Data; | |
161 | if (!ake || ake->format != CSSM_X509_DATAFORMAT_PARSED) { | |
162 | return result; | |
163 | } | |
164 | ||
165 | const CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)ake->value.parsedValue; | |
166 | if (!akid || !akid->keyIdentifierPresent) { | |
167 | return result; | |
168 | } | |
169 | ||
170 | CFDataRef akData = CFDataCreate(kCFAllocatorDefault, akid->keyIdentifier.Data, akid->keyIdentifier.Length); | |
171 | CFStringRef akString = CFDataCopyHexString(akData); | |
172 | ||
173 | /* search allow list for allowed certs for this distrusted root */ | |
174 | CFDictionaryRef allowList = InitializeAllowList(); | |
175 | if (NULL == allowList) { | |
176 | goto errout; | |
177 | } | |
178 | ||
179 | allowedCerts = (CFArrayRef)CFDictionaryGetValue(allowList, akString); | |
180 | if (!allowedCerts || !CFArrayGetCount(allowedCerts)) { | |
181 | goto errout; | |
182 | } | |
183 | ||
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}; | |
189 | ||
190 | const CSSM_DATA *certData = cert->itemData(); | |
191 | if (!certData || !certData->Data || (certData->Length <= 0)) { | |
192 | goto errout; | |
193 | } | |
194 | ||
195 | int err = CCDigest(kCCDigestSHA256, certData->Data, certData->Length, hashBytes); | |
196 | if (err) { | |
197 | goto errout; | |
198 | } | |
199 | ||
200 | CFDataRef hashData = CFDataCreate(kCFAllocatorDefault, hashBytes, sizeof(hashBytes)); | |
201 | ||
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 | |
207 | result = CSSM_OK; | |
208 | } | |
209 | } | |
210 | CFRelease(hashData); | |
211 | } | |
212 | ||
213 | errout: | |
214 | if (akString) | |
215 | CFRelease(akString); | |
216 | if (akData) | |
217 | CFRelease(akData); | |
218 | if (allowList) | |
219 | CFRelease(allowList); | |
220 | return result; | |
221 | } | |
222 |