[prev in list] [next in list] [prev in thread] [next in thread] 

List:       mina-commits
Subject:    [1/3] [SSHD-235][SSHD-287] Refactor client side authentication API, support partial authentication
From:       gnodet () apache ! org
Date:       2014-01-31 10:52:10
Message-ID: dcda6238e01d4dbd9d025edd5d487665 () git ! apache ! org
[Download RAW message or body]

Updated Branches:
  refs/heads/master d5b96d9fe -> 8f7dff123


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthServiceOld.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthServiceOld.java \
b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthServiceOld.java
 new file mode 100644
index 0000000..389fe39
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthServiceOld.java
 @@ -0,0 +1,189 @@
+/*
+ * 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.session;
+
+import java.io.IOException;
+
+import org.apache.sshd.client.auth.deprecated.UserAuth;
+import org.apache.sshd.client.UserInteraction;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.DefaultAuthFuture;
+import org.apache.sshd.common.Service;
+import org.apache.sshd.common.Session;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.util.Buffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Client side <code>ssh-auth</code> service.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientUserAuthServiceOld implements Service {
+
+    /** Our logger */
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * When !authFuture.isDone() the current authentication
+     */
+    private UserAuth userAuth;
+
+    /**
+     * 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;
+
+    protected final ClientSessionImpl session;
+    protected final Object lock;
+
+    public ClientUserAuthServiceOld(Session s) {
+        if (!(s instanceof ClientSessionImpl)) {
+            throw new IllegalStateException("Client side service used on server \
side"); +        }
+        session = (ClientSessionImpl) s;
+        lock = session.getLock();
+        // Maintain the current auth status in the authFuture.
+        authFuture = new DefaultAuthFuture(lock);
+    }
+
+    public ClientSessionImpl getSession() {
+        return session;
+    }
+
+    public void start() {
+        synchronized (lock) {
+            log.debug("accepted");
+            // kick start the authentication process by failing the pending auth.
+            this.authFuture.setAuthed(false);
+        }
+    }
+
+    public void process(byte cmd, Buffer buffer) throws Exception {
+        if (this.authFuture.isSuccess()) {
+            throw new IllegalStateException("UserAuth message delivered to \
authenticated client"); +        } else if (this.authFuture.isDone()) {
+            log.debug("Ignoring random message");
+            // ignore for now; TODO: random packets
+        } else if (cmd == SshConstants.SSH_MSG_USERAUTH_BANNER) {
+            String welcome = buffer.getString();
+            String lang = buffer.getString();
+            log.debug("Welcome banner: {}", welcome);
+            UserInteraction ui = session.getFactoryManager().getUserInteraction();
+            if (ui != null) {
+                ui.welcome(welcome);
+            }
+        } else {
+            buffer.rpos(buffer.rpos() - 1);
+            processUserAuth(buffer);
+        }
+    }
+
+    /**
+     * return true if/when ready for auth; false if never ready.
+     * @return server is ready and waiting for auth
+     */
+    private boolean readyForAuth(UserAuth userAuth) {
+        // 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?");
+        }
+        // Set up the next round of authentication.  Each round gets a new lock.
+        this.userAuth = userAuth;
+        // The new future !isDone() - i.e., in progress blocking out other waits.
+        this.authFuture = new DefaultAuthFuture(lock);
+        log.debug("ready to try authentication with new lock");
+        return true;
+    }
+
+    /**
+     * execute one step in user authentication.
+     * @param buffer
+     * @throws java.io.IOException
+     */
+    private void processUserAuth(Buffer buffer) throws IOException {
+        log.debug("processing {}", userAuth);
+        switch (userAuth.next(buffer)) {
+            case Success:
+                log.debug("succeeded with {}", userAuth);
+                session.setAuthenticated();
+                session.switchToNextService();
+                // Will wake up anyone sitting in waitFor
+                authFuture.setAuthed(true);
+                break;
+            case Failure:
+                log.debug("failed with {}", userAuth);
+                this.userAuth = null;
+                // Will wake up anyone sitting in waitFor
+                this.authFuture.setAuthed(false);
+                break;
+            case Continued:
+                // Will wake up anyone sitting in waitFor
+                log.debug("continuing with {}", userAuth);
+                break;
+        }
+    }
+
+    public CloseFuture close(boolean immediately) {
+        if (!authFuture.isDone()) {
+            authFuture.setException(new SshException("Session is closed"));
+        }
+        CloseFuture future = new DefaultCloseFuture(lock);
+        future.setClosed();
+        return future;
+    }
+
+    public AuthFuture auth(UserAuth userAuth) throws IOException {
+        log.debug("Trying authentication with {}", userAuth);
+        synchronized (lock) {
+            if (readyForAuth(userAuth)) {
+                processUserAuth(null);
+            }
+            return authFuture;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/common/KeyPairProvider.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/KeyPairProvider.java \
b/sshd-core/src/main/java/org/apache/sshd/common/KeyPairProvider.java index \
                63b87c2..70a379e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/KeyPairProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/KeyPairProvider.java
@@ -54,6 +54,13 @@ public interface KeyPairProvider {
     String ECDSA_SHA2_NISTP521 = "ecdsa-sha2-nistp521";
 
     /**
+     * Load available keys.
+     *
+     * @return an array of available keys, never <code>null</code>
+     */
+    KeyPair[] loadKeys();
+
+    /**
      * Load a key of the specified type which can be "ssh-rsa", "ssh-dss", or
      * "ecdsa-sha2-nistp{256,384,521}". If there is no key of this type, return
      * <code>null</code>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java \
b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java index \
                bb0cf45..0cb5f3e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -53,10 +53,14 @@ public interface SshConstants {
     static final byte SSH_MSG_USERAUTH_FAILURE=               51;
     static final byte SSH_MSG_USERAUTH_SUCCESS=               52;
     static final byte SSH_MSG_USERAUTH_BANNER=                53;
+
     static final byte SSH_MSG_USERAUTH_INFO_REQUEST=          60;
     static final byte SSH_MSG_USERAUTH_INFO_RESPONSE=         61;
+
     static final byte SSH_MSG_USERAUTH_PK_OK=                 60;
 
+    static final byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ=      60;
+
     static final byte SSH_MSG_USERAUTH_GSSAPI_MIC=            66;
 
     static final byte SSH_MSG_GLOBAL_REQUEST=                 80;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java \
b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
 index 0337e87..bf6650b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
                
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
 @@ -19,10 +19,6 @@
 package org.apache.sshd.common.keyprovider;
 
 import java.security.KeyPair;
-import java.security.interfaces.DSAKey;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.RSAKey;
-import java.security.spec.ECParameterSpec;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -30,6 +26,8 @@ import org.apache.sshd.common.KeyPairProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.sshd.common.util.KeyUtils.getKeyType;
+
 /**
  * TODO Add javadoc
  *
@@ -69,30 +67,5 @@ public abstract class AbstractKeyPairProvider implements \
KeyPairProvider {  return sb.toString();
     }
 
-    protected String getKeyType(KeyPair kp) {
-        Object key = kp.getPrivate() != null ? kp.getPrivate() : kp.getPublic();
-        if (key instanceof DSAKey) {
-            return SSH_DSS;
-        } else if (key instanceof RSAKey) {
-            return SSH_RSA;
-        } else if (key instanceof ECKey) {
-            ECKey ecKey = (ECKey) key;
-            ECParameterSpec ecSpec = ecKey.getParams();
-            /*
-             * TODO make this more robust by checking the actual parameters instead \
                of
-             * just the field size.
-             */
-            switch (ecSpec.getCurve().getField().getFieldSize()) {
-            case 256:
-                return ECDSA_SHA2_NISTP256;
-            case 384:
-                return ECDSA_SHA2_NISTP384;
-            case 521:
-                return ECDSA_SHA2_NISTP521;
-            }
-        }
-        return null;
-    }
-
-    protected abstract KeyPair[] loadKeys();
+    public abstract KeyPair[] loadKeys();
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java \
b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java index \
                cc050e2..e6d1639 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -255,9 +255,12 @@ public abstract class AbstractSession implements Session {
         return authed;
     }
 
-    public void setAuthenticated(String username) throws IOException {
-        this.authed = true;
+    public void setUsername(String username) {
         this.username = username;
+    }
+
+    public void setAuthenticated() throws IOException {
+        this.authed = true;
         sendEvent(SessionListener.Event.Authenticated);
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/common/util/KeyUtils.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/KeyUtils.java \
b/sshd-core/src/main/java/org/apache/sshd/common/util/KeyUtils.java index \
                cd9fd65..123d594 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/KeyUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/KeyUtils.java
@@ -18,8 +18,15 @@
  */
 package org.apache.sshd.common.util;
 
+import java.security.Key;
+import java.security.KeyPair;
 import java.security.PublicKey;
+import java.security.interfaces.DSAKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.RSAKey;
+import java.security.spec.ECParameterSpec;
 
+import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.digest.MD5;
 
 /**
@@ -49,6 +56,46 @@ public class KeyUtils {
         }
     }
 
+    /**
+     * Retrieve the key type
+     *
+     * @param kp a key pair
+     * @return the key type
+     */
+    public static String getKeyType(KeyPair kp) {
+        return getKeyType(kp.getPrivate() != null ? kp.getPrivate() : \
kp.getPublic()); +    }
+
+    /**
+     * Retrieve the key type
+     *
+     * @param key a public or private key
+     * @return the key type
+     */
+    public static String getKeyType(Key key) {
+        if (key instanceof DSAKey) {
+            return KeyPairProvider.SSH_DSS;
+        } else if (key instanceof RSAKey) {
+            return KeyPairProvider.SSH_RSA;
+        } else if (key instanceof ECKey) {
+            ECKey ecKey = (ECKey) key;
+            ECParameterSpec ecSpec = ecKey.getParams();
+            /*
+             * TODO make this more robust by checking the actual parameters instead \
of +             * just the field size.
+             */
+            switch (ecSpec.getCurve().getField().getFieldSize()) {
+                case 256:
+                    return KeyPairProvider.ECDSA_SHA2_NISTP256;
+                case 384:
+                    return KeyPairProvider.ECDSA_SHA2_NISTP384;
+                case 521:
+                    return KeyPairProvider.ECDSA_SHA2_NISTP521;
+            }
+        }
+        return null;
+    }
+
     private KeyUtils() {
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java \
b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
 index 94cfdba..e0e7152 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
                
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
 @@ -44,9 +44,6 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth \
{  
     @Override
     protected Boolean doAuth(Buffer buffer, boolean init) throws Exception {
-        if (!"ssh-connection".equals(service)) {
-            throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, \
                "Unsupported service '" + service + "'");
-        }
         if (init) {
             // Prompt for password
             buffer = \
session.createBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST, 0);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java \
b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java index \
                50754ac..6219c3a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
@@ -46,9 +46,6 @@ public class UserAuthPassword extends AbstractUserAuth {
         if (!init) {
             throw new IllegalStateException();
         }
-        if (!"ssh-connection".equals(service)) {
-            throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, \
                "Unsupported service '" + service + "'");
-        }
         boolean newPassword = buffer.getBoolean();
         if (newPassword) {
             throw new IllegalStateException("Password changes are not supported");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java \
b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java index \
                0e4de86..3f4971a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
@@ -50,9 +50,6 @@ public class UserAuthPublicKey extends AbstractUserAuth {
         if (!init) {
             throw new IllegalStateException();
         }
-        if (!"ssh-connection".equals(service)) {
-            throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, \
                "Unsupported service '" + service + "'");
-        }
         boolean hasSig = buffer.getBoolean();
         String alg = buffer.getString();
 
@@ -88,7 +85,7 @@ public class UserAuthPublicKey extends AbstractUserAuth {
             buf.putString(session.getKex().getH());
             buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
             buf.putString(username);
-            buf.putString("ssh-connection");
+            buf.putString(service);
             buf.putString("publickey");
             buf.putByte((byte) 1);
             buf.putString(keyAlg);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java \
b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java \
                index 49612e2..5f7dd19 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
                
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
 @@ -129,6 +129,7 @@ public class ServerUserAuthService implements Service {
                                 + username + ", " + service + ")");
                 return;
             }
+            // TODO: verify that the service is supported
             this.authMethod = method;
             if (nbAuthRequests++ > maxAuthRequests) {
                 session.disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Too \
many authentication failures"); @@ -197,7 +198,8 @@ public class \
ServerUserAuthService implements Service {  
                 buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_SUCCESS, \
0);  session.writePacket(buffer);
-                session.setAuthenticated(username);
+                session.setUsername(username);
+                session.setAuthenticated();
                 session.startService(authService);
                 session.resetIdleTimeout();
                 log.info("Session {}@{} authenticated", username, \
session.getIoSession().getRemoteAddress());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0824b93b/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
                
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ClientTest.java \
b/sshd-core/src/test/java/org/apache/sshd/ClientTest.java index 6fc48e5..6e0de51 \
                100644
--- a/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
@@ -364,6 +364,26 @@ public class ClientTest {
     }
 
     @Test
+    public void testPublicKeyAuthNew() throws Exception {
+        SshClient client = SshClient.setUpDefaultClient();
+        client.start();
+        ClientSession session = client.connect("smx", "localhost", \
port).await().getSession(); +        KeyPair pair = \
Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA); +        \
session.addPublicKeyIdentity(pair); +        session.auth().verify();
+    }
+
+    @Test
+    public void testPasswordAuthNew() throws Exception {
+        SshClient client = SshClient.setUpDefaultClient();
+        client.start();
+        ClientSession session = client.connect("smx", "localhost", \
port).await().getSession(); +        KeyPair pair = \
Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA); +        \
session.addPasswordIdentity("smx"); +        session.auth().verify();
+    }
+
+    @Test
     public void testClientDisconnect() throws Exception {
         TestEchoShellFactory.TestEchoShell.latch = new CountDownLatch(1);
         try


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic