001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.extensions;
028 import org.opends.messages.Message;
029
030
031
032 import java.io.BufferedReader;
033 import java.io.File;
034 import java.io.FileReader;
035 import java.io.IOException;
036 import java.security.KeyStore;
037 import java.util.ArrayList;
038 import java.util.List;
039 import javax.net.ssl.KeyManager;
040 import javax.net.ssl.KeyManagerFactory;
041
042 import org.opends.server.admin.server.ConfigurationChangeListener;
043 import org.opends.server.admin.std.server.PKCS11KeyManagerProviderCfg;
044 import org.opends.server.api.KeyManagerProvider;
045 import org.opends.server.config.ConfigException;
046 import org.opends.server.core.DirectoryServer;
047 import org.opends.server.types.ConfigChangeResult;
048 import org.opends.server.types.DirectoryException;
049 import org.opends.server.types.DN;
050 import org.opends.server.types.InitializationException;
051 import org.opends.server.types.ResultCode;
052
053 import static org.opends.server.loggers.debug.DebugLogger.*;
054 import org.opends.server.loggers.debug.DebugTracer;
055 import org.opends.server.types.DebugLogLevel;
056 import static org.opends.messages.ExtensionMessages.*;
057
058 import static org.opends.server.util.StaticUtils.*;
059
060
061
062 /**
063 * This class defines a key manager provider that will access keys stored on a
064 * PKCS#11 device. It will use the Java PKCS#11 interface, which may need to be
065 * configured on the underlying system.
066 */
067 public class PKCS11KeyManagerProvider
068 extends KeyManagerProvider<PKCS11KeyManagerProviderCfg>
069 implements ConfigurationChangeListener<PKCS11KeyManagerProviderCfg>
070 {
071 /**
072 * The tracer object for the debug logger.
073 */
074 private static final DebugTracer TRACER = getTracer();
075
076
077
078 /**
079 * The keystore type to use when accessing the PKCS#11 keystore.
080 */
081 public static final String PKCS11_KEYSTORE_TYPE = "PKCS11";
082
083
084
085 // The DN of the configuration entry for this key manager provider.
086 private DN configEntryDN;
087
088 // The PIN needed to access the keystore.
089 private char[] keyStorePIN;
090
091 // The current configuration for this key manager provider.
092 private PKCS11KeyManagerProviderCfg currentConfig;
093
094
095
096 /**
097 * Creates a new instance of this PKCS#11 key manager provider. The
098 * <CODE>initializeKeyManagerProvider</CODE> method must be called on the
099 * resulting object before it may be used.
100 */
101 public PKCS11KeyManagerProvider()
102 {
103 // No implementation is required.
104 }
105
106
107
108 /**
109 * {@inheritDoc}
110 */
111 @Override
112 public void initializeKeyManagerProvider(
113 PKCS11KeyManagerProviderCfg configuration)
114 throws ConfigException, InitializationException
115 {
116 // Store the DN of the configuration entry and register to be notified of
117 // configuration changes.
118 currentConfig = configuration;
119 configEntryDN = configuration.dn();
120 configuration.addPKCS11ChangeListener(this);
121
122 // Get the PIN needed to access the contents of the PKCS#11
123 // keystore. We will offer several places to look for the PIN, and
124 // we will do so in the following order:
125 //
126 // - In a specified Java property
127 // - In a specified environment variable
128 // - In a specified file on the server filesystem.
129 // - As the value of a configuration attribute.
130 //
131 // In any case, the PIN must be in the clear.
132 keyStorePIN = null;
133
134 if (configuration.getKeyStorePinProperty() != null) {
135 String propertyName = configuration.getKeyStorePinProperty();
136 String pinStr = System.getProperty(propertyName);
137
138 if (pinStr == null) {
139 Message message = ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
140 String.valueOf(propertyName), String.valueOf(configEntryDN));
141 throw new InitializationException(message);
142 }
143
144 keyStorePIN = pinStr.toCharArray();
145 } else if (configuration.getKeyStorePinEnvironmentVariable() != null) {
146 String enVarName = configuration
147 .getKeyStorePinEnvironmentVariable();
148 String pinStr = System.getenv(enVarName);
149
150 if (pinStr == null) {
151 Message message = ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
152 String.valueOf(enVarName), String.valueOf(configEntryDN));
153 throw new InitializationException(message);
154 }
155
156 keyStorePIN = pinStr.toCharArray();
157 } else if (configuration.getKeyStorePinFile() != null) {
158 String fileName = configuration.getKeyStorePinFile();
159 File pinFile = getFileForPath(fileName);
160
161 if (!pinFile.exists()) {
162 Message message = ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(
163 String.valueOf(fileName), String.valueOf(configEntryDN));
164 throw new InitializationException(message);
165 }
166
167 String pinStr;
168 try {
169 BufferedReader br = new BufferedReader(
170 new FileReader(pinFile));
171 pinStr = br.readLine();
172 br.close();
173 } catch (IOException ioe) {
174 if (debugEnabled())
175 {
176 TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
177 }
178
179 Message message = ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.
180 get(String.valueOf(fileName), String.valueOf(configEntryDN),
181 getExceptionMessage(ioe));
182 throw new InitializationException(message, ioe);
183 }
184
185 if (pinStr == null) {
186 Message message = ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(
187 String.valueOf(fileName), String.valueOf(configEntryDN));
188 throw new InitializationException(message);
189 }
190
191 keyStorePIN = pinStr.toCharArray();
192 } else if (configuration.getKeyStorePin() != null) {
193 keyStorePIN = configuration.getKeyStorePin().toCharArray();
194 } else {
195 // Pin wasn't defined anywhere.
196 Message message =
197 ERR_PKCS11_KEYMANAGER_NO_PIN.get(String.valueOf(configEntryDN));
198 throw new ConfigException(message);
199 }
200 }
201
202
203
204 /**
205 * Performs any finalization that may be necessary for this key
206 * manager provider.
207 */
208 public void finalizeKeyManagerProvider()
209 {
210 currentConfig.removePKCS11ChangeListener(this);
211 }
212
213
214
215 /**
216 * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
217 * interactions requiring access to a key manager.
218 *
219 * @return A set of <CODE>KeyManager</CODE> objects that may be used for
220 * interactions requiring access to a key manager.
221 *
222 * @throws DirectoryException If a problem occurs while attempting to obtain
223 * the set of key managers.
224 */
225 public KeyManager[] getKeyManagers()
226 throws DirectoryException
227 {
228 KeyStore keyStore;
229 try
230 {
231 keyStore = KeyStore.getInstance(PKCS11_KEYSTORE_TYPE);
232 keyStore.load(null, keyStorePIN);
233 }
234 catch (Exception e)
235 {
236 if (debugEnabled())
237 {
238 TRACER.debugCaught(DebugLogLevel.ERROR, e);
239 }
240
241 Message message =
242 ERR_PKCS11_KEYMANAGER_CANNOT_LOAD.get(getExceptionMessage(e));
243 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
244 message, e);
245 }
246
247
248 try
249 {
250 String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
251 KeyManagerFactory keyManagerFactory =
252 KeyManagerFactory.getInstance(keyManagerAlgorithm);
253 keyManagerFactory.init(keyStore, keyStorePIN);
254 return keyManagerFactory.getKeyManagers();
255 }
256 catch (Exception e)
257 {
258 if (debugEnabled())
259 {
260 TRACER.debugCaught(DebugLogLevel.ERROR, e);
261 }
262
263 Message message = ERR_PKCS11_KEYMANAGER_CANNOT_CREATE_FACTORY.get(
264 getExceptionMessage(e));
265 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
266 message, e);
267 }
268 }
269
270
271
272 /**
273 * {@inheritDoc}
274 */
275 @Override()
276 public boolean isConfigurationAcceptable(
277 PKCS11KeyManagerProviderCfg configuration,
278 List<Message> unacceptableReasons)
279 {
280 return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
281 }
282
283
284
285 /**
286 * {@inheritDoc}
287 */
288 public boolean isConfigurationChangeAcceptable(
289 PKCS11KeyManagerProviderCfg configuration,
290 List<Message> unacceptableReasons)
291 {
292 boolean configAcceptable = true;
293 DN cfgEntryDN = configuration.dn();
294
295
296 // Get the PIN needed to access the contents of the keystore file.
297 //
298 // We will offer several places to look for the PIN, and we will
299 // do so in the following order:
300 //
301 // - In a specified Java property
302 // - In a specified environment variable
303 // - In a specified file on the server filesystem.
304 // - As the value of a configuration attribute.
305 //
306 // In any case, the PIN must be in the clear.
307 if (configuration.getKeyStorePinProperty() != null)
308 {
309 String propertyName = configuration.getKeyStorePinProperty();
310 String pinStr = System.getProperty(propertyName);
311
312 if (pinStr == null)
313 {
314 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
315 String.valueOf(propertyName),
316 String.valueOf(cfgEntryDN)));
317 configAcceptable = false;
318 }
319 }
320 else if (configuration.getKeyStorePinEnvironmentVariable() != null)
321 {
322 String enVarName = configuration.getKeyStorePinEnvironmentVariable();
323 String pinStr = System.getenv(enVarName);
324
325 if (pinStr == null)
326 {
327 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
328 String.valueOf(enVarName),
329 String.valueOf(cfgEntryDN)));
330 configAcceptable = false;
331 }
332 }
333 else if (configuration.getKeyStorePinFile() != null)
334 {
335 String fileName = configuration.getKeyStorePinFile();
336 File pinFile = getFileForPath(fileName);
337
338 if (!pinFile.exists())
339 {
340 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(
341 String.valueOf(fileName),
342 String.valueOf(cfgEntryDN)));
343 configAcceptable = false;
344 }
345 else
346 {
347 String pinStr = null;
348 BufferedReader br = null;
349 try {
350 br = new BufferedReader(new FileReader(pinFile));
351 pinStr = br.readLine();
352 }
353 catch (IOException ioe)
354 {
355 unacceptableReasons.add(
356 ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
357 String.valueOf(fileName),
358 String.valueOf(cfgEntryDN),
359 getExceptionMessage(ioe)));
360 configAcceptable = false;
361 }
362 finally
363 {
364 try
365 {
366 br.close();
367 } catch (Exception e) {}
368 }
369
370 if (pinStr == null)
371 {
372
373 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(
374 String.valueOf(fileName),
375 String.valueOf(cfgEntryDN)));
376 configAcceptable = false;
377 }
378 }
379 }
380 else if (configuration.getKeyStorePin() != null)
381 {
382 configuration.getKeyStorePin().toCharArray();
383 }
384 else
385 {
386 // Pin wasn't defined anywhere.
387 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_NO_PIN.get(
388 String.valueOf(cfgEntryDN)));
389 configAcceptable = false;
390 }
391
392 return configAcceptable;
393 }
394
395
396
397 /**
398 * {@inheritDoc}
399 */
400 public ConfigChangeResult applyConfigurationChange(
401 PKCS11KeyManagerProviderCfg configuration)
402 {
403 ResultCode resultCode = ResultCode.SUCCESS;
404 boolean adminActionRequired = false;
405 ArrayList<Message> messages = new ArrayList<Message>();
406
407
408 // Get the PIN needed to access the contents of the keystore file.
409 //
410 // We will offer several places to look for the PIN, and we will
411 // do so in the following order:
412 //
413 // - In a specified Java property
414 // - In a specified environment variable
415 // - In a specified file on the server filesystem.
416 // - As the value of a configuration attribute.
417 //
418 // In any case, the PIN must be in the clear.
419 char[] newPIN = null;
420
421 if (configuration.getKeyStorePinProperty() != null)
422 {
423 String propertyName = configuration.getKeyStorePinProperty();
424 String pinStr = System.getProperty(propertyName);
425
426 if (pinStr == null)
427 {
428 resultCode = DirectoryServer.getServerErrorResultCode();
429
430 messages.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
431 String.valueOf(propertyName),
432 String.valueOf(configEntryDN)));
433 }
434 else
435 {
436 newPIN = pinStr.toCharArray();
437 }
438 }
439 else if (configuration.getKeyStorePinEnvironmentVariable() != null)
440 {
441 String enVarName = configuration.getKeyStorePinEnvironmentVariable();
442 String pinStr = System.getenv(enVarName);
443
444 if (pinStr == null)
445 {
446 resultCode = DirectoryServer.getServerErrorResultCode();
447
448 messages.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
449 String.valueOf(enVarName),
450 String.valueOf(configEntryDN)));
451 }
452 else
453 {
454 newPIN = pinStr.toCharArray();
455 }
456 }
457 else if (configuration.getKeyStorePinFile() != null)
458 {
459 String fileName = configuration.getKeyStorePinFile();
460 File pinFile = getFileForPath(fileName);
461
462 if (!pinFile.exists())
463 {
464 resultCode = DirectoryServer.getServerErrorResultCode();
465
466 messages.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(
467 String.valueOf(fileName),
468 String.valueOf(configEntryDN)));
469 }
470 else
471 {
472 String pinStr = null;
473 BufferedReader br = null;
474 try {
475 br = new BufferedReader(new FileReader(pinFile));
476 pinStr = br.readLine();
477 }
478 catch (IOException ioe)
479 {
480 resultCode = DirectoryServer.getServerErrorResultCode();
481
482 messages.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
483 String.valueOf(fileName),
484 String.valueOf(configEntryDN),
485 getExceptionMessage(ioe)));
486 }
487 finally
488 {
489 try
490 {
491 br.close();
492 } catch (Exception e) {}
493 }
494
495 if (pinStr == null)
496 {
497 resultCode = DirectoryServer.getServerErrorResultCode();
498
499 messages.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(
500 String.valueOf(fileName),
501 String.valueOf(configEntryDN)));
502 }
503 else
504 {
505 newPIN = pinStr.toCharArray();
506 }
507 }
508 }
509 else if (configuration.getKeyStorePin() != null)
510 {
511 newPIN = configuration.getKeyStorePin().toCharArray();
512 }
513 else
514 {
515 // Pin wasn't defined anywhere.
516 resultCode = DirectoryServer.getServerErrorResultCode();
517
518
519 messages.add(ERR_PKCS11_KEYMANAGER_NO_PIN.get(
520 String.valueOf(configEntryDN)));
521 }
522
523
524 if (resultCode == ResultCode.SUCCESS)
525 {
526 currentConfig = configuration;
527 keyStorePIN = newPIN;
528 }
529
530
531 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
532 }
533 }
534