1. /*
  2. * @(#)UnixLoginModule.java 1.7 04/05/05
  3. *
  4. * Copyright 2004 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.7, 05/05/04
  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<String,?> sharedState,
  67. Map<String,?> options) {
  68. this.subject = subject;
  69. this.callbackHandler = callbackHandler;
  70. this.sharedState = sharedState;
  71. this.options = options;
  72. // initialize any configured options
  73. debug = "true".equalsIgnoreCase((String)options.get("debug"));
  74. }
  75. /**
  76. * Authenticate the user (first phase).
  77. *
  78. * <p> The implementation of this method attempts to retrieve the user's
  79. * Unix <code>Subject</code> information by making a native Unix
  80. * system call.
  81. *
  82. * <p>
  83. *
  84. * @exception FailedLoginException if attempts to retrieve the underlying
  85. * system information fail.
  86. *
  87. * @return true in all cases (this <code>LoginModule</code>
  88. * should not be ignored).
  89. */
  90. public boolean login() throws LoginException {
  91. long[] unixGroups = null;
  92. ss = new UnixSystem();
  93. if (ss == null) {
  94. succeeded = false;
  95. throw new FailedLoginException
  96. ("Failed in attempt to import " +
  97. "the underlying system identity information");
  98. } else {
  99. userPrincipal = new UnixPrincipal(ss.getUsername());
  100. UIDPrincipal = new UnixNumericUserPrincipal(ss.getUid());
  101. GIDPrincipal = new UnixNumericGroupPrincipal(ss.getGid(), true);
  102. if (ss.getGroups() != null && ss.getGroups().length > 0)
  103. unixGroups = ss.getGroups();
  104. for (int i = 0; i < unixGroups.length; i++) {
  105. UnixNumericGroupPrincipal ngp =
  106. new UnixNumericGroupPrincipal
  107. (unixGroups[i], false);
  108. if (!ngp.getName().equals(GIDPrincipal.getName()))
  109. supplementaryGroups.add(ngp);
  110. }
  111. if (debug) {
  112. System.out.println("\t\t[UnixLoginModule]: " +
  113. "succeeded importing info: ");
  114. System.out.println("\t\t\tuid = " + ss.getUid());
  115. System.out.println("\t\t\tgid = " + ss.getGid());
  116. unixGroups = ss.getGroups();
  117. for (int i = 0; i < unixGroups.length; i++) {
  118. System.out.println("\t\t\tsupp gid = " + unixGroups[i]);
  119. }
  120. }
  121. succeeded = true;
  122. return true;
  123. }
  124. }
  125. /**
  126. * Commit the authentication (second phase).
  127. *
  128. * <p> This method is called if the LoginContext's
  129. * overall authentication succeeded
  130. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
  131. * succeeded).
  132. *
  133. * <p> If this LoginModule's own authentication attempt
  134. * succeeded (the importing of the Unix authentication information
  135. * succeeded), then this method associates the Unix Principals
  136. * with the <code>Subject</code> currently tied to the
  137. * <code>LoginModule</code>. If this LoginModule's
  138. * authentication attempted failed, then this method removes
  139. * any state that was originally saved.
  140. *
  141. * <p>
  142. *
  143. * @exception LoginException if the commit fails
  144. *
  145. * @return true if this LoginModule's own login and commit attempts
  146. * succeeded, or false otherwise.
  147. */
  148. public boolean commit() throws LoginException {
  149. if (succeeded == false) {
  150. if (debug) {
  151. System.out.println("\t\t[UnixLoginModule]: " +
  152. "did not add any Principals to Subject " +
  153. "because own authentication failed.");
  154. }
  155. return false;
  156. } else {
  157. if (subject.isReadOnly()) {
  158. throw new LoginException
  159. ("commit Failed: Subject is Readonly");
  160. }
  161. if (!subject.getPrincipals().contains(userPrincipal))
  162. subject.getPrincipals().add(userPrincipal);
  163. if (!subject.getPrincipals().contains(UIDPrincipal))
  164. subject.getPrincipals().add(UIDPrincipal);
  165. if (!subject.getPrincipals().contains(GIDPrincipal))
  166. subject.getPrincipals().add(GIDPrincipal);
  167. for (int i = 0; i < supplementaryGroups.size(); i++) {
  168. if (!subject.getPrincipals().contains
  169. ((UnixNumericGroupPrincipal)supplementaryGroups.get(i)))
  170. subject.getPrincipals().add((UnixNumericGroupPrincipal)
  171. supplementaryGroups.get(i));
  172. }
  173. if (debug) {
  174. System.out.println("\t\t[UnixLoginModule]: " +
  175. "added UnixPrincipal,");
  176. System.out.println("\t\t\t\tUnixNumericUserPrincipal,");
  177. System.out.println("\t\t\t\tUnixNumericGroupPrincipal(s),");
  178. System.out.println("\t\t\t to Subject");
  179. }
  180. commitSucceeded = true;
  181. return true;
  182. }
  183. }
  184. /**
  185. * Abort the authentication (second phase).
  186. *
  187. * <p> This method is called if the LoginContext's
  188. * overall authentication failed.
  189. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
  190. * did not succeed).
  191. *
  192. * <p> This method cleans up any state that was originally saved
  193. * as part of the authentication attempt from the <code>login</code>
  194. * and <code>commit</code> methods.
  195. *
  196. * <p>
  197. *
  198. * @exception LoginException if the abort fails
  199. *
  200. * @return false if this LoginModule's own login and/or commit attempts
  201. * failed, and true otherwise.
  202. */
  203. public boolean abort() throws LoginException {
  204. if (debug) {
  205. System.out.println("\t\t[UnixLoginModule]: " +
  206. "aborted authentication attempt");
  207. }
  208. if (succeeded == false) {
  209. return false;
  210. } else if (succeeded == true && commitSucceeded == false) {
  211. // Clean out state
  212. succeeded = false;
  213. ss = null;
  214. userPrincipal = null;
  215. UIDPrincipal = null;
  216. GIDPrincipal = null;
  217. supplementaryGroups = new LinkedList();
  218. } else {
  219. // overall authentication succeeded and commit succeeded,
  220. // but someone else's commit failed
  221. logout();
  222. }
  223. return true;
  224. }
  225. /**
  226. * Logout the user
  227. *
  228. * <p> This method removes the Principals associated
  229. * with the <code>Subject</code>.
  230. *
  231. * <p>
  232. *
  233. * @exception LoginException if the logout fails
  234. *
  235. * @return true in all cases (this <code>LoginModule</code>
  236. * should not be ignored).
  237. */
  238. public boolean logout() throws LoginException {
  239. if (subject.isReadOnly()) {
  240. throw new LoginException
  241. ("logout Failed: Subject is Readonly");
  242. }
  243. // remove the added Principals from the Subject
  244. subject.getPrincipals().remove(userPrincipal);
  245. subject.getPrincipals().remove(UIDPrincipal);
  246. subject.getPrincipals().remove(GIDPrincipal);
  247. for (int i = 0; i < supplementaryGroups.size(); i++) {
  248. subject.getPrincipals().remove
  249. ((UnixNumericGroupPrincipal)supplementaryGroups.get(i));
  250. }
  251. // clean out state
  252. ss = null;
  253. succeeded = false;
  254. commitSucceeded = false;
  255. userPrincipal = null;
  256. UIDPrincipal = null;
  257. GIDPrincipal = null;
  258. supplementaryGroups = new LinkedList();
  259. if (debug) {
  260. System.out.println("\t\t[UnixLoginModule]: " +
  261. "logged out Subject");
  262. }
  263. return true;
  264. }
  265. }