org.vngx.jsch.userauth.UserAuth Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vngx-jsch Show documentation
Show all versions of vngx-jsch Show documentation
**vngx-jsch** (beta) is an updated version of the popular JSch SSH library
written in pure Java. It has been updated to Java 6 with all the latest
language features and improved code clarity.
The newest version!
/*
* Copyright (c) 2002-2010 Atsuhiko Yamanaka, JCraft,Inc. All rights reserved.
* Copyright (c) 2010-2011 Michael Laudati, N1 Concepts LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
* INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.vngx.jsch.userauth;
import static org.vngx.jsch.constants.TransportLayerProtocol.*;
import org.vngx.jsch.Buffer;
import org.vngx.jsch.JSch;
import org.vngx.jsch.Packet;
import org.vngx.jsch.Session;
import org.vngx.jsch.UserInfo;
import org.vngx.jsch.Util;
import org.vngx.jsch.config.SessionConfig;
import org.vngx.jsch.algorithm.Algorithms;
import org.vngx.jsch.constants.SSHConstants;
import org.vngx.jsch.constants.UserAuthProtocol;
import org.vngx.jsch.exception.JSchException;
import org.vngx.jsch.util.Logger.Level;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
/**
* Base implementation of UserAuth
which performs the user
* authorization for a given SSH session. Implementations can define how the
* user should be authorized for the session. This class is part of the public
* JSch API to allow developers to provide other mechanisms for user auth.
*
* The server drives the authentication by telling the client which
* authentication methods can be used to continue the exchange at any given
* time. The client has the freedom to try the methods listed by the server in
* any order. This gives the server complete control over the authentication
* process if desired, but also gives enough flexibility for the client to use
* the methods it supports or that are most convenient for the user, when
* multiple methods are offered by the server.
*
* Authentication methods are identified by their name. The "none" method is
* reserved, and MUST NOT be listed as supported. However, it MAY be sent by
* the client. The server MUST always reject this request, unless the client is
* to be granted access without any authentication, in which case, the server
* MUST accept this request. The main purpose of sending this request is to get
* the list of supported methods from the server.
*
* The server SHOULD have a timeout for authentication and disconnect if the
* authentication has not been accepted within the timeout period. The
* RECOMMENDED timeout period is 10 minutes. Additionally, the implementation
* SHOULD limit the number of failed authentication attempts a client may
* perform in a single session (the RECOMMENDED limit is 20 attempts). If the
* threshold is exceeded, the server SHOULD disconnect.
*
* All authentication requests MUST use the following message format. Only the
* first few fields are defined; the remaining fields depend on the
* authentication method.
*
* byte SSH_MSG_USERAUTH_REQUEST
* string user name in ISO-10646 UTF-8 encoding [RFC3629]
* string service name in US-ASCII
* string method name in US-ASCII
* .... method specific fields
*
* The 'user name' and 'service name' are repeated in every new authentication
* attempt, and MAY change. The server implementation MUST carefully check them
* in every message, and MUST flush any accumulated authentication states if
* they change. If it is unable to flush an authentication state, it MUST
* disconnect if the 'user name' or 'service name' changes.
*
* The 'service name' specifies the service to start after authentication. There
* may be several different authenticated services provided. If the requested
* service is not available, the server MAY disconnect immediately or at any
* later time. Sending a proper disconnect message is RECOMMENDED. In any case,
* if the service does not exist, authentication MUST NOT be accepted.
*
* If the requested 'user name' does not exist, the server MAY disconnect, or
* MAY send a bogus list of acceptable authentication 'method name' values, but
* never accept any. This makes it possible for the server to avoid disclosing
* information on which accounts exist. In any case, if the 'user name' does
* not exist, the authentication request MUST NOT be accepted.
*
* While there is usually little point for clients to send requests that the
* server does not list as acceptable, sending such requests is not an error,
* and the server SHOULD simply reject requests that it does not recognize.
*
* An authentication request MAY result in a further exchange of messages. All
* such messages depend on the authentication 'method name' used, and the client
* MAY at any time continue with a new SSH_MSG_USERAUTH_REQUEST message, in
* which case the server MUST abandon the previous authentication attempt and
* continue with the new one.
*
* The following 'method name' values are defined.
* "publickey" REQUIRED
* "password" OPTIONAL
* "hostbased" OPTIONAL
* "none" NOT RECOMMENDED
*
* @author Atsuhiko Yamanaka
* @author Michael Laudati
*/
public abstract class UserAuth implements UserAuthProtocol {
/** SSH constant name for "none" user auth method. */
public static final String NONE = "none";
/** SSH constant name for "password" user auth method. */
public static final String PASSWORD = "password";
/** SSH constant name for "publickey" user auth method. */
public static final String PUBLICKEY = "publickey";
/** SSH constant name for "keyboard-interactive" user auth method. */
public static final String KEYBOARD_INTERACTIVE = "keyboard-interactive";
/** SSH constant name for "gssapi-with-mic" user auth method. */
public static final String GSSAPI_WITH_MIC = "gssapi-with-mic";
/** SSH constant message name for "ssh-userauth". */
protected static final String SSH_USERAUTH = "ssh-userauth";
/** SSH constant message name for "ssh-connection". */
protected static final String SSH_CONNECTION = "ssh-connection";
/* Each instance of UserAuth should have it's own, unshared buffer/packet
* to prevent leakage of passwords/passphrases, etc. If a buffer instances
* are shared, it might not be cleared allowing next instance to see
* confidential data.
*/
/** Buffer from session for sending user auth requests. */
protected final Buffer _buffer = new Buffer();
/** Packet from session for sending user auth requests. */
protected final Packet _packet = new Packet(_buffer);
/** Interface for UI for user. */
protected UserInfo _userinfo;
/**
* Performs the user authentication as defined by the implementation for the
* specified session.
*
* @param session
* @param password
* @return true if user is authenticated
* @throws Exception if any errors occur or if user cannot be authenticated
*/
protected boolean authUser(Session session, byte[] password) throws Exception {
_userinfo = session.getUserInfo();
return true;
}
/**
* Handles the user auth banner message.
*
* In some jurisdictions, sending a warning message before authentication
* may be relevant for getting legal protection. Many UNIX machines, for
* example, normally display text from /etc/issue, use TCP wrappers, or
* similar software to display a banner before issuing a login prompt.
*
* The SSH server may send an SSH_MSG_USERAUTH_BANNER message at any time
* after this authentication protocol starts and before authentication is
* successful. This message contains text to be displayed to the client
* user before authentication is attempted.
*
* By default, the client SHOULD display the 'message' on the screen.
* However, since the 'message' is likely to be sent for every login
* attempt, and since some client software will need to open a separate
* window for this warning, the client software may allow the user to
* explicitly disable the display of banners from the server. The 'message'
* may consist of multiple lines, with line breaks indicated by CRLF pairs.
*/
protected void userAuthBanner() {
// int packet-length
// byte padding-length
// byte SSH_MSG_USERAUTH_BANNER(53)
// string message in ISO-10646 UTF-8 encoding [RFC3629]
// string language tag [RFC3066]
_buffer.getInt();
_buffer.getShort();
if( _userinfo != null ) {
// TODO Add configuration flag to disable displaying banner message
_userinfo.showMessage(Util.byte2str(_buffer.getString()));
//_buffer.getString(); // language
}
}
/**
* Handles the user auth failure message.
*
* The 'authentications that can continue' is a comma-separated name-list of
* authentication 'method name' values that may productively continue the
* authentication dialog.
*
* It is RECOMMENDED that servers only include those 'method name' values in
* the name-list that are actually useful. However, it is not illegal to
* include 'method name' values that cannot be used to authenticate the
* user.
*
* Already successfully completed authentications SHOULD NOT be included in
* the name-list, unless they should be performed again for some reason.
*
* The value of 'partial success' MUST be TRUE if the authentication request
* to which this is a response was successful. It MUST be FALSE if the
* request was not successfully processed.
*
* @throws PartialAuthException if partial success flag not zero
*/
protected void userAuthFailure() throws PartialAuthException {
// int packet-length
// byte padding-length
// byte SSH_MSG_USERAUTH_FAILURE(51)
// string name list (of auth methods that can continue)
// byte partial success flag
_buffer.getInt();
_buffer.getShort();
String nameList = Util.byte2str(_buffer.getString());
if( this instanceof UserAuthNone || _buffer.getByte() != 0 ) { // Partial success flag
throw new PartialAuthException(nameList);
}
}
/**
* Starts the SSH user authentication service and attempts to authorize the
* user with the preferred list of authentication methods. The client
* preferences for user authentication methods can be set in the global or
* session configuration as a priority sorted list of method names with the
* property SessionConfig.PREFERRED_AUTHS
. If the user is
* successfully authenticated by the server then the method returns true.
* If authentication fails or is canceled by the user, then an exception is
* thrown.
*
* @param session to perform user authentication
* @param password
* @return true if the user is successfully authenticated for the session
* @throws Exception if any errors occur or if the user is not authenticated
*/
public static boolean authenticateUser(Session session, byte[] password) throws Exception {
// Retrieve list of preferred client user auth methods
LinkedList clientMethods = new LinkedList(session.getConfig().getList(SessionConfig.PREFFERED_AUTHS));
if( clientMethods.isEmpty() ) {
throw new JSchException("UserAuth failure, no client preferred authentication methods in config");
}
clientMethods.addFirst(UserAuth.NONE); // Add 'none' first to retrieve available server methods
// Request User Auth service to being auth process
sendUserAuthInit(session);
boolean authCanceled = false;
Set serverMethods = new HashSet(Arrays.asList(UserAuth.NONE));
UserAuth userAuth;
// Attempt to perform user auth using each of the client preferred methods
for( String userAuthMethod : clientMethods ) {
// 'none' can always be sent, otherwise check if auth method is supported by server
// TODO Add config override to allow sending auth methods even if not listed as supported
if( !UserAuth.NONE.equals(userAuthMethod) && !serverMethods.contains(userAuthMethod) ) {
continue; // Server does not support user auth method, skip
}
JSch.getLogger().log(Level.INFO, "Authentication methods that can continue: " + (serverMethods != null ? serverMethods : NONE));
JSch.getLogger().log(Level.INFO, "Current authentication method: " + userAuthMethod);
try { // Attempt to create UserAuth method instance
userAuth = session.getConfig().getClassImpl(Algorithms.USERAUTH + userAuthMethod);
} catch(Exception e) {
JSch.getLogger().log(Level.WARN, "Failed to load UserAuth method '" + userAuthMethod + "': "+e, e);
continue; // Attempt next user auth method since this one failed/not supported...
}
authCanceled = false;
try {
// Attempt to authenticate user with method
if( userAuth.authUser(session, password) ) {
JSch.getLogger().log(Level.INFO, "Authentication succeeded, method: " + userAuthMethod);
return true; // Return true since user has been authed!
}
} catch(AuthCancelException ee) {
authCanceled = true;
} catch(PartialAuthException pe) {
authCanceled = false;
serverMethods = pe.getUserAuthMethods(); // Update server list of user auth methods
}
}
// If not authenticated, throw appropriate exception
if( authCanceled ) {
throw new AuthCancelException("User authentication canceled by user");
}
throw new JSchException("User authentication failed");
}
/**
* Sends an SSH request to start the user authentication service. If the
* request fails or is rejected by server, an exception is thrown.
*
* @param session to request user auth service
* @throws Exception if request fails or services is rejected by server
*/
static void sendUserAuthInit(Session session) throws Exception {
// send user auth request
// byte SSH_MSG_SERVICE_REQUEST(5)
// string service name "ssh-userauth"
Buffer buffer = new Buffer(100);
Packet packet = new Packet(buffer);
packet.reset();
buffer.putByte(SSH_MSG_SERVICE_REQUEST);
buffer.putString(SSH_USERAUTH);
session.write(packet);
JSch.getLogger().log(Level.INFO, "SSH_MSG_SERVICE_REQUEST for UserAuth sent");
// receive user auth response
// byte SSH_MSG_SERVICE_ACCEPT(6)
// string service name
if( session.read(buffer).getCommand() != SSH_MSG_SERVICE_ACCEPT ) {
throw new JSchException("UserAuth service failed, expected SSH_MSG_SERVICE_ACCEPT(6): "+buffer.getCommand());
}
JSch.getLogger().log(Level.INFO, "SSH_MSG_SERVICE_ACCEPT for UserAuth received");
}
/**
* Creates the prompt for asking username/password.
*
* @param session
* @return password prompt
*/
static String createPasswordPrompt(Session session) {
StringBuilder sbuffer = new StringBuilder(50);
sbuffer.append(session.getUserName()).append('@').append(session.getHost());
if( session.getPort() != SSHConstants.DEFAULT_SSH_PORT ) {
sbuffer.append(':').append(session.getPort());
}
return sbuffer.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy