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
26package javax.crypto;
27
28import java.security.*;
29import java.util.Enumeration;
30import java.util.Hashtable;
31import java.util.Vector;
32import java.util.NoSuchElementException;
33import java.util.concurrent.ConcurrentHashMap;
34import java.io.Serializable;
35import java.io.InputStream;
36import java.io.InputStreamReader;
37import java.io.BufferedReader;
38import java.io.ObjectStreamField;
39import java.io.ObjectInputStream;
40import java.io.ObjectOutputStream;
41import 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 */
63final class CryptoPermissions extends PermissionCollection
64implements 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
462final 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