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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.extensions; 028 029import org.forgerock.i18n.LocalizableMessage; 030import java.io.BufferedReader; 031import java.io.File; 032import java.io.FileInputStream; 033import java.io.FileReader; 034import java.io.IOException; 035import java.security.*; 036import java.util.List; 037import javax.net.ssl.TrustManager; 038import javax.net.ssl.TrustManagerFactory; 039import javax.net.ssl.X509TrustManager; 040 041import org.opends.server.admin.server.ConfigurationChangeListener; 042import org.opends.server.admin.std.server.TrustManagerProviderCfg; 043import org.opends.server.admin.std.server.FileBasedTrustManagerProviderCfg; 044import org.opends.server.api.TrustManagerProvider; 045import org.forgerock.opendj.config.server.ConfigException; 046import org.opends.server.core.DirectoryServer; 047import org.forgerock.opendj.config.server.ConfigChangeResult; 048import org.opends.server.types.DirectoryException; 049import org.opends.server.types.DN; 050import org.opends.server.types.InitializationException; 051import org.forgerock.opendj.ldap.ResultCode; 052import org.opends.server.util.ExpirationCheckTrustManager; 053 054import org.forgerock.i18n.slf4j.LocalizedLogger; 055import static org.opends.messages.ExtensionMessages.*; 056import static org.opends.server.util.StaticUtils.*; 057 058/** 059 * This class defines a trust manager provider that will reference certificates 060 * stored in a file located on the Directory Server filesystem. 061 */ 062public class FileBasedTrustManagerProvider 063 extends TrustManagerProvider<FileBasedTrustManagerProviderCfg> 064 implements ConfigurationChangeListener<FileBasedTrustManagerProviderCfg> 065{ 066 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 067 068 069 070 071 /** The DN of the configuration entry for this trust manager provider. */ 072 private DN configEntryDN; 073 074 /** The PIN needed to access the trust store. */ 075 private char[] trustStorePIN; 076 077 /** The handle to the configuration for this trust manager. */ 078 private FileBasedTrustManagerProviderCfg currentConfig; 079 080 /** The path to the trust store backing file. */ 081 private String trustStoreFile; 082 083 /** The trust store type to use. */ 084 private String trustStoreType; 085 086 087 088 /** 089 * Creates a new instance of this file-based trust manager provider. The 090 * <CODE>initializeTrustManagerProvider</CODE> method must be called on the 091 * resulting object before it may be used. 092 */ 093 public FileBasedTrustManagerProvider() 094 { 095 // No implementation is required. 096 } 097 098 099 100 /** {@inheritDoc} */ 101 @Override 102 public void initializeTrustManagerProvider( 103 FileBasedTrustManagerProviderCfg configuration) 104 throws ConfigException, InitializationException 105 { 106 // Store the DN of the configuration entry and register to listen for any 107 // changes to the configuration entry. 108 currentConfig = configuration; 109 configEntryDN = configuration.dn(); 110 configuration.addFileBasedChangeListener(this); 111 112 113 // Get the path to the trust store file. 114 trustStoreFile = configuration.getTrustStoreFile(); 115 File f = getFileForPath(trustStoreFile); 116 if (!f.exists() || !f.isFile()) 117 { 118 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_NO_SUCH_FILE.get(trustStoreFile, configEntryDN); 119 throw new InitializationException(message); 120 } 121 122 123 // Get the trust store type. If none is specified, then use the default 124 // type. 125 trustStoreType = configuration.getTrustStoreType(); 126 if (trustStoreType == null) 127 { 128 trustStoreType = KeyStore.getDefaultType(); 129 } 130 131 try 132 { 133 KeyStore.getInstance(trustStoreType); 134 } 135 catch (KeyStoreException kse) 136 { 137 logger.traceException(kse); 138 139 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_INVALID_TYPE. 140 get(trustStoreType, configEntryDN, getExceptionMessage(kse)); 141 throw new InitializationException(message); 142 } 143 144 145 // Get the PIN needed to access the contents of the trust store file. We 146 // will offer several places to look for the PIN, and we will do so in the 147 // following order: 148 // - In a specified Java property 149 // - In a specified environment variable 150 // - In a specified file on the server filesystem. 151 // - As the value of a configuration attribute. 152 // In any case, the PIN must be in the clear. If no PIN is provided, then 153 // it will be assumed that none is required to access the information in the 154 // trust store. 155 String pinProperty = configuration.getTrustStorePinProperty(); 156 if (pinProperty == null) 157 { 158 String pinEnVar = configuration.getTrustStorePinEnvironmentVariable(); 159 if (pinEnVar == null) 160 { 161 String pinFilePath = configuration.getTrustStorePinFile(); 162 if (pinFilePath == null) 163 { 164 String pinStr = configuration.getTrustStorePin(); 165 if (pinStr == null) 166 { 167 trustStorePIN = null; 168 } 169 else 170 { 171 trustStorePIN = pinStr.toCharArray(); 172 } 173 } 174 else 175 { 176 File pinFile = getFileForPath(pinFilePath); 177 if (! pinFile.exists()) 178 { 179 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE.get(pinFilePath, configEntryDN); 180 throw new InitializationException(message); 181 } 182 else 183 { 184 String pinStr; 185 186 BufferedReader br = null; 187 try 188 { 189 br = new BufferedReader(new FileReader(pinFile)); 190 pinStr = br.readLine(); 191 } 192 catch (IOException ioe) 193 { 194 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ. 195 get(pinFilePath, configEntryDN, getExceptionMessage(ioe)); 196 throw new InitializationException(message, ioe); 197 } 198 finally 199 { 200 close(br); 201 } 202 203 if (pinStr == null) 204 { 205 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY.get(pinFilePath, configEntryDN); 206 throw new InitializationException(message); 207 } 208 else 209 { 210 trustStorePIN = pinStr.toCharArray(); 211 } 212 } 213 } 214 } 215 else 216 { 217 String pinStr = System.getenv(pinEnVar); 218 if (pinStr == null) 219 { 220 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET.get(pinProperty, configEntryDN); 221 throw new InitializationException(message); 222 } 223 else 224 { 225 trustStorePIN = pinStr.toCharArray(); 226 } 227 } 228 } 229 else 230 { 231 String pinStr = System.getProperty(pinProperty); 232 if (pinStr == null) 233 { 234 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET.get(pinProperty, configEntryDN); 235 throw new InitializationException(message); 236 } 237 else 238 { 239 trustStorePIN = pinStr.toCharArray(); 240 } 241 } 242 } 243 244 245 246 /** {@inheritDoc} */ 247 @Override 248 public void finalizeTrustManagerProvider() 249 { 250 currentConfig.removeFileBasedChangeListener(this); 251 } 252 253 254 255 /** {@inheritDoc} */ 256 @Override 257 public TrustManager[] getTrustManagers() 258 throws DirectoryException 259 { 260 KeyStore trustStore; 261 try 262 { 263 trustStore = KeyStore.getInstance(trustStoreType); 264 265 FileInputStream inputStream = 266 new FileInputStream(getFileForPath(trustStoreFile)); 267 trustStore.load(inputStream, trustStorePIN); 268 inputStream.close(); 269 } 270 catch (Exception e) 271 { 272 logger.traceException(e); 273 274 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_CANNOT_LOAD.get( 275 trustStoreFile, getExceptionMessage(e)); 276 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 277 message, e); 278 } 279 280 281 try 282 { 283 String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 284 TrustManagerFactory trustManagerFactory = 285 TrustManagerFactory.getInstance(trustManagerAlgorithm); 286 trustManagerFactory.init(trustStore); 287 TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); 288 TrustManager[] newTrustManagers = new TrustManager[trustManagers.length]; 289 for (int i=0; i < trustManagers.length; i++) 290 { 291 newTrustManagers[i] = new ExpirationCheckTrustManager( 292 (X509TrustManager) trustManagers[i]); 293 } 294 return newTrustManagers; 295 } 296 catch (Exception e) 297 { 298 logger.traceException(e); 299 300 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_CANNOT_CREATE_FACTORY.get( 301 trustStoreFile, getExceptionMessage(e)); 302 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 303 message, e); 304 } 305 } 306 307 308 309 /** {@inheritDoc} */ 310 @Override 311 public boolean isConfigurationAcceptable( 312 TrustManagerProviderCfg configuration, 313 List<LocalizableMessage> unacceptableReasons) 314 { 315 FileBasedTrustManagerProviderCfg config = 316 (FileBasedTrustManagerProviderCfg) configuration; 317 return isConfigurationChangeAcceptable(config, unacceptableReasons); 318 } 319 320 321 322 /** {@inheritDoc} */ 323 public boolean isConfigurationChangeAcceptable( 324 FileBasedTrustManagerProviderCfg configuration, 325 List<LocalizableMessage> unacceptableReasons) 326 { 327 boolean configAcceptable = true; 328 DN cfgEntryDN = configuration.dn(); 329 330 331 // Get the path to the trust store file. 332 String newTrustStoreFile = configuration.getTrustStoreFile(); 333 try 334 { 335 File f = getFileForPath(newTrustStoreFile); 336 if (!f.exists() || !f.isFile()) 337 { 338 unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_NO_SUCH_FILE.get(newTrustStoreFile, cfgEntryDN)); 339 configAcceptable = false; 340 } 341 } 342 catch (Exception e) 343 { 344 logger.traceException(e); 345 346 unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_CANNOT_DETERMINE_FILE.get(cfgEntryDN, getExceptionMessage(e))); 347 configAcceptable = false; 348 } 349 350 351 // Check to see if the trust store type is acceptable. 352 String storeType = configuration.getTrustStoreType(); 353 if (storeType != null) 354 { 355 try 356 { 357 KeyStore.getInstance(storeType); 358 } 359 catch (KeyStoreException kse) 360 { 361 logger.traceException(kse); 362 363 unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_INVALID_TYPE.get( 364 storeType, cfgEntryDN, getExceptionMessage(kse))); 365 configAcceptable = false; 366 } 367 } 368 369 370 // If there is a PIN property, then make sure the corresponding 371 // property is set. 372 String pinProp = configuration.getTrustStorePinProperty(); 373 if (pinProp != null && System.getProperty(pinProp) == null) 374 { 375 unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET.get(pinProp, cfgEntryDN)); 376 configAcceptable = false; 377 } 378 379 380 // If there is a PIN environment variable, then make sure the corresponding 381 // environment variable is set. 382 String pinEnVar = configuration.getTrustStorePinEnvironmentVariable(); 383 if (pinEnVar != null && System.getenv(pinEnVar) == null) 384 { 385 unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET.get(pinEnVar, cfgEntryDN)); 386 configAcceptable = false; 387 } 388 389 390 // If there is a PIN file, then make sure the file exists and is readable. 391 String pinFile = configuration.getTrustStorePinFile(); 392 if (pinFile != null) 393 { 394 File f = getFileForPath(pinFile); 395 if (f.exists()) 396 { 397 String pinStr = null; 398 399 BufferedReader br = null; 400 try 401 { 402 br = new BufferedReader(new FileReader(f)); 403 pinStr = br.readLine(); 404 } 405 catch (IOException ioe) 406 { 407 unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ.get( 408 pinFile, cfgEntryDN, getExceptionMessage(ioe))); 409 configAcceptable = false; 410 } 411 finally 412 { 413 close(br); 414 } 415 416 if (pinStr == null) 417 { 418 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY.get(pinFile, cfgEntryDN); 419 unacceptableReasons.add(message); 420 configAcceptable = false; 421 } 422 } 423 else 424 { 425 LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE.get(pinFile, cfgEntryDN); 426 unacceptableReasons.add(message); 427 configAcceptable = false; 428 } 429 } 430 431 432 return configAcceptable; 433 } 434 435 /** {@inheritDoc} */ 436 public ConfigChangeResult applyConfigurationChange( 437 FileBasedTrustManagerProviderCfg configuration) 438 { 439 final ConfigChangeResult ccr = new ConfigChangeResult(); 440 441 442 // Get the path to the trust store file. 443 String newTrustStoreFile = configuration.getTrustStoreFile(); 444 File f = getFileForPath(newTrustStoreFile); 445 if (!f.exists() || !f.isFile()) 446 { 447 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 448 ccr.addMessage(ERR_FILE_TRUSTMANAGER_NO_SUCH_FILE.get(newTrustStoreFile, configEntryDN)); 449 } 450 451 // Get the trust store type. If none is specified, then use the default type. 452 String newTrustStoreType = configuration.getTrustStoreType(); 453 if (newTrustStoreType == null) 454 { 455 newTrustStoreType = KeyStore.getDefaultType(); 456 } 457 458 try 459 { 460 KeyStore.getInstance(newTrustStoreType); 461 } 462 catch (KeyStoreException kse) 463 { 464 logger.traceException(kse); 465 466 ccr.addMessage(ERR_FILE_TRUSTMANAGER_INVALID_TYPE.get( 467 newTrustStoreType, configEntryDN, getExceptionMessage(kse))); 468 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 469 } 470 471 472 // Get the PIN needed to access the contents of the trust store file. We 473 // will offer several places to look for the PIN, and we will do so in the 474 // following order: 475 // - In a specified Java property 476 // - In a specified environment variable 477 // - In a specified file on the server filesystem. 478 // - As the value of a configuration attribute. 479 // In any case, the PIN must be in the clear. If no PIN is provided, then 480 // it will be assumed that none is required to access the information in the 481 // trust store. 482 char[] newPIN = null; 483 String newPINProperty = configuration.getTrustStorePinProperty(); 484 if (newPINProperty == null) 485 { 486 String newPINEnVar = configuration.getTrustStorePinEnvironmentVariable(); 487 if (newPINEnVar == null) 488 { 489 String newPINFile = configuration.getTrustStorePinFile(); 490 if (newPINFile == null) 491 { 492 String pinStr = configuration.getTrustStorePin(); 493 if (pinStr == null) 494 { 495 newPIN = null; 496 } 497 else 498 { 499 newPIN = pinStr.toCharArray(); 500 } 501 } 502 else 503 { 504 File pinFile = getFileForPath(newPINFile); 505 if (! pinFile.exists()) 506 { 507 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 508 ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE.get(newPINFile, configEntryDN)); 509 } 510 else 511 { 512 String pinStr = null; 513 514 BufferedReader br = null; 515 try 516 { 517 br = new BufferedReader(new FileReader(pinFile)); 518 pinStr = br.readLine(); 519 } 520 catch (IOException ioe) 521 { 522 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 523 ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ.get( 524 newPINFile, configEntryDN, getExceptionMessage(ioe))); 525 } 526 finally 527 { 528 close(br); 529 } 530 531 if (pinStr == null) 532 { 533 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 534 ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY.get(newPINFile, configEntryDN)); 535 } 536 else 537 { 538 newPIN = pinStr.toCharArray(); 539 } 540 } 541 } 542 } 543 else 544 { 545 String pinStr = System.getenv(newPINEnVar); 546 if (pinStr == null) 547 { 548 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 549 ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET.get(newPINEnVar, configEntryDN)); 550 } 551 else 552 { 553 newPIN = pinStr.toCharArray(); 554 } 555 } 556 } 557 else 558 { 559 String pinStr = System.getProperty(newPINProperty); 560 if (pinStr == null) 561 { 562 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 563 ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET.get(newPINProperty, configEntryDN)); 564 } 565 else 566 { 567 newPIN = pinStr.toCharArray(); 568 } 569 } 570 571 572 if (ccr.getResultCode() == ResultCode.SUCCESS) 573 { 574 trustStoreFile = newTrustStoreFile; 575 trustStoreType = newTrustStoreType; 576 trustStorePIN = newPIN; 577 currentConfig = configuration; 578 } 579 580 return ccr; 581 } 582}