You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2009/12/10 16:40:44 UTC

svn commit: r889300 - in /mina/sshd/trunk/sshd-core/src: main/java/org/apache/sshd/ main/java/org/apache/sshd/agent/ main/java/org/apache/sshd/client/auth/ main/java/org/apache/sshd/client/channel/ main/java/org/apache/sshd/client/kex/ main/java/org/ap...

Author: gnodet
Date: Thu Dec 10 15:40:43 2009
New Revision: 889300

URL: http://svn.apache.org/viewvc?rev=889300&view=rev
Log:
SSHD-8: Implement agent forwarding

Added:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshAgent.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentClient.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentLocal.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentServer.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelAgentForwarding.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/AgentForwardSupport.java
    mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
Modified:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelShell.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHGClient.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHGServer.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java Thu Dec 10 15:40:43 2009
@@ -26,7 +26,7 @@
 
 /**
  * A client channel used to communicate with
- * the SSH server.  Client cannels can be shells,
+ * the SSH server.  Client channels can be shells,
  * simple commands or subsystems
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java Thu Dec 10 15:40:43 2009
@@ -53,6 +53,8 @@
     int WAIT_AUTH =   0x0004;
     int AUTHED =      0x0008;
 
+    AuthFuture authAgent(String username) throws IOException;
+
     AuthFuture authPassword(String username, String password) throws IOException;
 
     AuthFuture authPublicKey(String username, KeyPair key) throws IOException;

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshAgent.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshAgent.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshAgent.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshAgent.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.future.ConnectFuture;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.apache.sshd.common.*;
+import org.apache.sshd.common.signature.SignatureDSA;
+import org.apache.sshd.common.signature.SignatureRSA;
+import org.apache.sshd.common.util.Buffer;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * SSH key agent server
+ */
+public interface SshAgent {
+
+    public static final String SSH_AUTHSOCKET_ENV_NAME = "SSH_AUTH_SOCK";
+
+    public static class Pair<U,V> {
+        private final U first;
+        private final V second;
+
+        public Pair(U first, V second) {
+            this.first = first;
+            this.second = second;
+        }
+
+        public U getFirst() {
+            return first;
+        }
+
+        public V getSecond() {
+            return second;
+        }
+    }
+
+    List<Pair<PublicKey, String>> getIdentities() throws IOException;
+
+    byte[] sign(PublicKey key, byte[] data) throws IOException;
+
+    void addIdentity(KeyPair key, String comment) throws IOException;
+
+    void removeIdentity(PublicKey key) throws IOException;
+
+    void removeAllIdentities() throws IOException;
+
+    void close();
+
+}

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java Thu Dec 10 15:40:43 2009
@@ -32,19 +32,17 @@
 import org.apache.mina.core.future.IoFutureListener;
 import org.apache.mina.core.service.IoConnector;
 import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.apache.sshd.agent.AgentClient;
+import org.apache.sshd.agent.AgentServer;
 import org.apache.sshd.client.SessionFactory;
+import org.apache.sshd.client.channel.ChannelAgentForwarding;
+import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.future.ConnectFuture;
 import org.apache.sshd.client.future.DefaultConnectFuture;
 import org.apache.sshd.client.kex.DHG1;
 import org.apache.sshd.client.kex.DHG14;
 import org.apache.sshd.client.session.ClientSessionImpl;
-import org.apache.sshd.common.AbstractFactoryManager;
-import org.apache.sshd.common.Cipher;
-import org.apache.sshd.common.Compression;
-import org.apache.sshd.common.KeyExchange;
-import org.apache.sshd.common.Mac;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.*;
 import org.apache.sshd.common.cipher.AES128CBC;
 import org.apache.sshd.common.cipher.AES192CBC;
 import org.apache.sshd.common.cipher.AES256CBC;
@@ -65,7 +63,6 @@
 import org.apache.sshd.common.util.NoCloseInputStream;
 import org.apache.sshd.common.util.NoCloseOutputStream;
 import org.apache.sshd.common.util.SecurityUtils;
-import org.bouncycastle.openssl.PEMReader;
 import org.bouncycastle.openssl.PasswordFinder;
 
 /**
@@ -211,6 +208,8 @@
         client.setSignatureFactories(Arrays.<NamedFactory<Signature>>asList(
                 new SignatureDSA.Factory(),
                 new SignatureRSA.Factory()));
+        client.setChannelFactories(Arrays.<NamedFactory<Channel>>asList(
+                new ChannelAgentForwarding.Factory()));
         return client;
     }
 
@@ -246,6 +245,7 @@
         int port = 22;
         String host = null;
         String login = System.getProperty("user.name");
+        boolean agentForward = false;
         List<String> command = null;
         int logLevel = 0;
         boolean error = false;
@@ -271,6 +271,10 @@
                 logLevel = 2;
             } else if (command == null && "-vvv".equals(args[i])) {
                 logLevel = 3;
+            } else if ("-A".equals(args[i])) {
+                agentForward = true;
+            } else if ("-a".equals(args[i])) {
+                agentForward = false;
             } else if (command == null && args[i].startsWith("-")) {
                 System.err.println("illegal option: " + args[i]);
                 error = true;
@@ -291,7 +295,7 @@
             error = true;
         }
         if (error) {
-            System.err.println("usage: ssh [-v[v][v]] [-l login] [-p port] hostname [command]");
+            System.err.println("usage: ssh [-A|-a] [-v[v][v]] [-l login] [-p port] hostname [command]");
             System.exit(-1);
         }
 
@@ -299,13 +303,18 @@
 
         SshClient client = SshClient.setUpDefaultClient();
         client.start();
+
         try {
             ClientSession session = client.connect(host, port).await().getSession();
 
             int ret = ClientSession.WAIT_AUTH;
 
             KeyPair[] keys = null;
-			/*
+            /*
+            AgentServer server = new AgentServer();
+            String authSock = server.start();
+            client.getProperties().put(org.apache.sshd.SshAgent.SSH_AUTHSOCKET_ENV_NAME, authSock);
+
             List<String> files = new ArrayList<String>();
             File f = new File(System.getProperty("user.home"), ".ssh/id_dsa");
             if (f.exists() && f.isFile() && f.canRead()) {
@@ -332,11 +341,15 @@
                 }
             } catch (Exception e) {
             }
-			*/
-            int nbKey = 0;
+            SshAgent agent = new AgentClient(authSock);
+            for (KeyPair key : keys) {
+                agent.addIdentity(key, "");
+            }
+            agent.close();
+            */
             while ((ret & ClientSession.WAIT_AUTH) != 0) {
-                if (keys != null && nbKey < keys.length) {
-                    session.authPublicKey(login, keys[nbKey++]);
+                if (keys != null) {
+                    session.authAgent(login);
                     ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
                 } else {
                     System.out.print("Password:");
@@ -353,6 +366,7 @@
             ClientChannel channel;
             if (command == null) {
                 channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                ((ChannelShell) channel).setAgentForwarding(agentForward);
                 channel.setIn(new NoCloseInputStream(System.in));
             } else {
                 channel = session.createChannel(ClientChannel.CHANNEL_EXEC);

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentClient.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentClient.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentClient.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentClient.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.agent;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.future.ConnectFuture;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.apache.sshd.SshAgent;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.Buffer;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * A client for a remote SSH agent
+ */
+public class AgentClient implements SshAgent {
+
+    private IoConnector connector;
+    private SocketAddress address;
+    private ConnectFuture connect;
+    private IoSession session;
+    private Buffer receiveBuffer;
+    private final Queue<Buffer> messages;
+
+    public AgentClient(String authSocket) {
+        connector = new NioSocketConnector();
+        connector.setHandler(new IoHandlerAdapter() {
+            @Override
+            public void messageReceived(IoSession session, Object message) throws Exception {
+                IoBuffer ioBuffer = (IoBuffer) message;
+                AgentClient.this.messageReceived(ioBuffer);
+            }
+//                @Override
+//                public void sessionClosed(IoSession session) throws Exception {
+//                    close();
+//                }
+        });
+        address = new InetSocketAddress("localhost", Integer.parseInt(authSocket));
+        receiveBuffer = new Buffer();
+        messages = new ArrayBlockingQueue<Buffer>(10);
+        connect = connector.connect(address);
+    }
+
+    protected IoSession getSession() throws Throwable {
+        if (session == null) {
+            connect.await();
+            if (connect.getException() != null) {
+                throw connect.getException();
+            }
+            session = connect.getSession();
+        }
+        return session;
+    }
+
+    protected void messageReceived(IoBuffer buffer) throws Exception {
+        Buffer message = null;
+        synchronized (receiveBuffer) {
+            receiveBuffer.putBuffer(buffer);
+            if (receiveBuffer.available() >= 4) {
+                int rpos = receiveBuffer.rpos();
+                int len = receiveBuffer.getInt();
+                receiveBuffer.rpos(rpos);
+                if (receiveBuffer.available() >= 4 + len) {
+                    message = new Buffer(receiveBuffer.getBytes());
+                    receiveBuffer.compact();
+                }
+            }
+        }
+        if (message != null) {
+            synchronized (messages) {
+                messages.offer(message);
+                messages.notifyAll();
+            }
+        }
+    }
+
+    public List<Pair<PublicKey, String>> getIdentities() throws IOException {
+        Buffer buffer = createBuffer(AgentServer.SSH2_AGENTC_REQUEST_IDENTITIES);
+        buffer = request(buffer);
+        int type = buffer.getByte();
+        if (type != AgentServer.SSH2_AGENT_IDENTITIES_ANSWER) {
+            throw new SshException("SSH agent failure");
+        }
+        int nbIdentities = buffer.getInt();
+        if (nbIdentities > 1024) {
+            throw new SshException("SSH agent failure");
+        }
+        List<Pair<PublicKey, String>> keys = new ArrayList<Pair<PublicKey, String>>();
+        for (int i = 0; i < nbIdentities; i++) {
+            PublicKey key = buffer.getPublicKey();
+            keys.add(new Pair<PublicKey, String>(key, buffer.getString()));
+        }
+        return keys;
+    }
+
+    public byte[] sign(PublicKey key, byte[] data) throws IOException {
+        Buffer buffer = createBuffer(AgentServer.SSH2_AGENTC_SIGN_REQUEST);
+        buffer.putPublicKey(key);
+        buffer.putBytes(data);
+        buffer.putInt(0);
+        buffer = request(buffer);
+        if (buffer.getByte() != AgentServer.SSH2_AGENT_SIGN_RESPONSE) {
+            throw new SshException("SSH agent failure");
+        }
+        Buffer buf = new Buffer(buffer.getBytes());
+        buf.getString(); // algo
+        return buf.getBytes();
+    }
+
+    public void addIdentity(KeyPair key, String comment) throws IOException {
+        Buffer buffer = createBuffer(AgentServer.SSH2_AGENTC_ADD_IDENTITY);
+        buffer.putKeyPair(key);
+        buffer.putString(comment);
+        buffer = request(buffer);
+        if (buffer.available() != 1 || buffer.getByte() != AgentServer.SSH_AGENT_SUCCESS) {
+            throw new SshException("SSH agent failure");
+        }
+    }
+
+    public void removeIdentity(PublicKey key) throws IOException {
+        Buffer buffer = createBuffer(AgentServer.SSH2_AGENTC_REMOVE_IDENTITY);
+        buffer.putPublicKey(key);
+        buffer = request(buffer);
+        if (buffer.available() != 1 || buffer.getByte() != AgentServer.SSH_AGENT_SUCCESS) {
+            throw new SshException("SSH agent failure");
+        }
+    }
+
+    public void removeAllIdentities() throws IOException {
+        Buffer buffer = createBuffer(AgentServer.SSH2_AGENTC_REMOVE_ALL_IDENTITIES);
+        buffer = request(buffer);
+        if (buffer.available() != 1 || buffer.getByte() != AgentServer.SSH_AGENT_SUCCESS) {
+            throw new SshException("SSH agent failure");
+        }
+    }
+
+    public void close() {
+        if (session != null) {
+            session.close(true);
+        }
+        connector.dispose();
+    }
+
+    protected Buffer createBuffer(byte cmd) {
+        Buffer buffer = new Buffer();
+        buffer.putInt(0);
+        buffer.putByte(cmd);
+        return buffer;
+    }
+
+    protected synchronized Buffer request(Buffer buffer) throws IOException {
+        int wpos = buffer.wpos();
+        buffer.wpos(0);
+        buffer.putInt(wpos - 4);
+        buffer.wpos(wpos);
+        synchronized (messages) {
+            try {
+                IoBuffer buf = IoBuffer.allocate(buffer.available());
+                buf.put(buffer.array(), buffer.rpos(), buffer.available());
+                buf.flip();
+                connect.await().getSession().write(buf);
+                if (messages.isEmpty()) {
+                    messages.wait();
+                }
+                return messages.poll();
+            } catch (InterruptedException e) {
+                throw (IOException) new InterruptedIOException().initCause(e);
+            }
+        }
+    }
+
+}

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentLocal.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentLocal.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentLocal.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentLocal.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.agent;
+
+import org.apache.sshd.SshAgent;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.signature.SignatureDSA;
+import org.apache.sshd.common.signature.SignatureRSA;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A local SSH agent implementation
+ */
+public class AgentLocal implements SshAgent {
+
+    private final List<Pair<KeyPair, String>> keys = new ArrayList<Pair<KeyPair, String>>();
+    private boolean closed;
+
+    public List<Pair<PublicKey, String>> getIdentities() throws IOException {
+        if (closed) {
+            throw new SshException("Agent closed");
+        }
+        List<Pair<PublicKey, String>> pks = new ArrayList<Pair<PublicKey, String>>();
+        for (Pair<KeyPair, String> kp : keys) {
+            pks.add(new Pair<PublicKey, String>(kp.getFirst().getPublic(), kp.getSecond()));
+        }
+        return pks;
+    }
+
+    public byte[] sign(PublicKey key, byte[] data) throws IOException {
+        if (closed) {
+            throw new SshException("Agent closed");
+        }
+        Pair<KeyPair, String> kp = getKeyPair(keys, key);
+        if (kp == null) {
+            throw new SshException("Key not found");
+        }
+        try {
+            Signature verif;
+            if (kp.getFirst().getPublic() instanceof RSAPublicKey) {
+                verif = new SignatureRSA();
+            } else {
+                verif = new SignatureDSA();
+            }
+            verif.init(kp.getFirst().getPublic(), kp.getFirst().getPrivate());
+            verif.update(data, 0, data.length);
+            return verif.sign();
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new SshException(e);
+        }
+    }
+
+    public void addIdentity(KeyPair key, String comment) throws IOException {
+        if (closed) {
+            throw new SshException("Agent closed");
+        }
+        keys.add(new Pair<KeyPair, String>(key, comment));
+    }
+
+    public void removeIdentity(PublicKey key) throws IOException {
+        if (closed) {
+            throw new SshException("Agent closed");
+        }
+        Pair<KeyPair, String> kp = getKeyPair(keys, key);
+        if (kp == null) {
+            throw new SshException("Key not found");
+        }
+        keys.remove(kp);
+    }
+
+    public void removeAllIdentities() throws IOException {
+        if (closed) {
+            throw new SshException("Agent closed");
+        }
+        keys.clear();
+    }
+
+    public void close() {
+        closed = true;
+        keys.clear();
+    }
+
+    protected static SshAgent.Pair<KeyPair, String> getKeyPair(List<SshAgent.Pair<KeyPair, String>> keys, PublicKey key) {
+        SshAgent.Pair<KeyPair, String> kp = null;
+        for (SshAgent.Pair<KeyPair, String> k : keys) {
+            if (areKeyEquals(key, k.getFirst().getPublic())) {
+                kp = k;
+                break;
+            }
+        }
+        return kp;
+    }
+
+    protected static boolean areKeyEquals(PublicKey k1, PublicKey k2) {
+        if (k1 instanceof DSAPublicKey && k2 instanceof DSAPublicKey) {
+            DSAPublicKey d1 = (DSAPublicKey) k1;
+            DSAPublicKey d2 = (DSAPublicKey) k2;
+            DSAParams p1 = d1.getParams();
+            DSAParams p2 = d2.getParams();
+            return d1.getY().equals(d2.getY())
+                        && p1.getG().equals(p2.getG())
+                        && p1.getP().equals(p2.getP())
+                        && p1.getQ().equals(p2.getQ());
+        } else if (k1 instanceof RSAPublicKey && k2 instanceof RSAPublicKey) {
+            RSAPublicKey r1 = (RSAPublicKey) k1;
+            RSAPublicKey r2 = (RSAPublicKey) k2;
+            return r1.getModulus().equals(r2.getModulus())
+                        && r1.getPublicExponent().equals(r2.getPublicExponent());
+        } else {
+            return false;
+        }
+    }
+
+}

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentServer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentServer.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentServer.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/agent/AgentServer.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.agent;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.sshd.SshAgent;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.util.Buffer;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.List;
+
+/**
+ * A server for an SSH Agent
+ */
+public class AgentServer implements SshAgent {
+
+    static final byte SSH_AGENT_SUCCESS = 6;
+    static final byte SSH2_AGENTC_REQUEST_IDENTITIES = 11;
+    static final byte SSH2_AGENT_IDENTITIES_ANSWER = 12;
+    static final byte SSH2_AGENTC_SIGN_REQUEST = 13;
+    static final byte SSH2_AGENT_SIGN_RESPONSE = 14;
+    static final byte SSH2_AGENTC_ADD_IDENTITY = 17;
+    static final byte SSH2_AGENTC_REMOVE_IDENTITY = 18;
+    static final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19;
+    static final byte SSH2_AGENT_FAILURE = 30;
+
+    private final SshAgent engine = new AgentLocal();
+    private IoAcceptor acceptor;
+    private SocketAddress address;
+
+
+    public String start() throws Exception {
+        acceptor = new NioSocketAcceptor();
+        acceptor.setHandler(new IoHandlerAdapter() {
+            @Override
+            public void sessionCreated(IoSession session) throws Exception {
+                SshAgentSession s = new SshAgentSession(session, engine);
+                session.setAttribute(SshAgentSession.class, s);
+            }
+            @Override
+            public void messageReceived(IoSession session, Object message) throws Exception {
+                SshAgentSession s = (SshAgentSession) session.getAttribute(SshAgentSession.class);
+                s.messageReceived(message);
+            }
+        });
+        acceptor.bind(new InetSocketAddress("localhost", 0));
+        address = acceptor.getLocalAddress();
+        return Integer.toString(((InetSocketAddress) address).getPort());
+    }
+
+    public void close() {
+        engine.close();
+        acceptor.dispose();
+    }
+
+    public List<Pair<PublicKey, String>> getIdentities() throws IOException {
+        return engine.getIdentities();
+    }
+
+    public byte[] sign(PublicKey key, byte[] data) throws IOException {
+        return engine.sign(key, data);
+    }
+
+    public void addIdentity(KeyPair key, String comment) throws IOException {
+        engine.addIdentity(key, comment);
+    }
+
+    public void removeIdentity(PublicKey key) throws IOException {
+        engine.removeIdentity(key);
+    }
+
+    public void removeAllIdentities() throws IOException {
+        engine.removeAllIdentities();
+    }
+
+    protected static class SshAgentSession {
+
+        private final IoSession session;
+        private final SshAgent engine;
+        private final Buffer buffer = new Buffer();
+
+        public SshAgentSession(IoSession session, SshAgent engine) {
+            this.session = session;
+            this.engine = engine;
+        }
+
+        public synchronized void messageReceived(Object message) throws Exception {
+            IoBuffer ioBuffer = (IoBuffer) message;
+            buffer.putBuffer(ioBuffer);
+            if (buffer.available() < 4) {
+                return;
+            }
+            int rpos = buffer.rpos();
+            int len = buffer.getInt();
+            buffer.rpos(rpos);
+            if (buffer.available() < len + 4) {
+                return;
+            }
+            Buffer rep = new Buffer();
+            rep.putInt(0);
+            rep.rpos(rep.wpos());
+            try {
+                process(new Buffer(buffer.getBytes()), rep);
+            } catch (Exception e) {
+                rep.clear();
+                rep.putInt(1);
+                rep.putByte(SSH2_AGENT_FAILURE);
+            }
+            reply(rep);
+        }
+
+        protected void process(Buffer req, Buffer rep) throws Exception {
+            int cmd = req.getByte();
+            switch (cmd) {
+                case SSH2_AGENTC_REQUEST_IDENTITIES:
+                {
+                    List<SshAgent.Pair<PublicKey,String>> keys = engine.getIdentities();
+                    rep.putByte(SSH2_AGENT_IDENTITIES_ANSWER);
+                    rep.putInt(keys.size());
+                    for (SshAgent.Pair<PublicKey,String> key : keys) {
+                        rep.putPublicKey(key.getFirst());
+                        rep.putString(key.getSecond());
+                    }
+                    break;
+                }
+                case SSH2_AGENTC_SIGN_REQUEST:
+                {
+                    PublicKey key = req.getPublicKey();
+                    byte[] data = req.getBytes();
+                    int flags = req.getInt();
+                    Buffer sig = new Buffer();
+                    sig.putString(key instanceof RSAPublicKey ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+                    sig.putBytes(engine.sign(key, data));
+                    rep.putByte(SSH2_AGENT_SIGN_RESPONSE);
+                    rep.putBytes(sig.array(), sig.rpos(), sig.available());
+                    break;
+                }
+                case SSH2_AGENTC_ADD_IDENTITY:
+                {
+                    engine.addIdentity(req.getKeyPair(), req.getString());
+                    rep.putByte(SSH_AGENT_SUCCESS);
+                    break;
+                }
+                case SSH2_AGENTC_REMOVE_IDENTITY:
+                {
+                    PublicKey key = req.getPublicKey();
+                    engine.removeIdentity(key);
+                    rep.putByte(SSH_AGENT_SUCCESS);
+                    break;
+                }
+                case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
+                {
+                    engine.removeAllIdentities();
+                    rep.putByte(SSH_AGENT_SUCCESS);
+                    break;
+                }
+                default:
+                {
+                    rep.putByte(SSH2_AGENT_FAILURE);
+                    break;
+                }
+            }
+        }
+
+        protected void reply(Buffer buf) throws Exception {
+            int len  = buf.available();
+            int rpos = buf.rpos();
+            int wpos = buf.wpos();
+            buf.rpos(rpos - 4);
+            buf.wpos(rpos - 4);
+            buf.putInt(len);
+            buf.wpos(wpos);
+            IoBuffer b = IoBuffer.allocate(buf.available());
+            b.put(buf.array(), buf.rpos(), buf.available());
+            b.flip();
+            session.write(b);
+        }
+
+    }
+
+}

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.client.auth;
+
+import org.apache.sshd.SshAgent;
+import org.apache.sshd.agent.AgentClient;
+import org.apache.sshd.agent.AgentServer;
+import org.apache.sshd.client.UserAuth;
+import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.Buffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Iterator;
+
+/**
+ * Authentication delegating to an SSH agent
+ */
+public class UserAuthAgent implements UserAuth {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final ClientSessionImpl session;
+    private final String username;
+    private final SshAgent agent;
+    private Iterator<SshAgent.Pair<PublicKey, String>> keys;
+
+    public UserAuthAgent(ClientSessionImpl session, String username) throws IOException {
+        this.session = session;
+        this.username = username;
+        String authSocket = session.getFactoryManager().getProperties().get(org.apache.sshd.SshAgent.SSH_AUTHSOCKET_ENV_NAME);
+        SshAgent agent = new AgentClient(authSocket);
+        this.agent = agent;
+        keys = agent.getIdentities().iterator();
+        sendNextKey();
+    }
+
+    protected void sendNextKey() throws IOException {
+        sendNextKey(keys.next().getFirst());
+    }
+
+    protected void sendNextKey(PublicKey key) throws IOException {
+        try {
+            log.info("Send SSH_MSG_USERAUTH_REQUEST for publickey");
+            Buffer buffer = session.createBuffer(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
+            int pos1 = buffer.wpos() - 1;
+            buffer.putString(username);
+            buffer.putString("ssh-connection");
+            buffer.putString("publickey");
+            buffer.putByte((byte) 1);
+            buffer.putString((key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            int pos2 = buffer.wpos();
+            buffer.putPublicKey(key);
+
+
+            Buffer bs = new Buffer();
+            bs.putString(session.getKex().getH());
+            bs.putCommand(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
+            bs.putString(username);
+            bs.putString("ssh-connection");
+            bs.putString("publickey");
+            bs.putByte((byte) 1);
+            bs.putString((key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            bs.putPublicKey(key);
+
+            Buffer bs2 = new Buffer();
+            bs2.putString((key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            bs2.putBytes(agent.sign(key, bs.getCompactData()));
+            buffer.putBytes(bs2.array(), bs2.rpos(), bs2.available());
+
+            session.writePacket(buffer);
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw (IOException) new IOException("Error performing public key authentication").initCause(e);
+        }
+    }
+
+    public Result next(Buffer buffer) throws IOException {
+        SshConstants.Message cmd = buffer.getCommand();
+        log.info("Received {}", cmd);
+        if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_SUCCESS) {
+            return Result.Success;
+        } if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_FAILURE) {
+            if (keys.hasNext()) {
+                sendNextKey(keys.next().getFirst());
+                return Result.Continued;
+            }
+            return Result.Failure;
+        } else {
+            // TODO: check packets
+            return Result.Continued;
+        }
+    }
+}

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java Thu Dec 10 15:40:43 2009
@@ -46,7 +46,7 @@
 
     public UserAuthPublicKey(ClientSessionImpl session, String username, KeyPair key) throws IOException {
         try {
-            log.info("Send SSH_MSG_USERAUTH_REQUEST for password");
+            log.info("Send SSH_MSG_USERAUTH_REQUEST for publickey");
             Buffer buffer = session.createBuffer(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
             int pos1 = buffer.wpos() - 1;
             buffer.putString(username);
@@ -55,10 +55,7 @@
             buffer.putByte((byte) 1);
             buffer.putString((key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
             int pos2 = buffer.wpos();
-            Buffer b = new Buffer();
-            b.putPublicKey(key.getPublic());
-            buffer.putInt(b.available());
-            buffer.putBuffer(b);
+            buffer.putPublicKey(key.getPublic());
 
             Signature verif = NamedFactory.Utils.create(session.getFactoryManager().getSignatureFactories(), (key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
             verif.init(key.getPublic(), key.getPrivate());
@@ -71,11 +68,13 @@
             bs.putString("publickey");
             bs.putByte((byte) 1);
             bs.putString((key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
-            bs.putInt(b.available());
-            bs.putBuffer(b);
+            bs.putPublicKey(key.getPublic());
             verif.update(bs.array(), bs.rpos(), bs.available());
 
-            buffer.putBytes(verif.sign());
+            bs = new Buffer();
+            bs.putString((key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            bs.putBytes(verif.sign());
+            buffer.putBytes(bs.array(), bs.rpos(), bs.available());
 
             session.writePacket(buffer);
         } catch (IOException e) {

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java Thu Dec 10 15:40:43 2009
@@ -99,6 +99,7 @@
                     }
                 } else {
                     closeFuture.setClosed();
+                    lock.notifyAll();
                 }
             }
         }

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelAgentForwarding.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelAgentForwarding.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelAgentForwarding.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelAgentForwarding.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.client.channel;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.future.ConnectFuture;
+import org.apache.mina.core.future.IoFutureListener;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandler;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.apache.mina.transport.vmpipe.VmPipeAddress;
+import org.apache.mina.transport.vmpipe.VmPipeConnector;
+import org.apache.sshd.SshAgent;
+import org.apache.sshd.client.future.DefaultOpenFuture;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.AbstractChannel;
+import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.util.BufferUtils;
+import org.apache.sshd.server.TcpIpForwardFilter;
+import org.apache.sshd.server.channel.AbstractServerChannel;
+import org.apache.sshd.server.channel.OpenChannelException;
+import org.apache.sshd.server.session.ServerSession;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+/**
+ * The client side channel that will receive requests forwards by the SSH server.
+ */
+public class ChannelAgentForwarding extends AbstractServerChannel {
+
+    public static class Factory implements NamedFactory<Channel> {
+
+        public String getName() {
+            return "auth-agent@openssh.com";
+        }
+
+        public Channel create() {
+            return new ChannelAgentForwarding();
+        }
+    }
+
+    private IoConnector connector;
+    private SocketAddress address;
+    private IoSession ioSession;
+    private OutputStream out;
+
+    public ChannelAgentForwarding() {
+    }
+
+    protected OpenFuture doInit(Buffer buffer) {
+        final OpenFuture f = new DefaultOpenFuture(this);
+
+        createConnectorAndAddress();
+
+        out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+        IoHandler handler = new IoHandlerAdapter() {
+            @Override
+            public void messageReceived(IoSession session, Object message) throws Exception {
+                IoBuffer ioBuffer = (IoBuffer) message;
+                int r = ioBuffer.remaining();
+                byte[] b = new byte[r];
+                ioBuffer.get(b, 0, r);
+                out.write(b, 0, r);
+                out.flush();
+            }
+
+            @Override
+            public void sessionClosed(IoSession session) throws Exception {
+                sendEof();
+            }
+        };
+        connector.setHandler(handler);
+        ConnectFuture future = connector.connect(address);
+        future.addListener(new IoFutureListener<ConnectFuture>() {
+            public void operationComplete(ConnectFuture future) {
+                if (future.isConnected()) {
+                    ioSession = future.getSession();
+                    f.setOpened();
+                } else if (future.getException() != null) {
+                    closeImmediately0();
+                    if (future.getException() instanceof ConnectException) {
+                        f.setException(new OpenChannelException(
+                            SshConstants.SSH_OPEN_CONNECT_FAILED,
+                            future.getException().getMessage(),
+                            future.getException()));
+                    } else {
+                        f.setException(future.getException());
+                    }
+                }
+            }
+        });
+        return f;
+    }
+
+    private void closeImmediately0() {
+        // We need to close the channel immediately to remove it from the
+        // server session's channel table and *not* send a packet to the
+        // client.  A notification was already sent by our caller, or will
+        // be sent after we return.
+        //
+        super.close(true);
+
+        // We also need to dispose of the connector, but unfortunately we
+        // are being invoked by the connector thread or the connector's
+        // own processor thread.  Disposing of the connector within either
+        // causes deadlock.  Instead create a new thread to dispose of the
+        // connector in the background.
+        //
+        new Thread("AgentForward-ConnectorCleanup") {
+            @Override
+            public void run() {
+                connector.dispose();
+            }
+        }.start();
+    }
+
+    public CloseFuture close(boolean immediately) {
+        return super.close(immediately).addListener(new SshFutureListener() {
+            public void operationComplete(SshFuture sshFuture) {
+                closeImmediately0();
+            }
+        });
+    }
+
+    @Override
+    public void handleEof() throws IOException {
+        super.handleEof();
+//        close(true);
+    }
+
+    protected void doWriteData(byte[] data, int off, int len) throws IOException {
+        IoBuffer buf = IoBuffer.allocate(len);
+        buf.put(data, off, len);
+        buf.flip();
+        ioSession.write(buf);
+    }
+
+    protected void doWriteExtendedData(byte[] data, int off, int len) throws IOException {
+        throw new UnsupportedOperationException("AgentForward channel does not support extended data");
+    }
+
+    public void handleRequest(Buffer buffer) throws IOException {
+        log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
+        String type = buffer.getString();
+        log.info("Received channel request: {}", type);
+        buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE);
+        buffer.putInt(recipient);
+        session.writePacket(buffer);
+    }
+
+    protected void createConnectorAndAddress() {
+        String authSocket = session.getFactoryManager().getProperties().get(SshAgent.SSH_AUTHSOCKET_ENV_NAME);
+        connector = new NioSocketConnector();
+        address = new InetSocketAddress("localhost", Integer.parseInt(authSocket));
+    }
+
+}

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelShell.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelShell.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelShell.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelShell.java Thu Dec 10 15:40:43 2009
@@ -33,6 +33,7 @@
  */
 public class ChannelShell extends ChannelSession {
 
+    private boolean agentForwarding;
     private String ptyType;
     private int ptyColumns;
     private int ptyLines;
@@ -75,6 +76,14 @@
         }
     }
 
+    public boolean isAgentForwarding() {
+        return agentForwarding;
+    }
+
+    public void setAgentForwarding(boolean agentForwarding) {
+        this.agentForwarding = agentForwarding;
+    }
+
     public String getPtyType() {
         return ptyType;
     }
@@ -128,6 +137,15 @@
 
         Buffer buffer;
 
+        if (agentForwarding) {
+            log.info("Send agent forwarding request");
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_REQUEST);
+            buffer.putInt(recipient);
+            buffer.putString("auth-agent-req@openssh.com");
+            buffer.putBoolean(false);
+            session.writePacket(buffer);
+        }
+
         log.info("Send SSH_MSG_CHANNEL_REQUEST pty-req");
         buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_REQUEST);
         buffer.putInt(recipient);

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHGClient.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHGClient.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHGClient.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHGClient.java Thu Dec 10 15:40:43 2009
@@ -98,7 +98,7 @@
         K = dh.getK();
 
         buffer = new Buffer(K_S);
-        PublicKey key = buffer.getPublicKey();
+        PublicKey key = buffer.getRawPublicKey();
         String keyAlg = (key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS;
 
         buffer = new Buffer();

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java Thu Dec 10 15:40:43 2009
@@ -20,12 +20,12 @@
 
 import java.io.IOException;
 import java.security.KeyPair;
-import java.security.PublicKey;
 
 import org.apache.mina.core.session.IoSession;
 import org.apache.sshd.ClientChannel;
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.client.UserAuth;
+import org.apache.sshd.client.auth.UserAuthAgent;
 import org.apache.sshd.client.auth.UserAuthPassword;
 import org.apache.sshd.client.auth.UserAuthPublicKey;
 import org.apache.sshd.client.channel.AbstractClientChannel;
@@ -33,11 +33,13 @@
 import org.apache.sshd.client.channel.ChannelExec;
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.DefaultAuthFuture;
+import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.*;
 import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.util.Buffer;
-import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.apache.sshd.server.channel.OpenChannelException;
 
 /**
  * TODO Add javadoc
@@ -65,6 +67,28 @@
         return kex;
     }
 
+    public AuthFuture authAgent(String username) throws IOException {
+        synchronized (lock) {
+            if (closeFuture.isClosed()) {
+                throw new IllegalStateException("Session is closed");
+            }
+            if (authed) {
+                throw new IllegalStateException("User authentication has already been performed");
+            }
+            if (userAuth != null) {
+                throw new IllegalStateException("A user authentication request is already pending");
+            }
+            waitFor(CLOSED | WAIT_AUTH, 0);
+            if (closeFuture.isClosed()) {
+                throw new IllegalStateException("Session is closed");
+            }
+            authFuture = new DefaultAuthFuture(lock);
+            userAuth = new UserAuthAgent(this, username);
+            setState(ClientSessionImpl.State.UserAuth);
+            return authFuture;
+        }
+    }
+
     public AuthFuture authPassword(String username, String password) throws IOException {
         synchronized (lock) {
             if (closeFuture.isClosed()) {
@@ -229,6 +253,9 @@
                         break;
                     case Running:
                         switch (cmd) {
+                            case SSH_MSG_CHANNEL_OPEN:
+                                channelOpen(buffer);
+                                break;
                             case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
                                 channelOpenConfirmation(buffer);
                                 break;
@@ -256,9 +283,12 @@
                             case SSH_MSG_CHANNEL_CLOSE:
                                 channelClose(buffer);
                                 break;
-                            // TODO: handle other requests
+                            default:
+                                throw new IllegalStateException("Unsupported command: " + cmd);
                         }
                         break;
+                    default:
+                        throw new IllegalStateException("Unsupported state: " + state);
                 }
         }
     }
@@ -350,4 +380,66 @@
         writePacket(buffer);
     }
 
+    private void channelOpen(Buffer buffer) throws Exception {
+        String type = buffer.getString();
+        final int id = buffer.getInt();
+        final int rwsize = buffer.getInt();
+        final int rmpsize = buffer.getInt();
+
+        log.info("Received SSH_MSG_CHANNEL_OPEN {}", type);
+
+        if (closing) {
+            Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE);
+            buf.putInt(id);
+            buf.putInt(SshConstants.SSH_OPEN_CONNECT_FAILED);
+            buf.putString("SSH server is shutting down: " + type);
+            buf.putString("");
+            writePacket(buf);
+            return;
+        }
+
+        final Channel channel = NamedFactory.Utils.create(getFactoryManager().getChannelFactories(), type);
+        if (channel == null) {
+            Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE);
+            buf.putInt(id);
+            buf.putInt(SshConstants.SSH_OPEN_UNKNOWN_CHANNEL_TYPE);
+            buf.putString("Unsupported channel type: " + type);
+            buf.putString("");
+            writePacket(buf);
+            return;
+        }
+
+        final int channelId = getNextChannelId();
+        channels.put(channelId, channel);
+        channel.init(this, channelId);
+        channel.open(id, rwsize, rmpsize, buffer).addListener(new SshFutureListener<OpenFuture>() {
+            public void operationComplete(OpenFuture future) {
+                try {
+                    if (future.isOpened()) {
+                        Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+                        buf.putInt(id);
+                        buf.putInt(channelId);
+                        buf.putInt(channel.getLocalWindow().getSize());
+                        buf.putInt(channel.getLocalWindow().getPacketSize());
+                        writePacket(buf);
+                    } else if (future.getException() != null) {
+                        Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE);
+                        buf.putInt(id);
+                        if (future.getException() instanceof OpenChannelException) {
+                            buf.putInt(((OpenChannelException)future.getException()).getReasonCode());
+                            buf.putString(future.getException().getMessage());
+                        } else {
+                            buf.putInt(0);
+                            buf.putString("Error opening channel: " + future.getException().getMessage());
+                        }
+                        buf.putString("");
+                        writePacket(buf);
+                    }
+                } catch (IOException e) {
+                    exceptionCaught(e);
+                }
+            }
+        });
+    }
+
 }

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java Thu Dec 10 15:40:43 2009
@@ -86,6 +86,7 @@
                 if (immediately) {
                     log.info("Closing channel {} immediately", id);
                     closeFuture.setClosed();
+                    lock.notifyAll();
                     session.unregisterChannel(this);
                 } else {
                     if (!closing) {

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/Buffer.java Thu Dec 10 15:40:43 2009
@@ -19,19 +19,14 @@
 package org.apache.sshd.common.util;
 
 import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PublicKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPublicKeySpec;
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
 
 import org.apache.mina.core.buffer.IoBuffer;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
 
 /**
  * TODO Add javadoc
@@ -55,13 +50,21 @@
     }
 
     public Buffer(byte[] data) {
-        this(data, true);
+        this(data, 0, data.length, true);
     }
 
     public Buffer(byte[] data, boolean read) {
+        this(data, 0, data.length, read);
+    }
+
+    public Buffer(byte[] data, int off, int len) {
+        this(data, off, len, true);
+    }
+
+    public Buffer(byte[] data, int off, int len, boolean read) {
         this.data = data;
-        this.rpos = 0;
-        this.wpos = read ? data.length : 0;
+        this.rpos = off;
+        this.wpos = read ? len : 0;
     }
 
     @Override
@@ -196,25 +199,83 @@
         rpos += len;
     }
 
-    public PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
-        PublicKey key;
-        String keyAlg = getString();
-        if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
-            BigInteger e = getMPInt();
-            BigInteger n = getMPInt();
-            KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
-            key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
-        } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
-            BigInteger p = getMPInt();
-            BigInteger q = getMPInt();
-            BigInteger g = getMPInt();
-            BigInteger y = getMPInt();
-            KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
-            key = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
-        } else {
-            throw new IllegalStateException("Unsupported algorithm: " + keyAlg);
+    public PublicKey getPublicKey() throws SshException {
+        int ow = wpos;
+        int len = getInt();
+        wpos = rpos + len;
+        try {
+            return getRawPublicKey();
+        } finally {
+            wpos = ow;
+        }
+    }
+
+    public PublicKey getRawPublicKey() throws SshException {
+        try {
+            PublicKey key;
+            String keyAlg = getString();
+            if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
+                BigInteger e = getMPInt();
+                BigInteger n = getMPInt();
+                KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
+                key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+            } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
+                BigInteger p = getMPInt();
+                BigInteger q = getMPInt();
+                BigInteger g = getMPInt();
+                BigInteger y = getMPInt();
+                KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
+                key = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+            } else {
+                throw new IllegalStateException("Unsupported algorithm: " + keyAlg);
+            }
+            return key;
+        } catch (InvalidKeySpecException e) {
+            throw new SshException(e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new SshException(e);
+        } catch (NoSuchProviderException e) {
+            throw new SshException(e);
+        }
+    }
+
+    public KeyPair getKeyPair() throws SshException {
+        try {
+            PublicKey pub;
+            PrivateKey prv;
+            String keyAlg = getString();
+            if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
+                BigInteger e = getMPInt();
+                BigInteger n = getMPInt();
+                BigInteger d = getMPInt();
+                BigInteger qInv = getMPInt();
+                BigInteger q = getMPInt();
+                BigInteger p = getMPInt();
+                BigInteger dP = d.remainder(p.subtract(BigInteger.valueOf(1)));
+                BigInteger dQ = d.remainder(q.subtract(BigInteger.valueOf(1)));
+                KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
+                pub = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+                prv = keyFactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, d, p, q, dP, dQ, qInv));
+            } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
+                BigInteger p = getMPInt();
+                BigInteger q = getMPInt();
+                BigInteger g = getMPInt();
+                BigInteger y = getMPInt();
+                BigInteger x = getMPInt();
+                KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
+                pub = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+                prv = keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+            } else {
+                throw new IllegalStateException("Unsupported algorithm: " + keyAlg);
+            }
+            return new KeyPair(pub, prv);
+        } catch (InvalidKeySpecException e) {
+            throw new SshException(e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new SshException(e);
+        } catch (NoSuchProviderException e) {
+            throw new SshException(e);
         }
-        return key;
     }
 
     public SshConstants.Message getCommand() {
@@ -314,6 +375,17 @@
     }
 
     public void putPublicKey(PublicKey key) {
+        int ow = wpos;
+        putInt(0);
+        int ow1 = wpos;
+        putRawPublicKey(key);
+        int ow2 = wpos;
+        wpos = ow;
+        putInt(ow2 - ow1);
+        wpos = ow2;
+    }
+
+    public void putRawPublicKey(PublicKey key) {
         if (key instanceof RSAPublicKey) {
             putString(KeyPairProvider.SSH_RSA);
             putMPInt(((RSAPublicKey) key).getPublicExponent());
@@ -329,6 +401,27 @@
         }
     }
 
+    public void putKeyPair(KeyPair key) {
+        if (key.getPrivate() instanceof RSAPrivateCrtKey) {
+            putString(KeyPairProvider.SSH_RSA);
+            putMPInt(((RSAPublicKey) key.getPublic()).getPublicExponent());
+            putMPInt(((RSAPublicKey) key.getPublic()).getModulus());
+            putMPInt(((RSAPrivateCrtKey) key.getPrivate()).getPrivateExponent());
+            putMPInt(((RSAPrivateCrtKey) key.getPrivate()).getCrtCoefficient());
+            putMPInt(((RSAPrivateCrtKey) key.getPrivate()).getPrimeQ());
+            putMPInt(((RSAPrivateCrtKey) key.getPrivate()).getPrimeP());
+        } else if (key.getPublic() instanceof DSAPublicKey) {
+            putString(KeyPairProvider.SSH_DSS);
+            putMPInt(((DSAPublicKey) key.getPublic()).getParams().getP());
+            putMPInt(((DSAPublicKey) key.getPublic()).getParams().getQ());
+            putMPInt(((DSAPublicKey) key.getPublic()).getParams().getG());
+            putMPInt(((DSAPublicKey) key.getPublic()).getY());
+            putMPInt(((DSAPrivateKey) key.getPrivate()).getX());
+        } else {
+            throw new IllegalStateException("Unsupported algorithm: " + key.getPublic().getAlgorithm());
+        }
+    }
+
     public void putCommand(SshConstants.Message cmd) {
         putByte(cmd.toByte());
     }

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java Thu Dec 10 15:40:43 2009
@@ -54,7 +54,7 @@
         int oldPos = buffer.rpos();
         int len = buffer.getInt();
         buffer.wpos(buffer.rpos() + len);
-        PublicKey key = buffer.getPublicKey();
+        PublicKey key = buffer.getRawPublicKey();
         String keyAlg = (key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS;
 
         Signature verif = NamedFactory.Utils.create(session.getFactoryManager().getSignatureFactories(), keyAlg);

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java Thu Dec 10 15:40:43 2009
@@ -28,6 +28,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
 
+import org.apache.sshd.SshAgent;
 import org.apache.sshd.common.Channel;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.PtyMode;
@@ -454,7 +455,10 @@
 
     protected boolean handleAgentForwarding(Buffer buffer) throws IOException {
         boolean wantReply = buffer.getBoolean();
-        // TODO: start agent forwarding
+
+        int authSocket = ((ServerSession) session).initAgentForward();
+        addEnvVariable(SshAgent.SSH_AUTHSOCKET_ENV_NAME, Integer.toString(authSocket));
+
         if (wantReply) {
             buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
             buffer.putInt(recipient);

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHGServer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHGServer.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHGServer.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHGServer.java Thu Dec 10 15:40:43 2009
@@ -92,7 +92,7 @@
         sig.init(kp.getPublic(), kp.getPrivate());
 
         buffer = new Buffer();
-        buffer.putPublicKey(kp.getPublic());
+        buffer.putRawPublicKey(kp.getPublic());
         K_S = buffer.getCompactData();
 
         buffer.clear();

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/AgentForwardSupport.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/AgentForwardSupport.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/AgentForwardSupport.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/AgentForwardSupport.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.session;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoEventType;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.executor.ExecutorFilter;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.sshd.client.channel.AbstractClientChannel;
+import org.apache.sshd.client.future.DefaultOpenFuture;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.TcpIpForwardFilter;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * The server side fake agent, acting as an agent, but actually forwarding the requests to the auth channel on the client side.
+ */
+public class AgentForwardSupport extends IoHandlerAdapter {
+
+    private final ServerSession session;
+    private IoAcceptor acceptor;
+
+    public AgentForwardSupport(ServerSession session) {
+        this.session = session;
+    }
+
+    public synchronized int initialize() throws IOException {
+        if (this.acceptor == null) {
+            NioSocketAcceptor acceptor = new NioSocketAcceptor();
+            acceptor.setHandler(this);
+            acceptor.setReuseAddress(true);
+            acceptor.getFilterChain().addLast("executor", new ExecutorFilter(EnumSet.complementOf(EnumSet.of(IoEventType.SESSION_CREATED)).toArray(new IoEventType[0])));
+            this.acceptor = acceptor;
+            SocketAddress address = new InetSocketAddress("localhost", 0);
+            acceptor.bind(address);
+        }
+        return ((InetSocketAddress) acceptor.getLocalAddress()).getPort();
+    }
+
+    public synchronized void close() {
+        if (acceptor != null) {
+            acceptor.dispose();
+            acceptor = null;
+        }
+    }
+
+    @Override
+    public void sessionCreated(IoSession session) throws Exception {
+        AgentForwardedChannel channel = new AgentForwardedChannel(session);
+        session.setAttribute(AgentForwardedChannel.class, channel);
+        this.session.registerChannel(channel);
+        OpenFuture future = channel.open().await();
+        Throwable t = future.getException();
+        if (t instanceof Exception) {
+            throw (Exception) t;
+        } else if (t != null) {
+            throw new Exception(t);
+        }
+    }
+
+    @Override
+    public void sessionClosed(IoSession session) throws Exception {
+        AgentForwardedChannel channel = (AgentForwardedChannel) session.getAttribute(AgentForwardedChannel.class);
+        channel.close(false);
+    }
+
+    @Override
+    public void messageReceived(IoSession session, Object message) throws Exception {
+        AgentForwardedChannel channel = (AgentForwardedChannel) session.getAttribute(AgentForwardedChannel.class);
+        IoBuffer ioBuffer = (IoBuffer) message;
+        int r = ioBuffer.remaining();
+        byte[] b = new byte[r];
+        ioBuffer.get(b, 0, r);
+        channel.getOut().write(b, 0, r);
+        channel.getOut().flush();
+    }
+
+    @Override
+    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
+        cause.printStackTrace();
+        session.close(false);
+    }
+
+    public static class AgentForwardedChannel extends AbstractClientChannel {
+
+        private final IoSession serverSession;
+
+        public AgentForwardedChannel(IoSession serverSession) {
+            super("auth-agent@openssh.com");
+            this.serverSession = serverSession;
+        }
+
+        public synchronized OpenFuture open() throws Exception {
+            return internalOpen();
+        }
+
+        @Override
+        protected synchronized void doOpen() throws Exception {
+            out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+        }
+
+        @Override
+        protected synchronized void doClose() {
+            serverSession.close(false);
+            super.doClose();
+        }
+
+        protected synchronized void doWriteData(byte[] data, int off, int len) throws IOException {
+            IoBuffer buf = IoBuffer.allocate(len);
+            buf.put(data, off, len);
+            buf.flip();
+            localWindow.consumeAndCheck(len);
+            serverSession.write(buf);
+        }
+
+        @Override
+        public void handleEof() throws IOException {
+            super.handleEof();
+            serverSession.close(false);
+        }
+    }
+
+}

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java?rev=889300&r1=889299&r2=889300&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java Thu Dec 10 15:40:43 2009
@@ -68,6 +68,7 @@
     private int authTimeout = 10 * 60 * 1000; // 10 minutes in milliseconds
     private boolean allowMoreSessions = true;
     private final TcpipForwardSupport tcpipForward;
+    private final AgentForwardSupport agentForward;
 
     private List<NamedFactory<UserAuth>> userAuthFactories;
 
@@ -80,6 +81,7 @@
         maxAuthRequests = getIntProperty(FactoryManager.MAX_AUTH_REQUESTS, maxAuthRequests);
         authTimeout = getIntProperty(FactoryManager.AUTH_TIMEOUT, authTimeout);
         tcpipForward = new TcpipForwardSupport(this);
+        agentForward = new AgentForwardSupport(this);
         log.info("Session created...");
         sendServerIdentification();
         sendKexInit();
@@ -89,6 +91,7 @@
     public CloseFuture close(boolean immediately) {
         unscheduleAuthTimer();
         tcpipForward.close();
+        agentForward.close();
         return super.close(immediately);
     }
 
@@ -452,5 +455,9 @@
         }
     }
 
+    public int initAgentForward() throws IOException {
+        return agentForward.initialize();
+    }
+
 
 }

Added: mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/AgentTest.java?rev=889300&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/AgentTest.java (added)
+++ mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/AgentTest.java Thu Dec 10 15:40:43 2009
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd;
+
+import org.apache.sshd.agent.AgentClient;
+import org.apache.sshd.agent.AgentServer;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.junit.Test;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.List;
+
+import static junit.framework.Assert.*;
+
+public class AgentTest {
+
+    @Test
+    public void testAgent() throws Exception {
+        AgentServer agent = new AgentServer();
+        String authSocket = agent.start();
+
+        SshAgent client = new AgentClient(authSocket);
+        List<SshAgent.Pair<PublicKey, String>> keys = client.getIdentities();
+        assertNotNull(keys);
+        assertEquals(0, keys.size());
+
+        KeyPair[] k = new FileKeyPairProvider(new String[] { "src/test/resources/hostkey.pem"}).loadKeys();
+        client.addIdentity(k[0], "");
+        keys = client.getIdentities();
+        assertNotNull(keys);
+        assertEquals(1, keys.size());
+
+        client.removeIdentity(k[0].getPublic());
+        keys = client.getIdentities();
+        assertNotNull(keys);
+        assertEquals(0, keys.size());
+
+        client.removeAllIdentities();
+
+        client.close();
+
+        agent.close();
+    }
+}