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-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.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 }