2 * Copyright (c) 2008-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecPolicyServer.c - Trust policies dealing with certificate revocation.
28 #include <securityd/SecPolicyServer.h>
29 #include <Security/SecPolicyInternal.h>
30 #include <Security/SecPolicyPriv.h>
31 #include <utilities/SecIOFormat.h>
32 #include <securityd/asynchttp.h>
33 #include <securityd/policytree.h>
34 #include <securityd/nameconstraints.h>
35 #include <CoreFoundation/CFTimeZone.h>
37 #include <libDER/oidsPriv.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <AssertMacros.h>
41 #include <utilities/debugging.h>
42 #include <security_asn1/SecAsn1Coder.h>
43 #include <security_asn1/ocspTemplates.h>
44 #include <security_asn1/oidsalg.h>
45 #include <security_asn1/oidsocsp.h>
46 #include <CommonCrypto/CommonDigest.h>
47 #include <Security/SecFramework.h>
48 #include <Security/SecPolicyInternal.h>
49 #include <Security/SecTrustPriv.h>
50 #include <Security/SecInternal.h>
51 #include <Security/SecKeyPriv.h>
52 #include <CFNetwork/CFHTTPMessage.h>
53 #include <CFNetwork/CFHTTPStream.h>
54 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
56 #include <securityd/SecOCSPRequest.h>
57 #include <securityd/SecOCSPResponse.h>
58 #include <securityd/asynchttp.h>
59 #include <securityd/SecTrustServer.h>
60 #include <securityd/SecOCSPCache.h>
61 #include <utilities/array_size.h>
62 #include <utilities/SecCFWrappers.h>
63 #include <utilities/SecAppleAnchorPriv.h>
64 #include "OTATrustUtilities.h"
66 #define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
68 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
69 #ifndef DUMP_OCSPRESPONSES
70 #define DUMP_OCSPRESPONSES 0
73 #if DUMP_OCSPRESPONSES
78 static void secdumpdata(CFDataRef data
, const char *name
) {
79 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
80 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
87 /********************************************************
88 ****************** SecPolicy object ********************
89 ********************************************************/
91 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
92 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
94 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
96 CFArrayRef result
= NULL
;
97 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
98 if (NULL
== otapkiRef
)
103 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
104 CFRelease(otapkiRef
);
106 if (NULL
== evToPolicyAnchorDigest
)
111 CFArrayRef roots
= NULL
;
112 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
113 if (oid
&& evToPolicyAnchorDigest
)
115 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
116 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
118 ocspdErrorLog("EVRoot.plist has non array value");
123 CFReleaseSafe(evToPolicyAnchorDigest
);
128 static bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
129 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
132 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
133 policy_set_t valid_policies
) {
134 /* Ensure that this certificate is a valid anchor for one of the
135 certificate policy oids specified in the leaf. */
136 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
138 bool good_ev_anchor
= false;
139 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
140 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
141 if (digests
&& CFArrayContainsValue(digests
,
142 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
143 secdebug("ev", "found anchor for policy oid");
144 good_ev_anchor
= true;
148 require_quiet(good_ev_anchor
, notEV
);
150 CFAbsoluteTime october2006
= 178761600;
151 if (SecCertificateVersion(certificate
) >= 3
152 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
153 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
154 require_quiet(bc
&& bc
->isCA
== true, notEV
);
155 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
156 require_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
157 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
);
160 CFAbsoluteTime jan2011
= 315532800;
161 if (SecCertificateNotValidBefore(certificate
) < jan2011
) {
162 /* At least MD5, SHA-1 with RSA 2048 or ECC NIST P-256. */
164 /* At least SHA-1, SHA-256, SHA-384 or SHA-512 with RSA 2048 or
173 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
174 const SecCECertificatePolicies
*cp
;
175 cp
= SecCertificateGetCertificatePolicies(certificate
);
176 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
177 /* SecCertificateGetCRLDistributionPoints() is a noop right now */
179 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
180 require_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
);
182 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
183 require_quiet(bc
&& bc
->isCA
== true, notEV
);
184 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
185 require_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
186 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
);
187 CFAbsoluteTime jan2011
= 315532800;
188 if (SecCertificateNotValidBefore(certificate
) < jan2011
) {
189 /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */
191 /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or
200 bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate
) {
201 /* 3. Subscriber Certificate. */
203 /* (a) certificate Policies */
204 const SecCECertificatePolicies
*cp
;
205 cp
= SecCertificateGetCertificatePolicies(certificate
);
206 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
207 /* Now find at least one policy in here that has a qualifierID of id-qt 2
208 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
210 bool found_ev_anchor_for_leaf_policy
= false;
211 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
212 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
213 found_ev_anchor_for_leaf_policy
= true;
216 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
218 /* SecCertificateGetCRLDistributionPoints() is a noop right now */
220 /* (b) cRLDistributionPoint
221 (c) authorityInformationAccess */
222 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
224 require_quiet(CFArrayGetCount(cdp
) > 0, notEV
);
226 CFArrayRef
or = SecCertificateGetOCSPResponders(certificate
);
227 require_quiet(or && CFArrayGetCount(or) > 0, notEV
);
228 //CFArrayRef ci = SecCertificateGetCAIssuers(certificate);
232 /* (d) basicConstraints
233 If present, the cA field MUST be set false. */
234 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
236 require_quiet(bc
->isCA
== false, notEV
);
240 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
242 require_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
);
246 /* The EV Cert Spec errata specifies this, though this is a check for SSL
247 not specifically EV. */
251 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
252 SecCertificateCopyExtendedKeyUsage(certificate
);
255 CFAbsoluteTime jan2011
= 315532800;
256 if (SecCertificateNotValidAfter(certificate
) < jan2011
) {
257 /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */
259 /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or
268 /********************************************************
269 **************** SecPolicy Callbacks *******************
270 ********************************************************/
271 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
275 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
277 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
278 CFDataRef parentSubjectKeyID
= NULL
;
279 for (ix
= count
- 1; ix
>= 0; --ix
) {
280 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
281 /* If the previous certificate in the chain had a SubjectKeyID,
282 make sure it matches the current certificates AuthorityKeyID. */
283 if (parentSubjectKeyID
) {
284 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
285 SubjectKeyID can be critical. Currenty we don't check
287 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
288 if (authorityKeyID
) {
289 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
290 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
291 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
297 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
301 static bool keyusage_allows(SecKeyUsage keyUsage
, CFTypeRef xku
) {
302 if (!xku
|| CFGetTypeID(xku
) != CFNumberGetTypeID())
306 CFNumberGetValue((CFNumberRef
)xku
, kCFNumberSInt32Type
, &dku
);
307 SecKeyUsage ku
= (SecKeyUsage
)dku
;
308 return (keyUsage
& ku
) == ku
;
311 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
313 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
314 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(leaf
);
316 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
317 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
319 CFIndex ix
, count
= CFArrayGetCount(xku
);
320 for (ix
= 0; ix
< count
; ++ix
) {
321 CFTypeRef ku
= CFArrayGetValueAtIndex(xku
, ix
);
322 if (keyusage_allows(keyUsage
, ku
)) {
328 match
= keyusage_allows(keyUsage
, xku
);
331 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
335 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage
,
337 if (!xeku
|| CFGetTypeID(xeku
) != CFDataGetTypeID())
339 if (extendedKeyUsage
) {
340 CFRange all
= { 0, CFArrayGetCount(extendedKeyUsage
) };
341 return CFArrayContainsValue(extendedKeyUsage
, all
, xeku
);
343 /* Certificate has no extended key usage, only a match if the policy
344 contains a 0 length CFDataRef. */
345 return CFDataGetLength((CFDataRef
)xeku
) == 0;
349 /* AUDIT[securityd](done):
350 policy->_options is a caller provided dictionary, only its cf type has
353 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
354 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
355 CFArrayRef leafExtendedKeyUsage
= SecCertificateCopyExtendedKeyUsage(leaf
);
357 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
358 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
360 CFIndex ix
, count
= CFArrayGetCount(xeku
);
361 for (ix
= 0; ix
< count
; ix
++) {
362 CFTypeRef eku
= CFArrayGetValueAtIndex(xeku
, ix
);
363 if (extendedkeyusage_allows(leafExtendedKeyUsage
, eku
)) {
369 match
= extendedkeyusage_allows(leafExtendedKeyUsage
, xeku
);
371 CFReleaseSafe(leafExtendedKeyUsage
);
373 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
378 static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc
,
379 CFStringRef key
, bool strict
) {
380 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
381 for (ix
= 0; ix
< count
; ++ix
) {
382 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
383 const SecCEBasicConstraints
*bc
=
384 SecCertificateGetBasicConstraints(cert
);
388 /* Leaf certificate has basic constraints extension. */
389 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
391 } else if (!bc
->critical
) {
392 /* Basic constraints extension is not marked critical. */
393 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
398 if (ix
> 0 || count
== 1) {
400 /* Non leaf certificate marked as isCA false. */
401 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
405 if (bc
->pathLenConstraintPresent
) {
406 if (bc
->pathLenConstraint
< (uint32_t)(ix
- 1)) {
408 /* @@@ If a self signed certificate is issued by
409 another cert that is trusted, then we are supposed
410 to treat the self signed cert itself as the anchor
411 for path length purposes. */
412 CFIndex ssix
= SecCertificatePathSelfSignedIndex(path
);
413 if (ssix
>= 0 && ix
>= ssix
) {
414 /* It's ok if the pathLenConstraint isn't met for
415 certificates signing a self signed cert in the
420 /* Path Length Constraint Exceeded. */
421 if (!SecPVCSetResult(pvc
, key
, ix
,
428 } else if (strict
&& ix
> 0) {
429 /* In strict mode all CA certificates *MUST* have a critical
430 basic constraints extension and the leaf certificate
431 *MUST NOT* have a basic constraints extension. */
432 /* CA certificate is missing basicConstraints extension. */
433 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
440 static void SecPolicyCheckBasicContraints(SecPVCRef pvc
,
442 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
445 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
447 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
448 for (ix
= 0; ix
< count
; ++ix
) {
449 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
450 /* If the certificate has a subject, or
451 if it doesn't, and it's the leaf and not self signed,
452 and also has a critical subjectAltName extension it's valid. */
453 if (!SecCertificateHasSubject(cert
)) {
454 if (ix
== 0 && count
> 1) {
455 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
456 /* Leaf certificate with empty subject does not have
457 a critical subject alt name extension. */
458 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
462 /* CA certificate has empty subject. */
463 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
470 static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc
,
474 /* Compare hostname suffix to domain name.
475 This function does not process wildcards, and allows hostname to match
476 any subdomain level of the provided domain.
478 To match, the last domain length chars of hostname must equal domain,
479 and the character immediately preceding domain in hostname (if any)
480 must be a dot. This means that domain 'bar.com' will match hostname
481 values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
483 Characters in each string are converted to lowercase for the comparison.
484 Trailing '.' characters in both names will be ignored.
486 Returns true on match, else false.
488 static bool SecDomainSuffixMatch(CFStringRef hostname
, CFStringRef domain
) {
489 CFStringInlineBuffer hbuf
, dbuf
;
492 hlength
= CFStringGetLength(hostname
),
493 dlength
= CFStringGetLength(domain
);
494 CFRange hrange
= { 0, hlength
}, drange
= { 0, dlength
};
495 CFStringInitInlineBuffer(hostname
, &hbuf
, hrange
);
496 CFStringInitInlineBuffer(domain
, &dbuf
, drange
);
498 if((hlength
== 0) || (dlength
== 0)) {
499 /* trivial case with at least one empty name */
500 return (hlength
== dlength
) ? true : false;
503 /* trim off trailing dots */
504 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hlength
-1);
505 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dlength
-1);
507 hrange
.length
= --hlength
;
510 drange
.length
= --dlength
;
513 /* trim off leading dot in suffix, if present */
514 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, 0);
515 if((dlength
> 0) && (dch
== '.')) {
517 drange
.length
= --dlength
;
520 if(hlength
< dlength
) {
524 /* perform case-insensitive comparison of domain suffix */
525 for (hix
= (hlength
-dlength
),
526 dix
= drange
.location
; dix
< drange
.length
; dix
++) {
527 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
);
528 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dix
);
529 if (towlower(hch
) != towlower(dch
)) {
534 /* require a dot prior to domain suffix, unless hostname == domain */
535 if(hlength
> dlength
) {
536 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, (hlength
-(dlength
+1)));
545 /* Compare hostname, to a server name obtained from the server's cert
546 Obtained from the SubjectAltName or the CommonName entry in the Subject.
547 Limited wildcard checking is performed here as outlined in
549 RFC 2818 Section 3.1. Server Identity
551 [...] Names may contain the wildcard
552 character * which is considered to match any single domain name
553 component or component fragment. E.g., *.a.com matches foo.a.com but
554 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
557 Trailing '.' characters in the hostname will be ignored.
559 Returns true on match, else false.
561 bool SecDNSMatch(CFStringRef hostname
, CFStringRef servername
) {
562 CFStringInlineBuffer hbuf
, sbuf
;
564 hlength
= CFStringGetLength(hostname
),
565 slength
= CFStringGetLength(servername
);
566 CFRange hrange
= { 0, hlength
}, srange
= { 0, slength
};
567 CFStringInitInlineBuffer(hostname
, &hbuf
, hrange
);
568 CFStringInitInlineBuffer(servername
, &sbuf
, srange
);
570 for (hix
= six
= 0; six
< slength
; ++six
) {
571 UniChar hch
, sch
= CFStringGetCharacterFromInlineBuffer(&sbuf
, six
);
573 if (six
+ 1 >= slength
) {
574 /* Trailing '*' in servername, match until end of hostname or
577 if (hix
>= hlength
) {
578 /* If we reach the end of the hostname we have a
582 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
++);
583 } while (hch
!= '.');
584 /* We reached the end of servername and found a '.' in
585 hostname. Return true if hostname has a single
586 trailing '.' return false if there is anything after it. */
587 return hix
== hlength
;
590 /* Grab the character after the '*'. */
591 sch
= CFStringGetCharacterFromInlineBuffer(&sbuf
, ++six
);
593 /* We have something of the form '*foo.com'. Or '**.com'
594 We don't deal with that yet, since it might require
595 backtracking. Also RFC 2818 doesn't seem to require it. */
599 /* We're looking at the '.' after the '*' in something of the
600 form 'foo*.com' or '*.com'. Match until next '.' in hostname. */
602 /* Since we're not at the end of servername yet (that case
603 was handled above), running out of chars in hostname
604 means we don't have a match. */
607 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
++);
608 } while (hch
!= '.');
610 /* We're looking at a non wildcard character in the servername.
611 If we reached the end of hostname it's not a match. */
615 /* Otherwise make sure the hostname matches the character in the
616 servername, case insensitively. */
617 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
++);
618 if (towlower(hch
) != towlower(sch
))
624 /* We reached the end of servername but we have one or more characters
625 left to compare against in the hostname. */
626 if (hix
+ 1 == hlength
&&
627 CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
) == '.') {
628 /* Hostname has a single trailing '.', we're ok with that. */
631 /* Anything else is not a match. */
638 #define kSecPolicySHA1Size 20
639 static const UInt8 kAppleCorpCASHA1
[kSecPolicySHA1Size
] = {
640 0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
641 0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
644 /* Check whether hostname is in a particular set of allowed domains.
645 Returns true if OK, false if not allowed.
647 static bool SecPolicyCheckDomain(SecPVCRef pvc
, CFStringRef hostname
)
649 CFIndex count
= SecPVCGetCertificateCount(pvc
);
650 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
651 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
653 /* is this chain anchored by kAppleCorpCASHA1? */
654 CFDataRef corpSHA1
= CFDataCreateWithBytesNoCopy(NULL
,
655 kAppleCorpCASHA1
, kSecPolicySHA1Size
, kCFAllocatorNull
);
656 bool isCorpSHA1
= (corpSHA1
&& CFEqual(anchorSHA1
, corpSHA1
));
657 CFReleaseSafe(corpSHA1
);
659 /* limit hostname to specified domains */
660 const CFStringRef dnlist
[] = {
664 unsigned int idx
, dncount
=2;
665 for (idx
= 0; idx
< dncount
; idx
++) {
666 if (SecDomainSuffixMatch(hostname
, dnlist
[idx
])) {
672 /* %%% other CA pinning checks TBA */
677 /* AUDIT[securityd](done):
678 policy->_options is a caller provided dictionary, only its cf type has
681 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
683 /* @@@ Consider what to do if the caller passes in no hostname. Should
684 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
685 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
686 CFStringRef hostName
= (CFStringRef
)
687 CFDictionaryGetValue(policy
->_options
, key
);
688 if (!isString(hostName
)) {
689 /* @@@ We can't return an error here and making the evaluation fail
690 won't help much either. */
694 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
695 bool dnsMatch
= false;
696 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(leaf
);
698 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
699 for (ix
= 0; ix
< count
; ++ix
) {
700 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
701 if (SecDNSMatch(hostName
, dns
)) {
710 /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
711 the values returned by SecCertificateCopyIPAddresses() instead. */
712 CFArrayRef ipAddresses
= SecCertificateCopyIPAddresses(leaf
);
714 CFIndex ix
, count
= CFArrayGetCount(ipAddresses
);
715 for (ix
= 0; ix
< count
; ++ix
) {
716 CFStringRef ipAddress
= (CFStringRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
717 if (!CFStringCompare(hostName
, ipAddress
, kCFCompareCaseInsensitive
)) {
722 CFRelease(ipAddresses
);
727 /* Hostname mismatch or no hostnames found in certificate. */
728 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
730 else if (!SecPolicyCheckDomain(pvc
, hostName
)) {
731 /* Hostname match, but domain not allowed for this CA */
732 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
735 if ((dnsMatch
|| pvc
->details
)
736 && SecPolicySubscriberCertificateCouldBeEV(leaf
)) {
737 secdebug("policy", "enabling optionally_ev");
738 pvc
->optionally_ev
= true;
739 /* optionally_ev => check_revocation, so we don't enable revocation
740 checking here, since we don't want it on for non EV ssl certs. */
742 /* Check revocation status if the certificate asks for it (and we
743 support it) currently we only support ocsp. */
744 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(leaf
);
745 if (ocspResponders
) {
746 SecPVCSetCheckRevocation(pvc
);
752 /* AUDIT[securityd](done):
753 policy->_options is a caller provided dictionary, only its cf type has
756 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
757 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
758 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
760 if (!isString(email
)) {
761 /* We can't return an error here and making the evaluation fail
762 won't help much either. */
766 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
767 CFArrayRef addrs
= SecCertificateCopyRFC822Names(leaf
);
769 CFIndex ix
, count
= CFArrayGetCount(addrs
);
770 for (ix
= 0; ix
< count
; ++ix
) {
771 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
772 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
781 /* Hostname mismatch or no hostnames found in certificate. */
782 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
786 static void SecPolicyCheckValidIntermediates(SecPVCRef pvc
,
788 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
789 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
790 for (ix
= 1; ix
< count
- 1; ++ix
) {
791 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
792 if (!SecCertificateIsValid(cert
, verifyTime
)) {
793 /* Intermediate certificate has expired. */
794 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
800 static void SecPolicyCheckValidLeaf(SecPVCRef pvc
,
802 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
803 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
804 if (!SecCertificateIsValid(cert
, verifyTime
)) {
805 /* Leaf certificate has expired. */
806 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
811 static void SecPolicyCheckValidRoot(SecPVCRef pvc
,
813 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
814 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
816 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
817 if (!SecCertificateIsValid(cert
, verifyTime
)) {
818 /* Root certificate has expired. */
819 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
824 /* AUDIT[securityd](done):
825 policy->_options is a caller provided dictionary, only its cf type has
828 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
830 CFIndex count
= SecPVCGetCertificateCount(pvc
);
832 /* Can't check intermediates common name if there is no intermediate. */
833 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
837 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
838 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
839 CFStringRef commonName
=
840 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
841 if (!isString(commonName
)) {
842 /* @@@ We can't return an error here and making the evaluation fail
843 won't help much either. */
846 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
847 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
848 !CFEqual(commonName
, CFArrayGetValueAtIndex(commonNames
, 0))) {
849 /* Common Name mismatch. */
850 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
852 CFReleaseSafe(commonNames
);
855 /* AUDIT[securityd](done):
856 policy->_options is a caller provided dictionary, only its cf type has
859 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
861 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
862 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
863 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
865 if (!isString(common_name
)) {
866 /* @@@ We can't return an error here and making the evaluation fail
867 won't help much either. */
870 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
871 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
872 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
873 /* Common Name mismatch. */
874 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
876 CFReleaseSafe(commonNames
);
879 /* AUDIT[securityd](done):
880 policy->_options is a caller provided dictionary, only its cf type has
883 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
885 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
886 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
887 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
889 if (!isString(prefix
)) {
890 /* @@@ We can't return an error here and making the evaluation fail
891 won't help much either. */
894 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
895 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
896 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
897 /* Common Name prefix mismatch. */
898 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
900 CFReleaseSafe(commonNames
);
903 /* AUDIT[securityd](done):
904 policy->_options is a caller provided dictionary, only its cf type has
907 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
909 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
910 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
911 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
913 if (!isString(common_name
)) {
914 /* @@@ We can't return an error here and making the evaluation fail
915 won't help much either. */
918 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
919 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
920 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
921 CFStringRef test_common_name
= common_name
?
922 CFStringCreateWithFormat(kCFAllocatorDefault
,
923 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
925 if (!CFEqual(common_name
, cert_common_name
) &&
926 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
927 /* Common Name mismatch. */
928 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
929 CFReleaseSafe(test_common_name
);
931 CFReleaseSafe(commonNames
);
934 /* AUDIT[securityd](done):
935 policy->_options is a caller provided dictionary, only its cf type has
938 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
940 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
941 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
942 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
944 /* @@@ We can't return an error here and making the evaluation fail
945 won't help much either. */
948 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
949 if (SecCertificateNotValidBefore(cert
) <= at
) {
950 /* Leaf certificate has not valid before that is too old. */
951 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
956 /* AUDIT[securityd](done):
957 policy->_options is a caller provided dictionary, only its cf type has
960 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
962 CFIndex count
= SecPVCGetCertificateCount(pvc
);
963 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
964 CFNumberRef chainLength
=
965 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
967 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
968 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
969 /* @@@ We can't return an error here and making the evaluation fail
970 won't help much either. */
973 if (value
!= count
) {
974 /* Chain length doesn't match policy requirement. */
975 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
980 /* AUDIT[securityd](done):
981 policy->_options is a caller provided dictionary, only its cf type has
984 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
986 CFIndex count
= SecPVCGetCertificateCount(pvc
);
987 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
988 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
989 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
990 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
992 bool foundMatch
= false;
995 foundMatch
= CFEqual(anchorSHA1
, value
);
996 else if (isArray(value
))
997 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), anchorSHA1
);
999 /* @@@ We only support Data and Array but we can't return an error here so.
1000 we let the evaluation fail (not much help) and assert in debug. */
1005 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, 0, kCFBooleanFalse
))
1012 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
1013 policy->_options is a caller provided dictionary, only its cf type has
1016 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
1018 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1019 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1020 SecCertificateRef cert
= NULL
;
1021 CFDataRef digest
= NULL
;
1022 bool foundMatch
= false;
1024 if (SecPVCGetCertificateCount(pvc
) < 2) {
1025 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
1029 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
1030 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
1033 foundMatch
= CFEqual(digest
, value
);
1034 else if (isArray(value
))
1035 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
1037 /* @@@ We only support Data and Array but we can't return an error here so.
1038 we let the evaluation fail (not much help) and assert in debug. */
1042 CFReleaseNull(digest
);
1045 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
1050 policy->_options is a caller provided dictionary, only its cf type has
1053 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
1055 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1056 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1057 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1058 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1059 SecAppleTrustAnchorFlags flags
= 0;
1061 if (isDictionary(value
)) {
1062 if (CFDictionaryGetValue(value
, kSecPolicyAppleAnchorIncludeTestRoots
))
1063 flags
|= kSecAppleTrustAnchorFlagsIncludeTestAnchors
;
1066 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
1069 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
1076 /* AUDIT[securityd](done):
1077 policy->_options is a caller provided dictionary, only its cf type has
1080 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
1082 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1083 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1084 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
1086 if (!isString(org
)) {
1087 /* @@@ We can't return an error here and making the evaluation fail
1088 won't help much either. */
1091 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
1092 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
1093 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
1094 /* Leaf Subject Organization mismatch. */
1095 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1097 CFReleaseSafe(organization
);
1100 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
1102 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1103 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1104 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
1106 if (!isString(orgUnit
)) {
1107 /* @@@ We can't return an error here and making the evaluation fail
1108 won't help much either. */
1111 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
1112 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
1113 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
1114 /* Leaf Subject Organizational Unit mismatch. */
1115 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1117 CFReleaseSafe(organizationalUnit
);
1120 /* AUDIT[securityd](done):
1121 policy->_options is a caller provided dictionary, only its cf type has
1124 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
1126 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1127 CFArrayRef trustedServerNames
= (CFArrayRef
)
1128 CFDictionaryGetValue(policy
->_options
, key
);
1129 /* No names specified means we accept any name. */
1130 if (!trustedServerNames
)
1132 if (!isArray(trustedServerNames
)) {
1133 /* @@@ We can't return an error here and making the evaluation fail
1134 won't help much either. */
1138 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
1139 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1140 bool dnsMatch
= false;
1141 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(leaf
);
1143 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
1144 // @@@ This is O(N^2) unfortunately we can't do better easily unless
1145 // we don't do wildcard matching. */
1146 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
1147 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
1149 for (tix
= 0; tix
< tsnCount
; ++tix
) {
1150 CFStringRef serverName
=
1151 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
1152 if (!isString(serverName
)) {
1153 /* @@@ We can't return an error here and making the
1154 evaluation fail won't help much either. */
1155 CFReleaseSafe(dnsNames
);
1158 /* we purposefully reverse the arguments here such that dns names
1159 from the cert are matched against a server name list, where
1160 the server names list can contain wildcards and the dns name
1161 cannot. References: http://support.microsoft.com/kb/941123
1162 It's easy to find occurrences where people tried to use
1163 wildcard certificates and were told that those don't work
1165 if (SecDNSMatch(dns
, serverName
)) {
1171 CFRelease(dnsNames
);
1175 /* Hostname mismatch or no hostnames found in certificate. */
1176 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1180 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
1181 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
1182 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
1183 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
1184 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
1185 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
1186 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
1187 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
1188 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
1189 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
1191 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
1192 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
1193 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
1194 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
1195 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
1196 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
1197 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
1198 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
1199 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
1200 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
1201 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
1202 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
1203 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
1204 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
1206 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
1209 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
1211 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1212 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
1214 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
1215 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
1216 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
1218 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1219 CFDataRef serial
= SecCertificateCopySerialNumber(cert
, NULL
);
1221 CFDataRef serial
= SecCertificateCopySerialNumber(cert
);
1225 CFIndex serial_length
= CFDataGetLength(serial
);
1226 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
1228 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
1233 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
1235 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
1237 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
1238 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
1240 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1241 CFReleaseSafe(serial
);
1250 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1251 if (NULL
!= otapkiRef
)
1253 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
1254 CFRelease(otapkiRef
);
1255 if (NULL
!= blackListedKeys
)
1257 /* Check for blacklisted intermediates keys. */
1258 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1261 /* Check dgst against blacklist. */
1262 if (CFSetContainsValue(blackListedKeys
, dgst
))
1264 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1268 CFRelease(blackListedKeys
);
1273 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
1275 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1276 if (NULL
!= otapkiRef
)
1278 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
1279 CFRelease(otapkiRef
);
1280 if (NULL
!= grayListedKeys
)
1282 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1284 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1287 /* Check dgst against gray. */
1288 if (CFSetContainsValue(grayListedKeys
, dgst
))
1290 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1294 CFRelease(grayListedKeys
);
1299 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1301 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1302 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1303 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1305 if (value
&& SecCertificateHasMarkerExtension(cert
, value
))
1308 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1311 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1313 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1314 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1315 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1317 for (ix
= 1; ix
< count
- 1; ix
++) {
1318 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1319 if (SecCertificateHasMarkerExtension(cert
, value
))
1322 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1325 /* Returns true if path is on the allow list, false otherwise */
1326 static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc
)
1328 bool result
= false;
1329 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
1330 CFStringRef authKey
= NULL
;
1331 SecOTAPKIRef otapkiRef
= NULL
;
1333 //get authKeyID from the last chain in the cert
1337 SecCertificateRef lastCert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1338 CFDataRef authKeyID
= SecCertificateGetAuthorityKeyID(lastCert
);
1339 if (NULL
== authKeyID
) {
1342 authKey
= CFDataCopyHexString(authKeyID
);
1344 //if allowList && key is in allowList, this would have chained up to a now-removed anchor
1345 otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1346 if (NULL
== otapkiRef
) {
1349 CFDictionaryRef allowList
= SecOTAPKICopyAllowList(otapkiRef
);
1350 if (NULL
== allowList
) {
1354 CFArrayRef allowedCerts
= CFDictionaryGetValue(allowList
, authKey
);
1355 if (!allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
1359 //search sorted array for the SHA256 hash of a cert in the chain
1360 CFRange range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
1361 for (ix
= 0; ix
< count
; ix
++) {
1362 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1367 CFDataRef certHash
= SecCertificateCopySHA256Digest(cert
);
1372 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, certHash
,
1373 (CFComparatorFunction
)CFDataCompare
, NULL
);
1374 if (position
< CFArrayGetCount(allowedCerts
)) {
1375 CFDataRef possibleMatch
= CFArrayGetValueAtIndex(allowedCerts
, position
);
1376 if (!CFDataCompare(certHash
, possibleMatch
)) {
1377 //this cert is in the allowlist
1382 CFRelease(certHash
);
1387 CFReleaseNull(otapkiRef
);
1388 CFReleaseNull(allowList
);
1393 /****************************************************************************
1394 *********************** New rfc5280 Chain Validation ***********************
1395 ****************************************************************************/
1398 typedef struct cert_path
*cert_path_t
;
1403 typedef struct x500_name
*x500_name_t
;
1407 typedef struct algorithm_id
*algorithm_id_t
;
1408 struct algorithm_id
{
1409 oid_t algorithm_oid
;
1413 typedef struct trust_anchor
*trust_anchor_t
;
1414 struct trust_anchor
{
1415 x500_name_t issuer_name
;
1416 algorithm_id_t public_key_algorithm
; /* includes optional params */
1417 SecKeyRef public_key
;
1420 typedef struct certificate_policy
*certificate_policy_t
;
1421 struct certificate_policy
{
1422 policy_qualifier_t qualifiers
;
1424 SLIST_ENTRY(certificate_policy
) policies
;
1427 typedef struct policy_mapping
*policy_mapping_t
;
1428 struct policy_mapping
{
1429 SLIST_ENTRY(policy_mapping
) mappings
;
1430 oid_t issuer_domain_policy
;
1431 oid_t subject_domain_policy
;
1434 typedef struct root_name
*root_name_t
;
1439 struct policy_tree_add_ctx
{
1441 policy_qualifier_t p_q
;
1444 /* For each node of depth i-1 in the valid_policy_tree where P-OID is in the expected_policy_set, create a child node as follows: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
1445 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1446 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1447 policy_set_t policy_set
;
1448 for (policy_set
= node
->expected_policy_set
;
1450 policy_set
= policy_set
->oid_next
) {
1451 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1452 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1459 /* If the valid_policy_tree includes a node of depth i-1 with the valid_policy anyPolicy, generate a child node with the following values: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
1460 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1461 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1462 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1463 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1469 /* Return true iff node has a child with a valid_policy equal to oid. */
1470 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1472 policy_tree_t child
;
1473 for (child
= node
->children
; child
; child
= child
->siblings
) {
1474 if (oid_equal(child
->valid_policy
, (*oid
))) {
1481 /* For each node in the valid_policy_tree of depth i-1, for each value in the expected_policy_set (including anyPolicy) that does not appear in a child node, create a child node with the following values: set the valid_policy to the value from the expected_policy_set in the parent node, set the qualifier_set to AP-Q, and set the expected_policy_set to the value in the valid_policy from this node. */
1482 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1483 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1484 policy_set_t policy_set
;
1485 bool added_node
= false;
1486 for (policy_set
= node
->expected_policy_set
;
1488 policy_set
= policy_set
->oid_next
) {
1489 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1490 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1498 /* For each node where ID-P is the valid_policy, set expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1499 static bool policy_tree_map(policy_tree_t node
, void *ctx
) {
1500 /* Can't map oidAnyPolicy. */
1501 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1504 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1505 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1506 policy_set_t policy_set
= NULL
;
1507 /* First count how many mappings match this nodes valid_policy. */
1508 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1509 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1510 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1511 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1512 p_node
->oid
= mapping
->subjectDomainPolicy
;
1513 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1514 policy_set
= p_node
;
1518 policy_tree_set_expected_policy(node
, policy_set
);
1525 #define POLICY_MAPPING 0
1526 #define POLICY_SUBTREES 1
1528 /* rfc5280 basic cert processing. */
1529 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1533 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1534 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1535 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1536 uint32_t n
= (uint32_t)count
;
1537 bool is_anchored
= SecPVCIsAnchored(pvc
);
1539 /* If the anchor is trusted we don't procces the last cert in the
1543 /* trust may be restored for a path with an untrusted root that matches the allow list */
1544 if (!SecPVCCheckCertificateAllowList(pvc
)) {
1545 /* Add a detail for the root not being trusted. */
1546 if (SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1547 n
- 1, kCFBooleanFalse
, true))
1552 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1553 //policy_set_t user_initial_policy_set = NULL;
1554 //trust_anchor_t anchor;
1555 bool initial_policy_mapping_inhibit
= false;
1556 bool initial_explicit_policy
= false;
1557 bool initial_any_policy_inhibit
= false;
1559 /* Initialization */
1560 pvc
->valid_policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1562 CFMutableArrayRef permitted_subtrees
= NULL
;
1563 CFMutableArrayRef excluded_subtrees
= NULL
;
1564 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1565 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1566 assert(permitted_subtrees
!= NULL
);
1567 assert(excluded_subtrees
!= NULL
);
1569 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1570 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1571 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1574 /* Path builder ensures we only get cert chains with proper issuer
1575 chaining with valid signatures along the way. */
1576 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1577 SecKeyRef working_public_key
= anchor
->public_key
;
1578 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1580 uint32_t i
, max_path_length
= n
;
1581 SecCertificateRef cert
= NULL
;
1582 for (i
= 1; i
<= n
; ++i
) {
1584 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1585 bool is_self_issued
= SecPVCIsCertificateAtIndexSelfSigned(pvc
, n
- i
);
1587 /* (a) Verify the basic certificate information. */
1588 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1589 using the working_public_key and the working_public_key_parameters. */
1591 /* Already done by chain builder. */
1592 if (!SecCertificateIsValid(cert
, verify_time
)) {
1593 CFStringRef fail_key
= i
== n
? kSecPolicyCheckValidLeaf
: kSecPolicyCheckValidIntermediates
;
1594 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
))
1599 /* Check revocation status if the certificate asks for it. */
1600 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
1601 if (ocspResponders
) {
1602 SecPVCSetCheckRevocation(pvc
);
1605 /* @@@ cert.issuer == working_issuer_name. */
1609 if (!is_self_issued
|| i
== n
) {
1611 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1612 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1613 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
)) || found
) {
1614 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) return;
1617 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1618 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1619 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
)) || !found
) {
1620 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) return;
1626 if (pvc
->valid_policy_tree
) {
1627 const SecCECertificatePolicies
*cp
=
1628 SecCertificateGetCertificatePolicies(cert
);
1629 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1630 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1631 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1632 oid_t p_oid
= policy
->policyIdentifier
;
1633 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1634 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1635 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1636 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1637 policy_tree_add_if_match
, &ctx
)) {
1638 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1639 policy_tree_add_if_any
, &ctx
);
1643 /* The certificate policies extension includes the policy
1644 anyPolicy with the qualifier set AP-Q and either
1645 (a) inhibit_anyPolicy is greater than 0 or
1646 (b) i < n and the certificate is self-issued. */
1647 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1648 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1649 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1650 oid_t p_oid
= policy
->policyIdentifier
;
1651 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1652 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1653 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1654 policy_tree_add_expected
, (void *)p_q
);
1658 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1661 if (pvc
->valid_policy_tree
)
1662 policy_tree_prune(&pvc
->valid_policy_tree
);
1665 /* (f) Verify that either explicit_policy is greater than 0 or the
1666 valid_policy_tree is not equal to NULL. */
1667 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1668 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1669 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
, true))
1672 /* If Last Cert in Path */
1676 /* Prepare for Next Cert */
1678 /* (a) verify that anyPolicy does not appear as an
1679 issuerDomainPolicy or a subjectDomainPolicy */
1680 CFDictionaryRef pm
= SecCertificateGetPolicyMappings(cert
);
1682 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1683 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1684 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1685 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1686 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1687 /* Policy mapping uses anyPolicy, illegal. */
1688 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
))
1693 /* (1) If the policy_mapping variable is greater than 0 */
1694 if (policy_mapping
> 0) {
1695 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1696 policy_tree_map
, (void *)pm
)) {
1697 /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1 that has a valid_policy of anyPolicy as follows:
1699 (i) set the valid_policy to ID-P;
1701 (ii) set the qualifier_set to the qualifier set of the
1702 policy anyPolicy in the certificate policies
1703 extension of certificate i; and
1704 (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1708 /* (i) delete each node of depth i in the valid_policy_tree
1709 where ID-P is the valid_policy. */
1710 struct policy_tree_map_ctx ctx
= { idp_oid
, sdp_oid
};
1711 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1712 policy_tree_delete_if_match
, &ctx
);
1714 /* (ii) If there is a node in the valid_policy_tree of depth
1715 i-1 or less without any child nodes, delete that
1716 node. Repeat this step until there are no nodes of
1717 depth i-1 or less without children. */
1718 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1721 #endif /* POLICY_MAPPING */
1723 //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
1724 //working_public_key = SecCertificateCopyPublicKey(cert);
1725 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1726 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1728 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1730 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1731 if (permitted_subtrees_in_cert
) {
1732 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1735 // could do something smart here to avoid inserting the exact same constraint
1736 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1737 if (excluded_subtrees_in_cert
) {
1738 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1739 CFRange range
= { 0, num_trees
};
1740 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1744 if (!is_self_issued
) {
1745 if (explicit_policy
)
1749 if (inhibit_any_policy
)
1750 inhibit_any_policy
--;
1753 const SecCEPolicyConstraints
*pc
=
1754 SecCertificateGetPolicyConstraints(cert
);
1756 if (pc
->requireExplicitPolicyPresent
1757 && pc
->requireExplicitPolicy
< explicit_policy
) {
1758 explicit_policy
= pc
->requireExplicitPolicy
;
1760 if (pc
->inhibitPolicyMappingPresent
1761 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1762 policy_mapping
= pc
->inhibitPolicyMapping
;
1766 uint32_t iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1767 if (iap
< inhibit_any_policy
) {
1768 inhibit_any_policy
= iap
;
1771 const SecCEBasicConstraints
*bc
=
1772 SecCertificateGetBasicConstraints(cert
);
1773 #if 0 /* Checked in chain builder pre signature verify already. */
1774 if (!bc
|| !bc
->isCA
) {
1775 /* Basic constraints not present or not marked as isCA, illegal. */
1776 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicContraints
,
1777 n
- i
, kCFBooleanFalse
))
1782 if (!is_self_issued
) {
1783 if (max_path_length
> 0) {
1786 /* max_path_len exceeded, illegal. */
1787 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicContraints
,
1788 n
- i
, kCFBooleanFalse
))
1793 if (bc
&& bc
->pathLenConstraintPresent
1794 && bc
->pathLenConstraint
< max_path_length
) {
1795 max_path_length
= bc
->pathLenConstraint
;
1797 #if 0 /* Checked in chain builder pre signature verify already. */
1798 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1799 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1800 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1801 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1802 n
- i
, kCFBooleanFalse
, true))
1806 /* (o) Recognize and process any other critical extension present in the certificate. Process any other recognized non-critical extension present in the certificate that is relevant to path processing. */
1807 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1808 /* Certificate contains one or more unknown critical extensions. */
1809 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1810 n
- i
, kCFBooleanFalse
))
1813 } /* end loop over certs in path */
1815 cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1817 if (explicit_policy
)
1820 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1822 if (pc
->requireExplicitPolicyPresent
1823 && pc
->requireExplicitPolicy
== 0) {
1824 explicit_policy
= 0;
1828 //working_public_key = SecCertificateCopyPublicKey(cert);
1830 /* If the subjectPublicKeyInfo field of the certificate contains an algorithm field with null parameters or parameters are omitted, compare the certificate subjectPublicKey algorithm to the working_public_key_algorithm. If the certificate subjectPublicKey algorithm and the
1831 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1832 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1834 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1835 /* (f) Recognize and process any other critical extension present in the certificate n. Process any other recognized non-critical extension present in certificate n that is relevant to path processing. */
1836 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1837 /* Certificate contains one or more unknown critical extensions. */
1838 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1839 0, kCFBooleanFalse
))
1842 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1844 if (pvc
->valid_policy_tree
) {
1845 #if !defined(NDEBUG)
1846 policy_tree_dump(pvc
->valid_policy_tree
);
1849 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1852 /* If either (1) the value of explicit_policy variable is greater than
1853 zero or (2) the valid_policy_tree is not NULL, then path processing
1855 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1856 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1857 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, 0, kCFBooleanFalse
, true))
1861 CFReleaseNull(permitted_subtrees
);
1862 CFReleaseNull(excluded_subtrees
);
1865 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1866 policy_set_t policies
= NULL
;
1867 const SecCECertificatePolicies
*cp
=
1868 SecCertificateGetCertificatePolicies(cert
);
1869 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1870 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1871 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1876 static void SecPolicyCheckEV(SecPVCRef pvc
,
1878 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1879 policy_set_t valid_policies
= NULL
;
1881 for (ix
= 0; ix
< count
; ++ix
) {
1882 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1883 policy_set_t policies
= policies_for_cert(cert
);
1886 /* anyPolicy in the leaf isn't allowed for EV, so only init
1887 valid_policies if we have real policies. */
1888 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1889 valid_policies
= policies
;
1892 } else if (ix
< count
- 1) {
1893 /* Subordinate CA */
1894 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1895 secdebug("ev", "subordinate certificate is not ev");
1896 if (SecPVCSetResultForced(pvc
, key
,
1897 ix
, kCFBooleanFalse
, true)) {
1898 policy_set_free(valid_policies
);
1899 policy_set_free(policies
);
1903 policy_set_intersect(&valid_policies
, policies
);
1906 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1907 secdebug("ev", "anchor certificate is not ev");
1908 if (SecPVCSetResultForced(pvc
, key
,
1909 ix
, kCFBooleanFalse
, true)) {
1910 policy_set_free(valid_policies
);
1911 policy_set_free(policies
);
1916 policy_set_free(policies
);
1917 if (!valid_policies
) {
1918 secdebug("ev", "valid_policies set is empty: chain not ev");
1919 /* If we ever get into a state where no policies are valid anymore
1920 this can't be an ev chain. */
1921 if (SecPVCSetResultForced(pvc
, key
,
1922 ix
, kCFBooleanFalse
, true)) {
1928 policy_set_free(valid_policies
);
1930 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1931 Subscriber MUST contain an OID defined by the CA in the certificate’s
1932 certificatePolicies extension that: (i) indicates which CA policy statement relates
1933 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1934 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1935 marks the certificate as being an EV Certificate.
1936 (b) EV Subordinate CA Certificates
1937 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1938 CA MUST contain one or more OIDs defined by the issuing CA that
1939 explicitly identify the EV Policies that are implemented by the Subordinate
1941 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1942 MAY contain the special anyPolicy OID (2.5.29.32.0).
1943 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1944 certificatePolicies or extendedKeyUsage extensions.
1950 * MARK: Certificate Transparency support
1956 Version sct_version; // 1 byte
1957 LogID id; // 32 bytes
1958 uint64 timestamp; // 8 bytes
1959 CtExtensions extensions; // 2 bytes len field, + n bytes data
1960 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1961 Version sct_version;
1962 SignatureType signature_type = certificate_timestamp;
1964 LogEntryType entry_type;
1965 select(entry_type) {
1966 case x509_entry: ASN.1Cert;
1967 case precert_entry: PreCert;
1969 CtExtensions extensions;
1971 } SignedCertificateTimestamp;
1975 #include <Security/SecureTransportPriv.h>
1978 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1981 case SSL_SignatureAlgorithmRSA
:
1983 case SSL_HashAlgorithmSHA1
:
1984 return &CSSMOID_SHA1WithRSA
;
1985 case SSL_HashAlgorithmSHA256
:
1986 return &CSSMOID_SHA256WithRSA
;
1987 case SSL_HashAlgorithmSHA384
:
1988 return &CSSMOID_SHA384WithRSA
;
1992 case SSL_SignatureAlgorithmECDSA
:
1994 case SSL_HashAlgorithmSHA1
:
1995 return &CSSMOID_ECDSA_WithSHA1
;
1996 case SSL_HashAlgorithmSHA256
:
1997 return &CSSMOID_ECDSA_WithSHA256
;
1998 case SSL_HashAlgorithmSHA384
:
1999 return &CSSMOID_ECDSA_WithSHA384
;
2011 static size_t SSLDecodeUint16(const uint8_t *p
)
2013 return (p
[0]<<8 | p
[1]);
2016 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
2018 p
[0] = (len
>> 8)&0xff;
2019 p
[1] = (len
& 0xff);
2023 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
2025 p
[0] = (len
>> 16)&0xff;
2026 p
[1] = (len
>> 8)&0xff;
2027 p
[2] = (len
& 0xff);
2033 uint64_t SSLDecodeUint64(const uint8_t *p
)
2036 for(int i
=0; i
<8; i
++) {
2043 #include <libDER/DER_CertCrl.h>
2044 #include <libDER/DER_Encode.h>
2045 #include <libDER/asn1Types.h>
2048 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
2050 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2052 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
2054 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
2056 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2057 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
2058 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
2064 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
2066 SecCertificateRef leafCert
= NULL
;
2067 SecCertificateRef issuer
= NULL
;
2068 CFDataRef issuerKeyHash
= NULL
;
2069 CFDataRef tbs_precert
= NULL
;
2070 CFMutableDataRef data
= NULL
;
2072 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2073 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2074 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2076 require(leafCert
, out
);
2077 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
2078 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
2079 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
2081 require(issuerKeyHash
, out
);
2082 require(tbs_precert
, out
);
2083 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2084 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2086 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2087 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
2088 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
2089 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
2092 CFReleaseSafe(issuerKeyHash
);
2093 CFReleaseSafe(tbs_precert
);
2097 /* If the 'sct' is valid, return the operator ID of the log that signed this sct.
2099 The SCT is valid if:
2100 - It decodes properly.
2101 - Its timestamp is less than 'verifyTime'.
2102 - It is signed by a log in 'trustedLogs'.
2103 - The signing log expiryTime (if any) is less than 'verifyTime' (entry_type==0) or 'issuanceTime' (entry_type==1).
2105 If the SCT is valid, the returned CFStringRef is the identifier for the log operator. That value is not retained.
2106 If the SCT is valid, '*validLogAtVerifyTime' is set to true if the log is not expired at 'verifyTime'
2108 If the SCT is not valid this function return NULL.
2110 static CFStringRef
get_valid_sct_operator(CFDataRef sct
, int entry_type
, CFDataRef entry
, CFAbsoluteTime verifyTime
, CFAbsoluteTime issuanceTime
, CFArrayRef trustedLogs
, bool *validLogAtVerifyTime
)
2113 const uint8_t *logID
;
2114 const uint8_t *timestampData
;
2116 size_t extensionsLen
;
2117 const uint8_t *extensionsData
;
2120 size_t signatureLen
;
2121 const uint8_t *signatureData
;
2122 CFStringRef result
= NULL
;
2123 SecKeyRef pubKey
= NULL
;
2124 uint8_t *signed_data
= NULL
;
2125 const SecAsn1Oid
*oid
= NULL
;
2128 const uint8_t *p
= CFDataGetBytePtr(sct
);
2129 size_t len
= CFDataGetLength(sct
);
2130 uint64_t vt
=(uint64_t)( verifyTime
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
2132 require(len
>=43, out
);
2134 version
= p
[0]; p
++; len
--;
2135 logID
= p
; p
+=32; len
-=32;
2136 timestampData
= p
; p
+=8; len
-=8;
2137 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2139 require(len
>=extensionsLen
, out
);
2140 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
2142 require(len
>=4, out
);
2143 hashAlg
=p
[0]; p
++; len
--;
2144 sigAlg
=p
[0]; p
++; len
--;
2145 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2146 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
2149 /* verify version: only v1(0) is supported */
2151 secerror("SCT version unsupported: %d\n", version
);
2155 /* verify timestamp not in the future */
2156 timestamp
= SSLDecodeUint64(timestampData
);
2157 if(timestamp
> vt
) {
2158 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
2165 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
2166 signed_data
= malloc(signed_data_len
);
2167 require(signed_data
, out
);
2170 *q
++ = 0; // certificate_timestamp
2171 memcpy(q
, timestampData
, 8); q
+=8;
2172 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
2173 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
2174 q
= SSLEncodeUint16(q
, extensionsLen
);
2175 memcpy(q
, extensionsData
, extensionsLen
);
2177 CFDataRef logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, NULL
);
2179 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
2180 const void *key_data
;
2181 if(!isDictionary(dict
)) return false;
2182 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
2183 if(!isData(key_data
)) return false;
2184 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
2185 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
2186 CFReleaseSafe(valueID
);
2189 require(logData
, out
);
2191 /* If an expiry date is specified, and is a valid CFDate, then we check it against issuanceTime or verifyTime */
2192 const void *expiry_date
;
2193 if(CFDictionaryGetValueIfPresent(logData
, CFSTR("expiry"), &expiry_date
) && isDate(expiry_date
)) {
2194 CFAbsoluteTime expiryTime
= CFDateGetAbsoluteTime(expiry_date
);
2195 if(entry_type
== 1) {/* pre-cert: check the validity of the log at issuanceTime */
2196 require(issuanceTime
<=expiryTime
, out
);
2198 require(verifyTime
<=expiryTime
, out
);
2200 *validLogAtVerifyTime
= (verifyTime
<=expiryTime
);
2202 *validLogAtVerifyTime
= true;
2205 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
2206 require(logKeyData
, out
); // This failing would be an internal logic error
2207 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
2208 require(pubKey
, out
);
2210 oid
= oidForSigAlg(hashAlg
, sigAlg
);
2213 algId
.algorithm
= *oid
;
2214 algId
.parameters
.Data
= NULL
;
2215 algId
.parameters
.Length
= 0;
2217 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
2218 result
= CFDictionaryGetValue(logData
, CFSTR("operator"));
2220 secerror("SCT signature failed (log=%@)\n", logData
);
2224 CFReleaseSafe(pubKey
);
2229 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
2231 CFMutableArrayRef SCTs
= NULL
;
2232 SecCertificateRef leafCert
= NULL
;
2233 SecCertificateRef issuer
= NULL
;
2234 CFArrayRef ocspResponsesData
= NULL
;
2235 SecOCSPRequestRef ocspRequest
= NULL
;
2237 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2238 require_quiet(ocspResponsesData
, out
);
2240 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2241 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2242 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2244 require(leafCert
, out
);
2245 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
2246 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
2248 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2251 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2252 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2253 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2254 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
2255 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
2256 if(ocspSingleResponse
) {
2257 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
2258 if(singleResponseSCTs
) {
2259 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
2260 CFRelease(singleResponseSCTs
);
2262 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
2264 SecOCSPResponseFinalize(ocspResponse
);
2268 if(CFArrayGetCount(SCTs
)==0) {
2269 CFReleaseNull(SCTs
);
2273 CFReleaseSafe(ocspResponsesData
);
2275 SecOCSPRequestFinalize(ocspRequest
);
2280 static void SecPolicyCheckCT(SecPVCRef pvc
, CFStringRef key
)
2282 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2283 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
2284 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
2285 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2286 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
2287 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
2288 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
2290 // This eventually contain the list of operators who validated the SCT.
2291 CFMutableSetRef operatorsValidatingEmbeddedScts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeSetCallBacks
);
2292 CFMutableSetRef operatorsValidatingExternalScts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeSetCallBacks
);
2294 __block
bool atLeastOneValidAtVerifyTime
= false;
2295 __block
int lifetime
; // in Months
2297 require(operatorsValidatingEmbeddedScts
, out
);
2298 require(operatorsValidatingExternalScts
, out
);
2300 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
2302 CFArrayForEach(embeddedScts
, ^(const void *value
){
2303 bool validLogAtVerifyTime
= false;
2304 CFStringRef
operator = get_valid_sct_operator(value
, 1, precertEntry
, pvc
->verifyTime
, SecCertificateNotValidBefore(leafCert
), trustedLogs
, &validLogAtVerifyTime
);
2305 if(operator) CFSetAddValue(operatorsValidatingEmbeddedScts
, operator);
2306 if(validLogAtVerifyTime
) atLeastOneValidAtVerifyTime
= true;
2311 CFArrayForEach(builderScts
, ^(const void *value
){
2312 bool validLogAtVerifyTime
= false;
2313 CFStringRef
operator = get_valid_sct_operator(value
, 0, x509Entry
, pvc
->verifyTime
, SecCertificateNotValidBefore(leafCert
), trustedLogs
, &validLogAtVerifyTime
);
2314 if(operator) CFSetAddValue(operatorsValidatingExternalScts
, operator);
2315 if(validLogAtVerifyTime
) atLeastOneValidAtVerifyTime
= true;
2320 CFArrayForEach(ocspScts
, ^(const void *value
){
2321 bool validLogAtVerifyTime
= false;
2322 CFStringRef
operator = get_valid_sct_operator(value
, 0, x509Entry
, pvc
->verifyTime
, SecCertificateNotValidBefore(leafCert
), trustedLogs
, &validLogAtVerifyTime
);
2323 if(operator) CFSetAddValue(operatorsValidatingExternalScts
, operator);
2324 if(validLogAtVerifyTime
) atLeastOneValidAtVerifyTime
= true;
2329 /* We now have 2 sets of operators that validated those SCTS, count them and make a final decision.
2331 is_ct = (A1 OR A2) AND B.
2333 A1: 2+ to 5+ SCTs from the cert from independent logs valid at issuance time
2334 (operatorsValidatingEmbeddedScts)
2335 A2: 2+ SCTs from external sources (OCSP stapled response and TLS extension)
2336 from independent logs valid at verify time. (operatorsValidatingExternalScts)
2337 B: All least one SCTs from a log valid at verify time.
2339 Policy is based on: https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxjZXJ0aWZpY2F0ZXRyYW5zcGFyZW5jeXxneDo0ODhjNGRlOTIyMzYwNTcz
2340 with one difference: we consider SCTs from OCSP and TLS extensions as a whole.
2341 It sounds like this is what Google will eventually do, per:
2342 https://groups.google.com/forum/?fromgroups#!topic/certificate-transparency/VdXuzA3TLWY
2346 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
2348 CFCalendarGetComponentDifference(zuluCalendar
,
2349 SecCertificateNotValidBefore(leafCert
),
2350 SecCertificateNotValidAfter(leafCert
),
2351 0, "M", &_lifetime
);
2352 lifetime
= _lifetime
;
2355 CFIndex requiredEmbeddedSctsCount
;
2357 if (lifetime
< 15) {
2358 requiredEmbeddedSctsCount
= 2;
2359 } else if (lifetime
<= 27) {
2360 requiredEmbeddedSctsCount
= 3;
2361 } else if (lifetime
<= 39) {
2362 requiredEmbeddedSctsCount
= 4;
2364 requiredEmbeddedSctsCount
= 5;
2367 pvc
->is_ct
= ((CFSetGetCount(operatorsValidatingEmbeddedScts
) >= requiredEmbeddedSctsCount
) ||
2368 (CFSetGetCount(operatorsValidatingExternalScts
) >= 2)
2369 ) && atLeastOneValidAtVerifyTime
;
2373 CFReleaseSafe(operatorsValidatingEmbeddedScts
);
2374 CFReleaseSafe(operatorsValidatingExternalScts
);
2375 CFReleaseSafe(builderScts
);
2376 CFReleaseSafe(embeddedScts
);
2377 CFReleaseSafe(ocspScts
);
2378 CFReleaseSafe(precertEntry
);
2379 CFReleaseSafe(trustedLogs
);
2380 CFReleaseSafe(x509Entry
);
2383 static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc
, CFStringRef key
)
2385 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2386 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2387 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2389 key_value
.data
= NULL
;
2390 key_value
.length
= 0;
2392 if (CFGetTypeID(value
) == CFDataGetTypeID())
2394 CFDataRef key_data
= (CFDataRef
)value
;
2395 key_value
.data
= (DERByte
*)CFDataGetBytePtr(key_data
);
2396 key_value
.length
= (DERSize
)CFDataGetLength(key_data
);
2398 for (ix
= 0; ix
< count
; ix
++) {
2399 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2400 policy_set_t policies
= policies_for_cert(cert
);
2402 if (policy_set_contains(policies
, &key_value
)) {
2406 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2411 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
2413 SecPVCSetCheckRevocation(pvc
);
2416 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
2418 SecPVCSetCheckRevocationResponseRequired(pvc
);
2421 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
2423 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
2428 /********************************************************
2429 ****************** SecRVCRef Functions *****************
2430 ********************************************************/
2432 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
2434 /* Revocation verification context. */
2435 struct OpaqueSecRVC
{
2436 /* Will contain the response data. */
2439 /* Pointer to the pvc for this revocation check. */
2442 /* The ocsp request we send to each responder. */
2443 SecOCSPRequestRef ocspRequest
;
2445 /* The freshest response we received so far, from stapling or cache or responder. */
2446 SecOCSPResponseRef ocspResponse
;
2448 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
2449 SecOCSPSingleResponseRef ocspSingleResponse
;
2451 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2454 /* Index in array returned by SecCertificateGetOCSPResponders() for current
2456 CFIndex responderIX
;
2458 /* URL of current responder. */
2461 /* Date until which this revocation status is valid. */
2462 CFAbsoluteTime nextUpdate
;
2466 typedef struct OpaqueSecRVC
*SecRVCRef
;
2468 static void SecRVCDelete(SecRVCRef rvc
) {
2469 secdebug("alloc", "%p", rvc
);
2470 asynchttp_free(&rvc
->http
);
2471 SecOCSPRequestFinalize(rvc
->ocspRequest
);
2472 if (rvc
->ocspResponse
) {
2473 SecOCSPResponseFinalize(rvc
->ocspResponse
);
2474 rvc
->ocspResponse
= NULL
;
2475 if (rvc
->ocspSingleResponse
) {
2476 SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2477 rvc
->ocspSingleResponse
= NULL
;
2482 /* Return the next responder we should contact for this rvc or NULL if we
2483 exhausted them all. */
2484 static CFURLRef
SecRVCGetNextResponder(SecRVCRef rvc
) {
2485 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2486 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2487 if (ocspResponders
) {
2488 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
2489 while (rvc
->responderIX
< responderCount
) {
2490 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
2492 CFStringRef scheme
= CFURLCopyScheme(responder
);
2494 /* We only support http and https responders currently. */
2495 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
2496 CFEqual(CFSTR("https"), scheme
));
2498 if (valid_responder
)
2506 /* Fire off an async http request for this certs revocation status, return
2507 false if request was queued, true if we're done. */
2508 static bool SecRVCFetchNext(SecRVCRef rvc
) {
2509 while ((rvc
->responder
= SecRVCGetNextResponder(rvc
))) {
2510 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
2514 secdebug("ocsp", "Sending http ocsp request for cert %ld", rvc
->certIX
);
2515 if (!asyncHttpPost(rvc
->responder
, request
, &rvc
->http
)) {
2516 /* Async request was posted, wait for reply. */
2526 /* Process a verified ocsp response for a given cert. Return true if the
2527 certificate status was obtained. */
2528 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
2531 switch (this->certStatus
) {
2533 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
2534 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
2535 in the info dictionary. */
2536 //cert.revokeCheckGood(true);
2537 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
2541 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
2542 /* @@@ Mark cert as revoked (with reason) at revocation date in
2543 the info dictionary, or perhaps we should use a different key per
2544 reason? That way a client using exceptions can ignore some but
2546 SInt32 reason
= this->crlReason
;
2547 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
2548 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
2550 if (rvc
->pvc
&& rvc
->pvc
->info
) {
2551 /* make the revocation reason available in the trust result */
2552 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
2554 CFRelease(cfreason
);
2558 /* not an error, no per-cert status, nothing here */
2559 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
2563 secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
2564 (int)this->certStatus
, rvc
->certIX
);
2572 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
2573 if (rvc
->ocspSingleResponse
) {
2574 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
2576 if (rvc
->ocspResponse
) {
2577 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
2581 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecRVCRef rvc
, CFAbsoluteTime verifyTime
) {
2583 SecCertificatePathRef issuer
= SecCertificatePathCopyFromParent(rvc
->pvc
->path
, rvc
->certIX
+ 1);
2584 SecCertificatePathRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
2588 if (signer
== issuer
) {
2589 /* We already know we trust issuer since it's the path we are
2590 trying to verify minus the leaf. */
2591 secdebug("ocsp", "ocsp responder: %@ response signed by issuer",
2596 "ocsp responder: %@ response signed by cert issued by issuer",
2598 /* @@@ Now check that we trust signer. */
2599 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
2600 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
2601 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
2602 CFRelease(ocspSigner
);
2603 struct OpaqueSecPVC ospvc
;
2604 SecPVCInit(&ospvc
, rvc
->pvc
->builder
, policies
, verifyTime
);
2605 CFRelease(policies
);
2606 SecPVCSetPath(&ospvc
, signer
, NULL
);
2607 SecPVCLeafChecks(&ospvc
);
2609 bool completed
= SecPVCPathChecks(&ospvc
);
2610 /* If completed is false we are waiting for a callback, this
2611 shouldn't happen since we aren't asking for details, no
2612 revocation checking is done. */
2614 ocspdErrorLog("SecPVCPathChecks unexpectedly started "
2616 /* @@@ assert() or abort here perhaps? */
2620 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
2624 /* @@@ We don't trust the cert so don't use this response. */
2625 ocspdErrorLog("ocsp response signed by certificate which "
2626 "does not satisfy ocspSigner policy");
2629 SecPVCDelete(&ospvc
);
2634 /* @@@ No signer found for this ocsp response, discard it. */
2635 secdebug("ocsp", "ocsp responder: %@ no signer found for response",
2640 #if DUMP_OCSPRESPONSES
2642 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
2643 rvc
->certIX
, (trusted
? "t" : "u"));
2644 secdumpdata(ocspResponse
->data
, buf
);
2650 static void SecRVCConsumeOCSPResponse(SecRVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
2651 SecOCSPSingleResponseRef sr
= NULL
;
2652 require_quiet(ocspResponse
, errOut
);
2653 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
2654 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
2655 secdebug("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
2656 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
2657 secdebug("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
2658 // Check if this response is fresher than any (cached) response we might still have in the rvc.
2659 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
2661 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
2662 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
2663 check whether the leaf was revoked (we are already checking the rest of
2665 /* Check the OCSP response signature and verify the response. */
2666 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
2667 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
2669 // If we get here, we have a properly signed ocsp response
2670 // but we haven't checked dates yet.
2672 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
2673 if (sr
->certStatus
== CS_Good
) {
2674 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
2675 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
2676 } else if (sr
->certStatus
== CS_Revoked
) {
2677 // Expire revoked responses when the subject certificate itself expires.
2678 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
));
2681 // Ok we like the new response, let's toss the old one.
2683 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
2685 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
2686 rvc
->ocspResponse
= ocspResponse
;
2687 ocspResponse
= NULL
;
2689 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2690 rvc
->ocspSingleResponse
= sr
;
2693 rvc
->done
= sr_valid
;
2696 if (sr
) SecOCSPSingleResponseDestroy(sr
);
2697 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2700 /* Callback from async http code after an ocsp response has been received. */
2701 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
2702 SecRVCRef rvc
= (SecRVCRef
)http
->info
;
2703 SecPVCRef pvc
= rvc
->pvc
;
2704 SecOCSPResponseRef ocspResponse
= NULL
;
2705 if (http
->response
) {
2706 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
2708 /* Parse the returned data as if it's an ocspResponse. */
2709 ocspResponse
= SecOCSPResponseCreate(data
);
2714 SecRVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
2715 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
2718 /* Clear the data for the next response. */
2719 asynchttp_free(http
);
2720 SecRVCFetchNext(rvc
);
2724 SecRVCUpdatePVC(rvc
);
2726 if (!--pvc
->asyncJobCount
) {
2727 SecPathBuilderStep(pvc
->builder
);
2732 static void SecRVCInit(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
2733 secdebug("alloc", "%p", rvc
);
2735 rvc
->certIX
= certIX
;
2736 rvc
->http
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
2737 rvc
->http
.token
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2738 rvc
->http
.completed
= SecOCSPFetchCompleted
;
2739 rvc
->http
.info
= rvc
;
2740 rvc
->ocspRequest
= NULL
;
2741 rvc
->responderIX
= 0;
2742 rvc
->responder
= NULL
;
2743 rvc
->nextUpdate
= NULL_TIME
;
2744 rvc
->ocspResponse
= NULL
;
2745 rvc
->ocspSingleResponse
= NULL
;
2750 static bool SecPVCCheckRevocation(SecPVCRef pvc
) {
2751 secdebug("ocsp", "checking revocation");
2752 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2753 bool completed
= true;
2754 if (certCount
<= 1) {
2755 /* Can't verify without an issuer; we're done */
2758 if (!SecPVCIsAnchored(pvc
)) {
2759 /* We can't check revocation for chains without a trusted anchor. */
2765 /* TODO: Implement getting this value from the client.
2766 Optional responder passed in though policy. */
2767 CFURLRef localResponder
= NULL
;
2768 /* Generate a nonce in outgoing request if true. */
2769 bool genNonce
= false;
2770 /* Require a nonce in response if true. */
2771 bool requireRespNonce
= false;
2772 bool cacheReadDisable
= false;
2773 bool cacheWriteDisable
= false;
2777 /* We have done revocation checking already, we're done. */
2778 secdebug("ocsp", "Not rechecking revocation");
2782 /* Setup things so we check revocation status of all certs except the
2784 pvc
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
2787 /* Lookup cached revocation data for each certificate. */
2788 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
2789 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2790 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2791 if (ocspResponders
) {
2792 /* First look though passed in ocsp responses. */
2793 //SecPVCGetOCSPResponseForCertificateAtIndex(pvc, ix, singleResponse);
2795 /* Then look though shared cache (we don't care which responder
2796 something came from here). */
2797 CFDataRef ocspResponse
= SecOCSPCacheCopyMatching(SecCertIDRef certID
, NULL
);
2799 /* Now let's parse the response. */
2800 if (decodeOCSPResponse(ocspResp
)) {
2801 secdebug("ocsp", "response ok: %@", ocspResp
);
2803 secdebug("ocsp", "response bad: %@", ocspResp
);
2804 /* ocsp response not ok. */
2805 if (!SecPVCSetResultForced(pvc
, key
, ix
, kCFBooleanFalse
, true))
2808 CFReleaseSafe(ocspResp
);
2810 /* Check if certificate has any crl distributionPoints. */
2811 CFArrayRef distributionPoints
= SecCertificateGetCRLDistributionPoints(cert
);
2812 if (distributionPoints
) {
2813 /* Look for a cached CRL and potentially delta CRL for this certificate. */
2819 /* Note that if we are multi threaded and a job completes after it
2820 is started but before we return from this function, we don't want
2821 a callback to decrement asyncJobCount to zero before we finish issuing
2822 all the jobs. To avoid this we pretend we issued certCount async jobs,
2823 and decrement pvc->asyncJobCount for each cert that we don't start a
2824 background fetch for. */
2825 pvc
->asyncJobCount
= (unsigned int) certCount
;
2827 /* Loop though certificates again and issue an ocsp fetch if the
2828 revocation status checking isn't done yet. */
2829 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
2830 secdebug("ocsp", "checking revocation for cert: %ld", certIX
);
2831 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
2832 SecRVCInit(rvc
, pvc
, certIX
);
2836 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
,
2838 /* The certIX + 1 is ok here since certCount is always at least 1
2839 less than the actual number of certs. */
2840 SecCertificateRef issuer
= SecPVCGetCertificateAtIndex(rvc
->pvc
,
2843 rvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
2845 /* Get stapled OCSP responses */
2846 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2848 /* If we have any OCSP stapled responses, check those first */
2849 if(ocspResponsesData
) {
2850 secdebug("ocsp", "Checking stapled responses for cert %ld", certIX
);
2851 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2852 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2853 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2854 SecRVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
2856 CFRelease(ocspResponsesData
);
2859 /* Then check the cached response */
2860 secdebug("ocsp", "Checking cached responses for cert %ld", certIX
);
2861 SecRVCConsumeOCSPResponse(rvc
, SecOCSPCacheCopyMatching(rvc
->ocspRequest
, NULL
), NULL_TIME
, false);
2863 /* Unless we successfully checked the revocation status of this cert
2864 based on the cache or stapled responses, attempt to fire off an async http request
2865 for this cert's revocation status. */
2866 bool fetch_done
= true;
2867 if (rvc
->done
|| !SecPathBuilderCanAccessNetwork(pvc
->builder
) ||
2868 (fetch_done
= SecRVCFetchNext(rvc
))) {
2869 /* We got a cache hit or we aren't allowed to access the network,
2870 or the async http post failed. */
2871 SecRVCUpdatePVC(rvc
);
2873 /* We didn't really start a background job for this cert. */
2874 pvc
->asyncJobCount
--;
2875 } else if (!fetch_done
) {
2876 /* We started at least one background fetch. */
2881 /* Return false if we started any background jobs. */
2882 /* We can't just return !pvc->asyncJobCount here, since if we started any
2883 jobs the completion callback will be called eventually and it will call
2884 SecPathBuilderStep(). If for some reason everything completed before we
2885 get here we still want the outer SecPathBuilderStep() to terminate so we
2886 keep track of whether we started any jobs and return false if so. */
2891 void SecPolicyServerInitalize(void) {
2892 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2893 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2894 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2895 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2897 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2898 kSecPolicyCheckBasicCertificateProcessing
,
2899 SecPolicyCheckBasicCertificateProcessing
);
2900 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2901 kSecPolicyCheckCriticalExtensions
,
2902 SecPolicyCheckCriticalExtensions
);
2903 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2904 kSecPolicyCheckIdLinkage
,
2905 SecPolicyCheckIdLinkage
);
2906 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2907 kSecPolicyCheckKeyUsage
,
2908 SecPolicyCheckKeyUsage
);
2909 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2910 kSecPolicyCheckExtendedKeyUsage
,
2911 SecPolicyCheckExtendedKeyUsage
);
2912 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2913 kSecPolicyCheckBasicContraints
,
2914 SecPolicyCheckBasicContraints
);
2915 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2916 kSecPolicyCheckNonEmptySubject
,
2917 SecPolicyCheckNonEmptySubject
);
2918 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2919 kSecPolicyCheckQualifiedCertStatements
,
2920 SecPolicyCheckQualifiedCertStatements
);
2921 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2922 kSecPolicyCheckSSLHostname
,
2923 SecPolicyCheckSSLHostname
);
2924 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2925 kSecPolicyCheckEmail
,
2926 SecPolicyCheckEmail
);
2927 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2928 kSecPolicyCheckValidIntermediates
,
2929 SecPolicyCheckValidIntermediates
);
2930 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2931 kSecPolicyCheckValidLeaf
,
2932 SecPolicyCheckValidLeaf
);
2933 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2934 kSecPolicyCheckValidRoot
,
2935 SecPolicyCheckValidRoot
);
2936 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2937 kSecPolicyCheckIssuerCommonName
,
2938 SecPolicyCheckIssuerCommonName
);
2939 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2940 kSecPolicyCheckSubjectCommonNamePrefix
,
2941 SecPolicyCheckSubjectCommonNamePrefix
);
2942 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2943 kSecPolicyCheckSubjectCommonName
,
2944 SecPolicyCheckSubjectCommonName
);
2945 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2946 kSecPolicyCheckNotValidBefore
,
2947 SecPolicyCheckNotValidBefore
);
2948 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2949 kSecPolicyCheckChainLength
,
2950 SecPolicyCheckChainLength
);
2951 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2952 kSecPolicyCheckAnchorSHA1
,
2953 SecPolicyCheckAnchorSHA1
);
2954 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2955 kSecPolicyCheckIntermediateSPKISHA256
,
2956 SecPolicyCheckIntermediateSPKISHA256
);
2957 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2958 kSecPolicyCheckAnchorApple
,
2959 SecPolicyCheckAnchorApple
);
2960 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2961 kSecPolicyCheckSubjectOrganization
,
2962 SecPolicyCheckSubjectOrganization
);
2963 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2964 kSecPolicyCheckSubjectOrganizationalUnit
,
2965 SecPolicyCheckSubjectOrganizationalUnit
);
2966 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2967 kSecPolicyCheckEAPTrustedServerNames
,
2968 SecPolicyCheckEAPTrustedServerNames
);
2969 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2970 kSecPolicyCheckSubjectCommonNameTEST
,
2971 SecPolicyCheckSubjectCommonNameTEST
);
2972 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2973 kSecPolicyCheckRevocation
,
2974 SecPolicyCheckRevocation
);
2975 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2976 kSecPolicyCheckRevocationResponseRequired
,
2977 SecPolicyCheckRevocationResponseRequired
);
2978 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2979 kSecPolicyCheckNoNetworkAccess
,
2980 SecPolicyCheckNoNetworkAccess
);
2981 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2982 kSecPolicyCheckBlackListedLeaf
,
2983 SecPolicyCheckBlackListedLeaf
);
2984 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2985 kSecPolicyCheckGrayListedLeaf
,
2986 SecPolicyCheckGrayListedLeaf
);
2987 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2988 kSecPolicyCheckLeafMarkerOid
,
2989 SecPolicyCheckLeafMarkerOid
);
2990 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2991 kSecPolicyCheckIntermediateMarkerOid
,
2992 SecPolicyCheckIntermediateMarkerOid
);
2993 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2994 kSecPolicyCheckCertificatePolicy
,
2995 SecPolicyCheckCertificatePolicyOid
);
2998 /* AUDIT[securityd](done):
2999 array (ok) is a caller provided array, only its cf type has
3001 The options (ok) field ends up in policy->_options unchecked, so every access
3002 of policy->_options needs to be validated.
3004 static SecPolicyRef
SecPolicyCreateWithArray(CFArrayRef array
) {
3005 SecPolicyRef policy
= NULL
;
3006 require_quiet(array
&& CFArrayGetCount(array
) == 2, errOut
);
3007 CFStringRef oid
= (CFStringRef
)CFArrayGetValueAtIndex(array
, 0);
3008 require_quiet(isString(oid
), errOut
);
3009 CFDictionaryRef options
= (CFDictionaryRef
)CFArrayGetValueAtIndex(array
, 1);
3010 require_quiet(isDictionary(options
), errOut
);
3011 policy
= SecPolicyCreate(oid
, options
);
3016 /* AUDIT[securityd](done):
3017 value (ok) is an element in a caller provided array.
3019 static void deserializePolicy(const void *value
, void *context
) {
3020 CFArrayRef policyArray
= (CFArrayRef
)value
;
3021 if (isArray(policyArray
)) {
3022 CFTypeRef deserializedPolicy
= SecPolicyCreateWithArray(policyArray
);
3023 if (deserializedPolicy
) {
3024 CFArrayAppendValue((CFMutableArrayRef
)context
, deserializedPolicy
);
3025 CFRelease(deserializedPolicy
);
3030 /* AUDIT[securityd](done):
3031 serializedPolicies (ok) is a caller provided array, only its cf type has
3034 CFArrayRef
SecPolicyArrayDeserialize(CFArrayRef serializedPolicies
) {
3035 CFMutableArrayRef result
= NULL
;
3036 require_quiet(isArray(serializedPolicies
), errOut
);
3037 CFIndex count
= CFArrayGetCount(serializedPolicies
);
3038 result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
3039 CFRange all_policies
= { 0, count
};
3040 CFArrayApplyFunction(serializedPolicies
, all_policies
, deserializePolicy
, result
);
3047 /********************************************************
3048 ****************** SecPVCRef Functions *****************
3049 ********************************************************/
3051 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
,
3052 CFAbsoluteTime verifyTime
) {
3053 secdebug("alloc", "%p", pvc
);
3054 // Weird logging policies crashes.
3055 //secdebug("policy", "%@", policies);
3056 pvc
->builder
= builder
;
3057 pvc
->policies
= policies
;
3060 pvc
->verifyTime
= verifyTime
;
3062 pvc
->details
= NULL
;
3064 pvc
->valid_policy_tree
= NULL
;
3065 pvc
->callbacks
= NULL
;
3068 pvc
->asyncJobCount
= 0;
3069 pvc
->check_revocation
= false;
3070 pvc
->response_required
= false;
3071 pvc
->optionally_ev
= false;
3076 static void SecPVCDeleteRVCs(SecPVCRef pvc
) {
3077 secdebug("alloc", "%p", pvc
);
3084 void SecPVCDelete(SecPVCRef pvc
) {
3085 secdebug("alloc", "%p", pvc
);
3086 CFReleaseNull(pvc
->policies
);
3087 CFReleaseNull(pvc
->details
);
3088 CFReleaseNull(pvc
->info
);
3089 if (pvc
->valid_policy_tree
) {
3090 policy_tree_prune(&pvc
->valid_policy_tree
);
3092 SecPVCDeleteRVCs(pvc
);
3095 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathRef path
,
3096 CF_CONSUMED CFArrayRef details
) {
3097 secdebug("policy", "%@", path
);
3098 if (pvc
->path
!= path
) {
3099 /* Changing path makes us clear the Revocation Verification Contexts */
3100 SecPVCDeleteRVCs(pvc
);
3103 pvc
->details
= details
;
3104 CFReleaseNull(pvc
->info
);
3105 if (pvc
->valid_policy_tree
) {
3106 policy_tree_prune(&pvc
->valid_policy_tree
);
3112 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
3113 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
3116 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
3117 return SecCertificatePathGetCount(pvc
->path
);
3120 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
3121 return SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3124 bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc
, CFIndex ix
) {
3125 return SecCertificatePathSelfSignedIndex(pvc
->path
) == ix
;
3128 void SecPVCSetCheckRevocation(SecPVCRef pvc
) {
3129 pvc
->check_revocation
= true;
3130 secdebug("ocsp", "deferred revocation checking enabled");
3133 void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc
) {
3134 pvc
->response_required
= true;
3135 secdebug("ocsp", "revocation response required");
3138 bool SecPVCIsAnchored(SecPVCRef pvc
) {
3139 return SecCertificatePathIsAnchored(pvc
->path
);
3142 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
3143 return pvc
->verifyTime
;
3146 /* AUDIT[securityd](done):
3147 policy->_options is a caller provided dictionary, only its cf type has
3150 bool SecPVCSetResultForced(SecPVCRef pvc
,
3151 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
3153 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
3154 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
3155 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
3157 (force
? "force" : ""), result
);
3159 /* If this is not something the current policy cares about ignore
3160 this error and return true so our caller continues evaluation. */
3162 /* @@@ The right long term fix might be to check if none of the passed
3163 in policies contain this key, since not all checks are run for all
3165 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3166 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
3170 /* @@@ Check to see if the SecTrustSettings for the certificate in question
3171 tell us to ignore this error. */
3172 pvc
->result
= false;
3176 CFMutableDictionaryRef detail
=
3177 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
3179 /* Perhaps detail should have an array of results per key? As it stands
3180 in the case of multiple policy failures the last failure stands. */
3181 CFDictionarySetValue(detail
, key
, result
);
3186 bool SecPVCSetResult(SecPVCRef pvc
,
3187 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
3188 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
3191 /* AUDIT[securityd](done):
3192 key(ok) is a caller provided.
3193 value(ok, unused) is a caller provided.
3195 static void SecPVCValidateKey(const void *key
, const void *value
,
3197 SecPVCRef pvc
= (SecPVCRef
)context
;
3199 /* If our caller doesn't want full details and we failed earlier there is
3200 no point in doing additional checks. */
3201 if (!pvc
->result
&& !pvc
->details
)
3204 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
3205 CFDictionaryGetValue(pvc
->callbacks
, key
);
3209 /* Why not to have optional policy checks rant:
3210 Not all keys are in all dictionaries anymore, so why not make checks
3211 optional? This way a client can ask for something and the server will
3212 do a best effort based on the supported flags. It works since they are
3213 synchronized now, but we need some debug checking here for now. */
3214 pvc
->result
= false;
3216 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
3217 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
3218 pvc
->result
= false;
3220 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
3221 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
3222 pvc
->result
= false;
3225 /* Non standard validation phase, nothing is optional. */
3226 pvc
->result
= false;
3231 fcn(pvc
, (CFStringRef
)key
);
3234 /* AUDIT[securityd](done):
3235 policy->_options is a caller provided dictionary, only its cf type has
3238 bool SecPVCLeafChecks(SecPVCRef pvc
) {
3240 CFArrayRef policies
= pvc
->policies
;
3241 CFIndex ix
, count
= CFArrayGetCount(policies
);
3242 for (ix
= 0; ix
< count
; ++ix
) {
3243 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3245 /* Validate all keys for all policies. */
3246 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3247 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3248 if (!pvc
->result
&& !pvc
->details
)
3255 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
3256 /* Check stuff common to intermediate and anchors. */
3257 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
3258 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3259 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3260 && SecPVCIsAnchored(pvc
));
3261 if (!SecCertificateIsValid(cert
, verifyTime
)) {
3262 /* Certificate has expired. */
3263 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckValidRoot
3264 : kSecPolicyCheckValidIntermediates
, ix
, kCFBooleanFalse
))
3269 /* Perform anchor specific checks. */
3270 /* Don't think we have any of these. */
3272 /* Perform intermediate specific checks. */
3275 const SecCEBasicConstraints
*bc
=
3276 SecCertificateGetBasicConstraints(cert
);
3277 if (!bc
|| !bc
->isCA
) {
3278 /* Basic constraints not present or not marked as isCA, illegal. */
3279 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicContraints
,
3280 ix
, kCFBooleanFalse
, true))
3283 /* Consider adding (l) max_path_length checking here. */
3285 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3286 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3287 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3288 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3289 ix
, kCFBooleanFalse
, true))
3298 bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3299 /* Check stuff common to intermediate and anchors. */
3301 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3302 if (NULL
!= otapkiRef
)
3304 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3305 CFRelease(otapkiRef
);
3306 if (NULL
!= blackListedKeys
)
3308 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3309 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3310 && SecPVCIsAnchored(pvc
));
3312 /* Check for blacklisted intermediates keys. */
3313 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3315 /* Check dgst against blacklist. */
3316 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3317 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3318 ix
, kCFBooleanFalse
, true);
3323 CFRelease(blackListedKeys
);
3331 bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3333 /* Check stuff common to intermediate and anchors. */
3334 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3335 if (NULL
!= otapkiRef
)
3337 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3338 CFRelease(otapkiRef
);
3339 if (NULL
!= grayListKeys
)
3341 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3342 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3343 && SecPVCIsAnchored(pvc
));
3345 /* Check for gray listed intermediates keys. */
3346 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3348 /* Check dgst against gray list. */
3349 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3350 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3351 ix
, kCFBooleanFalse
, true);
3356 CFRelease(grayListKeys
);
3364 /* AUDIT[securityd](done):
3365 policy->_options is a caller provided dictionary, only its cf type has
3368 bool SecPVCPathChecks(SecPVCRef pvc
) {
3369 secdebug("policy", "begin path: %@", pvc
->path
);
3370 bool completed
= true;
3371 /* This needs to be initialized before we call any function that might call
3372 SecPVCSetResultForced(). */
3374 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3375 if (pvc
->result
|| pvc
->details
) {
3376 SecPolicyCheckBasicCertificateProcessing(pvc
,
3377 kSecPolicyCheckBasicCertificateProcessing
);
3380 CFArrayRef policies
= pvc
->policies
;
3381 CFIndex count
= CFArrayGetCount(policies
);
3382 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3383 /* Validate all keys for all policies. */
3384 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3385 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3386 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3387 if (!pvc
->result
&& !pvc
->details
)
3391 /* Check the things we can't check statically for the certificate path. */
3392 /* Critical Extensions, chainLength. */
3396 if ((pvc
->result
|| pvc
->details
) && pvc
->optionally_ev
) {
3397 bool pre_ev_check_result
= pvc
->result
;
3398 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3399 pvc
->is_ev
= pvc
->result
;
3400 /* If ev checking failed, we still want to accept this chain
3401 as a non EV one, if it was valid as such. */
3402 pvc
->result
= pre_ev_check_result
;
3404 /* Check revocation only if the chain is valid so far. Then only check
3405 revocation if the client asked for it explicitly or is_ev is
3407 if (pvc
->result
&& (pvc
->is_ev
|| pvc
->check_revocation
)) {
3408 completed
= SecPVCCheckRevocation(pvc
);
3412 if (pvc
->result
|| pvc
->details
) {
3413 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3414 SecPolicyCheckCT(pvc
, kSecPolicyCheckCertificateTransparency
);
3419 secdebug("policy", "end %strusted completed: %d path: %@",
3420 (pvc
->result
? "" : "not "), completed
, pvc
->path
);
3424 /* This function returns 0 to indicate revocation checking was not completed
3425 for this certificate chain, otherwise return to date at which the first
3426 piece of revocation checking info we used expires. */
3427 CFAbsoluteTime
SecPVCGetEarliestNextUpdate(SecPVCRef pvc
) {
3428 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3429 CFAbsoluteTime enu
= 0;
3430 if (certCount
<= 1 || !pvc
->rvcs
) {
3435 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3436 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3437 if (rvc
->nextUpdate
== 0) {
3439 /* We allow for CA certs to not be revocation checked if they
3440 have no ocspResponders to check against, but the leaf
3441 must be checked in order for us to claim we did revocation
3443 SecCertificateRef cert
=
3444 SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3445 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
3446 if (!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) {
3447 /* We can't check this cert so we don't consider it a soft
3448 failure that we didn't. Ideally we should support crl
3449 checking and remove this workaround, since that more
3454 secdebug("ocsp", "revocation checking soft failure for cert: %ld",
3456 enu
= rvc
->nextUpdate
;
3459 if (enu
== 0 || rvc
->nextUpdate
< enu
) {
3460 enu
= rvc
->nextUpdate
;
3463 /* Perhaps we don't want to do this since some policies might
3464 ignore the certificate expiration but still use revocation
3467 /* Earliest certificate expiration date. */
3468 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3469 CFAbsoluteTime nva
= SecCertificateNotValidAfter(cert
);
3470 if (nva
&& (enu
== 0 || nva
< enu
)
3475 secdebug("ocsp", "revocation valid until: %lg", enu
);