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.util;
028
029
030
031 import java.lang.reflect.Method;
032 import java.util.Arrays;
033
034 import org.opends.server.api.DirectoryThread;
035
036
037
038 /**
039 * This class provides a means of interactively reading a password from the
040 * command-line without echoing it to the console. If it is running on a Java 6
041 * or higher VM, then it will use the System.console() method. If it is running
042 * on Java 5, then it will use an ugly hack in which one thread will be used to
043 * repeatedly send backspace characters to the console while another reads the
044 * password. Reflection is used to determine whether the Java 6 method is
045 * available and to invoke it if it is so that the code will still compile
046 * cleanly on Java 5 systems.
047 */
048 @org.opends.server.types.PublicAPI(
049 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
050 mayInstantiate=false,
051 mayExtend=false,
052 mayInvoke=true)
053 public final class PasswordReader
054 extends DirectoryThread
055 {
056 // Indicates whether the backspace thread should keep looping, sending
057 // backspace characters to the console.
058 private volatile boolean keepLooping;
059
060
061
062 /**
063 * Creates a new instance of this password reader. A new instance should only
064 * be created from within this class.
065 */
066 private PasswordReader()
067 {
068 super("Password Reader Thread");
069
070 // No implementation is required. However, this constructor is private to
071 // help prevent it being used for external purposes.
072 }
073
074
075
076 /**
077 * Operates in a loop, sending backspace characters to the console to attempt
078 * to prevent exposing what the user entered. It sets the priority to the
079 * maximum allowed value to reduce the chance of one or more characters being
080 * displayed temporarily before they can be erased.
081 */
082 @org.opends.server.types.PublicAPI(
083 stability=org.opends.server.types.StabilityLevel.PRIVATE,
084 mayInstantiate=false,
085 mayExtend=false,
086 mayInvoke=false)
087 public void run()
088 {
089 Thread currentThread = Thread.currentThread();
090 int initialPriority = currentThread.getPriority();
091
092 try
093 {
094 try
095 {
096 currentThread.setPriority(Thread.MAX_PRIORITY);
097 } catch (Exception e) {}
098
099 keepLooping = true;
100 while (keepLooping)
101 {
102 System.out.print("\u0008 ");
103
104 try
105 {
106 currentThread.sleep(1);
107 }
108 catch (InterruptedException ie)
109 {
110 currentThread.interrupt();
111 return;
112 }
113 }
114 }
115 finally
116 {
117 try
118 {
119 currentThread.setPriority(initialPriority);
120 } catch (Exception e) {}
121 }
122 }
123
124
125
126 /**
127 * Indicates that the backspace thread should stop looping as the complete
128 * password has been entered.
129 */
130 private void stopLooping()
131 {
132 keepLooping = false;
133 }
134
135
136
137 /**
138 * Reads a password from the console without echoing it to the client.
139 *
140 * @return The password as an array of characters.
141 */
142 public static char[] readPassword()
143 {
144 // First, use reflection to determine whether the System.console() method
145 // is available.
146 try
147 {
148 Method consoleMethod = System.class.getDeclaredMethod("console",
149 new Class[0]);
150 if (consoleMethod != null)
151 {
152 char[] password = readPasswordUsingConsole(consoleMethod);
153 if (password != null)
154 {
155 return password;
156 }
157 }
158 }
159 catch (Exception e)
160 {
161 // This must mean that we're running on a JVM that doesn't have the
162 // System.console() method, or that the call to Console.readPassword()
163 // isn't working. Fall back to using backspaces.
164 return readPasswordUsingBackspaces();
165 }
166
167
168 // If we've gotten here, then the System.console() method must not exist.
169 // Fall back on using backspaces.
170 return readPasswordUsingBackspaces();
171 }
172
173
174
175 /**
176 * Uses reflection to invoke the <CODE>java.io.Console.readPassword()</CODE>
177 * method in order to retrieve the password from the user.
178 *
179 * @param consoleMethod The <CODE>Method</CODE> object that may be used to
180 * obtain a <CODE>Console</CODE> instance.
181 *
182 * @return The password as an array of characters.
183 *
184 * @throws Exception If any problem occurs while attempting to read the
185 * password.
186 */
187 private static char[] readPasswordUsingConsole(Method consoleMethod)
188 throws Exception
189 {
190 Object consoleObject = consoleMethod.invoke(null);
191 Method passwordMethod =
192 consoleObject.getClass().getDeclaredMethod("readPassword",
193 new Class[0]);
194 return (char[]) passwordMethod.invoke(consoleObject);
195 }
196
197
198
199 /**
200 * Attempts to read a password from the console by repeatedly sending
201 * backspace characters to mask whatever the user may have entered. This will
202 * be used if the <CODE>java.io.Console</CODE> class is not available.
203 *
204 * @return The password read from the console.
205 */
206 private static char[] readPasswordUsingBackspaces()
207 {
208 char[] pwChars;
209 char[] pwBuffer = new char[100];
210 int pos = 0;
211
212 PasswordReader backspaceThread = new PasswordReader();
213 backspaceThread.start();
214
215 try
216 {
217 while (true)
218 {
219 int charRead = System.in.read();
220 if ((charRead == -1) || (charRead == '\n'))
221 {
222 // This is the end of the value.
223 pwChars = new char[pos];
224 if (0 < pos)
225 {
226 System.arraycopy(pwBuffer, 0, pwChars, 0, pos);
227 Arrays.fill(pwBuffer, '\u0000');
228 }
229 return pwChars;
230 }
231 else if (charRead == '\r')
232 {
233 int char2 = System.in.read();
234 if (char2 == '\n')
235 {
236 // This is the end of the value.
237 if (pos == 0)
238 {
239 return null;
240 }
241 else
242 {
243 pwChars = new char[pos];
244 System.arraycopy(pwBuffer, 0, pwChars, 0, pos);
245 Arrays.fill(pwBuffer, '\u0000');
246 return pwChars;
247 }
248 }
249 else
250 {
251 // Append the characters to the buffer and continue.
252 pwBuffer[pos++] = (char) charRead;
253 if (pos >= pwBuffer.length)
254 {
255 char[] newBuffer = new char[pwBuffer.length+100];
256 System.arraycopy(pwBuffer, 0, newBuffer, 0, pwBuffer.length);
257 Arrays.fill(pwBuffer, '\u0000');
258 pwBuffer = newBuffer;
259 }
260
261 pwBuffer[pos++] = (char) char2;
262 if (pos >= pwBuffer.length)
263 {
264 char[] newBuffer = new char[pwBuffer.length+100];
265 System.arraycopy(pwBuffer, 0, newBuffer, 0, pwBuffer.length);
266 Arrays.fill(pwBuffer, '\u0000');
267 pwBuffer = newBuffer;
268 }
269 }
270 }
271 else
272 {
273 // Append the value to the buffer and continue.
274 pwBuffer[pos++] = (char) charRead;
275
276 if (pos >= pwBuffer.length)
277 {
278 char[] newBuffer = new char[pwBuffer.length+100];
279 System.arraycopy(pwBuffer, 0, newBuffer, 0, pwBuffer.length);
280 Arrays.fill(pwBuffer, '\u0000');
281 pwBuffer = newBuffer;
282 }
283 }
284 }
285 }
286 catch (Exception e)
287 {
288 // We must have encountered an error while attempting to read. The only
289 // thing we can do is to dump a stack trace and return null.
290 e.printStackTrace();
291 return null;
292 }
293 finally
294 {
295 backspaceThread.stopLooping();
296 }
297 }
298 }
299