View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, 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 java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Enumeration;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.WeakHashMap;
34  import sun.misc.JavaSecurityProtectionDomainAccess;
35  import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
36  import sun.security.util.Debug;
37  import sun.security.util.SecurityConstants;
38  import sun.misc.JavaSecurityAccess;
39  import sun.misc.SharedSecrets;
40  
41  /**
42   *
43   *<p>
44   * This ProtectionDomain class encapsulates the characteristics of a domain,
45   * which encloses a set of classes whose instances are granted a set
46   * of permissions when being executed on behalf of a given set of Principals.
47   * <p>
48   * A static set of permissions can be bound to a ProtectionDomain when it is
49   * constructed; such permissions are granted to the domain regardless of the
50   * Policy in force. However, to support dynamic security policies, a
51   * ProtectionDomain can also be constructed such that it is dynamically
52   * mapped to a set of permissions by the current Policy whenever a permission
53   * is checked.
54   * <p>
55   *
56   * @author Li Gong
57   * @author Roland Schemers
58   * @author Gary Ellison
59   */
60  
61  public class ProtectionDomain {
62  
63      static {
64          // Set up JavaSecurityAccess in SharedSecrets
65          SharedSecrets.setJavaSecurityAccess(
66              new JavaSecurityAccess() {
67                  public <T> T doIntersectionPrivilege(
68                      PrivilegedAction<T> action,
69                      final AccessControlContext stack,
70                      final AccessControlContext context)
71                  {
72                      if (action == null) {
73                          throw new NullPointerException();
74                      }
75                      return AccessController.doPrivileged(
76                          action,
77                          new AccessControlContext(
78                              stack.getContext(), context).optimize()
79                      );
80                  }
81  
82                  public <T> T doIntersectionPrivilege(
83                      PrivilegedAction<T> action,
84                      AccessControlContext context)
85                  {
86                      return doIntersectionPrivilege(action,
87                          AccessController.getContext(), context);
88                  }
89              }
90         );
91      }
92  
93      /* CodeSource */
94      private CodeSource codesource ;
95  
96      /* ClassLoader the protection domain was consed from */
97      private ClassLoader classloader;
98  
99      /* Principals running-as within this protection domain */
100     private Principal[] principals;
101 
102     /* the rights this protection domain is granted */
103     private PermissionCollection permissions;
104 
105     /* if the permissions object has AllPermission */
106     private boolean hasAllPerm = false;
107 
108     /* the PermissionCollection is static (pre 1.4 constructor)
109        or dynamic (via a policy refresh) */
110     private boolean staticPermissions;
111 
112     /*
113      * An object used as a key when the ProtectionDomain is stored in a Map.
114      */
115     final Key key = new Key();
116 
117     private static final Debug debug = Debug.getInstance("domain");
118 
119     /**
120      * Creates a new ProtectionDomain with the given CodeSource and
121      * Permissions. If the permissions object is not null, then
122      *  {@code setReadOnly())} will be called on the passed in
123      * Permissions object. The only permissions granted to this domain
124      * are the ones specified; the current Policy will not be consulted.
125      *
126      * @param codesource the codesource associated with this domain
127      * @param permissions the permissions granted to this domain
128      */
129     public ProtectionDomain(CodeSource codesource,
130                             PermissionCollection permissions) {
131         this.codesource = codesource;
132         if (permissions != null) {
133             this.permissions = permissions;
134             this.permissions.setReadOnly();
135             if (permissions instanceof Permissions &&
136                 ((Permissions)permissions).allPermission != null) {
137                 hasAllPerm = true;
138             }
139         }
140         this.classloader = null;
141         this.principals = new Principal[0];
142         staticPermissions = true;
143     }
144 
145     /**
146      * Creates a new ProtectionDomain qualified by the given CodeSource,
147      * Permissions, ClassLoader and array of Principals. If the
148      * permissions object is not null, then {@code setReadOnly()}
149      * will be called on the passed in Permissions object.
150      * The permissions granted to this domain are dynamic; they include
151      * both the static permissions passed to this constructor, and any
152      * permissions granted to this domain by the current Policy at the
153      * time a permission is checked.
154      * <p>
155      * This constructor is typically used by
156      * {@link SecureClassLoader ClassLoaders}
157      * and {@link DomainCombiner DomainCombiners} which delegate to
158      * {@code Policy} to actively associate the permissions granted to
159      * this domain. This constructor affords the
160      * Policy provider the opportunity to augment the supplied
161      * PermissionCollection to reflect policy changes.
162      * <p>
163      *
164      * @param codesource the CodeSource associated with this domain
165      * @param permissions the permissions granted to this domain
166      * @param classloader the ClassLoader associated with this domain
167      * @param principals the array of Principals associated with this
168      * domain. The contents of the array are copied to protect against
169      * subsequent modification.
170      * @see Policy#refresh
171      * @see Policy#getPermissions(ProtectionDomain)
172      * @since 1.4
173      */
174     public ProtectionDomain(CodeSource codesource,
175                             PermissionCollection permissions,
176                             ClassLoader classloader,
177                             Principal[] principals) {
178         this.codesource = codesource;
179         if (permissions != null) {
180             this.permissions = permissions;
181             this.permissions.setReadOnly();
182             if (permissions instanceof Permissions &&
183                 ((Permissions)permissions).allPermission != null) {
184                 hasAllPerm = true;
185             }
186         }
187         this.classloader = classloader;
188         this.principals = (principals != null ? principals.clone():
189                            new Principal[0]);
190         staticPermissions = false;
191     }
192 
193     /**
194      * Returns the CodeSource of this domain.
195      * @return the CodeSource of this domain which may be null.
196      * @since 1.2
197      */
198     public final CodeSource getCodeSource() {
199         return this.codesource;
200     }
201 
202 
203     /**
204      * Returns the ClassLoader of this domain.
205      * @return the ClassLoader of this domain which may be null.
206      *
207      * @since 1.4
208      */
209     public final ClassLoader getClassLoader() {
210         return this.classloader;
211     }
212 
213 
214     /**
215      * Returns an array of principals for this domain.
216      * @return a non-null array of principals for this domain.
217      * Returns a new array each time this method is called.
218      *
219      * @since 1.4
220      */
221     public final Principal[] getPrincipals() {
222         return this.principals.clone();
223     }
224 
225     /**
226      * Returns the static permissions granted to this domain.
227      *
228      * @return the static set of permissions for this domain which may be null.
229      * @see Policy#refresh
230      * @see Policy#getPermissions(ProtectionDomain)
231      */
232     public final PermissionCollection getPermissions() {
233         return permissions;
234     }
235 
236     /**
237      * Check and see if this ProtectionDomain implies the permissions
238      * expressed in the Permission object.
239      * <p>
240      * The set of permissions evaluated is a function of whether the
241      * ProtectionDomain was constructed with a static set of permissions
242      * or it was bound to a dynamically mapped set of permissions.
243      * <p>
244      * If the ProtectionDomain was constructed to a
245      * {@link #ProtectionDomain(CodeSource, PermissionCollection)
246      * statically bound} PermissionCollection then the permission will
247      * only be checked against the PermissionCollection supplied at
248      * construction.
249      * <p>
250      * However, if the ProtectionDomain was constructed with
251      * the constructor variant which supports
252      * {@link #ProtectionDomain(CodeSource, PermissionCollection,
253      * ClassLoader, java.security.Principal[]) dynamically binding}
254      * permissions, then the permission will be checked against the
255      * combination of the PermissionCollection supplied at construction and
256      * the current Policy binding.
257      * <p>
258      *
259      * @param permission the Permission object to check.
260      *
261      * @return true if "permission" is implicit to this ProtectionDomain.
262      */
263     public boolean implies(Permission permission) {
264 
265         if (hasAllPerm) {
266             // internal permission collection already has AllPermission -
267             // no need to go to policy
268             return true;
269         }
270 
271         if (!staticPermissions &&
272             Policy.getPolicyNoCheck().implies(this, permission))
273             return true;
274         if (permissions != null)
275             return permissions.implies(permission);
276 
277         return false;
278     }
279 
280     // called by the VM -- do not remove
281     boolean impliesCreateAccessControlContext() {
282         return implies(SecurityConstants.CREATE_ACC_PERMISSION);
283     }
284 
285     /**
286      * Convert a ProtectionDomain to a String.
287      */
288     @Override public String toString() {
289         String pals = "<no principals>";
290         if (principals != null && principals.length > 0) {
291             StringBuilder palBuf = new StringBuilder("(principals ");
292 
293             for (int i = 0; i < principals.length; i++) {
294                 palBuf.append(principals[i].getClass().getName() +
295                             " \"" + principals[i].getName() +
296                             "\"");
297                 if (i < principals.length-1)
298                     palBuf.append(",\n");
299                 else
300                     palBuf.append(")\n");
301             }
302             pals = palBuf.toString();
303         }
304 
305         // Check if policy is set; we don't want to load
306         // the policy prematurely here
307         PermissionCollection pc = Policy.isSet() && seeAllp() ?
308                                       mergePermissions():
309                                       getPermissions();
310 
311         return "ProtectionDomain "+
312             " "+codesource+"\n"+
313             " "+classloader+"\n"+
314             " "+pals+"\n"+
315             " "+pc+"\n";
316     }
317 
318     /**
319      * Return true (merge policy permissions) in the following cases:
320      *
321      * . SecurityManager is null
322      *
323      * . SecurityManager is not null,
324      *          debug is not null,
325      *          SecurityManager impelmentation is in bootclasspath,
326      *          Policy implementation is in bootclasspath
327      *          (the bootclasspath restrictions avoid recursion)
328      *
329      * . SecurityManager is not null,
330      *          debug is null,
331      *          caller has Policy.getPolicy permission
332      */
333     private static boolean seeAllp() {
334         SecurityManager sm = System.getSecurityManager();
335 
336         if (sm == null) {
337             return true;
338         } else {
339             if (debug != null) {
340                 if (sm.getClass().getClassLoader() == null &&
341                     Policy.getPolicyNoCheck().getClass().getClassLoader()
342                                                                 == null) {
343                     return true;
344                 }
345             } else {
346                 try {
347                     sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION);
348                     return true;
349                 } catch (SecurityException se) {
350                     // fall thru and return false
351                 }
352             }
353         }
354 
355         return false;
356     }
357 
358     private PermissionCollection mergePermissions() {
359         if (staticPermissions)
360             return permissions;
361 
362         PermissionCollection perms =
363             java.security.AccessController.doPrivileged
364             (new java.security.PrivilegedAction<PermissionCollection>() {
365                     public PermissionCollection run() {
366                         Policy p = Policy.getPolicyNoCheck();
367                         return p.getPermissions(ProtectionDomain.this);
368                     }
369                 });
370 
371         Permissions mergedPerms = new Permissions();
372         int swag = 32;
373         int vcap = 8;
374         Enumeration<Permission> e;
375         List<Permission> pdVector = new ArrayList<>(vcap);
376         List<Permission> plVector = new ArrayList<>(swag);
377 
378         //
379         // Build a vector of domain permissions for subsequent merge
380         if (permissions != null) {
381             synchronized (permissions) {
382                 e = permissions.elements();
383                 while (e.hasMoreElements()) {
384                     pdVector.add(e.nextElement());
385                 }
386             }
387         }
388 
389         //
390         // Build a vector of Policy permissions for subsequent merge
391         if (perms != null) {
392             synchronized (perms) {
393                 e = perms.elements();
394                 while (e.hasMoreElements()) {
395                     plVector.add(e.nextElement());
396                     vcap++;
397                 }
398             }
399         }
400 
401         if (perms != null && permissions != null) {
402             //
403             // Weed out the duplicates from the policy. Unless a refresh
404             // has occurred since the pd was consed this should result in
405             // an empty vector.
406             synchronized (permissions) {
407                 e = permissions.elements();   // domain vs policy
408                 while (e.hasMoreElements()) {
409                     Permission pdp = e.nextElement();
410                     Class<?> pdpClass = pdp.getClass();
411                     String pdpActions = pdp.getActions();
412                     String pdpName = pdp.getName();
413                     for (int i = 0; i < plVector.size(); i++) {
414                         Permission pp = plVector.get(i);
415                         if (pdpClass.isInstance(pp)) {
416                             // The equals() method on some permissions
417                             // have some side effects so this manual
418                             // comparison is sufficient.
419                             if (pdpName.equals(pp.getName()) &&
420                                 pdpActions.equals(pp.getActions())) {
421                                 plVector.remove(i);
422                                 break;
423                             }
424                         }
425                     }
426                 }
427             }
428         }
429 
430         if (perms !=null) {
431             // the order of adding to merged perms and permissions
432             // needs to preserve the bugfix 4301064
433 
434             for (int i = plVector.size()-1; i >= 0; i--) {
435                 mergedPerms.add(plVector.get(i));
436             }
437         }
438         if (permissions != null) {
439             for (int i = pdVector.size()-1; i >= 0; i--) {
440                 mergedPerms.add(pdVector.get(i));
441             }
442         }
443 
444         return mergedPerms;
445     }
446 
447     /**
448      * Used for storing ProtectionDomains as keys in a Map.
449      */
450     final class Key {}
451 
452     static {
453         SharedSecrets.setJavaSecurityProtectionDomainAccess(
454             new JavaSecurityProtectionDomainAccess() {
455                 public ProtectionDomainCache getProtectionDomainCache() {
456                     return new ProtectionDomainCache() {
457                         private final Map<Key, PermissionCollection> map =
458                             Collections.synchronizedMap
459                                 (new WeakHashMap<Key, PermissionCollection>());
460                         public void put(ProtectionDomain pd,
461                             PermissionCollection pc) {
462                             map.put((pd == null ? null : pd.key), pc);
463                         }
464                         public PermissionCollection get(ProtectionDomain pd) {
465                             return pd == null ? map.get(null) : map.get(pd.key);
466                         }
467                     };
468                 }
469             });
470     }
471 }