1 /*
2  * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.provider.certpath;
27 
28 import java.io.IOException;
29 import java.security.GeneralSecurityException;
30 import java.security.cert.Certificate;
31 import java.security.cert.CertificateException;
32 import java.security.cert.CertPathValidatorException;
33 import java.security.cert.PKIXCertPathChecker;
34 import java.security.cert.PKIXReason;
35 import java.security.cert.PolicyNode;
36 import java.security.cert.PolicyQualifierInfo;
37 import java.security.cert.X509Certificate;
38 import java.util.*;
39 
40 import sun.security.util.Debug;
41 import sun.security.x509.CertificatePoliciesExtension;
42 import sun.security.x509.PolicyConstraintsExtension;
43 import sun.security.x509.PolicyMappingsExtension;
44 import sun.security.x509.CertificatePolicyMap;
45 import static sun.security.x509.PKIXExtensions.*;
46 import sun.security.x509.PolicyInformation;
47 import sun.security.x509.X509CertImpl;
48 import sun.security.x509.InhibitAnyPolicyExtension;
49 
50 /**
51  * PolicyChecker is a <code>PKIXCertPathChecker</code> that checks policy
52  * information on a PKIX certificate, namely certificate policies, policy
53  * mappings, policy constraints and policy qualifiers.
54  *
55  * @since       1.4
56  * @author      Yassir Elley
57  */
58 class PolicyChecker extends PKIXCertPathChecker {
59 
60     private final Set<String> initPolicies;
61     private final int certPathLen;
62     private final boolean expPolicyRequired;
63     private final boolean polMappingInhibited;
64     private final boolean anyPolicyInhibited;
65     private final boolean rejectPolicyQualifiers;
66     private PolicyNodeImpl rootNode;
67     private int explicitPolicy;
68     private int policyMapping;
69     private int inhibitAnyPolicy;
70     private int certIndex;
71 
72     private Set<String> supportedExts;
73 
74     private static final Debug debug = Debug.getInstance("certpath");
75     static final String ANY_POLICY = "2.5.29.32.0";
76 
77     /**
78      * Constructs a Policy Checker.
79      *
80      * @param initialPolicies Set of initial policies
81      * @param certPathLen length of the certification path to be checked
82      * @param expPolicyRequired true if explicit policy is required
83      * @param polMappingInhibited true if policy mapping is inhibited
84      * @param anyPolicyInhibited true if the ANY_POLICY OID should be inhibited
85      * @param rejectPolicyQualifiers true if pol qualifiers are to be rejected
86      * @param rootNode the initial root node of the valid policy tree
87      */
PolicyChecker(Set<String> initialPolicies, int certPathLen, boolean expPolicyRequired, boolean polMappingInhibited, boolean anyPolicyInhibited, boolean rejectPolicyQualifiers, PolicyNodeImpl rootNode)88     PolicyChecker(Set<String> initialPolicies, int certPathLen,
89         boolean expPolicyRequired, boolean polMappingInhibited,
90         boolean anyPolicyInhibited, boolean rejectPolicyQualifiers,
91         PolicyNodeImpl rootNode)
92     {
93         if (initialPolicies.isEmpty()) {
94             // if no initialPolicies are specified by user, set
95             // initPolicies to be anyPolicy by default
96             this.initPolicies = new HashSet<String>(1);
97             this.initPolicies.add(ANY_POLICY);
98         } else {
99             this.initPolicies = new HashSet<String>(initialPolicies);
100         }
101         this.certPathLen = certPathLen;
102         this.expPolicyRequired = expPolicyRequired;
103         this.polMappingInhibited = polMappingInhibited;
104         this.anyPolicyInhibited = anyPolicyInhibited;
105         this.rejectPolicyQualifiers = rejectPolicyQualifiers;
106         this.rootNode = rootNode;
107     }
108 
109     /**
110      * Initializes the internal state of the checker from parameters
111      * specified in the constructor
112      *
113      * @param forward a boolean indicating whether this checker should be
114      *        initialized capable of building in the forward direction
115      * @throws CertPathValidatorException if user wants to enable forward
116      *         checking and forward checking is not supported.
117      */
118     @Override
init(boolean forward)119     public void init(boolean forward) throws CertPathValidatorException {
120         if (forward) {
121             throw new CertPathValidatorException
122                                         ("forward checking not supported");
123         }
124 
125         certIndex = 1;
126         explicitPolicy = (expPolicyRequired ? 0 : certPathLen + 1);
127         policyMapping = (polMappingInhibited ? 0 : certPathLen + 1);
128         inhibitAnyPolicy = (anyPolicyInhibited ? 0 : certPathLen + 1);
129     }
130 
131     /**
132      * Checks if forward checking is supported. Forward checking refers
133      * to the ability of the PKIXCertPathChecker to perform its checks
134      * when presented with certificates in the forward direction (from
135      * target to anchor).
136      *
137      * @return true if forward checking is supported, false otherwise
138      */
139     @Override
isForwardCheckingSupported()140     public boolean isForwardCheckingSupported() {
141         return false;
142     }
143 
144     /**
145      * Gets an immutable Set of the OID strings for the extensions that
146      * the PKIXCertPathChecker supports (i.e. recognizes, is able to
147      * process), or null if no extensions are
148      * supported. All OID strings that a PKIXCertPathChecker might
149      * possibly be able to process should be included.
150      *
151      * @return the Set of extensions supported by this PKIXCertPathChecker,
152      * or null if no extensions are supported
153      */
154     @Override
getSupportedExtensions()155     public Set<String> getSupportedExtensions() {
156         if (supportedExts == null) {
157             supportedExts = new HashSet<String>(4);
158             supportedExts.add(CertificatePolicies_Id.toString());
159             supportedExts.add(PolicyMappings_Id.toString());
160             supportedExts.add(PolicyConstraints_Id.toString());
161             supportedExts.add(InhibitAnyPolicy_Id.toString());
162             supportedExts = Collections.unmodifiableSet(supportedExts);
163         }
164         return supportedExts;
165     }
166 
167     /**
168      * Performs the policy processing checks on the certificate using its
169      * internal state.
170      *
171      * @param cert the Certificate to be processed
172      * @param unresCritExts the unresolved critical extensions
173      * @throws CertPathValidatorException if the certificate does not verify
174      */
175     @Override
check(Certificate cert, Collection<String> unresCritExts)176     public void check(Certificate cert, Collection<String> unresCritExts)
177         throws CertPathValidatorException
178     {
179         // now do the policy checks
180         checkPolicy((X509Certificate) cert);
181 
182         if (unresCritExts != null && !unresCritExts.isEmpty()) {
183             unresCritExts.remove(CertificatePolicies_Id.toString());
184             unresCritExts.remove(PolicyMappings_Id.toString());
185             unresCritExts.remove(PolicyConstraints_Id.toString());
186             unresCritExts.remove(InhibitAnyPolicy_Id.toString());
187         }
188     }
189 
190     /**
191      * Internal method to run through all the checks.
192      *
193      * @param currCert the certificate to be processed
194      * @exception CertPathValidatorException Exception thrown if
195      * the certificate does not verify
196      */
checkPolicy(X509Certificate currCert)197     private void checkPolicy(X509Certificate currCert)
198         throws CertPathValidatorException
199     {
200         String msg = "certificate policies";
201         if (debug != null) {
202             debug.println("PolicyChecker.checkPolicy() ---checking " + msg
203                 + "...");
204             debug.println("PolicyChecker.checkPolicy() certIndex = "
205                 + certIndex);
206             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
207                 + "explicitPolicy = " + explicitPolicy);
208             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
209                 + "policyMapping = " + policyMapping);
210             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
211                 + "inhibitAnyPolicy = " + inhibitAnyPolicy);
212             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
213                 + "policyTree = " + rootNode);
214         }
215 
216         X509CertImpl currCertImpl = null;
217         try {
218             currCertImpl = X509CertImpl.toImpl(currCert);
219         } catch (CertificateException ce) {
220             throw new CertPathValidatorException(ce);
221         }
222 
223         boolean finalCert = (certIndex == certPathLen);
224 
225         rootNode = processPolicies(certIndex, initPolicies, explicitPolicy,
226             policyMapping, inhibitAnyPolicy, rejectPolicyQualifiers, rootNode,
227             currCertImpl, finalCert);
228 
229         if (!finalCert) {
230             explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCertImpl,
231                                                  finalCert);
232             policyMapping = mergePolicyMapping(policyMapping, currCertImpl);
233             inhibitAnyPolicy = mergeInhibitAnyPolicy(inhibitAnyPolicy,
234                                                      currCertImpl);
235         }
236 
237         certIndex++;
238 
239         if (debug != null) {
240             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
241                 + "explicitPolicy = " + explicitPolicy);
242             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
243                 + "policyMapping = " + policyMapping);
244             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
245                 + "inhibitAnyPolicy = " + inhibitAnyPolicy);
246             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
247                 + "policyTree = " + rootNode);
248             debug.println("PolicyChecker.checkPolicy() " + msg + " verified");
249         }
250     }
251 
252     /**
253      * Merges the specified explicitPolicy value with the
254      * requireExplicitPolicy field of the <code>PolicyConstraints</code>
255      * extension obtained from the certificate. An explicitPolicy
256      * value of -1 implies no constraint.
257      *
258      * @param explicitPolicy an integer which indicates if a non-null
259      * valid policy tree is required
260      * @param currCert the Certificate to be processed
261      * @param finalCert a boolean indicating whether currCert is
262      * the final cert in the cert path
263      * @return returns the new explicitPolicy value
264      * @exception CertPathValidatorException Exception thrown if an error
265      * occurs
266      */
mergeExplicitPolicy(int explicitPolicy, X509CertImpl currCert, boolean finalCert)267     static int mergeExplicitPolicy(int explicitPolicy, X509CertImpl currCert,
268         boolean finalCert) throws CertPathValidatorException
269     {
270         if ((explicitPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
271             explicitPolicy--;
272         }
273 
274         try {
275             PolicyConstraintsExtension polConstExt
276                 = currCert.getPolicyConstraintsExtension();
277             if (polConstExt == null)
278                 return explicitPolicy;
279             int require =
280                 polConstExt.get(PolicyConstraintsExtension.REQUIRE).intValue();
281             if (debug != null) {
282                 debug.println("PolicyChecker.mergeExplicitPolicy() "
283                    + "require Index from cert = " + require);
284             }
285             if (!finalCert) {
286                 if (require != -1) {
287                     if ((explicitPolicy == -1) || (require < explicitPolicy)) {
288                         explicitPolicy = require;
289                     }
290                 }
291             } else {
292                 if (require == 0)
293                     explicitPolicy = require;
294             }
295         } catch (IOException e) {
296             if (debug != null) {
297                 debug.println("PolicyChecker.mergeExplicitPolicy "
298                               + "unexpected exception");
299                 e.printStackTrace();
300             }
301             throw new CertPathValidatorException(e);
302         }
303 
304         return explicitPolicy;
305     }
306 
307     /**
308      * Merges the specified policyMapping value with the
309      * inhibitPolicyMapping field of the <code>PolicyConstraints</code>
310      * extension obtained from the certificate. A policyMapping
311      * value of -1 implies no constraint.
312      *
313      * @param policyMapping an integer which indicates if policy mapping
314      * is inhibited
315      * @param currCert the Certificate to be processed
316      * @return returns the new policyMapping value
317      * @exception CertPathValidatorException Exception thrown if an error
318      * occurs
319      */
mergePolicyMapping(int policyMapping, X509CertImpl currCert)320     static int mergePolicyMapping(int policyMapping, X509CertImpl currCert)
321         throws CertPathValidatorException
322     {
323         if ((policyMapping > 0) && !X509CertImpl.isSelfIssued(currCert)) {
324             policyMapping--;
325         }
326 
327         try {
328             PolicyConstraintsExtension polConstExt
329                 = currCert.getPolicyConstraintsExtension();
330             if (polConstExt == null)
331                 return policyMapping;
332 
333             int inhibit =
334                 polConstExt.get(PolicyConstraintsExtension.INHIBIT).intValue();
335             if (debug != null)
336                 debug.println("PolicyChecker.mergePolicyMapping() "
337                     + "inhibit Index from cert = " + inhibit);
338 
339             if (inhibit != -1) {
340                 if ((policyMapping == -1) || (inhibit < policyMapping)) {
341                     policyMapping = inhibit;
342                 }
343             }
344         } catch (IOException e) {
345             if (debug != null) {
346                 debug.println("PolicyChecker.mergePolicyMapping "
347                               + "unexpected exception");
348                 e.printStackTrace();
349             }
350             throw new CertPathValidatorException(e);
351         }
352 
353         return policyMapping;
354     }
355 
356     /**
357      * Merges the specified inhibitAnyPolicy value with the
358      * SkipCerts value of the InhibitAnyPolicy
359      * extension obtained from the certificate.
360      *
361      * @param inhibitAnyPolicy an integer which indicates whether
362      * "any-policy" is considered a match
363      * @param currCert the Certificate to be processed
364      * @return returns the new inhibitAnyPolicy value
365      * @exception CertPathValidatorException Exception thrown if an error
366      * occurs
367      */
mergeInhibitAnyPolicy(int inhibitAnyPolicy, X509CertImpl currCert)368     static int mergeInhibitAnyPolicy(int inhibitAnyPolicy,
369         X509CertImpl currCert) throws CertPathValidatorException
370     {
371         if ((inhibitAnyPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
372             inhibitAnyPolicy--;
373         }
374 
375         try {
376             InhibitAnyPolicyExtension inhAnyPolExt = (InhibitAnyPolicyExtension)
377                 currCert.getExtension(InhibitAnyPolicy_Id);
378             if (inhAnyPolExt == null)
379                 return inhibitAnyPolicy;
380 
381             int skipCerts =
382                 inhAnyPolExt.get(InhibitAnyPolicyExtension.SKIP_CERTS).intValue();
383             if (debug != null)
384                 debug.println("PolicyChecker.mergeInhibitAnyPolicy() "
385                     + "skipCerts Index from cert = " + skipCerts);
386 
387             if (skipCerts != -1) {
388                 if (skipCerts < inhibitAnyPolicy) {
389                     inhibitAnyPolicy = skipCerts;
390                 }
391             }
392         } catch (IOException e) {
393             if (debug != null) {
394                 debug.println("PolicyChecker.mergeInhibitAnyPolicy "
395                               + "unexpected exception");
396                 e.printStackTrace();
397             }
398             throw new CertPathValidatorException(e);
399         }
400 
401         return inhibitAnyPolicy;
402     }
403 
404     /**
405      * Processes certificate policies in the certificate.
406      *
407      * @param certIndex the index of the certificate
408      * @param initPolicies the initial policies required by the user
409      * @param explicitPolicy an integer which indicates if a non-null
410      * valid policy tree is required
411      * @param policyMapping an integer which indicates if policy
412      * mapping is inhibited
413      * @param inhibitAnyPolicy an integer which indicates whether
414      * "any-policy" is considered a match
415      * @param rejectPolicyQualifiers a boolean indicating whether the
416      * user wants to reject policies that have qualifiers
417      * @param origRootNode the root node of the valid policy tree
418      * @param currCert the Certificate to be processed
419      * @param finalCert a boolean indicating whether currCert is the final
420      * cert in the cert path
421      * @return the root node of the valid policy tree after modification
422      * @exception CertPathValidatorException Exception thrown if an
423      * error occurs while processing policies.
424      */
processPolicies(int certIndex, Set<String> initPolicies, int explicitPolicy, int policyMapping, int inhibitAnyPolicy, boolean rejectPolicyQualifiers, PolicyNodeImpl origRootNode, X509CertImpl currCert, boolean finalCert)425     static PolicyNodeImpl processPolicies(int certIndex, Set<String> initPolicies,
426         int explicitPolicy, int policyMapping, int inhibitAnyPolicy,
427         boolean rejectPolicyQualifiers, PolicyNodeImpl origRootNode,
428         X509CertImpl currCert, boolean finalCert)
429         throws CertPathValidatorException
430     {
431         boolean policiesCritical = false;
432         List<PolicyInformation> policyInfo;
433         PolicyNodeImpl rootNode = null;
434         Set<PolicyQualifierInfo> anyQuals = new HashSet<>();
435 
436         if (origRootNode == null)
437             rootNode = null;
438         else
439             rootNode = origRootNode.copyTree();
440 
441         // retrieve policyOIDs from currCert
442         CertificatePoliciesExtension currCertPolicies
443             = currCert.getCertificatePoliciesExtension();
444 
445         // PKIX: Section 6.1.3: Step (d)
446         if ((currCertPolicies != null) && (rootNode != null)) {
447             policiesCritical = currCertPolicies.isCritical();
448             if (debug != null)
449                 debug.println("PolicyChecker.processPolicies() "
450                     + "policiesCritical = " + policiesCritical);
451 
452             try {
453                 policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
454             } catch (IOException ioe) {
455                 throw new CertPathValidatorException("Exception while "
456                     + "retrieving policyOIDs", ioe);
457             }
458 
459             if (debug != null)
460                 debug.println("PolicyChecker.processPolicies() "
461                     + "rejectPolicyQualifiers = " + rejectPolicyQualifiers);
462 
463             boolean foundAnyPolicy = false;
464 
465             // process each policy in cert
466             for (PolicyInformation curPolInfo : policyInfo) {
467                 String curPolicy =
468                     curPolInfo.getPolicyIdentifier().getIdentifier().toString();
469 
470                 if (curPolicy.equals(ANY_POLICY)) {
471                     foundAnyPolicy = true;
472                     anyQuals = curPolInfo.getPolicyQualifiers();
473                 } else {
474                     // PKIX: Section 6.1.3: Step (d)(1)
475                     if (debug != null)
476                         debug.println("PolicyChecker.processPolicies() "
477                                       + "processing policy: " + curPolicy);
478 
479                     // retrieve policy qualifiers from cert
480                     Set<PolicyQualifierInfo> pQuals =
481                                         curPolInfo.getPolicyQualifiers();
482 
483                     // reject cert if we find critical policy qualifiers and
484                     // the policyQualifiersRejected flag is set in the params
485                     if (!pQuals.isEmpty() && rejectPolicyQualifiers &&
486                         policiesCritical) {
487                         throw new CertPathValidatorException(
488                             "critical policy qualifiers present in certificate",
489                             null, null, -1, PKIXReason.INVALID_POLICY);
490                     }
491 
492                     // PKIX: Section 6.1.3: Step (d)(1)(i)
493                     boolean foundMatch = processParents(certIndex,
494                         policiesCritical, rejectPolicyQualifiers, rootNode,
495                         curPolicy, pQuals, false);
496 
497                     if (!foundMatch) {
498                         // PKIX: Section 6.1.3: Step (d)(1)(ii)
499                         processParents(certIndex, policiesCritical,
500                             rejectPolicyQualifiers, rootNode, curPolicy,
501                             pQuals, true);
502                     }
503                 }
504             }
505 
506             // PKIX: Section 6.1.3: Step (d)(2)
507             if (foundAnyPolicy) {
508                 if ((inhibitAnyPolicy > 0) ||
509                         (!finalCert && X509CertImpl.isSelfIssued(currCert))) {
510                     if (debug != null) {
511                         debug.println("PolicyChecker.processPolicies() "
512                             + "processing policy: " + ANY_POLICY);
513                     }
514                     processParents(certIndex, policiesCritical,
515                         rejectPolicyQualifiers, rootNode, ANY_POLICY, anyQuals,
516                         true);
517                 }
518             }
519 
520             // PKIX: Section 6.1.3: Step (d)(3)
521             rootNode.prune(certIndex);
522             if (!rootNode.getChildren().hasNext()) {
523                 rootNode = null;
524             }
525         } else if (currCertPolicies == null) {
526             if (debug != null)
527                 debug.println("PolicyChecker.processPolicies() "
528                               + "no policies present in cert");
529             // PKIX: Section 6.1.3: Step (e)
530             rootNode = null;
531         }
532 
533         // We delay PKIX: Section 6.1.3: Step (f) to the end
534         // because the code that follows may delete some nodes
535         // resulting in a null tree
536         if (rootNode != null) {
537             if (!finalCert) {
538                 // PKIX: Section 6.1.4: Steps (a)-(b)
539                 rootNode = processPolicyMappings(currCert, certIndex,
540                     policyMapping, rootNode, policiesCritical, anyQuals);
541             }
542         }
543 
544         // At this point, we optimize the PKIX algorithm by
545         // removing those nodes which would later have
546         // been removed by PKIX: Section 6.1.5: Step (g)(iii)
547 
548         if ((rootNode != null) && (!initPolicies.contains(ANY_POLICY))
549             && (currCertPolicies != null)) {
550             rootNode = removeInvalidNodes(rootNode, certIndex,
551                                           initPolicies, currCertPolicies);
552 
553             // PKIX: Section 6.1.5: Step (g)(iii)
554             if ((rootNode != null) && finalCert) {
555                 // rewrite anyPolicy leaf nodes (see method comments)
556                 rootNode = rewriteLeafNodes(certIndex, initPolicies, rootNode);
557             }
558         }
559 
560 
561         if (finalCert) {
562             // PKIX: Section 6.1.5: Steps (a) and (b)
563             explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCert,
564                                              finalCert);
565         }
566 
567         // PKIX: Section 6.1.3: Step (f)
568         // verify that either explicit policy is greater than 0 or
569         // the valid_policy_tree is not equal to NULL
570 
571         if ((explicitPolicy == 0) && (rootNode == null)) {
572             throw new CertPathValidatorException
573                 ("non-null policy tree required and policy tree is null",
574                  null, null, -1, PKIXReason.INVALID_POLICY);
575         }
576 
577         return rootNode;
578     }
579 
580     /**
581      * Rewrite leaf nodes at the end of validation as described in RFC 3280
582      * section 6.1.5: Step (g)(iii). Leaf nodes with anyPolicy are replaced
583      * by nodes explicitly representing initial policies not already
584      * represented by leaf nodes.
585      *
586      * This method should only be called when processing the final cert
587      * and if the policy tree is not null and initial policies is not
588      * anyPolicy.
589      *
590      * @param certIndex the depth of the tree
591      * @param initPolicies Set of user specified initial policies
592      * @param rootNode the root of the policy tree
593      */
rewriteLeafNodes(int certIndex, Set<String> initPolicies, PolicyNodeImpl rootNode)594     private static PolicyNodeImpl rewriteLeafNodes(int certIndex,
595             Set<String> initPolicies, PolicyNodeImpl rootNode) {
596         Set<PolicyNodeImpl> anyNodes =
597                         rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
598         if (anyNodes.isEmpty()) {
599             return rootNode;
600         }
601         PolicyNodeImpl anyNode = anyNodes.iterator().next();
602         PolicyNodeImpl parentNode = (PolicyNodeImpl)anyNode.getParent();
603         parentNode.deleteChild(anyNode);
604         // see if there are any initialPolicies not represented by leaf nodes
605         Set<String> initial = new HashSet<>(initPolicies);
606         for (PolicyNodeImpl node : rootNode.getPolicyNodes(certIndex)) {
607             initial.remove(node.getValidPolicy());
608         }
609         if (initial.isEmpty()) {
610             // we deleted the anyPolicy node and have nothing to re-add,
611             // so we need to prune the tree
612             rootNode.prune(certIndex);
613             if (rootNode.getChildren().hasNext() == false) {
614                 rootNode = null;
615             }
616         } else {
617             boolean anyCritical = anyNode.isCritical();
618             Set<PolicyQualifierInfo> anyQualifiers =
619                                                 anyNode.getPolicyQualifiers();
620             for (String policy : initial) {
621                 Set<String> expectedPolicies = Collections.singleton(policy);
622                 PolicyNodeImpl node = new PolicyNodeImpl(parentNode, policy,
623                     anyQualifiers, anyCritical, expectedPolicies, false);
624             }
625         }
626         return rootNode;
627     }
628 
629     /**
630      * Finds the policy nodes of depth (certIndex-1) where curPolicy
631      * is in the expected policy set and creates a new child node
632      * appropriately. If matchAny is true, then a value of ANY_POLICY
633      * in the expected policy set will match any curPolicy. If matchAny
634      * is false, then the expected policy set must exactly contain the
635      * curPolicy to be considered a match. This method returns a boolean
636      * value indicating whether a match was found.
637      *
638      * @param certIndex the index of the certificate whose policy is
639      * being processed
640      * @param policiesCritical a boolean indicating whether the certificate
641      * policies extension is critical
642      * @param rejectPolicyQualifiers a boolean indicating whether the
643      * user wants to reject policies that have qualifiers
644      * @param rootNode the root node of the valid policy tree
645      * @param curPolicy a String representing the policy being processed
646      * @param pQuals the policy qualifiers of the policy being processed or an
647      * empty Set if there are no qualifiers
648      * @param matchAny a boolean indicating whether a value of ANY_POLICY
649      * in the expected policy set will be considered a match
650      * @return a boolean indicating whether a match was found
651      * @exception CertPathValidatorException Exception thrown if error occurs.
652      */
processParents(int certIndex, boolean policiesCritical, boolean rejectPolicyQualifiers, PolicyNodeImpl rootNode, String curPolicy, Set<PolicyQualifierInfo> pQuals, boolean matchAny)653     private static boolean processParents(int certIndex,
654         boolean policiesCritical, boolean rejectPolicyQualifiers,
655         PolicyNodeImpl rootNode, String curPolicy,
656         Set<PolicyQualifierInfo> pQuals,
657         boolean matchAny) throws CertPathValidatorException
658     {
659         boolean foundMatch = false;
660 
661         if (debug != null)
662             debug.println("PolicyChecker.processParents(): matchAny = "
663                 + matchAny);
664 
665         // find matching parents
666         Set<PolicyNodeImpl> parentNodes =
667                 rootNode.getPolicyNodesExpected(certIndex - 1,
668                                                 curPolicy, matchAny);
669 
670         // for each matching parent, extend policy tree
671         for (PolicyNodeImpl curParent : parentNodes) {
672             if (debug != null)
673                 debug.println("PolicyChecker.processParents() "
674                               + "found parent:\n" + curParent.asString());
675 
676             foundMatch = true;
677             String curParPolicy = curParent.getValidPolicy();
678 
679             PolicyNodeImpl curNode = null;
680             Set<String> curExpPols = null;
681 
682             if (curPolicy.equals(ANY_POLICY)) {
683                 // do step 2
684                 Set<String> parExpPols = curParent.getExpectedPolicies();
685             parentExplicitPolicies:
686                 for (String curParExpPol : parExpPols) {
687 
688                     Iterator<PolicyNodeImpl> childIter =
689                                         curParent.getChildren();
690                     while (childIter.hasNext()) {
691                         PolicyNodeImpl childNode = childIter.next();
692                         String childPolicy = childNode.getValidPolicy();
693                         if (curParExpPol.equals(childPolicy)) {
694                             if (debug != null)
695                                 debug.println(childPolicy + " in parent's "
696                                     + "expected policy set already appears in "
697                                     + "child node");
698                             continue parentExplicitPolicies;
699                         }
700                     }
701 
702                     Set<String> expPols = new HashSet<>();
703                     expPols.add(curParExpPol);
704 
705                     curNode = new PolicyNodeImpl
706                         (curParent, curParExpPol, pQuals,
707                          policiesCritical, expPols, false);
708                 }
709             } else {
710                 curExpPols = new HashSet<String>();
711                 curExpPols.add(curPolicy);
712 
713                 curNode = new PolicyNodeImpl
714                     (curParent, curPolicy, pQuals,
715                      policiesCritical, curExpPols, false);
716             }
717         }
718 
719         return foundMatch;
720     }
721 
722     /**
723      * Processes policy mappings in the certificate.
724      *
725      * @param currCert the Certificate to be processed
726      * @param certIndex the index of the current certificate
727      * @param policyMapping an integer which indicates if policy
728      * mapping is inhibited
729      * @param rootNode the root node of the valid policy tree
730      * @param policiesCritical a boolean indicating if the certificate policies
731      * extension is critical
732      * @param anyQuals the qualifiers associated with ANY-POLICY, or an empty
733      * Set if there are no qualifiers associated with ANY-POLICY
734      * @return the root node of the valid policy tree after modification
735      * @exception CertPathValidatorException exception thrown if an error
736      * occurs while processing policy mappings
737      */
processPolicyMappings(X509CertImpl currCert, int certIndex, int policyMapping, PolicyNodeImpl rootNode, boolean policiesCritical, Set<PolicyQualifierInfo> anyQuals)738     private static PolicyNodeImpl processPolicyMappings(X509CertImpl currCert,
739         int certIndex, int policyMapping, PolicyNodeImpl rootNode,
740         boolean policiesCritical, Set<PolicyQualifierInfo> anyQuals)
741         throws CertPathValidatorException
742     {
743         PolicyMappingsExtension polMappingsExt
744             = currCert.getPolicyMappingsExtension();
745 
746         if (polMappingsExt == null)
747             return rootNode;
748 
749         if (debug != null)
750             debug.println("PolicyChecker.processPolicyMappings() "
751                 + "inside policyMapping check");
752 
753         List<CertificatePolicyMap> maps = null;
754         try {
755             maps = polMappingsExt.get(PolicyMappingsExtension.MAP);
756         } catch (IOException e) {
757             if (debug != null) {
758                 debug.println("PolicyChecker.processPolicyMappings() "
759                     + "mapping exception");
760                 e.printStackTrace();
761             }
762             throw new CertPathValidatorException("Exception while checking "
763                                                  + "mapping", e);
764         }
765 
766         boolean childDeleted = false;
767         for (CertificatePolicyMap polMap : maps) {
768             String issuerDomain
769                 = polMap.getIssuerIdentifier().getIdentifier().toString();
770             String subjectDomain
771                 = polMap.getSubjectIdentifier().getIdentifier().toString();
772             if (debug != null) {
773                 debug.println("PolicyChecker.processPolicyMappings() "
774                               + "issuerDomain = " + issuerDomain);
775                 debug.println("PolicyChecker.processPolicyMappings() "
776                               + "subjectDomain = " + subjectDomain);
777             }
778 
779             if (issuerDomain.equals(ANY_POLICY)) {
780                 throw new CertPathValidatorException
781                     ("encountered an issuerDomainPolicy of ANY_POLICY",
782                      null, null, -1, PKIXReason.INVALID_POLICY);
783             }
784 
785             if (subjectDomain.equals(ANY_POLICY)) {
786                 throw new CertPathValidatorException
787                     ("encountered a subjectDomainPolicy of ANY_POLICY",
788                      null, null, -1, PKIXReason.INVALID_POLICY);
789             }
790 
791             Set<PolicyNodeImpl> validNodes =
792                 rootNode.getPolicyNodesValid(certIndex, issuerDomain);
793             if (!validNodes.isEmpty()) {
794                 for (PolicyNodeImpl curNode : validNodes) {
795                     if ((policyMapping > 0) || (policyMapping == -1)) {
796                         curNode.addExpectedPolicy(subjectDomain);
797                     } else if (policyMapping == 0) {
798                         PolicyNodeImpl parentNode =
799                             (PolicyNodeImpl) curNode.getParent();
800                         if (debug != null)
801                             debug.println("PolicyChecker.processPolicyMappings"
802                                 + "() before deleting: policy tree = "
803                                 + rootNode);
804                         parentNode.deleteChild(curNode);
805                         childDeleted = true;
806                         if (debug != null)
807                             debug.println("PolicyChecker.processPolicyMappings"
808                                 + "() after deleting: policy tree = "
809                                 + rootNode);
810                     }
811                 }
812             } else { // no node of depth i has a valid policy
813                 if ((policyMapping > 0) || (policyMapping == -1)) {
814                     Set<PolicyNodeImpl> validAnyNodes =
815                         rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
816                     for (PolicyNodeImpl curAnyNode : validAnyNodes) {
817                         PolicyNodeImpl curAnyNodeParent =
818                             (PolicyNodeImpl) curAnyNode.getParent();
819 
820                         Set<String> expPols = new HashSet<>();
821                         expPols.add(subjectDomain);
822 
823                         PolicyNodeImpl curNode = new PolicyNodeImpl
824                             (curAnyNodeParent, issuerDomain, anyQuals,
825                              policiesCritical, expPols, true);
826                     }
827                 }
828             }
829         }
830 
831         if (childDeleted) {
832             rootNode.prune(certIndex);
833             if (!rootNode.getChildren().hasNext()) {
834                 if (debug != null)
835                     debug.println("setting rootNode to null");
836                 rootNode = null;
837             }
838         }
839 
840         return rootNode;
841     }
842 
843     /**
844      * Removes those nodes which do not intersect with the initial policies
845      * specified by the user.
846      *
847      * @param rootNode the root node of the valid policy tree
848      * @param certIndex the index of the certificate being processed
849      * @param initPolicies the Set of policies required by the user
850      * @param currCertPolicies the CertificatePoliciesExtension of the
851      * certificate being processed
852      * @returns the root node of the valid policy tree after modification
853      * @exception CertPathValidatorException Exception thrown if error occurs.
854      */
removeInvalidNodes(PolicyNodeImpl rootNode, int certIndex, Set<String> initPolicies, CertificatePoliciesExtension currCertPolicies)855     private static PolicyNodeImpl removeInvalidNodes(PolicyNodeImpl rootNode,
856         int certIndex, Set<String> initPolicies,
857         CertificatePoliciesExtension currCertPolicies)
858         throws CertPathValidatorException
859     {
860         List<PolicyInformation> policyInfo = null;
861         try {
862             policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
863         } catch (IOException ioe) {
864             throw new CertPathValidatorException("Exception while "
865                 + "retrieving policyOIDs", ioe);
866         }
867 
868         boolean childDeleted = false;
869         for (PolicyInformation curPolInfo : policyInfo) {
870             String curPolicy =
871                 curPolInfo.getPolicyIdentifier().getIdentifier().toString();
872 
873             if (debug != null)
874                 debug.println("PolicyChecker.processPolicies() "
875                               + "processing policy second time: " + curPolicy);
876 
877             Set<PolicyNodeImpl> validNodes =
878                         rootNode.getPolicyNodesValid(certIndex, curPolicy);
879             for (PolicyNodeImpl curNode : validNodes) {
880                 PolicyNodeImpl parentNode = (PolicyNodeImpl)curNode.getParent();
881                 if (parentNode.getValidPolicy().equals(ANY_POLICY)) {
882                     if ((!initPolicies.contains(curPolicy)) &&
883                         (!curPolicy.equals(ANY_POLICY))) {
884                         if (debug != null)
885                             debug.println("PolicyChecker.processPolicies() "
886                                 + "before deleting: policy tree = " + rootNode);
887                         parentNode.deleteChild(curNode);
888                         childDeleted = true;
889                         if (debug != null)
890                             debug.println("PolicyChecker.processPolicies() "
891                                 + "after deleting: policy tree = " + rootNode);
892                     }
893                 }
894             }
895         }
896 
897         if (childDeleted) {
898             rootNode.prune(certIndex);
899             if (!rootNode.getChildren().hasNext()) {
900                 rootNode = null;
901             }
902         }
903 
904         return rootNode;
905     }
906 
907     /**
908      * Gets the root node of the valid policy tree, or null if the
909      * valid policy tree is null. Marks each node of the returned tree
910      * immutable and thread-safe.
911      *
912      * @returns the root node of the valid policy tree, or null if
913      * the valid policy tree is null
914      */
getPolicyTree()915     PolicyNode getPolicyTree() {
916         if (rootNode == null)
917             return null;
918         else {
919             PolicyNodeImpl policyTree = rootNode.copyTree();
920             policyTree.setImmutable();
921             return policyTree;
922         }
923     }
924 }
925