1 /*
2  * Copyright (c) 1999, 2016, 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 javax.crypto;
27
28 import java.security.*;
29 import java.util.Enumeration;
30 import java.util.Hashtable;
31 import java.util.Vector;
32 import java.util.NoSuchElementException;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.io.Serializable;
35 import java.io.InputStream;
36 import java.io.InputStreamReader;
37 import java.io.BufferedReader;
38 import java.io.ObjectStreamField;
39 import java.io.ObjectInputStream;
40 import java.io.ObjectOutputStream;
41 import java.io.IOException;
42
43 /**
44  * This class contains CryptoPermission objects, organized into
45  * PermissionCollections according to algorithm names.
46  *
47  * <p>When the <code>add</code> method is called to add a
48  * CryptoPermission, the CryptoPermission is stored in the
49  * appropriate PermissionCollection. If no such
50  * collection exists yet, the algorithm name associated with
51  * the CryptoPermission object is
52  * determined and the <code>newPermissionCollection</code> method
53  * is called on the CryptoPermission or CryptoAllPermission class to
54  * create the PermissionCollection and add it to the Permissions object.
55  *
56  * @see javax.crypto.CryptoPermission
57  * @see java.security.PermissionCollection
58  * @see java.security.Permissions
59  *
60  * @author Sharon Liu
61  * @since 1.4
62  */

63 final class CryptoPermissions extends PermissionCollection
64 implements Serializable {
65
66     private static final long serialVersionUID = 4946547168093391015L;
67
68     /**
69      * @serialField perms java.util.Hashtable
70      */

71     private static final ObjectStreamField[] serialPersistentFields = {
72         new ObjectStreamField("perms", Hashtable.class),
73     };
74
75     // Switched from Hashtable to ConcurrentHashMap to improve scalability.
76     // To maintain serialization compatibility, this field is made transient
77     // and custom readObject/writeObject methods are used.
78     private transient ConcurrentHashMap<String,PermissionCollection> perms;
79
80     /**
81      * Creates a new CryptoPermissions object containing
82      * no CryptoPermissionCollections.
83      */

84     CryptoPermissions() {
85         perms = new ConcurrentHashMap<>(7);
86     }
87
88     /**
89      * Populates the crypto policy from the specified
90      * InputStream into this CryptoPermissions object.
91      *
92      * @param in the InputStream to load from.
93      *
94      * @exception SecurityException if cannot load
95      * successfully.
96      */

97     void load(InputStream in)
98         throws IOException, CryptoPolicyParser.ParsingException {
99         CryptoPolicyParser parser = new CryptoPolicyParser();
100         parser.read(new BufferedReader(new InputStreamReader(in, "UTF-8")));
101
102         CryptoPermission[] parsingResult = parser.getPermissions();
103         for (int i = 0; i < parsingResult.length; i++) {
104             this.add(parsingResult[i]);
105         }
106     }
107
108     /**
109      * Returns true if this CryptoPermissions object doesn't
110      * contain any CryptoPermission objects; otherwise, returns
111      * false.
112      */

113     boolean isEmpty() {
114         return perms.isEmpty();
115     }
116
117     /**
118      * Adds a permission object to the PermissionCollection for the
119      * algorithm returned by
120      * <code>(CryptoPermission)permission.getAlgorithm()</code>.
121      *
122      * This method creates
123      * a new PermissionCollection object (and adds the permission to it)
124      * if an appropriate collection does not yet exist. <p>
125      *
126      * @param permission the Permission object to add.
127      *
128      * @exception SecurityException if this CryptoPermissions object is
129      * marked as readonly.
130      *
131      * @see isReadOnly
132      */

133     @Override
134     public void add(Permission permission) {
135
136         if (isReadOnly()) {
137             throw new SecurityException("Attempt to add a Permission " +
138                                         "to a readonly CryptoPermissions " +
139                                         "object");
140         }
141
142         if (!(permission instanceof CryptoPermission)) {
143             return;
144         }
145
146         CryptoPermission cryptoPerm = (CryptoPermission)permission;
147         PermissionCollection pc =
148                         getPermissionCollection(cryptoPerm);
149         pc.add(cryptoPerm);
150         String alg = cryptoPerm.getAlgorithm();
151         perms.putIfAbsent(alg, pc);
152     }
153
154     /**
155      * Checks if this object's PermissionCollection for permissons
156      * of the specified permission's algorithm implies the specified
157      * permission. Returns true if the checking succeeded.
158      *
159      * @param permission the Permission object to check.
160      *
161      * @return true if "permission" is implied by the permissions
162      * in the PermissionCollection it belongs to, false if not.
163      *
164      */

165     @Override
166     public boolean implies(Permission permission) {
167         if (!(permission instanceof CryptoPermission)) {
168             return false;
169         }
170
171         CryptoPermission cryptoPerm = (CryptoPermission)permission;
172
173         PermissionCollection pc =
174             getPermissionCollection(cryptoPerm.getAlgorithm());
175
176         if (pc != null) {
177             return pc.implies(cryptoPerm);
178         } else {
179             // none found
180             return false;
181         }
182     }
183
184     /**
185      * Returns an enumeration of all the Permission objects in all the
186      * PermissionCollections in this CryptoPermissions object.
187      *
188      * @return an enumeration of all the Permissions.
189      */

190     @Override
191     public Enumeration<Permission> elements() {
192         // go through each Permissions in the hash table
193         // and call their elements() function.
194         return new PermissionsEnumerator(perms.elements());
195     }
196
197     /**
198      * Returns a CryptoPermissions object which
199      * represents the minimum of the specified
200      * CryptoPermissions object and this
201      * CryptoPermissions object.
202      *
203      * @param other the CryptoPermission
204      * object to compare with this object.
205      */

206     CryptoPermissions getMinimum(CryptoPermissions other) {
207         if (other == null) {
208             return null;
209         }
210
211         if (this.perms.containsKey(CryptoAllPermission.ALG_NAME)) {
212             return other;
213         }
214
215         if (other.perms.containsKey(CryptoAllPermission.ALG_NAME)) {
216             return this;
217         }
218
219         CryptoPermissions ret = new CryptoPermissions();
220
221
222         PermissionCollection thatWildcard =
223                 other.perms.get(CryptoPermission.ALG_NAME_WILDCARD);
224         int maxKeySize = 0;
225         if (thatWildcard != null) {
226             maxKeySize = ((CryptoPermission)
227                     thatWildcard.elements().nextElement()).getMaxKeySize();
228         }
229         // For each algorithm in this CryptoPermissions,
230         // find out if there is anything we should add into
231         // ret.
232         Enumeration<String> thisKeys = this.perms.keys();
233         while (thisKeys.hasMoreElements()) {
234             String alg = thisKeys.nextElement();
235
236             PermissionCollection thisPc = this.perms.get(alg);
237             PermissionCollection thatPc = other.perms.get(alg);
238
239             CryptoPermission[] partialResult;
240
241             if (thatPc == null) {
242                 if (thatWildcard == null) {
243                     // The other CryptoPermissions
244                     // doesn't allow this given
245                     // algorithm at all. Just skip this
246                     // algorithm.
247                     continue;
248                 }
249                 partialResult = getMinimum(maxKeySize, thisPc);
250             } else {
251                 partialResult = getMinimum(thisPc, thatPc);
252             }
253
254             for (int i = 0; i < partialResult.length; i++) {
255                 ret.add(partialResult[i]);
256             }
257         }
258
259         PermissionCollection thisWildcard =
260                 this.perms.get(CryptoPermission.ALG_NAME_WILDCARD);
261
262         // If this CryptoPermissions doesn't
263         // have a wildcard, we are done.
264         if (thisWildcard == null) {
265             return ret;
266         }
267
268         // Deal with the algorithms only appear
269         // in the other CryptoPermissions.
270         maxKeySize =
271             ((CryptoPermission)
272                     thisWildcard.elements().nextElement()).getMaxKeySize();
273         Enumeration<String> thatKeys = other.perms.keys();
274         while (thatKeys.hasMoreElements()) {
275             String alg = thatKeys.nextElement();
276
277             if (this.perms.containsKey(alg)) {
278                 continue;
279             }
280
281             PermissionCollection thatPc = other.perms.get(alg);
282
283             CryptoPermission[] partialResult;
284
285             partialResult = getMinimum(maxKeySize, thatPc);
286
287             for (int i = 0; i < partialResult.length; i++) {
288                 ret.add(partialResult[i]);
289             }
290         }
291         return ret;
292     }
293
294     /**
295      * Get the minimum of the two given PermissionCollection
296      * <code>thisPc</code> and <code>thatPc</code>.
297      *
298      * @param thisPc the first given PermissionColloection
299      * object.
300      *
301      * @param thatPc the second given PermissionCollection
302      * object.
303      */

304     private CryptoPermission[] getMinimum(PermissionCollection thisPc,
305                                           PermissionCollection thatPc) {
306         Vector<CryptoPermission> permVector = new Vector<>(2);
307
308         Enumeration<Permission> thisPcPermissions = thisPc.elements();
309
310         // For each CryptoPermission in
311         // thisPc object, do the following:
312         // 1) if this CryptoPermission is implied
313         //     by thatPc, this CryptoPermission
314         //     should be returned, and we can
315         //     move on to check the next
316         //     CryptoPermission in thisPc.
317         // 2) otherwise, we should return
318         //     all CryptoPermissions in thatPc
319         //     which
320         //     are implied by this CryptoPermission.
321         //     Then we can move on to the
322         //     next CryptoPermission in thisPc.
323         while (thisPcPermissions.hasMoreElements()) {
324             CryptoPermission thisCp =
325                 (CryptoPermission)thisPcPermissions.nextElement();
326
327             Enumeration<Permission> thatPcPermissions = thatPc.elements();
328             while (thatPcPermissions.hasMoreElements()) {
329                 CryptoPermission thatCp =
330                     (CryptoPermission)thatPcPermissions.nextElement();
331
332                 if (thatCp.implies(thisCp)) {
333                     permVector.addElement(thisCp);
334                     break;
335                 }
336                 if (thisCp.implies(thatCp)) {
337                     permVector.addElement(thatCp);
338                 }
339             }
340         }
341
342         CryptoPermission[] ret = new CryptoPermission[permVector.size()];
343         permVector.copyInto(ret);
344         return ret;
345     }
346
347     /**
348      * Returns all the CryptoPermission objects in the given
349      * PermissionCollection object
350      * whose maximum keysize no greater than <code>maxKeySize</code>.
351      * For all CryptoPermission objects with a maximum keysize greater
352      * than <code>maxKeySize</code>, this method constructs a
353      * corresponding CryptoPermission object whose maximum keysize is
354      * set to <code>maxKeySize</code>, and includes that in the result.
355      *
356      * @param maxKeySize the given maximum key size.
357      *
358      * @param pc the given PermissionCollection object.
359      */

360     private CryptoPermission[] getMinimum(int maxKeySize,
361                                           PermissionCollection pc) {
362         Vector<CryptoPermission> permVector = new Vector<>(1);
363
364         Enumeration<Permission> enum_ = pc.elements();
365
366         while (enum_.hasMoreElements()) {
367             CryptoPermission cp =
368                 (CryptoPermission)enum_.nextElement();
369             if (cp.getMaxKeySize() <= maxKeySize) {
370                 permVector.addElement(cp);
371             } else {
372                 if (cp.getCheckParam()) {
373                     permVector.addElement(
374                            new CryptoPermission(cp.getAlgorithm(),
375                                                 maxKeySize,
376                                                 cp.getAlgorithmParameterSpec(),
377                                                 cp.getExemptionMechanism()));
378                 } else {
379                     permVector.addElement(
380                            new CryptoPermission(cp.getAlgorithm(),
381                                                 maxKeySize,
382                                                 cp.getExemptionMechanism()));
383                 }
384             }
385         }
386
387         CryptoPermission[] ret = new CryptoPermission[permVector.size()];
388         permVector.copyInto(ret);
389         return ret;
390     }
391
392     /**
393      * Returns the PermissionCollection for the
394      * specified algorithm. Returns null if there
395      * isn't such a PermissionCollection.
396      *
397      * @param alg the algorithm name.
398      */

399     PermissionCollection getPermissionCollection(String alg) {
400         // If this CryptoPermissions includes CryptoAllPermission,
401         // we should return CryptoAllPermission.
402         PermissionCollection pc = perms.get(CryptoAllPermission.ALG_NAME);
403         if (pc == null) {
404             pc = perms.get(alg);
405
406             // If there isn't a PermissionCollection for
407             // the given algorithm,we should return the
408             // PermissionCollection for the wildcard
409             // if there is one.
410             if (pc == null) {
411                 pc = perms.get(CryptoPermission.ALG_NAME_WILDCARD);
412             }
413         }
414         return pc;
415     }
416
417     /**
418      * Returns the PermissionCollection for the algorithm
419      * associated with the specified CryptoPermission
420      * object. Creates such a PermissionCollection
421      * if such a PermissionCollection does not
422      * exist yet.
423      *
424      * @param cryptoPerm the CryptoPermission object.
425      */

426     private PermissionCollection getPermissionCollection(
427                                           CryptoPermission cryptoPerm) {
428
429         String alg = cryptoPerm.getAlgorithm();
430
431         PermissionCollection pc = perms.get(alg);
432
433         if (pc == null) {
434             pc = cryptoPerm.newPermissionCollection();
435         }
436         return pc;
437     }
438
439     private void readObject(ObjectInputStream s)
440         throws IOException, ClassNotFoundException {
441         ObjectInputStream.GetField fields = s.readFields();
442         @SuppressWarnings("unchecked")
443         Hashtable<String,PermissionCollection> permTable =
444                 (Hashtable<String,PermissionCollection>)
445                 (fields.get("perms"null));
446         if (permTable != null) {
447             perms = new ConcurrentHashMap<>(permTable);
448         } else {
449             perms = new ConcurrentHashMap<>();
450         }
451     }
452
453     private void writeObject(ObjectOutputStream s) throws IOException {
454         Hashtable<String,PermissionCollection> permTable =
455                 new Hashtable<>(perms);
456         ObjectOutputStream.PutField fields = s.putFields();
457         fields.put("perms", permTable);
458         s.writeFields();
459     }
460 }
461
462 final class PermissionsEnumerator implements Enumeration<Permission> {
463
464     // all the perms
465     private final Enumeration<PermissionCollection> perms;
466     // the current set
467     private Enumeration<Permission> permset;
468
469     PermissionsEnumerator(Enumeration<PermissionCollection> e) {
470         perms = e;
471         permset = getNextEnumWithMore();
472     }
473
474     @Override
475     public synchronized boolean hasMoreElements() {
476         // if we enter with permissionimpl null, we know
477         // there are no more left.
478
479         if (permset == null) {
480             return  false;
481         }
482
483         // try to see if there are any left in the current one
484
485         if (permset.hasMoreElements()) {
486             return true;
487         }
488
489         // get the next one that has something in it...
490         permset = getNextEnumWithMore();
491
492         // if it is null, we are done!
493         return (permset != null);
494     }
495
496     @Override
497     public synchronized Permission nextElement() {
498         // hasMoreElements will update permset to the next permset
499         // with something in it...
500
501         if (hasMoreElements()) {
502             return permset.nextElement();
503         } else {
504             throw new NoSuchElementException("PermissionsEnumerator");
505         }
506     }
507
508     private Enumeration<Permission> getNextEnumWithMore() {
509         while (perms.hasMoreElements()) {
510             PermissionCollection pc = perms.nextElement();
511             Enumeration<Permission> next = pc.elements();
512             if (next.hasMoreElements()) {
513                 return next;
514             }
515         }
516         return null;
517     }
518 }
519