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