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 33 /** 34 * This class extends {@code ClassLoader} with additional support for defining 35 * classes with an associated code source and permissions. 36 * 37 * @apiNote 38 * Permissions cannot be used for controlling access to resources 39 * as the Security Manager is no longer supported. 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 * @apiNote If {@code parent} is specified as {@code null} (for the 68 * bootstrap class loader) then there is no guarantee that all platform 69 * classes are visible. 70 * See {@linkplain ClassLoader##builtinLoaders Run-time Built-in Class Loaders} 71 * for information on the bootstrap class loader and other built-in class loaders. 72 * 73 * @param parent the parent ClassLoader, can be {@code null} for the bootstrap 74 * class loader 75 */ 76 protected SecureClassLoader(ClassLoader parent) { 77 super(parent); 78 } 79 80 /** 81 * Creates a new {@code SecureClassLoader} using the 82 * {@linkplain ClassLoader#getSystemClassLoader() system class loader as the parent}. 83 */ 84 protected SecureClassLoader() { 85 super(); 86 } 87 88 /** 89 * Creates a new {@code SecureClassLoader} of the specified name and 90 * using the specified parent class loader for delegation. 91 * 92 * @apiNote If {@code parent} is specified as {@code null} (for the 93 * bootstrap class loader) then there is no guarantee that all platform 94 * classes are visible. 95 * See {@linkplain ClassLoader##builtinLoaders Run-time Built-in Class Loaders} 96 * for information on the bootstrap class loader and other built-in class loaders. 97 * 98 * @param name class loader name; or {@code null} if not named 99 * @param parent the parent class loader, can be {@code null} for the bootstrap 100 * class loader 101 * 102 * @throws IllegalArgumentException if the given name is empty. 103 * 104 * @since 9 105 */ 106 protected SecureClassLoader(String name, ClassLoader parent) { 107 super(name, parent); 108 } 109 110 /** 111 * Converts an array of bytes into an instance of class {@code Class}, 112 * with an optional CodeSource. Before the 113 * class can be used it must be resolved. 114 * <p> 115 * If a non-null CodeSource is supplied a ProtectionDomain is 116 * constructed and associated with the class being defined. 117 * 118 * @param name the expected name of the class, or {@code null} 119 * if not known, using '.' and not '/' as the separator 120 * and without a trailing ".class" suffix. 121 * @param b the bytes that make up the class data. The bytes in 122 * positions {@code off} through {@code off+len-1} 123 * should have the format of a valid class file as defined by 124 * <cite>The Java Virtual Machine Specification</cite>. 125 * @param off the start offset in {@code b} of the class data 126 * @param len the length of the class data 127 * @param cs the associated CodeSource, or {@code null} if none 128 * @return the {@code Class} object created from the data, 129 * and optional CodeSource. 130 * @throws ClassFormatError if the data did not contain a valid class 131 * @throws IndexOutOfBoundsException if either {@code off} or 132 * {@code len} is negative, or if 133 * {@code off+len} is greater than {@code b.length}. 134 * 135 * @throws SecurityException if an attempt is made to add this class 136 * to a package that contains classes that were signed by 137 * a different set of certificates than this class, or if 138 * the class name begins with "java.". 139 */ 140 protected final Class<?> defineClass(String name, 141 byte[] b, int off, int len, 142 CodeSource cs) 143 { 144 return defineClass(name, b, off, len, getProtectionDomain(cs)); 145 } 146 147 /** 148 * Converts a {@link java.nio.ByteBuffer ByteBuffer} 149 * into an instance of class {@code Class}, with an optional CodeSource. 150 * Before the class can be used it must be resolved. 151 * <p> 152 * If a non-null CodeSource is supplied a ProtectionDomain is 153 * constructed and associated with the class being defined. 154 * 155 * @param name the expected name of the class, or {@code null} 156 * if not known, using '.' and not '/' as the separator 157 * and without a trailing ".class" suffix. 158 * @param b the bytes that make up the class data. The bytes from positions 159 * {@code b.position()} through {@code b.position() + b.limit() -1} 160 * should have the format of a valid class file as defined by 161 * <cite>The Java Virtual Machine Specification</cite>. 162 * @param cs the associated CodeSource, or {@code null} if none 163 * @return the {@code Class} object created from the data, 164 * and optional CodeSource. 165 * @throws ClassFormatError if the data did not contain a valid class 166 * @throws SecurityException if an attempt is made to add this class 167 * to a package that contains classes that were signed by 168 * a different set of certificates than this class, or if 169 * the class name begins with "java.". 170 * 171 * @since 1.5 172 */ 173 protected final Class<?> defineClass(String name, java.nio.ByteBuffer b, 174 CodeSource cs) 175 { 176 return defineClass(name, b, getProtectionDomain(cs)); 177 } 178 179 /** 180 * Returns the permissions for the given CodeSource object. 181 * <p> 182 * This method is invoked by the defineClass method which takes 183 * a CodeSource as an argument when it is constructing the 184 * ProtectionDomain for the class being defined. 185 * 186 * @param codesource the codesource. 187 * 188 * @return the permissions for the codesource. 189 * 190 */ 191 protected PermissionCollection getPermissions(CodeSource codesource) 192 { 193 return new Permissions(); // ProtectionDomain defers the binding 194 } 195 196 /* 197 * Returned cached ProtectionDomain for the specified CodeSource. 198 */ 199 private ProtectionDomain getProtectionDomain(CodeSource cs) { 200 if (cs == null) { 201 return null; 202 } 203 204 // Use a CodeSourceKey object key. It should behave in the 205 // same manner as the CodeSource when compared for equality except 206 // that no nameservice lookup is done on the hostname (String comparison 207 // only), and the fragment is not considered. 208 CodeSourceKey key = new CodeSourceKey(cs); 209 return pdcache.computeIfAbsent(key, new Function<>() { 210 // Do not turn this into a lambda since it is executed during bootstrap 211 @Override 212 public ProtectionDomain apply(CodeSourceKey key) { 213 PermissionCollection perms 214 = SecureClassLoader.this.getPermissions(key.cs); 215 ProtectionDomain pd = new ProtectionDomain( 216 key.cs, perms, SecureClassLoader.this, null); 217 return pd; 218 } 219 }); 220 } 221 222 private record CodeSourceKey(CodeSource cs) { 223 224 @Override 225 public int hashCode() { 226 return Objects.hashCode(cs.getLocationNoFragString()); 227 } 228 229 @Override 230 public boolean equals(Object obj) { 231 if (obj == this) { 232 return true; 233 } 234 235 return obj instanceof CodeSourceKey other 236 && Objects.equals(cs.getLocationNoFragString(), 237 other.cs.getLocationNoFragString()) 238 && cs.matchCerts(other.cs, true); 239 } 240 } 241 242 /** 243 * Called by the VM, during -Xshare:dump 244 */ 245 private void resetArchivedStates() { 246 pdcache.clear(); 247 } 248 }