001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.security.auth.module;
027:
028: import java.util.*;
029: import java.io.IOException;
030: import javax.security.auth.*;
031: import javax.security.auth.callback.*;
032: import javax.security.auth.login.*;
033: import javax.security.auth.spi.*;
034: import com.sun.security.auth.UnixPrincipal;
035: import com.sun.security.auth.UnixNumericUserPrincipal;
036: import com.sun.security.auth.UnixNumericGroupPrincipal;
037:
038: /**
039: * <p> This <code>LoginModule</code> imports a user's Unix
040: * <code>Principal</code> information (<code>UnixPrincipal</code>,
041: * <code>UnixNumericUserPrincipal</code>,
042: * and <code>UnixNumericGroupPrincipal</code>)
043: * and associates them with the current <code>Subject</code>.
044: *
045: * <p> This LoginModule recognizes the debug option.
046: * If set to true in the login Configuration,
047: * debug messages will be output to the output stream, System.out.
048: *
049: * @version 1.16, 05/05/07
050: */
051: public class UnixLoginModule implements LoginModule {
052:
053: // initial state
054: private Subject subject;
055: private CallbackHandler callbackHandler;
056: private Map<String, ?> sharedState;
057: private Map<String, ?> options;
058:
059: // configurable option
060: private boolean debug = true;
061:
062: // UnixSystem to retrieve underlying system info
063: private UnixSystem ss;
064:
065: // the authentication status
066: private boolean succeeded = false;
067: private boolean commitSucceeded = false;
068:
069: // Underlying system info
070: private UnixPrincipal userPrincipal;
071: private UnixNumericUserPrincipal UIDPrincipal;
072: private UnixNumericGroupPrincipal GIDPrincipal;
073: private LinkedList<UnixNumericGroupPrincipal> supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>();
074:
075: /**
076: * Initialize this <code>LoginModule</code>.
077: *
078: * <p>
079: *
080: * @param subject the <code>Subject</code> to be authenticated. <p>
081: *
082: * @param callbackHandler a <code>CallbackHandler</code> for communicating
083: * with the end user (prompting for usernames and
084: * passwords, for example). <p>
085: *
086: * @param sharedState shared <code>LoginModule</code> state. <p>
087: *
088: * @param options options specified in the login
089: * <code>Configuration</code> for this particular
090: * <code>LoginModule</code>.
091: */
092: public void initialize(Subject subject,
093: CallbackHandler callbackHandler,
094: Map<String, ?> sharedState, Map<String, ?> options) {
095:
096: this .subject = subject;
097: this .callbackHandler = callbackHandler;
098: this .sharedState = sharedState;
099: 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(),
135: true);
136: if (ss.getGroups() != null && ss.getGroups().length > 0) {
137: unixGroups = ss.getGroups();
138: for (int i = 0; i < unixGroups.length; i++) {
139: UnixNumericGroupPrincipal ngp = 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 = "
153: + unixGroups[i]);
154: }
155: }
156: succeeded = true;
157: return true;
158: }
159: }
160:
161: /**
162: * Commit the authentication (second phase).
163: *
164: * <p> This method is called if the LoginContext's
165: * overall authentication succeeded
166: * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
167: * succeeded).
168: *
169: * <p> If this LoginModule's own authentication attempt
170: * succeeded (the importing of the Unix authentication information
171: * succeeded), then this method associates the Unix Principals
172: * with the <code>Subject</code> currently tied to the
173: * <code>LoginModule</code>. If this LoginModule's
174: * authentication attempted failed, then this method removes
175: * any state that was originally saved.
176: *
177: * <p>
178: *
179: * @exception LoginException if the commit fails
180: *
181: * @return true if this LoginModule's own login and commit attempts
182: * succeeded, or false otherwise.
183: */
184: public boolean commit() throws LoginException {
185: if (succeeded == false) {
186: if (debug) {
187: System.out.println("\t\t[UnixLoginModule]: "
188: + "did not add any Principals to Subject "
189: + "because own authentication failed.");
190: }
191: return false;
192: } else {
193: if (subject.isReadOnly()) {
194: throw new LoginException(
195: "commit Failed: Subject is Readonly");
196: }
197: if (!subject.getPrincipals().contains(userPrincipal))
198: subject.getPrincipals().add(userPrincipal);
199: if (!subject.getPrincipals().contains(UIDPrincipal))
200: subject.getPrincipals().add(UIDPrincipal);
201: if (!subject.getPrincipals().contains(GIDPrincipal))
202: subject.getPrincipals().add(GIDPrincipal);
203: for (int i = 0; i < supplementaryGroups.size(); i++) {
204: if (!subject.getPrincipals().contains(
205: supplementaryGroups.get(i)))
206: subject.getPrincipals().add(
207: supplementaryGroups.get(i));
208: }
209:
210: if (debug) {
211: System.out.println("\t\t[UnixLoginModule]: "
212: + "added UnixPrincipal,");
213: System.out.println("\t\t\t\tUnixNumericUserPrincipal,");
214: System.out
215: .println("\t\t\t\tUnixNumericGroupPrincipal(s),");
216: System.out.println("\t\t\t to Subject");
217: }
218:
219: commitSucceeded = true;
220: return true;
221: }
222: }
223:
224: /**
225: * Abort the authentication (second phase).
226: *
227: * <p> This method is called if the LoginContext's
228: * overall authentication failed.
229: * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
230: * did not succeed).
231: *
232: * <p> This method cleans up any state that was originally saved
233: * as part of the authentication attempt from the <code>login</code>
234: * and <code>commit</code> methods.
235: *
236: * <p>
237: *
238: * @exception LoginException if the abort fails
239: *
240: * @return false if this LoginModule's own login and/or commit attempts
241: * failed, and true otherwise.
242: */
243: public boolean abort() throws LoginException {
244: if (debug) {
245: System.out.println("\t\t[UnixLoginModule]: "
246: + "aborted authentication attempt");
247: }
248:
249: if (succeeded == false) {
250: return false;
251: } else if (succeeded == true && commitSucceeded == false) {
252:
253: // Clean out state
254: succeeded = false;
255: ss = null;
256: userPrincipal = null;
257: UIDPrincipal = null;
258: GIDPrincipal = null;
259: supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>();
260: } else {
261: // overall authentication succeeded and commit succeeded,
262: // but someone else's commit failed
263: logout();
264: }
265: return true;
266: }
267:
268: /**
269: * Logout the user
270: *
271: * <p> This method removes the Principals associated
272: * with the <code>Subject</code>.
273: *
274: * <p>
275: *
276: * @exception LoginException if the logout fails
277: *
278: * @return true in all cases (this <code>LoginModule</code>
279: * should not be ignored).
280: */
281: public boolean logout() throws LoginException {
282:
283: if (subject.isReadOnly()) {
284: throw new LoginException(
285: "logout Failed: Subject is Readonly");
286: }
287: // remove the added Principals from the Subject
288: subject.getPrincipals().remove(userPrincipal);
289: subject.getPrincipals().remove(UIDPrincipal);
290: subject.getPrincipals().remove(GIDPrincipal);
291: for (int i = 0; i < supplementaryGroups.size(); i++) {
292: subject.getPrincipals().remove(supplementaryGroups.get(i));
293: }
294:
295: // clean out state
296: ss = null;
297: succeeded = false;
298: commitSucceeded = false;
299: userPrincipal = null;
300: UIDPrincipal = null;
301: GIDPrincipal = null;
302: supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>();
303:
304: if (debug) {
305: System.out.println("\t\t[UnixLoginModule]: "
306: + "logged out Subject");
307: }
308: return true;
309: }
310: }
|