Package net.jini.security

Source Code of net.jini.security.Security$ClassContextAccess

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.jini.security;

import com.sun.jini.collection.WeakIdentityMap;
import com.sun.jini.logging.Levels;
import com.sun.jini.resource.Service;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.DomainCombiner;
import java.security.Permission;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.security.auth.Subject;
import javax.security.auth.SubjectDomainCombiner;
import net.jini.security.policy.DynamicPolicy;
import net.jini.security.policy.SecurityContextSource;

/**
* Provides methods for executing actions with privileges enabled, for
* snapshotting security contexts, for verifying trust in proxies, for
* verifying codebase integrity, and for dynamically granting permissions.
* This class cannot be instantiated.
*
* @com.sun.jini.impl
* This implementation uses the {@link Logger} named
* <code>net.jini.security.integrity</code> to log information at
* the following levels:
* <table summary="Describes what is logged by Security to
* the integrity logger at various logging levels" border=1 cellpadding=5>
* <tr>
* <th>Level</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>{@link Levels#FAILED FAILED}</td>
* <td><code>verifyCodebaseIntegrity</code> throws a
* <code>SecurityException</code> because no integrity verifier verifies
* a URL</td>
* </tr>
* <tr>
* <td>{@link Level#FINE FINE}</td>
* <td>integrity verifier returns <code>true</code></td>
* </tr>
* <tr>
* <td>{@link Level#FINE FINE}</td>
* <td>creation of cached integrity verifiers</td>
* </tr>
* </table>
* <p>
* This implementation uses the {@link Logger} named
* <code>net.jini.security.policy</code> to log information at
* the following level:
* <table summary="Describes what is logged by Security to
* the policy logger at various logging levels" border=1 cellpadding=5>
* <tr>
* <th>Level</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>{@link Level#FINER FINER}</td>
* <td>dynamic permission grants</td>
* </tr>
* </table>
* <p>
* This implementation uses the {@link Logger} named
* <code>net.jini.security.trust</code> to log information at
* the following levels:
* <table summary="Describes what is logged by Security to
* the trust logger at various logging levels" border=1 cellpadding=5>
* <tr>
* <th>Level</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>{@link Levels#FAILED FAILED}</td>
* <td><code>verifyObjectTrust</code> throws a <code>SecurityException</code>
* because no trust verifier trusts the specified object</td>
* </tr>
* <tr>
* <td>{@link Levels#FAILED FAILED}</td>
* <td><code>TrustVerifier.Context.isTrustedObject</code> throws an
* exception</td>
* </tr>
* <tr>
* <td>{@link Levels#HANDLED HANDLED}</td>
* <td>trust verifier throws a <code>RemoteException</code> or a
* <code>SecurityException</code></td>
* </tr>
* <tr>
* <td>{@link Level#FINE FINE}</td>
* <td>trust verifier returns <code>true</code></td>
* </tr>
* <tr>
* <td>{@link Level#FINE FINE}</td>
* <td>creation of cached trust verifiers</td>
* </tr>
* <tr>
* <td>{@link Level#FINE FINE}</td>
* <td><code>TrustVerifier.Context.isTrustedObject</code> returns
* <code>false</code> because no trust verifier trusts the specified
* object</td>
* </tr>
* </table>
*
* @author Sun Microsystems, Inc.
* @since 2.0
*/
public final class Security {

    private static final Logger trustLogger =
    Logger.getLogger("net.jini.security.trust");
    private static final Logger integrityLogger =
    Logger.getLogger("net.jini.security.integrity");
    private static final Logger policyLogger =
    Logger.getLogger("net.jini.security.policy");

    /**
     * Weak map from String to [URL[], SoftReference(key)]
     */
    private static Map pathToURLsCache = new WeakHashMap(5);
    /**
     * Weak map from ClassLoader to SoftReference(IntegrityVerifier[]).
     */
    private static final WeakIdentityMap integrityMap = new WeakIdentityMap();

    /**
     * SecurityManager instance used to obtain caller's Class.
     */
    private static final ClassContextAccess ctxAccess = (ClassContextAccess)
  AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() { return new ClassContextAccess(); }
  });

    /**
     * Non-instantiable.
     */
    private Security() {}

    /**
     * Verifies that the specified object can be trusted to correctly implement
     * its contract, using verifiers from the specified class loader and
     * using the specified collection of context objects as necessary. If a
     * <code>null</code> class loader is specified, the context class loader
     * of the current thread is used instead. Code that is itself downloaded
     * and that carries its own trust verifiers (to trust other downloaded
     * code) should specify an explicit class loader unless the calling code
     * is known to be reachable from the context class loader.
     * <p>
     * A {@link TrustVerifier.Context} is created, containing an ordered list
     * of trust verifiers (obtained as specified below) and the specified class
     * loader and collection of context objects. The
     * {@link TrustVerifier.Context#isTrustedObject isTrustedObject} method
     * of that context is then called with the specified object. If that call
     * returns <code>true</code>, then this method returns normally. If that
     * call throws a <code>RemoteException</code> or
     * <code>SecurityException</code> exception, that exception is thrown by
     * this method. If that call returns <code>false</code>, a
     * <code>SecurityException</code> is thrown.
     * <p>
     * The collection of context objects is provided as a means for the
     * caller to communicate additional information to the trust verifiers.
     * The meaning of an element in this collection is determined by its
     * type. As a specific example, if any trust verifiers might communicate
     * with a remote server (in particular, when verifying a proxy for a
     * remote server), the caller might be responsible for specifying any
     * necessary client constraints as a context object of type
     * {@link net.jini.core.constraint.MethodConstraints}.
     * <p>
     * When security is a concern, this method should be called with a
     * downloaded proxy before making any other use of the proxy, in order to
     * verify basic trust in the proxy to correctly implement its contract.
     * This method can also be used to verify trust in other types of objects,
     * depending on what verifiers have been configured. In general,
     * verification of an object involves verification of all of its
     * constituent objects. However, for objects that are instances of
     * {@link net.jini.core.constraint.RemoteMethodControl},
     * the client constraints (that would be returned by
     * {@link net.jini.core.constraint.RemoteMethodControl#getConstraints
     * RemoteMethodControl.getConstraints}) are not verified; it is assumed
     * that the caller will either replace them or independently decide that
     * it trusts them. Verification of other types of objects may similarly
     * exempt certain application-controlled state.
     * <p>
     * The list of trust verifiers is obtained as follows. For each resource
     * named
     * <code>META-INF/services/net.jini.security.TrustVerifier</code>
     * that is visible to the specified class loader, the contents of the
     * resource are parsed as UTF-8 text to produce a list of class names.
     * The resource must contain a list of fully qualified class names, one per
     * line. Space and tab characters surrounding each name, as well as blank
     * lines, are ignored.  The comment character is <tt>'#'</tt>; all
     * characters on each line starting with the first comment character are
     * ignored. Each class name (that is not a duplicate of any previous class
     * name) is loaded through the specified class loader, and the resulting
     * class must be assignable to {@link TrustVerifier} and have a public
     * no-argument constructor. The constructor is invoked to create a trust
     * verifier instance. An implementation of this method is permitted to
     * cache the verifier instances associated with a class loader, rather than
     * recreating them on every call.
     *
     * @param obj the object in which to verify trust
     * @param loader the class loader for finding trust verifiers, or
     * <code>null</code> to use the context class loader
     * @param context a collection of context objects for use by trust
     * verifiers
     * @throws SecurityException if the object is not trusted, or if a
     * <code>SecurityException</code> is thrown by the trust verifier context
     * @throws RemoteException if a communication-related exception occurs
     * @throws NullPointerException if the collection is <code>null</code>
     */
    public static void verifyObjectTrust(Object obj,
           ClassLoader loader,
           Collection context)
  throws RemoteException
    {
  if (context == null) {
      throw new NullPointerException("collection cannot be null");
  }
  if (new Context(loader, context).isTrustedObject(obj)) {
      return;
  }
  SecurityException e = new SecurityException(
              "object is not trusted: " + obj);
  if (trustLogger.isLoggable(Levels.FAILED)) {
      logThrow(trustLogger, Levels.FAILED,
         Security.class.getName(), "verifyObjectTrust",
         "no verifier trusts {0}",
         new Object[]{obj}, e);
  }
  throw e;
    }
   
    /**
     * Verifies that the URLs in the specified codebase all provide content
     * integrity, using verifiers from the specified class loader. If a
     * <code>null</code> class loader is specified, the context class loader of
     * the current thread is used instead. An ordered list of integrity
     * verifiers is obtained as specified below. For each URL (if any) in the
     * specified codebase, the {@link IntegrityVerifier#providesIntegrity
     * providesIntegrity} method of each verifier is called (in order) with
     * the URL. If any verifier call returns <code>true</code>, the URL is
     * verified (and no further verifiers are called with that URL). If all of
     * the verifier calls return <code>false</code> for a URL, this method
     * throws a <code>SecurityException</code>. If all of the URLs are
     * verified, this method returns normally.
     * <p>
     * The list of integrity verifiers is obtained as follows. For each
     * resource named
     * <code>META-INF/services/net.jini.security.IntegrityVerifier</code>
     * that is visible to the specified class loader, the contents of the
     * resource are parsed as UTF-8 text to produce a list of class names.
     * The resource must contain a list of fully qualified class names, one per
     * line. Space and tab characters surrounding each name, as well as blank
     * lines, are ignored.  The comment character is <tt>'#'</tt>; all
     * characters on each line starting with the first comment character are
     * ignored. Each class name (that is not a duplicate of any previous class
     * name) is loaded through the specified class loader, and the resulting
     * class must be assignable to {@link IntegrityVerifier} and have a public
     * no-argument constructor. The constructor is invoked to create an
     * integrity verifier instance. An implementation of this method is
     * permitted to cache the verifier instances associated with a
     * class loader, rather than recreating them on every call.
     *
     * @param codebase space-separated list of URLs, or <code>null</code>
     * @param loader the class loader for finding integrity verifiers, or
     * <code>null</code> to use the context class loader
     * @throws MalformedURLException if the specified codebase contains
     * an invalid URL
     * @throws SecurityException if any URL in the specified codebase
     * does not provide content integrity
     */
    public static void verifyCodebaseIntegrity(String codebase,
                 ClassLoader loader)
  throws MalformedURLException
    {
  if (codebase == null) {
      return;
  }
  if (loader == null) {
      loader = getContextClassLoader();
  }
  URL[] urls = pathToURLs(codebase);
  IntegrityVerifier[] verifiers = getIntegrityVerifiers(loader);
    outer:
  for (int i = urls.length; --i >= 0; ) {
      for (int j = 0; j < verifiers.length; j++) {
    if (verifiers[j].providesIntegrity(urls[i])) {
        if (integrityLogger.isLoggable(Level.FINE)) {
      integrityLogger.log(Level.FINE,
              "{0} verifies {1}",
               new Object[]{verifiers[j],
                urls[i]});
        }
        continue outer;
    }
      }
      SecurityException e =
    new SecurityException("URL does not provide integrity: " +
              urls[i]);
      if (integrityLogger.isLoggable(Levels.FAILED)) {
    logThrow(integrityLogger, Levels.FAILED,
       Security.class.getName(), "verifyCodebaseIntegrity",
       "no verifier verifies {0}", new Object[]{urls[i]}, e);
      }
      throw e;
  }
    }

    /**
     * Log a throw.
     */
    private static void logThrow(Logger logger,
         Level level,
         String clazz,
         String method,
         String msg,
         Object[] args,
         Throwable t)
    {
  LogRecord lr = new LogRecord(level, msg);
  lr.setLoggerName(logger.getName());
  lr.setSourceClassName(clazz);
  lr.setSourceMethodName(method);
  lr.setParameters(args);
  lr.setThrown(t);
  logger.log(lr);
    }

    /**
     * Convert a string containing a space-separated list of URLs into a
     * corresponding array of URL objects, throwing a MalformedURLException
     * if any of the URLs are invalid.
     */
    private static URL[] pathToURLs(String path) throws MalformedURLException {
  synchronized (pathToURLsCache) {
      Object[] v = (Object[]) pathToURLsCache.get(path);
      if (v != null) {
    return (URL[]) v[0];
      }
  }
  StringTokenizer st = new StringTokenizer(path)// divide by spaces
  URL[] urls = new URL[st.countTokens()];
  for (int i = 0; st.hasMoreTokens(); i++) {
      urls[i] = new URL(st.nextToken());
  }
  synchronized (pathToURLsCache) {
      pathToURLsCache.put(path,
        new Object[]{urls, new SoftReference(path)});
  }
  return urls;
    }

    /**
     * Return the integrity verifiers for the specified class loader.
     */
    private static IntegrityVerifier[] getIntegrityVerifiers(
              final ClassLoader cl)
    {
  SoftReference ref;
  synchronized (integrityMap) {
      ref = (SoftReference) integrityMap.get(cl);
  }
  IntegrityVerifier[] verifiers = null;
  if (ref != null) {
      verifiers = (IntegrityVerifier[]) ref.get();
  }
  if (verifiers == null) {
      final ArrayList list = new ArrayList(1);
      AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
        for (Iterator iter =
           Service.providers(IntegrityVerifier.class, cl);
       iter.hasNext(); )
        {
      list.add(iter.next());
        }
        return null;
    }
      });
      if (integrityLogger.isLoggable(Level.FINE)) {
    integrityLogger.logp(Level.FINE, Security.class.getName(),
             "verifyCodebaseIntegrity",
             "integrity verifiers {0}",
             new Object[]{list});
      }
      verifiers = (IntegrityVerifier[]) list.toArray(
            new IntegrityVerifier[list.size()]);
      synchronized (integrityMap) {
    integrityMap.put(cl, new SoftReference(verifiers));
      }
  }
  return verifiers;
    }

    /**
     * Returns a snapshot of the current security context, which can be used to
     * restore the context at a later time.  If either the installed security
     * manager or policy provider implements the {@link SecurityContextSource}
     * interface, then this method delegates to the {@link
     * SecurityContextSource#getContext getContext} method of the
     * implementing object, with precedence given to the security manager.  If
     * neither the security manager nor the policy provider implement
     * <code>SecurityContextSource</code>, then a new default
     * {@link SecurityContext} instance is
     * returned whose methods have the following semantics:
     * <ul>
     * <li>The <code>wrap</code> methods each return their respective
     * <code>PrivilegedAction</code> and <code>PrivilegedExceptionAction</code>
     * arguments, unmodified
     * <li>The <code>getAccessControlContext</code> method returns the
     * <code>AccessControlContext</code> in effect when the security context
     * was created
     * </ul>
     *
     * @return snapshot of the current security context
     */
    public static SecurityContext getContext() {
  SecurityManager sm = System.getSecurityManager();
  if (sm instanceof SecurityContextSource) {
      return ((SecurityContextSource) sm).getContext();
  }
  Policy policy = getPolicy();
  if (policy instanceof SecurityContextSource) {
      return ((SecurityContextSource) policy).getContext();
  }

  final AccessControlContext acc = AccessController.getContext();
  return new SecurityContext() {
      public PrivilegedAction wrap(PrivilegedAction a) {
    if (a == null) {
        throw new NullPointerException();
    }
    return a;
      }

      public PrivilegedExceptionAction wrap(PrivilegedExceptionAction a)
      {
    if (a == null) {
        throw new NullPointerException();
    }
    return a;
      }

      public AccessControlContext getAccessControlContext() {
    return acc;
      }
  };
    }

    /**
     * Executes the specified action's <code>run</code> method with privileges
     * enabled, preserving the domain combiner (if any) of the calling context.
     * If the action's <code>run</code> method throws an unchecked exception,
     * that exception is thrown by this method.  This method is equivalent to
     * the {@link AccessController#doPrivileged(PrivilegedAction)
     * AccessController.doPrivileged} method of the same signature, except that
     * it maintains, instead of clears, the domain combiner (if any) in place
     * at the time of the call.  This typically results in preservation of the
     * current {@link Subject} (if the combiner is a {@link
     * SubjectDomainCombiner}), thus retaining permissions granted to
     * principals of the <code>Subject</code>, as well as the ability to use
     * credentials of the <code>Subject</code> for authentication.
     *
     * @param action the action to be executed
     * @return the object returned by the action's <code>run</code> method
     * @throws NullPointerException if the action is <code>null</code>
     */
    public static Object doPrivileged(final PrivilegedAction action) {
  final Class caller = ctxAccess.getCaller();
  final AccessControlContext acc = AccessController.getContext();
  return AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
    return AccessController.doPrivileged(
        action, createPrivilegedContext(caller, acc));
      }
  });
    }
   
    /**
     * Executes the specified action's <code>run</code> method with privileges
     * enabled, preserving the domain combiner (if any) of the calling context.
     * If the action's <code>run</code> method throws an unchecked exception,
     * that exception is thrown by this method.  This method is equivalent to
     * the {@link AccessController#doPrivileged(PrivilegedExceptionAction)
     * AccessController.doPrivileged} method of the same signature, except that
     * it maintains, instead of clears, the domain combiner (if any) in place
     * at the time of the call.  This typically results in preservation of the
     * current <code>Subject</code> (if the combiner is a
     * <code>SubjectDomainCombiner</code>), thus retaining permissions granted
     * to principals of the <code>Subject</code>, as well as the ability to use
     * credentials of the <code>Subject</code> for authentication.
     *
     * @param action the action to be executed
     * @return the object returned by the action's <code>run</code> method
     * @throws PrivilegedActionException if the action's <code>run</code>
     * method throws a checked exception
     * @throws NullPointerException if the action is <code>null</code>
     */
    public static Object doPrivileged(final PrivilegedExceptionAction action)
  throws PrivilegedActionException
    {
  final Class caller = ctxAccess.getCaller();
  final AccessControlContext acc = AccessController.getContext();
  return AccessController.doPrivileged(new PrivilegedExceptionAction() {
      public Object run() throws Exception {
    try {
        return AccessController.doPrivileged(
      action, createPrivilegedContext(caller, acc));
    } catch (PrivilegedActionException e) {
        throw e.getException();
    }
      }
  });
    }
   
    /**
     * Creates privileged context that contains the protection domain of the
     * given caller class (if non-null) and uses the domain combiner of the
     * specified context.  This method assumes it is called from within a
     * privileged block.
     */
    private static AccessControlContext createPrivilegedContext(
                Class caller,
                AccessControlContext acc)
    {
  DomainCombiner comb = acc.getDomainCombiner();
  ProtectionDomain pd = caller.getProtectionDomain();
  ProtectionDomain[] pds = (pd != null) ?
      new ProtectionDomain[]{pd} : null;
  if (comb != null) {
      pds = comb.combine(pds, null);
  }
  if (pds == null) {
      pds = new ProtectionDomain[0];
  }
  return new AccessControlContext(new AccessControlContext(pds), comb);
    }

    /**
     * Returns <code>true</code> if the installed security policy provider
     * supports dynamic permission grants--i.e., if it implements the {@link
     * DynamicPolicy} interface and calling its {@link
     * DynamicPolicy#grantSupported grantSupported} method returns
     * <code>true</code>.  Returns <code>false</code> otherwise.
     *
     * @return <code>true</code> if the installed security policy provider
     * supports dynamic permission grants
     * @see #grant(Class,Permission[])
     * @see #grant(Class,Principal[],Permission[])
     * @see #grant(Class,Class)
     */
    public static boolean grantSupported() {
  Policy policy = getPolicy();
  return (policy instanceof DynamicPolicy &&
    ((DynamicPolicy) policy).grantSupported());
    }

    /**
     * If the installed security policy provider implements the
     * {@link DynamicPolicy} interface, delegates to the security policy
     * provider to grant the specified permissions to all protection domains
     * (including ones not yet created) that are associated with the class
     * loader of the given class and possess at least the principals of the
     * current subject (if any).  If the given class is <code>null</code>, then
     * the grant applies across all protection domains that possess at least
     * the current subject's principals.  The current subject is determined by
     * calling {@link Subject#getSubject Subject.getSubject} on the context
     * returned by {@link AccessController#getContext
     * AccessController.getContext}.  If the current subject is
     * <code>null</code> or has no principals, then principals are effectively
     * ignored in determining the protection domains to which the grant
     * applies. 
     * <p>
     * The given class, if non-<code>null</code>, must belong to either the
     * system domain or a protection domain whose associated class loader is
     * non-<code>null</code>.  If the class does not belong to such a
     * protection domain, then no permissions are granted and an
     * <code>UnsupportedOperationException</code> is thrown.
     * <p>
     * If a security manager is installed, its <code>checkPermission</code>
     * method is called with a {@link GrantPermission} containing the
     * permissions to grant; if the permission check fails, then no permissions
     * are granted and the resulting <code>SecurityException</code> is thrown.
     * The permissions array passed in is neither modified nor retained;
     * subsequent changes to the array have no effect on the grant operation.
     *
     * @param cl class to grant permissions to the class loader of, or
     * <code>null</code> if granting across all class loaders
     * @param permissions if non-<code>null</code>, permissions to grant
     * @throws UnsupportedOperationException if the installed security policy
     * provider does not support dynamic permission grants, or if
     * <code>cl</code> is non-<code>null</code> and belongs to a protection
     * domain other than the system domain with an associated class loader of
     * <code>null</code>
     * @throws SecurityException if a security manager is installed and the
     * calling context does not have <code>GrantPermission</code> for the given
     * permissions
     * @throws NullPointerException if any element of the permissions array is
     * <code>null</code>
     * @see #grantSupported()
     * @see DynamicPolicy#grant(Class,Principal[],Permission[])
     */
    public static void grant(Class cl, Permission[] permissions) {
  grant(cl, getCurrentPrincipals(), permissions);
    }

    /**
     * If the installed security policy provider implements the
     * {@link DynamicPolicy} interface, delegates to the security policy
     * provider to grant the specified permissions to all protection domains
     * (including ones not yet created) that are associated with the class
     * loader of the given class and possess at least the given set of
     * principals.  If the given class is <code>null</code>, then the grant
     * applies across all protection domains that possess at least the
     * specified principals.  If the list of principals is <code>null</code> or
     * empty, then principals are effectively ignored in determining the
     * protection domains to which the grant applies. 
     * <p>
     * The given class, if non-<code>null</code>, must belong to either the
     * system domain or a protection domain whose associated class loader is
     * non-<code>null</code>.  If the class does not belong to such a
     * protection domain, then no permissions are granted and an
     * <code>UnsupportedOperationException</code> is thrown.
     * <p>
     * If a security manager is installed, its <code>checkPermission</code>
     * method is called with a <code>GrantPermission</code> containing the
     * permissions to grant; if the permission check fails, then no permissions
     * are granted and the resulting <code>SecurityException</code> is thrown.
     * The principals and permissions arrays passed in are neither modified nor
     * retained; subsequent changes to the arrays have no effect on the grant
     * operation.
     *
     * @param cl class to grant permissions to the class loader of, or
     * <code>null</code> if granting across all class loaders
     * @param principals if non-<code>null</code>, minimum set of principals to
     * which grants apply
     * @param permissions if non-<code>null</code>, permissions to grant
     * @throws UnsupportedOperationException if the installed security policy
     * provider does not support dynamic permission grants, or if
     * <code>cl</code> is non-<code>null</code> and belongs to a protection
     * domain other than the system domain with an associated class loader of
     * <code>null</code>
     * @throws SecurityException if a security manager is installed and the
     * calling context does not have <code>GrantPermission</code> for the given
     * permissions
     * @throws NullPointerException if any element of the principals or
     * permissions arrays is <code>null</code>
     * @see #grantSupported()
     * @see DynamicPolicy#grant(Class,Principal[],Permission[])
     */
    public static void grant(Class cl,
                             Principal[] principals,
                             Permission[] permissions)
    {
  Policy policy = getPolicy();
  if (!(policy instanceof DynamicPolicy)) {
      throw new UnsupportedOperationException("grants not supported");
  }
  ((DynamicPolicy) policy).grant(cl, principals, permissions);
  if (policyLogger.isLoggable(Level.FINER)) {
      policyLogger.log(Level.FINER, "granted {0} to {1}, {2}",
    new Object[]{
        (permissions != null) ? Arrays.asList(permissions) : null,
        (cl != null) ? cl.getName() : null,
        (principals != null) ? Arrays.asList(principals) : null});
  }
    }

    /**
     * If the installed security policy provider implements the {@link
     * DynamicPolicy} interface, takes the set of permissions dynamically
     * granted to the class loader of <code>fromClass</code> with the current
     * subject's principals, determines which of those permissions the calling
     * context is authorized to grant, and dynamically grants that subset of
     * the permissions to the class loader of <code>toClass</code>, qualified
     * with the current subject's principals.  The current subject is
     * determined by calling {@link Subject#getSubject Subject.getSubject} on
     * the context returned by {@link AccessController#getContext
     * AccessController.getContext}; the permissions dynamically granted to
     * <code>fromClass</code> are determined by calling the {@link
     * DynamicPolicy#getGrants getGrants} method of the currently installed
     * policy, and the permission grant to <code>toClass</code> is performed by
     * invoking the {@link DynamicPolicy#grant grant} method of the current
     * policy.
     * <p>
     * Both of the given classes must be non-<code>null</code>, and must belong
     * to either the system domain or a protection domain whose associated
     * class loader is non-<code>null</code>.  If either class does not belong
     * to such a protection domain, then no permissions are granted and an
     * <code>UnsupportedOperationException</code> is thrown.
     *
     * @param fromClass class indicating the source class loader of the dynamic
     * grants to propagate
     * @param toClass class indicating the target class loader of the dynamic
     * grants to propagate
     * @throws NullPointerException if <code>fromClass</code> or
     * <code>toClass</code> is <code>null</code>
     * @throws UnsupportedOperationException if currently installed policy does
     * not support dynamic permission grants, or if either specified class
     * belongs to a protection domain with a <code>null</code> class loader,
     * other than the system domain
     */
    public static void grant(Class fromClass, Class toClass) {
  if (fromClass == null || toClass == null) {
      throw new NullPointerException();
  }
  Policy policy = getPolicy();
  if (!(policy instanceof DynamicPolicy)) {
      throw new UnsupportedOperationException("grants not supported");
  }

  DynamicPolicy dpolicy = (DynamicPolicy) policy;
  Principal[] principals = getCurrentPrincipals();
  Permission[] permissions =
      grantablePermissions(dpolicy.getGrants(fromClass, principals));

  dpolicy.grant(toClass, principals, permissions);
  if (policyLogger.isLoggable(Level.FINER)) {
      policyLogger.log(Level.FINER, "granted {0} from {1} to {2}, {3}",
    new Object[]{
        (permissions != null) ? Arrays.asList(permissions) : null,
        fromClass.getName(),
        toClass.getName(),
        (principals != null) ? Arrays.asList(principals) : null});
  }
    }

    /**
     * Returns current thread's context class loader.
     */
    private static ClassLoader getContextClassLoader() {
  return (ClassLoader)
      AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
      return Thread.currentThread().getContextClassLoader();
        }
    });
    }

    /**
     * Returns currently installed security policy, if any.
     */
    private static Policy getPolicy() {
  return (Policy) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() { return Policy.getPolicy(); }
  });
    }

    /**
     * Returns subset of given permissions that is grantable given the current
     * calling context.
     */
    private static Permission[] grantablePermissions(Permission[] permissions)
    {
  SecurityManager sm = System.getSecurityManager();
  if (sm == null || permissions.length == 0) {
      return permissions;
  }

  try {
      sm.checkPermission(new GrantPermission(permissions));
      return permissions;
  } catch (SecurityException e) {
  }

  ArrayList list = new ArrayList(permissions.length);
  for (int i = 0; i < permissions.length; i++) {
      try {
    Permission p = permissions[i];
    sm.checkPermission(new GrantPermission(p));
    list.add(p);
      } catch (SecurityException e) {
      }
  }
  return (Permission[]) list.toArray(new Permission[list.size()]);
    }

    /**
     * Returns principals of current subject, or null if no current subject.
     */
    private static Principal[] getCurrentPrincipals() {
  final AccessControlContext acc = AccessController.getContext();
  Subject s = (Subject) AccessController.doPrivileged(
      new PrivilegedAction() {
    public Object run() { return Subject.getSubject(acc); }
      });
  if (s != null) {
      Set ps = s.getPrincipals();
      return (Principal[]) ps.toArray(new Principal[ps.size()]);
  } else {
      return null;
  }
    }

    /**
     * TrustVerifier.Context implementation.
     */
    private static class Context implements TrustVerifier.Context {
  /**
   * Trust verifiers.
   */
  private final TrustVerifier[] verifiers;
  /**
   * The class loader.
   */
  private final ClassLoader cl;
  /**
   * Caller context.
   */
  private final Collection context;

  /**
   * Weak map from ClassLoader to SoftReference(TrustVerifier[]).
   */
  private static final WeakIdentityMap map = new WeakIdentityMap();

  /**
   * Creates an instance containing the trust verifiers found from
   * the specified class loader, using the verifier cache and
   * updating the cache as necessary.
   */
  Context(ClassLoader cl, Collection context) {
      this.cl = cl;
      if (cl == null) {
    cl = getContextClassLoader();
      }
      SoftReference ref;
      synchronized (map) {
    ref = (SoftReference) map.get(cl);
      }
      TrustVerifier[] verifiers = null;
      if (ref != null) {
    verifiers = (TrustVerifier[]) ref.get();
      }
      if (verifiers == null) {
    final ArrayList list = new ArrayList(1);
    final ClassLoader scl = cl;
    AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
      for (Iterator iter =
         Service.providers(TrustVerifier.class, scl);
           iter.hasNext(); )
      {
          list.add(iter.next());
      }
      return null;
        }
    });
    if (trustLogger.isLoggable(Level.FINE)) {
        trustLogger.logp(Level.FINE, Security.class.getName(),
             "verifyObjectTrust",
             "trust verifiers {0}", list);
    }
    verifiers = (TrustVerifier[]) list.toArray(
                 new TrustVerifier[list.size()]);
    synchronized (map) {
        map.put(cl, new SoftReference(verifiers));
    }
      }
      this.verifiers = verifiers;
      this.context = context;
  }

  public boolean isTrustedObject(Object obj)
      throws RemoteException
  {
      if (obj == null) {
    return true;
      }
      Exception ex = null;
      for (int i = 0; i < verifiers.length; i++) {
    try {
        if (verifiers[i].isTrustedObject(obj, this)) {
      if (trustLogger.isLoggable(Level.FINE)) {
          trustLogger.log(Level.FINE,
              "{0} trusts {1}",
              new Object[]{verifiers[i], obj});
      }
      return true;
        }
    } catch (Exception e) {
        boolean rethrow = (e instanceof RuntimeException &&
               !(e instanceof SecurityException));
        Level level = rethrow ? Levels.FAILED : Levels.HANDLED;
        if (trustLogger.isLoggable(level)) {
      logThrow(trustLogger, level,
         this.getClass().getName(),
         "isTrustedObject",
         "{0} checking {1} throws",
         new Object[]{verifiers[i], obj},
         e);
        }
        if (rethrow) {
      throw (RuntimeException) e;
        }
        ex = e;
    }
      }
      if (ex != null) {
    if (trustLogger.isLoggable(Levels.FAILED)) {
        logThrow(trustLogger, Levels.FAILED,
           this.getClass().getName(), "isTrustedObject",
           "checking {0} throws",
           new Object[]{obj}, ex);
    }
    if (ex instanceof RemoteException) {
        throw (RemoteException) ex;
    }
    throw (SecurityException) ex;
      }
      if (trustLogger.isLoggable(Level.FINE)) {
    trustLogger.log(Level.FINE, "no verifier trusts {0}", obj);
      }
      return false;
  }

  public ClassLoader getClassLoader() {
      return cl;
  }

  public Collection getCallerContext() {
      return context;
  }
    }
   
    /**
     * Dummy security manager providing access to getClassContext method.
     */
    private static class ClassContextAccess extends SecurityManager {
  /**
   * Returns caller's caller class.
   */
  Class getCaller() {
      return getClassContext()[2];
  }
    }
}
TOP

Related Classes of net.jini.security.Security$ClassContextAccess

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.