1 /*
   2  * Copyright (c) 2000, 2014, 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 5280
  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 }