1 /*
   2  * Copyright (c) 2000, 2011, 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.SolarisPrincipal;
  35 import com.sun.security.auth.SolarisNumericUserPrincipal;
  36 import com.sun.security.auth.SolarisNumericGroupPrincipal;
  37 
  38 /**
  39  * <p> This <code>LoginModule</code> imports a user's Solaris
  40  * <code>Principal</code> information (<code>SolarisPrincipal</code>,
  41  * <code>SolarisNumericUserPrincipal</code>,
  42  * and <code>SolarisNumericGroupPrincipal</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  * @deprecated  As of JDK1.4, replaced by
  49  * <code>com.sun.security.auth.module.UnixLoginModule</code>.
  50  *             This LoginModule is entirely deprecated and
  51  *             is here to allow for a smooth transition to the new
  52  *             UnixLoginModule.
  53  *
  54  */
  55 @Deprecated
  56 public class SolarisLoginModule implements LoginModule {
  57 
  58     // initial state
  59     private Subject subject;
  60     private CallbackHandler callbackHandler;
  61     private Map<String, ?> sharedState;
  62     private Map<String, ?> options;
  63 
  64     // configurable option
  65     private boolean debug = true;
  66 
  67     // SolarisSystem to retrieve underlying system info
  68     private SolarisSystem ss;
  69 
  70     // the authentication status
  71     private boolean succeeded = false;
  72     private boolean commitSucceeded = false;
  73 
  74     // Underlying system info
  75     private SolarisPrincipal userPrincipal;
  76     private SolarisNumericUserPrincipal UIDPrincipal;
  77     private SolarisNumericGroupPrincipal GIDPrincipal;
  78     private LinkedList<SolarisNumericGroupPrincipal> supplementaryGroups =
  79                 new LinkedList<>();
  80 
  81     /**
  82      * Initialize this <code>LoginModule</code>.
  83      *
  84      * <p>
  85      *
  86      * @param subject the <code>Subject</code> to be authenticated. <p>
  87      *
  88      * @param callbackHandler a <code>CallbackHandler</code> for communicating
  89      *                  with the end user (prompting for usernames and
  90      *                  passwords, for example). <p>
  91      *
  92      * @param sharedState shared <code>LoginModule</code> state. <p>
  93      *
  94      * @param options options specified in the login
  95      *                  <code>Configuration</code> for this particular
  96      *                  <code>LoginModule</code>.
  97      */
  98     public void initialize(Subject subject, CallbackHandler callbackHandler,
  99                            Map<String,?> sharedState,
 100                            Map<String,?> options)
 101     {
 102 
 103         this.subject = subject;
 104         this.callbackHandler = callbackHandler;
 105         this.sharedState = sharedState;
 106         this.options = options;
 107 
 108         // initialize any configured options
 109         debug = "true".equalsIgnoreCase((String)options.get("debug"));
 110     }
 111 
 112     /**
 113      * Authenticate the user (first phase).
 114      *
 115      * <p> The implementation of this method attempts to retrieve the user's
 116      * Solaris <code>Subject</code> information by making a native Solaris
 117      * system call.
 118      *
 119      * <p>
 120      *
 121      * @exception FailedLoginException if attempts to retrieve the underlying
 122      *          system information fail.
 123      *
 124      * @return true in all cases (this <code>LoginModule</code>
 125      *          should not be ignored).
 126      */
 127     public boolean login() throws LoginException {
 128 
 129         long[] solarisGroups = null;
 130 
 131         ss = new SolarisSystem();
 132 
 133         if (ss == null) {
 134             succeeded = false;
 135             throw new FailedLoginException
 136                                 ("Failed in attempt to import " +
 137                                 "the underlying system identity information");
 138         } else {
 139             userPrincipal = new SolarisPrincipal(ss.getUsername());
 140             UIDPrincipal = new SolarisNumericUserPrincipal(ss.getUid());
 141             GIDPrincipal = new SolarisNumericGroupPrincipal(ss.getGid(), true);
 142             if (ss.getGroups() != null && ss.getGroups().length > 0)
 143                 solarisGroups = ss.getGroups();
 144                 for (int i = 0; i < solarisGroups.length; i++) {
 145                     SolarisNumericGroupPrincipal ngp =
 146                         new SolarisNumericGroupPrincipal
 147                         (solarisGroups[i], false);
 148                     if (!ngp.getName().equals(GIDPrincipal.getName()))
 149                         supplementaryGroups.add(ngp);
 150                 }
 151             if (debug) {
 152                 System.out.println("\t\t[SolarisLoginModule]: " +
 153                         "succeeded importing info: ");
 154                 System.out.println("\t\t\tuid = " + ss.getUid());
 155                 System.out.println("\t\t\tgid = " + ss.getGid());
 156                 solarisGroups = ss.getGroups();
 157                 for (int i = 0; i < solarisGroups.length; i++) {
 158                     System.out.println("\t\t\tsupp gid = " + solarisGroups[i]);
 159                 }
 160             }
 161             succeeded = true;
 162             return true;
 163         }
 164     }
 165 
 166     /**
 167      * Commit the authentication (second phase).
 168      *
 169      * <p> This method is called if the LoginContext's
 170      * overall authentication succeeded
 171      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
 172      * succeeded).
 173      *
 174      * <p> If this LoginModule's own authentication attempt
 175      * succeeded (the importing of the Solaris authentication information
 176      * succeeded), then this method associates the Solaris Principals
 177      * with the <code>Subject</code> currently tied to the
 178      * <code>LoginModule</code>.  If this LoginModule's
 179      * authentication attempted failed, then this method removes
 180      * any state that was originally saved.
 181      *
 182      * <p>
 183      *
 184      * @exception LoginException if the commit fails
 185      *
 186      * @return true if this LoginModule's own login and commit attempts
 187      *          succeeded, or false otherwise.
 188      */
 189     public boolean commit() throws LoginException {
 190         if (succeeded == false) {
 191             if (debug) {
 192                 System.out.println("\t\t[SolarisLoginModule]: " +
 193                     "did not add any Principals to Subject " +
 194                     "because own authentication failed.");
 195             }
 196             return false;
 197         }
 198         if (subject.isReadOnly()) {
 199             throw new LoginException ("Subject is Readonly");
 200         }
 201         if (!subject.getPrincipals().contains(userPrincipal))
 202             subject.getPrincipals().add(userPrincipal);
 203         if (!subject.getPrincipals().contains(UIDPrincipal))
 204             subject.getPrincipals().add(UIDPrincipal);
 205         if (!subject.getPrincipals().contains(GIDPrincipal))
 206             subject.getPrincipals().add(GIDPrincipal);
 207         for (int i = 0; i < supplementaryGroups.size(); i++) {
 208             if (!subject.getPrincipals().contains(supplementaryGroups.get(i)))
 209                 subject.getPrincipals().add(supplementaryGroups.get(i));
 210         }
 211 
 212         if (debug) {
 213             System.out.println("\t\t[SolarisLoginModule]: " +
 214                                "added SolarisPrincipal,");
 215             System.out.println("\t\t\t\tSolarisNumericUserPrincipal,");
 216             System.out.println("\t\t\t\tSolarisNumericGroupPrincipal(s),");
 217             System.out.println("\t\t\t to Subject");
 218         }
 219 
 220         commitSucceeded = true;
 221         return true;
 222     }
 223 
 224 
 225     /**
 226      * Abort the authentication (second phase).
 227      *
 228      * <p> This method is called if the LoginContext's
 229      * overall authentication failed.
 230      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
 231      * did not succeed).
 232      *
 233      * <p> This method cleans up any state that was originally saved
 234      * as part of the authentication attempt from the <code>login</code>
 235      * and <code>commit</code> methods.
 236      *
 237      * <p>
 238      *
 239      * @exception LoginException if the abort fails
 240      *
 241      * @return false if this LoginModule's own login and/or commit attempts
 242      *          failed, and true otherwise.
 243      */
 244     public boolean abort() throws LoginException {
 245         if (debug) {
 246             System.out.println("\t\t[SolarisLoginModule]: " +
 247                 "aborted authentication attempt");
 248         }
 249 
 250         if (succeeded == false) {
 251             return false;
 252         } else if (succeeded == true && commitSucceeded == false) {
 253 
 254             // Clean out state
 255             succeeded = false;
 256             ss = null;
 257             userPrincipal = null;
 258             UIDPrincipal = null;
 259             GIDPrincipal = null;
 260             supplementaryGroups =
 261                         new LinkedList<SolarisNumericGroupPrincipal>();
 262         } else {
 263             // overall authentication succeeded and commit succeeded,
 264             // but someone else's commit failed
 265             logout();
 266         }
 267         return true;
 268     }
 269 
 270     /**
 271      * Logout the user
 272      *
 273      * <p> This method removes the Principals associated
 274      * with the <code>Subject</code>.
 275      *
 276      * <p>
 277      *
 278      * @exception LoginException if the logout fails
 279      *
 280      * @return true in all cases (this <code>LoginModule</code>
 281      *          should not be ignored).
 282      */
 283     public boolean logout() throws LoginException {
 284         if (debug) {
 285             System.out.println("\t\t[SolarisLoginModule]: " +
 286                 "Entering logout");
 287         }
 288         if (subject.isReadOnly()) {
 289             throw new LoginException ("Subject is Readonly");
 290         }
 291         // remove the added Principals from the Subject
 292         subject.getPrincipals().remove(userPrincipal);
 293         subject.getPrincipals().remove(UIDPrincipal);
 294         subject.getPrincipals().remove(GIDPrincipal);
 295         for (int i = 0; i < supplementaryGroups.size(); i++) {
 296             subject.getPrincipals().remove(supplementaryGroups.get(i));
 297         }
 298 
 299         // clean out state
 300         ss = null;
 301         succeeded = false;
 302         commitSucceeded = false;
 303         userPrincipal = null;
 304         UIDPrincipal = null;
 305         GIDPrincipal = null;
 306         supplementaryGroups = new LinkedList<SolarisNumericGroupPrincipal>();
 307 
 308         if (debug) {
 309             System.out.println("\t\t[SolarisLoginModule]: " +
 310                 "logged out Subject");
 311         }
 312         return true;
 313     }
 314 }