diff src/ch/ethz/ssh2/auth/AuthenticationManager.java @ 273:91a31873c42a ganymed

start conversion from trilead to ganymed
author Carl Byington <carl@five-ten-sg.com>
date Fri, 18 Jul 2014 11:21:46 -0700
parents
children d7e088fa2123
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AuthenticationManager.java	Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,396 @@
+/*
+ * 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.io.InterruptedIOException;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+import ch.ethz.ssh2.InteractiveCallback;
+import ch.ethz.ssh2.PacketTypeException;
+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.ClientTransportManager;
+import ch.ethz.ssh2.transport.MessageHandler;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: AuthenticationManager.java 161 2014-05-01 18:01:55Z dkocher@sudo.ch $
+ */
+public class AuthenticationManager implements MessageHandler {
+    private ClientTransportManager tm;
+
+    private final BlockingQueue<byte[]> packets
+            = new ArrayBlockingQueue<byte[]>(5);
+
+    private boolean connectionClosed = false;
+
+    private String banner;
+
+    private Set<String> remainingMethods
+            = new HashSet<String>();
+
+    private boolean isPartialSuccess = false;
+
+    private boolean authenticated = false;
+    private boolean initDone = false;
+
+    public AuthenticationManager(ClientTransportManager tm) {
+        this.tm = tm;
+    }
+
+    private byte[] deQueue() throws IOException {
+        if(connectionClosed) {
+            throw tm.getReasonClosedCause();
+        }
+        // Wait for packet
+        try {
+            return packets.take();
+        }
+        catch(InterruptedException e) {
+            throw new InterruptedIOException(e.getMessage());
+        }
+    }
+
+    byte[] getNextMessage() throws IOException {
+        while(true) {
+            byte[] message = deQueue();
+            switch(message[0]) {
+                case Packets.SSH_MSG_USERAUTH_BANNER:
+                    // The server may send an SSH_MSG_USERAUTH_BANNER message at any
+                    // time after this authentication protocol starts and before
+                    // authentication is successful.
+                    PacketUserauthBanner sb = new PacketUserauthBanner(message);
+                    banner = sb.getBanner();
+                    break;
+                default:
+                    return message;
+            }
+        }
+    }
+
+    public Set<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());
+
+            final PacketServiceAccept accept = new PacketServiceAccept(this.getNextMessage());
+
+            PacketUserauthRequestNone auth = new PacketUserauthRequestNone("ssh-connection", user);
+            tm.sendMessage(auth.getPayload());
+
+            byte[] message = this.getNextMessage();
+            initDone = true;
+            switch(message[0]) {
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    authenticated = true;
+                    tm.removeMessageHandler(this);
+                    return true;
+                case Packets.SSH_MSG_USERAUTH_FAILURE:
+                    PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                    remainingMethods = puf.getAuthThatCanContinue();
+                    isPartialSuccess = puf.isPartialSuccess();
+                    return false;
+            }
+            throw new PacketTypeException(message[0]);
+        }
+        return authenticated;
+    }
+
+    public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
+        initialize(user);
+
+        boolean success;
+        for(AgentIdentity identity : proxy.getIdentities()) {
+            success = authenticatePublicKey(user, identity);
+            if(success) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean authenticatePublicKey(String user, AgentIdentity identity) throws IOException {
+        if(!remainingMethods.contains("publickey")) {
+            throw new IOException("Authentication method not supported");
+        }
+
+        byte[] pubKeyBlob = identity.getPublicKeyBlob();
+        if(pubKeyBlob == null) {
+            return false;
+        }
+
+        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(identity.getAlgName());
+        tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);
+
+        byte[] msg = tw.getBytes();
+        byte[] response = identity.sign(msg);
+
+        PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
+                "ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
+        tm.sendMessage(ua.getPayload());
+
+        byte[] message = getNextMessage();
+        final int type = message[0];
+        switch(type) {
+            case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                authenticated = true;
+                tm.removeMessageHandler(this);
+                return true;
+            case Packets.SSH_MSG_USERAUTH_FAILURE:
+                PacketUserauthFailure puf = new PacketUserauthFailure(message);
+
+                remainingMethods = puf.getAuthThatCanContinue();
+                isPartialSuccess = puf.isPartialSuccess();
+
+                return false;
+        }
+        throw new PacketTypeException(type);
+    }
+
+    public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
+            throws IOException {
+        try {
+            initialize(user);
+
+            if(!remainingMethods.contains("publickey")) {
+                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[] message = getNextMessage();
+            final int type = message[0];
+            switch(type) {
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    authenticated = true;
+                    tm.removeMessageHandler(this);
+                    return true;
+                case Packets.SSH_MSG_USERAUTH_FAILURE:
+                    PacketUserauthFailure puf = new PacketUserauthFailure(message);
+
+                    remainingMethods = puf.getAuthThatCanContinue();
+                    isPartialSuccess = puf.isPartialSuccess();
+
+                    return false;
+            }
+            throw new PacketTypeException(type);
+        }
+        catch(IOException e) {
+            tm.close(e);
+            throw e;
+        }
+    }
+
+    public boolean authenticateNone(String user) throws IOException {
+        try {
+            initialize(user);
+            return authenticated;
+        }
+        catch(IOException e) {
+            tm.close(e);
+            throw e;
+        }
+    }
+
+    public boolean authenticatePassword(String user, String pass) throws IOException {
+        try {
+            initialize(user);
+
+            if(!remainingMethods.contains("password")) {
+                throw new IOException("Authentication method not supported");
+            }
+
+            PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
+            tm.sendMessage(ua.getPayload());
+
+            byte[] message = getNextMessage();
+            final int type = message[0];
+            switch(type) {
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    authenticated = true;
+                    tm.removeMessageHandler(this);
+                    return true;
+                case Packets.SSH_MSG_USERAUTH_FAILURE:
+                    PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                    remainingMethods = puf.getAuthThatCanContinue();
+                    isPartialSuccess = puf.isPartialSuccess();
+                    return false;
+            }
+            throw new PacketTypeException(type);
+        }
+        catch(IOException e) {
+            tm.close(e);
+            throw e;
+        }
+    }
+
+    public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException {
+        try {
+            initialize(user);
+
+            if(!remainingMethods.contains("keyboard-interactive")) {
+                throw new IOException(
+                        "Authentication method keyboard-interactive not supported by the server at this stage.");
+            }
+
+            PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
+                    submethods);
+
+            tm.sendMessage(ua.getPayload());
+
+            while(true) {
+                byte[] message = getNextMessage();
+                final int type = message[0];
+                switch(type) {
+                    case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                        authenticated = true;
+                        tm.removeMessageHandler(this);
+                        return true;
+                    case Packets.SSH_MSG_USERAUTH_FAILURE:
+                        PacketUserauthFailure puf = new PacketUserauthFailure(message);
+
+                        remainingMethods = puf.getAuthThatCanContinue();
+                        isPartialSuccess = puf.isPartialSuccess();
+
+                        return false;
+                    case Packets.SSH_MSG_USERAUTH_INFO_REQUEST:
+                        PacketUserauthInfoRequest info = new PacketUserauthInfoRequest(message);
+                        String[] responses;
+                        try {
+                            responses = cb.replyToChallenge(info.getName(), info.getInstruction(), info.getNumPrompts(),
+                                    info.getPrompt(), info.getEcho());
+                        }
+                        catch(Exception e) {
+                            throw new IOException("Exception in callback.", e);
+                        }
+                        PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
+                        tm.sendMessage(puir.getPayload());
+                        continue;
+                }
+                throw new PacketTypeException(type);
+            }
+        }
+        catch(IOException e) {
+            tm.close(e);
+            throw e;
+        }
+    }
+
+    @Override
+    public void handleFailure(final IOException failure) {
+        connectionClosed = true;
+    }
+
+    @Override
+    public void handleMessage(byte[] message) throws IOException {
+        packets.add(message);
+    }
+}