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 * docs/licenses/cddl.txt 011 * or http://www.opensource.org/licenses/cddl1.php. 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 * docs/licenses/cddl.txt. 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 2010-2012 UnboundID Corp. 026 */ 027 package com.unboundid.directory.sdk.ds.api; 028 029 030 031 import java.util.Collections; 032 import java.util.List; 033 import java.util.Map; 034 035 import com.unboundid.asn1.ASN1OctetString; 036 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 037 import com.unboundid.directory.sdk.common.internal.Reconfigurable; 038 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 039 import com.unboundid.directory.sdk.ds.config.PasswordStorageSchemeConfig; 040 import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension; 041 import com.unboundid.directory.sdk.ds.types.DirectoryServerContext; 042 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 043 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 044 import com.unboundid.ldap.sdk.DN; 045 import com.unboundid.ldap.sdk.LDAPException; 046 import com.unboundid.ldap.sdk.ResultCode; 047 import com.unboundid.util.ByteString; 048 import com.unboundid.util.Extensible; 049 import com.unboundid.util.StaticUtils; 050 import com.unboundid.util.ThreadSafety; 051 import com.unboundid.util.ThreadSafetyLevel; 052 import com.unboundid.util.args.ArgumentException; 053 import com.unboundid.util.args.ArgumentParser; 054 055 056 057 /** 058 * This class defines an API that must be implemented by extensions which may be 059 * used to encode passwords for storage in the server. Ideally, encoded 060 * passwords should be stored in a secure manner so that anyone with access to 061 * the encoded password will not be able to determine the clear-text password 062 * with which it is associated (e.g., using a one-way message digest, or 063 * using reversible encryption with a securely-obtained key). Passwords are not 064 * required to be stored in a reversible form that allows the server to 065 * determine the clear-text password used to generate an encoded representation 066 * as long as it is possible to determine whether a given clear-text password 067 * may be used to generate a provided encoded representation. 068 * <BR><BR> 069 * Encoded passwords may taken one of two forms. The first is the "user 070 * password" syntax, in which the encoded password is represented by the name of 071 * the storage scheme in curly braces followed by the transformed password 072 * (e.g., "{scheme}encoded"). This format isn't based on any defined standard, 073 * but is commonly used by a number of directory server implementations. The 074 * second format is the authentication password syntax as described in RFC 3112, 075 * in which the encoded representation is broken into scheme, authInfo, and 076 * authValue segments separated by dollar signs (e.g., 077 * "scheme$authInfo$authValue"). All password storage schemes are required to 078 * support the "user password" syntax and may optionally also support the 079 * authentication password syntax. 080 * <BR> 081 * <H2>Configuring Password Storage Schemes</H2> 082 * In order to configure a password storage scheme created using this API, use 083 * a command like: 084 * <PRE> 085 * dsconfig create-password-storage-scheme \ 086 * --scheme-name "<I>{scheme-name}</I>" \ 087 * --type third-party \ 088 * --set enabled:true \ 089 * --set "extension-class:<I>{class-name}</I>" \ 090 * --set "extension-argument:<I>{name=value}</I>" 091 * </PRE> 092 * where "<I>{scheme-name}</I>" is the name to use for the password storage 093 * scheme instance, "<I>{class-name}</I>" is the fully-qualified name of the 094 * Java class that extends 095 * {@code com.unboundid.directory.sdk.ds.api.PasswordStorageScheme}, and 096 * "<I>{name=value}</I>" represents name-value pairs for any arguments to 097 * provide to the password storage scheme. If multiple arguments should be 098 * provided to the password storage scheme, then the 099 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 100 * provided multiple times. 101 */ 102 @Extensible() 103 @DirectoryServerExtension() 104 @DirectoryProxyServerExtension(appliesToLocalContent=true, 105 appliesToRemoteContent=false) 106 @SynchronizationServerExtension(appliesToLocalContent=true, 107 appliesToSynchronizedContent=false) 108 @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 109 public abstract class PasswordStorageScheme 110 implements UnboundIDExtension, 111 Reconfigurable<PasswordStorageSchemeConfig>, 112 ExampleUsageProvider 113 { 114 /** 115 * Creates a new instance of this password storage scheme. All password 116 * storage scheme implementations must include a default constructor, but any 117 * initialization should generally be done in the 118 * {@code initializePasswordStorageScheme} method. 119 */ 120 public PasswordStorageScheme() 121 { 122 // No implementation is required. 123 } 124 125 126 127 /** 128 * {@inheritDoc} 129 */ 130 public abstract String getExtensionName(); 131 132 133 134 /** 135 * {@inheritDoc} 136 */ 137 public abstract String[] getExtensionDescription(); 138 139 140 141 /** 142 * {@inheritDoc} 143 */ 144 public void defineConfigArguments(final ArgumentParser parser) 145 throws ArgumentException 146 { 147 // No arguments will be allowed by default. 148 } 149 150 151 152 /** 153 * Initializes this password storage scheme. 154 * 155 * @param serverContext A handle to the server context for the server in 156 * which this extension is running. 157 * @param config The general configuration for this password storage 158 * scheme. 159 * @param parser The argument parser which has been initialized from 160 * the configuration for this password storage scheme. 161 * 162 * @throws LDAPException If a problem occurs while initializing this 163 * password storage scheme. 164 */ 165 public void initializePasswordStorageScheme( 166 final DirectoryServerContext serverContext, 167 final PasswordStorageSchemeConfig config, 168 final ArgumentParser parser) 169 throws LDAPException 170 { 171 // No initialization will be performed by default. 172 } 173 174 175 176 /** 177 * {@inheritDoc} 178 */ 179 public boolean isConfigurationAcceptable( 180 final PasswordStorageSchemeConfig config, 181 final ArgumentParser parser, 182 final List<String> unacceptableReasons) 183 { 184 // No extended validation will be performed by default. 185 return true; 186 } 187 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 public ResultCode applyConfiguration(final PasswordStorageSchemeConfig config, 194 final ArgumentParser parser, 195 final List<String> adminActionsRequired, 196 final List<String> messages) 197 { 198 // By default, no configuration changes will be applied. If there are any 199 // arguments, then add an admin action message indicating that the extension 200 // needs to be restarted for any changes to take effect. 201 if (! parser.getNamedArguments().isEmpty()) 202 { 203 adminActionsRequired.add( 204 "No configuration change has actually been applied. The new " + 205 "configuration will not take effect until this password " + 206 "storage scheme is disabled and re-enabled or until the " + 207 "server is restarted."); 208 } 209 210 return ResultCode.SUCCESS; 211 } 212 213 214 215 /** 216 * Performs any cleanup which may be necessary when this password storage 217 * scheme is to be taken out of service. 218 */ 219 public void finalizePasswordStorageScheme() 220 { 221 // No implementation is required. 222 } 223 224 225 226 /** 227 * Retrieves the name for this password storage scheme. This will be the 228 * identifier which appears in curly braces at the beginning of the encoded 229 * password. The name should not include curly braces. 230 * 231 * @return The name for this password storage scheme. 232 */ 233 public abstract String getStorageSchemeName(); 234 235 236 237 /** 238 * Indicates whether this password storage scheme encodes passwords in a form 239 * that allows the original plaintext value to be obtained from the encoded 240 * representation. 241 * 242 * @return {@code true} if the original plaintext password may be obtained 243 * from the encoded password, or {@code false} if not. 244 */ 245 public abstract boolean isReversible(); 246 247 248 249 /** 250 * Indicates whether this password storage scheme encodes passwords in a form 251 * that may be considered secure. A storage scheme should only be considered 252 * secure if it is not possible to trivially determine a clear-text value 253 * which may be used to generate a given encoded representation. 254 * 255 * @return {@code true} if this password storage scheme may be considered 256 * secure, or {@code false} if not. 257 */ 258 public abstract boolean isSecure(); 259 260 261 262 /** 263 * Encodes the provided plaintext password. The encoded password should not 264 * include the scheme name in curly braces. 265 * 266 * @param plaintext The plaintext password to be encoded. It must not be 267 * {@code null}. 268 * 269 * @return The encoded representation of the provided password. 270 * 271 * @throws LDAPException If a problem occurs while attempting to encode the 272 * password. 273 */ 274 public abstract ByteString encodePassword(final ByteString plaintext) 275 throws LDAPException; 276 277 278 279 /** 280 * Encodes the provided plaintext password, prefixing the encoded 281 * representation with the name of the storage scheme in curly braces. 282 * 283 * @param plaintext The plaintext password to be encoded. It must not be 284 * {@code null}. 285 * 286 * @return The encoded representation of the provided password, prefixed with 287 * the storage scheme name. 288 * 289 * @throws LDAPException If a problem occurs while attempting to encode the 290 * password. 291 */ 292 public ByteString encodePasswordWithScheme(final ByteString plaintext) 293 throws LDAPException 294 { 295 final byte[] schemeBytes = StaticUtils.getBytes(getStorageSchemeName()); 296 final byte[] encodedPW = encodePassword(plaintext).getValue(); 297 298 final byte[] b = new byte[schemeBytes.length + encodedPW.length + 2]; 299 300 int pos = 0; 301 b[pos++] = '{'; 302 303 System.arraycopy(schemeBytes, 0, b, pos, schemeBytes.length); 304 pos += schemeBytes.length; 305 306 b[pos++] = '}'; 307 System.arraycopy(encodedPW, 0, b, pos, encodedPW.length); 308 309 return new ASN1OctetString(b); 310 } 311 312 313 314 /** 315 * Encodes the provided plaintext password, prefixing the encoded 316 * representation with the name of the storage scheme in curly braces. If an 317 * entry DN is provided, then a deterministic encoding should be used (e.g., 318 * a salt generated from the provided DN) so that the same encoding will 319 * always be used for same plaintext in the same entry. This will primarily 320 * be used when importing an LDIF file containing plaintext passwords so that 321 * the same LDIF file can be imported into multiple servers and the same 322 * encoded passwords will be generated in each server. 323 * 324 * @param plaintext The plaintext password to be encoded. It must not be 325 * {@code null}. 326 * @param entryDN The DN of the entry in which the encoded password will 327 * appear. This may be {@code null} if it is not known. 328 * 329 * @return The encoded representation of the provided password, prefixed with 330 * the storage scheme name. 331 * 332 * @throws LDAPException If a problem occurs while attempting to encode the 333 * password. 334 */ 335 public ByteString encodePasswordWithScheme(final ByteString plaintext, 336 final DN entryDN) 337 throws LDAPException 338 { 339 return encodePasswordWithScheme(plaintext); 340 } 341 342 343 344 /** 345 * Indicates whether the provided plaintext password could have been used to 346 * generate the given encoded password. 347 * 348 * @param plaintext The plaintext password for which to make the 349 * determination. 350 * @param encoded The encoded password for which to make the 351 * determination. It will not include the scheme name. 352 * 353 * @return {@code true} if the provided clear-text password could have been 354 * used to generate the encoded password, or {@code false} if not. 355 */ 356 public abstract boolean passwordMatches(final ByteString plaintext, 357 final ByteString encoded); 358 359 360 361 /** 362 * Attempts to determine the plaintext password used to generate the provided 363 * encoded password. This method should only be called if the 364 * {@link #isReversible} method returns {@code true}. 365 * 366 * @param encoded The encoded password for which to obtain the original 367 * plaintext password. It must not be {@code null} and will 368 * not be prefixed with the scheme name. 369 * 370 * @return The plaintext password obtained from the given encoded password. 371 * 372 * @throws LDAPException If this password storage scheme is not reversible, 373 * or if the provided value could not be decoded to 374 * its plaintext representation. 375 */ 376 public abstract ByteString getPlaintextValue(final ByteString encoded) 377 throws LDAPException; 378 379 380 381 /** 382 * Indicates whether this password storage scheme provides the ability to 383 * encode passwords in the authentication password syntax as described in RFC 384 * 3112. 385 * 386 * @return {@code true} if this password storage scheme supports the 387 * authentication password syntax, or {@code false} if not. 388 */ 389 public boolean supportsAuthPasswordSyntax() 390 { 391 return false; 392 } 393 394 395 396 /** 397 * Retrieves the name that should be used to identify this password storage 398 * scheme when encoding passwords using the authentication password syntax as 399 * described in RFC 3112. This should only be used if the 400 * {@link #supportsAuthPasswordSyntax} method returns {@code true}. 401 * 402 * @return The name that should be used to identify this password storage 403 * scheme when encoding passwords using the authentication password 404 * syntax. 405 */ 406 public String getAuthPasswordSchemeName() 407 { 408 return getStorageSchemeName(); 409 } 410 411 412 413 /** 414 * Encodes the provided plaintext password using the authentication password 415 * syntax as defined in RFC 3112. This should only be used if the 416 * {@link #supportsAuthPasswordSyntax} method returns {@code true}. 417 * 418 * @param plaintext The plaintext password to be encoded. 419 * 420 * @return The encoded representation of the provided password. 421 * 422 * @throws LDAPException If a problem occurs while encoding the provided 423 * password, or if this password storage scheme does 424 * not support the authentication password syntax. 425 */ 426 public ByteString encodeAuthPassword(final ByteString plaintext) 427 throws LDAPException 428 { 429 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 430 "This password storage scheme does not support the use of the " + 431 "authentication password syntax."); 432 } 433 434 435 436 /** 437 * Indicates whether the provided plaintext password may be used to generate 438 * an encoded password with the given authInfo and authValue elements when 439 * using the authentication password syntax as defined in RFC 3112. This 440 * should only be used if the {@link #supportsAuthPasswordSyntax} method 441 * returns {@code true}. 442 * 443 * @param plaintext The plaintext password for which to make the 444 * determination. 445 * @param authInfo The authInfo portion of the encoded password for which 446 * to make the determination. 447 * @param authValue The authValue portion of the encoded password for which 448 * to make the determination. 449 * 450 * @return {@code true} if the provided plaintext password could be used to 451 * generate an encoded password with the given authInfo and authValue 452 * portions, or {@code false} if not. 453 */ 454 public boolean authPasswordMatches(final ByteString plaintext, 455 final String authInfo, 456 final String authValue) 457 { 458 return false; 459 } 460 461 462 463 /** 464 * Obtains the plaintext password that was used to generate an encoded 465 * password with the given authInfo and authValue elements when using the 466 * authentication password syntax as described in RFC 3112. This should only 467 * be used if both the {@link #supportsAuthPasswordSyntax} and 468 * {@link #isReversible} methods return {@code true}. 469 * 470 * @param authInfo The authInfo portion of the encoded password for which 471 * to retrieve the corresponding plaintext value. 472 * @param authValue The authValue portion of the encoded password for which 473 * to retrieve the corresponding plaintext value. 474 * 475 * @return The plaintext password that was used to generate the encoded 476 * password. 477 * 478 * @throws LDAPException If this password storage scheme is not reversible, 479 * if this password storage scheme does not support 480 * the authentication password syntax, or if some 481 * other problem is encountered while attempting to 482 * determine the plaintext password. 483 */ 484 public ByteString getAuthPasswordPlaintextValue(final String authInfo, 485 final String authValue) 486 throws LDAPException 487 { 488 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 489 "This password storage scheme does not support the use of the " + 490 "authentication password syntax."); 491 } 492 493 494 495 /** 496 * {@inheritDoc} 497 */ 498 public Map<List<String>,String> getExamplesArgumentSets() 499 { 500 return Collections.emptyMap(); 501 } 502 }