blob: 9b40307b1668cdcfd7a1393cd312c4605b618e40 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.jca;
27
28import java.io.File;
29import java.lang.reflect.*;
30
31import java.security.*;
32
33import sun.security.util.PropertyExpander;
34
35/**
36 * Class representing a configured provider. Encapsulates configuration
37 * (className plus optional argument), the provider loading logic, and
38 * the loaded Provider object itself.
39 *
40 * @author Andreas Sterbenz
41 * @since 1.5
42 */
43final class ProviderConfig {
44
45 private final static sun.security.util.Debug debug =
46 sun.security.util.Debug.getInstance("jca", "ProviderConfig");
47
48 // classname of the SunPKCS11-Solaris provider
49 private static final String P11_SOL_NAME =
50 "sun.security.pkcs11.SunPKCS11";
51
52 // config file argument of the SunPKCS11-Solaris provider
53 private static final String P11_SOL_ARG =
54 "${java.home}/lib/security/sunpkcs11-solaris.cfg";
55
56 // maximum number of times to try loading a provider before giving up
57 private final static int MAX_LOAD_TRIES = 30;
58
59 // parameters for the Provider(String) constructor,
60 // use by doLoadProvider()
61 private final static Class[] CL_STRING = { String.class };
62
63 // lock to use while loading a provider. it ensures that each provider
64 // is loaded only once and that we can detect recursion.
65 // NOTE that because of 4944382 we use the system classloader as lock.
66 // By using the same lock to load classes as to load providers we avoid
67 // deadlock due to lock ordering. However, this class may be initialized
68 // early in the startup when the system classloader has not yet been set
69 // up. Use a temporary lock object if that is the case.
70 // Any of this may break if the class loading implementation is changed.
71 private static volatile Object LOCK = new Object();
72
73 private static Object getLock() {
74 Object o = LOCK;
75 // check if lock is already set to the class loader
76 if (o instanceof ClassLoader) {
77 return o;
78 }
79 Object cl = AccessController.doPrivileged(
80 new PrivilegedAction<Object>() {
81 public Object run() {
82 return ClassLoader.getSystemClassLoader();
83 }
84 });
85 // check if class loader initialized now (non-null)
86 if (cl != null) {
87 LOCK = cl;
88 o = cl;
89 }
90 return o;
91 }
92
93
94 // name of the provider class
95 private final String className;
96
97 // argument to the provider constructor,
98 // empty string indicates no-arg constructor
99 private final String argument;
100
101 // number of times we have already tried to load this provider
102 private int tries;
103
104 // Provider object, if loaded
105 private volatile Provider provider;
106
107 // flag indicating if we are currently trying to load the provider
108 // used to detect recursion
109 private boolean isLoading;
110
111 ProviderConfig(String className, String argument) {
112 if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) {
113 checkSunPKCS11Solaris();
114 }
115 this.className = className;
116 this.argument = expand(argument);
117 }
118
119 ProviderConfig(String className) {
120 this(className, "");
121 }
122
123 ProviderConfig(Provider provider) {
124 this.className = provider.getClass().getName();
125 this.argument = "";
126 this.provider = provider;
127 }
128
129 // check if we should try to load the SunPKCS11-Solaris provider
130 // avoid if not available (pre Solaris 10) to reduce startup time
131 // or if disabled via system property
132 private void checkSunPKCS11Solaris() {
133 Boolean o = AccessController.doPrivileged(
134 new PrivilegedAction<Boolean>() {
135 public Boolean run() {
136 File file = new File("/usr/lib/libpkcs11.so");
137 if (file.exists() == false) {
138 return Boolean.FALSE;
139 }
140 if ("false".equalsIgnoreCase(System.getProperty
141 ("sun.security.pkcs11.enable-solaris"))) {
142 return Boolean.FALSE;
143 }
144 return Boolean.TRUE;
145 }
146 });
147 if (o == Boolean.FALSE) {
148 tries = MAX_LOAD_TRIES;
149 }
150 }
151
152 private boolean hasArgument() {
153 return argument.length() != 0;
154 }
155
156 // should we try to load this provider?
157 private boolean shouldLoad() {
158 return (tries < MAX_LOAD_TRIES);
159 }
160
161 // do not try to load this provider again
162 private void disableLoad() {
163 tries = MAX_LOAD_TRIES;
164 }
165
166 boolean isLoaded() {
167 return (provider != null);
168 }
169
170 public boolean equals(Object obj) {
171 if (this == obj) {
172 return true;
173 }
174 if (obj instanceof ProviderConfig == false) {
175 return false;
176 }
177 ProviderConfig other = (ProviderConfig)obj;
178 return this.className.equals(other.className)
179 && this.argument.equals(other.argument);
180 }
181
182 public int hashCode() {
183 return className.hashCode() + argument.hashCode();
184 }
185
186 public String toString() {
187 if (hasArgument()) {
188 return className + "('" + argument + "')";
189 } else {
190 return className;
191 }
192 }
193
194 /**
195 * Get the provider object. Loads the provider if it is not already loaded.
196 */
197 Provider getProvider() {
198 // volatile variable load
199 Provider p = provider;
200 if (p != null) {
201 return p;
202 }
203 if (shouldLoad() == false) {
204 return null;
205 }
206 synchronized (getLock()) {
207 p = provider;
208 if (p != null) {
209 // loaded by another thread while we were blocked on lock
210 return p;
211 }
212 if (isLoading) {
213 // because this method is synchronized, this can only
214 // happen if there is recursion.
215 if (debug != null) {
216 debug.println("Recursion loading provider: " + this);
217 new Exception("Call trace").printStackTrace();
218 }
219 return null;
220 }
221 try {
222 isLoading = true;
223 tries++;
224 p = doLoadProvider();
225 } finally {
226 isLoading = false;
227 }
228 provider = p;
229 }
230 return p;
231 }
232
233 /**
234 * Load and instantiate the Provider described by this class.
235 *
236 * NOTE use of doPrivileged().
237 *
238 * @return null if the Provider could not be loaded
239 *
240 * @throws ProviderException if executing the Provider's constructor
241 * throws a ProviderException. All other Exceptions are ignored.
242 */
243 private Provider doLoadProvider() {
244 return AccessController.doPrivileged(new PrivilegedAction<Provider>() {
245 public Provider run() {
246 if (debug != null) {
247 debug.println("Loading provider: " + ProviderConfig.this);
248 }
249 try {
250 ClassLoader cl = ClassLoader.getSystemClassLoader();
251 Class<?> provClass;
252 if (cl != null) {
253 provClass = cl.loadClass(className);
254 } else {
255 provClass = Class.forName(className);
256 }
257 Object obj;
258 if (hasArgument() == false) {
259 obj = provClass.newInstance();
260 } else {
261 Constructor<?> cons = provClass.getConstructor(CL_STRING);
262 obj = cons.newInstance(argument);
263 }
264 if (obj instanceof Provider) {
265 if (debug != null) {
266 debug.println("Loaded provider " + obj);
267 }
268 return (Provider)obj;
269 } else {
270 if (debug != null) {
271 debug.println(className + " is not a provider");
272 }
273 disableLoad();
274 return null;
275 }
276 } catch (Exception e) {
277 Throwable t;
278 if (e instanceof InvocationTargetException) {
279 t = ((InvocationTargetException)e).getCause();
280 } else {
281 t = e;
282 }
283 if (debug != null) {
284 debug.println("Error loading provider " + ProviderConfig.this);
285 t.printStackTrace();
286 }
287 // provider indicates fatal error, pass through exception
288 if (t instanceof ProviderException) {
289 throw (ProviderException)t;
290 }
291 // provider indicates that loading should not be retried
292 if (t instanceof UnsupportedOperationException) {
293 disableLoad();
294 }
295 return null;
296 }
297 }
298 });
299 }
300
301 /**
302 * Perform property expansion of the provider value.
303 *
304 * NOTE use of doPrivileged().
305 */
306 private static String expand(final String value) {
307 // shortcut if value does not contain any properties
308 if (value.contains("${") == false) {
309 return value;
310 }
311 return AccessController.doPrivileged(new PrivilegedAction<String>() {
312 public String run() {
313 try {
314 return PropertyExpander.expand(value);
315 } catch (GeneralSecurityException e) {
316 throw new ProviderException(e);
317 }
318 }
319 });
320 }
321
322}