1 /*
   2  * Copyright (c) 1997, 2019, 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.Map;
  29 import java.util.Objects;
  30 import java.util.concurrent.ConcurrentHashMap;
  31 import java.util.function.Function;
  32 
  33 import sun.security.util.Debug;
  34 
  35 /**
  36  * This class extends ClassLoader with additional support for defining
  37  * classes with an associated code source and permissions which are
  38  * retrieved by the system policy by default.
  39  *
  40  * @author  Li Gong
  41  * @author  Roland Schemers
  42  * @since 1.2
  43  */
  44 public class SecureClassLoader extends ClassLoader {
  45 
  46     /*
  47      * Map that maps the CodeSource to a ProtectionDomain. The key is a
  48      * CodeSourceKey class that uses a String instead of a URL to avoid
  49      * potential expensive name service lookups. This does mean that URLs that
  50      * are equivalent after nameservice lookup will be placed in separate
  51      * ProtectionDomains; however during policy enforcement these URLs will be
  52      * canonicalized and resolved resulting in a consistent set of granted
  53      * permissions.
  54      */
  55     private final Map<CodeSourceKey, ProtectionDomain> pdcache
  56             = new ConcurrentHashMap<>(11);
  57 
  58     static {
  59         ClassLoader.registerAsParallelCapable();
  60     }
  61 
  62     /**
  63      * Creates a new SecureClassLoader using the specified parent
  64      * class loader for delegation.
  65      *
  66      * <p>If there is a security manager, this method first
  67      * calls the security manager's {@code checkCreateClassLoader}
  68      * method  to ensure creation of a class loader is allowed.
  69      *
  70      * @param parent the parent ClassLoader
  71      * @throws     SecurityException  if a security manager exists and its
  72      *             {@code checkCreateClassLoader} method doesn't allow
  73      *             creation of a class loader.
  74      * @see SecurityManager#checkCreateClassLoader
  75      */
  76     protected SecureClassLoader(ClassLoader parent) {
  77         super(parent);
  78     }
  79 
  80     /**
  81      * Creates a new SecureClassLoader using the default parent class
  82      * loader for delegation.
  83      *
  84      * <p>If there is a security manager, this method first
  85      * calls the security manager's {@code checkCreateClassLoader}
  86      * method  to ensure creation of a class loader is allowed.
  87      *
  88      * @throws     SecurityException  if a security manager exists and its
  89      *             {@code checkCreateClassLoader} method doesn't allow
  90      *             creation of a class loader.
  91      * @see SecurityManager#checkCreateClassLoader
  92      */
  93     protected SecureClassLoader() {
  94         super();
  95     }
  96 
  97     /**
  98      * Creates a new {@code SecureClassLoader} of the specified name and
  99      * using the specified parent class loader for delegation.
 100      *
 101      * @param name class loader name; or {@code null} if not named
 102      * @param parent the parent class loader
 103      *
 104      * @throws IllegalArgumentException if the given name is empty.
 105      *
 106      * @throws SecurityException  if a security manager exists and its
 107      *         {@link SecurityManager#checkCreateClassLoader()} method
 108      *         doesn't allow creation of a class loader.
 109      *
 110      * @since 9
 111      * @spec JPMS
 112      */
 113     protected SecureClassLoader(String name, ClassLoader parent) {
 114         super(name, parent);
 115     }
 116 
 117     /**
 118      * Converts an array of bytes into an instance of class Class,
 119      * with an optional CodeSource. Before the
 120      * class can be used it must be resolved.
 121      * <p>
 122      * If a non-null CodeSource is supplied a ProtectionDomain is
 123      * constructed and associated with the class being defined.
 124      *
 125      * @param      name the expected name of the class, or {@code null}
 126      *                  if not known, using '.' and not '/' as the separator
 127      *                  and without a trailing ".class" suffix.
 128      * @param      b    the bytes that make up the class data. The bytes in
 129      *             positions {@code off} through {@code off+len-1}
 130      *             should have the format of a valid class file as defined by
 131      *             <cite>The Java Virtual Machine Specification</cite>.
 132      * @param      off  the start offset in {@code b} of the class data
 133      * @param      len  the length of the class data
 134      * @param      cs   the associated CodeSource, or {@code null} if none
 135      * @return the {@code Class} object created from the data,
 136      *         and optional CodeSource.
 137      * @throws     ClassFormatError if the data did not contain a valid class
 138      * @throws     IndexOutOfBoundsException if either {@code off} or
 139      *             {@code len} is negative, or if
 140      *             {@code off+len} is greater than {@code b.length}.
 141      *
 142      * @throws     SecurityException if an attempt is made to add this class
 143      *             to a package that contains classes that were signed by
 144      *             a different set of certificates than this class, or if
 145      *             the class name begins with "java.".
 146      */
 147     protected final Class<?> defineClass(String name,
 148                                          byte[] b, int off, int len,
 149                                          CodeSource cs)
 150     {
 151         return defineClass(name, b, off, len, getProtectionDomain(cs));
 152     }
 153 
 154     /**
 155      * Converts a {@link java.nio.ByteBuffer ByteBuffer}
 156      * into an instance of class {@code Class}, with an optional CodeSource.
 157      * Before the class can be used it must be resolved.
 158      * <p>
 159      * If a non-null CodeSource is supplied a ProtectionDomain is
 160      * constructed and associated with the class being defined.
 161      *
 162      * @param      name the expected name of the class, or {@code null}
 163      *                  if not known, using '.' and not '/' as the separator
 164      *                  and without a trailing ".class" suffix.
 165      * @param      b    the bytes that make up the class data.  The bytes from positions
 166      *                  {@code b.position()} through {@code b.position() + b.limit() -1}
 167      *                  should have the format of a valid class file as defined by
 168      *                  <cite>The Java Virtual Machine Specification</cite>.
 169      * @param      cs   the associated CodeSource, or {@code null} if none
 170      * @return the {@code Class} object created from the data,
 171      *         and optional CodeSource.
 172      * @throws     ClassFormatError if the data did not contain a valid class
 173      * @throws     SecurityException if an attempt is made to add this class
 174      *             to a package that contains classes that were signed by
 175      *             a different set of certificates than this class, or if
 176      *             the class name begins with "java.".
 177      *
 178      * @since  1.5
 179      */
 180     protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
 181                                          CodeSource cs)
 182     {
 183         return defineClass(name, b, getProtectionDomain(cs));
 184     }
 185 
 186     /**
 187      * Returns the permissions for the given CodeSource object.
 188      * <p>
 189      * This method is invoked by the defineClass method which takes
 190      * a CodeSource as an argument when it is constructing the
 191      * ProtectionDomain for the class being defined.
 192      *
 193      * @param codesource the codesource.
 194      *
 195      * @return the permissions granted to the codesource.
 196      *
 197      */
 198     protected PermissionCollection getPermissions(CodeSource codesource)
 199     {
 200         return new Permissions(); // ProtectionDomain defers the binding
 201     }
 202 
 203     /*
 204      * holder class for the static field "debug" to delay its initialization
 205      */
 206     private static class DebugHolder {
 207         private static final Debug debug = Debug.getInstance("scl");
 208     }
 209 
 210     /*
 211      * Returned cached ProtectionDomain for the specified CodeSource.
 212      */
 213     private ProtectionDomain getProtectionDomain(CodeSource cs) {
 214         if (cs == null) {
 215             return null;
 216         }
 217 
 218         // Use a CodeSourceKey object key. It should behave in the
 219         // same manner as the CodeSource when compared for equality except
 220         // that no nameservice lookup is done on the hostname (String comparison
 221         // only), and the fragment is not considered.
 222         CodeSourceKey key = new CodeSourceKey(cs);
 223         return pdcache.computeIfAbsent(key, new Function<>() {
 224             @Override
 225             public ProtectionDomain apply(CodeSourceKey key /* not used */) {
 226                 PermissionCollection perms
 227                         = SecureClassLoader.this.getPermissions(cs);
 228                 ProtectionDomain pd = new ProtectionDomain(
 229                         cs, perms, SecureClassLoader.this, null);
 230                 if (DebugHolder.debug != null) {
 231                     DebugHolder.debug.println(" getPermissions " + pd);
 232                     DebugHolder.debug.println("");
 233                 }
 234                 return pd;
 235             }
 236         });
 237     }
 238 
 239     private static class CodeSourceKey {
 240         private final CodeSource cs;
 241 
 242         CodeSourceKey(CodeSource cs) {
 243             this.cs = cs;
 244         }
 245 
 246         @Override
 247         public int hashCode() {
 248             String locationNoFrag = cs.getLocationNoFragString();
 249             return locationNoFrag != null ? locationNoFrag.hashCode() : 0;
 250         }
 251 
 252         @Override
 253         public boolean equals(Object obj) {
 254             if (obj == this) {
 255                 return true;
 256             }
 257 
 258             if (!(obj instanceof CodeSourceKey)) {
 259                 return false;
 260             }
 261 
 262             CodeSourceKey csk = (CodeSourceKey) obj;
 263 
 264             if (!Objects.equals(cs.getLocationNoFragString(),
 265                                 csk.cs.getLocationNoFragString())) {
 266                 return false;
 267             }
 268 
 269             return cs.matchCerts(csk.cs, true);
 270         }
 271     }
 272 }