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