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