com.sshtools.ssh2.AuthenticationProtocol Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see .
*/
package com.sshtools.ssh2;
import java.io.IOException;
import com.sshtools.events.Event;
import com.sshtools.events.EventServiceImplementation;
import com.sshtools.events.J2SSHEventCodes;
import com.sshtools.ssh.SshAuthentication;
import com.sshtools.ssh.SshException;
import com.sshtools.ssh.components.SshKeyExchangeClient;
import com.sshtools.util.ByteArrayReader;
import com.sshtools.util.ByteArrayWriter;
/**
*
*
* Main implementation of the SSH Authentication Protocol. This class is used by
* AuthenticationClient implementations
* and exposes a readMessage() method that is used
* to read authentication method specific messages and
* sendRequest method to send authenticaiton requests.
*
* .
*
* By using these method's the protocol is also able to detect when
* authentication has succeeded or failed and when this happens an AuthenticationResult is thrown. The
* following detailed example shows how to use at the higest level. See the PasswordAuthentication implementation
* for how to implement such a method.
*
*
* try {
* TransportProtocol transport = new TransportProtocol();
* transport.ignoreHostKeyVerification(true);
* transport.startTransportProtocol(new SocketProvider("mars", 10022));
*
* AuthenticationProtocol authentication = new AuthenticationProtocol(
* transport);
*
* authentication.setBannerDisplay(new BannerDisplay() {
* public void displayBanner(String message) {
* System.out.println(message);
*
* try {
* System.out.println("Press enter to continue...");
* System.in.read();
* } catch (Exception e) {
* }
* ;
* }
* });
*
* StringTokenizer tokens = new StringTokenizer(
* authentication.getAuthenticationMethods("lee", "ssh-connection"),
* ",");
*
* int count = 1;
*
* System.out.println("Available authentication methods");
*
* while (tokens.hasMoreElements()) {
* System.out.println(String.valueOf(count++) + ". "
* + tokens.nextElement());
* }
*
* System.out.println("\nAttempting password authentication\n");
*
* PasswordAuthentication pwd = new PasswordAuthentication();
*
* int result;
*
* BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
* do {
* // Get the username and password if we have not already sent it
* if (!pwd.requiresPasswordChange()) {
*
* System.out.print("Username: ");
* pwd.setUsername(reader.readLine());
*
* System.out.print("Password: ");
* pwd.setPassword(reader.readLine());
* } else {
* // We have already failed and need to change the password.
* System.out.println("You need to change your password!");
* System.out.print("New Password: ");
* pwd.setNewPassword(reader.readLine());
* }
*
* result = authentication.authenticate(pwd, "ssh-connection");
*
* } while (result != AuthenticationResult.COMPLETE
* && result != AuthenticationResult.CANCELLED);
*
* System.out.println("Authentication "
* + (result == AuthenticationResult.COMPLETE ? "completed"
* : "cancelled"));
*
* } catch (Throwable t) {
* t.printStackTrace();
* }
*
*
*
*
*
* @author Lee David Painter
*/
public class AuthenticationProtocol {
public final static int SSH_MSG_USERAUTH_REQUEST = 50;
final static int SSH_MSG_USERAUTH_FAILURE = 51;
final static int SSH_MSG_USERAUTH_SUCCESS = 52;
final static int SSH_MSG_USERAUTH_BANNER = 53;
TransportProtocol transport;
BannerDisplay display;
int state = SshAuthentication.FAILED;
/** The name of this service "ssh-userauth" */
public static final String SERVICE_NAME = "ssh-userauth";
public SshKeyExchangeClient getKeyExchange() {
return transport.getKeyExchange();
}
/**
* Construct the protocol using the given transport
*
* @param transport
* @throws SshException
*/
public AuthenticationProtocol(TransportProtocol transport)
throws SshException {
this.transport = transport;
transport.startService("ssh-userauth");
}
/**
* Set a callback interface for banner messages. It is advisable to pause
* processing within the callback implementation to allow the user time to
* read and accept the message.
*
* @param display
*/
public void setBannerDisplay(BannerDisplay display) {
this.display = display;
}
/**
* Read a message from the underlying transport layer. This method processes
* the incoming message to determine whether it is an
* SSH_MSG_USERAUTH_SUCCESS or SSH_MSG_USERAUTH_FAILURE. If these messages
* are detected an AuthenticationResult is thrown.
*
* @return the next available message
* @throws SshException
* @throws AuthenticationResult
*/
public byte[] readMessage() throws SshException, AuthenticationResult {
byte[] msg;
while (processMessage(msg = transport.nextMessage())) {
;
}
return msg;
}
/**
* Authenticate using the mechanism provided.
*
* @param auth
* @param servicename
* @return Any of the constants defined in AuthenticationResult
* @throws SshException
*/
public int authenticate(AuthenticationClient auth, String servicename)
throws SshException {
try {
auth.authenticate(this, servicename);
readMessage();
transport
.disconnect(TransportProtocol.PROTOCOL_ERROR,
"Unexpected response received from Authentication Protocol");
throw new SshException(
"Unexpected response received from Authentication Protocol",
SshException.PROTOCOL_VIOLATION);
} catch (AuthenticationResult result) {
state = result.getResult();
if (state == SshAuthentication.COMPLETE)
transport.completedAuthentication();
return state;
}
}
/**
* Get a list of available authentication methods for the user. It is
* advisable to call this method after contsructing the protocol instance
* and setting a BannerDisplay. If the
* server has a banner message to display it is most likely that the server
* will send it before completing this list.
*
* @param username
* @param servicename
* @return a comma delimited list of authentication methods.
* @throws SshException
*/
public String getAuthenticationMethods(String username, String servicename)
throws SshException {
sendRequest(username, servicename, "none", null);
try {
readMessage();
transport
.disconnect(TransportProtocol.PROTOCOL_ERROR,
"Unexpected response received from Authentication Protocol");
throw new SshException(
"Unexpected response received from Authentication Protocol",
SshException.PROTOCOL_VIOLATION);
} catch (AuthenticationResult result) {
state = result.getResult();
EventServiceImplementation
.getInstance()
.fireEvent(
(new Event(
this,
J2SSHEventCodes.EVENT_AUTHENTICATION_METHODS_RECEIVED,
true))
.addAttribute(
J2SSHEventCodes.ATTRIBUTE_AUTHENTICATION_METHODS,
result.getAuthenticationMethods()));
return result.getAuthenticationMethods();
}
}
/**
* Send an authentication request. This sends an SSH_MSG_USERAUTH_REQUEST
* message.
*
* @param username
* @param servicename
* @param methodname
* @param requestdata
* the request data as defined by the authentication
* specification
* @throws SshException
*/
public void sendRequest(String username, String servicename,
String methodname, byte[] requestdata) throws SshException {
ByteArrayWriter msg = new ByteArrayWriter();
try {
msg.write(SSH_MSG_USERAUTH_REQUEST);
msg.writeString(username);
msg.writeString(servicename);
msg.writeString(methodname);
if (requestdata != null) {
msg.write(requestdata);
}
transport.sendMessage(msg.toByteArray(), true);
} catch (IOException ex) {
throw new SshException(ex, SshException.INTERNAL_ERROR);
} finally {
try {
msg.close();
} catch (IOException e) {
}
}
}
/**
* Determine whether the protocol has made a sucessfull authentication
* attempt.
*
* @return true
if the user is authenticated, otherwise
* false
*/
public boolean isAuthenticated() {
return state == SshAuthentication.COMPLETE;
}
public byte[] getSessionIdentifier() {
return transport.getSessionIdentifier();
}
private boolean processMessage(byte[] msg) throws SshException,
AuthenticationResult {
try {
switch (msg[0]) {
case SSH_MSG_USERAUTH_FAILURE: {
ByteArrayReader bar = new ByteArrayReader(msg); // , 6,
// msg.length -
// 6);
try {
bar.skip(1);
String auths = bar.readString();
if (bar.read() == 0) {
EventServiceImplementation.getInstance().fireEvent(
new Event(this,
J2SSHEventCodes.EVENT_USERAUTH_FAILURE,
true));
throw new AuthenticationResult(
SshAuthentication.FAILED, auths);
}
EventServiceImplementation
.getInstance()
.fireEvent(
new Event(
this,
J2SSHEventCodes.EVENT_USERAUTH_FURTHER_AUTHENTICATION_REQUIRED,
true));
throw new AuthenticationResult(
SshAuthentication.FURTHER_AUTHENTICATION_REQUIRED,
auths);
} finally {
bar.close();
}
}
case SSH_MSG_USERAUTH_SUCCESS: {
EventServiceImplementation.getInstance().fireEvent(
new Event(this, J2SSHEventCodes.EVENT_USERAUTH_SUCCESS,
true));
throw new AuthenticationResult(SshAuthentication.COMPLETE);
}
case SSH_MSG_USERAUTH_BANNER: {
ByteArrayReader bar = new ByteArrayReader(msg); // , 6,
// msg.length -
// 6);
try {
bar.skip(1);
// Show the banner on the current display or print to stdout
if (display != null) {
display.displayBanner(bar.readString());
}
return true;
} finally {
bar.close();
}
}
default:
return false;
}
} catch (IOException ex) {
throw new SshException(ex, SshException.INTERNAL_ERROR);
}
}
public void sendMessage(byte[] messg) throws SshException {
transport.sendMessage(messg, true);
}
public String getHost() {
return transport.provider.getHost();
}
}