-
Notifications
You must be signed in to change notification settings - Fork 5.8k
/
Copy pathJceSecurityManager.java
265 lines (240 loc) · 10.2 KB
/
JceSecurityManager.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
/*
* Copyright (c) 1999, 2024, 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 javax.crypto;
import java.security.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.lang.StackWalker.*;
/**
* The JCE security manager.
*
* <p>The JCE security manager is responsible for determining the maximum
* allowable cryptographic strength for a given application, for a given
* algorithm, by consulting the configured jurisdiction policy files and
* the cryptographic permissions bundled with the application.
*
* @author Jan Luehe
*
* @since 1.4
*/
final class JceSecurityManager {
private static final CryptoPermissions defaultPolicy;
private static final CryptoPermissions exemptPolicy;
private static final CryptoAllPermission allPerm;
private static final Vector<Class<?>> TrustedCallersCache =
new Vector<>(2);
private static final ConcurrentMap<URL,CryptoPermissions> exemptCache =
new ConcurrentHashMap<>();
private static final CryptoPermissions CACHE_NULL_MARK =
new CryptoPermissions();
// singleton instance
static final JceSecurityManager INSTANCE;
static final StackWalker WALKER;
static {
defaultPolicy = JceSecurity.getDefaultPolicy();
exemptPolicy = JceSecurity.getExemptPolicy();
allPerm = CryptoAllPermission.INSTANCE;
INSTANCE = new JceSecurityManager();
WALKER = StackWalker.getInstance(
Set.of(Option.DROP_METHOD_INFO, Option.RETAIN_CLASS_REFERENCE));
}
private JceSecurityManager() {
// empty
}
/**
* Returns the maximum allowable crypto strength for the given
* application, for the given algorithm.
*/
CryptoPermission getCryptoPermission(String theAlg) {
// Need to convert to uppercase since the crypto perm
// lookup is case-sensitive.
final String alg = theAlg.toUpperCase(Locale.ENGLISH);
// If CryptoAllPermission is granted by default, we return that.
// Otherwise, this will be the permission we return if anything goes
// wrong.
CryptoPermission defaultPerm = getDefaultPermission(alg);
if (defaultPerm == CryptoAllPermission.INSTANCE) {
return defaultPerm;
}
// Determine the codebase of the caller of the JCE API.
// This is the codebase of the first class which is not in
// javax.crypto.* packages.
// NOTE: javax.crypto.* package maybe subject to package
// insertion, so need to check its classloader as well.
return WALKER.walk(s -> s.map(StackFrame::getDeclaringClass)
.filter(c -> !c.getPackageName().equals("javax.crypto"))
.map(cls -> {
URL callerCodeBase = JceSecurity.getCodeBase(cls);
return (callerCodeBase != null) ?
getCryptoPermissionFromURL(callerCodeBase,
alg, defaultPerm) : defaultPerm;})
.findFirst().get() // nulls not possible for Optional
);
}
CryptoPermission getCryptoPermissionFromURL(URL callerCodeBase,
String alg, CryptoPermission defaultPerm) {
CryptoPermissions appPerms = exemptCache.get(callerCodeBase);
if (appPerms == null) {
// no match found in cache
synchronized (this.getClass()) {
appPerms = exemptCache.get(callerCodeBase);
if (appPerms == null) {
appPerms = getAppPermissions(callerCodeBase);
exemptCache.putIfAbsent(callerCodeBase,
(appPerms == null? CACHE_NULL_MARK:appPerms));
}
}
}
if (appPerms == null || appPerms == CACHE_NULL_MARK) {
return defaultPerm;
}
// If the app was granted the special CryptoAllPermission, return that.
if (appPerms.implies(allPerm)) {
return allPerm;
}
// Check if the crypto permissions granted to the app contain a
// crypto permission for the requested algorithm that does not require
// any exemption mechanism to be enforced.
// Return that permission, if present.
PermissionCollection appPc = appPerms.getPermissionCollection(alg);
if (appPc == null) {
return defaultPerm;
}
Enumeration<Permission> enum_ = appPc.elements();
while (enum_.hasMoreElements()) {
CryptoPermission cp = (CryptoPermission)enum_.nextElement();
if (cp.getExemptionMechanism() == null) {
return cp;
}
}
// Check if the jurisdiction file for exempt applications contains
// any entries for the requested algorithm.
// If not, return the default permission.
PermissionCollection exemptPc =
exemptPolicy.getPermissionCollection(alg);
if (exemptPc == null) {
return defaultPerm;
}
// In the jurisdiction file for exempt applications, go through the
// list of CryptoPermission entries for the requested algorithm, and
// stop at the first entry:
// - that is implied by the collection of crypto permissions granted
// to the app, and
// - whose exemption mechanism is available from one of the
// registered CSPs
enum_ = exemptPc.elements();
while (enum_.hasMoreElements()) {
CryptoPermission cp = (CryptoPermission)enum_.nextElement();
try {
ExemptionMechanism.getInstance(cp.getExemptionMechanism());
if (cp.getAlgorithm().equals(
CryptoPermission.ALG_NAME_WILDCARD)) {
CryptoPermission newCp;
if (cp.getCheckParam()) {
newCp = new CryptoPermission(
alg, cp.getMaxKeySize(),
cp.getAlgorithmParameterSpec(),
cp.getExemptionMechanism());
} else {
newCp = new CryptoPermission(
alg, cp.getMaxKeySize(),
cp.getExemptionMechanism());
}
if (appPerms.implies(newCp)) {
return newCp;
}
}
if (appPerms.implies(cp)) {
return cp;
}
} catch (Exception e) {
}
}
return defaultPerm;
}
private static CryptoPermissions getAppPermissions(URL callerCodeBase) {
// Check if app is exempt, and retrieve the permissions bundled with it
try {
return JceSecurity.verifyExemptJar(callerCodeBase);
} catch (Exception e) {
// Jar verification fails
return null;
}
}
/**
* Returns the default permission for the given algorithm.
*/
private CryptoPermission getDefaultPermission(String alg) {
Enumeration<Permission> enum_ =
defaultPolicy.getPermissionCollection(alg).elements();
return (CryptoPermission)enum_.nextElement();
}
// Only used by javax.crypto.Cipher constructor to disallow Cipher
// objects being constructed by untrusted code (See bug 4341369 &
// 4334690 for more info).
boolean isCallerTrusted(Class<?> caller, Provider provider) {
// Get the caller and its codebase.
if (caller != null) {
URL callerCodeBase = JceSecurity.getCodeBase(caller);
if (callerCodeBase == null) {
return true;
}
// The caller has been verified.
if (TrustedCallersCache.contains(caller)) {
return true;
}
// Check the association between caller and provider
Class<?> pCls = provider.getClass();
Module pMod = pCls.getModule();
// If they are in the same named module or share
// the same codebase, then they are associated
boolean sameOrigin = (pMod.isNamed()?
caller.getModule().equals(pMod) :
callerCodeBase.equals(JceSecurity.getCodeBase(pCls)));
if (sameOrigin) {
// The caller is from trusted provider
if (ProviderVerifier.isTrustedCryptoProvider(provider)) {
TrustedCallersCache.addElement(caller);
return true;
}
} else {
// Don't include the provider in the subsequent
// JceSecurity.verifyProvider(...) call
provider = null;
}
// Check whether the caller is a trusted provider.
try {
JceSecurity.verifyProvider(callerCodeBase, provider);
} catch (Exception e2) {
return false;
}
TrustedCallersCache.addElement(caller);
return true;
}
return false;
}
}