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