947 lines · 34.2 KB · Java
RawDownload
/* * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.security.auth.module; import javax.security.auth.x500.X500Principal;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.PushbackInputStream;import java.net.MalformedURLException;import java.net.URL;import java.security.AuthProvider;import java.security.GeneralSecurityException;import java.security.Key;import java.security.KeyStore;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.Principal;import java.security.PrivateKey;import java.security.Provider;import java.security.UnrecoverableKeyException;import java.security.cert.*;import java.security.cert.X509Certificate;import java.util.Arrays;import java.util.Iterator;import java.util.LinkedList;import java.util.Map;import java.util.ResourceBundle;import javax.security.auth.Destroyable;import javax.security.auth.DestroyFailedException;import javax.security.auth.Subject;import javax.security.auth.x500.*;import javax.security.auth.Subject;import javax.security.auth.x500.*;import javax.security.auth.callback.Callback;import javax.security.auth.callback.CallbackHandler;import javax.security.auth.callback.ConfirmationCallback;import javax.security.auth.callback.NameCallback;import javax.security.auth.callback.PasswordCallback;import javax.security.auth.callback.TextOutputCallback;import javax.security.auth.callback.UnsupportedCallbackException;import javax.security.auth.login.FailedLoginException;import javax.security.auth.login.LoginException;import javax.security.auth.spi.LoginModule; import sun.security.util.AuthResources;import sun.security.util.Password; /** * Provides a JAAS login module that prompts for a key store alias and * populates the subject with the alias's principal and credentials. Stores * an <code>X500Principal</code> for the subject distinguished name of the * first certificate in the alias's credentials in the subject's principals, * the alias's certificate path in the subject's public credentials, and a * <code>X500PrivateCredential</code> whose certificate is the first * certificate in the alias's certificate path and whose private key is the * alias's private key in the subject's private credentials. <p> * * Recognizes the following options in the configuration file: * <dl> * * <dt> <code>keyStoreURL</code> </dt> * <dd> A URL that specifies the location of the key store.  Defaults to *      a URL pointing to the .keystore file in the directory specified by the *      <code>user.home</code> system property.  The input stream from this *      URL is passed to the <code>KeyStore.load</code> method. *      "NONE" may be specified if a <code>null</code> stream must be *      passed to the <code>KeyStore.load</code> method. *      "NONE" should be specified if the KeyStore resides *      on a hardware token device, for example.</dd> * * <dt> <code>keyStoreType</code> </dt> * <dd> The key store type.  If not specified, defaults to the result of *      calling <code>KeyStore.getDefaultType()</code>. *      If the type is "PKCS11", then keyStoreURL must be "NONE" *      and privateKeyPasswordURL must not be specified.</dd> * * <dt> <code>keyStoreProvider</code> </dt> * <dd> The key store provider.  If not specified, uses the standard search *      order to find the provider. </dd> * * <dt> <code>keyStoreAlias</code> </dt> * <dd> The alias in the key store to login as.  Required when no callback *      handler is provided.  No default value. </dd> * * <dt> <code>keyStorePasswordURL</code> </dt> * <dd> A URL that specifies the location of the key store password.  Required *      when no callback handler is provided and *      <code>protected</code> is false. *      No default value. </dd> * * <dt> <code>privateKeyPasswordURL</code> </dt> * <dd> A URL that specifies the location of the specific private key password *      needed to access the private key for this alias. *      The keystore password *      is used if this value is needed and not specified. </dd> * * <dt> <code>protected</code> </dt> * <dd> This value should be set to "true" if the KeyStore *      has a separate, protected authentication path *      (for example, a dedicated PIN-pad attached to a smart card). *      Defaults to "false". If "true" keyStorePasswordURL and *      privateKeyPasswordURL must not be specified.</dd> * * </dl> */public class KeyStoreLoginModule implements LoginModule {    static final java.util.ResourceBundle rb =        java.util.ResourceBundle.getBundle("sun.security.util.AuthResources");     /* -- Fields -- */     private static final int UNINITIALIZED = 0;    private static final int INITIALIZED = 1;    private static final int AUTHENTICATED = 2;    private static final int LOGGED_IN = 3;     private static final int PROTECTED_PATH = 0;    private static final int TOKEN = 1;    private static final int NORMAL = 2;     private static final String NONE = "NONE";    private static final String P11KEYSTORE = "PKCS11";     private static final TextOutputCallback bannerCallback =                new TextOutputCallback                        (TextOutputCallback.INFORMATION,                        rb.getString("Please.enter.keystore.information"));    private final ConfirmationCallback confirmationCallback =                new ConfirmationCallback                        (ConfirmationCallback.INFORMATION,                        ConfirmationCallback.OK_CANCEL_OPTION,                        ConfirmationCallback.OK);     private Subject subject;    private CallbackHandler callbackHandler;    private Map sharedState;    private Map<String, ?> options;     private char[] keyStorePassword;    private char[] privateKeyPassword;    private KeyStore keyStore;     private String keyStoreURL;    private String keyStoreType;    private String keyStoreProvider;    private String keyStoreAlias;    private String keyStorePasswordURL;    private String privateKeyPasswordURL;    private boolean debug;    private javax.security.auth.x500.X500Principal principal;    private Certificate[] fromKeyStore;    private java.security.cert.CertPath certP = null;    private X500PrivateCredential privateCredential;    private int status = UNINITIALIZED;    private boolean nullStream = false;    private boolean token = false;    private boolean protectedPath = false;     /* -- Methods -- */     /**     * Initialize this <code>LoginModule</code>.     *     * <p>     *     * @param subject the <code>Subject</code> to be authenticated. <p>     *     * @param callbackHandler a <code>CallbackHandler</code> for communicating     *                  with the end user (prompting for usernames and     *                  passwords, for example),     *                  which may be <code>null</code>. <p>     *     * @param sharedState shared <code>LoginModule</code> state. <p>     *     * @param options options specified in the login     *                  <code>Configuration</code> for this particular     *                  <code>LoginModule</code>.     */     public void initialize(Subject subject,                           CallbackHandler callbackHandler,                           Map<String,?> sharedState,                           Map<String,?> options)    {        this.subject = subject;        this.callbackHandler = callbackHandler;        this.sharedState = sharedState;        this.options = options;         processOptions();        status = INITIALIZED;    }     private void processOptions() {        keyStoreURL = (String) options.get("keyStoreURL");        if (keyStoreURL == null) {            keyStoreURL =                "file:" +                System.getProperty("user.home").replace(                    File.separatorChar, '/') +                '/' + ".keystore";        } else if (NONE.equals(keyStoreURL)) {            nullStream = true;        }        keyStoreType = (String) options.get("keyStoreType");        if (keyStoreType == null) {            keyStoreType = KeyStore.getDefaultType();        }        if (P11KEYSTORE.equalsIgnoreCase(keyStoreType)) {            token = true;        }         keyStoreProvider = (String) options.get("keyStoreProvider");         keyStoreAlias = (String) options.get("keyStoreAlias");         keyStorePasswordURL = (String) options.get("keyStorePasswordURL");         privateKeyPasswordURL = (String) options.get("privateKeyPasswordURL");         protectedPath = "true".equalsIgnoreCase((String)options.get                                        ("protected"));         debug = "true".equalsIgnoreCase((String) options.get("debug"));        if (debug) {            debugPrint(null);            debugPrint("keyStoreURL=" + keyStoreURL);            debugPrint("keyStoreType=" + keyStoreType);            debugPrint("keyStoreProvider=" + keyStoreProvider);            debugPrint("keyStoreAlias=" + keyStoreAlias);            debugPrint("keyStorePasswordURL=" + keyStorePasswordURL);            debugPrint("privateKeyPasswordURL=" + privateKeyPasswordURL);            debugPrint("protectedPath=" + protectedPath);            debugPrint(null);        }    }     /**     * Authenticate the user.     *     * <p> Get the Keystore alias and relevant passwords.     * Retrieve the alias's principal and credentials from the Keystore.     *     * <p>     *     * @exception FailedLoginException if the authentication fails. <p>     *     * @return true in all cases (this <code>LoginModule</code>     *          should not be ignored).     */     public boolean login() throws LoginException {        switch (status) {        case UNINITIALIZED:        default:            throw new LoginException("The login module is not initialized");        case INITIALIZED:        case AUTHENTICATED:             if (token && !nullStream) {                throw new LoginException                        ("if keyStoreType is " + P11KEYSTORE +                        " then keyStoreURL must be " + NONE);            }             if (token && privateKeyPasswordURL != null) {                throw new LoginException                        ("if keyStoreType is " + P11KEYSTORE +                        " then privateKeyPasswordURL must not be specified");            }             if (protectedPath &&                (keyStorePasswordURL != null ||                        privateKeyPasswordURL != null)) {                throw new LoginException                        ("if protected is true then keyStorePasswordURL and " +                        "privateKeyPasswordURL must not be specified");            }             // get relevant alias and password info             if (protectedPath) {                getAliasAndPasswords(PROTECTED_PATH);            } else if (token) {                getAliasAndPasswords(TOKEN);            } else {                getAliasAndPasswords(NORMAL);            }             // log into KeyStore to retrieve data,            // then clear passwords             try {                getKeyStoreInfo();            } finally {                if (privateKeyPassword != null &&                    privateKeyPassword != keyStorePassword) {                    Arrays.fill(privateKeyPassword, '\0');                    privateKeyPassword = null;                }                if (keyStorePassword != null) {                    Arrays.fill(keyStorePassword, '\0');                    keyStorePassword = null;                }            }            status = AUTHENTICATED;            return true;        case LOGGED_IN:            return true;        }    }     /** Get the alias and passwords to use for looking up in the KeyStore. */    private void getAliasAndPasswords(int env) throws LoginException {        if (callbackHandler == null) {             // No callback handler - check for alias and password options             switch (env) {            case PROTECTED_PATH:                checkAlias();                break;            case TOKEN:                checkAlias();                checkStorePass();                break;            case NORMAL:                checkAlias();                checkStorePass();                checkKeyPass();                break;            }         } else {             // Callback handler available - prompt for alias and passwords             NameCallback aliasCallback;            if (keyStoreAlias == null || keyStoreAlias.length() == 0) {                aliasCallback = new NameCallback(                                        rb.getString("Keystore.alias."));            } else {                aliasCallback =                    new NameCallback(rb.getString("Keystore.alias."),                                     keyStoreAlias);            }             PasswordCallback storePassCallback = null;            PasswordCallback keyPassCallback = null;             switch (env) {            case PROTECTED_PATH:                break;            case NORMAL:                keyPassCallback = new PasswordCallback                    (rb.getString("Private.key.password.optional."), false);                // fall thru            case TOKEN:                storePassCallback = new PasswordCallback                    (rb.getString("Keystore.password."), false);                break;            }            prompt(aliasCallback, storePassCallback, keyPassCallback);        }         if (debug) {            debugPrint("alias=" + keyStoreAlias);        }    }     private void checkAlias() throws LoginException {        if (keyStoreAlias == null) {            throw new LoginException                ("Need to specify an alias option to use " +                "KeyStoreLoginModule non-interactively.");        }    }     private void checkStorePass() throws LoginException {        if (keyStorePasswordURL == null) {            throw new LoginException                ("Need to specify keyStorePasswordURL option to use " +                "KeyStoreLoginModule non-interactively.");        }        InputStream in = null;        try {            in = new URL(keyStorePasswordURL).openStream();            keyStorePassword = Password.readPassword(in);        } catch (IOException e) {            LoginException le = new LoginException                ("Problem accessing keystore password \"" +                keyStorePasswordURL + "\"");            le.initCause(e);            throw le;        } finally {            if (in != null) {                try {                    in.close();                } catch (IOException ioe) {                    LoginException le = new LoginException(                        "Problem closing the keystore password stream");                    le.initCause(ioe);                    throw le;                }            }        }    }     private void checkKeyPass() throws LoginException {        if (privateKeyPasswordURL == null) {            privateKeyPassword = keyStorePassword;        } else {            InputStream in = null;            try {                in = new URL(privateKeyPasswordURL).openStream();                privateKeyPassword = Password.readPassword(in);            } catch (IOException e) {                LoginException le = new LoginException                        ("Problem accessing private key password \"" +                        privateKeyPasswordURL + "\"");                le.initCause(e);                throw le;            } finally {                if (in != null) {                    try {                        in.close();                    } catch (IOException ioe) {                        LoginException le = new LoginException(                            "Problem closing the private key password stream");                        le.initCause(ioe);                        throw le;                    }                }            }        }    }     private void prompt(NameCallback aliasCallback,                        PasswordCallback storePassCallback,                        PasswordCallback keyPassCallback)                throws LoginException {         if (storePassCallback == null) {             // only prompt for alias             try {                callbackHandler.handle(                    new Callback[] {                        bannerCallback, aliasCallback, confirmationCallback                    });            } catch (IOException e) {                LoginException le = new LoginException                        ("Problem retrieving keystore alias");                le.initCause(e);                throw le;            } catch (UnsupportedCallbackException e) {                throw new LoginException(                    "Error: " + e.getCallback().toString() +                    " is not available to retrieve authentication " +                    " information from the user");            }             int confirmationResult = confirmationCallback.getSelectedIndex();             if (confirmationResult == ConfirmationCallback.CANCEL) {                throw new LoginException("Login cancelled");            }             saveAlias(aliasCallback);         } else if (keyPassCallback == null) {             // prompt for alias and key store password

Lines 1–500 of 947.