-
Notifications
You must be signed in to change notification settings - Fork 668
/
SecureClassLoader.java
300 lines (275 loc) · 12 KB
/
SecureClassLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.security;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import sun.security.util.Debug;
/**
* This class extends ClassLoader with additional support for defining
* classes with an associated code source and permissions which are
* retrieved by the system policy by default.
*
* @author Li Gong
* @author Roland Schemers
* @since 1.2
*/
// 类加载器的安全层
public class SecureClassLoader extends ClassLoader {
/**
* If initialization succeed this is set to true and security checks will
* succeed. Otherwise the object is not initialized and the object is
* useless.
*/
private final boolean initialized;
/**
* Map that maps the CodeSource to a ProtectionDomain. The key is a
* CodeSourceKey class that uses a String instead of a URL to avoid
* potential expensive name service lookups. This does mean that URLs that
* are equivalent after nameservice lookup will be placed in separate
* ProtectionDomains; however during policy enforcement these URLs will be
* canonicalized and resolved resulting in a consistent set of granted
* permissions.
*/
private final Map<CodeSourceKey, ProtectionDomain> pdcache = new ConcurrentHashMap<>(11);
static {
ClassLoader.registerAsParallelCapable();
}
/**
* Creates a new SecureClassLoader using the default parent class
* loader for delegation.
*
* <p>If there is a security manager, this method first
* calls the security manager's {@code checkCreateClassLoader}
* method to ensure creation of a class loader is allowed.
*
* @throws SecurityException if a security manager exists and its
* {@code checkCreateClassLoader} method doesn't allow
* creation of a class loader.
* @see SecurityManager#checkCreateClassLoader
*/
protected SecureClassLoader() {
super();
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if(security != null) {
security.checkCreateClassLoader();
}
initialized = true;
}
/**
* Creates a new SecureClassLoader using the specified parent
* class loader for delegation.
*
* <p>If there is a security manager, this method first
* calls the security manager's {@code checkCreateClassLoader}
* method to ensure creation of a class loader is allowed.
*
* @param parent the parent ClassLoader
*
* @throws SecurityException if a security manager exists and its
* {@code checkCreateClassLoader} method doesn't allow
* creation of a class loader.
* @see SecurityManager#checkCreateClassLoader
*/
protected SecureClassLoader(ClassLoader parent) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if(security != null) {
security.checkCreateClassLoader();
}
initialized = true;
}
/**
* Creates a new {@code SecureClassLoader} of the specified name and
* using the specified parent class loader for delegation.
*
* @param name class loader name; or {@code null} if not named
* @param parent the parent class loader
*
* @throws IllegalArgumentException if the given name is empty.
* @throws SecurityException if a security manager exists and its
* {@link SecurityManager#checkCreateClassLoader()} method
* doesn't allow creation of a class loader.
* @spec JPMS
* @since 9
*/
protected SecureClassLoader(String name, ClassLoader parent) {
super(name, parent);
SecurityManager security = System.getSecurityManager();
if(security != null) {
security.checkCreateClassLoader();
}
initialized = true;
}
/**
* Converts an array of bytes into an instance of class Class,
* with an optional CodeSource. Before the
* class can be used it must be resolved.
* <p>
* If a non-null CodeSource is supplied a ProtectionDomain is
* constructed and associated with the class being defined.
*
* @param name the expected name of the class, or {@code null}
* if not known, using '.' and not '/' as the separator
* and without a trailing ".class" suffix.
* @param b the bytes that make up the class data. The bytes in
* positions {@code off} through {@code off+len-1}
* should have the format of a valid class file as defined by
* <cite>The Java™ Virtual Machine Specification</cite>.
* @param off the start offset in {@code b} of the class data
* @param len the length of the class data
* @param cs the associated CodeSource, or {@code null} if none
*
* @return the {@code Class} object created from the data,
* and optional CodeSource.
*
* @throws ClassFormatError if the data did not contain a valid class
* @throws IndexOutOfBoundsException if either {@code off} or
* {@code len} is negative, or if
* {@code off+len} is greater than {@code b.length}.
* @throws SecurityException if an attempt is made to add this class
* to a package that contains classes that were signed by
* a different set of certificates than this class, or if
* the class name begins with "java.".
*/
// 利用存储在字节数组中的字节码去定义类
protected final Class<?> defineClass(String name, byte[] b, int off, int len, CodeSource cs) {
return defineClass(name, b, off, len, getProtectionDomain(cs));
}
/**
* Converts a {@link java.nio.ByteBuffer ByteBuffer}
* into an instance of class {@code Class}, with an optional CodeSource.
* Before the class can be used it must be resolved.
* <p>
* If a non-null CodeSource is supplied a ProtectionDomain is
* constructed and associated with the class being defined.
*
* @param name the expected name of the class, or {@code null}
* if not known, using '.' and not '/' as the separator
* and without a trailing ".class" suffix.
* @param b the bytes that make up the class data. The bytes from positions
* {@code b.position()} through {@code b.position() + b.limit() -1}
* should have the format of a valid class file as defined by
* <cite>The Java™ Virtual Machine Specification</cite>.
* @param cs the associated CodeSource, or {@code null} if none
*
* @return the {@code Class} object created from the data,
* and optional CodeSource.
*
* @throws ClassFormatError if the data did not contain a valid class
* @throws SecurityException if an attempt is made to add this class
* to a package that contains classes that were signed by
* a different set of certificates than this class, or if
* the class name begins with "java.".
* @since 1.5
*/
// 利用存储在缓冲区中的字节码去定义类
protected final Class<?> defineClass(String name, ByteBuffer b, CodeSource cs) {
return defineClass(name, b, getProtectionDomain(cs));
}
/**
* Returns the permissions for the given CodeSource object.
* <p>
* This method is invoked by the defineClass method which takes
* a CodeSource as an argument when it is constructing the
* ProtectionDomain for the class being defined.
*
* @param codesource the codesource.
*
* @return the permissions granted to the codesource.
*/
protected PermissionCollection getPermissions(CodeSource codesource) {
check();
return new Permissions(); // ProtectionDomain defers the binding
}
/**
* Returned cached ProtectionDomain for the specified CodeSource.
*/
private ProtectionDomain getProtectionDomain(CodeSource cs) {
if(cs == null) {
return null;
}
// Use a CodeSourceKey object key. It should behave in the
// same manner as the CodeSource when compared for equality except
// that no nameservice lookup is done on the hostname (String comparison
// only), and the fragment is not considered.
CodeSourceKey key = new CodeSourceKey(cs);
return pdcache.computeIfAbsent(key, new Function<>() {
@Override
public ProtectionDomain apply(CodeSourceKey key /* not used */) {
PermissionCollection perms = SecureClassLoader.this.getPermissions(cs);
ProtectionDomain pd = new ProtectionDomain(cs, perms, SecureClassLoader.this, null);
if(DebugHolder.debug != null) {
DebugHolder.debug.println(" getPermissions " + pd);
DebugHolder.debug.println("");
}
return pd;
}
});
}
/**
* Check to make sure the class loader has been initialized.
*/
private void check() {
if(!initialized) {
throw new SecurityException("ClassLoader object not initialized");
}
}
/**
* holder class for the static field "debug" to delay its initialization
*/
private static class DebugHolder {
private static final Debug debug = Debug.getInstance("scl");
}
private static class CodeSourceKey {
private final CodeSource cs;
CodeSourceKey(CodeSource cs) {
this.cs = cs;
}
@Override
public int hashCode() {
String locationNoFrag = cs.getLocationNoFragString();
return locationNoFrag != null ? locationNoFrag.hashCode() : 0;
}
@Override
public boolean equals(Object obj) {
if(obj == this) {
return true;
}
if(!(obj instanceof CodeSourceKey)) {
return false;
}
CodeSourceKey csk = (CodeSourceKey) obj;
if(!Objects.equals(cs.getLocationNoFragString(), csk.cs.getLocationNoFragString())) {
return false;
}
return cs.matchCerts(csk.cs, true);
}
}
}