]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/tpCertAllowList.c
Security-57337.40.85.tar.gz
[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 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