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 2013 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.directory.sdk.common.internal.ExampleUsageProvider; 036 import com.unboundid.directory.sdk.common.internal.Reconfigurable; 037 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 038 import com.unboundid.directory.sdk.common.types.Entry; 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.LDAPException; 045 import com.unboundid.ldap.sdk.Modification; 046 import com.unboundid.ldap.sdk.ResultCode; 047 import com.unboundid.util.ByteString; 048 import com.unboundid.util.Extensible; 049 import com.unboundid.util.ThreadSafety; 050 import com.unboundid.util.ThreadSafetyLevel; 051 import com.unboundid.util.args.ArgumentException; 052 import com.unboundid.util.args.ArgumentParser; 053 054 055 056 /** 057 * This class defines an API that may be implemented by extensions which provide 058 * enhanced password storage scheme support. Although the older 059 * {@link PasswordStorageScheme} API is still supported, this enhanced version 060 * is both simpler and provides additional functionality, including providing 061 * access to the user entry (which may be useful, for example, if salt or 062 * encryption information is to be derived from other content in the entry, or 063 * from another repository that can be accessed based on content in the entry). 064 * However, this API is generally simpler than the {@code PasswordStorageScheme} 065 * API, so it may be desirable for all new implementations to implement this API 066 * instead. 067 * <BR><BR> 068 * Encoded passwords may take one of two forms. The first is the "user 069 * password" syntax, in which the encoded password is represented by the name of 070 * the storage scheme in curly braces followed by the transformed password 071 * (e.g., "{scheme}encoded"). This format isn't based on any defined standard, 072 * but is commonly used by a number of directory server implementations. The 073 * second format is the authentication password syntax as described in RFC 3112, 074 * in which the encoded representation is broken into scheme, authInfo, and 075 * authValue segments separated by dollar signs (e.g., 076 * "scheme$authInfo$authValue"). All password storage schemes are required to 077 * support the "user password" syntax and may optionally also support the 078 * authentication password syntax. 079 * <BR> 080 * <H2>Configuring Enhanced Password Storage Schemes</H2> 081 * In order to configure a password storage scheme created using this API, use 082 * a command like: 083 * <PRE> 084 * dsconfig create-password-storage-scheme \ 085 * --scheme-name "<I>{scheme-name}</I>" \ 086 * --type third-party-enhanced \ 087 * --set enabled:true \ 088 * --set "extension-class:<I>{class-name}</I>" \ 089 * --set "extension-argument:<I>{name=value}</I>" 090 * </PRE> 091 * where "<I>{scheme-name}</I>" is the name to use for the password storage 092 * scheme instance, "<I>{class-name}</I>" is the fully-qualified name of the 093 * Java class that extends 094 * {@code com.unboundid.directory.sdk.ds.api.PasswordStorageScheme}, and 095 * "<I>{name=value}</I>" represents name-value pairs for any arguments to 096 * provide to the password storage scheme. If multiple arguments should be 097 * provided to the password storage scheme, then the 098 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 099 * provided multiple times. 100 * 101 * @see PasswordStorageScheme 102 */ 103 @Extensible() 104 @DirectoryServerExtension() 105 @DirectoryProxyServerExtension(appliesToLocalContent=true, 106 appliesToRemoteContent=false) 107 @SynchronizationServerExtension(appliesToLocalContent=true, 108 appliesToSynchronizedContent=false) 109 @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 110 public abstract class EnhancedPasswordStorageScheme 111 implements UnboundIDExtension, 112 Reconfigurable<PasswordStorageSchemeConfig>, 113 ExampleUsageProvider 114 { 115 /** 116 * Creates a new instance of this password storage scheme. All password 117 * storage scheme implementations must include a default constructor, but any 118 * initialization should generally be done in the 119 * {@code initializePasswordStorageScheme} method. 120 */ 121 public EnhancedPasswordStorageScheme() 122 { 123 // No implementation is required. 124 } 125 126 127 128 /** 129 * {@inheritDoc} 130 */ 131 public abstract String getExtensionName(); 132 133 134 135 /** 136 * {@inheritDoc} 137 */ 138 public abstract String[] getExtensionDescription(); 139 140 141 142 /** 143 * {@inheritDoc} 144 */ 145 public void defineConfigArguments(final ArgumentParser parser) 146 throws ArgumentException 147 { 148 // No arguments will be allowed by default. 149 } 150 151 152 153 /** 154 * Initializes this password storage scheme. 155 * 156 * @param serverContext A handle to the server context for the server in 157 * which this extension is running. 158 * @param config The general configuration for this password storage 159 * scheme. 160 * @param parser The argument parser which has been initialized from 161 * the configuration for this password storage scheme. 162 * 163 * @throws LDAPException If a problem occurs while initializing this 164 * password storage scheme. 165 */ 166 public void initializePasswordStorageScheme( 167 final DirectoryServerContext serverContext, 168 final PasswordStorageSchemeConfig config, 169 final ArgumentParser parser) 170 throws LDAPException 171 { 172 // No initialization will be performed by default. 173 } 174 175 176 177 /** 178 * {@inheritDoc} 179 */ 180 public boolean isConfigurationAcceptable( 181 final PasswordStorageSchemeConfig config, 182 final ArgumentParser parser, 183 final List<String> unacceptableReasons) 184 { 185 // No extended validation will be performed by default. 186 return true; 187 } 188 189 190 191 /** 192 * {@inheritDoc} 193 */ 194 public ResultCode applyConfiguration(final PasswordStorageSchemeConfig config, 195 final ArgumentParser parser, 196 final List<String> adminActionsRequired, 197 final List<String> messages) 198 { 199 // By default, no configuration changes will be applied. If there are any 200 // arguments, then add an admin action message indicating that the extension 201 // needs to be restarted for any changes to take effect. 202 if (! parser.getNamedArguments().isEmpty()) 203 { 204 adminActionsRequired.add( 205 "No configuration change has actually been applied. The new " + 206 "configuration will not take effect until this password " + 207 "storage scheme is disabled and re-enabled or until the " + 208 "server is restarted."); 209 } 210 211 return ResultCode.SUCCESS; 212 } 213 214 215 216 /** 217 * Performs any cleanup which may be necessary when this password storage 218 * scheme is to be taken out of service. 219 */ 220 public void finalizePasswordStorageScheme() 221 { 222 // No implementation is required. 223 } 224 225 226 227 /** 228 * Retrieves the name for this password storage scheme. This will be the 229 * identifier which appears in curly braces at the beginning of the encoded 230 * password. The name should not include curly braces. 231 * 232 * @return The name for this password storage scheme. 233 */ 234 public abstract String getStorageSchemeName(); 235 236 237 238 /** 239 * Indicates whether this password storage scheme encodes passwords in a form 240 * that allows the original plaintext value to be obtained from the encoded 241 * representation. 242 * 243 * @return {@code true} if the original plaintext password may be obtained 244 * from the encoded password, or {@code false} if not. 245 */ 246 public abstract boolean isReversible(); 247 248 249 250 /** 251 * Indicates whether this password storage scheme encodes passwords in a form 252 * that may be considered secure. A storage scheme should only be considered 253 * secure if it is not possible to trivially determine a clear-text value 254 * which may be used to generate a given encoded representation. 255 * 256 * @return {@code true} if this password storage scheme may be considered 257 * secure, or {@code false} if not. 258 */ 259 public abstract boolean isSecure(); 260 261 262 263 /** 264 * Indicates whether this password storage scheme requires access to the user 265 * entry in order to perform processing. If the storage scheme does require 266 * access to the user entry (e.g., in order to obtain key or salt information 267 * from another attribute, or to use information in the entry to access 268 * information in another data store), then some features (e.g., the ability 269 * to perform extensible matching with password values, or the ability to use 270 * the encode-password tool to encode and compare passwords) may not be 271 * available. 272 * <BR><BR> 273 * Note that if this method returns {@code true}, then the server will never 274 * provide a {@code null} value for the {@code userEntry} parameter to methods 275 * that accept it. However, if this method returns {@code false}, then there 276 * may be some instances in which the server may invoke those methods with a 277 * {@code null} value for the {@code userEntry} parameter (e.g., when invoked 278 * by the encode-password tool or when targeting a password with an extensible 279 * match search filter). 280 * 281 * @return {@code true} if this password storage scheme requires access to 282 * the user entry, or {@code false} if not. 283 */ 284 public boolean requiresUserEntry() 285 { 286 return false; 287 } 288 289 290 291 /** 292 * Encodes the provided plaintext password for this storage scheme, 293 * without the name of the associated scheme. Note that the 294 * provided plaintext password should not be altered in any way. 295 * 296 * @param plaintext The plaintext version of the password. This will 297 * not be {@code null}. 298 * @param userEntry The complete entry for the user for whom the 299 * password is to be encoded. This will not be 300 * {@code null} for schemes in which 301 * {@link #requiresUserEntry} returns {@code true}. 302 * @param modifications The set of modifications to be applied to the user 303 * entry. This will generally be non-{@code null} only 304 * for operations that use a modify to change the user 305 * password. If a modification list is provided, then 306 * it will contain the complete set of modifications 307 * for the operation, some of which may have no 308 * impact on password encoding. 309 * @param deterministic Indicates whether the password encoding should be 310 * deterministic. If this is {@code true}, then the 311 * scheme should attempt to generate a consistent 312 * encoding for the password (e.g., by determining the 313 * salt from a normalized representation of the user's 314 * DN). This will be used during LDIF import 315 * processing (and potentially at other times) to help 316 * ensure that if the LDIF file is imported into 317 * multiple servers, any clear-text password 318 * encountered in the LDIF file will have the same 319 * encoded representation on all servers. Some 320 * password storage schemes may choose to ignore this 321 * property if it does not apply or is not possible to 322 * achieve. 323 * @param includeScheme Indicates whether to include the name of the scheme 324 * in curly braces before the encoded password. 325 * 326 * @return The password that has been encoded using this storage 327 * scheme. 328 * 329 * @throws LDAPException If a problem occurs while attempting to encode the 330 * password. 331 */ 332 public abstract ByteString encodePassword(final ByteString plaintext, 333 final Entry userEntry, 334 final List<Modification> modifications, 335 final boolean deterministic, 336 final boolean includeScheme) 337 throws LDAPException; 338 339 340 341 /** 342 * Indicates whether the provided plaintext password could have been used to 343 * generate the given encoded password. 344 * 345 * @param plaintextPassword The plaintext password provided by the user as 346 * part of a simple bind attempt. 347 * @param storedPassword The stored password to compare against the 348 * provided plaintext password. It will not 349 * include the scheme name in curly braces. 350 * @param userEntry The complete entry for the user for whom the 351 * password is to be validated. This will not be 352 * {@code null} for schemes in which 353 * {@link #requiresUserEntry} returns {@code true}. 354 * 355 * @return {@code true} if the provided clear-text password could have been 356 * used to generate the encoded password, or {@code false} if not. 357 */ 358 public abstract boolean passwordMatches(final ByteString plaintextPassword, 359 final ByteString storedPassword, 360 final Entry userEntry); 361 362 363 364 /** 365 * Attempts to determine the plaintext password used to generate the provided 366 * encoded password. This method should only be called if the 367 * {@link #isReversible} method returns {@code true}. 368 * 369 * @param storedPassword The password for which to obtain the plaintext 370 * value. It should not include the scheme name in 371 * curly braces. 372 * @param userEntry The complete entry for the user for whom the 373 * password is to be validated. This will not be 374 * {@code null} for schemes in which 375 * {@link #requiresUserEntry} returns {@code true}. 376 * 377 * @return The plaintext password obtained from the given encoded password. 378 * 379 * @throws LDAPException If this password storage scheme is not reversible, 380 * or if the provided value could not be decoded to 381 * its plaintext representation. 382 */ 383 public abstract ByteString getPlaintextValue(final ByteString storedPassword, 384 final Entry userEntry) 385 throws LDAPException; 386 387 388 389 /** 390 * Indicates whether this password storage scheme provides the ability to 391 * encode passwords in the authentication password syntax as described in RFC 392 * 3112. 393 * 394 * @return {@code true} if this password storage scheme supports the 395 * authentication password syntax, or {@code false} if not. 396 */ 397 public boolean supportsAuthPasswordSyntax() 398 { 399 return false; 400 } 401 402 403 404 /** 405 * Retrieves the name that should be used to identify this password storage 406 * scheme when encoding passwords using the authentication password syntax as 407 * described in RFC 3112. This should only be used if the 408 * {@link #supportsAuthPasswordSyntax} method returns {@code true}. 409 * 410 * @return The name that should be used to identify this password storage 411 * scheme when encoding passwords using the authentication password 412 * syntax. 413 */ 414 public String getAuthPasswordSchemeName() 415 { 416 return getStorageSchemeName(); 417 } 418 419 420 421 /** 422 * Encodes the provided plaintext password using the authentication password 423 * syntax as defined in RFC 3112. This should only be used if the 424 * {@link #supportsAuthPasswordSyntax} method returns {@code true}. 425 * 426 * @param plaintext The plaintext version of the password. This will 427 * not be {@code null}. 428 * @param userEntry The complete entry for the user for whom the 429 * password is to be encoded. This will not be 430 * {@code null} for schemes in which 431 * {@link #requiresUserEntry} returns {@code true}. 432 * @param modifications The set of modifications to be applied to the user 433 * entry. This will generally be non-{@code null} only 434 * for operations that use a modify to change the user 435 * password. If a modification list is provided, then 436 * it will contain the complete set of modifications 437 * for the operation, some of which may have no 438 * impact on password encoding. 439 * @param deterministic Indicates whether the password encoding should be 440 * deterministic. If this is {@code true}, then the 441 * scheme should attempt to generate a consistent 442 * encoding for the password (e.g., by determining the 443 * salt from a normalized representation of the user's 444 * DN). This will be used during LDIF import 445 * processing (and potentially at other times) to help 446 * ensure that if the LDIF file is imported into 447 * multiple servers, any clear-text password 448 * encountered in the LDIF file will have the same 449 * encoded representation on all servers. Some 450 * password storage schemes may choose to ignore this 451 * property if it does not apply or is not possible to 452 * achieve. 453 * 454 * @return The encoded representation of the provided password. 455 * 456 * @throws LDAPException If a problem occurs while encoding the provided 457 * password, or if this password storage scheme does 458 * not support the authentication password syntax. 459 */ 460 public ByteString encodeAuthPassword(final ByteString plaintext, 461 final Entry userEntry, 462 final List<Modification> modifications, 463 final boolean deterministic) 464 throws LDAPException 465 { 466 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 467 "This password storage scheme does not support the use of the " + 468 "authentication password syntax."); 469 } 470 471 472 473 /** 474 * Indicates whether the provided plaintext password may be used to generate 475 * an encoded password with the given authInfo and authValue elements when 476 * using the authentication password syntax as defined in RFC 3112. This 477 * should only be used if the {@link #supportsAuthPasswordSyntax} method 478 * returns {@code true}. 479 * 480 * @param plaintextPassword The plaintext password provided by the user. 481 * @param authInfo The authInfo component of the password encoded 482 * in the authentication password syntax. 483 * @param authValue The authValue component of the password encoded 484 * in the authentication password syntax. 485 * @param userEntry The complete entry for the user for whom the 486 * password is to be validated. This will not be 487 * {@code null} for schemes in which 488 * {@link #requiresUserEntry} returns {@code true}. 489 * 490 * @return {@code true} if the provided plaintext password could be used to 491 * generate an encoded password with the given authInfo and authValue 492 * portions, or {@code false} if not. 493 */ 494 public boolean authPasswordMatches(final ByteString plaintextPassword, 495 final String authInfo, 496 final String authValue, 497 final Entry userEntry) 498 { 499 return false; 500 } 501 502 503 504 /** 505 * Obtains the plaintext password that was used to generate an encoded 506 * password with the given authInfo and authValue elements when using the 507 * authentication password syntax as described in RFC 3112. This should only 508 * be used if both the {@link #supportsAuthPasswordSyntax} and 509 * {@link #isReversible} methods return {@code true}. 510 * 511 * @param authInfo The authInfo component of the password encoded in the 512 * authentication password syntax. 513 * @param authValue The authValue component of the password encoded in the 514 * authentication password syntax. 515 * @param userEntry The complete entry for the user for whom the password is 516 * to be validated. This will not be {@code null} for 517 * schemes in which {@link #requiresUserEntry} returns 518 * {@code true}. The entry should not be altered in any 519 * way by this password storage scheme. 520 * 521 * @return The plaintext password that was used to generate the encoded 522 * password. 523 * 524 * @throws LDAPException If this password storage scheme is not reversible, 525 * if this password storage scheme does not support 526 * the authentication password syntax, or if some 527 * other problem is encountered while attempting to 528 * determine the plaintext password. 529 */ 530 public ByteString getAuthPasswordPlaintextValue(final String authInfo, 531 final String authValue, 532 final Entry userEntry) 533 throws LDAPException 534 { 535 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 536 "This password storage scheme does not support the use of the " + 537 "authentication password syntax."); 538 } 539 540 541 542 /** 543 * {@inheritDoc} 544 */ 545 public Map<List<String>,String> getExamplesArgumentSets() 546 { 547 return Collections.emptyMap(); 548 } 549 }