001    /*
002     * Copyright 2011-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2011-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.io.File;
026    import java.io.FileInputStream;
027    import java.io.Serializable;
028    import java.util.Arrays;
029    
030    import com.unboundid.util.Debug;
031    import com.unboundid.util.StaticUtils;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    import com.unboundid.util.Validator;
035    
036    import static com.unboundid.ldap.sdk.LDAPMessages.*;
037    
038    
039    
040    /**
041     * This class provides an implementation of a password provider that will obtain
042     * the password from a specified file.  All bytes up to (but not including) the
043     * first end-of-line character (or to the end of the file if it does not contain
044     * an end-of-line character) will be considered part of the password.
045     */
046    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
047    public final class ReadFromFilePasswordProvider
048           extends PasswordProvider
049           implements Serializable
050    {
051      /**
052       * The serial version UID for this serializable file.
053       */
054      private static final long serialVersionUID = -3343425971796985100L;
055    
056    
057    
058      // The password file to use.
059      private final File passwordFile;
060    
061    
062    
063      /**
064       * Creates a new instance of this password provider that will read passwords
065       * from the specified file.
066       *
067       * @param  passwordFile  The path to the file containing the password to use.
068       *                       It must not be {@code null}.
069       */
070      public ReadFromFilePasswordProvider(final String passwordFile)
071      {
072        Validator.ensureNotNull(passwordFile);
073    
074        this.passwordFile = new File(passwordFile);
075      }
076    
077    
078    
079      /**
080       * Creates a new instance of this password provider that will read passwords
081       * from the specified file.
082       *
083       * @param  passwordFile  The file containing the password to use.  It must not
084       *                       be {@code null}.
085       */
086      public ReadFromFilePasswordProvider(final File passwordFile)
087      {
088        Validator.ensureNotNull(passwordFile);
089    
090        this.passwordFile = passwordFile;
091      }
092    
093    
094    
095      /**
096       * Retrieves a password in a newly-created byte array.  Once the password is
097       * no longer required, the contents of the array will be overwritten so that
098       * the password is no longer contained in memory.
099       *
100       * @return  A byte array containing the password that should be used.
101       *
102       * @throws  LDAPException  If a problem is encountered while attempting to
103       *                         obtain the password.
104       */
105      @Override()
106      public byte[] getPasswordBytes()
107             throws LDAPException
108      {
109        byte[] pwBytes = null;
110    
111        try
112        {
113          final int fileLength = (int) passwordFile.length();
114          pwBytes = new byte[fileLength];
115    
116          final FileInputStream inputStream = new FileInputStream(passwordFile);
117    
118          try
119          {
120            int pos = 0;
121            while (pos < fileLength)
122            {
123              final int bytesRead =
124                   inputStream.read(pwBytes, pos, pwBytes.length - pos);
125              if (bytesRead < 0)
126              {
127                break;
128              }
129    
130              pos += bytesRead;
131            }
132          }
133          finally
134          {
135            inputStream.close();
136          }
137    
138          // If there is an end-of-line marker before the end of the file, then
139          // create a password only up to that point and zero out the current array.
140          for (int i=0; i < pwBytes.length; i++)
141          {
142            if ((pwBytes[i] == '\n') || (pwBytes[i] == '\r'))
143            {
144              final byte[] pwWithoutEOL = new byte[i];
145              System.arraycopy(pwBytes, 0, pwWithoutEOL, 0, i);
146              Arrays.fill(pwBytes, (byte) 0x00);
147              pwBytes = pwWithoutEOL;
148              break;
149            }
150          }
151        }
152        catch (final Exception e)
153        {
154          Debug.debugException(e);
155    
156          if (pwBytes != null)
157          {
158            Arrays.fill(pwBytes, (byte) 0x00);
159          }
160    
161          throw new LDAPException(ResultCode.LOCAL_ERROR,
162               ERR_FILE_PW_PROVIDER_ERROR_READING_PW.get(
163                    passwordFile.getAbsolutePath(),
164                    StaticUtils.getExceptionMessage(e)),
165               e);
166        }
167    
168        if (pwBytes.length == 0)
169        {
170          throw new LDAPException(ResultCode.PARAM_ERROR,
171               ERR_FILE_PW_PROVIDER_EMPTY_PW.get(passwordFile.getAbsolutePath()));
172        }
173    
174        return pwBytes;
175      }
176    }