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 @jdk.Supported
  56 @Deprecated
  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     private SolarisSystem ss;
  70 
  71     // the authentication status
  72     private boolean succeeded = false;
  73     private boolean commitSucceeded = false;
  74 
  75     // Underlying system info
  76     private SolarisPrincipal userPrincipal;
  77     private SolarisNumericUserPrincipal UIDPrincipal;
  78     private SolarisNumericGroupPrincipal GIDPrincipal;
  79     private LinkedList<SolarisNumericGroupPrincipal> supplementaryGroups =
  80                 new LinkedList<>();
  81 
  82     /**
  83      * Initialize this <code>LoginModule</code>.
  84      *
  85      * <p>
  86      *
  87      * @param subject the <code>Subject</code> to be authenticated. <p>
  88      *
  89      * @param callbackHandler a <code>CallbackHandler</code> for communicating
  90      *                  with the end user (prompting for usernames and
  91      *                  passwords, for example). <p>
  92      *
  93      * @param sharedState shared <code>LoginModule</code> state. <p>
  94      *
  95      * @param options options specified in the login
  96      *                  <code>Configuration</code> for this particular
  97      *                  <code>LoginModule</code>.
  98      */
  99     public void initialize(Subject subject, CallbackHandler callbackHandler,
 100                            Map<String,?> sharedState,
 101                            Map<String,?> options)
 102     {
 103 
 104         this.subject = subject;
 105         this.callbackHandler = callbackHandler;
 106         this.sharedState = sharedState;
 107         this.options = options;
 108 
 109         // initialize any configured options
 110         debug = "true".equalsIgnoreCase((String)options.get("debug"));
 111     }
 112 
 113     /**
 114      * Authenticate the user (first phase).
 115      *
 116      * <p> The implementation of this method attempts to retrieve the user's
 117      * Solaris <code>Subject</code> information by making a native Solaris
 118      * system call.
 119      *
 120      * <p>
 121      *
 122      * @exception FailedLoginException if attempts to retrieve the underlying
 123      *          system information fail.
 124      *
 125      * @return true in all cases (this <code>LoginModule</code>
 126      *          should not be ignored).
 127      */
 128     public boolean login() throws LoginException {
 129 
 130         long[] solarisGroups = null;
 131 
 132         ss = new SolarisSystem();
 133 
 134         if (ss == null) {
 135             succeeded = false;
 136             throw new FailedLoginException
 137                                 ("Failed in attempt to import " +
 138                                 "the underlying system identity information");
 139         } else {
 140             userPrincipal = new SolarisPrincipal(ss.getUsername());
 141             UIDPrincipal = new SolarisNumericUserPrincipal(ss.getUid());
 142             GIDPrincipal = new SolarisNumericGroupPrincipal(ss.getGid(), true);
 143             if (ss.getGroups() != null && ss.getGroups().length > 0)
 144                 solarisGroups = ss.getGroups();
 145                 for (int i = 0; i < solarisGroups.length; i++) {
 146                     SolarisNumericGroupPrincipal ngp =
 147                         new SolarisNumericGroupPrincipal
 148                         (solarisGroups[i], false);
 149                     if (!ngp.getName().equals(GIDPrincipal.getName()))
 150                         supplementaryGroups.add(ngp);
 151                 }
 152             if (debug) {
 153                 System.out.println("\t\t[SolarisLoginModule]: " +
 154                         "succeeded importing info: ");
 155                 System.out.println("\t\t\tuid = " + ss.getUid());
 156                 System.out.println("\t\t\tgid = " + ss.getGid());
 157                 solarisGroups = ss.getGroups();
 158                 for (int i = 0; i < solarisGroups.length; i++) {
 159                     System.out.println("\t\t\tsupp gid = " + solarisGroups[i]);
 160                 }
 161             }
 162             succeeded = true;
 163             return true;
 164         }
 165     }
 166 
 167     /**
 168      * Commit the authentication (second phase).
 169      *
 170      * <p> This method is called if the LoginContext's
 171      * overall authentication succeeded
 172      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
 173      * succeeded).
 174      *
 175      * <p> If this LoginModule's own authentication attempt
 176      * succeeded (the importing of the Solaris authentication information
 177      * succeeded), then this method associates the Solaris Principals
 178      * with the <code>Subject</code> currently tied to the
 179      * <code>LoginModule</code>.  If this LoginModule's
 180      * authentication attempted failed, then this method removes
 181      * any state that was originally saved.
 182      *
 183      * <p>
 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</code>
 236      * and <code>commit</code> methods.
 237      *
 238      * <p>
 239      *
 240      * @exception LoginException if the abort fails
 241      *
 242      * @return false if this LoginModule's own login and/or commit attempts
 243      *          failed, and true otherwise.
 244      */
 245     public boolean abort() throws LoginException {
 246         if (debug) {
 247             System.out.println("\t\t[SolarisLoginModule]: " +
 248                 "aborted authentication attempt");
 249         }
 250 
 251         if (succeeded == false) {
 252             return false;
 253         } else if (succeeded == true && commitSucceeded == false) {
 254 
 255             // Clean out state
 256             succeeded = false;
 257             ss = null;
 258             userPrincipal = null;
 259             UIDPrincipal = null;
 260             GIDPrincipal = null;
 261             supplementaryGroups =
 262                         new LinkedList<SolarisNumericGroupPrincipal>();
 263         } else {
 264             // overall authentication succeeded and commit succeeded,
 265             // but someone else's commit failed
 266             logout();
 267         }
 268         return true;
 269     }
 270 
 271     /**
 272      * Logout the user
 273      *
 274      * <p> This method removes the Principals associated
 275      * with the <code>Subject</code>.
 276      *
 277      * <p>
 278      *
 279      * @exception LoginException if the logout fails
 280      *
 281      * @return true in all cases (this <code>LoginModule</code>
 282      *          should not be ignored).
 283      */
 284     public boolean logout() throws LoginException {
 285         if (debug) {
 286             System.out.println("\t\t[SolarisLoginModule]: " +
 287                 "Entering logout");
 288         }
 289         if (subject.isReadOnly()) {
 290             throw new LoginException ("Subject is Readonly");
 291         }
 292         // remove the added Principals from the Subject
 293         subject.getPrincipals().remove(userPrincipal);
 294         subject.getPrincipals().remove(UIDPrincipal);
 295         subject.getPrincipals().remove(GIDPrincipal);
 296         for (int i = 0; i < supplementaryGroups.size(); i++) {
 297             subject.getPrincipals().remove(supplementaryGroups.get(i));
 298         }
 299 
 300         // clean out state
 301         ss = null;
 302         succeeded = false;
 303         commitSucceeded = false;
 304         userPrincipal = null;
 305         UIDPrincipal = null;
 306         GIDPrincipal = null;
 307         supplementaryGroups = new LinkedList<SolarisNumericGroupPrincipal>();
 308 
 309         if (debug) {
 310             System.out.println("\t\t[SolarisLoginModule]: " +
 311                 "logged out Subject");
 312         }
 313         return true;
 314     }
 315 }