blob: cdb90398f3092c8198f4f5033891df4669a9ba3a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.security.auth.module;
27
28import java.util.*;
29import java.io.IOException;
30import javax.security.auth.*;
31import javax.security.auth.callback.*;
32import javax.security.auth.login.*;
33import javax.security.auth.spi.*;
34import com.sun.security.auth.UnixPrincipal;
35import com.sun.security.auth.UnixNumericUserPrincipal;
36import com.sun.security.auth.UnixNumericGroupPrincipal;
37
38/**
39 * <p> This <code>LoginModule</code> imports a user's Unix
40 * <code>Principal</code> information (<code>UnixPrincipal</code>,
41 * <code>UnixNumericUserPrincipal</code>,
42 * and <code>UnixNumericGroupPrincipal</code>)
43 * and associates them with the current <code>Subject</code>.
44 *
45 * <p> This LoginModule recognizes the debug option.
46 * If set to true in the login Configuration,
47 * debug messages will be output to the output stream, System.out.
48 *
49 */
50public class UnixLoginModule implements LoginModule {
51
52 // initial state
53 private Subject subject;
54 private CallbackHandler callbackHandler;
55 private Map<String, ?> sharedState;
56 private Map<String, ?> options;
57
58 // configurable option
59 private boolean debug = true;
60
61 // UnixSystem to retrieve underlying system info
62 private UnixSystem ss;
63
64 // the authentication status
65 private boolean succeeded = false;
66 private boolean commitSucceeded = false;
67
68 // Underlying system info
69 private UnixPrincipal userPrincipal;
70 private UnixNumericUserPrincipal UIDPrincipal;
71 private UnixNumericGroupPrincipal GIDPrincipal;
72 private LinkedList<UnixNumericGroupPrincipal> supplementaryGroups =
73 new LinkedList<UnixNumericGroupPrincipal>();
74
75 /**
76 * Initialize this <code>LoginModule</code>.
77 *
78 * <p>
79 *
80 * @param subject the <code>Subject</code> to be authenticated. <p>
81 *
82 * @param callbackHandler a <code>CallbackHandler</code> for communicating
83 * with the end user (prompting for usernames and
84 * passwords, for example). <p>
85 *
86 * @param sharedState shared <code>LoginModule</code> state. <p>
87 *
88 * @param options options specified in the login
89 * <code>Configuration</code> for this particular
90 * <code>LoginModule</code>.
91 */
92 public void initialize(Subject subject, CallbackHandler callbackHandler,
93 Map<String,?> sharedState,
94 Map<String,?> options) {
95
96 this.subject = subject;
97 this.callbackHandler = callbackHandler;
98 this.sharedState = sharedState;
99 this.options = options;
100
101 // initialize any configured options
102 debug = "true".equalsIgnoreCase((String)options.get("debug"));
103 }
104
105 /**
106 * Authenticate the user (first phase).
107 *
108 * <p> The implementation of this method attempts to retrieve the user's
109 * Unix <code>Subject</code> information by making a native Unix
110 * system call.
111 *
112 * <p>
113 *
114 * @exception FailedLoginException if attempts to retrieve the underlying
115 * system information fail.
116 *
117 * @return true in all cases (this <code>LoginModule</code>
118 * should not be ignored).
119 */
120 public boolean login() throws LoginException {
121
122 long[] unixGroups = null;
123
124 ss = new UnixSystem();
125
126 if (ss == null) {
127 succeeded = false;
128 throw new FailedLoginException
129 ("Failed in attempt to import " +
130 "the underlying system identity information");
131 } else {
132 userPrincipal = new UnixPrincipal(ss.getUsername());
133 UIDPrincipal = new UnixNumericUserPrincipal(ss.getUid());
134 GIDPrincipal = new UnixNumericGroupPrincipal(ss.getGid(), true);
135 if (ss.getGroups() != null && ss.getGroups().length > 0) {
136 unixGroups = ss.getGroups();
137 for (int i = 0; i < unixGroups.length; i++) {
138 UnixNumericGroupPrincipal ngp =
139 new UnixNumericGroupPrincipal
140 (unixGroups[i], false);
141 if (!ngp.getName().equals(GIDPrincipal.getName()))
142 supplementaryGroups.add(ngp);
143 }
144 }
145 if (debug) {
146 System.out.println("\t\t[UnixLoginModule]: " +
147 "succeeded importing info: ");
148 System.out.println("\t\t\tuid = " + ss.getUid());
149 System.out.println("\t\t\tgid = " + ss.getGid());
150 unixGroups = ss.getGroups();
151 for (int i = 0; i < unixGroups.length; i++) {
152 System.out.println("\t\t\tsupp gid = " + unixGroups[i]);
153 }
154 }
155 succeeded = true;
156 return true;
157 }
158 }
159
160 /**
161 * Commit the authentication (second phase).
162 *
163 * <p> This method is called if the LoginContext's
164 * overall authentication succeeded
165 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
166 * succeeded).
167 *
168 * <p> If this LoginModule's own authentication attempt
169 * succeeded (the importing of the Unix authentication information
170 * succeeded), then this method associates the Unix Principals
171 * with the <code>Subject</code> currently tied to the
172 * <code>LoginModule</code>. If this LoginModule's
173 * authentication attempted failed, then this method removes
174 * any state that was originally saved.
175 *
176 * <p>
177 *
178 * @exception LoginException if the commit fails
179 *
180 * @return true if this LoginModule's own login and commit attempts
181 * succeeded, or false otherwise.
182 */
183 public boolean commit() throws LoginException {
184 if (succeeded == false) {
185 if (debug) {
186 System.out.println("\t\t[UnixLoginModule]: " +
187 "did not add any Principals to Subject " +
188 "because own authentication failed.");
189 }
190 return false;
191 } else {
192 if (subject.isReadOnly()) {
193 throw new LoginException
194 ("commit Failed: Subject is Readonly");
195 }
196 if (!subject.getPrincipals().contains(userPrincipal))
197 subject.getPrincipals().add(userPrincipal);
198 if (!subject.getPrincipals().contains(UIDPrincipal))
199 subject.getPrincipals().add(UIDPrincipal);
200 if (!subject.getPrincipals().contains(GIDPrincipal))
201 subject.getPrincipals().add(GIDPrincipal);
202 for (int i = 0; i < supplementaryGroups.size(); i++) {
203 if (!subject.getPrincipals().contains
204 (supplementaryGroups.get(i)))
205 subject.getPrincipals().add(supplementaryGroups.get(i));
206 }
207
208 if (debug) {
209 System.out.println("\t\t[UnixLoginModule]: " +
210 "added UnixPrincipal,");
211 System.out.println("\t\t\t\tUnixNumericUserPrincipal,");
212 System.out.println("\t\t\t\tUnixNumericGroupPrincipal(s),");
213 System.out.println("\t\t\t to Subject");
214 }
215
216 commitSucceeded = true;
217 return true;
218 }
219 }
220
221 /**
222 * Abort the authentication (second phase).
223 *
224 * <p> This method is called if the LoginContext's
225 * overall authentication failed.
226 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
227 * did not succeed).
228 *
229 * <p> This method cleans up any state that was originally saved
230 * as part of the authentication attempt from the <code>login</code>
231 * and <code>commit</code> methods.
232 *
233 * <p>
234 *
235 * @exception LoginException if the abort fails
236 *
237 * @return false if this LoginModule's own login and/or commit attempts
238 * failed, and true otherwise.
239 */
240 public boolean abort() throws LoginException {
241 if (debug) {
242 System.out.println("\t\t[UnixLoginModule]: " +
243 "aborted authentication attempt");
244 }
245
246 if (succeeded == false) {
247 return false;
248 } else if (succeeded == true && commitSucceeded == false) {
249
250 // Clean out state
251 succeeded = false;
252 ss = null;
253 userPrincipal = null;
254 UIDPrincipal = null;
255 GIDPrincipal = null;
256 supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>();
257 } else {
258 // overall authentication succeeded and commit succeeded,
259 // but someone else's commit failed
260 logout();
261 }
262 return true;
263 }
264
265 /**
266 * Logout the user
267 *
268 * <p> This method removes the Principals associated
269 * with the <code>Subject</code>.
270 *
271 * <p>
272 *
273 * @exception LoginException if the logout fails
274 *
275 * @return true in all cases (this <code>LoginModule</code>
276 * should not be ignored).
277 */
278 public boolean logout() throws LoginException {
279
280 if (subject.isReadOnly()) {
281 throw new LoginException
282 ("logout Failed: Subject is Readonly");
283 }
284 // remove the added Principals from the Subject
285 subject.getPrincipals().remove(userPrincipal);
286 subject.getPrincipals().remove(UIDPrincipal);
287 subject.getPrincipals().remove(GIDPrincipal);
288 for (int i = 0; i < supplementaryGroups.size(); i++) {
289 subject.getPrincipals().remove(supplementaryGroups.get(i));
290 }
291
292 // clean out state
293 ss = null;
294 succeeded = false;
295 commitSucceeded = false;
296 userPrincipal = null;
297 UIDPrincipal = null;
298 GIDPrincipal = null;
299 supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>();
300
301 if (debug) {
302 System.out.println("\t\t[UnixLoginModule]: " +
303 "logged out Subject");
304 }
305 return true;
306 }
307}