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