1 7 8 package com.sun.jmx.remote.security; 9 10 import java.io.BufferedInputStream ; 11 import java.io.File ; 12 import java.io.FileInputStream ; 13 import java.io.FilePermission ; 14 import java.io.IOException ; 15 import java.security.AccessControlException ; 16 import java.security.AccessController ; 17 import java.util.Arrays ; 18 import java.util.Hashtable ; 19 import java.util.Map ; 20 import java.util.Properties ; 21 22 import javax.security.auth.*; 23 import javax.security.auth.callback.*; 24 import javax.security.auth.login.*; 25 import javax.security.auth.spi.*; 26 import javax.management.remote.JMXPrincipal ; 27 28 import com.sun.jmx.remote.util.ClassLogger; 29 import com.sun.jmx.remote.util.EnvHelp; 30 import sun.management.jmxremote.ConnectorBootstrap; 31 32 import sun.security.action.GetPropertyAction; 33 34 93 public class FileLoginModule implements LoginModule { 94 95 private static final String DEFAULT_PASSWORD_FILE_NAME = 97 ((String ) AccessController.doPrivileged( 98 new GetPropertyAction("java.home"))) + 99 File.separatorChar + "lib" + 100 File.separatorChar + "management" + File.separatorChar + 101 ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME; 102 103 private static final String USERNAME_KEY = 105 "javax.security.auth.login.name"; 106 107 private static final String PASSWORD_KEY = 109 "javax.security.auth.login.password"; 110 111 private static final ClassLogger logger = 113 new ClassLogger("javax.management.remote.misc", "FileLoginModule"); 114 115 private boolean useFirstPass = false; 117 private boolean tryFirstPass = false; 118 private boolean storePass = false; 119 private boolean clearPass = false; 120 121 private boolean succeeded = false; 123 private boolean commitSucceeded = false; 124 125 private String username; 127 private char[] password; 128 private JMXPrincipal user; 129 130 private Subject subject; 132 private CallbackHandler callbackHandler; 133 private Map sharedState; 134 private Map options; 135 private String passwordFile; 136 private String passwordFileDisplayName; 137 private boolean userSuppliedPasswordFile; 138 private boolean hasJavaHomePermission; 139 private Properties userCredentials; 140 141 152 public void initialize(Subject subject, CallbackHandler callbackHandler, 153 Map <String ,?> sharedState, 154 Map <String ,?> options) 155 { 156 157 this.subject = subject; 158 this.callbackHandler = callbackHandler; 159 this.sharedState = sharedState; 160 this.options = options; 161 162 tryFirstPass = 164 "true".equalsIgnoreCase((String )options.get("tryFirstPass")); 165 useFirstPass = 166 "true".equalsIgnoreCase((String )options.get("useFirstPass")); 167 storePass = 168 "true".equalsIgnoreCase((String )options.get("storePass")); 169 clearPass = 170 "true".equalsIgnoreCase((String )options.get("clearPass")); 171 172 passwordFile = (String )options.get("passwordFile"); 173 passwordFileDisplayName = passwordFile; 174 userSuppliedPasswordFile = true; 175 176 if (passwordFile == null) { 178 passwordFile = DEFAULT_PASSWORD_FILE_NAME; 179 userSuppliedPasswordFile = false; 180 try { 181 System.getProperty("java.home"); 182 hasJavaHomePermission = true; 183 passwordFileDisplayName = passwordFile; 184 } catch (SecurityException e) { 185 hasJavaHomePermission = false; 186 passwordFileDisplayName = 187 ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME; 188 } 189 } 190 } 191 192 204 public boolean login() throws LoginException { 205 206 try { 207 loadPasswordFile(); 208 } catch (IOException ioe) { 209 LoginException le = new LoginException( 210 "Error: unable to load the password file: " + 211 passwordFileDisplayName); 212 throw (LoginException) EnvHelp.initCause(le, ioe); 213 } 214 215 if (userCredentials == null) { 216 throw new LoginException 217 ("Error: unable to locate the users' credentials."); 218 } 219 220 if (logger.debugOn()) { 221 logger.debug("login", 222 "Using password file: " + passwordFileDisplayName); 223 } 224 225 if (tryFirstPass) { 227 228 try { 229 attemptAuthentication(true); 232 233 succeeded = true; 235 if (logger.debugOn()) { 236 logger.debug("login", 237 "Authentication using cached password has succeeded"); 238 } 239 return true; 240 241 } catch (LoginException le) { 242 cleanState(); 244 logger.debug("login", 245 "Authentication using cached password has failed"); 246 } 247 248 } else if (useFirstPass) { 249 250 try { 251 attemptAuthentication(true); 254 255 succeeded = true; 257 if (logger.debugOn()) { 258 logger.debug("login", 259 "Authentication using cached password has succeeded"); 260 } 261 return true; 262 263 } catch (LoginException le) { 264 cleanState(); 266 logger.debug("login", 267 "Authentication using cached password has failed"); 268 269 throw le; 270 } 271 } 272 273 if (logger.debugOn()) { 274 logger.debug("login", "Acquiring password"); 275 } 276 277 try { 279 attemptAuthentication(false); 280 281 succeeded = true; 283 if (logger.debugOn()) { 284 logger.debug("login", "Authentication has succeeded"); 285 } 286 return true; 287 288 } catch (LoginException le) { 289 cleanState(); 290 logger.debug("login", "Authentication has failed"); 291 292 throw le; 293 } 294 } 295 296 316 public boolean commit() throws LoginException { 317 318 if (succeeded == false) { 319 return false; 320 } else { 321 if (subject.isReadOnly()) { 322 cleanState(); 323 throw new LoginException("Subject is read-only"); 324 } 325 if (!subject.getPrincipals().contains(user)) { 327 subject.getPrincipals().add(user); 328 } 329 330 if (logger.debugOn()) { 331 logger.debug("commit", 332 "Authentication has completed successfully"); 333 } 334 } 335 cleanState(); 337 commitSucceeded = true; 338 return true; 339 } 340 341 357 public boolean abort() throws LoginException { 358 359 if (logger.debugOn()) { 360 logger.debug("abort", 361 "Authentication has not completed successfully"); 362 } 363 364 if (succeeded == false) { 365 return false; 366 } else if (succeeded == true && commitSucceeded == false) { 367 368 succeeded = false; 370 cleanState(); 371 user = null; 372 } else { 373 logout(); 376 } 377 return true; 378 } 379 380 390 public boolean logout() throws LoginException { 391 if (subject.isReadOnly()) { 392 cleanState(); 393 throw new LoginException ("Subject is read-only"); 394 } 395 subject.getPrincipals().remove(user); 396 397 cleanState(); 399 succeeded = false; 400 commitSucceeded = false; 401 user = null; 402 403 if (logger.debugOn()) { 404 logger.debug("logout", "Subject is being logged out"); 405 } 406 407 return true; 408 } 409 410 416 private void attemptAuthentication(boolean usePasswdFromSharedState) 417 throws LoginException { 418 419 getUsernamePassword(usePasswdFromSharedState); 421 422 String localPassword = null; 423 424 if (((localPassword = userCredentials.getProperty(username)) == null) || 426 (! localPassword.equals(new String (password)))) { 427 428 if (logger.debugOn()) { 430 logger.debug("login", "Invalid username or password"); 431 } 432 throw new FailedLoginException("Invalid username or password"); 433 } 434 435 if (storePass && 438 !sharedState.containsKey(USERNAME_KEY) && 439 !sharedState.containsKey(PASSWORD_KEY)) { 440 sharedState.put(USERNAME_KEY, username); 441 sharedState.put(PASSWORD_KEY, password); 442 } 443 444 user = new JMXPrincipal (username); 446 447 if (logger.debugOn()) { 448 logger.debug("login", 449 "User '" + username + "' successfully validated"); 450 } 451 } 452 453 456 private void loadPasswordFile() throws IOException { 457 FileInputStream fis; 458 try { 459 fis = new FileInputStream (passwordFile); 460 } catch (SecurityException e) { 461 if (userSuppliedPasswordFile || hasJavaHomePermission) { 462 throw e; 463 } else { 464 FilePermission fp = 465 new FilePermission (passwordFileDisplayName, "read"); 466 AccessControlException ace = new AccessControlException ( 467 "access denied " + fp.toString()); 468 ace.setStackTrace(e.getStackTrace()); 469 throw ace; 470 } 471 } 472 BufferedInputStream bis = new BufferedInputStream (fis); 473 userCredentials = new Properties (); 474 userCredentials.load(bis); 475 bis.close(); 476 } 477 478 490 private void getUsernamePassword(boolean usePasswdFromSharedState) 491 throws LoginException { 492 493 if (usePasswdFromSharedState) { 494 username = (String )sharedState.get(USERNAME_KEY); 496 password = (char[])sharedState.get(PASSWORD_KEY); 497 return; 498 } 499 500 if (callbackHandler == null) 502 throw new LoginException("Error: no CallbackHandler available " + 503 "to garner authentication information from the user"); 504 505 Callback[] callbacks = new Callback[2]; 506 callbacks[0] = new NameCallback("username"); 507 callbacks[1] = new PasswordCallback("password", false); 508 509 try { 510 callbackHandler.handle(callbacks); 511 username = ((NameCallback)callbacks[0]).getName(); 512 char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword(); 513 password = new char[tmpPassword.length]; 514 System.arraycopy(tmpPassword, 0, 515 password, 0, tmpPassword.length); 516 ((PasswordCallback)callbacks[1]).clearPassword(); 517 518 } catch (IOException ioe) { 519 LoginException le = new LoginException(ioe.toString()); 520 throw (LoginException) EnvHelp.initCause(le, ioe); 521 } catch (UnsupportedCallbackException uce) { 522 LoginException le = new LoginException( 523 "Error: " + uce.getCallback().toString() + 524 " not available to garner authentication " + 525 "information from the user"); 526 throw (LoginException) EnvHelp.initCause(le, uce); 527 } 528 } 529 530 533 private void cleanState() { 534 username = null; 535 if (password != null) { 536 Arrays.fill(password, ' '); 537 password = null; 538 } 539 540 if (clearPass) { 541 sharedState.remove(USERNAME_KEY); 542 sharedState.remove(PASSWORD_KEY); 543 } 544 } 545 } 546 | Popular Tags |