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();
+ }
+}