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.io.BufferedReader; 033 import java.io.File; 034 import java.io.FileReader; 035 import java.io.IOException; 036 import java.security.KeyStore; 037 import java.util.ArrayList; 038 import java.util.List; 039 import javax.net.ssl.KeyManager; 040 import javax.net.ssl.KeyManagerFactory; 041 042 import org.opends.server.admin.server.ConfigurationChangeListener; 043 import org.opends.server.admin.std.server.PKCS11KeyManagerProviderCfg; 044 import org.opends.server.api.KeyManagerProvider; 045 import org.opends.server.config.ConfigException; 046 import org.opends.server.core.DirectoryServer; 047 import org.opends.server.types.ConfigChangeResult; 048 import org.opends.server.types.DirectoryException; 049 import org.opends.server.types.DN; 050 import org.opends.server.types.InitializationException; 051 import org.opends.server.types.ResultCode; 052 053 import static org.opends.server.loggers.debug.DebugLogger.*; 054 import org.opends.server.loggers.debug.DebugTracer; 055 import org.opends.server.types.DebugLogLevel; 056 import static org.opends.messages.ExtensionMessages.*; 057 058 import static org.opends.server.util.StaticUtils.*; 059 060 061 062 /** 063 * This class defines a key manager provider that will access keys stored on a 064 * PKCS#11 device. It will use the Java PKCS#11 interface, which may need to be 065 * configured on the underlying system. 066 */ 067 public class PKCS11KeyManagerProvider 068 extends KeyManagerProvider<PKCS11KeyManagerProviderCfg> 069 implements ConfigurationChangeListener<PKCS11KeyManagerProviderCfg> 070 { 071 /** 072 * The tracer object for the debug logger. 073 */ 074 private static final DebugTracer TRACER = getTracer(); 075 076 077 078 /** 079 * The keystore type to use when accessing the PKCS#11 keystore. 080 */ 081 public static final String PKCS11_KEYSTORE_TYPE = "PKCS11"; 082 083 084 085 // The DN of the configuration entry for this key manager provider. 086 private DN configEntryDN; 087 088 // The PIN needed to access the keystore. 089 private char[] keyStorePIN; 090 091 // The current configuration for this key manager provider. 092 private PKCS11KeyManagerProviderCfg currentConfig; 093 094 095 096 /** 097 * Creates a new instance of this PKCS#11 key manager provider. The 098 * <CODE>initializeKeyManagerProvider</CODE> method must be called on the 099 * resulting object before it may be used. 100 */ 101 public PKCS11KeyManagerProvider() 102 { 103 // No implementation is required. 104 } 105 106 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override 112 public void initializeKeyManagerProvider( 113 PKCS11KeyManagerProviderCfg configuration) 114 throws ConfigException, InitializationException 115 { 116 // Store the DN of the configuration entry and register to be notified of 117 // configuration changes. 118 currentConfig = configuration; 119 configEntryDN = configuration.dn(); 120 configuration.addPKCS11ChangeListener(this); 121 122 // Get the PIN needed to access the contents of the PKCS#11 123 // keystore. We will offer several places to look for the PIN, and 124 // we will do so in the following order: 125 // 126 // - In a specified Java property 127 // - In a specified environment variable 128 // - In a specified file on the server filesystem. 129 // - As the value of a configuration attribute. 130 // 131 // In any case, the PIN must be in the clear. 132 keyStorePIN = null; 133 134 if (configuration.getKeyStorePinProperty() != null) { 135 String propertyName = configuration.getKeyStorePinProperty(); 136 String pinStr = System.getProperty(propertyName); 137 138 if (pinStr == null) { 139 Message message = ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get( 140 String.valueOf(propertyName), String.valueOf(configEntryDN)); 141 throw new InitializationException(message); 142 } 143 144 keyStorePIN = pinStr.toCharArray(); 145 } else if (configuration.getKeyStorePinEnvironmentVariable() != null) { 146 String enVarName = configuration 147 .getKeyStorePinEnvironmentVariable(); 148 String pinStr = System.getenv(enVarName); 149 150 if (pinStr == null) { 151 Message message = ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get( 152 String.valueOf(enVarName), String.valueOf(configEntryDN)); 153 throw new InitializationException(message); 154 } 155 156 keyStorePIN = pinStr.toCharArray(); 157 } else if (configuration.getKeyStorePinFile() != null) { 158 String fileName = configuration.getKeyStorePinFile(); 159 File pinFile = getFileForPath(fileName); 160 161 if (!pinFile.exists()) { 162 Message message = ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get( 163 String.valueOf(fileName), String.valueOf(configEntryDN)); 164 throw new InitializationException(message); 165 } 166 167 String pinStr; 168 try { 169 BufferedReader br = new BufferedReader( 170 new FileReader(pinFile)); 171 pinStr = br.readLine(); 172 br.close(); 173 } catch (IOException ioe) { 174 if (debugEnabled()) 175 { 176 TRACER.debugCaught(DebugLogLevel.ERROR, ioe); 177 } 178 179 Message message = ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ. 180 get(String.valueOf(fileName), String.valueOf(configEntryDN), 181 getExceptionMessage(ioe)); 182 throw new InitializationException(message, ioe); 183 } 184 185 if (pinStr == null) { 186 Message message = ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get( 187 String.valueOf(fileName), String.valueOf(configEntryDN)); 188 throw new InitializationException(message); 189 } 190 191 keyStorePIN = pinStr.toCharArray(); 192 } else if (configuration.getKeyStorePin() != null) { 193 keyStorePIN = configuration.getKeyStorePin().toCharArray(); 194 } else { 195 // Pin wasn't defined anywhere. 196 Message message = 197 ERR_PKCS11_KEYMANAGER_NO_PIN.get(String.valueOf(configEntryDN)); 198 throw new ConfigException(message); 199 } 200 } 201 202 203 204 /** 205 * Performs any finalization that may be necessary for this key 206 * manager provider. 207 */ 208 public void finalizeKeyManagerProvider() 209 { 210 currentConfig.removePKCS11ChangeListener(this); 211 } 212 213 214 215 /** 216 * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for 217 * interactions requiring access to a key manager. 218 * 219 * @return A set of <CODE>KeyManager</CODE> objects that may be used for 220 * interactions requiring access to a key manager. 221 * 222 * @throws DirectoryException If a problem occurs while attempting to obtain 223 * the set of key managers. 224 */ 225 public KeyManager[] getKeyManagers() 226 throws DirectoryException 227 { 228 KeyStore keyStore; 229 try 230 { 231 keyStore = KeyStore.getInstance(PKCS11_KEYSTORE_TYPE); 232 keyStore.load(null, keyStorePIN); 233 } 234 catch (Exception e) 235 { 236 if (debugEnabled()) 237 { 238 TRACER.debugCaught(DebugLogLevel.ERROR, e); 239 } 240 241 Message message = 242 ERR_PKCS11_KEYMANAGER_CANNOT_LOAD.get(getExceptionMessage(e)); 243 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 244 message, e); 245 } 246 247 248 try 249 { 250 String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); 251 KeyManagerFactory keyManagerFactory = 252 KeyManagerFactory.getInstance(keyManagerAlgorithm); 253 keyManagerFactory.init(keyStore, keyStorePIN); 254 return keyManagerFactory.getKeyManagers(); 255 } 256 catch (Exception e) 257 { 258 if (debugEnabled()) 259 { 260 TRACER.debugCaught(DebugLogLevel.ERROR, e); 261 } 262 263 Message message = ERR_PKCS11_KEYMANAGER_CANNOT_CREATE_FACTORY.get( 264 getExceptionMessage(e)); 265 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 266 message, e); 267 } 268 } 269 270 271 272 /** 273 * {@inheritDoc} 274 */ 275 @Override() 276 public boolean isConfigurationAcceptable( 277 PKCS11KeyManagerProviderCfg configuration, 278 List<Message> unacceptableReasons) 279 { 280 return isConfigurationChangeAcceptable(configuration, unacceptableReasons); 281 } 282 283 284 285 /** 286 * {@inheritDoc} 287 */ 288 public boolean isConfigurationChangeAcceptable( 289 PKCS11KeyManagerProviderCfg configuration, 290 List<Message> unacceptableReasons) 291 { 292 boolean configAcceptable = true; 293 DN cfgEntryDN = configuration.dn(); 294 295 296 // Get the PIN needed to access the contents of the keystore file. 297 // 298 // We will offer several places to look for the PIN, and we will 299 // do so in the following order: 300 // 301 // - In a specified Java property 302 // - In a specified environment variable 303 // - In a specified file on the server filesystem. 304 // - As the value of a configuration attribute. 305 // 306 // In any case, the PIN must be in the clear. 307 if (configuration.getKeyStorePinProperty() != null) 308 { 309 String propertyName = configuration.getKeyStorePinProperty(); 310 String pinStr = System.getProperty(propertyName); 311 312 if (pinStr == null) 313 { 314 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get( 315 String.valueOf(propertyName), 316 String.valueOf(cfgEntryDN))); 317 configAcceptable = false; 318 } 319 } 320 else if (configuration.getKeyStorePinEnvironmentVariable() != null) 321 { 322 String enVarName = configuration.getKeyStorePinEnvironmentVariable(); 323 String pinStr = System.getenv(enVarName); 324 325 if (pinStr == null) 326 { 327 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get( 328 String.valueOf(enVarName), 329 String.valueOf(cfgEntryDN))); 330 configAcceptable = false; 331 } 332 } 333 else if (configuration.getKeyStorePinFile() != null) 334 { 335 String fileName = configuration.getKeyStorePinFile(); 336 File pinFile = getFileForPath(fileName); 337 338 if (!pinFile.exists()) 339 { 340 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get( 341 String.valueOf(fileName), 342 String.valueOf(cfgEntryDN))); 343 configAcceptable = false; 344 } 345 else 346 { 347 String pinStr = null; 348 BufferedReader br = null; 349 try { 350 br = new BufferedReader(new FileReader(pinFile)); 351 pinStr = br.readLine(); 352 } 353 catch (IOException ioe) 354 { 355 unacceptableReasons.add( 356 ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get( 357 String.valueOf(fileName), 358 String.valueOf(cfgEntryDN), 359 getExceptionMessage(ioe))); 360 configAcceptable = false; 361 } 362 finally 363 { 364 try 365 { 366 br.close(); 367 } catch (Exception e) {} 368 } 369 370 if (pinStr == null) 371 { 372 373 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get( 374 String.valueOf(fileName), 375 String.valueOf(cfgEntryDN))); 376 configAcceptable = false; 377 } 378 } 379 } 380 else if (configuration.getKeyStorePin() != null) 381 { 382 configuration.getKeyStorePin().toCharArray(); 383 } 384 else 385 { 386 // Pin wasn't defined anywhere. 387 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_NO_PIN.get( 388 String.valueOf(cfgEntryDN))); 389 configAcceptable = false; 390 } 391 392 return configAcceptable; 393 } 394 395 396 397 /** 398 * {@inheritDoc} 399 */ 400 public ConfigChangeResult applyConfigurationChange( 401 PKCS11KeyManagerProviderCfg configuration) 402 { 403 ResultCode resultCode = ResultCode.SUCCESS; 404 boolean adminActionRequired = false; 405 ArrayList<Message> messages = new ArrayList<Message>(); 406 407 408 // Get the PIN needed to access the contents of the keystore file. 409 // 410 // We will offer several places to look for the PIN, and we will 411 // do so in the following order: 412 // 413 // - In a specified Java property 414 // - In a specified environment variable 415 // - In a specified file on the server filesystem. 416 // - As the value of a configuration attribute. 417 // 418 // In any case, the PIN must be in the clear. 419 char[] newPIN = null; 420 421 if (configuration.getKeyStorePinProperty() != null) 422 { 423 String propertyName = configuration.getKeyStorePinProperty(); 424 String pinStr = System.getProperty(propertyName); 425 426 if (pinStr == null) 427 { 428 resultCode = DirectoryServer.getServerErrorResultCode(); 429 430 messages.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get( 431 String.valueOf(propertyName), 432 String.valueOf(configEntryDN))); 433 } 434 else 435 { 436 newPIN = pinStr.toCharArray(); 437 } 438 } 439 else if (configuration.getKeyStorePinEnvironmentVariable() != null) 440 { 441 String enVarName = configuration.getKeyStorePinEnvironmentVariable(); 442 String pinStr = System.getenv(enVarName); 443 444 if (pinStr == null) 445 { 446 resultCode = DirectoryServer.getServerErrorResultCode(); 447 448 messages.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get( 449 String.valueOf(enVarName), 450 String.valueOf(configEntryDN))); 451 } 452 else 453 { 454 newPIN = pinStr.toCharArray(); 455 } 456 } 457 else if (configuration.getKeyStorePinFile() != null) 458 { 459 String fileName = configuration.getKeyStorePinFile(); 460 File pinFile = getFileForPath(fileName); 461 462 if (!pinFile.exists()) 463 { 464 resultCode = DirectoryServer.getServerErrorResultCode(); 465 466 messages.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get( 467 String.valueOf(fileName), 468 String.valueOf(configEntryDN))); 469 } 470 else 471 { 472 String pinStr = null; 473 BufferedReader br = null; 474 try { 475 br = new BufferedReader(new FileReader(pinFile)); 476 pinStr = br.readLine(); 477 } 478 catch (IOException ioe) 479 { 480 resultCode = DirectoryServer.getServerErrorResultCode(); 481 482 messages.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get( 483 String.valueOf(fileName), 484 String.valueOf(configEntryDN), 485 getExceptionMessage(ioe))); 486 } 487 finally 488 { 489 try 490 { 491 br.close(); 492 } catch (Exception e) {} 493 } 494 495 if (pinStr == null) 496 { 497 resultCode = DirectoryServer.getServerErrorResultCode(); 498 499 messages.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get( 500 String.valueOf(fileName), 501 String.valueOf(configEntryDN))); 502 } 503 else 504 { 505 newPIN = pinStr.toCharArray(); 506 } 507 } 508 } 509 else if (configuration.getKeyStorePin() != null) 510 { 511 newPIN = configuration.getKeyStorePin().toCharArray(); 512 } 513 else 514 { 515 // Pin wasn't defined anywhere. 516 resultCode = DirectoryServer.getServerErrorResultCode(); 517 518 519 messages.add(ERR_PKCS11_KEYMANAGER_NO_PIN.get( 520 String.valueOf(configEntryDN))); 521 } 522 523 524 if (resultCode == ResultCode.SUCCESS) 525 { 526 currentConfig = configuration; 527 keyStorePIN = newPIN; 528 } 529 530 531 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 532 } 533 } 534