]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
427c49bc A |
2 | * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved. |
3 | * | |
b1ab9ed8 | 4 | * @APPLE_LICENSE_HEADER_START@ |
427c49bc | 5 | * |
b1ab9ed8 A |
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. | |
427c49bc | 12 | * |
b1ab9ed8 A |
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. | |
427c49bc | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | * | |
23 | * SecTrust.c - CoreFoundation based certificate trust evaluator | |
24 | * | |
25 | * Created by Michael Brouwer on 10/17/06. | |
26 | */ | |
27 | ||
28 | #include <Security/SecTrustPriv.h> | |
427c49bc | 29 | #include <Security/SecItemPriv.h> |
b1ab9ed8 A |
30 | #include <Security/SecCertificateInternal.h> |
31 | #include <Security/SecCertificatePath.h> | |
32 | #include <Security/SecFramework.h> | |
33 | #include <Security/SecPolicyInternal.h> | |
427c49bc | 34 | #include <utilities/SecIOFormat.h> |
b1ab9ed8 A |
35 | #include <CoreFoundation/CFRuntime.h> |
36 | #include <CoreFoundation/CFSet.h> | |
37 | #include <CoreFoundation/CFString.h> | |
38 | #include <CoreFoundation/CFNumber.h> | |
39 | #include <CoreFoundation/CFArray.h> | |
40 | #include <CoreFoundation/CFPropertyList.h> | |
41 | #include <AssertMacros.h> | |
42 | #include <stdbool.h> | |
43 | #include <string.h> | |
44 | #include <stdlib.h> | |
45 | #include <pthread.h> | |
427c49bc A |
46 | #include <Security/SecBasePriv.h> |
47 | #include <utilities/SecCFError.h> | |
48 | #include <utilities/SecCFWrappers.h> | |
4d3cab3d | 49 | #include <utilities/SecCertificateTrace.h> |
427c49bc | 50 | |
b1ab9ed8 A |
51 | #include "SecRSAKey.h" |
52 | #include <libDER/oids.h> | |
427c49bc | 53 | #include <utilities/debugging.h> |
b1ab9ed8 A |
54 | #include <Security/SecInternal.h> |
55 | #include <ipc/securityd_client.h> | |
427c49bc A |
56 | #include <SecuritydXPC.h> |
57 | #include <securityd/SecTrustServer.h> | |
58 | ||
59 | #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v)); | |
b1ab9ed8 | 60 | |
427c49bc A |
61 | SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey, "ExtendedValidation"); |
62 | SEC_CONST_DECL (kSecTrustInfoCompanyNameKey, "CompanyName"); | |
63 | SEC_CONST_DECL (kSecTrustInfoRevocationKey, "Revocation"); | |
64 | SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey, "RevocationValidUntil"); | |
65 | ||
66 | /* Public trust result constants */ | |
67 | SEC_CONST_DECL (kSecTrustEvaluationDate, "TrustEvaluationDate"); | |
68 | SEC_CONST_DECL (kSecTrustExtendedValidation, "TrustExtendedValidation"); | |
69 | SEC_CONST_DECL (kSecTrustOrganizationName, "Organization"); | |
70 | SEC_CONST_DECL (kSecTrustResultValue, "TrustResultValue"); | |
71 | SEC_CONST_DECL (kSecTrustRevocationChecked, "TrustRevocationChecked"); | |
72 | SEC_CONST_DECL (kSecTrustRevocationValidUntilDate, "TrustExpirationDate"); | |
73 | SEC_CONST_DECL (kSecTrustResultDetails, "TrustResultDetails"); | |
b1ab9ed8 A |
74 | |
75 | #pragma mark - | |
76 | #pragma mark SecTrust | |
427c49bc | 77 | |
b1ab9ed8 A |
78 | /******************************************************** |
79 | ****************** SecTrust object ********************* | |
80 | ********************************************************/ | |
81 | struct __SecTrust { | |
427c49bc A |
82 | CFRuntimeBase _base; |
83 | CFArrayRef _certificates; | |
84 | CFArrayRef _anchors; | |
85 | CFTypeRef _policies; | |
86 | CFArrayRef _responses; | |
b1ab9ed8 A |
87 | CFDateRef _verifyDate; |
88 | SecCertificatePathRef _chain; | |
89 | SecKeyRef _publicKey; | |
90 | CFArrayRef _details; | |
91 | CFDictionaryRef _info; | |
92 | CFArrayRef _exceptions; | |
93 | ||
427c49bc A |
94 | /* Note that a value of kSecTrustResultInvalid (0) |
95 | * indicates the trust must be (re)evaluated; any | |
96 | * functions which modify trust parameters in a way | |
97 | * that would invalidate the current result must set | |
98 | * this value back to kSecTrustResultInvalid. | |
99 | */ | |
100 | SecTrustResultType _trustResult; | |
101 | ||
b1ab9ed8 A |
102 | /* If true we don't trust any anchors other than the ones in _anchors. */ |
103 | bool _anchorsOnly; | |
427c49bc A |
104 | |
105 | /* Master switch to permit or disable network use in policy evaluation */ | |
106 | SecNetworkPolicy _networkPolicy; | |
b1ab9ed8 A |
107 | }; |
108 | ||
427c49bc | 109 | /* CFRuntime registration data. */ |
b1ab9ed8 A |
110 | static pthread_once_t kSecTrustRegisterClass = PTHREAD_ONCE_INIT; |
111 | static CFTypeID kSecTrustTypeID = _kCFRuntimeNotATypeID; | |
112 | ||
427c49bc | 113 | /* Forward declarations of static functions. */ |
b1ab9ed8 A |
114 | static CFStringRef SecTrustDescribe(CFTypeRef cf); |
115 | static void SecTrustDestroy(CFTypeRef cf); | |
427c49bc | 116 | static OSStatus SecTrustEvaluateIfNecessary(SecTrustRef trust); |
b1ab9ed8 A |
117 | |
118 | /* Static functions. */ | |
427c49bc A |
119 | static CF_RETURNS_RETAINED CFStringRef SecTrustDescribe(CFTypeRef cf) { |
120 | SecTrustRef trust = (SecTrustRef)cf; | |
b1ab9ed8 | 121 | return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, |
427c49bc | 122 | CFSTR("<SecTrustRef: %p>"), trust); |
b1ab9ed8 A |
123 | } |
124 | ||
125 | static void SecTrustDestroy(CFTypeRef cf) { | |
126 | SecTrustRef trust = (SecTrustRef)cf; | |
127 | CFReleaseSafe(trust->_certificates); | |
128 | CFReleaseSafe(trust->_policies); | |
427c49bc | 129 | CFReleaseSafe(trust->_responses); |
b1ab9ed8 A |
130 | CFReleaseSafe(trust->_verifyDate); |
131 | CFReleaseSafe(trust->_anchors); | |
132 | CFReleaseSafe(trust->_chain); | |
133 | CFReleaseSafe(trust->_publicKey); | |
134 | CFReleaseSafe(trust->_details); | |
135 | CFReleaseSafe(trust->_info); | |
136 | CFReleaseSafe(trust->_exceptions); | |
137 | } | |
138 | ||
139 | static void SecTrustRegisterClass(void) { | |
140 | static const CFRuntimeClass kSecTrustClass = { | |
141 | 0, /* version */ | |
142 | "SecTrust", /* class name */ | |
143 | NULL, /* init */ | |
144 | NULL, /* copy */ | |
145 | SecTrustDestroy, /* dealloc */ | |
146 | NULL, /* equal */ | |
147 | NULL, /* hash */ | |
148 | NULL, /* copyFormattingDesc */ | |
149 | SecTrustDescribe /* copyDebugDesc */ | |
150 | }; | |
151 | ||
152 | kSecTrustTypeID = _CFRuntimeRegisterClass(&kSecTrustClass); | |
153 | } | |
154 | ||
155 | /* Public API functions. */ | |
156 | CFTypeID SecTrustGetTypeID(void) { | |
157 | pthread_once(&kSecTrustRegisterClass, SecTrustRegisterClass); | |
158 | return kSecTrustTypeID; | |
159 | } | |
160 | ||
161 | OSStatus SecTrustCreateWithCertificates(CFTypeRef certificates, | |
427c49bc | 162 | CFTypeRef policies, SecTrustRef *trust) { |
b1ab9ed8 A |
163 | OSStatus status = errSecParam; |
164 | CFAllocatorRef allocator = kCFAllocatorDefault; | |
165 | CFArrayRef l_certs = NULL, l_policies = NULL; | |
166 | SecTrustRef result = NULL; | |
167 | ||
168 | check(certificates); | |
427c49bc | 169 | check(trust); |
b1ab9ed8 A |
170 | CFTypeID certType = CFGetTypeID(certificates); |
171 | if (certType == CFArrayGetTypeID()) { | |
172 | /* We need at least 1 certificate. */ | |
173 | require_quiet(CFArrayGetCount(certificates) > 0, errOut); | |
174 | l_certs = CFArrayCreateCopy(allocator, certificates); | |
175 | } else if (certType == SecCertificateGetTypeID()) { | |
176 | l_certs = CFArrayCreate(allocator, &certificates, 1, | |
177 | &kCFTypeArrayCallBacks); | |
178 | } else { | |
179 | goto errOut; | |
180 | } | |
181 | if (!l_certs) { | |
182 | status = errSecAllocate; | |
183 | goto errOut; | |
184 | } | |
185 | ||
186 | if (!policies) { | |
187 | CFTypeRef policy = SecPolicyCreateBasicX509(); | |
188 | l_policies = CFArrayCreate(allocator, &policy, 1, | |
189 | &kCFTypeArrayCallBacks); | |
190 | CFRelease(policy); | |
191 | } else if (CFGetTypeID(policies) == CFArrayGetTypeID()) { | |
192 | l_policies = CFArrayCreateCopy(allocator, policies); | |
193 | } else if (CFGetTypeID(policies) == SecPolicyGetTypeID()) { | |
194 | l_policies = CFArrayCreate(allocator, &policies, 1, | |
195 | &kCFTypeArrayCallBacks); | |
196 | } else { | |
197 | goto errOut; | |
198 | } | |
199 | if (!l_policies) { | |
200 | status = errSecAllocate; | |
201 | goto errOut; | |
202 | } | |
203 | ||
204 | CFIndex size = sizeof(struct __SecTrust); | |
205 | require_quiet(result = (SecTrustRef)_CFRuntimeCreateInstance(allocator, | |
206 | SecTrustGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut); | |
207 | memset((char*)result + sizeof(result->_base), 0, | |
208 | sizeof(*result) - sizeof(result->_base)); | |
427c49bc | 209 | status = errSecSuccess; |
b1ab9ed8 A |
210 | |
211 | errOut: | |
212 | if (status) { | |
213 | CFReleaseSafe(result); | |
214 | CFReleaseSafe(l_certs); | |
215 | CFReleaseSafe(l_policies); | |
216 | } else { | |
217 | result->_certificates = l_certs; | |
218 | result->_policies = l_policies; | |
427c49bc | 219 | *trust = result; |
b1ab9ed8 A |
220 | } |
221 | return status; | |
222 | } | |
223 | ||
427c49bc A |
224 | static void SetTrustSetNeedsEvaluation(SecTrustRef trust) { |
225 | check(trust); | |
226 | if (trust) { | |
227 | trust->_trustResult = kSecTrustResultInvalid; | |
228 | } | |
229 | } | |
230 | ||
b1ab9ed8 A |
231 | OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust, |
232 | Boolean anchorCertificatesOnly) { | |
427c49bc A |
233 | if (!trust) { |
234 | return errSecParam; | |
235 | } | |
236 | SetTrustSetNeedsEvaluation(trust); | |
b1ab9ed8 A |
237 | trust->_anchorsOnly = anchorCertificatesOnly; |
238 | ||
427c49bc | 239 | return errSecSuccess; |
b1ab9ed8 A |
240 | } |
241 | ||
242 | OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust, | |
243 | CFArrayRef anchorCertificates) { | |
427c49bc A |
244 | if (!trust) { |
245 | return errSecParam; | |
246 | } | |
247 | SetTrustSetNeedsEvaluation(trust); | |
248 | if (anchorCertificates) | |
249 | CFRetain(anchorCertificates); | |
b1ab9ed8 A |
250 | if (trust->_anchors) |
251 | CFRelease(trust->_anchors); | |
252 | trust->_anchors = anchorCertificates; | |
427c49bc A |
253 | trust->_anchorsOnly = (anchorCertificates != NULL); |
254 | ||
255 | return errSecSuccess; | |
256 | } | |
257 | ||
258 | OSStatus SecTrustCopyCustomAnchorCertificates(SecTrustRef trust, | |
259 | CFArrayRef *anchors) { | |
260 | if (!trust|| !anchors) { | |
261 | return errSecParam; | |
262 | } | |
263 | CFArrayRef anchorsArray = NULL; | |
264 | if (trust->_anchors) { | |
265 | anchorsArray = CFArrayCreateCopy(kCFAllocatorDefault, trust->_anchors); | |
266 | if (!anchorsArray) { | |
267 | return errSecAllocate; | |
268 | } | |
269 | } | |
270 | *anchors = anchorsArray; | |
271 | return errSecSuccess; | |
272 | } | |
273 | ||
274 | OSStatus SecTrustSetOCSPResponse(SecTrustRef trust, CFTypeRef responseData) { | |
275 | if (!trust) { | |
276 | return errSecParam; | |
277 | } | |
278 | SetTrustSetNeedsEvaluation(trust); | |
279 | CFArrayRef responseArray = NULL; | |
280 | if (responseData) { | |
281 | if (CFGetTypeID(responseData) == CFArrayGetTypeID()) { | |
282 | responseArray = CFArrayCreateCopy(kCFAllocatorDefault, responseData); | |
283 | } else if (CFGetTypeID(responseData) == CFDataGetTypeID()) { | |
284 | responseArray = CFArrayCreate(kCFAllocatorDefault, &responseData, 1, | |
285 | &kCFTypeArrayCallBacks); | |
286 | } else { | |
287 | return errSecParam; | |
288 | } | |
289 | } | |
290 | if (trust->_responses) | |
291 | CFRelease(trust->_responses); | |
292 | trust->_responses = responseArray; | |
b1ab9ed8 | 293 | |
427c49bc | 294 | return errSecSuccess; |
b1ab9ed8 A |
295 | } |
296 | ||
297 | OSStatus SecTrustSetVerifyDate(SecTrustRef trust, CFDateRef verifyDate) { | |
427c49bc A |
298 | if (!trust) { |
299 | return errSecParam; | |
300 | } | |
301 | SetTrustSetNeedsEvaluation(trust); | |
b1ab9ed8 | 302 | check(verifyDate); |
427c49bc | 303 | CFRetainSafe(verifyDate); |
b1ab9ed8 A |
304 | if (trust->_verifyDate) |
305 | CFRelease(trust->_verifyDate); | |
306 | trust->_verifyDate = verifyDate; | |
307 | ||
427c49bc A |
308 | return errSecSuccess; |
309 | } | |
310 | ||
311 | OSStatus SecTrustSetPolicies(SecTrustRef trust, CFTypeRef newPolicies) { | |
312 | if (!trust || !newPolicies) { | |
313 | return errSecParam; | |
314 | } | |
315 | SetTrustSetNeedsEvaluation(trust); | |
316 | check(newPolicies); | |
317 | ||
318 | CFArrayRef policyArray = NULL; | |
319 | if (CFGetTypeID(newPolicies) == CFArrayGetTypeID()) { | |
320 | policyArray = CFArrayCreateCopy(kCFAllocatorDefault, newPolicies); | |
321 | } else if (CFGetTypeID(newPolicies) == SecPolicyGetTypeID()) { | |
322 | policyArray = CFArrayCreate(kCFAllocatorDefault, &newPolicies, 1, | |
323 | &kCFTypeArrayCallBacks); | |
324 | } else { | |
325 | return errSecParam; | |
326 | } | |
327 | ||
328 | if (trust->_policies) | |
329 | CFRelease(trust->_policies); | |
330 | trust->_policies = policyArray; | |
331 | ||
332 | return errSecSuccess; | |
333 | } | |
334 | ||
335 | OSStatus SecTrustCopyPolicies(SecTrustRef trust, CFArrayRef *policies) { | |
336 | if (!trust|| !policies) { | |
337 | return errSecParam; | |
338 | } | |
339 | if (!trust->_policies) { | |
340 | return errSecInternal; | |
341 | } | |
342 | CFArrayRef policyArray = CFArrayCreateCopy(kCFAllocatorDefault, trust->_policies); | |
343 | if (!policyArray) { | |
344 | return errSecAllocate; | |
345 | } | |
346 | *policies = policyArray; | |
347 | return errSecSuccess; | |
348 | } | |
349 | ||
350 | OSStatus SecTrustSetNetworkFetchAllowed(SecTrustRef trust, Boolean allowFetch) { | |
351 | if (!trust) { | |
352 | return errSecParam; | |
353 | } | |
354 | trust->_networkPolicy = (allowFetch) ? useNetworkEnabled : useNetworkDisabled; | |
355 | return errSecSuccess; | |
356 | } | |
357 | ||
358 | OSStatus SecTrustGetNetworkFetchAllowed(SecTrustRef trust, Boolean *allowFetch) { | |
359 | if (!trust || !allowFetch) { | |
360 | return errSecParam; | |
361 | } | |
362 | Boolean allowed = false; | |
363 | SecNetworkPolicy netPolicy = trust->_networkPolicy; | |
364 | if (netPolicy == useNetworkDefault) { | |
365 | // network fetch is enabled by default for SSL only | |
366 | CFIndex idx, count = (trust->_policies) ? CFArrayGetCount(trust->_policies) : 0; | |
367 | for (idx=0; idx<count; idx++) { | |
368 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(trust->_policies, idx); | |
369 | if (policy) { | |
370 | CFDictionaryRef props = SecPolicyCopyProperties(policy); | |
371 | if (props) { | |
372 | CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(props, kSecPolicyOid); | |
373 | if (value) { | |
374 | if (CFEqual(value, kSecPolicyAppleSSL)) { | |
375 | allowed = true; | |
376 | } | |
377 | } | |
378 | CFRelease(props); | |
379 | } | |
380 | } | |
381 | } | |
382 | } else { | |
383 | // caller has explicitly set the network policy | |
384 | allowed = (netPolicy == useNetworkEnabled); | |
385 | } | |
386 | *allowFetch = allowed; | |
387 | return errSecSuccess; | |
b1ab9ed8 A |
388 | } |
389 | ||
390 | CFAbsoluteTime SecTrustGetVerifyTime(SecTrustRef trust) { | |
391 | CFAbsoluteTime verifyTime; | |
427c49bc | 392 | if (trust && trust->_verifyDate) { |
b1ab9ed8 A |
393 | verifyTime = CFDateGetAbsoluteTime(trust->_verifyDate); |
394 | } else { | |
395 | verifyTime = CFAbsoluteTimeGetCurrent(); | |
396 | /* Record the verifyDate we ended up using. */ | |
427c49bc A |
397 | if (trust) { |
398 | trust->_verifyDate = CFDateCreate(CFGetAllocator(trust), verifyTime); | |
399 | } | |
b1ab9ed8 | 400 | } |
b1ab9ed8 A |
401 | return verifyTime; |
402 | } | |
403 | ||
404 | CFArrayRef SecTrustGetDetails(SecTrustRef trust) { | |
427c49bc A |
405 | if (!trust) { |
406 | return NULL; | |
407 | } | |
408 | SecTrustEvaluateIfNecessary(trust); | |
b1ab9ed8 A |
409 | return trust->_details; |
410 | } | |
411 | ||
427c49bc A |
412 | OSStatus SecTrustGetTrustResult(SecTrustRef trust, |
413 | SecTrustResultType *result) { | |
414 | if (!trust || !result) { | |
415 | return errSecParam; | |
416 | } | |
417 | *result = trust->_trustResult; | |
418 | return errSecSuccess; | |
419 | } | |
420 | ||
b1ab9ed8 A |
421 | static CFStringRef kSecCertificateDetailSHA1Digest = CFSTR("SHA1Digest"); |
422 | ||
423 | static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix) { | |
424 | if (!trust->_exceptions || ix >= CFArrayGetCount(trust->_exceptions)) | |
425 | return NULL; | |
426 | CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_exceptions, ix); | |
427 | if (CFGetTypeID(exception) != CFDictionaryGetTypeID()) | |
428 | return NULL; | |
429 | ||
427c49bc | 430 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix); |
b1ab9ed8 A |
431 | if (!certificate) |
432 | return NULL; | |
433 | ||
434 | /* If the exception contains the current certificates sha1Digest in the | |
435 | kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */ | |
436 | CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificate); | |
437 | CFTypeRef digestValue = CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest); | |
438 | if (!digestValue || !CFEqual(sha1Digest, digestValue)) | |
439 | exception = NULL; | |
440 | ||
441 | return exception; | |
442 | } | |
443 | ||
444 | struct SecTrustCheckExceptionContext { | |
445 | CFDictionaryRef exception; | |
446 | bool exceptionNotFound; | |
447 | }; | |
448 | ||
449 | static void SecTrustCheckException(const void *key, const void *value, void *context) { | |
450 | struct SecTrustCheckExceptionContext *cec = (struct SecTrustCheckExceptionContext *)context; | |
451 | if (cec->exception) { | |
452 | CFTypeRef exceptionValue = CFDictionaryGetValue(cec->exception, key); | |
453 | if (!exceptionValue || !CFEqual(value, exceptionValue)) { | |
454 | cec->exceptionNotFound = true; | |
455 | } | |
456 | } else { | |
457 | cec->exceptionNotFound = true; | |
458 | } | |
459 | } | |
460 | ||
4d3cab3d | 461 | |
b1ab9ed8 | 462 | OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) { |
427c49bc A |
463 | if (!trust) { |
464 | return errSecParam; | |
465 | } | |
466 | OSStatus status = SecTrustEvaluateIfNecessary(trust); | |
467 | if (status || !result) | |
468 | return status; | |
469 | ||
470 | /* post-process trust result based on exceptions */ | |
471 | SecTrustResultType trustResult = trust->_trustResult; | |
472 | if (trustResult == kSecTrustResultUnspecified) { | |
473 | /* If leaf is in exceptions -> proceed, otherwise unspecified. */ | |
474 | if (SecTrustGetExceptionForCertificateAtIndex(trust, 0)) | |
475 | trustResult = kSecTrustResultProceed; | |
476 | } else if (trustResult == kSecTrustResultRecoverableTrustFailure) { | |
477 | /* If we have exceptions get details and match to exceptions. */ | |
478 | CFIndex pathLength = CFArrayGetCount(trust->_details); | |
479 | struct SecTrustCheckExceptionContext context = {}; | |
480 | CFIndex ix; | |
481 | for (ix = 0; ix < pathLength; ++ix) { | |
482 | CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_details, ix); | |
483 | ||
484 | if ((ix == 0) && CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedLeaf)) | |
485 | { | |
486 | trustResult = kSecTrustResultFatalTrustFailure; | |
487 | goto DoneCheckingTrust; | |
488 | } | |
4d3cab3d | 489 | |
427c49bc A |
490 | if (CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedKey)) |
491 | { | |
492 | trustResult = kSecTrustResultFatalTrustFailure; | |
493 | goto DoneCheckingTrust; | |
494 | } | |
4d3cab3d | 495 | |
427c49bc A |
496 | context.exception = SecTrustGetExceptionForCertificateAtIndex(trust, ix); |
497 | CFDictionaryApplyFunction(detail, SecTrustCheckException, &context); | |
498 | if (context.exceptionNotFound) { | |
499 | break; | |
500 | } | |
501 | } | |
502 | if (!context.exceptionNotFound) | |
503 | trustResult = kSecTrustResultProceed; | |
504 | } | |
505 | DoneCheckingTrust: | |
506 | trust->_trustResult = trustResult; | |
507 | ||
508 | #if DEBUG | |
509 | /* log to syslog when there is a trust failure */ | |
510 | if (trustResult != kSecTrustResultProceed && | |
511 | trustResult != kSecTrustResultConfirm && | |
512 | trustResult != kSecTrustResultUnspecified) { | |
513 | CFStringRef failureDesc = SecTrustCopyFailureDescription(trust); | |
514 | secerror("%@", failureDesc); | |
515 | CFRelease(failureDesc); | |
516 | } | |
517 | #endif | |
b1ab9ed8 | 518 | |
4d3cab3d | 519 | |
427c49bc A |
520 | *result = trustResult; |
521 | ||
522 | return status; | |
523 | } | |
524 | ||
525 | OSStatus SecTrustEvaluateAsync(SecTrustRef trust, | |
526 | dispatch_queue_t queue, SecTrustCallback result) | |
527 | { | |
528 | dispatch_async(queue, ^{ | |
529 | SecTrustResultType trustResult; | |
530 | if (errSecSuccess != SecTrustEvaluate(trust, &trustResult)) { | |
531 | trustResult = kSecTrustResultInvalid; | |
532 | } | |
533 | result(trust, trustResult); | |
534 | }); | |
535 | return errSecSuccess; | |
536 | } | |
537 | ||
538 | static bool SecXPCDictionarySetCertificates(xpc_object_t message, const char *key, CFArrayRef certificates, CFErrorRef *error) { | |
539 | xpc_object_t xpc_certificates = SecCertificateArrayCopyXPCArray(certificates, error); | |
540 | if (!xpc_certificates) | |
541 | return false; | |
542 | ||
543 | xpc_dictionary_set_value(message, key, xpc_certificates); | |
544 | xpc_release(xpc_certificates); | |
545 | ||
546 | return true; | |
547 | } | |
b1ab9ed8 | 548 | |
427c49bc A |
549 | static bool SecXPCDictionarySetPolicies(xpc_object_t message, const char *key, CFArrayRef policies, CFErrorRef *error) { |
550 | xpc_object_t xpc_policies = SecPolicyArrayCopyXPCArray(policies, error); | |
551 | if (!xpc_policies) | |
552 | return false; | |
553 | xpc_dictionary_set_value(message, key, xpc_policies); | |
554 | xpc_release(xpc_policies); | |
555 | return true; | |
556 | } | |
b1ab9ed8 | 557 | |
427c49bc A |
558 | static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message, const char *key, SecCertificatePathRef *path, CFErrorRef *error) { |
559 | xpc_object_t xpc_path = xpc_dictionary_get_value(message, key); | |
560 | if (!xpc_path) { | |
561 | *path = NULL; | |
562 | return true; | |
563 | } | |
564 | *path = SecCertificatePathCreateWithXPCArray(xpc_path, error); | |
565 | return *path; | |
566 | } | |
b1ab9ed8 | 567 | |
427c49bc A |
568 | static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message, const char *key, CFErrorRef *error) { |
569 | int64_t value = xpc_dictionary_get_int64(message, key); | |
570 | if (!value) { | |
571 | SecError(errSecInternal, error, CFSTR("object for key %s is 0"), key); | |
b1ab9ed8 | 572 | } |
427c49bc A |
573 | return (int)value; |
574 | } | |
b1ab9ed8 | 575 | |
427c49bc A |
576 | static SecTrustResultType certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request(enum SecXPCOperation op, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, CFArrayRef policies, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *details, CFDictionaryRef *info, SecCertificatePathRef *chain, CFErrorRef *error) |
577 | { | |
578 | __block SecTrustResultType tr = kSecTrustResultInvalid; | |
579 | securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) { | |
580 | if (!SecXPCDictionarySetCertificates(message, kSecTrustCertificatesKey, certificates, error)) | |
581 | return false; | |
582 | if (anchors && !SecXPCDictionarySetCertificates(message, kSecTrustAnchorsKey, anchors, error)) | |
583 | return false; | |
584 | if (anchorsOnly) | |
585 | xpc_dictionary_set_bool(message, kSecTrustAnchorsOnlyKey, anchorsOnly); | |
586 | if (!SecXPCDictionarySetPolicies(message, kSecTrustPoliciesKey, policies, error)) | |
587 | return false; | |
588 | xpc_dictionary_set_double(message, kSecTrustVerifyDateKey, verifyTime); | |
589 | return true; | |
590 | }, ^bool(xpc_object_t response, CFErrorRef *error) { | |
591 | secdebug("trust", "response: %@", response); | |
592 | return SecXPCDictionaryCopyArrayOptional(response, kSecTrustDetailsKey, details, error) && | |
593 | SecXPCDictionaryCopyDictionaryOptional(response, kSecTrustInfoKey, info, error) && | |
594 | SecXPCDictionaryCopyChainOptional(response, kSecTrustChainKey, chain, error) && | |
595 | ((tr = SecXPCDictionaryGetNonZeroInteger(response, kSecTrustResultKey, error)) != kSecTrustResultInvalid); | |
596 | }); | |
597 | return tr; | |
598 | } | |
599 | ||
600 | static OSStatus SecTrustEvaluateIfNecessary(SecTrustRef trust) { | |
601 | check(trust); | |
602 | if (!trust) | |
603 | return errSecParam; | |
4d3cab3d | 604 | |
427c49bc A |
605 | if (trust->_trustResult != kSecTrustResultInvalid) |
606 | return errSecSuccess; | |
b1ab9ed8 | 607 | |
427c49bc A |
608 | trust->_trustResult = kSecTrustResultOtherError; /* to avoid potential recursion */ |
609 | ||
610 | CFReleaseNull(trust->_chain); | |
611 | CFReleaseNull(trust->_details); | |
612 | CFReleaseNull(trust->_info); | |
b1ab9ed8 A |
613 | |
614 | /* @@@ Consider an optimization where we keep a side dictionary with the SHA1 hash of ever SecCertificateRef we send, so we only send potential duplicates once, and have the server respond with either just the SHA1 hash of a certificate, or the complete certificate in the response depending on whether the client already sent it, so we don't send back certificates to the client it already has. */ | |
427c49bc A |
615 | return SecOSStatusWith(^bool (CFErrorRef *error) { |
616 | trust->_trustResult = SECURITYD_XPC(sec_trust_evaluate, certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request, trust->_certificates, trust->_anchors, trust->_anchorsOnly, trust->_policies, SecTrustGetVerifyTime(trust), SecAccessGroupsGetCurrent(), &trust->_details, &trust->_info, &trust->_chain, error); | |
617 | if (trust->_trustResult == kSecTrustResultInvalid /* TODO check domain */ && | |
618 | SecErrorGetOSStatus(*error) == errSecNotAvailable && | |
619 | CFArrayGetCount(trust->_certificates)) { | |
b1ab9ed8 A |
620 | /* We failed to talk to securityd. The only time this should |
621 | happen is when we are running prior to launchd enabling | |
622 | registration of services. This currently happens when we | |
623 | are running from the ramdisk. To make ASR happy we initialize | |
624 | _chain and return success with a failure as the trustResult, to | |
625 | make it seem like we did a cert evaluation, so ASR can extract | |
626 | the public key from the leaf. */ | |
427c49bc A |
627 | trust->_chain = SecCertificatePathCreate(NULL, (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0)); |
628 | if (error) | |
629 | CFReleaseNull(*error); | |
630 | return true; | |
b1ab9ed8 | 631 | } |
427c49bc A |
632 | return trust->_trustResult != kSecTrustResultInvalid; |
633 | }); | |
b1ab9ed8 A |
634 | } |
635 | ||
427c49bc A |
636 | /* Helper for the qsort below. */ |
637 | static int compare_strings(const void *a1, const void *a2) { | |
638 | CFStringRef s1 = *(CFStringRef *)a1; | |
639 | CFStringRef s2 = *(CFStringRef *)a2; | |
640 | return (int) CFStringCompare(s1, s2, kCFCompareForcedOrdering); | |
641 | } | |
b1ab9ed8 | 642 | |
427c49bc A |
643 | CFStringRef SecTrustCopyFailureDescription(SecTrustRef trust) { |
644 | CFMutableStringRef reason = CFStringCreateMutable(NULL, 0); | |
645 | CFArrayRef details = SecTrustGetDetails(trust); | |
646 | CFIndex pathLength = details ? CFArrayGetCount(details) : 0; | |
647 | for (CFIndex ix = 0; ix < pathLength; ++ix) { | |
648 | CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix); | |
649 | CFIndex dCount = CFDictionaryGetCount(detail); | |
650 | if (dCount) { | |
651 | if (ix == 0) | |
652 | CFStringAppend(reason, CFSTR(" [leaf")); | |
653 | else if (ix == pathLength - 1) | |
654 | CFStringAppend(reason, CFSTR(" [root")); | |
655 | else | |
656 | CFStringAppendFormat(reason, NULL, CFSTR(" [ca%" PRIdCFIndex ), ix); | |
657 | ||
658 | const void *keys[dCount]; | |
659 | CFDictionaryGetKeysAndValues(detail, &keys[0], NULL); | |
660 | qsort(&keys[0], dCount, sizeof(keys[0]), compare_strings); | |
661 | for (CFIndex kix = 0; kix < dCount; ++kix) { | |
662 | CFStringRef key = keys[kix]; | |
663 | const void *value = CFDictionaryGetValue(detail, key); | |
664 | CFStringAppendFormat(reason, NULL, CFSTR(" %@%@"), key, | |
665 | (CFGetTypeID(value) == CFBooleanGetTypeID() | |
666 | ? CFSTR("") : value)); | |
b1ab9ed8 | 667 | } |
427c49bc | 668 | CFStringAppend(reason, CFSTR("]")); |
b1ab9ed8 | 669 | } |
427c49bc A |
670 | } |
671 | return reason; | |
b1ab9ed8 A |
672 | } |
673 | ||
b1ab9ed8 | 674 | SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) { |
427c49bc A |
675 | if (!trust) { |
676 | return NULL; | |
677 | } | |
678 | if (!trust->_publicKey) { | |
679 | if (!trust->_chain) { | |
680 | /* Trust hasn't been evaluated yet, first attempt to retrieve public key from leaf cert as is. */ | |
681 | trust->_publicKey = SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust, 0)); | |
682 | #if 0 | |
683 | if (!trust->_publicKey) { | |
684 | /* If this fails use the passed in certs in order as if they are a valid cert path an attempt to extract the key. */ | |
685 | SecCertificatePathRef path; | |
686 | // SecCertificatePathCreateWithArray Would have crashed if this code was every called | |
687 | // since it expected an array of CFDataRefs not an array of certificates. | |
688 | path = SecCertificatePathCreateWithArray(trust->_certificates); | |
689 | trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(path, 0); | |
690 | CFRelease(path); | |
691 | } | |
692 | #endif | |
693 | if (!trust->_publicKey) { | |
694 | /* Last resort, we evaluate the trust to get a _chain. */ | |
695 | SecTrustEvaluateIfNecessary(trust); | |
696 | } | |
697 | } | |
698 | if (trust->_chain) { | |
699 | trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(trust->_chain, 0); | |
700 | } | |
b1ab9ed8 | 701 | } |
427c49bc | 702 | |
b1ab9ed8 A |
703 | if (trust->_publicKey) |
704 | CFRetain(trust->_publicKey); | |
705 | ||
706 | return trust->_publicKey; | |
707 | } | |
708 | ||
709 | CFIndex SecTrustGetCertificateCount(SecTrustRef trust) { | |
427c49bc A |
710 | if (!trust) { |
711 | return 0; | |
712 | } | |
713 | SecTrustEvaluateIfNecessary(trust); | |
714 | return (trust->_chain) ? SecCertificatePathGetCount(trust->_chain) : 1; | |
b1ab9ed8 A |
715 | } |
716 | ||
717 | SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust, | |
718 | CFIndex ix) { | |
427c49bc | 719 | if (!trust) { |
b1ab9ed8 | 720 | return NULL; |
427c49bc A |
721 | } |
722 | if (ix == 0) { | |
723 | return (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0); | |
724 | } | |
725 | SecTrustEvaluateIfNecessary(trust); | |
726 | return (trust->_chain) ? SecCertificatePathGetCertificateAtIndex(trust->_chain, ix) : NULL; | |
b1ab9ed8 A |
727 | } |
728 | ||
729 | CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust) { | |
427c49bc A |
730 | if (!trust) { |
731 | return NULL; | |
732 | } | |
733 | SecTrustEvaluateIfNecessary(trust); | |
b1ab9ed8 A |
734 | CFDictionaryRef info = trust->_info; |
735 | if (info) | |
736 | CFRetain(info); | |
737 | return info; | |
738 | } | |
739 | ||
740 | CFDataRef SecTrustCopyExceptions(SecTrustRef trust) { | |
b1ab9ed8 | 741 | CFArrayRef details = SecTrustGetDetails(trust); |
427c49bc | 742 | CFIndex pathLength = details ? CFArrayGetCount(details) : 0; |
b1ab9ed8 A |
743 | CFMutableArrayRef exceptions = CFArrayCreateMutable(kCFAllocatorDefault, pathLength, &kCFTypeArrayCallBacks); |
744 | CFIndex ix; | |
745 | for (ix = 0; ix < pathLength; ++ix) { | |
746 | CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix); | |
747 | CFIndex detailCount = CFDictionaryGetCount(detail); | |
748 | CFMutableDictionaryRef exception; | |
749 | if (ix == 0 || detailCount > 0) { | |
750 | exception = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, detailCount + 1, detail); | |
751 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix); | |
752 | CFDataRef digest = SecCertificateGetSHA1Digest(certificate); | |
753 | CFDictionaryAddValue(exception, kSecCertificateDetailSHA1Digest, digest); | |
754 | } else { | |
755 | /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */ | |
756 | exception = (CFMutableDictionaryRef)CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, | |
757 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
758 | } | |
759 | CFArrayAppendValue(exceptions, exception); | |
760 | CFRelease(exception); | |
761 | } | |
762 | ||
763 | /* Remove any trailing empty dictionaries to save even more space (we skip the leaf | |
764 | since it will never be empty). */ | |
427c49bc | 765 | for (ix = pathLength; ix-- > 1;) { |
b1ab9ed8 A |
766 | CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix); |
767 | if (CFDictionaryGetCount(exception) == 0) { | |
768 | CFArrayRemoveValueAtIndex(exceptions, ix); | |
769 | } else { | |
770 | break; | |
771 | } | |
772 | } | |
773 | ||
774 | CFDataRef encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault, | |
775 | exceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL); | |
776 | CFRelease(exceptions); | |
777 | ||
778 | return encodedExceptions; | |
779 | } | |
780 | ||
781 | bool SecTrustSetExceptions(SecTrustRef trust, CFDataRef encodedExceptions) { | |
427c49bc A |
782 | if (!trust) { |
783 | return false; | |
784 | } | |
785 | CFArrayRef exceptions = NULL; | |
4d3cab3d | 786 | |
427c49bc A |
787 | if (NULL != encodedExceptions) { |
788 | exceptions = CFPropertyListCreateWithData(kCFAllocatorDefault, | |
789 | encodedExceptions, kCFPropertyListImmutable, NULL, NULL); | |
790 | } | |
4d3cab3d | 791 | |
b1ab9ed8 A |
792 | if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) { |
793 | CFRelease(exceptions); | |
794 | exceptions = NULL; | |
795 | } | |
b1ab9ed8 A |
796 | CFReleaseSafe(trust->_exceptions); |
797 | trust->_exceptions = exceptions; | |
798 | ||
799 | /* If there is a valid exception entry for our current leaf we're golden. */ | |
800 | if (SecTrustGetExceptionForCertificateAtIndex(trust, 0)) | |
801 | return true; | |
802 | ||
803 | /* The passed in exceptions didn't match our current leaf, so we discard it. */ | |
804 | CFReleaseNull(trust->_exceptions); | |
805 | return false; | |
806 | } | |
807 | ||
808 | CFArrayRef SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust, CFIndex ix) { | |
809 | CFMutableArrayRef summary; | |
810 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix); | |
811 | summary = SecCertificateCopySummaryProperties(certificate, | |
812 | SecTrustGetVerifyTime(trust)); | |
813 | /* FIXME Add more details in the failure case. */ | |
814 | ||
815 | return summary; | |
816 | } | |
817 | ||
818 | CFArrayRef SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust, CFIndex ix) { | |
819 | CFArrayRef summary; | |
820 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix); | |
821 | summary = SecCertificateCopyProperties(certificate); | |
822 | ||
823 | return summary; | |
824 | } | |
825 | ||
826 | #if 0 | |
827 | ||
828 | ||
829 | ||
830 | /* Valid chain. | |
831 | Can be on any non root cert in the chain. | |
832 | Priority: Top down | |
833 | Short circuit: Yes (No other errors matter after this one) | |
834 | Non recoverable error | |
835 | Trust UI: Invalid certificate chain linkage | |
836 | Cert UI: Invalid linkage to parent certificate | |
837 | */ | |
838 | CFStringRef kSecPolicyCheckIdLinkage = CFSTR("IdLinkage"); | |
839 | ||
840 | /* X.509 required checks. | |
841 | Can be on any cert in the chain | |
842 | Priority: Top down | |
843 | Short circuit: Yes (No other errors matter after this one) | |
844 | Non recoverable error | |
845 | Trust UI: (One or more) unsupported critical extensions found. | |
846 | */ | |
847 | /* If we have no names for the extention oids use: | |
848 | Cert UI: One or more unsupported critical extensions found (Non recoverable error). | |
849 | Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found. | |
850 | */ | |
851 | CFStringRef kSecPolicyCheckCriticalExtensions = CFSTR("CriticalExtensions"); | |
852 | /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */ | |
853 | CFStringRef kSecPolicyCheckQualifiedCertStatements = CFSTR("QualifiedCertStatements"); | |
854 | /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */ | |
855 | ||
856 | /* Trusted root. | |
857 | Only apply to the anchor. | |
858 | Priority: N/A | |
859 | Short circuit: No (Under discussion) | |
860 | Recoverable | |
861 | Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?) | |
862 | Cert UI: Not a valid anchor | |
863 | */ | |
864 | CFStringRef kSecPolicyCheckAnchorTrusted = CFSTR("AnchorTrusted"); | |
865 | CFStringRef kSecPolicyCheckAnchorSHA1 = CFSTR("AnchorSHA1"); | |
866 | ||
867 | /* Binding. | |
868 | Only applies to leaf | |
869 | Priority: N/A | |
870 | Short Circuit: No | |
871 | Recoverable | |
872 | Trust UI: (Hostname|email address) mismatch | |
873 | */ | |
874 | CFStringRef kSecPolicyCheckSSLHostname = CFSTR("SSLHostname"); | |
875 | ||
876 | /* Policy specific checks. | |
877 | Can be on any cert in the chain | |
878 | Priority: Top down | |
879 | Short Circuit: No | |
880 | Recoverable | |
881 | Trust UI: Certificate chain is not valid for the current policy. | |
882 | OR: (One or more) certificates in the chain are not valid for the current policy/application | |
883 | */ | |
884 | CFStringRef kSecPolicyCheckNonEmptySubject = CFSTR("NonEmptySubject"); | |
885 | /* Cert UI: Non CA certificate used as CA. | |
886 | Cert UI: CA certificate used as leaf. | |
887 | Cert UI: Cert chain length exceeded. | |
888 | Cert UI: Basic constraints extension not critical (non fatal). | |
889 | Cert UI: Leaf certificate has basic constraints extension (non fatal). | |
890 | */ | |
891 | CFStringRef kSecPolicyCheckBasicContraints = CFSTR("BasicContraints"); | |
892 | CFStringRef kSecPolicyCheckKeyUsage = CFSTR("KeyUsage"); | |
893 | CFStringRef kSecPolicyCheckExtendedKeyUsage = CFSTR("ExtendedKeyUsage"); | |
894 | /* Checks that the issuer of the leaf has exactly one Common Name and that it | |
895 | matches the specified string. */ | |
896 | CFStringRef kSecPolicyCheckIssuerCommonName = CFSTR("IssuerCommonName"); | |
897 | /* Checks that the leaf has exactly one Common Name and that it has the | |
898 | specified string as a prefix. */ | |
899 | CFStringRef kSecPolicyCheckSubjectCommonNamePrefix = CFSTR("SubjectCommonNamePrefix"); | |
900 | /* Check that the certificate chain length matches the specificed CFNumberRef | |
901 | length. */ | |
902 | CFStringRef kSecPolicyCheckChainLength = CFSTR("ChainLength"); | |
903 | CFStringRef kSecPolicyCheckNotValidBefore = CFSTR("NotValidBefore"); | |
904 | ||
905 | /* Expiration. | |
906 | Can be on any cert in the chain | |
907 | Priority: Top down | |
908 | Short Circuit: No | |
909 | Recoverable | |
910 | Trust UI: One or more certificates have expired or are not valid yet. | |
911 | OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date') | |
912 | Cert UI: Certificate (expired on 'date'|is not valid until 'date') | |
913 | */ | |
914 | CFStringRef kSecPolicyCheckValidIntermediates = CFSTR("ValidIntermediates"); | |
915 | CFStringRef kSecPolicyCheckValidLeaf = CFSTR("ValidLeaf"); | |
916 | CFStringRef kSecPolicyCheckValidRoot = CFSTR("ValidRoot"); | |
917 | ||
918 | #endif | |
919 | ||
920 | struct TrustFailures { | |
921 | bool badLinkage; | |
922 | bool unknownCritExtn; | |
923 | bool untrustedAnchor; | |
924 | bool hostnameMismatch; | |
925 | bool policyFail; | |
926 | bool invalidCert; | |
927 | }; | |
928 | ||
929 | static void applyDetailProperty(const void *_key, const void *_value, | |
930 | void *context) { | |
931 | CFStringRef key = (CFStringRef)_key; | |
932 | struct TrustFailures *tf = (struct TrustFailures *)context; | |
933 | if (CFGetTypeID(_value) != CFBooleanGetTypeID()) { | |
934 | /* Value isn't a CFBooleanRef, oh no! */ | |
935 | return; | |
936 | } | |
937 | CFBooleanRef value = (CFBooleanRef)_value; | |
938 | if (CFBooleanGetValue(value)) { | |
939 | /* Not an actual failure so we don't report it. */ | |
940 | return; | |
941 | } | |
942 | ||
943 | /* @@@ FIXME: Report a different return value when something is in the | |
944 | details but masked out by an exception and use that below for display | |
945 | purposes. */ | |
946 | if (CFEqual(key, kSecPolicyCheckIdLinkage)) { | |
947 | tf->badLinkage = true; | |
948 | } else if (CFEqual(key, kSecPolicyCheckCriticalExtensions) | |
949 | || CFEqual(key, kSecPolicyCheckQualifiedCertStatements)) { | |
950 | tf->unknownCritExtn = true; | |
951 | } else if (CFEqual(key, kSecPolicyCheckAnchorTrusted) | |
952 | || CFEqual(key, kSecPolicyCheckAnchorSHA1)) { | |
953 | tf->untrustedAnchor = true; | |
954 | } else if (CFEqual(key, kSecPolicyCheckSSLHostname)) { | |
955 | tf->hostnameMismatch = true; | |
956 | } else if (CFEqual(key, kSecPolicyCheckValidIntermediates) | |
957 | || CFEqual(key, kSecPolicyCheckValidLeaf) | |
958 | || CFEqual(key, kSecPolicyCheckValidLeaf)) { | |
959 | tf->invalidCert = true; | |
960 | } else | |
961 | /* Anything else is a policy failure. */ | |
962 | #if 0 | |
963 | if (CFEqual(key, kSecPolicyCheckNonEmptySubject) | |
964 | || CFEqual(key, kSecPolicyCheckBasicContraints) | |
965 | || CFEqual(key, kSecPolicyCheckKeyUsage) | |
966 | || CFEqual(key, kSecPolicyCheckExtendedKeyUsage) | |
967 | || CFEqual(key, kSecPolicyCheckIssuerCommonName) | |
968 | || CFEqual(key, kSecPolicyCheckSubjectCommonNamePrefix) | |
969 | || CFEqual(key, kSecPolicyCheckChainLength) | |
970 | || CFEqual(key, kSecPolicyCheckNotValidBefore)) | |
971 | #endif | |
972 | { | |
973 | tf->policyFail = true; | |
974 | } | |
975 | } | |
976 | ||
977 | static void appendError(CFMutableArrayRef properties, CFStringRef error) { | |
978 | CFStringRef localizedError = SecFrameworkCopyLocalizedString(error, | |
979 | CFSTR("SecCertificate")); | |
980 | appendProperty(properties, kSecPropertyTypeError, NULL, NULL, | |
981 | localizedError); | |
427c49bc | 982 | CFReleaseNull(localizedError); |
b1ab9ed8 A |
983 | } |
984 | ||
985 | CFArrayRef SecTrustCopyProperties(SecTrustRef trust) { | |
986 | CFArrayRef details = SecTrustGetDetails(trust); | |
987 | if (!details) | |
988 | return NULL; | |
989 | ||
990 | struct TrustFailures tf = {}; | |
991 | ||
992 | CFIndex ix, count = CFArrayGetCount(details); | |
993 | for (ix = 0; ix < count; ++ix) { | |
994 | CFDictionaryRef detail = (CFDictionaryRef) | |
995 | CFArrayGetValueAtIndex(details, ix); | |
996 | /* We now have a detail dictionary for certificate at index ix, with | |
997 | a key value pair for each failed policy check. Let's convert it | |
998 | from Ro-Man form into something a Hu-Man can understand. */ | |
999 | CFDictionaryApplyFunction(detail, applyDetailProperty, &tf); | |
1000 | } | |
1001 | ||
1002 | CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, 0, | |
1003 | &kCFTypeArrayCallBacks); | |
1004 | /* The badLinkage and unknownCritExtn failures are short circuited, since | |
1005 | you can't recover from those errors. */ | |
1006 | if (tf.badLinkage) { | |
1007 | appendError(properties, CFSTR("Invalid certificate chain linkage.")); | |
1008 | } else if (tf.unknownCritExtn) { | |
1009 | appendError(properties, CFSTR("One or more unsupported critical extensions found.")); | |
1010 | } else { | |
1011 | if (tf.untrustedAnchor) { | |
1012 | appendError(properties, CFSTR("Root certificate is not trusted.")); | |
1013 | } | |
1014 | if (tf.hostnameMismatch) { | |
1015 | appendError(properties, CFSTR("Hostname mismatch.")); | |
1016 | } | |
1017 | if (tf.policyFail) { | |
1018 | appendError(properties, CFSTR("Policy requirements not met.")); | |
1019 | } | |
1020 | if (tf.invalidCert) { | |
1021 | appendError(properties, CFSTR("One or more certificates have expired or are not valid yet.")); | |
1022 | } | |
1023 | } | |
1024 | ||
1025 | if (CFArrayGetCount(properties) == 0) { | |
1026 | /* The certificate chain is trusted, return an empty plist */ | |
1027 | CFReleaseNull(properties); | |
1028 | } | |
1029 | ||
1030 | return properties; | |
1031 | } | |
1032 | ||
427c49bc A |
1033 | CFDictionaryRef SecTrustCopyResult(SecTrustRef trust) { |
1034 | // Builds and returns a dictionary of evaluation results. | |
1035 | if (!trust) { | |
1036 | return NULL; | |
1037 | } | |
1038 | CFMutableDictionaryRef results = CFDictionaryCreateMutable(NULL, 0, | |
1039 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1040 | ||
1041 | // kSecTrustResultDetails (per-cert results) | |
1042 | CFArrayRef details = SecTrustGetDetails(trust); | |
1043 | if (details) { | |
1044 | CFDictionarySetValue(results, (const void *)kSecTrustResultDetails, (const void *)details); | |
1045 | } | |
1046 | ||
1047 | // kSecTrustResultValue (overall trust result) | |
1048 | CFNumberRef numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, &trust->_trustResult); | |
1049 | if (numValue) { | |
1050 | CFDictionarySetValue(results, (const void *)kSecTrustResultValue, (const void *)numValue); | |
1051 | CFRelease(numValue); | |
1052 | } | |
1053 | if (trust->_trustResult == kSecTrustResultInvalid || !trust->_info) | |
1054 | return results; // we have nothing more to add | |
1055 | ||
1056 | // kSecTrustEvaluationDate | |
1057 | CFDateRef evaluationDate = trust->_verifyDate; | |
1058 | if (evaluationDate) { | |
1059 | CFDictionarySetValue(results, (const void *)kSecTrustEvaluationDate, (const void *)evaluationDate); | |
1060 | } | |
1061 | ||
1062 | // kSecTrustExtendedValidation, kSecTrustOrganizationName | |
1063 | CFDictionaryRef info = trust->_info; | |
1064 | if (info) { | |
1065 | CFBooleanRef evValue; | |
1066 | if (CFDictionaryGetValueIfPresent(info, kSecTrustInfoExtendedValidationKey, (const void **)&evValue)) { | |
1067 | CFDictionarySetValue(results, (const void *)kSecTrustExtendedValidation, (const void *)evValue); | |
1068 | } | |
1069 | CFStringRef organizationName; | |
1070 | if (CFDictionaryGetValueIfPresent(info, kSecTrustInfoCompanyNameKey, (const void **)&organizationName)) { | |
1071 | CFDictionarySetValue(results, (const void *)kSecTrustOrganizationName, (const void *)organizationName); | |
1072 | } | |
1073 | } | |
1074 | ||
1075 | #if 0 | |
1076 | //FIXME: need to add revocation results here | |
1077 | // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate | |
1078 | CFTypeRef expirationDate; | |
1079 | if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecTrustExpirationDate, (const void **)&expirationDate)) { | |
1080 | CFDictionarySetValue(results, (const void *)kSecTrustRevocationValidUntilDate, (const void *)expirationDate); | |
1081 | CFDictionarySetValue(results, (const void *)kSecTrustRevocationChecked, (const void *)kCFBooleanTrue); | |
1082 | } | |
1083 | #endif | |
1084 | ||
1085 | return results; | |
1086 | } | |
1087 | ||
1088 | // Return 0 upon error. | |
1089 | static int to_int_error_request(enum SecXPCOperation op, CFErrorRef *error) { | |
1090 | __block int64_t result = 0; | |
1091 | securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) { | |
1092 | result = xpc_dictionary_get_int64(response, kSecXPCKeyResult); | |
1093 | if (!result) | |
1094 | return SecError(errSecInternal, error, CFSTR("int64 missing in response")); | |
1095 | return true; | |
1096 | }); | |
1097 | return (int)result; | |
1098 | } | |
b1ab9ed8 | 1099 | |
427c49bc A |
1100 | // version 0 -> error, so we need to start at version 1 or later. |
1101 | OSStatus SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber) | |
1102 | { | |
1103 | return SecOSStatusWith(^bool(CFErrorRef *error) { | |
1104 | if (!versionNumber) | |
1105 | return SecError(errSecParam, error, CFSTR("versionNumber is NULL")); | |
1106 | ||
1107 | return (*versionNumber = SECURITYD_XPC(sec_ota_pki_asset_version, to_int_error_request, error)) != 0; | |
1108 | }); | |
1109 | } | |
1110 | ||
1111 | #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); } | |
1112 | ||
1113 | static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary, const char *key, xpc_type_t type) | |
1114 | { | |
1115 | xpc_object_t value = xpc_dictionary_get_value(dictionary, key); | |
4d3cab3d | 1116 | |
427c49bc A |
1117 | return value && (xpc_get_type(value) == type); |
1118 | } | |
4d3cab3d | 1119 | |
427c49bc A |
1120 | OSStatus SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset) |
1121 | { | |
1122 | CFErrorRef error = NULL; | |
1123 | do_if_registered(sec_ota_pki_get_new_asset, &error); | |
4d3cab3d | 1124 | |
427c49bc A |
1125 | int64_t num = 0; |
1126 | xpc_object_t message = securityd_create_message(kSecXPCOpOTAPKIGetNewAsset, &error); | |
4d3cab3d | 1127 | if (message) |
427c49bc A |
1128 | { |
1129 | xpc_object_t response = securityd_message_with_reply_sync(message, &error); | |
4d3cab3d A |
1130 | |
1131 | if (response && xpc_dictionary_entry_is_type(response, kSecXPCKeyResult, XPC_TYPE_INT64)) | |
427c49bc A |
1132 | { |
1133 | num = (int64_t) xpc_dictionary_get_int64(response, kSecXPCKeyResult); | |
1134 | xpc_release(response); | |
4d3cab3d A |
1135 | } |
1136 | ||
427c49bc A |
1137 | xpc_release(message); |
1138 | } | |
1139 | ||
1140 | if (NULL != didUpdateAsset) | |
1141 | { | |
1142 | *didUpdateAsset = (int)num; | |
1143 | } | |
1144 | return noErr; | |
1145 | } | |
1146 | ||
4d3cab3d | 1147 | |
b1ab9ed8 | 1148 | #if 0 |
427c49bc A |
1149 | // MARK: - |
1150 | // MARK: SecTrustNode | |
b1ab9ed8 A |
1151 | /******************************************************** |
1152 | **************** SecTrustNode object ******************* | |
1153 | ********************************************************/ | |
1154 | typedef uint8_t SecFetchingState; | |
1155 | enum { | |
1156 | kSecFetchingStatePassedIn = 0, | |
1157 | kSecFetchingStateLocal, | |
1158 | kSecFetchingStateFromURL, | |
1159 | kSecFetchingStateDone, | |
1160 | }; | |
1161 | ||
1162 | typedef uint8_t SecTrustState; | |
1163 | enum { | |
1164 | kSecTrustStateUnknown = 0, | |
1165 | kSecTrustStateNotSigner, | |
1166 | kSecTrustStateValidSigner, | |
1167 | }; | |
1168 | ||
1169 | typedef struct __SecTrustNode *SecTrustNodeRef; | |
1170 | struct __SecTrustNode { | |
1171 | SecTrustNodeRef _child; | |
1172 | SecCertificateRef _certificate; | |
1173 | ||
1174 | /* Cached information about _certificate */ | |
1175 | bool _isAnchor; | |
1176 | bool _isSelfSigned; | |
1177 | ||
1178 | /* Set of all certificates we have ever considered as a parent. We use | |
1179 | this to avoid having to recheck certs when we go to the next phase. */ | |
1180 | CFMutableSet _certificates; | |
1181 | ||
1182 | /* Parents that are still partial chains we haven't yet considered. */ | |
1183 | CFMutableSet _partials; | |
1184 | /* Parents that are still partial chains we have rejected. We reconsider | |
1185 | these if we get to the final phase and we still haven't found a valid | |
1186 | candidate. */ | |
1187 | CFMutableSet _rejected_partials; | |
1188 | ||
1189 | /* Parents that are complete chains we haven't yet considered. */ | |
1190 | CFMutableSet _candidates; | |
1191 | /* Parents that are complete chains we have rejected. */ | |
1192 | CFMutableSet _rejected_candidates; | |
1193 | ||
1194 | /* State of candidate fetching. */ | |
1195 | SecFetchingState _fetchingState; | |
1196 | ||
1197 | /* Trust state of _candidates[_candidateIndex] */ | |
1198 | SecTrustState _trustState; | |
1199 | }; | |
1200 | typedef struct __SecTrustNode SecTrustNode; | |
1201 | ||
427c49bc | 1202 | /* CFRuntime registration data. */ |
b1ab9ed8 A |
1203 | static pthread_once_t kSecTrustNodeRegisterClass = PTHREAD_ONCE_INIT; |
1204 | static CFTypeID kSecTrustNodeTypeID = _kCFRuntimeNotATypeID; | |
1205 | ||
427c49bc | 1206 | /* Forward declarations of static functions. */ |
b1ab9ed8 A |
1207 | static CFStringRef SecTrustNodeDescribe(CFTypeRef cf); |
1208 | static void SecTrustNodeDestroy(CFTypeRef cf); | |
1209 | ||
1210 | /* Static functions. */ | |
1211 | static CFStringRef SecTrustNodeDescribe(CFTypeRef cf) { | |
1212 | SecTrustNodeRef node = (SecTrustNodeRef)cf; | |
1213 | return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, | |
1214 | CFSTR("<SecTrustNodeRef: %p>"), node); | |
1215 | } | |
1216 | ||
1217 | static void SecTrustNodeDestroy(CFTypeRef cf) { | |
1218 | SecTrustNodeRef trust = (SecTrustNodeRef)cf; | |
1219 | if (trust->_child) { | |
1220 | free(trust->_child); | |
1221 | } | |
1222 | if (trust->_certificate) { | |
1223 | free(trust->_certificate); | |
1224 | } | |
1225 | if (trust->_candidates) { | |
1226 | CFRelease(trust->_candidates); | |
1227 | } | |
1228 | } | |
1229 | ||
1230 | static void SecTrustNodeRegisterClass(void) { | |
1231 | static const CFRuntimeClass kSecTrustNodeClass = { | |
1232 | 0, /* version */ | |
1233 | "SecTrustNode", /* class name */ | |
1234 | NULL, /* init */ | |
1235 | NULL, /* copy */ | |
1236 | SecTrustNodeDestroy, /* dealloc */ | |
1237 | NULL, /* equal */ | |
1238 | NULL, /* hash */ | |
1239 | NULL, /* copyFormattingDesc */ | |
1240 | SecTrustNodeDescribe /* copyDebugDesc */ | |
1241 | }; | |
1242 | ||
1243 | kSecTrustNodeTypeID = _CFRuntimeRegisterClass(&kSecTrustNodeClass); | |
1244 | } | |
1245 | ||
1246 | /* SecTrustNode API functions. */ | |
1247 | CFTypeID SecTrustNodeGetTypeID(void) { | |
1248 | pthread_once(&kSecTrustNodeRegisterClass, SecTrustNodeRegisterClass); | |
1249 | return kSecTrustNodeTypeID; | |
1250 | } | |
1251 | ||
1252 | SecTrustNodeRef SecTrustNodeCreate(SecTrustRef trust, | |
1253 | SecCertificateRef certificate, SecTrustNodeRef child) { | |
1254 | CFAllocatorRef allocator = kCFAllocatorDefault; | |
1255 | check(trust); | |
1256 | check(certificate); | |
1257 | ||
1258 | CFIndex size = sizeof(struct __SecTrustNode); | |
1259 | SecTrustNodeRef result = (SecTrustNodeRef)_CFRuntimeCreateInstance( | |
1260 | allocator, SecTrustNodeGetTypeID(), size - sizeof(CFRuntimeBase), 0); | |
1261 | if (!result) | |
1262 | return NULL; | |
1263 | ||
1264 | memset((char*)result + sizeof(result->_base), 0, | |
1265 | sizeof(*result) - sizeof(result->_base)); | |
1266 | if (child) { | |
1267 | CFRetain(child); | |
1268 | result->_child = child; | |
1269 | } | |
1270 | CFRetain(certificate); | |
1271 | result->_certificate = certificate; | |
1272 | result->_isAnchor = SecTrustCertificateIsAnchor(certificate); | |
1273 | ||
1274 | return result; | |
1275 | } | |
1276 | ||
1277 | SecCertificateRef SecTrustGetCertificate(SecTrustNodeRef node) { | |
1278 | check(node); | |
1279 | return node->_certificate; | |
1280 | } | |
1281 | ||
1282 | CFArrayRef SecTrustNodeCopyProperties(SecTrustNodeRef node, | |
1283 | SecTrustRef trust) { | |
1284 | check(node); | |
1285 | check(trust); | |
1286 | CFMutableArrayRef summary = SecCertificateCopySummaryProperties( | |
1287 | node->_certificate, SecTrustGetVerifyTime(trust)); | |
1288 | /* FIXME Add more details in the failure case. */ | |
1289 | return summary; | |
1290 | } | |
1291 | ||
1292 | /* Attempt to verify this node's signature chain down to the child. */ | |
1293 | SecTrustState SecTrustNodeVerifySignatureChain(SecTrustNodeRef node) { | |
1294 | /* FIXME */ | |
1295 | return kSecTrustStateUnknown; | |
1296 | } | |
1297 | ||
1298 | ||
1299 | /* See if the next candidate works. */ | |
1300 | SecTrustNodeRef SecTrustNodeCopyNextCandidate(SecTrustNodeRef node, | |
1301 | SecTrustRef trust, SecFetchingState fetchingState) { | |
1302 | check(node); | |
1303 | check(trust); | |
1304 | ||
1305 | CFAbsoluteTime verifyTime = SecTrustGetVerifyTime(trust); | |
1306 | ||
1307 | for (;;) { | |
1308 | /* If we have any unconsidered candidates left check those first. */ | |
1309 | while (node->_candidateIndex < CFArrayGetCount(node->_candidates)) { | |
1310 | SecCertificateRef candidate = (SecCertificateRef) | |
1311 | CFArrayGetValueAtIndex(node->_candidates, node->_candidateIndex); | |
1312 | if (node->_fetchingState != kSecFetchingStateDone) { | |
1313 | /* If we still have potential sources to fetch other candidates | |
1314 | from we ignore expired candidates. */ | |
1315 | if (!SecCertificateIsValidOn(candidate, verifyTime)) { | |
1316 | node->_candidateIndex++; | |
1317 | continue; | |
1318 | } | |
1319 | } | |
1320 | ||
1321 | SecTrustNodeRef parent = SecTrustNodeCreate(candidate, node); | |
1322 | CFArrayRemoveValueAtIndex(node->_candidates, node->_candidateIndex); | |
1323 | if (SecTrustNodeVerifySignatureChain(parent) == | |
1324 | kSecTrustStateNotSigner) { | |
1325 | /* This candidate parent is not a valid signer of its | |
1326 | child. */ | |
1327 | CFRelease(parent); | |
1328 | /* If another signature failed further down the chain we need | |
1329 | to backtrack down to whatever child is still a valid | |
427c49bc | 1330 | candidate and has additional candidates to consider. |
b1ab9ed8 A |
1331 | @@@ We really want to make the fetchingState a global of |
1332 | SecTrust itself as well and not have any node go beyond the | |
1333 | current state of SecTrust if there are other (read cheap) | |
1334 | options to consider. */ | |
1335 | continue; | |
1336 | } | |
1337 | return parent; | |
1338 | } | |
1339 | ||
1340 | /* We've run out of candidates in our current state so let's try to | |
1341 | find some more. Note we fetch candidates in increasing order of | |
1342 | cost in the hope we won't ever get to the more expensive fetching | |
1343 | methods. */ | |
1344 | SecCertificateRef certificate = node->_certificate; | |
1345 | switch (node->_fetchingState) { | |
1346 | case kSecFetchingStatePassedIn: | |
1347 | /* Get the list of candidates from SecTrust. */ | |
1348 | CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate); | |
1349 | if (akid) { | |
1350 | SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates); | |
1351 | } else { | |
1352 | CFDataRef issuer = | |
1353 | SecCertificateGetNormalizedIssuerContent(certificate); | |
1354 | SecTrustAppendCandidatesWithSubject(issuer, node->_candidates); | |
1355 | } | |
1356 | node->_fetchingState = kSecFetchingStateLocal; | |
1357 | break; | |
1358 | case kSecFetchingStateLocal: | |
1359 | /* Lookup candidates in the local database. */ | |
1360 | node->_fetchingState = kSecFetchingStateFromURL; | |
1361 | break; | |
1362 | case kSecFetchingStateFromURL: | |
1363 | node->_fetchingState = kSecFetchingStateCheckExpired; | |
1364 | break; | |
1365 | case kSecFetchingStateCheckExpired: | |
1366 | /* Time to start considering expired candidates as well. */ | |
1367 | node->_candidateIndex = 0; | |
1368 | node->_fetchingState = kSecFetchingStateDone; | |
1369 | break; | |
1370 | case kSecFetchingStateDone; | |
1371 | return NULL; | |
1372 | } | |
1373 | } | |
1374 | ||
1375 | CFAllocatorRef allocator = CFGetAllocator(node); | |
1376 | ||
1377 | /* A trust node has a number of states. | |
1378 | 1) Look for issuing certificates by asking SecTrust about known | |
1379 | parent certificates. | |
1380 | 2) Look for issuing certificates in certificate databases (keychains) | |
1381 | 3) Look for issuing certificates by going out to the web if the nodes | |
1382 | certificate has a issuer location URL. | |
1383 | 4) Look through expired or not yet valid candidates we have put aside. | |
1384 | ||
1385 | We go though the stages 1 though 3 looking for candidate issuer | |
1386 | certificates. If a candidate certificate is not valid at verifyTime | |
1387 | we put it in a to be examined later queue. If a candidate certificate | |
1388 | is valid we verify if it actually signed our certificate (if possible). | |
1389 | If not we discard it and continue on to the next candidate certificate. | |
1390 | If it is we return a new SecTrustNodeRef for that certificate. */ | |
1391 | ||
1392 | CFMutableArrayRef issuers = CFArrayCreateMutable(allocator, 0, | |
1393 | &kCFTypeArrayCallBacks); | |
1394 | ||
1395 | /* Find a node's parent. */ | |
1396 | certificate = node->_certificate; | |
1397 | CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate); | |
1398 | CFTypeRef candidates = NULL; | |
1399 | if (akid) { | |
1400 | candidates = (CFTypeRef)CFDictionaryGetValueForKey(skidDict, akid); | |
1401 | if (candidates) { | |
1402 | addValidIssuersFrom(issuers, certificate, candidates, true); | |
1403 | } | |
1404 | } | |
1405 | if (!candidates) { | |
1406 | CFDataRef issuer = | |
1407 | SecCertificateGetNormalizedIssuerContent(certificate); | |
1408 | candidates = (CFTypeRef) | |
1409 | CFDictionaryGetValueForKey(subjectDict, issuer); | |
1410 | addValidIssuersFrom(issuers, certificate, candidates, false); | |
1411 | } | |
1412 | ||
1413 | if (CFArrayGetCount(issuers) == 0) { | |
1414 | /* O no! we can't find an issuer for this certificate. Let's see | |
1415 | if we can find one in the local database. */ | |
1416 | } | |
1417 | ||
427c49bc | 1418 | return errSecSuccess; |
b1ab9ed8 A |
1419 | } |
1420 | ||
1421 | CFArrayRef SecTrustNodeCopyNextChain(SecTrustNodeRef node, | |
1422 | SecTrustRef trust) { | |
1423 | /* Return the next full chain that isn't a reject unless we are in | |
1424 | a state where we consider returning rejects. */ | |
1425 | ||
1426 | switch (node->_fetchingState) { | |
1427 | case kSecFetchingStatePassedIn: | |
1428 | /* Get the list of candidates from SecTrust. */ | |
1429 | CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate); | |
1430 | if (akid) { | |
1431 | SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates); | |
1432 | } else { | |
1433 | CFDataRef issuer = | |
1434 | SecCertificateGetNormalizedIssuerContent(certificate); | |
1435 | SecTrustAppendCandidatesWithSubject(issuer, node->_candidates); | |
1436 | } | |
1437 | node->_fetchingState = kSecFetchingStateLocal; | |
1438 | break; | |
1439 | case kSecFetchingStateLocal: | |
1440 | /* Lookup candidates in the local database. */ | |
1441 | node->_fetchingState = kSecFetchingStateFromURL; | |
1442 | break; | |
1443 | case kSecFetchingStateFromURL: | |
1444 | node->_fetchingState = kSecFetchingStateCheckExpired; | |
1445 | break; | |
1446 | case kSecFetchingStateCheckExpired: | |
1447 | /* Time to start considering expired candidates as well. */ | |
1448 | node->_candidateIndex = 0; | |
1449 | node->_fetchingState = kSecFetchingStateDone; | |
1450 | break; | |
1451 | case kSecFetchingStateDone; | |
1452 | return NULL; | |
1453 | } | |
1454 | } | |
1455 | ||
1456 | class Source { | |
1457 | Iterator parentIterator(Cert); | |
1458 | }; | |
1459 | ||
1460 | class NodeCache { | |
1461 | Set nodes; | |
1462 | ||
1463 | static bool unique(Node node) { | |
1464 | if (nodes.contains(node)) | |
1465 | return false; | |
1466 | nodes.add(node); | |
1467 | return true; | |
1468 | } | |
1469 | ||
1470 | static bool isAnchor(Cert cert); | |
1471 | }; | |
1472 | ||
1473 | class Node { | |
1474 | Cert cert; | |
1475 | Node child; | |
1476 | int nextSource; | |
1477 | Iterator parentIterator; /* For current source of parents. */ | |
1478 | ||
1479 | Node(Cert inCert) : child(nil), cert(inCert), nextSource(0) {} | |
1480 | Node(Node inChild, Cert inCert) : child(inChild), cert(inCert), | |
1481 | nextSource(0) {} | |
1482 | ||
1483 | CertPath certPath() { | |
1484 | CertPath path; | |
1485 | Node node = this; | |
1486 | while (node) { | |
1487 | path.add(node.cert); | |
1488 | node = node.child; | |
1489 | } | |
1490 | return path; | |
1491 | } | |
1492 | ||
1493 | void contains(Cert cert) { | |
1494 | Node node = this; | |
1495 | while (node) { | |
1496 | if (cert == node.cert) | |
1497 | return true; | |
1498 | node = node.child; | |
1499 | } | |
1500 | return false; | |
1501 | } | |
1502 | ||
1503 | Node nextParent(Array currentSources) { | |
1504 | for (;;) { | |
1505 | if (!nextSource || | |
1506 | parentIterator == currentSources[nextSource - 1].end()) { | |
1507 | if (nextSource == currentSources.count) { | |
1508 | /* We ran out of parent sources. */ | |
1509 | return nil; | |
1510 | } | |
1511 | parentIterator = currentSources[nextSource++].begin(); | |
1512 | } | |
1513 | Certificate cert = *parentIterator++; | |
1514 | /* Check for cycles and self signed chains. */ | |
1515 | if (!contains(cert)) { | |
1516 | Node node = Node(this, parent); | |
1517 | if (!NodeCache.unique(node)) | |
1518 | return node; | |
1519 | } | |
1520 | } | |
1521 | } | |
1522 | }; | |
1523 | ||
1524 | ||
1525 | class PathBuilder { | |
1526 | List nodes; | |
1527 | List rejects; | |
1528 | Array currentSources; | |
1529 | Iterator nit; | |
1530 | Array allSources; | |
1531 | Iterator sourceIT; | |
1532 | CertPath chain; | |
1533 | ||
1534 | PathBuilder(Cert cert) { | |
1535 | nodes.append(Node(cert)); | |
1536 | nit = nodes.begin(); | |
1537 | sourceIT = allSources.begin(); | |
1538 | } | |
1539 | ||
1540 | nextAnchoredPath() { | |
1541 | if (nit == nodes.end()) { | |
1542 | /* We should add another source to the list of sources to | |
1543 | search. */ | |
1544 | if (sourceIT == allSources.end()) { | |
1545 | /* No more sources to add. */ | |
1546 | } | |
1547 | currentSources += *sourceIT++; | |
1548 | /* Resort nodes by size. */ | |
1549 | Nodes.sortBySize(); | |
1550 | nit = nodes.begin(); | |
1551 | /* Set the source list for all nodes. */ | |
427c49bc | 1552 | |
b1ab9ed8 A |
1553 | } |
1554 | while (Node node = *nit) { | |
1555 | Node candidate = node.nextParent(currentSources); | |
1556 | if (!candidate) { | |
1557 | /* The current node has no more candidate parents so move | |
1558 | along. */ | |
1559 | nit++; | |
1560 | continue; | |
1561 | } | |
1562 | ||
1563 | if (candidate.isAnchored) { | |
1564 | candidates.append(candidate); | |
1565 | } else | |
1566 | nodes.insert(candidate, nit); | |
1567 | } | |
1568 | } | |
1569 | ||
1570 | findValidPath() { | |
1571 | while (Node node = nextAnchoredPath()) { | |
1572 | if (node.isValid()) { | |
1573 | chain = node.certPath; | |
1574 | break; | |
1575 | } | |
1576 | rejects.append(node); | |
1577 | } | |
1578 | } | |
1579 | } | |
1580 | ||
1581 | ||
1582 | #endif |