1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.danube.auth.jaas.memory;
14
15 import java.io.IOException;
16 import java.security.NoSuchAlgorithmException;
17 import java.security.Principal;
18 import java.util.Collection;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Properties;
23
24 import javax.security.auth.DestroyFailedException;
25 import javax.security.auth.Destroyable;
26 import javax.security.auth.Subject;
27 import javax.security.auth.callback.Callback;
28 import javax.security.auth.callback.CallbackHandler;
29 import javax.security.auth.callback.ConfirmationCallback;
30 import javax.security.auth.callback.NameCallback;
31 import javax.security.auth.callback.PasswordCallback;
32 import javax.security.auth.callback.TextOutputCallback;
33 import javax.security.auth.callback.UnsupportedCallbackException;
34 import javax.security.auth.login.LoginException;
35 import javax.security.auth.spi.LoginModule;
36 import javax.security.auth.x500.X500PrivateCredential;
37
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47 public class PropertiesLoginModule implements LoginModule {
48
49
50 protected Logger logger = LoggerFactory.getLogger(getClass());
51
52
53 protected Subject subject;
54
55
56 protected CallbackHandler callbackHandler;
57
58 protected Map<String, ?> sharedState;
59
60 protected Map<String, ?> options;
61
62
63
64 private char[] userPassword;
65
66
67 protected String username;
68
69
70
71 protected static final int UNINITIALIZED = 0;
72
73
74 protected static final int INITIALIZED = 1;
75
76
77 protected static final int AUTHENTICATED = 2;
78
79
80 protected static final int LOGGED_IN = 3;
81
82
83 protected int status = UNINITIALIZED;
84
85
86 private Collection<Principal> principals;
87
88
89 private java.security.cert.CertPath publicCredentials = null;
90
91
92 private X500PrivateCredential privateCredential;
93
94
95 private Properties properties;
96
97
98
99
100 public PropertiesLoginModule() {
101 }
102
103 public void setProperties(Properties properties) {
104 this.properties = properties;
105 }
106
107 public Properties getProperties() {
108 return properties;
109 }
110
111
112
113
114
115
116
117
118 public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
119 this.subject = subject;
120 this.callbackHandler = callbackHandler;
121 this.sharedState = sharedState;
122 this.options = options;
123
124 setProperties((Properties)options.get("properties"));
125
126 status = INITIALIZED;
127 }
128
129
130
131
132
133
134 public boolean login() throws LoginException {
135 if (status == LOGGED_IN) {
136 return true;
137 }
138 if ((status == INITIALIZED) || (status == AUTHENTICATED)) {
139 obtainAuthenticationDetails();
140 obtainCertificates();
141 status = AUTHENTICATED;
142 return true;
143 }
144
145 throw new LoginException("The login module is not initialized");
146 }
147
148
149
150
151
152 private void obtainAuthenticationDetails() throws LoginException {
153 TextOutputCallback bannerCallback = new TextOutputCallback(TextOutputCallback.INFORMATION, "Please login to keystore");
154 NameCallback aliasCallback = new NameCallback("Keystore alias: ");
155 PasswordCallback privateKeyPasswordCallback = new PasswordCallback("Password: ", false);
156 ConfirmationCallback confirmationCallback = new ConfirmationCallback(ConfirmationCallback.INFORMATION, ConfirmationCallback.OK_CANCEL_OPTION,
157 ConfirmationCallback.OK);
158 try {
159 callbackHandler.handle(
160 new Callback[]{
161 bannerCallback,
162 aliasCallback,
163 privateKeyPasswordCallback,
164 confirmationCallback}
165 );
166 } catch (IOException e) {
167 throw new LoginException("Exception while getting keystore alias and password: " + e);
168 } catch (UnsupportedCallbackException e) {
169 throw new LoginException("Error: " + e.getCallback().toString() + " is not available to retrieve authentication "
170 + " information from the user");
171 }
172
173 int confirmationResult = confirmationCallback.getSelectedIndex();
174 if (confirmationResult == ConfirmationCallback.CANCEL) {
175 throw new LoginException("Login cancelled");
176 }
177
178 username = aliasCallback.getName();
179
180 char[] tmpPassword = privateKeyPasswordCallback.getPassword();
181 userPassword = new char[tmpPassword.length];
182 System.arraycopy(tmpPassword, 0, userPassword, 0, tmpPassword.length);
183 for (int i = 0; i < tmpPassword.length; i++) {
184 tmpPassword[0] = ' ';
185 }
186 tmpPassword = null;
187 privateKeyPasswordCallback.clearPassword();
188 }
189
190
191
192
193
194 private void obtainCertificates() throws LoginException {
195
196
197 String line = properties.getProperty(username);
198 if (line == null) {
199 throw new LoginException("Username doesn't exist");
200 }
201 String[] roles = line.trim().split(",");
202 String pass = roles[0];
203
204 boolean checked = false;
205 if (pass.startsWith("{")) {
206 int i = pass.indexOf('}');
207 if (i > 0) {
208 checked = true;
209 String algorithm = pass.substring(1, i);
210
211 try {
212 String digest = PropertiesModuleService.generateHash(algorithm, new String(userPassword));
213
214 if (!pass.equals(new String(digest))) {
215 throw new LoginException("Password is wrong");
216 }
217
218 } catch (NoSuchAlgorithmException e) {
219 throw new LoginException(e.getMessage());
220 }
221 }
222 }
223 if (!checked) {
224 if (!pass.equals(new String(userPassword))) {
225 throw new LoginException("Password is wrong");
226 }
227 }
228
229
230 principals = new HashSet<Principal>();
231 for (int i = 1; i < roles.length; i++) {
232 principals.add(new PropertiesPrincipal(roles[i]));
233 }
234 }
235
236
237
238
239
240
241 public boolean commit() throws LoginException {
242 if (status == LOGGED_IN) {
243 return true;
244 }
245 if (status == AUTHENTICATED) {
246 if (subject.isReadOnly()) {
247 logoutImpl();
248 throw new LoginException("Subject is set readonly");
249 } else {
250 subject.getPrincipals().addAll(principals);
251 subject.getPublicCredentials().add(publicCredentials);
252 subject.getPrivateCredentials().add(privateCredential);
253 status = LOGGED_IN;
254 return true;
255 }
256 }
257 if (status == INITIALIZED) {
258 logoutImpl();
259 throw new LoginException("Authentication failed");
260 }
261
262 throw new LoginException("The login module is not initialized");
263 }
264
265
266
267
268
269 public boolean abort() throws LoginException {
270 if ((status == AUTHENTICATED) || (status == LOGGED_IN)) {
271 logoutImpl();
272 return true;
273 }
274
275 return false;
276 }
277
278
279
280
281
282 public boolean logout() throws LoginException {
283 if (status == LOGGED_IN) {
284 logoutImpl();
285 return true;
286 }
287
288 return false;
289 }
290
291
292
293
294
295 private void logoutImpl() throws LoginException {
296 for (int i = 0; i < userPassword.length; i++) {
297 userPassword[i] = '\0';
298 }
299 userPassword = null;
300
301 if (subject.isReadOnly()) {
302 principals = null;
303 publicCredentials = null;
304 status = INITIALIZED;
305
306 Iterator<Object> it = subject.getPrivateCredentials().iterator();
307 while (it.hasNext()) {
308 Object obj = it.next();
309 if (privateCredential.equals(obj)) {
310 privateCredential = null;
311 try {
312 ((Destroyable) obj).destroy();
313 break;
314 } catch (DestroyFailedException dfe) {
315 throw new LoginException("Unable to destroy private credential, " + obj.getClass().getName() + ": " + dfe.getMessage());
316 }
317 }
318 }
319
320 throw new LoginException("Unable to remove Principal (X500Principal) and public credential from read-only Subject");
321 }
322
323
324 if (principals != null) {
325 for (Principal p : principals) {
326 subject.getPrincipals().remove(p);
327 }
328 principals = null;
329 }
330 if (publicCredentials != null) {
331 subject.getPublicCredentials().remove(publicCredentials);
332 publicCredentials = null;
333 }
334 if (privateCredential != null) {
335 subject.getPrivateCredentials().remove(privateCredential);
336 privateCredential = null;
337 }
338 status = INITIALIZED;
339 }
340
341 private static class PropertiesPrincipal implements Principal {
342
343 private String name;
344
345 public PropertiesPrincipal(String name) {
346 this.name = name;
347 }
348
349 public String getName() {
350 return name;
351 }
352
353 }
354
355 }