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     * 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}.
275       *
276       * @return  The encoded representation of the provided password.
277       *
278       * @throws  LDAPException  If a problem occurs while attempting to encode the
279       *                         password.
280       */
281      public abstract ByteString encodePassword(final ByteString plaintext)
282             throws LDAPException;
283    
284    
285    
286      /**
287       * Encodes the provided plaintext password, prefixing the encoded
288       * representation with the name of the storage scheme in curly braces.
289       *
290       * @param  plaintext  The plaintext password to be encoded.  It must not be
291       *                    {@code null}.
292       *
293       * @return  The encoded representation of the provided password, prefixed with
294       *          the storage scheme name.
295       *
296       * @throws  LDAPException  If a problem occurs while attempting to encode the
297       *                         password.
298       */
299      public ByteString encodePasswordWithScheme(final ByteString plaintext)
300             throws LDAPException
301      {
302        final byte[] schemeBytes = StaticUtils.getBytes(getStorageSchemeName());
303        final byte[] encodedPW   = encodePassword(plaintext).getValue();
304    
305        final byte[] b = new byte[schemeBytes.length + encodedPW.length + 2];
306    
307        int pos = 0;
308        b[pos++] = '{';
309    
310        System.arraycopy(schemeBytes, 0, b, pos, schemeBytes.length);
311        pos += schemeBytes.length;
312    
313        b[pos++] = '}';
314        System.arraycopy(encodedPW, 0, b, pos, encodedPW.length);
315    
316        return new ASN1OctetString(b);
317      }
318    
319    
320    
321      /**
322       * Encodes the provided plaintext password, prefixing the encoded
323       * representation with the name of the storage scheme in curly braces.  If an
324       * entry DN is provided, then a deterministic encoding should be used (e.g.,
325       * a salt generated from the provided DN) so that the same encoding will
326       * always be used for same plaintext in the same entry.  This will primarily
327       * be used when importing an LDIF file containing plaintext passwords so that
328       * the same LDIF file can be imported into multiple servers and the same
329       * encoded passwords will be generated in each server.
330       *
331       * @param  plaintext  The plaintext password to be encoded.  It must not be
332       *                    {@code null}.
333       * @param  entryDN    The DN of the entry in which the encoded password will
334       *                    appear.  This may be {@code null} if it is not known.
335       *
336       * @return  The encoded representation of the provided password, prefixed with
337       *          the storage scheme name.
338       *
339       * @throws  LDAPException  If a problem occurs while attempting to encode the
340       *                         password.
341       */
342      public ByteString encodePasswordWithScheme(final ByteString plaintext,
343                                                 final DN entryDN)
344             throws LDAPException
345      {
346        return encodePasswordWithScheme(plaintext);
347      }
348    
349    
350    
351      /**
352       * Indicates whether the provided plaintext password could have been used to
353       * generate the given encoded password.
354       *
355       * @param  plaintext  The plaintext password for which to make the
356       *                    determination.
357       * @param  encoded    The encoded password for which to make the
358       *                    determination.  It will not include the scheme name.
359       *
360       * @return  {@code true} if the provided clear-text password could have been
361       *          used to generate the encoded password, or {@code false} if not.
362       */
363      public abstract boolean passwordMatches(final ByteString plaintext,
364                                              final ByteString encoded);
365    
366    
367    
368      /**
369       * Attempts to determine the plaintext password used to generate the provided
370       * encoded password.  This method should only be called if the
371       * {@link #isReversible} method returns {@code true}.
372       *
373       * @param  encoded  The encoded password for which to obtain the original
374       *                  plaintext password.  It must not be {@code null} and will
375       *                  not be prefixed with the scheme name.
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 encoded)
384             throws LDAPException;
385    
386    
387    
388      /**
389       * Indicates whether this password storage scheme provides the ability to
390       * encode passwords in the authentication password syntax as described in RFC
391       * 3112.
392       *
393       * @return  {@code true} if this password storage scheme supports the
394       *          authentication password syntax, or {@code false} if not.
395       */
396      public boolean supportsAuthPasswordSyntax()
397      {
398        return false;
399      }
400    
401    
402    
403      /**
404       * Retrieves the name that should be used to identify this password storage
405       * scheme when encoding passwords using the authentication password syntax as
406       * described in RFC 3112.  This should only be used if the
407       * {@link #supportsAuthPasswordSyntax} method returns {@code true}.
408       *
409       * @return  The name that should be used to identify this password storage
410       *          scheme when encoding passwords using the authentication password
411       *          syntax.
412       */
413      public String getAuthPasswordSchemeName()
414      {
415        return getStorageSchemeName();
416      }
417    
418    
419    
420      /**
421       * Encodes the provided plaintext password using the authentication password
422       * syntax as defined in RFC 3112.  This should only be used if the
423       * {@link #supportsAuthPasswordSyntax} method returns {@code true}.
424       *
425       * @param  plaintext  The plaintext password to be encoded.
426       *
427       * @return  The encoded representation of the provided password.
428       *
429       * @throws  LDAPException  If a problem occurs while encoding the provided
430       *                         password, or if this password storage scheme does
431       *                         not support the authentication password syntax.
432       */
433      public ByteString encodeAuthPassword(final ByteString plaintext)
434             throws LDAPException
435      {
436        throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
437             "This password storage scheme does not support the use of the " +
438             "authentication password syntax.");
439      }
440    
441    
442    
443      /**
444       * Indicates whether the provided plaintext password may be used to generate
445       * an encoded password with the given authInfo and authValue elements when
446       * using the authentication password syntax as defined in RFC 3112.  This
447       * should only be used if the {@link #supportsAuthPasswordSyntax} method
448       * returns {@code true}.
449       *
450       * @param  plaintext  The plaintext password for which to make the
451       *                    determination.
452       * @param  authInfo   The authInfo portion of the encoded password for which
453       *                    to make the determination.
454       * @param  authValue  The authValue portion of the encoded password for which
455       *                    to make the determination.
456       *
457       * @return  {@code true} if the provided plaintext password could be used to
458       *          generate an encoded password with the given authInfo and authValue
459       *          portions, or {@code false} if not.
460       */
461      public boolean authPasswordMatches(final ByteString plaintext,
462                                         final String authInfo,
463                                         final String authValue)
464      {
465        return false;
466      }
467    
468    
469    
470      /**
471       * Obtains the plaintext password that was used to generate an encoded
472       * password with the given authInfo and authValue elements when using the
473       * authentication password syntax as described in RFC 3112.  This should only
474       * be used if both the {@link #supportsAuthPasswordSyntax} and
475       * {@link #isReversible} methods return {@code true}.
476       *
477       * @param  authInfo   The authInfo portion of the encoded password for which
478       *                    to retrieve the corresponding plaintext value.
479       * @param  authValue  The authValue portion of the encoded password for which
480       *                    to retrieve the corresponding plaintext value.
481       *
482       * @return  The plaintext password that was used to generate the encoded
483       *          password.
484       *
485       * @throws  LDAPException  If this password storage scheme is not reversible,
486       *                         if this password storage scheme does not support
487       *                         the authentication password syntax, or if some
488       *                         other problem is encountered while attempting to
489       *                         determine the plaintext password.
490       */
491      public ByteString getAuthPasswordPlaintextValue(final String authInfo,
492                                                      final String authValue)
493             throws LDAPException
494      {
495        throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
496             "This password storage scheme does not support the use of the " +
497             "authentication password syntax.");
498      }
499    
500    
501    
502      /**
503       * {@inheritDoc}
504       */
505      public Map<List<String>,String> getExamplesArgumentSets()
506      {
507        return Collections.emptyMap();
508      }
509    }