1 /*
2  * Copyright (c) 1997, 2021, 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 java.security;
27 
28 import sun.security.util.IOUtils;
29 
30 import java.io.IOException;
31 import java.io.ByteArrayInputStream;
32 import java.security.cert.Certificate;
33 import java.util.ArrayList;
34 import java.util.Hashtable;
35 import java.lang.reflect.*;
36 import java.security.cert.*;
37 import java.util.List;
38 
39 /**
40  * The UnresolvedPermission class is used to hold Permissions that
41  * were "unresolved" when the Policy was initialized.
42  * An unresolved permission is one whose actual Permission class
43  * does not yet exist at the time the Policy is initialized (see below).
44  *
45  * <p>The policy for a Java runtime (specifying
46  * which permissions are available for code from various principals)
47  * is represented by a Policy object.
48  * Whenever a Policy is initialized or refreshed, Permission objects of
49  * appropriate classes are created for all permissions
50  * allowed by the Policy.
51  *
52  * <p>Many permission class types
53  * referenced by the policy configuration are ones that exist
54  * locally (i.e., ones that can be found on CLASSPATH).
55  * Objects for such permissions can be instantiated during
56  * Policy initialization. For example, it is always possible
57  * to instantiate a java.io.FilePermission, since the
58  * FilePermission class is found on the CLASSPATH.
59  *
60  * <p>Other permission classes may not yet exist during Policy
61  * initialization. For example, a referenced permission class may
62  * be in a JAR file that will later be loaded.
63  * For each such class, an UnresolvedPermission is instantiated.
64  * Thus, an UnresolvedPermission is essentially a "placeholder"
65  * containing information about the permission.
66  *
67  * <p>Later, when code calls AccessController.checkPermission
68  * on a permission of a type that was previously unresolved,
69  * but whose class has since been loaded, previously-unresolved
70  * permissions of that type are "resolved". That is,
71  * for each such UnresolvedPermission, a new object of
72  * the appropriate class type is instantiated, based on the
73  * information in the UnresolvedPermission.
74  *
75  * <p> To instantiate the new class, UnresolvedPermission assumes
76  * the class provides a zero, one, and/or two-argument constructor.
77  * The zero-argument constructor would be used to instantiate
78  * a permission without a name and without actions.
79  * A one-arg constructor is assumed to take a {@code String}
80  * name as input, and a two-arg constructor is assumed to take a
81  * {@code String} name and {@code String} actions
82  * as input.  UnresolvedPermission may invoke a
83  * constructor with a {@code null} name and/or actions.
84  * If an appropriate permission constructor is not available,
85  * the UnresolvedPermission is ignored and the relevant permission
86  * will not be granted to executing code.
87  *
88  * <p> The newly created permission object replaces the
89  * UnresolvedPermission, which is removed.
90  *
91  * <p> Note that the {@code getName} method for an
92  * {@code UnresolvedPermission} returns the
93  * {@code type} (class name) for the underlying permission
94  * that has not been resolved.
95  *
96  * @see java.security.Permission
97  * @see java.security.Permissions
98  * @see java.security.PermissionCollection
99  * @see java.security.Policy
100  *
101  *
102  * @author Roland Schemers
103  * @since 1.2
104  */
105 
106 public final class UnresolvedPermission extends Permission
107 implements java.io.Serializable
108 {
109 
110     @java.io.Serial
111     private static final long serialVersionUID = -4821973115467008846L;
112 
113     private static final sun.security.util.Debug debug =
114         sun.security.util.Debug.getInstance
115         ("policy,access", "UnresolvedPermission");
116 
117     /**
118      * The class name of the Permission class that will be
119      * created when this unresolved permission is resolved.
120      *
121      * @serial
122      */
123     private String type;
124 
125     /**
126      * The permission name.
127      *
128      * @serial
129      */
130     private String name;
131 
132     /**
133      * The actions of the permission.
134      *
135      * @serial
136      */
137     private String actions;
138 
139     private transient java.security.cert.Certificate[] certs;
140 
141     /**
142      * Creates a new UnresolvedPermission containing the permission
143      * information needed later to actually create a Permission of the
144      * specified class, when the permission is resolved.
145      *
146      * @param type the class name of the Permission class that will be
147      * created when this unresolved permission is resolved.
148      * @param name the name of the permission.
149      * @param actions the actions of the permission.
150      * @param certs the certificates the permission's class was signed with.
151      * This is a list of certificate chains, where each chain is composed of a
152      * signer certificate and optionally its supporting certificate chain.
153      * Each chain is ordered bottom-to-top (i.e., with the signer certificate
154      * first and the (root) certificate authority last). The signer
155      * certificates are copied from the array. Subsequent changes to
156      * the array will not affect this UnsolvedPermission.
157      */
UnresolvedPermission(String type, String name, String actions, java.security.cert.Certificate[] certs)158     public UnresolvedPermission(String type,
159                                 String name,
160                                 String actions,
161                                 java.security.cert.Certificate[] certs)
162     {
163         super(type);
164 
165         if (type == null)
166                 throw new NullPointerException("type can't be null");
167 
168         this.type = type;
169         this.name = name;
170         this.actions = actions;
171         if (certs != null) {
172             // Extract the signer certs from the list of certificates.
173             for (int i=0; i<certs.length; i++) {
174                 if (!(certs[i] instanceof X509Certificate)) {
175                     // there is no concept of signer certs, so we store the
176                     // entire cert array
177                     this.certs = certs.clone();
178                     break;
179                 }
180             }
181 
182             if (this.certs == null) {
183                 // Go through the list of certs and see if all the certs are
184                 // signer certs.
185                 int i = 0;
186                 int count = 0;
187                 while (i < certs.length) {
188                     count++;
189                     while (((i+1) < certs.length) &&
190                            ((X509Certificate)certs[i]).getIssuerX500Principal().equals(
191                                ((X509Certificate)certs[i+1]).getSubjectX500Principal())) {
192                         i++;
193                     }
194                     i++;
195                 }
196                 if (count == certs.length) {
197                     // All the certs are signer certs, so we store the entire
198                     // array
199                     this.certs = certs.clone();
200                 }
201 
202                 if (this.certs == null) {
203                     // extract the signer certs
204                     ArrayList<java.security.cert.Certificate> signerCerts =
205                         new ArrayList<>();
206                     i = 0;
207                     while (i < certs.length) {
208                         signerCerts.add(certs[i]);
209                         while (((i+1) < certs.length) &&
210                             ((X509Certificate)certs[i]).getIssuerX500Principal().equals(
211                               ((X509Certificate)certs[i+1]).getSubjectX500Principal())) {
212                             i++;
213                         }
214                         i++;
215                     }
216                     this.certs =
217                         new java.security.cert.Certificate[signerCerts.size()];
218                     signerCerts.toArray(this.certs);
219                 }
220             }
221         }
222     }
223 
224 
225     private static final Class<?>[] PARAMS0 = { };
226     private static final Class<?>[] PARAMS1 = { String.class };
227     private static final Class<?>[] PARAMS2 = { String.class, String.class };
228 
229     /**
230      * try and resolve this permission using the class loader of the permission
231      * that was passed in.
232      */
resolve(Permission p, java.security.cert.Certificate[] certs)233     Permission resolve(Permission p, java.security.cert.Certificate[] certs) {
234         if (this.certs != null) {
235             // if p wasn't signed, we don't have a match
236             if (certs == null) {
237                 return null;
238             }
239 
240             // all certs in this.certs must be present in certs
241             boolean match;
242             for (int i = 0; i < this.certs.length; i++) {
243                 match = false;
244                 for (int j = 0; j < certs.length; j++) {
245                     if (this.certs[i].equals(certs[j])) {
246                         match = true;
247                         break;
248                     }
249                 }
250                 if (!match) return null;
251             }
252         }
253         try {
254             Class<?> pc = p.getClass();
255 
256             if (name == null && actions == null) {
257                 try {
258                     Constructor<?> c = pc.getConstructor(PARAMS0);
259                     return (Permission)c.newInstance(new Object[] {});
260                 } catch (NoSuchMethodException ne) {
261                     try {
262                         Constructor<?> c = pc.getConstructor(PARAMS1);
263                         return (Permission) c.newInstance(
264                               new Object[] { name});
265                     } catch (NoSuchMethodException ne1) {
266                         Constructor<?> c = pc.getConstructor(PARAMS2);
267                         return (Permission) c.newInstance(
268                               new Object[] { name, actions });
269                     }
270                 }
271             } else {
272                 if (name != null && actions == null) {
273                     try {
274                         Constructor<?> c = pc.getConstructor(PARAMS1);
275                         return (Permission) c.newInstance(
276                               new Object[] { name});
277                     } catch (NoSuchMethodException ne) {
278                         Constructor<?> c = pc.getConstructor(PARAMS2);
279                         return (Permission) c.newInstance(
280                               new Object[] { name, actions });
281                     }
282                 } else {
283                     Constructor<?> c = pc.getConstructor(PARAMS2);
284                     return (Permission) c.newInstance(
285                           new Object[] { name, actions });
286                 }
287             }
288         } catch (NoSuchMethodException nsme) {
289             if (debug != null ) {
290                 debug.println("NoSuchMethodException:\n  could not find " +
291                         "proper constructor for " + type);
292                 nsme.printStackTrace();
293             }
294             return null;
295         } catch (Exception e) {
296             if (debug != null ) {
297                 debug.println("unable to instantiate " + name);
298                 e.printStackTrace();
299             }
300             return null;
301         }
302     }
303 
304     /**
305      * This method always returns false for unresolved permissions.
306      * That is, an UnresolvedPermission is never considered to
307      * imply another permission.
308      *
309      * @param p the permission to check against.
310      *
311      * @return false.
312      */
implies(Permission p)313     public boolean implies(Permission p) {
314         return false;
315     }
316 
317     /**
318      * Checks two UnresolvedPermission objects for equality.
319      * Checks that {@code obj} is an UnresolvedPermission, and has
320      * the same type (class) name, permission name, actions, and
321      * certificates as this object.
322      *
323      * <p> To determine certificate equality, this method only compares
324      * actual signer certificates.  Supporting certificate chains
325      * are not taken into consideration by this method.
326      *
327      * @param obj the object we are testing for equality with this object.
328      *
329      * @return true if obj is an UnresolvedPermission, and has the same
330      * type (class) name, permission name, actions, and
331      * certificates as this object.
332      */
equals(Object obj)333     public boolean equals(Object obj) {
334         if (obj == this)
335             return true;
336 
337         if (!(obj instanceof UnresolvedPermission that))
338             return false;
339 
340         // check type
341         if (!this.type.equals(that.type)) {
342             return false;
343         }
344 
345         // check name
346         if (this.name == null) {
347             if (that.name != null) {
348                 return false;
349             }
350         } else if (!this.name.equals(that.name)) {
351             return false;
352         }
353 
354         // check actions
355         if (this.actions == null) {
356             if (that.actions != null) {
357                 return false;
358             }
359         } else {
360             if (!this.actions.equals(that.actions)) {
361                 return false;
362             }
363         }
364 
365         // check certs
366         if ((this.certs == null && that.certs != null) ||
367             (this.certs != null && that.certs == null) ||
368             (this.certs != null && that.certs != null &&
369                 this.certs.length != that.certs.length)) {
370             return false;
371         }
372 
373         int i,j;
374         boolean match;
375 
376         for (i = 0; this.certs != null && i < this.certs.length; i++) {
377             match = false;
378             for (j = 0; j < that.certs.length; j++) {
379                 if (this.certs[i].equals(that.certs[j])) {
380                     match = true;
381                     break;
382                 }
383             }
384             if (!match) return false;
385         }
386 
387         for (i = 0; that.certs != null && i < that.certs.length; i++) {
388             match = false;
389             for (j = 0; j < this.certs.length; j++) {
390                 if (that.certs[i].equals(this.certs[j])) {
391                     match = true;
392                     break;
393                 }
394             }
395             if (!match) return false;
396         }
397         return true;
398     }
399 
400     /**
401      * Returns the hash code value for this object.
402      *
403      * @return a hash code value for this object.
404      */
405 
hashCode()406     public int hashCode() {
407         int hash = type.hashCode();
408         if (name != null)
409             hash ^= name.hashCode();
410         if (actions != null)
411             hash ^= actions.hashCode();
412         return hash;
413     }
414 
415     /**
416      * Returns the canonical string representation of the actions,
417      * which currently is the empty string "", since there are no actions for
418      * an UnresolvedPermission. That is, the actions for the
419      * permission that will be created when this UnresolvedPermission
420      * is resolved may be non-null, but an UnresolvedPermission
421      * itself is never considered to have any actions.
422      *
423      * @return the empty string "".
424      */
getActions()425     public String getActions()
426     {
427         return "";
428     }
429 
430     /**
431      * Get the type (class name) of the underlying permission that
432      * has not been resolved.
433      *
434      * @return the type (class name) of the underlying permission that
435      *  has not been resolved
436      *
437      * @since 1.5
438      */
getUnresolvedType()439     public String getUnresolvedType() {
440         return type;
441     }
442 
443     /**
444      * Get the target name of the underlying permission that
445      * has not been resolved.
446      *
447      * @return the target name of the underlying permission that
448      *          has not been resolved, or {@code null},
449      *          if there is no target name
450      *
451      * @since 1.5
452      */
getUnresolvedName()453     public String getUnresolvedName() {
454         return name;
455     }
456 
457     /**
458      * Get the actions for the underlying permission that
459      * has not been resolved.
460      *
461      * @return the actions for the underlying permission that
462      *          has not been resolved, or {@code null}
463      *          if there are no actions
464      *
465      * @since 1.5
466      */
getUnresolvedActions()467     public String getUnresolvedActions() {
468         return actions;
469     }
470 
471     /**
472      * Get the signer certificates (without any supporting chain)
473      * for the underlying permission that has not been resolved.
474      *
475      * @return the signer certificates for the underlying permission that
476      * has not been resolved, or null, if there are no signer certificates.
477      * Returns a new array each time this method is called.
478      *
479      * @since 1.5
480      */
getUnresolvedCerts()481     public java.security.cert.Certificate[] getUnresolvedCerts() {
482         return (certs == null) ? null : certs.clone();
483     }
484 
485     /**
486      * Returns a string describing this UnresolvedPermission.  The convention
487      * is to specify the class name, the permission name, and the actions, in
488      * the following format: '(unresolved "ClassName" "name" "actions")'.
489      *
490      * @return information about this UnresolvedPermission.
491      */
toString()492     public String toString() {
493         return "(unresolved " + type + " " + name + " " + actions + ")";
494     }
495 
496     /**
497      * Returns a new PermissionCollection object for storing
498      * UnresolvedPermission  objects.
499      *
500      * @return a new PermissionCollection object suitable for
501      * storing UnresolvedPermissions.
502      */
503 
newPermissionCollection()504     public PermissionCollection newPermissionCollection() {
505         return new UnresolvedPermissionCollection();
506     }
507 
508     /**
509      * Writes this object out to a stream (i.e., serializes it).
510      *
511      * @serialData An initial {@code String} denoting the
512      * {@code type} is followed by a {@code String} denoting the
513      * {@code name} is followed by a {@code String} denoting the
514      * {@code actions} is followed by an {@code int} indicating the
515      * number of certificates to follow
516      * (a value of "zero" denotes that there are no certificates associated
517      * with this object).
518      * Each certificate is written out starting with a {@code String}
519      * denoting the certificate type, followed by an
520      * {@code int} specifying the length of the certificate encoding,
521      * followed by the certificate encoding itself which is written out as an
522      * array of bytes.
523      *
524      * @param  oos the {@code ObjectOutputStream} to which data is written
525      * @throws IOException if an I/O error occurs
526      */
527     @java.io.Serial
writeObject(java.io.ObjectOutputStream oos)528     private void writeObject(java.io.ObjectOutputStream oos)
529         throws IOException
530     {
531         oos.defaultWriteObject();
532 
533         if (certs==null || certs.length==0) {
534             oos.writeInt(0);
535         } else {
536             // write out the total number of certs
537             oos.writeInt(certs.length);
538             // write out each cert, including its type
539             for (int i=0; i < certs.length; i++) {
540                 java.security.cert.Certificate cert = certs[i];
541                 try {
542                     oos.writeUTF(cert.getType());
543                     byte[] encoded = cert.getEncoded();
544                     oos.writeInt(encoded.length);
545                     oos.write(encoded);
546                 } catch (CertificateEncodingException cee) {
547                     throw new IOException(cee.getMessage());
548                 }
549             }
550         }
551     }
552 
553     /**
554      * Restores this object from a stream (i.e., deserializes it).
555      *
556      * @param  ois the {@code ObjectInputStream} from which data is read
557      * @throws IOException if an I/O error occurs
558      * @throws ClassNotFoundException if a serialized class cannot be loaded
559      */
560     @java.io.Serial
readObject(java.io.ObjectInputStream ois)561     private void readObject(java.io.ObjectInputStream ois)
562         throws IOException, ClassNotFoundException
563     {
564         CertificateFactory cf;
565         Hashtable<String, CertificateFactory> cfs = null;
566         List<Certificate> certList = null;
567 
568         ois.defaultReadObject();
569 
570         if (type == null)
571                 throw new NullPointerException("type can't be null");
572 
573         // process any new-style certs in the stream (if present)
574         int size = ois.readInt();
575         if (size > 0) {
576             // we know of 3 different cert types: X.509, PGP, SDSI, which
577             // could all be present in the stream at the same time
578             cfs = new Hashtable<>(3);
579             certList = new ArrayList<>(size > 20 ? 20 : size);
580         } else if (size < 0) {
581             throw new IOException("size cannot be negative");
582         }
583 
584         for (int i=0; i<size; i++) {
585             // read the certificate type, and instantiate a certificate
586             // factory of that type (reuse existing factory if possible)
587             String certType = ois.readUTF();
588             if (cfs.containsKey(certType)) {
589                 // reuse certificate factory
590                 cf = cfs.get(certType);
591             } else {
592                 // create new certificate factory
593                 try {
594                     cf = CertificateFactory.getInstance(certType);
595                 } catch (CertificateException ce) {
596                     throw new ClassNotFoundException
597                         ("Certificate factory for "+certType+" not found");
598                 }
599                 // store the certificate factory so we can reuse it later
600                 cfs.put(certType, cf);
601             }
602             // parse the certificate
603             byte[] encoded = IOUtils.readExactlyNBytes(ois, ois.readInt());
604             ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
605             try {
606                 certList.add(cf.generateCertificate(bais));
607             } catch (CertificateException ce) {
608                 throw new IOException(ce.getMessage());
609             }
610             bais.close();
611         }
612         if (certList != null) {
613             this.certs = certList.toArray(
614                     new java.security.cert.Certificate[size]);
615         }
616     }
617 }
618