]> git.saurik.com Git - apple/security.git/blob - sec/Security/SecTrust.c
Security-55178.0.1.tar.gz
[apple/security.git] / sec / Security / SecTrust.c
1 /*
2 * Copyright (c) 2006-2010 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 * SecTrust.c - CoreFoundation based certificate trust evaluator
24 *
25 * Created by Michael Brouwer on 10/17/06.
26 */
27
28 #include <Security/SecTrustPriv.h>
29 #include <Security/SecItem.h>
30 #include <Security/SecCertificateInternal.h>
31 #include <Security/SecCertificatePath.h>
32 #include <Security/SecFramework.h>
33 #include <Security/SecPolicyInternal.h>
34 #include <CoreFoundation/CFRuntime.h>
35 #include <CoreFoundation/CFSet.h>
36 #include <CoreFoundation/CFString.h>
37 #include <CoreFoundation/CFNumber.h>
38 #include <CoreFoundation/CFArray.h>
39 #include <CoreFoundation/CFPropertyList.h>
40 #include <AssertMacros.h>
41 #include <stdbool.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <pthread.h>
45 #include <MacErrors.h>
46 #include "SecRSAKey.h"
47 #include <libDER/oids.h>
48 #include <security_utilities/debugging.h>
49 #include <Security/SecInternal.h>
50 #include <ipc/securityd_client.h>
51 #include "securityd_server.h"
52
53 CFStringRef kSecTrustInfoExtendedValidationKey = CFSTR("ExtendedValidation");
54 CFStringRef kSecTrustInfoCompanyNameKey = CFSTR("CompanyName");
55 CFStringRef kSecTrustInfoRevocationKey = CFSTR("Revocation");
56 CFStringRef kSecTrustInfoRevocationValidUntilKey =
57 CFSTR("RevocationValidUntil");
58
59 #pragma mark -
60 #pragma mark SecTrust
61 /********************************************************
62 ****************** SecTrust object *********************
63 ********************************************************/
64 struct __SecTrust {
65 CFRuntimeBase _base;
66 CFArrayRef _certificates;
67 CFArrayRef _anchors;
68 CFTypeRef _policies;
69 CFDateRef _verifyDate;
70 SecCertificatePathRef _chain;
71 SecKeyRef _publicKey;
72 CFArrayRef _details;
73 CFDictionaryRef _info;
74 CFArrayRef _exceptions;
75
76 /* If true we don't trust any anchors other than the ones in _anchors. */
77 bool _anchorsOnly;
78 };
79
80 /* CFRuntime regsitration data. */
81 static pthread_once_t kSecTrustRegisterClass = PTHREAD_ONCE_INIT;
82 static CFTypeID kSecTrustTypeID = _kCFRuntimeNotATypeID;
83
84 /* Forward declartions of static functions. */
85 static CFStringRef SecTrustDescribe(CFTypeRef cf);
86 static void SecTrustDestroy(CFTypeRef cf);
87
88 /* Static functions. */
89 static CFStringRef SecTrustDescribe(CFTypeRef cf) {
90 SecTrustRef certificate = (SecTrustRef)cf;
91 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
92 CFSTR("<SecTrustRef: %p>"), certificate);
93 }
94
95 static void SecTrustDestroy(CFTypeRef cf) {
96 SecTrustRef trust = (SecTrustRef)cf;
97 CFReleaseSafe(trust->_certificates);
98 CFReleaseSafe(trust->_policies);
99 CFReleaseSafe(trust->_verifyDate);
100 CFReleaseSafe(trust->_anchors);
101 CFReleaseSafe(trust->_chain);
102 CFReleaseSafe(trust->_publicKey);
103 CFReleaseSafe(trust->_details);
104 CFReleaseSafe(trust->_info);
105 CFReleaseSafe(trust->_exceptions);
106 }
107
108 static void SecTrustRegisterClass(void) {
109 static const CFRuntimeClass kSecTrustClass = {
110 0, /* version */
111 "SecTrust", /* class name */
112 NULL, /* init */
113 NULL, /* copy */
114 SecTrustDestroy, /* dealloc */
115 NULL, /* equal */
116 NULL, /* hash */
117 NULL, /* copyFormattingDesc */
118 SecTrustDescribe /* copyDebugDesc */
119 };
120
121 kSecTrustTypeID = _CFRuntimeRegisterClass(&kSecTrustClass);
122 }
123
124 /* Public API functions. */
125 CFTypeID SecTrustGetTypeID(void) {
126 pthread_once(&kSecTrustRegisterClass, SecTrustRegisterClass);
127 return kSecTrustTypeID;
128 }
129
130 OSStatus SecTrustCreateWithCertificates(CFTypeRef certificates,
131 CFTypeRef policies, SecTrustRef *trustRef) {
132 OSStatus status = errSecParam;
133 CFAllocatorRef allocator = kCFAllocatorDefault;
134 CFArrayRef l_certs = NULL, l_policies = NULL;
135 SecTrustRef result = NULL;
136
137 check(certificates);
138 check(trustRef);
139 CFTypeID certType = CFGetTypeID(certificates);
140 if (certType == CFArrayGetTypeID()) {
141 /* We need at least 1 certificate. */
142 require_quiet(CFArrayGetCount(certificates) > 0, errOut);
143 l_certs = CFArrayCreateCopy(allocator, certificates);
144 } else if (certType == SecCertificateGetTypeID()) {
145 l_certs = CFArrayCreate(allocator, &certificates, 1,
146 &kCFTypeArrayCallBacks);
147 } else {
148 goto errOut;
149 }
150 if (!l_certs) {
151 status = errSecAllocate;
152 goto errOut;
153 }
154
155 if (!policies) {
156 CFTypeRef policy = SecPolicyCreateBasicX509();
157 l_policies = CFArrayCreate(allocator, &policy, 1,
158 &kCFTypeArrayCallBacks);
159 CFRelease(policy);
160 } else if (CFGetTypeID(policies) == CFArrayGetTypeID()) {
161 l_policies = CFArrayCreateCopy(allocator, policies);
162 } else if (CFGetTypeID(policies) == SecPolicyGetTypeID()) {
163 l_policies = CFArrayCreate(allocator, &policies, 1,
164 &kCFTypeArrayCallBacks);
165 } else {
166 goto errOut;
167 }
168 if (!l_policies) {
169 status = errSecAllocate;
170 goto errOut;
171 }
172
173 CFIndex size = sizeof(struct __SecTrust);
174 require_quiet(result = (SecTrustRef)_CFRuntimeCreateInstance(allocator,
175 SecTrustGetTypeID(), size - sizeof(CFRuntimeBase), 0), errOut);
176 memset((char*)result + sizeof(result->_base), 0,
177 sizeof(*result) - sizeof(result->_base));
178 status = noErr;
179
180 errOut:
181 if (status) {
182 CFReleaseSafe(result);
183 CFReleaseSafe(l_certs);
184 CFReleaseSafe(l_policies);
185 } else {
186 result->_certificates = l_certs;
187 result->_policies = l_policies;
188 *trustRef = result;
189 }
190 return status;
191 }
192
193 OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust,
194 Boolean anchorCertificatesOnly) {
195 check(trust);
196 trust->_anchorsOnly = anchorCertificatesOnly;
197
198 /* FIXME changing this options should (potentially) invalidate the chain. */
199 return noErr;
200 }
201
202 OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust,
203 CFArrayRef anchorCertificates) {
204 check(trust);
205 check(anchorCertificates);
206 CFRetain(anchorCertificates);
207 if (trust->_anchors)
208 CFRelease(trust->_anchors);
209 trust->_anchors = anchorCertificates;
210 trust->_anchorsOnly = true;
211
212 /* FIXME changing the anchor set should invalidate the chain. */
213 return noErr;
214 }
215
216 OSStatus SecTrustSetVerifyDate(SecTrustRef trust, CFDateRef verifyDate) {
217 check(trust);
218 check(verifyDate);
219 CFRetain(verifyDate);
220 if (trust->_verifyDate)
221 CFRelease(trust->_verifyDate);
222 trust->_verifyDate = verifyDate;
223
224 /* FIXME changing the verifydate should invalidate the chain. */
225 return noErr;
226 }
227
228 CFAbsoluteTime SecTrustGetVerifyTime(SecTrustRef trust) {
229 CFAbsoluteTime verifyTime;
230 if (trust->_verifyDate) {
231 verifyTime = CFDateGetAbsoluteTime(trust->_verifyDate);
232 } else {
233 verifyTime = CFAbsoluteTimeGetCurrent();
234 /* Record the verifyDate we ended up using. */
235 trust->_verifyDate = CFDateCreate(CFGetAllocator(trust), verifyTime);
236 }
237
238 return verifyTime;
239 }
240
241 CFArrayRef SecTrustGetDetails(SecTrustRef trust) {
242 return trust->_details;
243 }
244
245 static CFStringRef kSecCertificateDetailSHA1Digest = CFSTR("SHA1Digest");
246
247 static CFDictionaryRef SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust, CFIndex ix) {
248 if (!trust->_exceptions || ix >= CFArrayGetCount(trust->_exceptions))
249 return NULL;
250 CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(trust->_exceptions, ix);
251 if (CFGetTypeID(exception) != CFDictionaryGetTypeID())
252 return NULL;
253
254 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
255 if (!certificate)
256 return NULL;
257
258 /* If the exception contains the current certificates sha1Digest in the
259 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
260 CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificate);
261 CFTypeRef digestValue = CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest);
262 if (!digestValue || !CFEqual(sha1Digest, digestValue))
263 exception = NULL;
264
265 return exception;
266 }
267
268 struct SecTrustCheckExceptionContext {
269 CFDictionaryRef exception;
270 bool exceptionNotFound;
271 };
272
273 static void SecTrustCheckException(const void *key, const void *value, void *context) {
274 struct SecTrustCheckExceptionContext *cec = (struct SecTrustCheckExceptionContext *)context;
275 if (cec->exception) {
276 CFTypeRef exceptionValue = CFDictionaryGetValue(cec->exception, key);
277 if (!exceptionValue || !CFEqual(value, exceptionValue)) {
278 cec->exceptionNotFound = true;
279 }
280 } else {
281 cec->exceptionNotFound = true;
282 }
283 }
284
285 OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
286 CFMutableDictionaryRef args_in = NULL;
287 CFTypeRef args_out = NULL;
288 OSStatus status;
289
290 check(trust);
291 check(result);
292 CFReleaseNull(trust->_chain);
293 CFReleaseNull(trust->_details);
294 CFReleaseNull(trust->_info);
295
296 args_in = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
297 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
298
299 /* Translate certificates to CFDataRefs. */
300 CFArrayRef certificates = SecCertificateArrayCopyDataArray(trust->_certificates);
301 CFDictionaryAddValue(args_in, kSecTrustCertificatesKey, certificates);
302 CFRelease(certificates);
303
304 if (trust->_anchors) {
305 /* Translate anchors to CFDataRefs. */
306 CFArrayRef anchors = SecCertificateArrayCopyDataArray(trust->_anchors);
307 CFDictionaryAddValue(args_in, kSecTrustAnchorsKey, anchors);
308 CFRelease(anchors);
309 }
310 if (trust->_anchorsOnly)
311 CFDictionaryAddValue(args_in, kSecTrustAnchorsOnlyKey, kCFBooleanTrue);
312
313 /* Translate policies to plist capable CFTypeRefs. */
314 CFArrayRef serializedPolicies = SecPolicyArraySerialize(trust->_policies);
315 CFDictionaryAddValue(args_in, kSecTrustPoliciesKey, serializedPolicies);
316 CFRelease(serializedPolicies);
317
318 /* Ensure trust->_verifyDate is initialized. */
319 SecTrustGetVerifyTime(trust);
320 CFDictionaryAddValue(args_in, kSecTrustVerifyDateKey, trust->_verifyDate);
321
322 /* @@@ 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. */
323 //status = SECURITYD(sec_trust_evaluate, args_in, &args_out);
324 if (gSecurityd) {
325 status = gSecurityd->sec_trust_evaluate(args_in, &args_out);
326 } else {
327 status = ServerCommandSendReceive(sec_trust_evaluate_id,
328 args_in, &args_out);
329 }
330 if (status == errSecNotAvailable && CFArrayGetCount(trust->_certificates)) {
331 /* We failed to talk to securityd. The only time this should
332 happen is when we are running prior to launchd enabling
333 registration of services. This currently happens when we
334 are running from the ramdisk. To make ASR happy we initialize
335 _chain and return success with a failure as the trustResult, to
336 make it seem like we did a cert evaluation, so ASR can extract
337 the public key from the leaf. */
338 trust->_chain = SecCertificatePathCreate(NULL,
339 (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0));
340 if (result)
341 *result = kSecTrustResultOtherError;
342 status = noErr;
343 goto errOut;
344 }
345
346 require_quiet(args_out, errOut);
347
348 CFArrayRef details = (CFArrayRef)CFDictionaryGetValue(args_out, kSecTrustDetailsKey);
349 if (details) {
350 require(CFGetTypeID(details) == CFArrayGetTypeID(), errOut);
351 CFRetain(details);
352 trust->_details = details;
353 }
354
355 CFDictionaryRef info = (CFDictionaryRef)CFDictionaryGetValue(args_out, kSecTrustInfoKey);
356 if (info) {
357 require(CFGetTypeID(info) == CFDictionaryGetTypeID(), errOut);
358 CFRetain(info);
359 trust->_info = info;
360 }
361
362 CFArrayRef chainArray = (CFArrayRef)CFDictionaryGetValue(args_out, kSecTrustChainKey);
363 require(chainArray && CFGetTypeID(chainArray) == CFArrayGetTypeID(), errOut);
364 trust->_chain = SecCertificatePathCreateWithArray(chainArray);
365 require(trust->_chain, errOut);
366
367 CFNumberRef cfResult = (CFNumberRef)CFDictionaryGetValue(args_out, kSecTrustResultKey);
368 require(cfResult && CFGetTypeID(cfResult) == CFNumberGetTypeID(), errOut);
369 if (result) {
370 SInt32 trustResult;
371 CFNumberGetValue(cfResult, kCFNumberSInt32Type, &trustResult);
372 if (trustResult == kSecTrustResultUnspecified) {
373 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
374 if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
375 trustResult = kSecTrustResultProceed;
376 } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
377 /* If we have exceptions get details and match to exceptions. */
378 CFIndex pathLength = CFArrayGetCount(details);
379 struct SecTrustCheckExceptionContext context = {};
380 CFIndex ix;
381 for (ix = 0; ix < pathLength; ++ix) {
382 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
383 if ((ix == 0) && CFDictionaryContainsKey(detail, kSecPolicyCheckBlackListedLeaf))
384 trustResult = kSecTrustResultFatalTrustFailure;
385 context.exception = SecTrustGetExceptionForCertificateAtIndex(trust, ix);
386 CFDictionaryApplyFunction(detail, SecTrustCheckException, &context);
387 if (context.exceptionNotFound) {
388 break;
389 }
390 }
391
392 if (!context.exceptionNotFound)
393 trustResult = kSecTrustResultProceed;
394 }
395
396 *result = trustResult;
397 }
398
399 errOut:
400 CFReleaseSafe(args_out);
401 CFReleaseSafe(args_in);
402 return status;
403 }
404
405
406 SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) {
407 if (!trust->_publicKey && trust->_chain) {
408 trust->_publicKey = SecCertificatePathCopyPublicKeyAtIndex(
409 trust->_chain, 0);
410 }
411 if (trust->_publicKey)
412 CFRetain(trust->_publicKey);
413
414 return trust->_publicKey;
415 }
416
417 CFIndex SecTrustGetCertificateCount(SecTrustRef trust) {
418 return trust->_chain ? SecCertificatePathGetCount(trust->_chain) : 1;
419 }
420
421 SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust,
422 CFIndex ix) {
423 if (trust->_chain)
424 return SecCertificatePathGetCertificateAtIndex(trust->_chain, ix);
425 else if (ix == 0)
426 return (SecCertificateRef)CFArrayGetValueAtIndex(trust->_certificates, 0);
427 else
428 return NULL;
429 }
430
431 CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust) {
432 CFDictionaryRef info = trust->_info;
433 if (info)
434 CFRetain(info);
435 return info;
436 }
437
438 CFDataRef SecTrustCopyExceptions(SecTrustRef trust) {
439
440 CFArrayRef details = SecTrustGetDetails(trust);
441 CFIndex pathLength = CFArrayGetCount(details);
442 CFMutableArrayRef exceptions = CFArrayCreateMutable(kCFAllocatorDefault, pathLength, &kCFTypeArrayCallBacks);
443 CFIndex ix;
444 for (ix = 0; ix < pathLength; ++ix) {
445 CFDictionaryRef detail = (CFDictionaryRef)CFArrayGetValueAtIndex(details, ix);
446 CFIndex detailCount = CFDictionaryGetCount(detail);
447 CFMutableDictionaryRef exception;
448 if (ix == 0 || detailCount > 0) {
449 exception = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, detailCount + 1, detail);
450 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
451 CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
452 CFDictionaryAddValue(exception, kSecCertificateDetailSHA1Digest, digest);
453 } else {
454 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
455 exception = (CFMutableDictionaryRef)CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0,
456 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
457 }
458 CFArrayAppendValue(exceptions, exception);
459 CFRelease(exception);
460 }
461
462 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
463 since it will never be empty). */
464 for (ix = pathLength - 1; ix > 0; --ix) {
465 CFDictionaryRef exception = (CFDictionaryRef)CFArrayGetValueAtIndex(exceptions, ix);
466 if (CFDictionaryGetCount(exception) == 0) {
467 CFArrayRemoveValueAtIndex(exceptions, ix);
468 } else {
469 break;
470 }
471 }
472
473 CFDataRef encodedExceptions = CFPropertyListCreateData(kCFAllocatorDefault,
474 exceptions, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
475 CFRelease(exceptions);
476
477 return encodedExceptions;
478 }
479
480 bool SecTrustSetExceptions(SecTrustRef trust, CFDataRef encodedExceptions) {
481 CFArrayRef exceptions;
482 exceptions = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, encodedExceptions, kCFPropertyListImmutable, NULL);
483 if (exceptions && CFGetTypeID(exceptions) != CFArrayGetTypeID()) {
484 CFRelease(exceptions);
485 exceptions = NULL;
486 }
487
488 CFReleaseSafe(trust->_exceptions);
489 trust->_exceptions = exceptions;
490
491 /* If there is a valid exception entry for our current leaf we're golden. */
492 if (SecTrustGetExceptionForCertificateAtIndex(trust, 0))
493 return true;
494
495 /* The passed in exceptions didn't match our current leaf, so we discard it. */
496 CFReleaseNull(trust->_exceptions);
497 return false;
498 }
499
500 CFArrayRef SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
501 CFMutableArrayRef summary;
502 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
503 summary = SecCertificateCopySummaryProperties(certificate,
504 SecTrustGetVerifyTime(trust));
505 /* FIXME Add more details in the failure case. */
506
507 return summary;
508 }
509
510 CFArrayRef SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust, CFIndex ix) {
511 CFArrayRef summary;
512 SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, ix);
513 summary = SecCertificateCopyProperties(certificate);
514
515 return summary;
516 }
517
518 #if 0
519
520
521
522 /* Valid chain.
523 Can be on any non root cert in the chain.
524 Priority: Top down
525 Short circuit: Yes (No other errors matter after this one)
526 Non recoverable error
527 Trust UI: Invalid certificate chain linkage
528 Cert UI: Invalid linkage to parent certificate
529 */
530 CFStringRef kSecPolicyCheckIdLinkage = CFSTR("IdLinkage");
531
532 /* X.509 required checks.
533 Can be on any cert in the chain
534 Priority: Top down
535 Short circuit: Yes (No other errors matter after this one)
536 Non recoverable error
537 Trust UI: (One or more) unsupported critical extensions found.
538 */
539 /* If we have no names for the extention oids use:
540 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
541 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
542 */
543 CFStringRef kSecPolicyCheckCriticalExtensions = CFSTR("CriticalExtensions");
544 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
545 CFStringRef kSecPolicyCheckQualifiedCertStatements = CFSTR("QualifiedCertStatements");
546 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
547
548 /* Trusted root.
549 Only apply to the anchor.
550 Priority: N/A
551 Short circuit: No (Under discussion)
552 Recoverable
553 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
554 Cert UI: Not a valid anchor
555 */
556 CFStringRef kSecPolicyCheckAnchorTrusted = CFSTR("AnchorTrusted");
557 CFStringRef kSecPolicyCheckAnchorSHA1 = CFSTR("AnchorSHA1");
558
559 /* Binding.
560 Only applies to leaf
561 Priority: N/A
562 Short Circuit: No
563 Recoverable
564 Trust UI: (Hostname|email address) mismatch
565 */
566 CFStringRef kSecPolicyCheckSSLHostname = CFSTR("SSLHostname");
567
568 /* Policy specific checks.
569 Can be on any cert in the chain
570 Priority: Top down
571 Short Circuit: No
572 Recoverable
573 Trust UI: Certificate chain is not valid for the current policy.
574 OR: (One or more) certificates in the chain are not valid for the current policy/application
575 */
576 CFStringRef kSecPolicyCheckNonEmptySubject = CFSTR("NonEmptySubject");
577 /* Cert UI: Non CA certificate used as CA.
578 Cert UI: CA certificate used as leaf.
579 Cert UI: Cert chain length exceeded.
580 Cert UI: Basic constraints extension not critical (non fatal).
581 Cert UI: Leaf certificate has basic constraints extension (non fatal).
582 */
583 CFStringRef kSecPolicyCheckBasicContraints = CFSTR("BasicContraints");
584 CFStringRef kSecPolicyCheckKeyUsage = CFSTR("KeyUsage");
585 CFStringRef kSecPolicyCheckExtendedKeyUsage = CFSTR("ExtendedKeyUsage");
586 /* Checks that the issuer of the leaf has exactly one Common Name and that it
587 matches the specified string. */
588 CFStringRef kSecPolicyCheckIssuerCommonName = CFSTR("IssuerCommonName");
589 /* Checks that the leaf has exactly one Common Name and that it has the
590 specified string as a prefix. */
591 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix = CFSTR("SubjectCommonNamePrefix");
592 /* Check that the certificate chain length matches the specificed CFNumberRef
593 length. */
594 CFStringRef kSecPolicyCheckChainLength = CFSTR("ChainLength");
595 CFStringRef kSecPolicyCheckNotValidBefore = CFSTR("NotValidBefore");
596
597 /* Expiration.
598 Can be on any cert in the chain
599 Priority: Top down
600 Short Circuit: No
601 Recoverable
602 Trust UI: One or more certificates have expired or are not valid yet.
603 OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date')
604 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
605 */
606 CFStringRef kSecPolicyCheckValidIntermediates = CFSTR("ValidIntermediates");
607 CFStringRef kSecPolicyCheckValidLeaf = CFSTR("ValidLeaf");
608 CFStringRef kSecPolicyCheckValidRoot = CFSTR("ValidRoot");
609
610 #endif
611
612 struct TrustFailures {
613 bool badLinkage;
614 bool unknownCritExtn;
615 bool untrustedAnchor;
616 bool hostnameMismatch;
617 bool policyFail;
618 bool invalidCert;
619 };
620
621 static void applyDetailProperty(const void *_key, const void *_value,
622 void *context) {
623 CFStringRef key = (CFStringRef)_key;
624 struct TrustFailures *tf = (struct TrustFailures *)context;
625 if (CFGetTypeID(_value) != CFBooleanGetTypeID()) {
626 /* Value isn't a CFBooleanRef, oh no! */
627 return;
628 }
629 CFBooleanRef value = (CFBooleanRef)_value;
630 if (CFBooleanGetValue(value)) {
631 /* Not an actual failure so we don't report it. */
632 return;
633 }
634
635 /* @@@ FIXME: Report a different return value when something is in the
636 details but masked out by an exception and use that below for display
637 purposes. */
638 if (CFEqual(key, kSecPolicyCheckIdLinkage)) {
639 tf->badLinkage = true;
640 } else if (CFEqual(key, kSecPolicyCheckCriticalExtensions)
641 || CFEqual(key, kSecPolicyCheckQualifiedCertStatements)) {
642 tf->unknownCritExtn = true;
643 } else if (CFEqual(key, kSecPolicyCheckAnchorTrusted)
644 || CFEqual(key, kSecPolicyCheckAnchorSHA1)) {
645 tf->untrustedAnchor = true;
646 } else if (CFEqual(key, kSecPolicyCheckSSLHostname)) {
647 tf->hostnameMismatch = true;
648 } else if (CFEqual(key, kSecPolicyCheckValidIntermediates)
649 || CFEqual(key, kSecPolicyCheckValidLeaf)
650 || CFEqual(key, kSecPolicyCheckValidLeaf)) {
651 tf->invalidCert = true;
652 } else
653 /* Anything else is a policy failure. */
654 #if 0
655 if (CFEqual(key, kSecPolicyCheckNonEmptySubject)
656 || CFEqual(key, kSecPolicyCheckBasicContraints)
657 || CFEqual(key, kSecPolicyCheckKeyUsage)
658 || CFEqual(key, kSecPolicyCheckExtendedKeyUsage)
659 || CFEqual(key, kSecPolicyCheckIssuerCommonName)
660 || CFEqual(key, kSecPolicyCheckSubjectCommonNamePrefix)
661 || CFEqual(key, kSecPolicyCheckChainLength)
662 || CFEqual(key, kSecPolicyCheckNotValidBefore))
663 #endif
664 {
665 tf->policyFail = true;
666 }
667 }
668
669 static void appendError(CFMutableArrayRef properties, CFStringRef error) {
670 CFStringRef localizedError = SecFrameworkCopyLocalizedString(error,
671 CFSTR("SecCertificate"));
672 appendProperty(properties, kSecPropertyTypeError, NULL, NULL,
673 localizedError);
674 }
675
676 CFArrayRef SecTrustCopyProperties(SecTrustRef trust) {
677 CFArrayRef details = SecTrustGetDetails(trust);
678 if (!details)
679 return NULL;
680
681 struct TrustFailures tf = {};
682
683 CFIndex ix, count = CFArrayGetCount(details);
684 for (ix = 0; ix < count; ++ix) {
685 CFDictionaryRef detail = (CFDictionaryRef)
686 CFArrayGetValueAtIndex(details, ix);
687 /* We now have a detail dictionary for certificate at index ix, with
688 a key value pair for each failed policy check. Let's convert it
689 from Ro-Man form into something a Hu-Man can understand. */
690 CFDictionaryApplyFunction(detail, applyDetailProperty, &tf);
691 }
692
693 CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, 0,
694 &kCFTypeArrayCallBacks);
695 /* The badLinkage and unknownCritExtn failures are short circuited, since
696 you can't recover from those errors. */
697 if (tf.badLinkage) {
698 appendError(properties, CFSTR("Invalid certificate chain linkage."));
699 } else if (tf.unknownCritExtn) {
700 appendError(properties, CFSTR("One or more unsupported critical extensions found."));
701 } else {
702 if (tf.untrustedAnchor) {
703 appendError(properties, CFSTR("Root certificate is not trusted."));
704 }
705 if (tf.hostnameMismatch) {
706 appendError(properties, CFSTR("Hostname mismatch."));
707 }
708 if (tf.policyFail) {
709 appendError(properties, CFSTR("Policy requirements not met."));
710 }
711 if (tf.invalidCert) {
712 appendError(properties, CFSTR("One or more certificates have expired or are not valid yet."));
713 }
714 }
715
716 if (CFArrayGetCount(properties) == 0) {
717 /* The certificate chain is trusted, return an empty plist */
718 CFReleaseNull(properties);
719 }
720
721 return properties;
722 }
723
724
725 #if 0
726 #pragma mark -
727 #pragma mark SecTrustNode
728 /********************************************************
729 **************** SecTrustNode object *******************
730 ********************************************************/
731 typedef uint8_t SecFetchingState;
732 enum {
733 kSecFetchingStatePassedIn = 0,
734 kSecFetchingStateLocal,
735 kSecFetchingStateFromURL,
736 kSecFetchingStateDone,
737 };
738
739 typedef uint8_t SecTrustState;
740 enum {
741 kSecTrustStateUnknown = 0,
742 kSecTrustStateNotSigner,
743 kSecTrustStateValidSigner,
744 };
745
746 typedef struct __SecTrustNode *SecTrustNodeRef;
747 struct __SecTrustNode {
748 SecTrustNodeRef _child;
749 SecCertificateRef _certificate;
750
751 /* Cached information about _certificate */
752 bool _isAnchor;
753 bool _isSelfSigned;
754
755 /* Set of all certificates we have ever considered as a parent. We use
756 this to avoid having to recheck certs when we go to the next phase. */
757 CFMutableSet _certificates;
758
759 /* Parents that are still partial chains we haven't yet considered. */
760 CFMutableSet _partials;
761 /* Parents that are still partial chains we have rejected. We reconsider
762 these if we get to the final phase and we still haven't found a valid
763 candidate. */
764 CFMutableSet _rejected_partials;
765
766 /* Parents that are complete chains we haven't yet considered. */
767 CFMutableSet _candidates;
768 /* Parents that are complete chains we have rejected. */
769 CFMutableSet _rejected_candidates;
770
771 /* State of candidate fetching. */
772 SecFetchingState _fetchingState;
773
774 /* Trust state of _candidates[_candidateIndex] */
775 SecTrustState _trustState;
776 };
777 typedef struct __SecTrustNode SecTrustNode;
778
779 /* CFRuntime regsitration data. */
780 static pthread_once_t kSecTrustNodeRegisterClass = PTHREAD_ONCE_INIT;
781 static CFTypeID kSecTrustNodeTypeID = _kCFRuntimeNotATypeID;
782
783 /* Forward declartions of static functions. */
784 static CFStringRef SecTrustNodeDescribe(CFTypeRef cf);
785 static void SecTrustNodeDestroy(CFTypeRef cf);
786
787 /* Static functions. */
788 static CFStringRef SecTrustNodeDescribe(CFTypeRef cf) {
789 SecTrustNodeRef node = (SecTrustNodeRef)cf;
790 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
791 CFSTR("<SecTrustNodeRef: %p>"), node);
792 }
793
794 static void SecTrustNodeDestroy(CFTypeRef cf) {
795 SecTrustNodeRef trust = (SecTrustNodeRef)cf;
796 if (trust->_child) {
797 free(trust->_child);
798 }
799 if (trust->_certificate) {
800 free(trust->_certificate);
801 }
802 if (trust->_candidates) {
803 CFRelease(trust->_candidates);
804 }
805 }
806
807 static void SecTrustNodeRegisterClass(void) {
808 static const CFRuntimeClass kSecTrustNodeClass = {
809 0, /* version */
810 "SecTrustNode", /* class name */
811 NULL, /* init */
812 NULL, /* copy */
813 SecTrustNodeDestroy, /* dealloc */
814 NULL, /* equal */
815 NULL, /* hash */
816 NULL, /* copyFormattingDesc */
817 SecTrustNodeDescribe /* copyDebugDesc */
818 };
819
820 kSecTrustNodeTypeID = _CFRuntimeRegisterClass(&kSecTrustNodeClass);
821 }
822
823 /* SecTrustNode API functions. */
824 CFTypeID SecTrustNodeGetTypeID(void) {
825 pthread_once(&kSecTrustNodeRegisterClass, SecTrustNodeRegisterClass);
826 return kSecTrustNodeTypeID;
827 }
828
829 SecTrustNodeRef SecTrustNodeCreate(SecTrustRef trust,
830 SecCertificateRef certificate, SecTrustNodeRef child) {
831 CFAllocatorRef allocator = kCFAllocatorDefault;
832 check(trust);
833 check(certificate);
834
835 CFIndex size = sizeof(struct __SecTrustNode);
836 SecTrustNodeRef result = (SecTrustNodeRef)_CFRuntimeCreateInstance(
837 allocator, SecTrustNodeGetTypeID(), size - sizeof(CFRuntimeBase), 0);
838 if (!result)
839 return NULL;
840
841 memset((char*)result + sizeof(result->_base), 0,
842 sizeof(*result) - sizeof(result->_base));
843 if (child) {
844 CFRetain(child);
845 result->_child = child;
846 }
847 CFRetain(certificate);
848 result->_certificate = certificate;
849 result->_isAnchor = SecTrustCertificateIsAnchor(certificate);
850
851 return result;
852 }
853
854 SecCertificateRef SecTrustGetCertificate(SecTrustNodeRef node) {
855 check(node);
856 return node->_certificate;
857 }
858
859 CFArrayRef SecTrustNodeCopyProperties(SecTrustNodeRef node,
860 SecTrustRef trust) {
861 check(node);
862 check(trust);
863 CFMutableArrayRef summary = SecCertificateCopySummaryProperties(
864 node->_certificate, SecTrustGetVerifyTime(trust));
865 /* FIXME Add more details in the failure case. */
866 return summary;
867 }
868
869 /* Attempt to verify this node's signature chain down to the child. */
870 SecTrustState SecTrustNodeVerifySignatureChain(SecTrustNodeRef node) {
871 /* FIXME */
872 return kSecTrustStateUnknown;
873 }
874
875
876 /* See if the next candidate works. */
877 SecTrustNodeRef SecTrustNodeCopyNextCandidate(SecTrustNodeRef node,
878 SecTrustRef trust, SecFetchingState fetchingState) {
879 check(node);
880 check(trust);
881
882 CFAbsoluteTime verifyTime = SecTrustGetVerifyTime(trust);
883
884 for (;;) {
885 /* If we have any unconsidered candidates left check those first. */
886 while (node->_candidateIndex < CFArrayGetCount(node->_candidates)) {
887 SecCertificateRef candidate = (SecCertificateRef)
888 CFArrayGetValueAtIndex(node->_candidates, node->_candidateIndex);
889 if (node->_fetchingState != kSecFetchingStateDone) {
890 /* If we still have potential sources to fetch other candidates
891 from we ignore expired candidates. */
892 if (!SecCertificateIsValidOn(candidate, verifyTime)) {
893 node->_candidateIndex++;
894 continue;
895 }
896 }
897
898 SecTrustNodeRef parent = SecTrustNodeCreate(candidate, node);
899 CFArrayRemoveValueAtIndex(node->_candidates, node->_candidateIndex);
900 if (SecTrustNodeVerifySignatureChain(parent) ==
901 kSecTrustStateNotSigner) {
902 /* This candidate parent is not a valid signer of its
903 child. */
904 CFRelease(parent);
905 /* If another signature failed further down the chain we need
906 to backtrack down to whatever child is still a valid
907 candidate and has additional candidates to consider.
908 @@@ We really want to make the fetchingState a global of
909 SecTrust itself as well and not have any node go beyond the
910 current state of SecTrust if there are other (read cheap)
911 options to consider. */
912 continue;
913 }
914 return parent;
915 }
916
917 /* We've run out of candidates in our current state so let's try to
918 find some more. Note we fetch candidates in increasing order of
919 cost in the hope we won't ever get to the more expensive fetching
920 methods. */
921 SecCertificateRef certificate = node->_certificate;
922 switch (node->_fetchingState) {
923 case kSecFetchingStatePassedIn:
924 /* Get the list of candidates from SecTrust. */
925 CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
926 if (akid) {
927 SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
928 } else {
929 CFDataRef issuer =
930 SecCertificateGetNormalizedIssuerContent(certificate);
931 SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
932 }
933 node->_fetchingState = kSecFetchingStateLocal;
934 break;
935 case kSecFetchingStateLocal:
936 /* Lookup candidates in the local database. */
937 node->_fetchingState = kSecFetchingStateFromURL;
938 break;
939 case kSecFetchingStateFromURL:
940 node->_fetchingState = kSecFetchingStateCheckExpired;
941 break;
942 case kSecFetchingStateCheckExpired:
943 /* Time to start considering expired candidates as well. */
944 node->_candidateIndex = 0;
945 node->_fetchingState = kSecFetchingStateDone;
946 break;
947 case kSecFetchingStateDone;
948 return NULL;
949 }
950 }
951
952 CFAllocatorRef allocator = CFGetAllocator(node);
953
954 /* A trust node has a number of states.
955 1) Look for issuing certificates by asking SecTrust about known
956 parent certificates.
957 2) Look for issuing certificates in certificate databases (keychains)
958 3) Look for issuing certificates by going out to the web if the nodes
959 certificate has a issuer location URL.
960 4) Look through expired or not yet valid candidates we have put aside.
961
962 We go though the stages 1 though 3 looking for candidate issuer
963 certificates. If a candidate certificate is not valid at verifyTime
964 we put it in a to be examined later queue. If a candidate certificate
965 is valid we verify if it actually signed our certificate (if possible).
966 If not we discard it and continue on to the next candidate certificate.
967 If it is we return a new SecTrustNodeRef for that certificate. */
968
969 CFMutableArrayRef issuers = CFArrayCreateMutable(allocator, 0,
970 &kCFTypeArrayCallBacks);
971
972 /* Find a node's parent. */
973 certificate = node->_certificate;
974 CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
975 CFTypeRef candidates = NULL;
976 if (akid) {
977 candidates = (CFTypeRef)CFDictionaryGetValueForKey(skidDict, akid);
978 if (candidates) {
979 addValidIssuersFrom(issuers, certificate, candidates, true);
980 }
981 }
982 if (!candidates) {
983 CFDataRef issuer =
984 SecCertificateGetNormalizedIssuerContent(certificate);
985 candidates = (CFTypeRef)
986 CFDictionaryGetValueForKey(subjectDict, issuer);
987 addValidIssuersFrom(issuers, certificate, candidates, false);
988 }
989
990 if (CFArrayGetCount(issuers) == 0) {
991 /* O no! we can't find an issuer for this certificate. Let's see
992 if we can find one in the local database. */
993 }
994
995 return noErr;
996 }
997
998 CFArrayRef SecTrustNodeCopyNextChain(SecTrustNodeRef node,
999 SecTrustRef trust) {
1000 /* Return the next full chain that isn't a reject unless we are in
1001 a state where we consider returning rejects. */
1002
1003 switch (node->_fetchingState) {
1004 case kSecFetchingStatePassedIn:
1005 /* Get the list of candidates from SecTrust. */
1006 CFDataRef akid = SecCertificateGetAuthorityKeyID(certificate);
1007 if (akid) {
1008 SecTrustAppendCandidatesWithAuthorityKeyID(akid, node->_candidates);
1009 } else {
1010 CFDataRef issuer =
1011 SecCertificateGetNormalizedIssuerContent(certificate);
1012 SecTrustAppendCandidatesWithSubject(issuer, node->_candidates);
1013 }
1014 node->_fetchingState = kSecFetchingStateLocal;
1015 break;
1016 case kSecFetchingStateLocal:
1017 /* Lookup candidates in the local database. */
1018 node->_fetchingState = kSecFetchingStateFromURL;
1019 break;
1020 case kSecFetchingStateFromURL:
1021 node->_fetchingState = kSecFetchingStateCheckExpired;
1022 break;
1023 case kSecFetchingStateCheckExpired:
1024 /* Time to start considering expired candidates as well. */
1025 node->_candidateIndex = 0;
1026 node->_fetchingState = kSecFetchingStateDone;
1027 break;
1028 case kSecFetchingStateDone;
1029 return NULL;
1030 }
1031 }
1032
1033 class Source {
1034 Iterator parentIterator(Cert);
1035 };
1036
1037 class NodeCache {
1038 Set nodes;
1039
1040 static bool unique(Node node) {
1041 if (nodes.contains(node))
1042 return false;
1043 nodes.add(node);
1044 return true;
1045 }
1046
1047 static bool isAnchor(Cert cert);
1048 };
1049
1050 class Node {
1051 Cert cert;
1052 Node child;
1053 int nextSource;
1054 Iterator parentIterator; /* For current source of parents. */
1055
1056 Node(Cert inCert) : child(nil), cert(inCert), nextSource(0) {}
1057 Node(Node inChild, Cert inCert) : child(inChild), cert(inCert),
1058 nextSource(0) {}
1059
1060 CertPath certPath() {
1061 CertPath path;
1062 Node node = this;
1063 while (node) {
1064 path.add(node.cert);
1065 node = node.child;
1066 }
1067 return path;
1068 }
1069
1070 void contains(Cert cert) {
1071 Node node = this;
1072 while (node) {
1073 if (cert == node.cert)
1074 return true;
1075 node = node.child;
1076 }
1077 return false;
1078 }
1079
1080 Node nextParent(Array currentSources) {
1081 for (;;) {
1082 if (!nextSource ||
1083 parentIterator == currentSources[nextSource - 1].end()) {
1084 if (nextSource == currentSources.count) {
1085 /* We ran out of parent sources. */
1086 return nil;
1087 }
1088 parentIterator = currentSources[nextSource++].begin();
1089 }
1090 Certificate cert = *parentIterator++;
1091 /* Check for cycles and self signed chains. */
1092 if (!contains(cert)) {
1093 Node node = Node(this, parent);
1094 if (!NodeCache.unique(node))
1095 return node;
1096 }
1097 }
1098 }
1099 };
1100
1101
1102 class PathBuilder {
1103 List nodes;
1104 List rejects;
1105 Array currentSources;
1106 Iterator nit;
1107 Array allSources;
1108 Iterator sourceIT;
1109 CertPath chain;
1110
1111 PathBuilder(Cert cert) {
1112 nodes.append(Node(cert));
1113 nit = nodes.begin();
1114 sourceIT = allSources.begin();
1115 }
1116
1117 nextAnchoredPath() {
1118 if (nit == nodes.end()) {
1119 /* We should add another source to the list of sources to
1120 search. */
1121 if (sourceIT == allSources.end()) {
1122 /* No more sources to add. */
1123 }
1124 currentSources += *sourceIT++;
1125 /* Resort nodes by size. */
1126 Nodes.sortBySize();
1127 nit = nodes.begin();
1128 /* Set the source list for all nodes. */
1129
1130 }
1131 while (Node node = *nit) {
1132 Node candidate = node.nextParent(currentSources);
1133 if (!candidate) {
1134 /* The current node has no more candidate parents so move
1135 along. */
1136 nit++;
1137 continue;
1138 }
1139
1140 if (candidate.isAnchored) {
1141 candidates.append(candidate);
1142 } else
1143 nodes.insert(candidate, nit);
1144 }
1145 }
1146
1147 findValidPath() {
1148 while (Node node = nextAnchoredPath()) {
1149 if (node.isValid()) {
1150 chain = node.certPath;
1151 break;
1152 }
1153 rejects.append(node);
1154 }
1155 }
1156 }
1157
1158
1159 #endif