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     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
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     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    
029    
030    
031    import java.security.MessageDigest;
032    import java.util.Arrays;
033    
034    import org.opends.messages.Message;
035    import org.opends.server.admin.std.server.SHA1PasswordStorageSchemeCfg;
036    import org.opends.server.api.PasswordStorageScheme;
037    import org.opends.server.config.ConfigException;
038    import org.opends.server.core.DirectoryServer;
039    import org.opends.server.loggers.debug.DebugTracer;
040    import org.opends.server.types.ByteString;
041    import org.opends.server.types.ByteStringFactory;
042    import org.opends.server.types.DebugLogLevel;
043    import org.opends.server.types.DirectoryException;
044    import org.opends.server.types.InitializationException;
045    import org.opends.server.types.ResultCode;
046    import org.opends.server.util.Base64;
047    
048    import static org.opends.messages.ExtensionMessages.*;
049    import static org.opends.server.extensions.ExtensionsConstants.*;
050    import static org.opends.server.loggers.ErrorLogger.*;
051    import static org.opends.server.loggers.debug.DebugLogger.*;
052    import static org.opends.server.util.StaticUtils.*;
053    
054    
055    
056    /**
057     * This class defines a Directory Server password storage scheme based on the
058     * SHA-1 algorithm defined in FIPS 180-1.  This is a one-way digest algorithm
059     * so there is no way to retrieve the original clear-text version of the
060     * password from the hashed value (although this means that it is not suitable
061     * for things that need the clear-text password like DIGEST-MD5).  This
062     * implementation does not perform any salting, which means that it is more
063     * vulnerable to dictionary attacks than salted variants.
064     */
065    public class SHA1PasswordStorageScheme
066           extends PasswordStorageScheme<SHA1PasswordStorageSchemeCfg>
067    {
068      /**
069       * The tracer object for the debug logger.
070       */
071      private static final DebugTracer TRACER = getTracer();
072    
073      /**
074       * The fully-qualified name of this class.
075       */
076      private static final String CLASS_NAME =
077           "org.opends.server.extensions.SHA1PasswordStorageScheme";
078    
079    
080    
081      // The message digest that will actually be used to generate the SHA-1 hashes.
082      private MessageDigest messageDigest;
083    
084      // The lock used to provide threadsafe access to the message digest.
085      private Object digestLock;
086    
087    
088    
089      /**
090       * Creates a new instance of this password storage scheme.  Note that no
091       * initialization should be performed here, as all initialization should be
092       * done in the <CODE>initializePasswordStorageScheme</CODE> method.
093       */
094      public SHA1PasswordStorageScheme()
095      {
096        super();
097      }
098    
099    
100    
101      /**
102       * {@inheritDoc}
103       */
104      @Override()
105      public void initializePasswordStorageScheme(
106                       SHA1PasswordStorageSchemeCfg configuration)
107             throws ConfigException, InitializationException
108      {
109        try
110        {
111          messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1);
112        }
113        catch (Exception e)
114        {
115          if (debugEnabled())
116          {
117            TRACER.debugCaught(DebugLogLevel.ERROR, e);
118          }
119    
120          Message message = ERR_PWSCHEME_CANNOT_INITIALIZE_MESSAGE_DIGEST.get(
121              MESSAGE_DIGEST_ALGORITHM_SHA_1, String.valueOf(e));
122          throw new InitializationException(message, e);
123        }
124    
125        digestLock = new Object();
126      }
127    
128    
129    
130      /**
131       * {@inheritDoc}
132       */
133      @Override()
134      public String getStorageSchemeName()
135      {
136        return STORAGE_SCHEME_NAME_SHA_1;
137      }
138    
139    
140    
141      /**
142       * {@inheritDoc}
143       */
144      @Override()
145      public ByteString encodePassword(ByteString plaintext)
146             throws DirectoryException
147      {
148        byte[] digestBytes;
149    
150        synchronized (digestLock)
151        {
152          try
153          {
154            digestBytes = messageDigest.digest(plaintext.value());
155          }
156          catch (Exception e)
157          {
158            if (debugEnabled())
159            {
160              TRACER.debugCaught(DebugLogLevel.ERROR, e);
161            }
162    
163            Message message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
164                CLASS_NAME, getExceptionMessage(e));
165            throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
166                                         message, e);
167          }
168        }
169    
170        return ByteStringFactory.create(Base64.encode(digestBytes));
171      }
172    
173    
174    
175      /**
176       * {@inheritDoc}
177       */
178      @Override()
179      public ByteString encodePasswordWithScheme(ByteString plaintext)
180             throws DirectoryException
181      {
182        StringBuilder buffer = new StringBuilder();
183        buffer.append('{');
184        buffer.append(STORAGE_SCHEME_NAME_SHA_1);
185        buffer.append('}');
186    
187        byte[] digestBytes;
188    
189        synchronized (digestLock)
190        {
191          try
192          {
193            digestBytes = messageDigest.digest(plaintext.value());
194          }
195          catch (Exception e)
196          {
197            if (debugEnabled())
198            {
199              TRACER.debugCaught(DebugLogLevel.ERROR, e);
200            }
201    
202            Message message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
203                CLASS_NAME, getExceptionMessage(e));
204            throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
205                                         message, e);
206          }
207        }
208    
209        buffer.append(Base64.encode(digestBytes));
210    
211        return ByteStringFactory.create(buffer.toString());
212      }
213    
214    
215    
216      /**
217       * {@inheritDoc}
218       */
219      @Override()
220      public boolean passwordMatches(ByteString plaintextPassword,
221                                     ByteString storedPassword)
222      {
223        byte[] userPWDigestBytes;
224    
225        synchronized (digestLock)
226        {
227          try
228          {
229            userPWDigestBytes = messageDigest.digest(plaintextPassword.value());
230          }
231          catch (Exception e)
232          {
233            if (debugEnabled())
234            {
235              TRACER.debugCaught(DebugLogLevel.ERROR, e);
236            }
237    
238            return false;
239          }
240        }
241    
242        byte[] storedPWDigestBytes;
243        try
244        {
245          storedPWDigestBytes = Base64.decode(storedPassword.stringValue());
246        }
247        catch (Exception e)
248        {
249          if (debugEnabled())
250          {
251            TRACER.debugCaught(DebugLogLevel.ERROR, e);
252          }
253    
254          logError(ERR_PWSCHEME_CANNOT_BASE64_DECODE_STORED_PASSWORD.get(
255              storedPassword.stringValue(), String.valueOf(e)));
256    
257          return false;
258        }
259    
260        return Arrays.equals(userPWDigestBytes, storedPWDigestBytes);
261      }
262    
263    
264    
265      /**
266       * {@inheritDoc}
267       */
268      @Override()
269      public boolean supportsAuthPasswordSyntax()
270      {
271        // This storage scheme does not support the authentication password syntax.
272        return false;
273      }
274    
275    
276    
277      /**
278       * {@inheritDoc}
279       */
280      @Override()
281      public ByteString encodeAuthPassword(ByteString plaintext)
282             throws DirectoryException
283      {
284        Message message =
285            ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
286        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
287      }
288    
289    
290    
291      /**
292       * {@inheritDoc}
293       */
294      @Override()
295      public boolean authPasswordMatches(ByteString plaintextPassword,
296                                         String authInfo, String authValue)
297      {
298        // This storage scheme does not support the authentication password syntax.
299        return false;
300      }
301    
302    
303    
304      /**
305       * {@inheritDoc}
306       */
307      @Override()
308      public boolean isReversible()
309      {
310        return false;
311      }
312    
313    
314    
315      /**
316       * {@inheritDoc}
317       */
318      @Override()
319      public ByteString getPlaintextValue(ByteString storedPassword)
320             throws DirectoryException
321      {
322        Message message =
323            ERR_PWSCHEME_NOT_REVERSIBLE.get(STORAGE_SCHEME_NAME_SHA_1);
324        throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
325      }
326    
327    
328    
329      /**
330       * {@inheritDoc}
331       */
332      @Override()
333      public ByteString getAuthPasswordPlaintextValue(String authInfo,
334                                                      String authValue)
335             throws DirectoryException
336      {
337        Message message =
338            ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
339        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
340      }
341    
342    
343    
344      /**
345       * {@inheritDoc}
346       */
347      @Override()
348      public boolean isStorageSchemeSecure()
349      {
350        // SHA-1 should be considered secure.
351        return true;
352      }
353    }
354