1 /*
2 * JBoss, the OpenSource WebOS
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7
8 package org.jboss.security;
9
10
11 import java.util.Map;
12 import java.security.Principal;
13 import javax.security.auth.Subject;
14 import javax.security.auth.callback.Callback;
15 import javax.security.auth.callback.CallbackHandler;
16 import javax.security.auth.callback.NameCallback;
17 import javax.security.auth.callback.PasswordCallback;
18 import javax.security.auth.callback.UnsupportedCallbackException;
19 import javax.security.auth.login.LoginException;
20 import javax.security.auth.spi.LoginModule;
21
22 /** A simple implementation of LoginModule for use by JBoss clients for
23 the establishment of the caller identity and credentials. This simply sets
24 the SecurityAssociation principal to the value of the NameCallback
25 filled in by the CallbackHandler, and the SecurityAssociation credential
26 to the value of the PasswordCallback filled in by the CallbackHandler.
27
28 It has the following options:
29 <ul>
30 <li>multi-threaded=[true|false]
31 When the multi-threaded option is set to true, the SecurityAssociation.setServer()
32 so that each login thread has its own principal and credential storage.
33 <li>password-stacking=tryFirstPass|useFirstPass
34 When password-stacking option is set, this module first looks for a shared
35 username and password using "javax.security.auth.login.name" and
36 "javax.security.auth.login.password" respectively. This allows a module configured
37 prior to this one to establish a valid username and password that should be passed
38 to JBoss.
39 </ul>
40
41 @author <a href="mailto:on@ibis.odessa.ua">Oleg Nitz</a>
42 @author Scott.Stark@jboss.org
43 */
44 public class ClientLoginModule implements LoginModule
45 {
46 private Subject subject;
47 private CallbackHandler callbackHandler;
48 /** Shared state between login modules */
49 private Map sharedState;
50 /** Flag indicating if the shared password should be used */
51 private boolean useFirstPass;
52
53 /**
54 * Initialize this LoginModule.
55 */
56 public void initialize(Subject subject, CallbackHandler callbackHandler,
57 Map sharedState, Map options)
58 {
59 this.subject = subject;
60 this.callbackHandler = callbackHandler;
61 this.sharedState = sharedState;
62 // Check for multi-threaded option
63 String mt = (String) options.get("multi-threaded");
64 if( mt != null && Boolean.valueOf(mt).booleanValue() == true )
65 { /* Turn on the server mode which uses thread local storage for
66 the principal information.
67 */
68 SecurityAssociation.setServer();
69 }
70
71 /* Check for password sharing options. Any non-null value for
72 password_stacking sets useFirstPass as this module has no way to
73 validate any shared password.
74 */
75 String passwordStacking = (String) options.get("password-stacking");
76 useFirstPass = passwordStacking != null;
77 }
78
79 /**
80 * Method to authenticate a Subject (phase 1).
81 */
82 public boolean login() throws LoginException
83 {
84 // If useFirstPass is true, look for the shared password
85 if( useFirstPass == true )
86 {
87 try
88 {
89 Object user = sharedState.get("javax.security.auth.login.name");
90 Principal principal;
91 if( (user instanceof Principal) == false )
92 {
93 String username = user != null ? user.toString() : "";
94 principal = new SimplePrincipal(username);
95 }
96 else
97 {
98 principal = (Principal) user;
99 }
100 Object credential = sharedState.get("javax.security.auth.login.password");
101 SecurityAssociation.setPrincipal(principal);
102 SecurityAssociation.setCredential(credential);
103 SecurityAssociation.setSubject(subject);
104 return true;
105 }
106 catch(Exception e)
107 { // Dump the exception and continue
108 e.printStackTrace();
109 }
110 }
111
112 /* There is no password sharing or we are the first login module. Get
113 the username and password from the callback hander.
114 */
115 if (callbackHandler == null)
116 throw new LoginException("Error: no CallbackHandler available " +
117 "to garner authentication information from the user");
118
119 PasswordCallback pc = new PasswordCallback("Password: ", false);
120 NameCallback nc = new NameCallback("User name: ", "guest");
121 Callback[] callbacks = {nc, pc};
122 try
123 {
124 String username;
125 char[] password = null;
126 char[] tmpPassword;
127
128 callbackHandler.handle(callbacks);
129 username = nc.getName();
130 SecurityAssociation.setPrincipal(new SimplePrincipal(username));
131 tmpPassword = pc.getPassword();
132 if (tmpPassword != null)
133 {
134 password = new char[tmpPassword.length];
135 System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
136 pc.clearPassword();
137 }
138 SecurityAssociation.setCredential(password);
139 SecurityAssociation.setSubject(subject);
140 }
141 catch (java.io.IOException ioe)
142 {
143 throw new LoginException(ioe.toString());
144 }
145 catch (UnsupportedCallbackException uce)
146 {
147 throw new LoginException("Error: " + uce.getCallback().toString() +
148 " not available to garner authentication information " +
149 "from the user");
150 }
151 return true;
152 }
153
154 /**
155 * Method to commit the authentication process (phase 2).
156 */
157 public boolean commit() throws LoginException
158 {
159 return true;
160 }
161
162 /**
163 * Method to abort the authentication process (phase 2).
164 */
165 public boolean abort() throws LoginException
166 {
167 SecurityAssociation.clear();
168 return true;
169 }
170
171 public boolean logout() throws LoginException
172 {
173 SecurityAssociation.clear();
174 return true;
175 }
176 }