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 import jdk.internal.misc.CDS; 35 36 /** 37 * This class extends {@code ClassLoader} with additional support for defining 38 * classes with an associated code source and permissions which are 39 * retrieved by the system policy by default. 40 * 41 * @author Li Gong 42 * @author Roland Schemers 43 * @since 1.2 44 */ 45 public class SecureClassLoader extends ClassLoader { 46 47 /* 48 * Map that maps the CodeSource to a ProtectionDomain. The key is a 49 * CodeSourceKey class that uses a {@code String} instead of a URL to avoid 50 * potential expensive name service lookups. This does mean that URLs that 51 * are equivalent after nameservice lookup will be placed in separate 52 * ProtectionDomains; however during policy enforcement these URLs will be 53 * canonicalized and resolved resulting in a consistent set of granted 54 * permissions. 55 */ 56 private final Map<CodeSourceKey, ProtectionDomain> pdcache 57 = new ConcurrentHashMap<>(11); 58 59 static { 60 ClassLoader.registerAsParallelCapable(); 61 } 62 63 /** 64 * Creates a new {@code SecureClassLoader} using the specified parent 65 * class loader for delegation. 66 * 67 * <p>If there is a security manager, this method first 68 * calls the security manager's {@code checkCreateClassLoader} 69 * method to ensure creation of a class loader is allowed. 70 * 71 * @param parent the parent ClassLoader 72 * @throws SecurityException if a security manager exists and its 73 * {@code checkCreateClassLoader} method doesn't allow 74 * creation of a class loader. 75 * @see SecurityManager#checkCreateClassLoader 76 */ 77 protected SecureClassLoader(ClassLoader parent) { 78 super(parent); 79 } 80 81 /** 82 * Creates a new {@code SecureClassLoader} using the default parent class 83 * loader for delegation. 84 * 85 * <p>If there is a security manager, this method first 86 * calls the security manager's {@code checkCreateClassLoader} 87 * method to ensure creation of a class loader is allowed. 88 * 89 * @throws SecurityException if a security manager exists and its 90 * {@code checkCreateClassLoader} method doesn't allow 91 * creation of a class loader. 92 * @see SecurityManager#checkCreateClassLoader 93 */ 94 protected SecureClassLoader() { 95 super(); 96 } 97 98 /** 99 * Creates a new {@code SecureClassLoader} of the specified name and 100 * using the specified parent class loader for delegation. 101 * 102 * @param name class loader name; or {@code null} if not named 103 * @param parent the parent class loader 104 * 105 * @throws IllegalArgumentException if the given name is empty. 106 * 107 * @throws SecurityException if a security manager exists and its 108 * {@link SecurityManager#checkCreateClassLoader()} method 109 * doesn't allow creation of a class loader. 110 * 111 * @since 9 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 {@code 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 // Do not turn this into a lambda since it is executed during bootstrap 225 @Override 226 public ProtectionDomain apply(CodeSourceKey key) { 227 PermissionCollection perms 228 = SecureClassLoader.this.getPermissions(key.cs); 229 ProtectionDomain pd = new ProtectionDomain( 230 key.cs, perms, SecureClassLoader.this, null); 231 if (DebugHolder.debug != null) { 232 DebugHolder.debug.println(" getPermissions " + pd); 233 DebugHolder.debug.println(""); 234 } 235 return pd; 236 } 237 }); 238 } 239 240 private record CodeSourceKey(CodeSource cs) { 241 242 @Override 243 public int hashCode() { 244 return Objects.hashCode(cs.getLocationNoFragString()); 245 } 246 247 @Override 248 public boolean equals(Object obj) { 249 if (obj == this) { 250 return true; 251 } 252 253 return obj instanceof CodeSourceKey other 254 && Objects.equals(cs.getLocationNoFragString(), 255 other.cs.getLocationNoFragString()) 256 && cs.matchCerts(other.cs, true); 257 } 258 } 259 260 /** 261 * Called by the VM, during -Xshare:dump 262 */ 263 private void resetArchivedStates() { 264 if (CDS.isDumpingProtectionDomains()) { 265 if (System.getProperty("cds.debug.archived.protection.domains") != null) { 266 for (Map.Entry<CodeSourceKey, ProtectionDomain> entry : pdcache.entrySet()) { 267 CodeSourceKey key = entry.getKey(); 268 System.out.println("Archiving ProtectionDomain " + key.cs + " for " + this); 269 } 270 } 271 } else { 272 pdcache.clear(); 273 } 274 } 275 }