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    }