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 26package com.sun.security.auth.module; 27 28import java.util.*; 29import java.io.IOException; 30import javax.security.auth.*; 31import javax.security.auth.callback.*; 32import javax.security.auth.login.*; 33import javax.security.auth.spi.*; 34import java.security.Principal; 35import com.sun.security.auth.NTUserPrincipal; 36import com.sun.security.auth.NTSidUserPrincipal; 37import com.sun.security.auth.NTDomainPrincipal; 38import com.sun.security.auth.NTSidDomainPrincipal; 39import com.sun.security.auth.NTSidPrimaryGroupPrincipal; 40import com.sun.security.auth.NTSidGroupPrincipal; 41import com.sun.security.auth.NTNumericCredential; 42 43/** 44 * This {@code LoginModule} 45 * renders a user's NT security information as some number of 46 * {@code Principal}s 47 * and associates them with a {@code Subject}. 48 * 49 * <p> This LoginModule recognizes the debug option. 50 * If set to true in the login Configuration, 51 * debug messages will be output to the output stream, System.out. 52 * 53 * <p> This LoginModule also recognizes the debugNative option. 54 * If set to true in the login Configuration, 55 * debug messages from the native component of the module 56 * will be output to the output stream, System.out. 57 * 58 * @see javax.security.auth.spi.LoginModule 59 */ 60public class NTLoginModule implements LoginModule { 61 62 private NTSystem ntSystem; 63 64 // initial state 65 private Subject subject; 66 private CallbackHandler callbackHandler; 67 private Map<String, ?> sharedState; 68 private Map<String, ?> options; 69 70 // configurable option 71 private boolean debug = false; 72 private boolean debugNative = false; 73 74 // the authentication status 75 private boolean succeeded = false; 76 private boolean commitSucceeded = false; 77 78 private NTUserPrincipal userPrincipal; // user name 79 private NTSidUserPrincipal userSID; // user SID 80 private NTDomainPrincipal userDomain; // user domain 81 private NTSidDomainPrincipal domainSID; // domain SID 82 private NTSidPrimaryGroupPrincipal primaryGroup; // primary group 83 private NTSidGroupPrincipal[] groups; // supplementary groups 84 private NTNumericCredential iToken; // impersonation token 85 86 /** 87 * Initialize this {@code LoginModule}. 88 * 89 * @param subject the {@code Subject} to be authenticated. 90 * 91 * @param callbackHandler a {@code CallbackHandler} for communicating 92 * with the end user (prompting for usernames and 93 * passwords, for example). This particular LoginModule only 94 * extracts the underlying NT system information, so this 95 * parameter is ignored. 96 * 97 * @param sharedState shared {@code LoginModule} state. 98 * 99 * @param options options specified in the login 100 * {@code Configuration} for this particular 101 * {@code LoginModule}. 102 */ 103 public void initialize(Subject subject, CallbackHandler callbackHandler, 104 Map<String,?> sharedState, 105 Map<String,?> options) 106 { 107 108 this.subject = subject; 109 this.callbackHandler = callbackHandler; 110 this.sharedState = sharedState; 111 this.options = options; 112 113 // initialize any configured options 114 debug = "true".equalsIgnoreCase((String)options.get("debug")); 115 debugNative="true".equalsIgnoreCase((String)options.get("debugNative")); 116 117 if (debugNative == true) { 118 debug = true; 119 } 120 } 121 122 /** 123 * Import underlying NT system identity information. 124 * 125 * @return true in all cases since this {@code LoginModule} 126 * should not be ignored. 127 * 128 * @exception FailedLoginException if the authentication fails. 129 * 130 * @exception LoginException if this {@code LoginModule} 131 * is unable to perform the authentication. 132 */ 133 public boolean login() throws LoginException { 134 135 succeeded = false; // Indicate not yet successful 136 137 try { 138 ntSystem = new NTSystem(debugNative); 139 } catch (UnsatisfiedLinkError ule) { 140 if (debug) { 141 System.out.println("\t\t[NTLoginModule] " + 142 "Failed in NT login"); 143 } 144 throw new FailedLoginException 145 ("Failed in attempt to import the " + 146 "underlying NT system identity information" + 147 " on " + System.getProperty("os.name")); 148 } 149 150 if (ntSystem.getName() == null) { 151 throw new FailedLoginException 152 ("Failed in attempt to import the " + 153 "underlying NT system identity information"); 154 } 155 userPrincipal = new NTUserPrincipal(ntSystem.getName()); 156 if (debug) { 157 System.out.println("\t\t[NTLoginModule] " + 158 "succeeded importing info: "); 159 System.out.println("\t\t\tuser name = " + 160 userPrincipal.getName()); 161 } 162 163 if (ntSystem.getUserSID() != null) { 164 userSID = new NTSidUserPrincipal(ntSystem.getUserSID()); 165 if (debug) { 166 System.out.println("\t\t\tuser SID = " + 167 userSID.getName()); 168 } 169 } 170 if (ntSystem.getDomain() != null) { 171 userDomain = new NTDomainPrincipal(ntSystem.getDomain()); 172 if (debug) { 173 System.out.println("\t\t\tuser domain = " + 174 userDomain.getName()); 175 } 176 } 177 if (ntSystem.getDomainSID() != null) { 178 domainSID = 179 new NTSidDomainPrincipal(ntSystem.getDomainSID()); 180 if (debug) { 181 System.out.println("\t\t\tuser domain SID = " + 182 domainSID.getName()); 183 } 184 } 185 if (ntSystem.getPrimaryGroupID() != null) { 186 primaryGroup = 187 new NTSidPrimaryGroupPrincipal(ntSystem.getPrimaryGroupID()); 188 if (debug) { 189 System.out.println("\t\t\tuser primary group = " + 190 primaryGroup.getName()); 191 } 192 } 193 if (ntSystem.getGroupIDs() != null && 194 ntSystem.getGroupIDs().length > 0) { 195 196 String[] groupSIDs = ntSystem.getGroupIDs(); 197 groups = new NTSidGroupPrincipal[groupSIDs.length]; 198 for (int i = 0; i < groupSIDs.length; i++) { 199 groups[i] = new NTSidGroupPrincipal(groupSIDs[i]); 200 if (debug) { 201 System.out.println("\t\t\tuser group = " + 202 groups[i].getName()); 203 } 204 } 205 } 206 if (ntSystem.getImpersonationToken() != 0) { 207 iToken = new NTNumericCredential(ntSystem.getImpersonationToken()); 208 if (debug) { 209 System.out.println("\t\t\timpersonation token = " + 210 ntSystem.getImpersonationToken()); 211 } 212 } 213 214 succeeded = true; 215 return succeeded; 216 } 217 218 /** 219 * This method is called if the LoginContext's 220 * overall authentication succeeded 221 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules 222 * succeeded). 223 * 224 * <p> If this LoginModule's own authentication attempt 225 * succeeded (checked by retrieving the private state saved by the 226 * {@code login} method), then this method associates some 227 * number of various {@code Principal}s 228 * with the {@code Subject} located in the 229 * {@code LoginModuleContext}. If this LoginModule's own 230 * authentication attempted failed, then this method removes 231 * any state that was originally saved. 232 * 233 * @exception LoginException if the commit fails. 234 * 235 * @return true if this LoginModule's own login and commit 236 * attempts succeeded, or false otherwise. 237 */ 238 public boolean commit() throws LoginException { 239 if (succeeded == false) { 240 if (debug) { 241 System.out.println("\t\t[NTLoginModule]: " + 242 "did not add any Principals to Subject " + 243 "because own authentication failed."); 244 } 245 return false; 246 } 247 if (subject.isReadOnly()) { 248 throw new LoginException ("Subject is ReadOnly"); 249 } 250 Set<Principal> principals = subject.getPrincipals(); 251 252 // we must have a userPrincipal - everything else is optional 253 if (!principals.contains(userPrincipal)) { 254 principals.add(userPrincipal); 255 } 256 if (userSID != null && !principals.contains(userSID)) { 257 principals.add(userSID); 258 } 259 260 if (userDomain != null && !principals.contains(userDomain)) { 261 principals.add(userDomain); 262 } 263 if (domainSID != null && !principals.contains(domainSID)) { 264 principals.add(domainSID); 265 } 266 267 if (primaryGroup != null && !principals.contains(primaryGroup)) { 268 principals.add(primaryGroup); 269 } 270 for (int i = 0; groups != null && i < groups.length; i++) { 271 if (!principals.contains(groups[i])) { 272 principals.add(groups[i]); 273 } 274 } 275 276 Set<Object> pubCreds = subject.getPublicCredentials(); 277 if (iToken != null && !pubCreds.contains(iToken)) { 278 pubCreds.add(iToken); 279 } 280 commitSucceeded = true; 281 return true; 282 } 283 284 285 /** 286 * This method is called if the LoginContext's 287 * overall authentication failed. 288 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules 289 * did not succeed). 290 * 291 * <p> If this LoginModule's own authentication attempt 292 * succeeded (checked by retrieving the private state saved by the 293 * {@code login} and {@code commit} methods), 294 * then this method cleans up any state that was originally saved. 295 * 296 * @exception LoginException if the abort fails. 297 * 298 * @return false if this LoginModule's own login and/or commit attempts 299 * failed, and true otherwise. 300 */ 301 public boolean abort() throws LoginException { 302 if (debug) { 303 System.out.println("\t\t[NTLoginModule]: " + 304 "aborted authentication attempt"); 305 } 306 307 if (succeeded == false) { 308 return false; 309 } else if (succeeded == true && commitSucceeded == false) { 310 ntSystem = null; 311 userPrincipal = null; 312 userSID = null; 313 userDomain = null; 314 domainSID = null; 315 primaryGroup = null; 316 groups = null; 317 iToken = null; 318 succeeded = false; 319 } else { 320 // overall authentication succeeded and commit succeeded, 321 // but someone else's commit failed 322 logout(); 323 } 324 return succeeded; 325 } 326 327 /** 328 * Logout the user. 329 * 330 * <p> This method removes the {@code NTUserPrincipal}, 331 * {@code NTDomainPrincipal}, {@code NTSidUserPrincipal}, 332 * {@code NTSidDomainPrincipal}, {@code NTSidGroupPrincipal}s, 333 * and {@code NTSidPrimaryGroupPrincipal} 334 * that may have been added by the {@code commit} method. 335 * 336 * @exception LoginException if the logout fails. 337 * 338 * @return true in all cases since this {@code LoginModule} 339 * should not be ignored. 340 */ 341 public boolean logout() throws LoginException { 342 343 if (subject.isReadOnly()) { 344 throw new LoginException ("Subject is ReadOnly"); 345 } 346 Set<Principal> principals = subject.getPrincipals(); 347 if (principals.contains(userPrincipal)) { 348 principals.remove(userPrincipal); 349 } 350 if (principals.contains(userSID)) { 351 principals.remove(userSID); 352 } 353 if (principals.contains(userDomain)) { 354 principals.remove(userDomain); 355 } 356 if (principals.contains(domainSID)) { 357 principals.remove(domainSID); 358 } 359 if (principals.contains(primaryGroup)) { 360 principals.remove(primaryGroup); 361 } 362 for (int i = 0; groups != null && i < groups.length; i++) { 363 if (principals.contains(groups[i])) { 364 principals.remove(groups[i]); 365 } 366 } 367 368 Set<Object> pubCreds = subject.getPublicCredentials(); 369 if (pubCreds.contains(iToken)) { 370 pubCreds.remove(iToken); 371 } 372 373 succeeded = false; 374 commitSucceeded = false; 375 userPrincipal = null; 376 userDomain = null; 377 userSID = null; 378 domainSID = null; 379 groups = null; 380 primaryGroup = null; 381 iToken = null; 382 ntSystem = null; 383 384 if (debug) { 385 System.out.println("\t\t[NTLoginModule] " + 386 "completed logout processing"); 387 } 388 return true; 389 } 390} 391