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