jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
changeset 25859 3317bb8137f4
parent 21278 ef8a3a2a72f2
child 27181 29f9c4f56e80
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.security;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import sun.misc.JavaSecurityProtectionDomainAccess;
+import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
+import sun.security.util.Debug;
+import sun.security.util.SecurityConstants;
+import sun.misc.JavaSecurityAccess;
+import sun.misc.SharedSecrets;
+
+/**
+ *
+ *<p>
+ * This ProtectionDomain class encapsulates the characteristics of a domain,
+ * which encloses a set of classes whose instances are granted a set
+ * of permissions when being executed on behalf of a given set of Principals.
+ * <p>
+ * A static set of permissions can be bound to a ProtectionDomain when it is
+ * constructed; such permissions are granted to the domain regardless of the
+ * Policy in force. However, to support dynamic security policies, a
+ * ProtectionDomain can also be constructed such that it is dynamically
+ * mapped to a set of permissions by the current Policy whenever a permission
+ * is checked.
+ * <p>
+ *
+ * @author Li Gong
+ * @author Roland Schemers
+ * @author Gary Ellison
+ */
+
+public class ProtectionDomain {
+
+    static {
+        // Set up JavaSecurityAccess in SharedSecrets
+        SharedSecrets.setJavaSecurityAccess(
+            new JavaSecurityAccess() {
+                public <T> T doIntersectionPrivilege(
+                    PrivilegedAction<T> action,
+                    final AccessControlContext stack,
+                    final AccessControlContext context)
+                {
+                    if (action == null) {
+                        throw new NullPointerException();
+                    }
+                    return AccessController.doPrivileged(
+                        action,
+                        new AccessControlContext(
+                            stack.getContext(), context).optimize()
+                    );
+                }
+
+                public <T> T doIntersectionPrivilege(
+                    PrivilegedAction<T> action,
+                    AccessControlContext context)
+                {
+                    return doIntersectionPrivilege(action,
+                        AccessController.getContext(), context);
+                }
+            }
+       );
+    }
+
+    /* CodeSource */
+    private CodeSource codesource ;
+
+    /* ClassLoader the protection domain was consed from */
+    private ClassLoader classloader;
+
+    /* Principals running-as within this protection domain */
+    private Principal[] principals;
+
+    /* the rights this protection domain is granted */
+    private PermissionCollection permissions;
+
+    /* if the permissions object has AllPermission */
+    private boolean hasAllPerm = false;
+
+    /* the PermissionCollection is static (pre 1.4 constructor)
+       or dynamic (via a policy refresh) */
+    private boolean staticPermissions;
+
+    /*
+     * An object used as a key when the ProtectionDomain is stored in a Map.
+     */
+    final Key key = new Key();
+
+    private static final Debug debug = Debug.getInstance("domain");
+
+    /**
+     * Creates a new ProtectionDomain with the given CodeSource and
+     * Permissions. If the permissions object is not null, then
+     *  {@code setReadOnly())} will be called on the passed in
+     * Permissions object. The only permissions granted to this domain
+     * are the ones specified; the current Policy will not be consulted.
+     *
+     * @param codesource the codesource associated with this domain
+     * @param permissions the permissions granted to this domain
+     */
+    public ProtectionDomain(CodeSource codesource,
+                            PermissionCollection permissions) {
+        this.codesource = codesource;
+        if (permissions != null) {
+            this.permissions = permissions;
+            this.permissions.setReadOnly();
+            if (permissions instanceof Permissions &&
+                ((Permissions)permissions).allPermission != null) {
+                hasAllPerm = true;
+            }
+        }
+        this.classloader = null;
+        this.principals = new Principal[0];
+        staticPermissions = true;
+    }
+
+    /**
+     * Creates a new ProtectionDomain qualified by the given CodeSource,
+     * Permissions, ClassLoader and array of Principals. If the
+     * permissions object is not null, then {@code setReadOnly()}
+     * will be called on the passed in Permissions object.
+     * The permissions granted to this domain are dynamic; they include
+     * both the static permissions passed to this constructor, and any
+     * permissions granted to this domain by the current Policy at the
+     * time a permission is checked.
+     * <p>
+     * This constructor is typically used by
+     * {@link SecureClassLoader ClassLoaders}
+     * and {@link DomainCombiner DomainCombiners} which delegate to
+     * {@code Policy} to actively associate the permissions granted to
+     * this domain. This constructor affords the
+     * Policy provider the opportunity to augment the supplied
+     * PermissionCollection to reflect policy changes.
+     * <p>
+     *
+     * @param codesource the CodeSource associated with this domain
+     * @param permissions the permissions granted to this domain
+     * @param classloader the ClassLoader associated with this domain
+     * @param principals the array of Principals associated with this
+     * domain. The contents of the array are copied to protect against
+     * subsequent modification.
+     * @see Policy#refresh
+     * @see Policy#getPermissions(ProtectionDomain)
+     * @since 1.4
+     */
+    public ProtectionDomain(CodeSource codesource,
+                            PermissionCollection permissions,
+                            ClassLoader classloader,
+                            Principal[] principals) {
+        this.codesource = codesource;
+        if (permissions != null) {
+            this.permissions = permissions;
+            this.permissions.setReadOnly();
+            if (permissions instanceof Permissions &&
+                ((Permissions)permissions).allPermission != null) {
+                hasAllPerm = true;
+            }
+        }
+        this.classloader = classloader;
+        this.principals = (principals != null ? principals.clone():
+                           new Principal[0]);
+        staticPermissions = false;
+    }
+
+    /**
+     * Returns the CodeSource of this domain.
+     * @return the CodeSource of this domain which may be null.
+     * @since 1.2
+     */
+    public final CodeSource getCodeSource() {
+        return this.codesource;
+    }
+
+
+    /**
+     * Returns the ClassLoader of this domain.
+     * @return the ClassLoader of this domain which may be null.
+     *
+     * @since 1.4
+     */
+    public final ClassLoader getClassLoader() {
+        return this.classloader;
+    }
+
+
+    /**
+     * Returns an array of principals for this domain.
+     * @return a non-null array of principals for this domain.
+     * Returns a new array each time this method is called.
+     *
+     * @since 1.4
+     */
+    public final Principal[] getPrincipals() {
+        return this.principals.clone();
+    }
+
+    /**
+     * Returns the static permissions granted to this domain.
+     *
+     * @return the static set of permissions for this domain which may be null.
+     * @see Policy#refresh
+     * @see Policy#getPermissions(ProtectionDomain)
+     */
+    public final PermissionCollection getPermissions() {
+        return permissions;
+    }
+
+    /**
+     * Check and see if this ProtectionDomain implies the permissions
+     * expressed in the Permission object.
+     * <p>
+     * The set of permissions evaluated is a function of whether the
+     * ProtectionDomain was constructed with a static set of permissions
+     * or it was bound to a dynamically mapped set of permissions.
+     * <p>
+     * If the ProtectionDomain was constructed to a
+     * {@link #ProtectionDomain(CodeSource, PermissionCollection)
+     * statically bound} PermissionCollection then the permission will
+     * only be checked against the PermissionCollection supplied at
+     * construction.
+     * <p>
+     * However, if the ProtectionDomain was constructed with
+     * the constructor variant which supports
+     * {@link #ProtectionDomain(CodeSource, PermissionCollection,
+     * ClassLoader, java.security.Principal[]) dynamically binding}
+     * permissions, then the permission will be checked against the
+     * combination of the PermissionCollection supplied at construction and
+     * the current Policy binding.
+     * <p>
+     *
+     * @param permission the Permission object to check.
+     *
+     * @return true if "permission" is implicit to this ProtectionDomain.
+     */
+    public boolean implies(Permission permission) {
+
+        if (hasAllPerm) {
+            // internal permission collection already has AllPermission -
+            // no need to go to policy
+            return true;
+        }
+
+        if (!staticPermissions &&
+            Policy.getPolicyNoCheck().implies(this, permission))
+            return true;
+        if (permissions != null)
+            return permissions.implies(permission);
+
+        return false;
+    }
+
+    // called by the VM -- do not remove
+    boolean impliesCreateAccessControlContext() {
+        return implies(SecurityConstants.CREATE_ACC_PERMISSION);
+    }
+
+    /**
+     * Convert a ProtectionDomain to a String.
+     */
+    @Override public String toString() {
+        String pals = "<no principals>";
+        if (principals != null && principals.length > 0) {
+            StringBuilder palBuf = new StringBuilder("(principals ");
+
+            for (int i = 0; i < principals.length; i++) {
+                palBuf.append(principals[i].getClass().getName() +
+                            " \"" + principals[i].getName() +
+                            "\"");
+                if (i < principals.length-1)
+                    palBuf.append(",\n");
+                else
+                    palBuf.append(")\n");
+            }
+            pals = palBuf.toString();
+        }
+
+        // Check if policy is set; we don't want to load
+        // the policy prematurely here
+        PermissionCollection pc = Policy.isSet() && seeAllp() ?
+                                      mergePermissions():
+                                      getPermissions();
+
+        return "ProtectionDomain "+
+            " "+codesource+"\n"+
+            " "+classloader+"\n"+
+            " "+pals+"\n"+
+            " "+pc+"\n";
+    }
+
+    /**
+     * Return true (merge policy permissions) in the following cases:
+     *
+     * . SecurityManager is null
+     *
+     * . SecurityManager is not null,
+     *          debug is not null,
+     *          SecurityManager impelmentation is in bootclasspath,
+     *          Policy implementation is in bootclasspath
+     *          (the bootclasspath restrictions avoid recursion)
+     *
+     * . SecurityManager is not null,
+     *          debug is null,
+     *          caller has Policy.getPolicy permission
+     */
+    private static boolean seeAllp() {
+        SecurityManager sm = System.getSecurityManager();
+
+        if (sm == null) {
+            return true;
+        } else {
+            if (debug != null) {
+                if (sm.getClass().getClassLoader() == null &&
+                    Policy.getPolicyNoCheck().getClass().getClassLoader()
+                                                                == null) {
+                    return true;
+                }
+            } else {
+                try {
+                    sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION);
+                    return true;
+                } catch (SecurityException se) {
+                    // fall thru and return false
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private PermissionCollection mergePermissions() {
+        if (staticPermissions)
+            return permissions;
+
+        PermissionCollection perms =
+            java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedAction<PermissionCollection>() {
+                    public PermissionCollection run() {
+                        Policy p = Policy.getPolicyNoCheck();
+                        return p.getPermissions(ProtectionDomain.this);
+                    }
+                });
+
+        Permissions mergedPerms = new Permissions();
+        int swag = 32;
+        int vcap = 8;
+        Enumeration<Permission> e;
+        List<Permission> pdVector = new ArrayList<>(vcap);
+        List<Permission> plVector = new ArrayList<>(swag);
+
+        //
+        // Build a vector of domain permissions for subsequent merge
+        if (permissions != null) {
+            synchronized (permissions) {
+                e = permissions.elements();
+                while (e.hasMoreElements()) {
+                    pdVector.add(e.nextElement());
+                }
+            }
+        }
+
+        //
+        // Build a vector of Policy permissions for subsequent merge
+        if (perms != null) {
+            synchronized (perms) {
+                e = perms.elements();
+                while (e.hasMoreElements()) {
+                    plVector.add(e.nextElement());
+                    vcap++;
+                }
+            }
+        }
+
+        if (perms != null && permissions != null) {
+            //
+            // Weed out the duplicates from the policy. Unless a refresh
+            // has occurred since the pd was consed this should result in
+            // an empty vector.
+            synchronized (permissions) {
+                e = permissions.elements();   // domain vs policy
+                while (e.hasMoreElements()) {
+                    Permission pdp = e.nextElement();
+                    Class<?> pdpClass = pdp.getClass();
+                    String pdpActions = pdp.getActions();
+                    String pdpName = pdp.getName();
+                    for (int i = 0; i < plVector.size(); i++) {
+                        Permission pp = plVector.get(i);
+                        if (pdpClass.isInstance(pp)) {
+                            // The equals() method on some permissions
+                            // have some side effects so this manual
+                            // comparison is sufficient.
+                            if (pdpName.equals(pp.getName()) &&
+                                pdpActions.equals(pp.getActions())) {
+                                plVector.remove(i);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (perms !=null) {
+            // the order of adding to merged perms and permissions
+            // needs to preserve the bugfix 4301064
+
+            for (int i = plVector.size()-1; i >= 0; i--) {
+                mergedPerms.add(plVector.get(i));
+            }
+        }
+        if (permissions != null) {
+            for (int i = pdVector.size()-1; i >= 0; i--) {
+                mergedPerms.add(pdVector.get(i));
+            }
+        }
+
+        return mergedPerms;
+    }
+
+    /**
+     * Used for storing ProtectionDomains as keys in a Map.
+     */
+    final class Key {}
+
+    static {
+        SharedSecrets.setJavaSecurityProtectionDomainAccess(
+            new JavaSecurityProtectionDomainAccess() {
+                public ProtectionDomainCache getProtectionDomainCache() {
+                    return new ProtectionDomainCache() {
+                        private final Map<Key, PermissionCollection> map =
+                            Collections.synchronizedMap
+                                (new WeakHashMap<Key, PermissionCollection>());
+                        public void put(ProtectionDomain pd,
+                            PermissionCollection pc) {
+                            map.put((pd == null ? null : pd.key), pc);
+                        }
+                        public PermissionCollection get(ProtectionDomain pd) {
+                            return pd == null ? map.get(null) : map.get(pd.key);
+                        }
+                    };
+                }
+            });
+    }
+}