2 * Copyright (c) 2015-2017 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 * nameconstraints.c - rfc5280 section 4.2.1.10 and later name constraints implementation.
28 #include "nameconstraints.h"
29 #include <AssertMacros.h>
30 #include <utilities/SecCFWrappers.h>
31 #include <Security/SecCertificateInternal.h>
32 #include <securityd/SecPolicyServer.h>
33 #include <libDER/asn1Types.h>
34 #include <libDER/oids.h>
36 /* RFC 5280 Section 4.2.1.10:
37 DNS name restrictions are expressed as host.example.com. Any DNS
38 name that can be constructed by simply adding zero or more labels to
39 the left-hand side of the name satisfies the name constraint. For
40 example, www.host.example.com would satisfy the constraint but
41 host1.example.com would not.
43 static bool SecDNSNameConstraintsMatch(CFStringRef DNSName
, CFStringRef constraint
) {
44 CFIndex clength
= CFStringGetLength(constraint
);
45 CFIndex dlength
= CFStringGetLength(DNSName
);
47 if (dlength
< clength
) return false;
49 /* Ensure that character to the left of the constraint in the DNSName is a '.'
50 so that badexample.com does not match example.com, but good.example.com does.
52 if ((dlength
!= clength
) && ('.' != CFStringGetCharacterAtIndex(constraint
, 0)) &&
53 ('.' != CFStringGetCharacterAtIndex(DNSName
, dlength
- clength
-1))) {
57 CFRange compareRange
= { dlength
- clength
, clength
};
59 if (!CFStringCompareWithOptions(DNSName
, constraint
, compareRange
, kCFCompareCaseInsensitive
)) {
66 /* RFC 5280 Section 4.2.1.10:
67 For URIs, the constraint applies to the host part of the name. The
68 constraint MUST be specified as a fully qualified domain name and MAY
69 specify a host or a domain. Examples would be "host.example.com" and
70 ".example.com". When the constraint begins with a period, it MAY be
71 expanded with one or more labels. That is, the constraint
72 ".example.com" is satisfied by both host.example.com and
73 my.host.example.com. However, the constraint ".example.com" is not
74 satisfied by "example.com". When the constraint does not begin with
75 a period, it specifies a host.
77 static bool SecURIMatch(CFStringRef URI
, CFStringRef hostname
) {
79 CFStringRef URI_hostname
= NULL
;
80 CFCharacterSetRef port_or_path_separator
= NULL
;
81 /* URI must have scheme specified */
82 CFRange URI_scheme
= CFStringFind(URI
, CFSTR("://"), 0);
83 require_quiet(URI_scheme
.location
!= kCFNotFound
, out
);
85 /* Remove scheme prefix and port or resource path suffix */
86 CFRange URI_hostname_range
= { URI_scheme
.location
+ URI_scheme
.length
,
87 CFStringGetLength(URI
) - URI_scheme
.location
- URI_scheme
.length
};
88 port_or_path_separator
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, CFSTR(":/"));
89 CFRange separator
= {kCFNotFound
, 0};
90 if(CFStringFindCharacterFromSet(URI
, port_or_path_separator
, URI_hostname_range
, 0, &separator
)) {
91 URI_hostname_range
.length
-= (CFStringGetLength(URI
) - separator
.location
);
93 URI_hostname
= CFStringCreateWithSubstring(kCFAllocatorDefault
, URI
, URI_hostname_range
);
95 /* Hostname in URI must not begin with '.' */
96 require_quiet('.' != CFStringGetCharacterAtIndex(URI_hostname
, 0), out
);
98 CFIndex ulength
= CFStringGetLength(URI_hostname
);
99 CFIndex hlength
= CFStringGetLength(hostname
);
100 require_quiet(ulength
>= hlength
, out
);
101 CFRange compare_range
= { 0, hlength
};
103 /* Allow one or more preceding labels */
104 if ('.' == CFStringGetCharacterAtIndex(hostname
, 0)) {
105 compare_range
.location
= ulength
- hlength
;
108 if(kCFCompareEqualTo
== CFStringCompareWithOptions(URI_hostname
,
111 kCFCompareCaseInsensitive
)) {
116 CFReleaseNull(port_or_path_separator
);
117 CFReleaseNull(URI_hostname
);
121 /* RFC 5280 Section 4.2.1.10:
122 A name constraint for Internet mail addresses MAY specify a
123 particular mailbox, all addresses at a particular host, or all
124 mailboxes in a domain. To indicate a particular mailbox, the
125 constraint is the complete mail address. For example,
126 "root@example.com" indicates the root mailbox on the host
127 "example.com". To indicate all Internet mail addresses on a
128 particular host, the constraint is specified as the host name. For
129 example, the constraint "example.com" is satisfied by any mail
130 address at the host "example.com". To specify any address within a
131 domain, the constraint is specified with a leading period (as with
134 static bool SecRFC822NameMatch(CFStringRef emailAddress
, CFStringRef constraint
) {
135 CFRange mailbox_range
= CFStringFind(constraint
,CFSTR("@"),0);
137 /* Constraint specifies a particular mailbox. Perform full comparison. */
138 if (mailbox_range
.location
!= kCFNotFound
) {
139 if (!CFStringCompare(emailAddress
, constraint
, kCFCompareCaseInsensitive
)) {
145 mailbox_range
= CFStringFind(emailAddress
, CFSTR("@"), 0);
146 require_quiet(mailbox_range
.location
!= kCFNotFound
, out
);
147 CFRange hostname_range
= {mailbox_range
.location
+ 1,
148 CFStringGetLength(emailAddress
) - mailbox_range
.location
- 1 };
150 /* Constraint specificies a particular host. Compare hostname of address. */
151 if ('.' != CFStringGetCharacterAtIndex(constraint
, 0)) {
152 if (!CFStringCompareWithOptions(emailAddress
, constraint
, hostname_range
, kCFCompareCaseInsensitive
)) {
158 /* Constraint specificies a domain. Match hostname of address to domain name. */
159 require_quiet('.' != CFStringGetCharacterAtIndex(emailAddress
, mailbox_range
.location
+1), out
);
160 if (CFStringHasSuffix(emailAddress
, constraint
)) {
168 static bool nc_compare_directoryNames(const DERItem
*certName
, const DERItem
*subtreeName
) {
169 /* Get content of certificate name and subtree name */
170 DERDecodedInfo certName_content
;
171 require_noerr_quiet(DERDecodeItem(certName
, &certName_content
), out
);
173 DERDecodedInfo subtreeName_content
;
174 require_noerr_quiet(DERDecodeItem(subtreeName
, &subtreeName_content
), out
);
176 if (certName
->length
> subtreeName
->length
) {
177 if(0 == memcmp(certName_content
.content
.data
,
178 subtreeName_content
.content
.data
,
179 subtreeName_content
.content
.length
)) {
188 static bool nc_compare_DNSNames(const DERItem
*certName
, const DERItem
*subtreeName
) {
190 CFStringRef certName_str
= CFStringCreateWithBytes(kCFAllocatorDefault
,
191 certName
->data
, certName
->length
,
192 kCFStringEncodingUTF8
, FALSE
);
193 CFStringRef subtreeName_str
= CFStringCreateWithBytes(kCFAllocatorDefault
,
194 subtreeName
->data
, subtreeName
->length
,
195 kCFStringEncodingUTF8
, FALSE
);
196 require_quiet(certName_str
, out
);
197 require_quiet(subtreeName_str
, out
);
199 if (SecDNSNameConstraintsMatch(certName_str
, subtreeName_str
)) {
204 CFReleaseNull(certName_str
) ;
205 CFReleaseNull(subtreeName_str
);
209 static bool nc_compare_URIs(const DERItem
*certName
, const DERItem
*subtreeName
) {
211 CFStringRef certName_str
= CFStringCreateWithBytes(kCFAllocatorDefault
,
212 certName
->data
, certName
->length
,
213 kCFStringEncodingUTF8
, FALSE
);
214 CFStringRef subtreeName_str
= CFStringCreateWithBytes(kCFAllocatorDefault
,
215 subtreeName
->data
, subtreeName
->length
,
216 kCFStringEncodingUTF8
, FALSE
);
217 require_quiet(certName_str
, out
);
218 require_quiet(subtreeName_str
, out
);
220 if (SecURIMatch(certName_str
, subtreeName_str
)) {
225 CFReleaseNull(certName_str
);
226 CFReleaseNull(subtreeName_str
);
230 static bool nc_compare_RFC822Names(const DERItem
*certName
, const DERItem
*subtreeName
) {
232 CFStringRef certName_str
= CFStringCreateWithBytes(kCFAllocatorDefault
,
233 certName
->data
, certName
->length
,
234 kCFStringEncodingUTF8
, FALSE
);
235 CFStringRef subtreeName_str
= CFStringCreateWithBytes(kCFAllocatorDefault
,
236 subtreeName
->data
, subtreeName
->length
,
237 kCFStringEncodingUTF8
, FALSE
);
238 require_quiet(certName_str
, out
);
239 require_quiet(subtreeName_str
, out
);
241 if (SecRFC822NameMatch(certName_str
, subtreeName_str
)) {
246 CFReleaseNull(certName_str
);
247 CFReleaseNull(subtreeName_str
);
251 static bool nc_compare_IPAddresses(const DERItem
*certAddr
, const DERItem
*subtreeAddr
) {
254 /* Verify Subtree Address has correct number of bytes for IP and mask */
255 require_quiet((subtreeAddr
->length
== 8) || (subtreeAddr
->length
== 32), out
);
256 /* Verify Cert Address has correct number of bytes for IP */
257 require_quiet((certAddr
->length
== 4) || (certAddr
->length
==16), out
);
258 /* Verify Subtree Address and Cert Address are the same version */
259 require_quiet(subtreeAddr
->length
== 2*certAddr
->length
, out
);
261 DERByte
* mask
= subtreeAddr
->data
+ certAddr
->length
;
262 for (DERSize i
= 0; i
< certAddr
->length
; i
++) {
263 if((subtreeAddr
->data
[i
] & mask
[i
]) != (certAddr
->data
[i
] & mask
[i
])) {
279 const SecCEGeneralNameType gnType
;
280 const DERItem
*cert_item
;
282 } nc_match_context_t
;
285 const CFArrayRef subtrees
;
288 } nc_san_match_context_t
;
290 static OSStatus
nc_compare_subtree(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
291 nc_match_context_t
*item_context
= context
;
292 if (item_context
&& gnType
== item_context
->gnType
293 && item_context
->match
&& item_context
->cert_item
) {
295 item_context
->match
->present
= true;
297 * We set isMatch such that if there are multiple subtrees of the same type, matching to any one
298 * of them is considered a match.
301 case GNT_DirectoryName
: {
302 item_context
->match
->isMatch
|= nc_compare_directoryNames(item_context
->cert_item
, generalName
);
303 return errSecSuccess
;
306 item_context
->match
->isMatch
|= nc_compare_DNSNames(item_context
->cert_item
, generalName
);
307 return errSecSuccess
;
310 item_context
->match
->isMatch
|= nc_compare_URIs(item_context
->cert_item
, generalName
);
311 return errSecSuccess
;
313 case GNT_RFC822Name
: {
314 item_context
->match
->isMatch
|= nc_compare_RFC822Names(item_context
->cert_item
, generalName
);
315 return errSecSuccess
;
317 case GNT_IPAddress
: {
318 item_context
->match
->isMatch
|= nc_compare_IPAddresses(item_context
->cert_item
, generalName
);
319 return errSecSuccess
;
322 /* If the name form is not supported, reject the certificate. */
323 return errSecInvalidCertificate
;
328 return errSecInvalidCertificate
;
331 static void nc_decode_and_compare_subtree(const void *value
, void *context
) {
332 CFDataRef subtree
= value
;
333 nc_match_context_t
*match_context
= context
;
335 /* convert subtree to DERItem */
336 const DERItem general_name
= { (unsigned char *)CFDataGetBytePtr(subtree
), CFDataGetLength(subtree
) };
337 DERDecodedInfo general_name_content
;
338 require_noerr_quiet(DERDecodeItem(&general_name
, &general_name_content
),out
);
340 OSStatus status
= SecCertificateParseGeneralNameContentProperty(general_name_content
.tag
,
341 &general_name_content
.content
,
344 if (status
== errSecInvalidCertificate
) {
345 secnotice("policy","can't parse general name or not a type we support");
352 static bool isEmptySubject(CFDataRef subject
) {
353 const DERItem subject_der
= { (unsigned char *)CFDataGetBytePtr(subject
), CFDataGetLength(subject
) };
355 /* Get content of certificate name */
356 DERDecodedInfo subject_content
;
357 require_noerr_quiet(DERDecodeItem(&subject_der
, &subject_content
), out
);
358 if (subject_content
.content
.length
) return false;
365 * We update match structs as follows:
366 * 'present' is true if there's any subtree of the same type as any Subject/SAN
367 * 'match' is false if the subtree(s) and Subject(s)/SAN(s) don't match.
368 * Note: the state of 'match' is meaningless without 'present' also being true.
370 static void update_match(bool permit
, match_t
*input_match
, match_t
*output_match
) {
371 if (!input_match
|| !output_match
) {
374 if (input_match
->present
) {
375 output_match
->present
= true;
377 output_match
->isMatch
&= input_match
->isMatch
;
379 output_match
->isMatch
|= input_match
->isMatch
;
384 static void nc_compare_RFC822Name_to_subtrees(const void *value
, void *context
) {
385 CFStringRef rfc822Name
= (CFStringRef
)value
;
386 char *rfc822NameString
= NULL
;
387 nc_san_match_context_t
*san_context
= context
;
388 CFArrayRef subtrees
= NULL
;
390 subtrees
= san_context
->subtrees
;
393 CFIndex num_trees
= CFArrayGetCount(subtrees
);
394 CFRange range
= { 0, num_trees
};
395 match_t match
= { false, false };
396 rfc822NameString
= CFStringToCString(rfc822Name
);
397 if (!rfc822NameString
) { return; }
398 const DERItem addr
= { (unsigned char *)rfc822NameString
,
399 CFStringGetLength(rfc822Name
) };
400 nc_match_context_t match_context
= {GNT_RFC822Name
, &addr
, &match
};
401 CFArrayApplyFunction(subtrees
, range
, nc_decode_and_compare_subtree
, &match_context
);
402 free(rfc822NameString
);
404 update_match(san_context
->permit
, &match
, san_context
->match
);
409 static void nc_compare_subject_to_subtrees(SecCertificateRef certificate
, CFArrayRef subtrees
,
410 bool permit
, match_t
*match
) {
411 CFDataRef subject
= SecCertificateCopySubjectSequence(certificate
);
412 /* An empty subject name is considered not present */
413 if (!subject
|| isEmptySubject(subject
)) {
414 CFReleaseNull(subject
);
418 /* Compare X.500 distinguished name constraints */
419 CFIndex num_trees
= CFArrayGetCount(subtrees
);
420 CFRange range
= { 0, num_trees
};
421 match_t x500_match
= { false, false };
422 const DERItem subject_der
= { (unsigned char *)CFDataGetBytePtr(subject
), CFDataGetLength(subject
) };
423 nc_match_context_t context
= {GNT_DirectoryName
, &subject_der
, &x500_match
};
424 CFArrayApplyFunction(subtrees
, range
, nc_decode_and_compare_subtree
, &context
);
425 CFReleaseNull(subject
);
426 update_match(permit
, &x500_match
, match
);
428 /* Compare RFC822 constraints to subject email address */
429 match_t email_match
= { false, permit
};
430 CFArrayRef rfc822Names
= SecCertificateCopyRFC822NamesFromSubject(certificate
);
432 CFRange emailRange
= { 0, CFArrayGetCount(rfc822Names
) };
433 nc_san_match_context_t emailContext
= { subtrees
, &email_match
, permit
};
434 CFArrayApplyFunction(rfc822Names
, emailRange
, nc_compare_RFC822Name_to_subtrees
, &emailContext
);
436 CFReleaseNull(rfc822Names
);
437 update_match(permit
, &email_match
, match
);
440 static OSStatus
nc_compare_subjectAltName_to_subtrees(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
441 nc_san_match_context_t
*san_context
= context
;
442 CFArrayRef subtrees
= NULL
;
444 subtrees
= san_context
->subtrees
;
447 CFIndex num_trees
= CFArrayGetCount(subtrees
);
448 CFRange range
= { 0, num_trees
};
449 match_t match
= { false, false };
450 nc_match_context_t match_context
= {gnType
, generalName
, &match
};
451 CFArrayApplyFunction(subtrees
, range
, nc_decode_and_compare_subtree
, &match_context
);
453 update_match(san_context
->permit
, &match
, san_context
->match
);
455 return errSecSuccess
;
458 return errSecInvalidCertificate
;
461 OSStatus
SecNameContraintsMatchSubtrees(SecCertificateRef certificate
, CFArrayRef subtrees
, bool *matched
, bool permit
) {
462 CFDataRef subject
= NULL
;
463 OSStatus status
= errSecSuccess
;
465 require_action_quiet(subject
= SecCertificateCopySubjectSequence(certificate
),
467 status
= errSecInvalidCertificate
);
468 const DERItem
*subjectAltNames
= SecCertificateGetSubjectAltName(certificate
);
470 /* Reject certificates with neither Subject Name nor SubjectAltName */
471 require_action_quiet(!isEmptySubject(subject
) || subjectAltNames
, out
, status
= errSecInvalidCertificate
);
473 /* Verify that the subject name is within all of the subtrees */
474 match_t subject_match
= { false, permit
};
475 nc_compare_subject_to_subtrees(certificate
, subtrees
, permit
, &subject_match
);
477 /* permit tells us whether to start with true or false. If we are looking at permitted
478 * subtrees, we are going to "and" the matching results because all present types must match
479 * to permit. For excluded subtrees, we are going to "or" the matching results because
480 * any matching present types causes exclusion. */
481 match_t san_match
= { false, permit
};
482 nc_san_match_context_t san_context
= {subtrees
, &san_match
, permit
};
484 /* And verify that each of the alternative names in the subjectAltName extension (critical or non-critical)
485 * is within any of the subtrees for that name type. */
486 if (subjectAltNames
) {
487 status
= SecCertificateParseGeneralNames(subjectAltNames
,
489 nc_compare_subjectAltName_to_subtrees
);
490 /* If failed to parse */
491 require_action_quiet(status
== errSecSuccess
, out
, *matched
= false);
494 /* If we are excluding based on the subtrees, lack of names of the
495 same type is not a match. But if we are permitting, it is.
497 if (subject_match
.present
) {
498 if (san_match
.present
&&
499 ((subject_match
.isMatch
&& !san_match
.isMatch
) ||
500 (!subject_match
.isMatch
&& san_match
.isMatch
))) {
501 /* If both san and subject types are present, but don't agree on match
502 * we should exclude on the basis of the match and not permit on the
503 * basis of the failed match. */
504 *matched
= permit
? false : true;
507 /* If san type wasn't present or both had the same result, use the
508 * result from matching against the subject. */
509 *matched
= subject_match
.isMatch
;
512 else if (san_match
.present
) {
513 *matched
= san_match
.isMatch
;
516 /* Neither subject nor san had same type as subtrees, permit and don't
517 * exclude the cert. */
518 *matched
= permit
? true : false;
522 CFReleaseNull(subject
);
527 CFMutableArrayRef existing_trees
;
528 CFMutableArrayRef trees_to_add
;
529 } nc_intersect_context_t
;
531 static SecCEGeneralNameType
nc_gn_type_convert (DERTag tag
) {
533 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
534 return GNT_OtherName
;
535 case ASN1_CONTEXT_SPECIFIC
| 1:
536 return GNT_RFC822Name
;
537 case ASN1_CONTEXT_SPECIFIC
| 2:
539 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
540 return GNT_X400Address
;
541 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
542 return GNT_DirectoryName
;
543 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
544 return GNT_EdiPartyName
;
545 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
546 case ASN1_CONTEXT_SPECIFIC
| 6:
548 case ASN1_CONTEXT_SPECIFIC
| 7:
549 return GNT_IPAddress
;
550 case ASN1_CONTEXT_SPECIFIC
| 8:
551 return GNT_RegisteredID
;
553 return GNT_OtherName
;
557 /* The recommended processing algorithm states:
558 * If permittedSubtrees is present in the certificate, set the permitted_subtrees state variable to the intersection
559 * of its previous value and the value indicated in the extension field.
560 * However, in practice, certs are issued with permittedSubtrees whose intersection would be the empty set. For now,
561 * wherever a new permittedSubtree is a subset of an existing subtree, we'll replace the existing subtree; otherwise,
562 * we just append the new subtree.
564 static void nc_intersect_tree_with_subtrees (const void *value
, void *context
) {
565 CFDataRef new_subtree
= value
;
566 nc_intersect_context_t
*intersect_context
= context
;
567 CFMutableArrayRef existing_subtrees
= intersect_context
->existing_trees
;
568 CFMutableArrayRef trees_to_append
= intersect_context
->trees_to_add
;
570 if (!new_subtree
|| !existing_subtrees
) return;
572 /* convert new subtree to DERItem */
573 const DERItem general_name
= { (unsigned char *)CFDataGetBytePtr(new_subtree
), CFDataGetLength(new_subtree
) };
574 DERDecodedInfo general_name_content
;
575 if(DR_Success
!= DERDecodeItem(&general_name
, &general_name_content
)) return;
577 SecCEGeneralNameType gnType
;
578 DERItem
*new_subtree_item
= &general_name_content
.content
;
580 /* Attempt to intersect if one of the supported types: DirectoryName and DNSName.
581 * Otherwise, just append the new tree. */
582 gnType
= nc_gn_type_convert(general_name_content
.tag
);
583 if (!(gnType
== GNT_DirectoryName
|| gnType
== GNT_DNSName
)) {
584 CFArrayAppendValue(trees_to_append
, new_subtree
);
588 CFIndex num_existing_subtrees
= CFArrayGetCount(existing_subtrees
);
589 match_t match
= { false, false };
590 nc_match_context_t match_context
= { gnType
, new_subtree_item
, &match
};
591 for (subtreeIX
= 0; subtreeIX
< num_existing_subtrees
; subtreeIX
++) {
592 CFDataRef candidate_subtree
= CFArrayGetValueAtIndex(existing_subtrees
, subtreeIX
);
593 /* Convert candidate subtree to DERItem */
594 const DERItem candidate
= { (unsigned char *)CFDataGetBytePtr(candidate_subtree
), CFDataGetLength(candidate_subtree
) };
595 DERDecodedInfo candidate_content
;
596 /* We could probably just delete any subtrees in the array that don't decode */
597 if(DR_Success
!= DERDecodeItem(&candidate
, &candidate_content
)) continue;
599 /* first test whether new tree matches the existing tree */
600 OSStatus status
= SecCertificateParseGeneralNameContentProperty(candidate_content
.tag
,
601 &candidate_content
.content
,
604 if((status
== errSecSuccess
) && match
.present
&& match
.isMatch
) {
608 /* then test whether existing tree matches the new tree*/
609 match_t local_match
= { false , false };
610 nc_match_context_t local_match_context
= { nc_gn_type_convert(candidate_content
.tag
),
611 &candidate_content
.content
,
613 status
= SecCertificateParseGeneralNameContentProperty(general_name_content
.tag
,
614 &general_name_content
.content
,
615 &local_match_context
,
617 if((status
== errSecSuccess
) && local_match
.present
&& local_match
.isMatch
) {
621 if (subtreeIX
== num_existing_subtrees
) {
622 /* No matches found. Append new subtree */
623 CFArrayAppendValue(trees_to_append
, new_subtree
);
625 else if (match
.present
&& match
.isMatch
) {
626 /* new subtree \subseteq existing subtree, replace existing tree */
627 CFArraySetValueAtIndex(existing_subtrees
, subtreeIX
, new_subtree
);
629 /* existing subtree \subset new subtree, drop the new tree so as not to broaden constraints*/
634 void SecNameConstraintsIntersectSubtrees(CFMutableArrayRef subtrees_state
, CFArrayRef subtrees_new
) {
635 assert(subtrees_state
);
636 assert(subtrees_new
);
638 CFIndex num_new_trees
= CFArrayGetCount(subtrees_new
);
639 CFRange range
= { 0, num_new_trees
};
641 /* if existing subtrees state contains no subtrees, append new subtrees whole */
642 if (!CFArrayGetCount(subtrees_state
)) {
643 CFArrayAppendArray(subtrees_state
, subtrees_new
, range
);
647 CFMutableArrayRef trees_to_append
= NULL
;
648 trees_to_append
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
649 nc_intersect_context_t context
= { subtrees_state
, trees_to_append
};
650 CFArrayApplyFunction(subtrees_new
, range
, nc_intersect_tree_with_subtrees
, &context
);
652 /* don't append to the state until we've processed all the new trees */
653 num_new_trees
= CFArrayGetCount(trees_to_append
);
654 if (trees_to_append
&& num_new_trees
) {
655 range
.length
= num_new_trees
;
656 CFArrayAppendArray(subtrees_state
, trees_to_append
, range
);
659 CFReleaseNull(trees_to_append
);