1 /* 2 * Copyright (c) 2000, 2013, 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 @jdk.Exported 51 public class UnixLoginModule implements LoginModule { 52 53 // initial state 54 private Subject subject; 55 private CallbackHandler callbackHandler; 56 private Map<String, ?> sharedState; 57 private Map<String, ?> options; 58 59 // configurable option 60 private boolean debug = true; 61 62 // UnixSystem to retrieve underlying system info 63 private UnixSystem ss; 64 65 // the authentication status 66 private boolean succeeded = false; 67 private boolean commitSucceeded = false; 68 69 // Underlying system info 70 private UnixPrincipal userPrincipal; 71 private UnixNumericUserPrincipal UIDPrincipal; 72 private UnixNumericGroupPrincipal GIDPrincipal; 73 private LinkedList<UnixNumericGroupPrincipal> supplementaryGroups = 74 new LinkedList<>(); 75 76 /** 77 * Initialize this <code>LoginModule</code>. 78 * 79 * <p> 80 * 81 * @param subject the <code>Subject</code> to be authenticated. <p> 82 * 83 * @param callbackHandler a <code>CallbackHandler</code> for communicating 84 * with the end user (prompting for usernames and 85 * passwords, for example). <p> 86 * 87 * @param sharedState shared <code>LoginModule</code> state. <p> 88 * 89 * @param options options specified in the login 90 * <code>Configuration</code> for this particular 91 * <code>LoginModule</code>. 92 */ initialize(Subject subject, CallbackHandler callbackHandler, Map<String,?> sharedState, Map<String,?> options)93 public void initialize(Subject subject, CallbackHandler callbackHandler, 94 Map<String,?> sharedState, 95 Map<String,?> options) { 96 97 this.subject = subject; 98 this.callbackHandler = callbackHandler; 99 this.sharedState = sharedState; 100 this.options = options; 101 102 // initialize any configured options 103 debug = "true".equalsIgnoreCase((String)options.get("debug")); 104 } 105 106 /** 107 * Authenticate the user (first phase). 108 * 109 * <p> The implementation of this method attempts to retrieve the user's 110 * Unix <code>Subject</code> information by making a native Unix 111 * system call. 112 * 113 * <p> 114 * 115 * @exception FailedLoginException if attempts to retrieve the underlying 116 * system information fail. 117 * 118 * @return true in all cases (this <code>LoginModule</code> 119 * should not be ignored). 120 */ login()121 public boolean login() throws LoginException { 122 123 long[] unixGroups = null; 124 125 ss = new UnixSystem(); 126 127 if (ss == null) { 128 succeeded = false; 129 throw new FailedLoginException 130 ("Failed in attempt to import " + 131 "the underlying system identity information"); 132 } else { 133 userPrincipal = new UnixPrincipal(ss.getUsername()); 134 UIDPrincipal = new UnixNumericUserPrincipal(ss.getUid()); 135 GIDPrincipal = new UnixNumericGroupPrincipal(ss.getGid(), 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 = 140 new UnixNumericGroupPrincipal 141 (unixGroups[i], false); 142 if (!ngp.getName().equals(GIDPrincipal.getName())) 143 supplementaryGroups.add(ngp); 144 } 145 } 146 if (debug) { 147 System.out.println("\t\t[UnixLoginModule]: " + 148 "succeeded importing info: "); 149 System.out.println("\t\t\tuid = " + ss.getUid()); 150 System.out.println("\t\t\tgid = " + ss.getGid()); 151 unixGroups = ss.getGroups(); 152 for (int i = 0; i < unixGroups.length; i++) { 153 System.out.println("\t\t\tsupp gid = " + 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 */ commit()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(supplementaryGroups.get(i)); 207 } 208 209 if (debug) { 210 System.out.println("\t\t[UnixLoginModule]: " + 211 "added UnixPrincipal,"); 212 System.out.println("\t\t\t\tUnixNumericUserPrincipal,"); 213 System.out.println("\t\t\t\tUnixNumericGroupPrincipal(s),"); 214 System.out.println("\t\t\t to Subject"); 215 } 216 217 commitSucceeded = true; 218 return true; 219 } 220 } 221 222 /** 223 * Abort the authentication (second phase). 224 * 225 * <p> This method is called if the LoginContext's 226 * overall authentication failed. 227 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules 228 * did not succeed). 229 * 230 * <p> This method cleans up any state that was originally saved 231 * as part of the authentication attempt from the <code>login</code> 232 * and <code>commit</code> methods. 233 * 234 * <p> 235 * 236 * @exception LoginException if the abort fails 237 * 238 * @return false if this LoginModule's own login and/or commit attempts 239 * failed, and true otherwise. 240 */ abort()241 public boolean abort() throws LoginException { 242 if (debug) { 243 System.out.println("\t\t[UnixLoginModule]: " + 244 "aborted authentication attempt"); 245 } 246 247 if (succeeded == false) { 248 return false; 249 } else if (succeeded == true && commitSucceeded == false) { 250 251 // Clean out state 252 succeeded = false; 253 ss = null; 254 userPrincipal = null; 255 UIDPrincipal = null; 256 GIDPrincipal = null; 257 supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>(); 258 } else { 259 // overall authentication succeeded and commit succeeded, 260 // but someone else's commit failed 261 logout(); 262 } 263 return true; 264 } 265 266 /** 267 * Logout the user 268 * 269 * <p> This method removes the Principals associated 270 * with the <code>Subject</code>. 271 * 272 * <p> 273 * 274 * @exception LoginException if the logout fails 275 * 276 * @return true in all cases (this <code>LoginModule</code> 277 * should not be ignored). 278 */ logout()279 public boolean logout() throws LoginException { 280 281 if (subject.isReadOnly()) { 282 throw new LoginException 283 ("logout Failed: Subject is Readonly"); 284 } 285 // remove the added Principals from the Subject 286 subject.getPrincipals().remove(userPrincipal); 287 subject.getPrincipals().remove(UIDPrincipal); 288 subject.getPrincipals().remove(GIDPrincipal); 289 for (int i = 0; i < supplementaryGroups.size(); i++) { 290 subject.getPrincipals().remove(supplementaryGroups.get(i)); 291 } 292 293 // clean out state 294 ss = null; 295 succeeded = false; 296 commitSucceeded = false; 297 userPrincipal = null; 298 UIDPrincipal = null; 299 GIDPrincipal = null; 300 supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>(); 301 302 if (debug) { 303 System.out.println("\t\t[UnixLoginModule]: " + 304 "logged out Subject"); 305 } 306 return true; 307 } 308 } 309