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