1. /*
  2. * @(#)UnixLoginModule.java 1.5 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.security.auth.module;
  8. import java.util.*;
  9. import java.io.IOException;
  10. import javax.security.auth.*;
  11. import javax.security.auth.callback.*;
  12. import javax.security.auth.login.*;
  13. import javax.security.auth.spi.*;
  14. import com.sun.security.auth.UnixPrincipal;
  15. import com.sun.security.auth.UnixNumericUserPrincipal;
  16. import com.sun.security.auth.UnixNumericGroupPrincipal;
  17. /**
  18. * <p> This <code>LoginModule</code> imports a user's Unix
  19. * <code>Principal</code> information (<code>UnixPrincipal</code>,
  20. * <code>UnixNumericUserPrincipal</code>,
  21. * and <code>UnixNumericGroupPrincipal</code>)
  22. * and associates them with the current <code>Subject</code>.
  23. *
  24. * <p> This LoginModule recognizes the debug option.
  25. * If set to true in the login Configuration,
  26. * debug messages will be output to the output stream, System.out.
  27. *
  28. * @version 1.5, 01/23/03
  29. */
  30. public class UnixLoginModule implements LoginModule {
  31. // initial state
  32. private Subject subject;
  33. private CallbackHandler callbackHandler;
  34. private Map sharedState;
  35. private Map options;
  36. // configurable option
  37. private boolean debug = true;
  38. // UnixSystem to retrieve underlying system info
  39. private UnixSystem ss;
  40. // the authentication status
  41. private boolean succeeded = false;
  42. private boolean commitSucceeded = false;
  43. // Underlying system info
  44. private UnixPrincipal userPrincipal;
  45. private UnixNumericUserPrincipal UIDPrincipal;
  46. private UnixNumericGroupPrincipal GIDPrincipal;
  47. private LinkedList supplementaryGroups = new LinkedList();
  48. /**
  49. * Initialize this <code>LoginModule</code>.
  50. *
  51. * <p>
  52. *
  53. * @param subject the <code>Subject</code> to be authenticated. <p>
  54. *
  55. * @param callbackHandler a <code>CallbackHandler</code> for communicating
  56. * with the end user (prompting for usernames and
  57. * passwords, for example). <p>
  58. *
  59. * @param sharedState shared <code>LoginModule</code> state. <p>
  60. *
  61. * @param options options specified in the login
  62. * <code>Configuration</code> for this particular
  63. * <code>LoginModule</code>.
  64. */
  65. public void initialize(Subject subject, CallbackHandler callbackHandler,
  66. Map sharedState, Map options) {
  67. this.subject = subject;
  68. this.callbackHandler = callbackHandler;
  69. this.sharedState = sharedState;
  70. this.options = options;
  71. // initialize any configured options
  72. debug = "true".equalsIgnoreCase((String)options.get("debug"));
  73. }
  74. /**
  75. * Authenticate the user (first phase).
  76. *
  77. * <p> The implementation of this method attempts to retrieve the user's
  78. * Unix <code>Subject</code> information by making a native Unix
  79. * system call.
  80. *
  81. * <p>
  82. *
  83. * @exception FailedLoginException if attempts to retrieve the underlying
  84. * system information fail.
  85. *
  86. * @return true in all cases (this <code>LoginModule</code>
  87. * should not be ignored).
  88. */
  89. public boolean login() throws LoginException {
  90. long[] unixGroups = null;
  91. ss = new UnixSystem();
  92. if (ss == null) {
  93. succeeded = false;
  94. throw new FailedLoginException
  95. ("Failed in attempt to import " +
  96. "the underlying system identity information");
  97. } else {
  98. userPrincipal = new UnixPrincipal(ss.getUsername());
  99. UIDPrincipal = new UnixNumericUserPrincipal(ss.getUid());
  100. GIDPrincipal = new UnixNumericGroupPrincipal(ss.getGid(), true);
  101. if (ss.getGroups() != null && ss.getGroups().length > 0)
  102. unixGroups = ss.getGroups();
  103. for (int i = 0; i < unixGroups.length; i++) {
  104. UnixNumericGroupPrincipal ngp =
  105. new UnixNumericGroupPrincipal
  106. (unixGroups[i], false);
  107. if (!ngp.getName().equals(GIDPrincipal.getName()))
  108. supplementaryGroups.add(ngp);
  109. }
  110. if (debug) {
  111. System.out.println("\t\t[UnixLoginModule]: " +
  112. "succeeded importing info: ");
  113. System.out.println("\t\t\tuid = " + ss.getUid());
  114. System.out.println("\t\t\tgid = " + ss.getGid());
  115. unixGroups = ss.getGroups();
  116. for (int i = 0; i < unixGroups.length; i++) {
  117. System.out.println("\t\t\tsupp gid = " + unixGroups[i]);
  118. }
  119. }
  120. succeeded = true;
  121. return true;
  122. }
  123. }
  124. /**
  125. * Commit the authentication (second phase).
  126. *
  127. * <p> This method is called if the LoginContext's
  128. * overall authentication succeeded
  129. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
  130. * succeeded).
  131. *
  132. * <p> If this LoginModule's own authentication attempt
  133. * succeeded (the importing of the Unix authentication information
  134. * succeeded), then this method associates the Unix Principals
  135. * with the <code>Subject</code> currently tied to the
  136. * <code>LoginModule</code>. If this LoginModule's
  137. * authentication attempted failed, then this method removes
  138. * any state that was originally saved.
  139. *
  140. * <p>
  141. *
  142. * @exception LoginException if the commit fails
  143. *
  144. * @return true if this LoginModule's own login and commit attempts
  145. * succeeded, or false otherwise.
  146. */
  147. public boolean commit() throws LoginException {
  148. if (succeeded == false) {
  149. if (debug) {
  150. System.out.println("\t\t[UnixLoginModule]: " +
  151. "did not add any Principals to Subject " +
  152. "because own authentication failed.");
  153. }
  154. return false;
  155. } else {
  156. if (subject.isReadOnly()) {
  157. throw new LoginException
  158. ("commit Failed: Subject is Readonly");
  159. }
  160. if (!subject.getPrincipals().contains(userPrincipal))
  161. subject.getPrincipals().add(userPrincipal);
  162. if (!subject.getPrincipals().contains(UIDPrincipal))
  163. subject.getPrincipals().add(UIDPrincipal);
  164. if (!subject.getPrincipals().contains(GIDPrincipal))
  165. subject.getPrincipals().add(GIDPrincipal);
  166. for (int i = 0; i < supplementaryGroups.size(); i++) {
  167. if (!subject.getPrincipals().contains
  168. ((UnixNumericGroupPrincipal)supplementaryGroups.get(i)))
  169. subject.getPrincipals().add((UnixNumericGroupPrincipal)
  170. supplementaryGroups.get(i));
  171. }
  172. if (debug) {
  173. System.out.println("\t\t[UnixLoginModule]: " +
  174. "added UnixPrincipal,");
  175. System.out.println("\t\t\t\tUnixNumericUserPrincipal,");
  176. System.out.println("\t\t\t\tUnixNumericGroupPrincipal(s),");
  177. System.out.println("\t\t\t to Subject");
  178. }
  179. commitSucceeded = true;
  180. return true;
  181. }
  182. }
  183. /**
  184. * Abort the authentication (second phase).
  185. *
  186. * <p> This method is called if the LoginContext's
  187. * overall authentication failed.
  188. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
  189. * did not succeed).
  190. *
  191. * <p> This method cleans up any state that was originally saved
  192. * as part of the authentication attempt from the <code>login</code>
  193. * and <code>commit</code> methods.
  194. *
  195. * <p>
  196. *
  197. * @exception LoginException if the abort fails
  198. *
  199. * @return false if this LoginModule's own login and/or commit attempts
  200. * failed, and true otherwise.
  201. */
  202. public boolean abort() throws LoginException {
  203. if (debug) {
  204. System.out.println("\t\t[UnixLoginModule]: " +
  205. "aborted authentication attempt");
  206. }
  207. if (succeeded == false) {
  208. return false;
  209. } else if (succeeded == true && commitSucceeded == false) {
  210. // Clean out state
  211. succeeded = false;
  212. ss = null;
  213. userPrincipal = null;
  214. UIDPrincipal = null;
  215. GIDPrincipal = null;
  216. supplementaryGroups = new LinkedList();
  217. } else {
  218. // overall authentication succeeded and commit succeeded,
  219. // but someone else's commit failed
  220. logout();
  221. }
  222. return true;
  223. }
  224. /**
  225. * Logout the user
  226. *
  227. * <p> This method removes the Principals associated
  228. * with the <code>Subject</code>.
  229. *
  230. * <p>
  231. *
  232. * @exception LoginException if the logout fails
  233. *
  234. * @return true in all cases (this <code>LoginModule</code>
  235. * should not be ignored).
  236. */
  237. public boolean logout() throws LoginException {
  238. if (subject.isReadOnly()) {
  239. throw new LoginException
  240. ("logout Failed: Subject is Readonly");
  241. }
  242. // remove the added Principals from the Subject
  243. subject.getPrincipals().remove(userPrincipal);
  244. subject.getPrincipals().remove(UIDPrincipal);
  245. subject.getPrincipals().remove(GIDPrincipal);
  246. for (int i = 0; i < supplementaryGroups.size(); i++) {
  247. subject.getPrincipals().remove
  248. ((UnixNumericGroupPrincipal)supplementaryGroups.get(i));
  249. }
  250. // clean out state
  251. ss = null;
  252. succeeded = false;
  253. commitSucceeded = false;
  254. userPrincipal = null;
  255. UIDPrincipal = null;
  256. GIDPrincipal = null;
  257. supplementaryGroups = new LinkedList();
  258. if (debug) {
  259. System.out.println("\t\t[UnixLoginModule]: " +
  260. "logged out Subject");
  261. }
  262. return true;
  263. }
  264. }