/* * @(#)UnixLoginModule.java 1.7 04/05/05 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.sun.security.auth.module; import java.util.*; import java.io.IOException; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; import javax.security.auth.spi.*; import com.sun.security.auth.UnixPrincipal; import com.sun.security.auth.UnixNumericUserPrincipal; import com.sun.security.auth.UnixNumericGroupPrincipal; /** *

This LoginModule imports a user's Unix * Principal information (UnixPrincipal, * UnixNumericUserPrincipal, * and UnixNumericGroupPrincipal) * and associates them with the current Subject. * *

This LoginModule recognizes the debug option. * If set to true in the login Configuration, * debug messages will be output to the output stream, System.out. * * @version 1.7, 05/05/04 */ public class UnixLoginModule implements LoginModule { // initial state private Subject subject; private CallbackHandler callbackHandler; private Map sharedState; private Map options; // configurable option private boolean debug = true; // UnixSystem to retrieve underlying system info private UnixSystem ss; // the authentication status private boolean succeeded = false; private boolean commitSucceeded = false; // Underlying system info private UnixPrincipal userPrincipal; private UnixNumericUserPrincipal UIDPrincipal; private UnixNumericGroupPrincipal GIDPrincipal; private LinkedList supplementaryGroups = new LinkedList(); /** * Initialize this LoginModule. * *

* * @param subject the Subject to be authenticated.

* * @param callbackHandler a CallbackHandler for communicating * with the end user (prompting for usernames and * passwords, for example).

* * @param sharedState shared LoginModule state.

* * @param options options specified in the login * Configuration for this particular * LoginModule. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; // initialize any configured options debug = "true".equalsIgnoreCase((String)options.get("debug")); } /** * Authenticate the user (first phase). * *

The implementation of this method attempts to retrieve the user's * Unix Subject information by making a native Unix * system call. * *

* * @exception FailedLoginException if attempts to retrieve the underlying * system information fail. * * @return true in all cases (this LoginModule * should not be ignored). */ public boolean login() throws LoginException { long[] unixGroups = null; ss = new UnixSystem(); if (ss == null) { succeeded = false; throw new FailedLoginException ("Failed in attempt to import " + "the underlying system identity information"); } else { userPrincipal = new UnixPrincipal(ss.getUsername()); UIDPrincipal = new UnixNumericUserPrincipal(ss.getUid()); GIDPrincipal = new UnixNumericGroupPrincipal(ss.getGid(), true); if (ss.getGroups() != null && ss.getGroups().length > 0) unixGroups = ss.getGroups(); for (int i = 0; i < unixGroups.length; i++) { UnixNumericGroupPrincipal ngp = new UnixNumericGroupPrincipal (unixGroups[i], false); if (!ngp.getName().equals(GIDPrincipal.getName())) supplementaryGroups.add(ngp); } if (debug) { System.out.println("\t\t[UnixLoginModule]: " + "succeeded importing info: "); System.out.println("\t\t\tuid = " + ss.getUid()); System.out.println("\t\t\tgid = " + ss.getGid()); unixGroups = ss.getGroups(); for (int i = 0; i < unixGroups.length; i++) { System.out.println("\t\t\tsupp gid = " + unixGroups[i]); } } succeeded = true; return true; } } /** * Commit the authentication (second phase). * *

This method is called if the LoginContext's * overall authentication succeeded * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * succeeded). * *

If this LoginModule's own authentication attempt * succeeded (the importing of the Unix authentication information * succeeded), then this method associates the Unix Principals * with the Subject currently tied to the * LoginModule. If this LoginModule's * authentication attempted failed, then this method removes * any state that was originally saved. * *

* * @exception LoginException if the commit fails * * @return true if this LoginModule's own login and commit attempts * succeeded, or false otherwise. */ public boolean commit() throws LoginException { if (succeeded == false) { if (debug) { System.out.println("\t\t[UnixLoginModule]: " + "did not add any Principals to Subject " + "because own authentication failed."); } return false; } else { if (subject.isReadOnly()) { throw new LoginException ("commit Failed: Subject is Readonly"); } if (!subject.getPrincipals().contains(userPrincipal)) subject.getPrincipals().add(userPrincipal); if (!subject.getPrincipals().contains(UIDPrincipal)) subject.getPrincipals().add(UIDPrincipal); if (!subject.getPrincipals().contains(GIDPrincipal)) subject.getPrincipals().add(GIDPrincipal); for (int i = 0; i < supplementaryGroups.size(); i++) { if (!subject.getPrincipals().contains ((UnixNumericGroupPrincipal)supplementaryGroups.get(i))) subject.getPrincipals().add((UnixNumericGroupPrincipal) supplementaryGroups.get(i)); } if (debug) { System.out.println("\t\t[UnixLoginModule]: " + "added UnixPrincipal,"); System.out.println("\t\t\t\tUnixNumericUserPrincipal,"); System.out.println("\t\t\t\tUnixNumericGroupPrincipal(s),"); System.out.println("\t\t\t to Subject"); } commitSucceeded = true; return true; } } /** * Abort the authentication (second phase). * *

This method is called if the LoginContext's * overall authentication failed. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * did not succeed). * *

This method cleans up any state that was originally saved * as part of the authentication attempt from the login * and commit methods. * *

* * @exception LoginException if the abort fails * * @return false if this LoginModule's own login and/or commit attempts * failed, and true otherwise. */ public boolean abort() throws LoginException { if (debug) { System.out.println("\t\t[UnixLoginModule]: " + "aborted authentication attempt"); } if (succeeded == false) { return false; } else if (succeeded == true && commitSucceeded == false) { // Clean out state succeeded = false; ss = null; userPrincipal = null; UIDPrincipal = null; GIDPrincipal = null; supplementaryGroups = new LinkedList(); } else { // overall authentication succeeded and commit succeeded, // but someone else's commit failed logout(); } return true; } /** * Logout the user * *

This method removes the Principals associated * with the Subject. * *

* * @exception LoginException if the logout fails * * @return true in all cases (this LoginModule * should not be ignored). */ public boolean logout() throws LoginException { if (subject.isReadOnly()) { throw new LoginException ("logout Failed: Subject is Readonly"); } // remove the added Principals from the Subject subject.getPrincipals().remove(userPrincipal); subject.getPrincipals().remove(UIDPrincipal); subject.getPrincipals().remove(GIDPrincipal); for (int i = 0; i < supplementaryGroups.size(); i++) { subject.getPrincipals().remove ((UnixNumericGroupPrincipal)supplementaryGroups.get(i)); } // clean out state ss = null; succeeded = false; commitSucceeded = false; userPrincipal = null; UIDPrincipal = null; GIDPrincipal = null; supplementaryGroups = new LinkedList(); if (debug) { System.out.println("\t\t[UnixLoginModule]: " + "logged out Subject"); } return true; } }