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.FileInputStream;
035 import java.io.FileReader;
036 import java.io.IOException;
037 import java.security.KeyStore;
038 import java.security.KeyStoreException;
039 import java.util.ArrayList;
040 import java.util.List;
041 import javax.net.ssl.KeyManager;
042 import javax.net.ssl.KeyManagerFactory;
043
044 import org.opends.server.admin.server.ConfigurationChangeListener;
045 import org.opends.server.admin.std.server.FileBasedKeyManagerProviderCfg;
046 import org.opends.server.api.KeyManagerProvider;
047 import org.opends.server.config.ConfigException;
048 import org.opends.server.core.DirectoryServer;
049 import org.opends.server.types.ConfigChangeResult;
050 import org.opends.server.types.DirectoryException;
051 import org.opends.server.types.DN;
052 import org.opends.server.types.InitializationException;
053 import org.opends.server.types.ResultCode;
054
055 import static org.opends.server.loggers.debug.DebugLogger.*;
056 import org.opends.server.loggers.debug.DebugTracer;
057 import org.opends.server.types.DebugLogLevel;
058 import static org.opends.messages.ExtensionMessages.*;
059
060 import static org.opends.server.util.StaticUtils.*;
061
062
063
064 /**
065 * This class defines a key manager provider that will access keys stored in a
066 * file located on the Directory Server filesystem.
067 */
068 public class FileBasedKeyManagerProvider
069 extends KeyManagerProvider<FileBasedKeyManagerProviderCfg>
070 implements ConfigurationChangeListener<FileBasedKeyManagerProviderCfg>
071 {
072 /**
073 * The tracer object for the debug logger.
074 */
075 private static final DebugTracer TRACER = getTracer();
076
077
078
079 // The DN of the configuration entry for this key manager provider.
080 private DN configEntryDN;
081
082 // The PIN needed to access the keystore.
083 private char[] keyStorePIN;
084
085 // The configuration for this key manager provider.
086 private FileBasedKeyManagerProviderCfg currentConfig;
087
088 // The path to the key store backing file.
089 private String keyStoreFile;
090
091 // The key store type to use.
092 private String keyStoreType;
093
094
095
096 /**
097 * Creates a new instance of this file-based 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 FileBasedKeyManagerProvider()
102 {
103 // No implementation is required.
104 }
105
106
107
108 /**
109 * {@inheritDoc}
110 */
111 @Override
112 public void initializeKeyManagerProvider(
113 FileBasedKeyManagerProviderCfg configuration)
114 throws ConfigException, InitializationException {
115 // Store the DN of the configuration entry and register as a change
116 // listener.
117 currentConfig = configuration;
118 configEntryDN = configuration.dn();
119 configuration.addFileBasedChangeListener(this);
120
121
122 // Get the path to the key store file.
123 keyStoreFile = configuration.getKeyStoreFile();
124 try {
125 File f = getFileForPath(keyStoreFile);
126 if (!(f.exists() && f.isFile())) {
127 Message message = ERR_FILE_KEYMANAGER_NO_SUCH_FILE.get(
128 String.valueOf(keyStoreFile), String.valueOf(configEntryDN));
129 throw new InitializationException(message);
130 }
131 } catch (SecurityException e) {
132 if (debugEnabled())
133 {
134 TRACER.debugCaught(DebugLogLevel.ERROR, e);
135 }
136
137 Message message = ERR_FILE_KEYMANAGER_CANNOT_DETERMINE_FILE.get(
138 String.valueOf(configEntryDN), getExceptionMessage(e));
139 throw new InitializationException(message, e);
140 }
141
142 // Get the keystore type. If none is specified, then use the
143 // default type.
144 if (configuration.getKeyStoreType() != null) {
145 try {
146 KeyStore.getInstance(configuration.getKeyStoreType());
147 keyStoreType = configuration.getKeyStoreType();
148 } catch (KeyStoreException kse) {
149 if (debugEnabled())
150 {
151 TRACER.debugCaught(DebugLogLevel.ERROR, kse);
152 }
153
154 Message message = ERR_FILE_KEYMANAGER_INVALID_TYPE.
155 get(String.valueOf(configuration.getKeyStoreType()),
156 String.valueOf(configEntryDN), getExceptionMessage(kse));
157 throw new InitializationException(message);
158 }
159 } else {
160 keyStoreType = KeyStore.getDefaultType();
161 }
162
163 // Get the PIN needed to access the contents of the keystore file.
164 //
165 // We will offer several places to look for the PIN, and we will
166 // do so in the following order:
167 //
168 // - In a specified Java property
169 // - In a specified environment variable
170 // - In a specified file on the server filesystem.
171 // - As the value of a configuration attribute.
172 //
173 // In any case, the PIN must be in the clear.
174 keyStorePIN = null;
175
176 if (configuration.getKeyStorePinProperty() != null) {
177 String propertyName = configuration.getKeyStorePinProperty();
178 String pinStr = System.getProperty(propertyName);
179
180 if (pinStr == null) {
181 Message message = ERR_FILE_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
182 String.valueOf(propertyName), String.valueOf(configEntryDN));
183 throw new InitializationException(message);
184 }
185
186 keyStorePIN = pinStr.toCharArray();
187 } else if (configuration.getKeyStorePinEnvironmentVariable() != null) {
188 String enVarName = configuration
189 .getKeyStorePinEnvironmentVariable();
190 String pinStr = System.getenv(enVarName);
191
192 if (pinStr == null) {
193 Message message = ERR_FILE_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
194 String.valueOf(enVarName), String.valueOf(configEntryDN));
195 throw new InitializationException(message);
196 }
197
198 keyStorePIN = pinStr.toCharArray();
199 } else if (configuration.getKeyStorePinFile() != null) {
200 String fileName = configuration.getKeyStorePinFile();
201 File pinFile = getFileForPath(fileName);
202
203 if (!pinFile.exists()) {
204 Message message = ERR_FILE_KEYMANAGER_PIN_NO_SUCH_FILE.get(
205 String.valueOf(fileName), String.valueOf(configEntryDN));
206 throw new InitializationException(message);
207 }
208
209 String pinStr;
210 try {
211 BufferedReader br = new BufferedReader(
212 new FileReader(pinFile));
213 pinStr = br.readLine();
214 br.close();
215 } catch (IOException ioe) {
216 Message message = ERR_FILE_KEYMANAGER_PIN_FILE_CANNOT_READ.
217 get(String.valueOf(fileName), String.valueOf(configEntryDN),
218 getExceptionMessage(ioe));
219 throw new InitializationException(message, ioe);
220 }
221
222 if (pinStr == null) {
223 Message message = ERR_FILE_KEYMANAGER_PIN_FILE_EMPTY.get(
224 String.valueOf(fileName), String.valueOf(configEntryDN));
225 throw new InitializationException(message);
226 }
227
228 keyStorePIN = pinStr.toCharArray();
229 } else if (configuration.getKeyStorePin() != null) {
230 keyStorePIN = configuration.getKeyStorePin().toCharArray();
231 } else {
232 // Pin wasn't defined anywhere.
233 Message message =
234 ERR_FILE_KEYMANAGER_NO_PIN.get(String.valueOf(configEntryDN));
235 throw new ConfigException(message);
236 }
237 }
238
239
240
241 /**
242 * Performs any finalization that may be necessary for this key
243 * manager provider.
244 */
245 public void finalizeKeyManagerProvider()
246 {
247 currentConfig.removeFileBasedChangeListener(this);
248 }
249
250
251
252 /**
253 * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
254 * interactions requiring access to a key manager.
255 *
256 * @return A set of <CODE>KeyManager</CODE> objects that may be used for
257 * interactions requiring access to a key manager.
258 *
259 * @throws DirectoryException If a problem occurs while attempting to obtain
260 * the set of key managers.
261 */
262 public KeyManager[] getKeyManagers()
263 throws DirectoryException
264 {
265 KeyStore keyStore;
266 try
267 {
268 keyStore = KeyStore.getInstance(keyStoreType);
269
270 FileInputStream inputStream =
271 new FileInputStream(getFileForPath(keyStoreFile));
272 keyStore.load(inputStream, keyStorePIN);
273 inputStream.close();
274 }
275 catch (Exception e)
276 {
277 if (debugEnabled())
278 {
279 TRACER.debugCaught(DebugLogLevel.ERROR, e);
280 }
281
282 Message message = ERR_FILE_KEYMANAGER_CANNOT_LOAD.get(
283 keyStoreFile, getExceptionMessage(e));
284 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
285 message, e);
286 }
287
288
289 try
290 {
291 String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
292 KeyManagerFactory keyManagerFactory =
293 KeyManagerFactory.getInstance(keyManagerAlgorithm);
294 keyManagerFactory.init(keyStore, keyStorePIN);
295 return keyManagerFactory.getKeyManagers();
296 }
297 catch (Exception e)
298 {
299 if (debugEnabled())
300 {
301 TRACER.debugCaught(DebugLogLevel.ERROR, e);
302 }
303
304 Message message = ERR_FILE_KEYMANAGER_CANNOT_CREATE_FACTORY.get(
305 keyStoreFile, getExceptionMessage(e));
306 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
307 message, e);
308 }
309 }
310
311
312
313 /**
314 * {@inheritDoc}
315 */
316 @Override()
317 public boolean isConfigurationAcceptable(
318 FileBasedKeyManagerProviderCfg configuration,
319 List<Message> unacceptableReasons)
320 {
321 return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
322 }
323
324
325
326 /**
327 * {@inheritDoc}
328 */
329 public boolean isConfigurationChangeAcceptable(
330 FileBasedKeyManagerProviderCfg configuration,
331 List<Message> unacceptableReasons)
332 {
333 boolean configAcceptable = true;
334 DN cfgEntryDN = configuration.dn();
335
336
337 // Get the path to the key store file.
338 String newKeyStoreFile = configuration.getKeyStoreFile();
339 try
340 {
341 File f = getFileForPath(newKeyStoreFile);
342 if (!(f.exists() && f.isFile()))
343 {
344 unacceptableReasons.add(ERR_FILE_KEYMANAGER_NO_SUCH_FILE.get(
345 String.valueOf(newKeyStoreFile),
346 String.valueOf(cfgEntryDN)));
347 configAcceptable = false;
348 }
349 }
350 catch (Exception e)
351 {
352 if (debugEnabled())
353 {
354 TRACER.debugCaught(DebugLogLevel.ERROR, e);
355 }
356
357 unacceptableReasons.add(ERR_FILE_KEYMANAGER_CANNOT_DETERMINE_FILE.get(
358 String.valueOf(cfgEntryDN),
359 getExceptionMessage(e)));
360 configAcceptable = false;
361 }
362
363 // Get the keystore type. If none is specified, then use the default type.
364 if (configuration.getKeyStoreType() != null)
365 {
366 try
367 {
368 KeyStore.getInstance(configuration.getKeyStoreType());
369 }
370 catch (KeyStoreException kse)
371 {
372 if (debugEnabled())
373 {
374 TRACER.debugCaught(DebugLogLevel.ERROR, kse);
375 }
376
377 unacceptableReasons.add(ERR_FILE_KEYMANAGER_INVALID_TYPE.get(
378 String.valueOf(configuration.getKeyStoreType()),
379 String.valueOf(cfgEntryDN), getExceptionMessage(kse)));
380 configAcceptable = false;
381 }
382 }
383
384 // Get the PIN needed to access the contents of the keystore file.
385 //
386 // We will offer several places to look for the PIN, and we will
387 // do so in the following order:
388 //
389 // - In a specified Java property
390 // - In a specified environment variable
391 // - In a specified file on the server filesystem.
392 // - As the value of a configuration attribute.
393 //
394 // In any case, the PIN must be in the clear.
395 if (configuration.getKeyStorePinProperty() != null)
396 {
397 String propertyName = configuration.getKeyStorePinProperty();
398 String pinStr = System.getProperty(propertyName);
399
400 if (pinStr == null)
401 {
402 unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
403 String.valueOf(propertyName),
404 String.valueOf(cfgEntryDN)));
405 configAcceptable = false;
406 }
407 }
408 else if (configuration.getKeyStorePinEnvironmentVariable() != null)
409 {
410 String enVarName = configuration.getKeyStorePinEnvironmentVariable();
411 String pinStr = System.getenv(enVarName);
412
413 if (pinStr == null)
414 {
415 unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
416 String.valueOf(enVarName),
417 String.valueOf(cfgEntryDN)));
418 configAcceptable = false;
419 }
420 }
421 else if (configuration.getKeyStorePinFile() != null)
422 {
423 String fileName = configuration.getKeyStorePinFile();
424 File pinFile = getFileForPath(fileName);
425
426 if (!pinFile.exists())
427 {
428 unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_NO_SUCH_FILE.get(
429 String.valueOf(fileName),
430 String.valueOf(cfgEntryDN)));
431 configAcceptable = false;
432 }
433 else
434 {
435 String pinStr = null;
436 BufferedReader br = null;
437 try {
438 br = new BufferedReader(new FileReader(pinFile));
439 pinStr = br.readLine();
440 }
441 catch (IOException ioe)
442 {
443 unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
444 String.valueOf(fileName),
445 String.valueOf(cfgEntryDN),
446 getExceptionMessage(ioe)));
447 configAcceptable = false;
448 }
449 finally
450 {
451 try
452 {
453 br.close();
454 } catch (Exception e) {}
455 }
456
457 if (pinStr == null)
458 {
459 unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_FILE_EMPTY.get(
460 String.valueOf(fileName),
461 String.valueOf(cfgEntryDN)));
462 configAcceptable = false;
463 }
464 }
465 }
466 else if (configuration.getKeyStorePin() != null)
467 {
468 configuration.getKeyStorePin().toCharArray();
469 }
470 else
471 {
472 // Pin wasn't defined anywhere.
473 unacceptableReasons.add(ERR_FILE_KEYMANAGER_NO_PIN.get(
474 String.valueOf(cfgEntryDN)));
475 configAcceptable = false;
476 }
477
478 return configAcceptable;
479 }
480
481
482
483 /**
484 * {@inheritDoc}
485 */
486 public ConfigChangeResult applyConfigurationChange(
487 FileBasedKeyManagerProviderCfg configuration)
488 {
489 ResultCode resultCode = ResultCode.SUCCESS;
490 boolean adminActionRequired = false;
491 ArrayList<Message> messages = new ArrayList<Message>();
492
493
494 // Get the path to the key store file.
495 String newKeyStoreFile = configuration.getKeyStoreFile();
496 try
497 {
498 File f = getFileForPath(newKeyStoreFile);
499 if (!(f.exists() && f.isFile()))
500 {
501 resultCode = DirectoryServer.getServerErrorResultCode();
502
503 messages.add(ERR_FILE_KEYMANAGER_NO_SUCH_FILE.get(
504 String.valueOf(newKeyStoreFile),
505 String.valueOf(configEntryDN)));
506 }
507 }
508 catch (Exception e)
509 {
510 if (debugEnabled())
511 {
512 TRACER.debugCaught(DebugLogLevel.ERROR, e);
513 }
514
515 resultCode = DirectoryServer.getServerErrorResultCode();
516
517 messages.add(ERR_FILE_KEYMANAGER_CANNOT_DETERMINE_FILE.get(
518 String.valueOf(configEntryDN),
519 getExceptionMessage(e)));
520 }
521
522 // Get the keystore type. If none is specified, then use the default type.
523 String newKeyStoreType = KeyStore.getDefaultType();
524 if (configuration.getKeyStoreType() != null)
525 {
526 try
527 {
528 KeyStore.getInstance(configuration.getKeyStoreType());
529 newKeyStoreType = configuration.getKeyStoreType();
530 }
531 catch (KeyStoreException kse)
532 {
533 if (debugEnabled())
534 {
535 TRACER.debugCaught(DebugLogLevel.ERROR, kse);
536 }
537
538 resultCode = DirectoryServer.getServerErrorResultCode();
539
540 messages.add(ERR_FILE_KEYMANAGER_INVALID_TYPE.get(
541 String.valueOf(configuration.getKeyStoreType()),
542 String.valueOf(configEntryDN),
543 getExceptionMessage(kse)));
544 }
545 }
546
547 // Get the PIN needed to access the contents of the keystore file.
548 //
549 // We will offer several places to look for the PIN, and we will
550 // do so in the following order:
551 //
552 // - In a specified Java property
553 // - In a specified environment variable
554 // - In a specified file on the server filesystem.
555 // - As the value of a configuration attribute.
556 //
557 // In any case, the PIN must be in the clear.
558 char[] newPIN = null;
559
560 if (configuration.getKeyStorePinProperty() != null)
561 {
562 String propertyName = configuration.getKeyStorePinProperty();
563 String pinStr = System.getProperty(propertyName);
564
565 if (pinStr == null)
566 {
567 resultCode = DirectoryServer.getServerErrorResultCode();
568
569 messages.add(ERR_FILE_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
570 String.valueOf(propertyName),
571 String.valueOf(configEntryDN)));
572 }
573 else
574 {
575 newPIN = pinStr.toCharArray();
576 }
577 }
578 else if (configuration.getKeyStorePinEnvironmentVariable() != null)
579 {
580 String enVarName = configuration.getKeyStorePinEnvironmentVariable();
581 String pinStr = System.getenv(enVarName);
582
583 if (pinStr == null)
584 {
585 resultCode = DirectoryServer.getServerErrorResultCode();
586
587 messages.add(ERR_FILE_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
588 String.valueOf(enVarName),
589 String.valueOf(configEntryDN)));
590 }
591 else
592 {
593 newPIN = pinStr.toCharArray();
594 }
595 }
596 else if (configuration.getKeyStorePinFile() != null)
597 {
598 String fileName = configuration.getKeyStorePinFile();
599 File pinFile = getFileForPath(fileName);
600
601 if (!pinFile.exists())
602 {
603 resultCode = DirectoryServer.getServerErrorResultCode();
604
605 messages.add(ERR_FILE_KEYMANAGER_PIN_NO_SUCH_FILE.get(
606 String.valueOf(fileName),
607 String.valueOf(configEntryDN)));
608 }
609 else
610 {
611 String pinStr = null;
612 BufferedReader br = null;
613 try {
614 br = new BufferedReader(new FileReader(pinFile));
615 pinStr = br.readLine();
616 }
617 catch (IOException ioe)
618 {
619 resultCode = DirectoryServer.getServerErrorResultCode();
620
621 messages.add(ERR_FILE_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
622 String.valueOf(fileName),
623 String.valueOf(configEntryDN),
624 getExceptionMessage(ioe)));
625 }
626 finally
627 {
628 try
629 {
630 br.close();
631 } catch (Exception e) {}
632 }
633
634 if (pinStr == null)
635 {
636 resultCode = DirectoryServer.getServerErrorResultCode();
637
638 messages.add(ERR_FILE_KEYMANAGER_PIN_FILE_EMPTY.get(
639 String.valueOf(fileName),
640 String.valueOf(configEntryDN)));
641 }
642 else
643 {
644 newPIN = pinStr.toCharArray();
645 }
646 }
647 }
648 else if (configuration.getKeyStorePin() != null)
649 {
650 newPIN = configuration.getKeyStorePin().toCharArray();
651 }
652 else
653 {
654 // Pin wasn't defined anywhere.
655 resultCode = DirectoryServer.getServerErrorResultCode();
656
657 messages.add(ERR_FILE_KEYMANAGER_NO_PIN.get(
658 String.valueOf(configEntryDN)));
659 }
660
661
662 if (resultCode == ResultCode.SUCCESS)
663 {
664 currentConfig = configuration;
665 keyStorePIN = newPIN;
666 keyStoreFile = newKeyStoreFile;
667 keyStoreType = newKeyStoreType;
668 }
669
670
671 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
672 }
673 }
674