Home | History | Annotate | Download | only in certpath
      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.util.Collections;
     29 import java.util.HashSet;
     30 import java.util.Iterator;
     31 import java.util.Set;
     32 
     33 import java.security.cert.*;
     34 
     35 /**
     36  * Implements the <code>PolicyNode</code> interface.
     37  * <p>
     38  * This class provides an implementation of the <code>PolicyNode</code>
     39  * interface, and is used internally to build and search Policy Trees.
     40  * While the implementation is mutable during construction, it is immutable
     41  * before returning to a client and no mutable public or protected methods
     42  * are exposed by this implementation, as per the contract of PolicyNode.
     43  *
     44  * @since       1.4
     45  * @author      Seth Proctor
     46  * @author      Sean Mullan
     47  */
     48 final class PolicyNodeImpl implements PolicyNode {
     49 
     50     /**
     51      * Use to specify the special policy "Any Policy"
     52      */
     53     private static final String ANY_POLICY = "2.5.29.32.0";
     54 
     55     // every node has one parent, and zero or more children
     56     private PolicyNodeImpl mParent;
     57     private HashSet<PolicyNodeImpl> mChildren;
     58 
     59     // the 4 fields specified by RFC 3280
     60     private String mValidPolicy;
     61     private HashSet<PolicyQualifierInfo> mQualifierSet;
     62     private boolean mCriticalityIndicator;
     63     private HashSet<String> mExpectedPolicySet;
     64     private boolean mOriginalExpectedPolicySet;
     65 
     66     // the tree depth
     67     private int mDepth;
     68     // immutability flag
     69     private boolean isImmutable = false;
     70 
     71     /**
     72      * Constructor which takes a <code>PolicyNodeImpl</code> representing the
     73      * parent in the Policy Tree to this node. If null, this is the
     74      * root of the tree. The constructor also takes the associated data
     75      * for this node, as found in the certificate. It also takes a boolean
     76      * argument specifying whether this node is being created as a result
     77      * of policy mapping.
     78      *
     79      * @param parent the PolicyNode above this in the tree, or null if this
     80      *               node is the tree's root node
     81      * @param validPolicy a String representing this node's valid policy OID
     82      * @param qualifierSet the Set of qualifiers for this policy
     83      * @param criticalityIndicator a boolean representing whether or not the
     84      *                             extension is critical
     85      * @param expectedPolicySet a Set of expected policies
     86      * @param generatedByPolicyMapping a boolean indicating whether this
     87      * node was generated by a policy mapping
     88      */
     89     PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy,
     90                 Set<PolicyQualifierInfo> qualifierSet,
     91                 boolean criticalityIndicator, Set<String> expectedPolicySet,
     92                 boolean generatedByPolicyMapping) {
     93         mParent = parent;
     94         mChildren = new HashSet<PolicyNodeImpl>();
     95 
     96         if (validPolicy != null)
     97             mValidPolicy = validPolicy;
     98         else
     99             mValidPolicy = "";
    100 
    101         if (qualifierSet != null)
    102             mQualifierSet = new HashSet<PolicyQualifierInfo>(qualifierSet);
    103         else
    104             mQualifierSet = new HashSet<PolicyQualifierInfo>();
    105 
    106         mCriticalityIndicator = criticalityIndicator;
    107 
    108         if (expectedPolicySet != null)
    109             mExpectedPolicySet = new HashSet<String>(expectedPolicySet);
    110         else
    111             mExpectedPolicySet = new HashSet<String>();
    112 
    113         mOriginalExpectedPolicySet = !generatedByPolicyMapping;
    114 
    115         // see if we're the root, and act appropriately
    116         if (mParent != null) {
    117             mDepth = mParent.getDepth() + 1;
    118             mParent.addChild(this);
    119         } else {
    120             mDepth = 0;
    121         }
    122     }
    123 
    124     /**
    125      * Alternate constructor which makes a new node with the policy data
    126      * in an existing <code>PolicyNodeImpl</code>.
    127      *
    128      * @param parent a PolicyNode that's the new parent of the node, or
    129      *               null if this is the root node
    130      * @param node a PolicyNode containing the policy data to copy
    131      */
    132     PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) {
    133         this(parent, node.mValidPolicy, node.mQualifierSet,
    134              node.mCriticalityIndicator, node.mExpectedPolicySet, false);
    135     }
    136 
    137     @Override
    138     public PolicyNode getParent() {
    139         return mParent;
    140     }
    141 
    142     @Override
    143     public Iterator<PolicyNodeImpl> getChildren() {
    144         return Collections.unmodifiableSet(mChildren).iterator();
    145     }
    146 
    147     @Override
    148     public int getDepth() {
    149         return mDepth;
    150     }
    151 
    152     @Override
    153     public String getValidPolicy() {
    154         return mValidPolicy;
    155     }
    156 
    157     @Override
    158     public Set<PolicyQualifierInfo> getPolicyQualifiers() {
    159         return Collections.unmodifiableSet(mQualifierSet);
    160     }
    161 
    162     @Override
    163     public Set<String> getExpectedPolicies() {
    164         return Collections.unmodifiableSet(mExpectedPolicySet);
    165     }
    166 
    167     @Override
    168     public boolean isCritical() {
    169         return mCriticalityIndicator;
    170     }
    171 
    172     /**
    173      * Return a printable representation of the PolicyNode.
    174      * Starting at the node on which this method is called,
    175      * it recurses through the tree and prints out each node.
    176      *
    177      * @return a String describing the contents of the Policy Node
    178      */
    179     @Override
    180     public String toString() {
    181         StringBuilder buffer = new StringBuilder(this.asString());
    182 
    183         for (PolicyNodeImpl node : mChildren) {
    184             buffer.append(node);
    185         }
    186         return buffer.toString();
    187     }
    188 
    189     // private methods and package private operations
    190 
    191     boolean isImmutable() {
    192         return isImmutable;
    193     }
    194 
    195     /**
    196      * Sets the immutability flag of this node and all of its children
    197      * to true.
    198      */
    199     void setImmutable() {
    200         if (isImmutable)
    201             return;
    202         for (PolicyNodeImpl node : mChildren) {
    203             node.setImmutable();
    204         }
    205         isImmutable = true;
    206     }
    207 
    208     /**
    209      * Private method sets a child node. This is called from the child's
    210      * constructor.
    211      *
    212      * @param child new <code>PolicyNodeImpl</code> child node
    213      */
    214     private void addChild(PolicyNodeImpl child) {
    215         if (isImmutable) {
    216             throw new IllegalStateException("PolicyNode is immutable");
    217         }
    218         mChildren.add(child);
    219     }
    220 
    221     /**
    222      * Adds an expectedPolicy to the expected policy set.
    223      * If this is the original expected policy set initialized
    224      * by the constructor, then the expected policy set is cleared
    225      * before the expected policy is added.
    226      *
    227      * @param expectedPolicy a String representing an expected policy.
    228      */
    229     void addExpectedPolicy(String expectedPolicy) {
    230         if (isImmutable) {
    231             throw new IllegalStateException("PolicyNode is immutable");
    232         }
    233         if (mOriginalExpectedPolicySet) {
    234             mExpectedPolicySet.clear();
    235             mOriginalExpectedPolicySet = false;
    236         }
    237         mExpectedPolicySet.add(expectedPolicy);
    238     }
    239 
    240     /**
    241      * Removes all paths which don't reach the specified depth.
    242      *
    243      * @param depth an int representing the desired minimum depth of all paths
    244      */
    245     void prune(int depth) {
    246         if (isImmutable)
    247             throw new IllegalStateException("PolicyNode is immutable");
    248 
    249         // if we have no children, we can't prune below us...
    250         if (mChildren.size() == 0)
    251             return;
    252 
    253         Iterator<PolicyNodeImpl> it = mChildren.iterator();
    254         while (it.hasNext()) {
    255             PolicyNodeImpl node = it.next();
    256             node.prune(depth);
    257             // now that we've called prune on the child, see if we should
    258             // remove it from the tree
    259             if ((node.mChildren.size() == 0) && (depth > mDepth + 1))
    260                 it.remove();
    261         }
    262     }
    263 
    264     /**
    265      * Deletes the specified child node of this node, if it exists.
    266      *
    267      * @param childNode the child node to be deleted
    268      */
    269     void deleteChild(PolicyNode childNode) {
    270         if (isImmutable) {
    271             throw new IllegalStateException("PolicyNode is immutable");
    272         }
    273         mChildren.remove(childNode);
    274     }
    275 
    276     /**
    277      * Returns a copy of the tree, without copying the policy-related data,
    278      * rooted at the node on which this was called.
    279      *
    280      * @return a copy of the tree
    281      */
    282     PolicyNodeImpl copyTree() {
    283         return copyTree(null);
    284     }
    285 
    286     private PolicyNodeImpl copyTree(PolicyNodeImpl parent) {
    287         PolicyNodeImpl newNode = new PolicyNodeImpl(parent, this);
    288 
    289         for (PolicyNodeImpl node : mChildren) {
    290             node.copyTree(newNode);
    291         }
    292 
    293         return newNode;
    294     }
    295 
    296     /**
    297      * Returns all nodes at the specified depth in the tree.
    298      *
    299      * @param depth an int representing the depth of the desired nodes
    300      * @return a <code>Set</code> of all nodes at the specified depth
    301      */
    302     Set<PolicyNodeImpl> getPolicyNodes(int depth) {
    303         Set<PolicyNodeImpl> set = new HashSet<>();
    304         getPolicyNodes(depth, set);
    305         return set;
    306     }
    307 
    308     /**
    309      * Add all nodes at depth depth to set and return the Set.
    310      * Internal recursion helper.
    311      */
    312     private void getPolicyNodes(int depth, Set<PolicyNodeImpl> set) {
    313         // if we've reached the desired depth, then return ourself
    314         if (mDepth == depth) {
    315             set.add(this);
    316         } else {
    317             for (PolicyNodeImpl node : mChildren) {
    318                 node.getPolicyNodes(depth, set);
    319             }
    320         }
    321     }
    322 
    323     /**
    324      * Finds all nodes at the specified depth whose expected_policy_set
    325      * contains the specified expected OID (if matchAny is false)
    326      * or the special OID "any value" (if matchAny is true).
    327      *
    328      * @param depth an int representing the desired depth
    329      * @param expectedOID a String encoding the valid OID to match
    330      * @param matchAny a boolean indicating whether an expected_policy_set
    331      * containing ANY_POLICY should be considered a match
    332      * @return a Set of matched <code>PolicyNode</code>s
    333      */
    334     Set<PolicyNodeImpl> getPolicyNodesExpected(int depth,
    335         String expectedOID, boolean matchAny) {
    336 
    337         if (expectedOID.equals(ANY_POLICY)) {
    338             return getPolicyNodes(depth);
    339         } else {
    340             return getPolicyNodesExpectedHelper(depth, expectedOID, matchAny);
    341         }
    342     }
    343 
    344     private Set<PolicyNodeImpl> getPolicyNodesExpectedHelper(int depth,
    345         String expectedOID, boolean matchAny) {
    346 
    347         HashSet<PolicyNodeImpl> set = new HashSet<>();
    348 
    349         if (mDepth < depth) {
    350             for (PolicyNodeImpl node : mChildren) {
    351                 set.addAll(node.getPolicyNodesExpectedHelper(depth,
    352                                                              expectedOID,
    353                                                              matchAny));
    354             }
    355         } else {
    356             if (matchAny) {
    357                 if (mExpectedPolicySet.contains(ANY_POLICY))
    358                     set.add(this);
    359             } else {
    360                 if (mExpectedPolicySet.contains(expectedOID))
    361                     set.add(this);
    362             }
    363         }
    364 
    365         return set;
    366     }
    367 
    368     /**
    369      * Finds all nodes at the specified depth that contains the
    370      * specified valid OID
    371      *
    372      * @param depth an int representing the desired depth
    373      * @param validOID a String encoding the valid OID to match
    374      * @return a Set of matched <code>PolicyNode</code>s
    375      */
    376     Set<PolicyNodeImpl> getPolicyNodesValid(int depth, String validOID) {
    377         HashSet<PolicyNodeImpl> set = new HashSet<>();
    378 
    379         if (mDepth < depth) {
    380             for (PolicyNodeImpl node : mChildren) {
    381                 set.addAll(node.getPolicyNodesValid(depth, validOID));
    382             }
    383         } else {
    384             if (mValidPolicy.equals(validOID))
    385                 set.add(this);
    386         }
    387 
    388         return set;
    389     }
    390 
    391     private static String policyToString(String oid) {
    392         if (oid.equals(ANY_POLICY)) {
    393             return "anyPolicy";
    394         } else {
    395             return oid;
    396         }
    397     }
    398 
    399     /**
    400      * Prints out some data on this node.
    401      */
    402     String asString() {
    403         if (mParent == null) {
    404             return "anyPolicy  ROOT\n";
    405         } else {
    406             StringBuilder sb = new StringBuilder();
    407             for (int i = 0, n = getDepth(); i < n; i++) {
    408                 sb.append("  ");
    409             }
    410             sb.append(policyToString(getValidPolicy()));
    411             sb.append("  CRIT: ");
    412             sb.append(isCritical());
    413             sb.append("  EP: ");
    414             for (String policy : getExpectedPolicies()) {
    415                 sb.append(policyToString(policy));
    416                 sb.append(" ");
    417             }
    418             sb.append(" (");
    419             sb.append(getDepth());
    420             sb.append(")\n");
    421             return sb.toString();
    422         }
    423     }
    424 }
    425