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