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