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