Mercurial > 510Connectbot
diff src/com/trilead/ssh2/auth/AuthenticationManager.java @ 0:0ce5cc452d02
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 22 May 2014 10:41:19 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/trilead/ssh2/auth/AuthenticationManager.java Thu May 22 10:41:19 2014 -0700 @@ -0,0 +1,378 @@ + +package com.trilead.ssh2.auth; + +import java.io.IOException; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Vector; + +import com.trilead.ssh2.InteractiveCallback; +import com.trilead.ssh2.crypto.PEMDecoder; +import com.trilead.ssh2.packets.PacketServiceAccept; +import com.trilead.ssh2.packets.PacketServiceRequest; +import com.trilead.ssh2.packets.PacketUserauthBanner; +import com.trilead.ssh2.packets.PacketUserauthFailure; +import com.trilead.ssh2.packets.PacketUserauthInfoRequest; +import com.trilead.ssh2.packets.PacketUserauthInfoResponse; +import com.trilead.ssh2.packets.PacketUserauthRequestInteractive; +import com.trilead.ssh2.packets.PacketUserauthRequestNone; +import com.trilead.ssh2.packets.PacketUserauthRequestPassword; +import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey; +import com.trilead.ssh2.packets.Packets; +import com.trilead.ssh2.packets.TypesWriter; +import com.trilead.ssh2.signature.DSASHA1Verify; +import com.trilead.ssh2.signature.ECDSASHA2Verify; +import com.trilead.ssh2.signature.RSASHA1Verify; +import com.trilead.ssh2.transport.MessageHandler; +import com.trilead.ssh2.transport.TransportManager; + + +/** + * AuthenticationManager. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class AuthenticationManager implements MessageHandler { + TransportManager tm; + + Vector packets = new Vector(); + boolean connectionClosed = false; + + String banner; + + String[] remainingMethods = new String[0]; + boolean isPartialSuccess = false; + + boolean authenticated = false; + 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 { + 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) { + } + } + + /* This sequence works with J2ME */ + byte[] res = (byte[]) packets.firstElement(); + packets.removeElementAt(0); + return res; + } + } + + 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 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()); + PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user); + tm.sendMessage(urn.getPayload()); + byte[] msg = getNextMessage(); + new PacketServiceAccept(msg, 0, msg.length); + 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 { + KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password); + return authenticatePublicKey(user, pair, rnd); + } + + public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd) + throws IOException { + PrivateKey key = pair.getPrivate(); + + try { + initialize(user); + + if (methodPossible("publickey") == false) + throw new IOException("Authentication method publickey not supported by the server at this stage."); + + if (key instanceof DSAPrivateKey) { + DSAPrivateKey pk = (DSAPrivateKey) key; + byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic()); + 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(); + byte[] 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((RSAPublicKey) pair.getPublic()); + 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(); + byte[] 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 if (key instanceof ECPrivateKey) { + ECPrivateKey pk = (ECPrivateKey) key; + final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX + + ECDSASHA2Verify.getCurveName(pk.getParams()); + byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic()); + 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(algo); + tw.writeString(pk_enc, 0, pk_enc.length); + } + byte[] msg = tw.getBytes(); + byte[] ds = ECDSASHA2Verify.generateSignature(msg, pk); + byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams()); + PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, + algo, pk_enc, ec_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) { + e.printStackTrace(); + 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.addElement(tmp); + } + + packets.notifyAll(); + + if (packets.size() > 5) { + connectionClosed = true; + throw new IOException("Error, peer is flooding us with authentication packets."); + } + } + } +}