KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > remote > security > JMXPluggableAuthenticator


1 /*
2  * @(#)JMXPluggableAuthenticator.java 1.4 04/05/27
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.jmx.remote.security;
9
10 import java.io.IOException JavaDoc;
11 import java.security.AccessController JavaDoc;
12 import java.security.Principal JavaDoc;
13 import java.security.PrivilegedAction JavaDoc;
14 import java.security.PrivilegedActionException JavaDoc;
15 import java.security.PrivilegedExceptionAction JavaDoc;
16 import java.util.Collections JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.Properties JavaDoc;
20 import javax.management.remote.JMXPrincipal JavaDoc;
21 import javax.management.remote.JMXAuthenticator JavaDoc;
22 import javax.security.auth.AuthPermission JavaDoc;
23 import javax.security.auth.Subject JavaDoc;
24 import javax.security.auth.callback.*;
25 import javax.security.auth.login.AppConfigurationEntry JavaDoc;
26 import javax.security.auth.login.Configuration JavaDoc;
27 import javax.security.auth.login.LoginContext JavaDoc;
28 import javax.security.auth.login.LoginException JavaDoc;
29 import javax.security.auth.spi.LoginModule JavaDoc;
30 import com.sun.jmx.remote.util.ClassLogger;
31 import com.sun.jmx.remote.util.EnvHelp;
32
33 /**
34  * <p>This class represents a
35  * <a HREF="{@docRoot}/../guide/security/jaas/JAASRefGuide.html">JAAS</a>
36  * based implementation of the {@link JMXAuthenticator} interface.</p>
37  *
38  * <p>Authentication is performed by passing the supplied user's credentials
39  * to one or more authentication mechanisms ({@link LoginModule}) for
40  * verification. An authentication mechanism acquires the user's credentials
41  * by calling {@link NameCallback} and/or {@link PasswordCallback}.
42  * If authentication is successful then an authenticated {@link Subject}
43  * filled in with a {@link Principal} is returned. Authorization checks
44  * will then be performed based on this <code>Subject</code>.</p>
45  *
46  * <p>By default, a single file-based authentication mechanism
47  * {@link FileLoginModule} is configured (<code>FileLoginConfig</code>).</p>
48  *
49  * <p>To override the default configuration use the
50  * <code>com.sun.management.jmxremote.login.config</code> management property
51  * described in the JRE/lib/management/management.properties file.
52  * Set this property to the name of a JAAS configuration entry and ensure that
53  * the entry is loaded by the installed {@link Configuration}. In addition,
54  * ensure that the authentication mechanisms specified in the entry acquire
55  * the user's credentials by calling {@link NameCallback} and
56  * {@link PasswordCallback} and that they return a {@link Subject} filled-in
57  * with a {@link Principal}, for those users that are successfully
58  * authenticated.</p>
59  */

60 public final class JMXPluggableAuthenticator implements JMXAuthenticator JavaDoc {
61
62     /**
63      * Creates an instance of <code>JMXPluggableAuthenticator</code>
64      * and initializes it with a {@link LoginContext}.
65      *
66      * @param env the environment containing configuration properties for the
67      * authenticator. Can be null, which is equivalent to an empty
68      * Map.
69      * @exception SecurityException if the authentication mechanism cannot be
70      * initialized.
71      */

72     public JMXPluggableAuthenticator(Map JavaDoc env) {
73
74     String JavaDoc loginConfigName = null;
75     String JavaDoc passwordFile = null;
76
77     if (env != null) {
78         loginConfigName = (String JavaDoc) env.get(LOGIN_CONFIG_PROP);
79         passwordFile = (String JavaDoc) env.get(PASSWORD_FILE_PROP);
80     }
81
82     try {
83
84         if (loginConfigName != null) {
85             // use the supplied JAAS login configuration
86
loginContext =
87             new LoginContext JavaDoc(loginConfigName, new JMXCallbackHandler());
88
89         } else {
90             // use the default JAAS login configuration (file-based)
91
SecurityManager JavaDoc sm = System.getSecurityManager();
92                 if (sm != null) {
93                     sm.checkPermission(
94                             new AuthPermission JavaDoc("createLoginContext." +
95                                                LOGIN_CONFIG_NAME));
96                 }
97
98                 final String JavaDoc pf = passwordFile;
99                 try {
100                     loginContext = (LoginContext JavaDoc) AccessController.doPrivileged(
101                         new PrivilegedExceptionAction JavaDoc() {
102                             public Object JavaDoc run() throws LoginException JavaDoc {
103                                 return new LoginContext JavaDoc(
104                                                 LOGIN_CONFIG_NAME,
105                                                 null,
106                                                 new JMXCallbackHandler(),
107                                                 new FileLoginConfig(pf));
108                             }
109                         });
110                 } catch (PrivilegedActionException JavaDoc pae) {
111                     throw (LoginException JavaDoc) pae.getException();
112                 }
113             }
114
115     } catch (LoginException JavaDoc le) {
116         authenticationFailure("authenticate", le);
117
118     } catch (SecurityException JavaDoc se) {
119         authenticationFailure("authenticate", se);
120     }
121     }
122
123     /**
124      * Authenticate the <code>MBeanServerConnection</code> client
125      * with the given client credentials.
126      *
127      * @param credentials the user-defined credentials to be passed in
128      * to the server in order to authenticate the user before creating
129      * the <code>MBeanServerConnection</code>. This parameter must
130      * be a two-element <code>String[]</code> containing the client's
131      * username and password in that order.
132      *
133      * @return the authenticated subject containing a
134      * <code>JMXPrincipal(username)</code>.
135      *
136      * @exception SecurityException if the server cannot authenticate the user
137      * with the provided credentials.
138      */

139     public Subject JavaDoc authenticate(Object JavaDoc credentials) {
140     // Verify that credentials is of type String[].
141
//
142
if (!(credentials instanceof String JavaDoc[])) {
143         // Special case for null so we get a more informative message
144
if (credentials == null)
145         authenticationFailure("authenticate", "Credentials required");
146
147         final String JavaDoc message =
148         "Credentials should be String[] instead of " +
149          credentials.getClass().getName();
150         authenticationFailure("authenticate", message);
151     }
152     // Verify that the array contains two elements.
153
//
154
final String JavaDoc[] aCredentials = (String JavaDoc[]) credentials;
155     if (aCredentials.length != 2) {
156         final String JavaDoc message =
157         "Credentials should have 2 elements not " +
158         aCredentials.length;
159         authenticationFailure("authenticate", message);
160     }
161     // Verify that username exists and the associated
162
// password matches the one supplied by the client.
163
//
164
username = (String JavaDoc) aCredentials[0];
165     password = (String JavaDoc) aCredentials[1];
166     if (username == null || password == null) {
167         final String JavaDoc message = "Username or password is null";
168         authenticationFailure("authenticate", message);
169     }
170
171     // Perform authentication
172
try {
173         loginContext.login();
174         final Subject JavaDoc subject = loginContext.getSubject();
175             AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
176                     public Object JavaDoc run() {
177                         subject.setReadOnly();
178                         return null;
179                     }
180                 });
181
182         return subject;
183
184     } catch (LoginException JavaDoc le) {
185         authenticationFailure("authenticate", le);
186     }
187     return null;
188     }
189
190     private static void authenticationFailure(String JavaDoc method, String JavaDoc message)
191     throws SecurityException JavaDoc {
192     final String JavaDoc msg = "Authentication failed! " + message;
193     final SecurityException JavaDoc e = new SecurityException JavaDoc(msg);
194     logException(method, msg, e);
195     throw e;
196     }
197
198     private static void authenticationFailure(String JavaDoc method,
199                                               Exception JavaDoc exception)
200         throws SecurityException JavaDoc {
201         String JavaDoc msg;
202         SecurityException JavaDoc se;
203         if (exception instanceof SecurityException JavaDoc) {
204             msg = exception.getMessage();
205             se = (SecurityException JavaDoc) exception;
206         } else {
207             msg = "Authentication failed! " + exception.getMessage();
208             final SecurityException JavaDoc e = new SecurityException JavaDoc(msg);
209             EnvHelp.initCause(e, exception);
210             se = e;
211         }
212         logException(method, msg, se);
213         throw se;
214     }
215
216     private static void logException(String JavaDoc method,
217                      String JavaDoc message,
218                      Exception JavaDoc e) {
219     if (logger.traceOn()) {
220         logger.trace(method, message);
221     }
222     if (logger.debugOn()) {
223         logger.debug(method, e);
224     }
225     }
226
227     private LoginContext JavaDoc loginContext;
228     private String JavaDoc username;
229     private String JavaDoc password;
230     private static final String JavaDoc LOGIN_CONFIG_PROP =
231     "jmx.remote.x.login.config";
232     private static final String JavaDoc LOGIN_CONFIG_NAME = "JMXPluggableAuthenticator";
233     private static final String JavaDoc PASSWORD_FILE_PROP =
234     "jmx.remote.x.password.file";
235     private static final ClassLogger logger =
236     new ClassLogger("javax.management.remote.misc", LOGIN_CONFIG_NAME);
237
238 /**
239  * This callback handler supplies the username and password (which was
240  * originally supplied by the JMX user) to the JAAS login module performing
241  * the authentication. No interactive user prompting is required because the
242  * credentials are already available to this class (via its enclosing class).
243  */

244 private final class JMXCallbackHandler implements CallbackHandler {
245
246     /**
247      * Sets the username and password in the appropriate Callback object.
248      */

249     public void handle(Callback[] callbacks)
250     throws IOException JavaDoc, UnsupportedCallbackException {
251
252     for (int i = 0; i < callbacks.length; i++) {
253             if (callbacks[i] instanceof NameCallback) {
254         ((NameCallback)callbacks[i]).setName(username);
255
256         } else if (callbacks[i] instanceof PasswordCallback) {
257         ((PasswordCallback)callbacks[i])
258             .setPassword(password.toCharArray());
259
260         } else {
261         throw new UnsupportedCallbackException
262             (callbacks[i], "Unrecognized Callback");
263         }
264     }
265     }
266 }
267
268 /**
269  * This class defines the JAAS configuration for file-based authentication.
270  * It is equivalent to the following textual configuration entry:
271  * <pre>
272  * JMXPluggableAuthenticator {
273  * com.sun.jmx.remote.security.FileLoginModule required;
274  * };
275  * </pre>
276  */

277 private static class FileLoginConfig extends Configuration JavaDoc {
278
279     // The JAAS configuration for file-based authentication
280
private static AppConfigurationEntry JavaDoc[] entries;
281
282     // The classname of the login module for file-based authentication
283
private static final String JavaDoc FILE_LOGIN_MODULE =
284     FileLoginModule.class.getName();
285
286     // The option that identifies the password file to use
287
private static final String JavaDoc PASSWORD_FILE_OPTION = "passwordFile";
288
289     /**
290      * Creates an instance of <code>FileLoginConfig</code>
291      *
292      * @param passwordFile A filepath that identifies the password file to use.
293      * If null then the default password file is used.
294      */

295     public FileLoginConfig(String JavaDoc passwordFile) {
296
297     Map JavaDoc options;
298     if (passwordFile != null) {
299         options = new HashMap JavaDoc(1);
300         options.put(PASSWORD_FILE_OPTION, passwordFile);
301     } else {
302         options = Collections.EMPTY_MAP;
303     }
304
305     entries = new AppConfigurationEntry JavaDoc[] {
306         new AppConfigurationEntry JavaDoc(FILE_LOGIN_MODULE,
307         AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
308             options)
309     };
310     }
311
312     /**
313      * Gets the JAAS configuration for file-based authentication
314      */

315     public AppConfigurationEntry JavaDoc[] getAppConfigurationEntry(String JavaDoc name) {
316
317     return name.equals(LOGIN_CONFIG_NAME) ? entries : null;
318     }
319
320     /**
321      * Refreshes the configuration.
322      */

323     public void refresh() {
324     // the configuration is fixed
325
}
326 }
327
328 }
329
Popular Tags