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