1 /* 2 * Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.security.auth.module; 27 28 import java.util.*; 29 import java.io.IOException; 30 import javax.security.auth.*; 31 import javax.security.auth.callback.*; 32 import javax.security.auth.login.*; 33 import javax.security.auth.spi.*; 34 import com.sun.security.auth.UnixPrincipal; 35 import com.sun.security.auth.UnixNumericUserPrincipal; 36 import 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 */ 50 public 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<>(); 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 }