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 2013/07/19 08:33:24 UTC
[1/2] git commit: [SSHD-214] Test
com.apache.sshd.AgentTest.testAgentForwarding may hang
Updated Branches:
refs/heads/master 833cedeef -> 2382d6060
[SSHD-214] Test com.apache.sshd.AgentTest.testAgentForwarding may hang
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/eaef1c30
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/eaef1c30
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/eaef1c30
Branch: refs/heads/master
Commit: eaef1c30d456974294cfc9c03426fa0c7bdee27c
Parents: 833cede
Author: Guillaume Nodet <gn...@apache.org>
Authored: Fri Jul 19 08:31:28 2013 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Fri Jul 19 08:31:28 2013 +0200
----------------------------------------------------------------------
sshd-core/src/test/java/org/apache/sshd/AgentTest.java | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/eaef1c30/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
index 0d2db43..134c093 100644
--- a/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
@@ -131,7 +131,10 @@ public class AgentTest {
channel1.open().await();
synchronized (shellFactory.shell) {
- shellFactory.shell.wait();
+ System.out.println("Possibly waiting for remote shell to start");
+ if (!shellFactory.shell.started) {
+ shellFactory.shell.wait();
+ }
}
SshClient client2 = SshClient.setUpDefaultClient();
@@ -168,9 +171,12 @@ public class AgentTest {
public class TestEchoShell extends EchoShell {
+ boolean started;
+
@Override
public synchronized void start(Environment env) throws IOException {
super.start(env);
+ started = true;
notifyAll();
}
[2/2] git commit: [SSHD-220] Return a usable AuthFuture when
authenticating on the client side
Posted by gn...@apache.org.
[SSHD-220] Return a usable AuthFuture when authenticating on the client side
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/2382d606
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/2382d606
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/2382d606
Branch: refs/heads/master
Commit: 2382d6060d6e490fe8cdba670e053f4b4bc4c710
Parents: eaef1c3
Author: Guillaume Nodet <gn...@apache.org>
Authored: Fri Jul 19 08:33:17 2013 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Fri Jul 19 08:33:17 2013 +0200
----------------------------------------------------------------------
.../main/java/org/apache/sshd/SshClient.java | 15 +-
.../sshd/client/session/ClientSessionImpl.java | 249 ++++++++-----------
.../test/java/org/apache/sshd/ServerTest.java | 34 ++-
3 files changed, 139 insertions(+), 159 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2382d606/sshd-core/src/main/java/org/apache/sshd/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/SshClient.java
index 9d0bfcb..797d6a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshClient.java
@@ -42,6 +42,7 @@ import org.apache.sshd.client.ServerKeyVerifier;
import org.apache.sshd.client.SessionFactory;
import org.apache.sshd.client.UserInteraction;
import org.apache.sshd.client.channel.ChannelShell;
+import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.future.DefaultConnectFuture;
import org.apache.sshd.client.kex.DHG1;
@@ -452,19 +453,19 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
ClientSession session = client.connect(host, port).await().getSession();
int ret = ClientSession.WAIT_AUTH;
- while ((ret & ClientSession.WAIT_AUTH) != 0) {
+ AuthFuture authFuture;
+ do {
if (hasKeys) {
- session.authAgent(login);
- ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+ authFuture = session.authAgent(login);
} else {
System.out.print("Password:");
BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
String password = r.readLine();
- session.authPassword(login, password);
- ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+ authFuture = session.authPassword(login, password);
}
- }
- if ((ret & ClientSession.CLOSED) != 0) {
+ authFuture.await();
+ } while (authFuture.isFailure());
+ if (!authFuture.isSuccess()) {
System.err.println("error");
System.exit(-1);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2382d606/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index de12cd4..76a2f54 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -66,7 +66,11 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
private static final String AUTHENTICATION_SERVICE = "ssh-connection";
private UserAuth userAuth;
- private AuthFuture authFuture;
+ /**
+ * The AuthFuture that is being used by the current auth request. This encodes the state.
+ * isSuccess -> authenticated, else if isDone -> server waiting for user auth, else authenticating.
+ */
+ private volatile AuthFuture authFuture;
/**
* For clients to store their own metadata
@@ -78,6 +82,8 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
log.info("Session created...");
sendClientIdentification();
sendKexInit();
+ // Maintain the current auth status in the authFuture.
+ authFuture = new DefaultAuthFuture(lock);
}
public ClientFactoryManager getClientFactoryManager() {
@@ -88,156 +94,117 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
return kex;
}
+ /**
+ * return true if/when ready for auth; false if never ready.
+ * @return server is ready and waiting for auth
+ */
+ private boolean readyForAuth() {
+ // isDone indicates that the last auth finished and a new one can commence.
+ while (!this.authFuture.isDone()) {
+ log.debug("waiting to send authentication");
+ try {
+ this.authFuture.await();
+ } catch (InterruptedException e) {
+ log.debug("Unexpected interrupt", e);
+ throw new RuntimeException(e);
+ }
+ }
+ if (this.authFuture.isSuccess()) {
+ log.debug("already authenticated");
+ throw new IllegalStateException("Already authenticated");
+ }
+ if (this.authFuture.getException() != null) {
+ log.debug("probably closed", this.authFuture.getException());
+ return false;
+ }
+ if (!this.authFuture.isFailure()) {
+ log.debug("unexpected state");
+ throw new IllegalStateException("Unexpected authentication state");
+ }
+ if (this.userAuth != null) {
+ log.debug("authentication already in progress");
+ throw new IllegalStateException("Authentication already in progress?");
+ }
+ log.debug("ready to try authentication with new lock");
+ // The new future !isDone() - i.e., in progress blocking out other waits.
+ this.authFuture = new DefaultAuthFuture(lock);
+ return true;
+ }
+
+ /**
+ * execute one step in user authentication.
+ * @param buffer
+ * @throws IOException
+ */
+ private void processUserAuth(Buffer buffer) throws IOException {
+ log.debug("processing {}", userAuth);
+ switch (userAuth.next(buffer)) {
+ case Success:
+ log.debug("succeeded with {}", userAuth);
+ this.authed = true;
+ this.username = userAuth.getUsername();
+ setState(State.Running);
+ // Will wake up anyone sitting in waitFor
+ authFuture.setAuthed(true);
+ startHeartBeat();
+ break;
+ case Failure:
+ log.debug("failed with {}", userAuth);
+ this.userAuth = null;
+ setState(State.WaitForAuth);
+ // Will wake up anyone sitting in waitFor
+ this.authFuture.setAuthed(false);
+ break;
+ case Continued:
+ // Will wake up anyone sitting in waitFor
+ setState(State.UserAuth);
+ log.debug("continuing with {}", userAuth);
+ break;
+ }
+ }
+
public AuthFuture authAgent(String user) throws IOException {
+ log.debug("Trying agent authentication");
+ if (getFactoryManager().getAgentFactory() == null) {
+ throw new IllegalStateException("No ssh agent factory has been configured");
+ }
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");
- }
- if (getFactoryManager().getAgentFactory() == null) {
- throw new IllegalStateException("No ssh agent factory has been configured");
- }
- waitFor(CLOSED | WAIT_AUTH, 0);
- if (closeFuture.isClosed()) {
- throw new IllegalStateException("Session is closed");
- }
- authFuture = new DefaultAuthFuture(lock);
- userAuth = new UserAuthAgent(this, AUTHENTICATION_SERVICE, user);
- setState(State.UserAuth);
-
- switch (userAuth.next(null)) {
- case Success:
- authFuture.setAuthed(true);
- username = userAuth.getUsername();
- authed = true;
- setState(State.Running);
- break;
- case Failure:
- authFuture.setAuthed(false);
- userAuth = null;
- setState(State.WaitForAuth);
- break;
- case Continued:
- break;
+ if (readyForAuth()) {
+ userAuth = new UserAuthAgent(this, AUTHENTICATION_SERVICE, user);
+ processUserAuth(null);
}
return authFuture;
}
}
public AuthFuture authPassword(String user, String password) throws IOException {
+ log.debug("Trying password authentication");
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 UserAuthPassword(this, AUTHENTICATION_SERVICE, user, password);
- setState(State.UserAuth);
-
- switch (userAuth.next(null)) {
- case Success:
- authFuture.setAuthed(true);
- username = userAuth.getUsername();
- authed = true;
- setState(State.Running);
- break;
- case Failure:
- authFuture.setAuthed(false);
- userAuth = null;
- setState(State.WaitForAuth);
- break;
- case Continued:
- break;
+ if (readyForAuth()) {
+ userAuth = new UserAuthPassword(this, AUTHENTICATION_SERVICE, user, password);
+ processUserAuth(null);
}
return authFuture;
}
}
public AuthFuture authInteractive(String user, String password) throws IOException {
+ log.debug("Trying keyboard-interactive authentication");
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 UserAuthKeyboardInteractive(this, AUTHENTICATION_SERVICE, user, password);
- setState(State.UserAuth);
-
- switch (userAuth.next(null)) {
- case Success:
- authFuture.setAuthed(true);
- username = userAuth.getUsername();
- authed = true;
- setState(State.Running);
- break;
- case Failure:
- authFuture.setAuthed(false);
- userAuth = null;
- setState(State.WaitForAuth);
- break;
- case Continued:
- break;
+ if (readyForAuth()) {
+ userAuth = new UserAuthKeyboardInteractive(this, AUTHENTICATION_SERVICE, user, password);
+ processUserAuth(null);
}
return authFuture;
}
- }
+ }
public AuthFuture authPublicKey(String user, KeyPair key) throws IOException {
+ log.debug("Trying publickey authentication");
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 UserAuthPublicKey(this, AUTHENTICATION_SERVICE, user, key);
- setState(State.UserAuth);
-
- switch (userAuth.next(null)) {
- case Success:
- authFuture.setAuthed(true);
- username = userAuth.getUsername();
- authed = true;
- setState(State.Running);
- break;
- case Failure:
- authFuture.setAuthed(false);
- userAuth = null;
- setState(State.WaitForAuth);
- break;
- case Continued:
- break;
+ if (readyForAuth()) {
+ userAuth = new UserAuthPublicKey(this, AUTHENTICATION_SERVICE, user, key);
+ processUserAuth(null);
}
return authFuture;
}
@@ -302,7 +269,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
@Override
public CloseFuture close(boolean immediately) {
synchronized (lock) {
- if (authFuture != null && !authFuture.isDone()) {
+ if (!authFuture.isDone()) {
authFuture.setException(new SshException("Session is closed"));
}
return super.close(immediately);
@@ -377,6 +344,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Protocol error: expected packet SSH_MSG_SERVICE_ACCEPT, got " + cmd);
return;
}
+ authFuture.setAuthed(false);
setState(State.WaitForAuth);
break;
case WaitForAuth:
@@ -397,22 +365,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
}
} else {
buffer.rpos(buffer.rpos() - 1);
- switch (userAuth.next(buffer)) {
- case Success:
- authFuture.setAuthed(true);
- username = userAuth.getUsername();
- authed = true;
- setState(State.Running);
- startHeartBeat();
- break;
- case Failure:
- authFuture.setAuthed(false);
- userAuth = null;
- setState(State.WaitForAuth);
- break;
- case Continued:
- break;
- }
+ processUserAuth(buffer);
}
break;
case Running:
@@ -471,10 +424,10 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
if (closeFuture.isClosed()) {
cond |= CLOSED;
}
- if (authed) {
+ if (authed) { // authFuture.isSuccess()
cond |= AUTHED;
}
- if (getState() == State.WaitForAuth) {
+ if (authFuture.isFailure()) {
cond |= WAIT_AUTH;
}
if ((cond & mask) != 0) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2382d606/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
index ebdb569..9b7253f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeoutException;
import org.apache.mina.core.session.IoSession;
import org.apache.sshd.client.SessionFactory;
+import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.session.ClientSessionImpl;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.SshConstants;
@@ -35,10 +36,13 @@ import org.apache.sshd.util.BogusPasswordAuthenticator;
import org.apache.sshd.util.EchoShellFactory;
import org.apache.sshd.util.Utils;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
/**
* TODO Add javadoc
*
@@ -79,7 +83,7 @@ public class ServerTest {
* @throws Exception
*/
@Test
- public void testFailAuthentication() throws Exception {
+ public void testFailAuthenticationWithWaitFor() throws Exception {
sshd.getProperties().put(SshServer.MAX_AUTH_REQUESTS, "10");
client = SshClient.setUpDefaultClient();
@@ -95,7 +99,29 @@ public class ServerTest {
throw new TimeoutException();
}
}
- Assert.assertTrue(nbTrials > 10);
+ assertTrue(nbTrials > 10);
+ }
+
+ @Test
+ public void testFailAuthenticationWithFuture() throws Exception {
+ sshd.getProperties().put(SshServer.MAX_AUTH_REQUESTS, "10");
+
+ client = SshClient.setUpDefaultClient();
+ client.start();
+ ClientSession s = client.connect("localhost", port).await().getSession();
+ int nbTrials = 0;
+ AuthFuture authFuture;
+ do {
+ nbTrials++;
+ assertTrue(nbTrials < 100);
+ authFuture = s.authPassword("smx", "buggy");
+ assertTrue(authFuture.await(5000));
+ assertTrue(authFuture.isDone());
+ assertFalse(authFuture.isSuccess());
+ }
+ while (authFuture.isFailure());
+ assertNotNull(authFuture.getException());
+ assertTrue(nbTrials > 10);
}
@Test
@@ -106,7 +132,7 @@ public class ServerTest {
client.start();
ClientSession s = client.connect("localhost", port).await().getSession();
int res = s.waitFor(ClientSession.CLOSED, 5000);
- Assert.assertTrue((res & ClientSession.CLOSED) != 0);
+ assertTrue((res & ClientSession.CLOSED) != 0);
}
@Test