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 */ PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy, Set<PolicyQualifierInfo> qualifierSet, boolean criticalityIndicator, Set<String> expectedPolicySet, boolean generatedByPolicyMapping)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 */ PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node)132 PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) { 133 this(parent, node.mValidPolicy, node.mQualifierSet, 134 node.mCriticalityIndicator, node.mExpectedPolicySet, false); 135 } 136 137 @Override getParent()138 public PolicyNode getParent() { 139 return mParent; 140 } 141 142 @Override getChildren()143 public Iterator<PolicyNodeImpl> getChildren() { 144 return Collections.unmodifiableSet(mChildren).iterator(); 145 } 146 147 @Override getDepth()148 public int getDepth() { 149 return mDepth; 150 } 151 152 @Override getValidPolicy()153 public String getValidPolicy() { 154 return mValidPolicy; 155 } 156 157 @Override getPolicyQualifiers()158 public Set<PolicyQualifierInfo> getPolicyQualifiers() { 159 return Collections.unmodifiableSet(mQualifierSet); 160 } 161 162 @Override getExpectedPolicies()163 public Set<String> getExpectedPolicies() { 164 return Collections.unmodifiableSet(mExpectedPolicySet); 165 } 166 167 @Override isCritical()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 toString()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 isImmutable()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 */ setImmutable()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 */ addChild(PolicyNodeImpl child)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 */ addExpectedPolicy(String expectedPolicy)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 */ prune(int depth)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 */ deleteChild(PolicyNode childNode)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 */ copyTree()282 PolicyNodeImpl copyTree() { 283 return copyTree(null); 284 } 285 copyTree(PolicyNodeImpl parent)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 */ getPolicyNodes(int depth)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 */ getPolicyNodes(int depth, Set<PolicyNodeImpl> set)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 */ getPolicyNodesExpected(int depth, String expectedOID, boolean matchAny)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 getPolicyNodesExpectedHelper(int depth, String expectedOID, boolean matchAny)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 */ getPolicyNodesValid(int depth, String validOID)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 policyToString(String oid)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 */ asString()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