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