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