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