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