- /*
- * @(#)JMXPluggableAuthenticator.java 1.4 04/05/27
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package com.sun.jmx.remote.security;
-
- import java.io.IOException;
- import java.security.AccessController;
- import java.security.Principal;
- import java.security.PrivilegedAction;
- import java.security.PrivilegedActionException;
- import java.security.PrivilegedExceptionAction;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Properties;
- import javax.management.remote.JMXPrincipal;
- import javax.management.remote.JMXAuthenticator;
- import javax.security.auth.AuthPermission;
- import javax.security.auth.Subject;
- import javax.security.auth.callback.*;
- import javax.security.auth.login.AppConfigurationEntry;
- import javax.security.auth.login.Configuration;
- import javax.security.auth.login.LoginContext;
- import javax.security.auth.login.LoginException;
- import javax.security.auth.spi.LoginModule;
- import com.sun.jmx.remote.util.ClassLogger;
- import com.sun.jmx.remote.util.EnvHelp;
-
- /**
- * <p>This class represents a
- * <a href="{@docRoot}/../guide/security/jaas/JAASRefGuide.html">JAAS</a>
- * based implementation of the {@link JMXAuthenticator} interface.</p>
- *
- * <p>Authentication is performed by passing the supplied user's credentials
- * to one or more authentication mechanisms ({@link LoginModule}) for
- * verification. An authentication mechanism acquires the user's credentials
- * by calling {@link NameCallback} and/or {@link PasswordCallback}.
- * If authentication is successful then an authenticated {@link Subject}
- * filled in with a {@link Principal} is returned. Authorization checks
- * will then be performed based on this <code>Subject</code>.</p>
- *
- * <p>By default, a single file-based authentication mechanism
- * {@link FileLoginModule} is configured (<code>FileLoginConfig</code>).</p>
- *
- * <p>To override the default configuration use the
- * <code>com.sun.management.jmxremote.login.config</code> management property
- * described in the JRE/lib/management/management.properties file.
- * Set this property to the name of a JAAS configuration entry and ensure that
- * the entry is loaded by the installed {@link Configuration}. In addition,
- * ensure that the authentication mechanisms specified in the entry acquire
- * the user's credentials by calling {@link NameCallback} and
- * {@link PasswordCallback} and that they return a {@link Subject} filled-in
- * with a {@link Principal}, for those users that are successfully
- * authenticated.</p>
- */
- public final class JMXPluggableAuthenticator implements JMXAuthenticator {
-
- /**
- * Creates an instance of <code>JMXPluggableAuthenticator</code>
- * and initializes it with a {@link LoginContext}.
- *
- * @param env the environment containing configuration properties for the
- * authenticator. Can be null, which is equivalent to an empty
- * Map.
- * @exception SecurityException if the authentication mechanism cannot be
- * initialized.
- */
- public JMXPluggableAuthenticator(Map env) {
-
- String loginConfigName = null;
- String passwordFile = null;
-
- if (env != null) {
- loginConfigName = (String) env.get(LOGIN_CONFIG_PROP);
- passwordFile = (String) env.get(PASSWORD_FILE_PROP);
- }
-
- try {
-
- if (loginConfigName != null) {
- // use the supplied JAAS login configuration
- loginContext =
- new LoginContext(loginConfigName, new JMXCallbackHandler());
-
- } else {
- // use the default JAAS login configuration (file-based)
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(
- new AuthPermission("createLoginContext." +
- LOGIN_CONFIG_NAME));
- }
-
- final String pf = passwordFile;
- try {
- loginContext = (LoginContext) AccessController.doPrivileged(
- new PrivilegedExceptionAction() {
- public Object run() throws LoginException {
- return new LoginContext(
- LOGIN_CONFIG_NAME,
- null,
- new JMXCallbackHandler(),
- new FileLoginConfig(pf));
- }
- });
- } catch (PrivilegedActionException pae) {
- throw (LoginException) pae.getException();
- }
- }
-
- } catch (LoginException le) {
- authenticationFailure("authenticate", le);
-
- } catch (SecurityException se) {
- authenticationFailure("authenticate", se);
- }
- }
-
- /**
- * Authenticate the <code>MBeanServerConnection</code> client
- * with the given client credentials.
- *
- * @param credentials the user-defined credentials to be passed in
- * to the server in order to authenticate the user before creating
- * the <code>MBeanServerConnection</code>. This parameter must
- * be a two-element <code>String[]</code> containing the client's
- * username and password in that order.
- *
- * @return the authenticated subject containing a
- * <code>JMXPrincipal(username)</code>.
- *
- * @exception SecurityException if the server cannot authenticate the user
- * with the provided credentials.
- */
- public Subject authenticate(Object credentials) {
- // Verify that credentials is of type String[].
- //
- if (!(credentials instanceof String[])) {
- // Special case for null so we get a more informative message
- if (credentials == null)
- authenticationFailure("authenticate", "Credentials required");
-
- final String message =
- "Credentials should be String[] instead of " +
- credentials.getClass().getName();
- authenticationFailure("authenticate", message);
- }
- // Verify that the array contains two elements.
- //
- final String[] aCredentials = (String[]) credentials;
- if (aCredentials.length != 2) {
- final String message =
- "Credentials should have 2 elements not " +
- aCredentials.length;
- authenticationFailure("authenticate", message);
- }
- // Verify that username exists and the associated
- // password matches the one supplied by the client.
- //
- username = (String) aCredentials[0];
- password = (String) aCredentials[1];
- if (username == null || password == null) {
- final String message = "Username or password is null";
- authenticationFailure("authenticate", message);
- }
-
- // Perform authentication
- try {
- loginContext.login();
- final Subject subject = loginContext.getSubject();
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- subject.setReadOnly();
- return null;
- }
- });
-
- return subject;
-
- } catch (LoginException le) {
- authenticationFailure("authenticate", le);
- }
- return null;
- }
-
- private static void authenticationFailure(String method, String message)
- throws SecurityException {
- final String msg = "Authentication failed! " + message;
- final SecurityException e = new SecurityException(msg);
- logException(method, msg, e);
- throw e;
- }
-
- private static void authenticationFailure(String method,
- Exception exception)
- throws SecurityException {
- String msg;
- SecurityException se;
- if (exception instanceof SecurityException) {
- msg = exception.getMessage();
- se = (SecurityException) exception;
- } else {
- msg = "Authentication failed! " + exception.getMessage();
- final SecurityException e = new SecurityException(msg);
- EnvHelp.initCause(e, exception);
- se = e;
- }
- logException(method, msg, se);
- throw se;
- }
-
- private static void logException(String method,
- String message,
- Exception e) {
- if (logger.traceOn()) {
- logger.trace(method, message);
- }
- if (logger.debugOn()) {
- logger.debug(method, e);
- }
- }
-
- private LoginContext loginContext;
- private String username;
- private String password;
- private static final String LOGIN_CONFIG_PROP =
- "jmx.remote.x.login.config";
- private static final String LOGIN_CONFIG_NAME = "JMXPluggableAuthenticator";
- private static final String PASSWORD_FILE_PROP =
- "jmx.remote.x.password.file";
- private static final ClassLogger logger =
- new ClassLogger("javax.management.remote.misc", LOGIN_CONFIG_NAME);
-
- /**
- * This callback handler supplies the username and password (which was
- * originally supplied by the JMX user) to the JAAS login module performing
- * the authentication. No interactive user prompting is required because the
- * credentials are already available to this class (via its enclosing class).
- */
- private final class JMXCallbackHandler implements CallbackHandler {
-
- /**
- * Sets the username and password in the appropriate Callback object.
- */
- public void handle(Callback[] callbacks)
- throws IOException, UnsupportedCallbackException {
-
- for (int i = 0; i < callbacks.length; i++) {
- if (callbacks[i] instanceof NameCallback) {
- ((NameCallback)callbacks[i]).setName(username);
-
- } else if (callbacks[i] instanceof PasswordCallback) {
- ((PasswordCallback)callbacks[i])
- .setPassword(password.toCharArray());
-
- } else {
- throw new UnsupportedCallbackException
- (callbacks[i], "Unrecognized Callback");
- }
- }
- }
- }
-
- /**
- * This class defines the JAAS configuration for file-based authentication.
- * It is equivalent to the following textual configuration entry:
- * <pre>
- * JMXPluggableAuthenticator {
- * com.sun.jmx.remote.security.FileLoginModule required;
- * };
- * </pre>
- */
- private static class FileLoginConfig extends Configuration {
-
- // The JAAS configuration for file-based authentication
- private static AppConfigurationEntry[] entries;
-
- // The classname of the login module for file-based authentication
- private static final String FILE_LOGIN_MODULE =
- FileLoginModule.class.getName();
-
- // The option that identifies the password file to use
- private static final String PASSWORD_FILE_OPTION = "passwordFile";
-
- /**
- * Creates an instance of <code>FileLoginConfig</code>
- *
- * @param passwordFile A filepath that identifies the password file to use.
- * If null then the default password file is used.
- */
- public FileLoginConfig(String passwordFile) {
-
- Map options;
- if (passwordFile != null) {
- options = new HashMap(1);
- options.put(PASSWORD_FILE_OPTION, passwordFile);
- } else {
- options = Collections.EMPTY_MAP;
- }
-
- entries = new AppConfigurationEntry[] {
- new AppConfigurationEntry(FILE_LOGIN_MODULE,
- AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
- options)
- };
- }
-
- /**
- * Gets the JAAS configuration for file-based authentication
- */
- public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
-
- return name.equals(LOGIN_CONFIG_NAME) ? entries : null;
- }
-
- /**
- * Refreshes the configuration.
- */
- public void refresh() {
- // the configuration is fixed
- }
- }
-
- }