aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java')
-rw-r--r--src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java438
1 files changed, 438 insertions, 0 deletions
diff --git a/src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java b/src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java
new file mode 100644
index 0000000..d559aaa
--- /dev/null
+++ b/src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.Vector;
+
+import ch.ethz.ssh2.InteractiveCallback;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.packets.PacketServiceAccept;
+import ch.ethz.ssh2.packets.PacketServiceRequest;
+import ch.ethz.ssh2.packets.PacketUserauthBanner;
+import ch.ethz.ssh2.packets.PacketUserauthFailure;
+import ch.ethz.ssh2.packets.PacketUserauthInfoRequest;
+import ch.ethz.ssh2.packets.PacketUserauthInfoResponse;
+import ch.ethz.ssh2.packets.PacketUserauthRequestInteractive;
+import ch.ethz.ssh2.packets.PacketUserauthRequestNone;
+import ch.ethz.ssh2.packets.PacketUserauthRequestPassword;
+import ch.ethz.ssh2.packets.PacketUserauthRequestPublicKey;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.DSASignature;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.signature.RSASignature;
+import ch.ethz.ssh2.transport.MessageHandler;
+import ch.ethz.ssh2.transport.TransportManager;
+
+/**
+ * AuthenticationManager.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class AuthenticationManager implements MessageHandler
+{
+ private TransportManager tm;
+
+ private final List<byte[]> packets = new Vector<byte[]>();
+ private boolean connectionClosed = false;
+
+ private String banner;
+
+ private String[] remainingMethods = new String[0];
+ private boolean isPartialSuccess = false;
+
+ private boolean authenticated = false;
+ private boolean initDone = false;
+
+ public AuthenticationManager(TransportManager tm)
+ {
+ this.tm = tm;
+ }
+
+ boolean methodPossible(String methName)
+ {
+ if (remainingMethods == null)
+ return false;
+
+ for (int i = 0; i < remainingMethods.length; i++)
+ {
+ if (remainingMethods[i].compareTo(methName) == 0)
+ return true;
+ }
+ return false;
+ }
+
+ byte[] deQueue() throws IOException
+ {
+ boolean wasInterrupted = false;
+
+ try
+ {
+ synchronized (packets)
+ {
+ while (packets.size() == 0)
+ {
+ if (connectionClosed)
+ throw (IOException) new IOException("The connection is closed.").initCause(tm
+ .getReasonClosedCause());
+
+ try
+ {
+ packets.wait();
+ }
+ catch (InterruptedException ign)
+ {
+ wasInterrupted = true;
+ }
+ }
+ byte[] res = packets.get(0);
+ packets.remove(0);
+ return res;
+ }
+ }
+ finally
+ {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ byte[] getNextMessage() throws IOException
+ {
+ while (true)
+ {
+ byte[] msg = deQueue();
+
+ if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
+ return msg;
+
+ PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
+
+ banner = sb.getBanner();
+ }
+ }
+
+ public String[] getRemainingMethods(String user) throws IOException
+ {
+ initialize(user);
+ return remainingMethods;
+ }
+
+ public String getBanner()
+ {
+ return banner;
+
+ }
+ public boolean getPartialSuccess()
+ {
+ return isPartialSuccess;
+ }
+
+ private boolean initialize(String user) throws IOException
+ {
+ if (initDone == false)
+ {
+ tm.registerMessageHandler(this, 0, 255);
+
+ PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
+ tm.sendMessage(sr.getPayload());
+
+ byte[] msg = getNextMessage();
+ new PacketServiceAccept(msg, 0, msg.length);
+
+ PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
+ tm.sendMessage(urn.getPayload());
+
+ msg = getNextMessage();
+
+ initDone = true;
+
+ if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+ return false;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
+ }
+ return authenticated;
+ }
+
+ public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
+ throws IOException
+ {
+ try
+ {
+ initialize(user);
+
+ if (methodPossible("publickey") == false)
+ throw new IOException("Authentication method publickey not supported by the server at this stage.");
+
+ Object key = PEMDecoder.decode(PEMPrivateKey, password);
+
+ if (key instanceof DSAPrivateKey)
+ {
+ DSAPrivateKey pk = (DSAPrivateKey) key;
+
+ byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
+
+ TypesWriter tw = new TypesWriter();
+
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-dss");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+
+ byte[] msg = tw.getBytes();
+
+ DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
+
+ byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-dss", pk_enc, ds_enc);
+ tm.sendMessage(ua.getPayload());
+ }
+ else if (key instanceof RSAPrivateKey)
+ {
+ RSAPrivateKey pk = (RSAPrivateKey) key;
+
+ byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());
+
+ TypesWriter tw = new TypesWriter();
+ {
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-rsa");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+ }
+
+ byte[] msg = tw.getBytes();
+
+ RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);
+
+ byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-rsa", pk_enc, rsa_sig_enc);
+ tm.sendMessage(ua.getPayload());
+ }
+ else
+ {
+ throw new IOException("Unknown private key type returned by the PEM decoder.");
+ }
+
+ byte[] ar = getNextMessage();
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
+
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
+ }
+ }
+
+ public boolean authenticateNone(String user) throws IOException
+ {
+ try
+ {
+ initialize(user);
+ return authenticated;
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("None authentication failed.").initCause(e);
+ }
+ }
+
+ public boolean authenticatePassword(String user, String pass) throws IOException
+ {
+ try
+ {
+ initialize(user);
+
+ if (methodPossible("password") == false)
+ throw new IOException("Authentication method password not supported by the server at this stage.");
+
+ PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
+ tm.sendMessage(ua.getPayload());
+
+ byte[] ar = getNextMessage();
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
+
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("Password authentication failed.").initCause(e);
+ }
+ }
+
+ public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
+ {
+ try
+ {
+ initialize(user);
+
+ if (methodPossible("keyboard-interactive") == false)
+ throw new IOException(
+ "Authentication method keyboard-interactive not supported by the server at this stage.");
+
+ if (submethods == null)
+ submethods = new String[0];
+
+ PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
+ submethods);
+
+ tm.sendMessage(ua.getPayload());
+
+ while (true)
+ {
+ byte[] ar = getNextMessage();
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
+ {
+ PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
+
+ String[] responses;
+
+ try
+ {
+ responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
+ .getPrompt(), pui.getEcho());
+ }
+ catch (Exception e)
+ {
+ throw (IOException) new IOException("Exception in callback.").initCause(e);
+ }
+
+ if (responses == null)
+ throw new IOException("Your callback may not return NULL!");
+
+ PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
+ tm.sendMessage(puir.getPayload());
+
+ continue;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
+ }
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
+ }
+ }
+
+ public void handleMessage(byte[] msg, int msglen) throws IOException
+ {
+ synchronized (packets)
+ {
+ if (msg == null)
+ {
+ connectionClosed = true;
+ }
+ else
+ {
+ byte[] tmp = new byte[msglen];
+ System.arraycopy(msg, 0, tmp, 0, msglen);
+ packets.add(tmp);
+ }
+
+ packets.notifyAll();
+
+ if (packets.size() > 5)
+ {
+ connectionClosed = true;
+ throw new IOException("Error, peer is flooding us with authentication packets.");
+ }
+ }
+ }
+}