]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/tpCertAllowList.c
da3050abcc3f5500ced4500dc903096523f1a99d
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / tpCertAllowList.c
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 #if !SECTRUST_OSX
50
51 static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
52
53 static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
54 CFStringRef resourceType, CFStringRef subDirName)
55 {
56 CFURLRef rsrcUrl = NULL;
57 CFBundleRef bundle = NULL;
58
59 CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kSecSystemTrustStoreBundlePath, kCFURLPOSIXPathStyle, true);
60 if (bundleUrl) {
61 bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
62 CFRelease(bundleUrl);
63 }
64
65 if (bundle) {
66 rsrcUrl = CFBundleCopyResourceURL(bundle, resourceName, resourceType, subDirName);
67 if (!rsrcUrl) {
68 tpDebug("resource: not found");
69 }
70 CFRelease(bundle);
71 }
72
73 return rsrcUrl;
74 }
75
76 static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
77 CFStringRef resourceType, CFStringRef subDirName)
78 {
79 CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
80 CFDataRef data = NULL;
81 if (url) {
82 SInt32 error;
83 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
84 url, &data, NULL, NULL, &error)) {
85 tpDebug("Allow list read: %ld", (long) error);
86 }
87 CFRelease(url);
88 }
89 return data;
90 }
91
92 static CFDictionaryRef InitializeAllowList()
93 {
94 CFDataRef xmlData = NULL;
95 // Use the file in the system trust store bundle
96 xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("Allowed"), CFSTR("plist"), NULL);
97
98 CFPropertyListRef allowList = NULL;
99 if (xmlData) {
100 allowList = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
101 CFRelease(xmlData);
102 }
103
104 if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
105 return (CFDictionaryRef) allowList;
106 } else {
107 if (allowList)
108 CFRelease(allowList);
109 return NULL;
110 }
111 }
112
113 /* helper functions borrowed from SecCFWrappers */
114 static inline CFComparisonResult CFDataCompare(CFDataRef left, CFDataRef right)
115 {
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;
119
120 int comparison = memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), shortest);
121
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;
126 else
127 return kCFCompareEqualTo;
128 }
129
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]);
135 }
136 }
137
138 static inline CF_RETURNS_RETAINED CFStringRef CFDataCopyHexString(CFDataRef data) {
139 CFMutableStringRef hexString = CFStringCreateMutable(kCFAllocatorDefault, 2 * CFDataGetLength(data));
140 CFStringAppendHexData(hexString, data);
141 return hexString;
142 }
143
144 CSSM_RETURN tpCheckCertificateAllowList(TPCertGroup &certGroup) {
145 CSSM_RETURN result = CSSMERR_TP_NOT_TRUSTED;
146 unsigned numCerts = certGroup.numCerts();
147 int i;
148 CFRange range;
149 CFArrayRef allowedCerts = NULL;
150
151 TPCertInfo *last = certGroup.lastCert();
152 if (!last) {
153 return result;
154 }
155
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) {
159 return result;
160 }
161
162 CSSM_X509_EXTENSION *ake = (CSSM_X509_EXTENSION *)authKeyID->Data;
163 if (!ake || ake->format != CSSM_X509_DATAFORMAT_PARSED) {
164 return result;
165 }
166
167 const CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)ake->value.parsedValue;
168 if (!akid || !akid->keyIdentifierPresent) {
169 return result;
170 }
171
172 CFDataRef akData = CFDataCreate(kCFAllocatorDefault, akid->keyIdentifier.Data, akid->keyIdentifier.Length);
173 CFStringRef akString = CFDataCopyHexString(akData);
174
175 /* search allow list for allowed certs for this distrusted root */
176 CFDictionaryRef allowList = InitializeAllowList();
177 if (NULL == allowList) {
178 goto errout;
179 }
180
181 allowedCerts = (CFArrayRef)CFDictionaryGetValue(allowList, akString);
182 if (!allowedCerts || !CFArrayGetCount(allowedCerts)) {
183 goto errout;
184 }
185
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};
191
192 const CSSM_DATA *certData = cert->itemData();
193 if (!certData || !certData->Data || (certData->Length <= 0)) {
194 goto errout;
195 }
196
197 int err = CCDigest(kCCDigestSHA256, certData->Data, certData->Length, hashBytes);
198 if (err) {
199 goto errout;
200 }
201
202 CFDataRef hashData = CFDataCreate(kCFAllocatorDefault, hashBytes, sizeof(hashBytes));
203
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
209 result = CSSM_OK;
210 }
211 }
212 CFRelease(hashData);
213 }
214
215 errout:
216 if (akString)
217 CFRelease(akString);
218 if (akData)
219 CFRelease(akData);
220 if (allowList)
221 CFRelease(allowList);
222 return result;
223 }
224
225 #else
226
227 /* Legacy code path, only known to be used by IdentityCursorPolicyAndID::next. (rdar://28622060) */
228
229 CSSM_RETURN tpCheckCertificateAllowList(TPCertGroup &certGroup) {
230 return CSSMERR_TP_NOT_TRUSTED;
231 }
232
233 #endif /* !SECTRUST_OSX */
234