You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/09/17 08:00:02 UTC
[1/3] mina-sshd git commit: [SSHD-563] Provide server-side
KeyboardInteractiveAuthenticator
Repository: mina-sshd
Updated Branches:
refs/heads/master d19f5eca5 -> 75643fcb1
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
new file mode 100644
index 0000000..f6cc26e
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
@@ -0,0 +1,523 @@
+/*
+ * 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.common.auth;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.session.ClientConnectionServiceFactory;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.FactoryManagerUtils;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.deprecated.ClientUserAuthServiceOld;
+import org.apache.sshd.deprecated.UserAuthKeyboardInteractive;
+import org.apache.sshd.deprecated.UserAuthPassword;
+import org.apache.sshd.deprecated.UserAuthPublicKey;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.UserAuthKeyboardInteractiveFactory;
+import org.apache.sshd.server.auth.UserAuthPasswordFactory;
+import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator;
+import org.apache.sshd.server.auth.keyboard.InteractiveChallenge;
+import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
+import org.apache.sshd.server.auth.keyboard.PromptEntry;
+import org.apache.sshd.server.auth.password.PasswordAuthenticator;
+import org.apache.sshd.server.auth.password.PasswordChangeRequiredException;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.session.ServerSessionImpl;
+import org.apache.sshd.server.session.SessionFactory;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AuthenticationTest extends BaseTestSupport {
+
+ private static final String WELCOME = "Welcome to SSHD";
+
+ private SshServer sshd;
+ private int port;
+
+ public AuthenticationTest() {
+ super();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ sshd = setupTestServer();
+ FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.WELCOME_BANNER, WELCOME);
+ FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, "publickey,password publickey,keyboard-interactive");
+ sshd.setSessionFactory(new SessionFactory(sshd) {
+ @Override
+ protected ServerSessionImpl doCreateSession(IoSession ioSession) throws Exception {
+ return new TestSession(getServer(), ioSession);
+ }
+ });
+ sshd.start();
+ port = sshd.getPort();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (sshd != null) {
+ sshd.stop(true);
+ }
+ }
+
+ @Test
+ public void testWrongPassword() throws Exception {
+ try (SshClient client = setupTestClient()) {
+ client.start();
+ try (ClientSession s = client.connect("user", TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.addPasswordIdentity("bad password");
+ assertAuthenticationResult(getCurrentTestName(), s.auth(), false);
+ }
+ }
+ }
+
+ @Test
+ public void testChangeUser() throws Exception {
+ try (SshClient client = setupTestClient()) {
+ client.setServiceFactories(Arrays.asList(
+ new ClientUserAuthServiceOld.Factory(),
+ ClientConnectionServiceFactory.INSTANCE
+ ));
+
+ client.start();
+
+ try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
+
+ for (String username : new String[]{"user1", "user2"}) {
+ assertAuthenticationResult(username, authPassword(s, username, "the-password"), false);
+ }
+
+ // Note that WAIT_AUTH flag should be false, but since the internal
+ // authentication future is not updated, it's still returned
+ assertEquals("Mismatched client session close mask", ClientSession.CLOSED | ClientSession.WAIT_AUTH, s.waitFor(ClientSession.CLOSED, 1000));
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ @Test // see SSHD-196
+ public void testChangePassword() throws Exception {
+ final PasswordAuthenticator delegate = sshd.getPasswordAuthenticator();
+ final AtomicInteger attemptsCount = new AtomicInteger(0);
+ sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
+ @Override
+ public boolean authenticate(String username, String password, ServerSession session)
+ throws PasswordChangeRequiredException {
+ if (attemptsCount.incrementAndGet() == 1) {
+ throw new PasswordChangeRequiredException(attemptsCount.toString(), getCurrentTestName(), ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
+ }
+
+ return delegate.authenticate(username, password, session);
+ }
+ });
+
+ final AtomicInteger changesCount = new AtomicInteger(0);
+ sshd.setUserAuthFactories(Collections.<NamedFactory<org.apache.sshd.server.auth.UserAuth>>singletonList(
+ new org.apache.sshd.server.auth.UserAuthPasswordFactory() {
+ @Override
+ public org.apache.sshd.server.auth.UserAuth create() {
+ return new org.apache.sshd.server.auth.UserAuthPassword() {
+ @Override
+ protected Boolean handleClientPasswordChangeRequest(
+ Buffer buffer, ServerSession session, String username, String oldPassword, String newPassword)
+ throws Exception {
+ if (changesCount.incrementAndGet() == 1) {
+ assertNotEquals("Non-different passwords", oldPassword, newPassword);
+ return checkPassword(buffer, session, username, newPassword);
+ } else {
+ return super.handleClientPasswordChangeRequest(buffer, session, username, oldPassword, newPassword);
+ }
+ }
+ };
+ }
+ }
+ ));
+ FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+
+ try (SshClient client = setupTestClient()) {
+ final AtomicInteger updatesCount = new AtomicInteger(0);
+ client.setUserInteraction(new UserInteraction() {
+ @Override
+ public void welcome(ClientSession session, String banner, String lang) {
+ // ignored
+ }
+
+ @Override
+ public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+ throw new UnsupportedOperationException("Unexpected call");
+ }
+
+ @Override
+ public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
+ assertEquals("Mismatched prompt", getCurrentTestName(), prompt);
+ assertEquals("Mismatched language", ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE, lang);
+ assertEquals("Unexpected repeated call", 1, updatesCount.incrementAndGet());
+ return getCurrentTestName();
+ }
+ });
+
+ final AtomicInteger sentCount = new AtomicInteger(0);
+ client.setUserAuthFactories(Collections.<NamedFactory<org.apache.sshd.client.auth.UserAuth>>singletonList(
+ new org.apache.sshd.client.auth.UserAuthPasswordFactory() {
+ @Override
+ public org.apache.sshd.client.auth.UserAuth create() {
+ return new org.apache.sshd.client.auth.UserAuthPassword() {
+ @Override
+ protected void sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
+ int count = sentCount.incrementAndGet();
+ // 1st one is the original one (which is denied by the server)
+ // 2nd one is the updated one retrieved from the user interaction
+ if (count == 2) {
+ super.sendPassword(buffer, session, getClass().getName(), newPassword);
+ } else {
+ super.sendPassword(buffer, session, oldPassword, newPassword);
+ }
+ }
+ };
+ }
+ }));
+ FactoryManagerUtils.updateProperty(client, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+
+ client.start();
+
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.addPasswordIdentity(getCurrentTestName());
+ s.auth().verify(11L, TimeUnit.SECONDS);
+ assertEquals("No password change request generated", 2, attemptsCount.get());
+ assertEquals("No password change handled", 1, changesCount.get());
+ assertEquals("No user interaction invoked", 1, updatesCount.get());
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ @Test
+ public void testAuthPasswordOnly() throws Exception {
+ try (SshClient client = setupTestClient()) {
+ client.setServiceFactories(Arrays.asList(
+ new ClientUserAuthServiceOld.Factory(),
+ ClientConnectionServiceFactory.INSTANCE
+ ));
+ client.start();
+
+ try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
+ assertAuthenticationResult(getCurrentTestName(), authPassword(s, getCurrentTestName(), getCurrentTestName()), false);
+ s.close(true);
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ @Test
+ public void testAuthKeyPassword() throws Exception {
+ try (SshClient client = setupTestClient()) {
+ client.setServiceFactories(Arrays.asList(
+ new ClientUserAuthServiceOld.Factory(),
+ ClientConnectionServiceFactory.INSTANCE
+ ));
+ client.start();
+
+ try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
+
+ KeyPair pair = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+ assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
+ assertAuthenticationResult(UserAuthMethodFactory.PASSWORD, authPassword(s, getCurrentTestName(), getCurrentTestName()), true);
+ s.close(true);
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ @Test
+ public void testAuthDefaultKeyInteractive() throws Exception {
+ try (SshClient client = setupTestClient()) {
+ client.setServiceFactories(Arrays.asList(
+ new ClientUserAuthServiceOld.Factory(),
+ ClientConnectionServiceFactory.INSTANCE
+ ));
+
+ sshd.setKeyboardInteractiveAuthenticator(new DefaultKeyboardInteractiveAuthenticator() {
+ @Override
+ public InteractiveChallenge generateChallenge(ServerSession session, String username, String lang, String subMethods) {
+ assertEquals("Mismatched user language",
+ FactoryManagerUtils.getStringProperty(
+ client,
+ org.apache.sshd.client.auth.UserAuthKeyboardInteractive.INTERACTIVE_LANGUAGE_TAG,
+ org.apache.sshd.client.auth.UserAuthKeyboardInteractive.DEFAULT_INTERACTIVE_LANGUAGE_TAG),
+ lang);
+ assertEquals("Mismatched client sub-methods",
+ FactoryManagerUtils.getStringProperty(
+ client,
+ org.apache.sshd.client.auth.UserAuthKeyboardInteractive.INTERACTIVE_SUBMETHODS,
+ org.apache.sshd.client.auth.UserAuthKeyboardInteractive.DEFAULT_INTERACTIVE_SUBMETHODS),
+ subMethods);
+
+ InteractiveChallenge challenge = super.generateChallenge(session, username, lang, subMethods);
+ assertEquals("Mismatched interaction name", getInteractionName(session), challenge.getInteractionName());
+ assertEquals("Mismatched interaction instruction", getInteractionInstruction(session), challenge.getInteractionInstruction());
+ assertEquals("Mismatched language tag", getInteractionLanguage(session), challenge.getLanguageTag());
+
+ List<PromptEntry> entries = challenge.getPrompts();
+ assertEquals("Mismatched prompts count", 1, GenericUtils.size(entries));
+
+ PromptEntry entry = entries.get(0);
+ assertEquals("Mismatched prompt", getInteractionPrompt(session), entry.getPrompt());
+ assertEquals("Mismatched echo", isInteractionPromptEchoEnabled(session), entry.isEcho());
+
+ return challenge;
+ }
+
+ @Override
+ public boolean authenticate(ServerSession session, String username, List<String> responses) throws Exception {
+ return super.authenticate(session, username, responses);
+ }
+
+ });
+ client.start();
+
+ try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
+
+ KeyPair pair = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+ assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
+ assertAuthenticationResult(UserAuthMethodFactory.KB_INTERACTIVE, authInteractive(s, getCurrentTestName(), getCurrentTestName()), true);
+
+ s.close(true);
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ @Test // see SSHD-563
+ public void testAuthMultiChallengeKeyInteractive() throws Exception {
+ final Class<?> anchor = getClass();
+ final InteractiveChallenge challenge = new InteractiveChallenge();
+ challenge.setInteractionName(getCurrentTestName());
+ challenge.setInteractionInstruction(anchor.getPackage().getName());
+ challenge.setLanguageTag(Locale.getDefault().getLanguage());
+
+ final Map<String,String> rspMap = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER) {
+ private static final long serialVersionUID = 1L; // we're not serializing it
+
+ {
+ put("class", anchor.getSimpleName());
+ put("package", anchor.getPackage().getName());
+ put("test", getCurrentTestName());
+ }
+ };
+ for (String prompt : rspMap.keySet()) {
+ challenge.addPrompt(prompt, (GenericUtils.size(challenge.getPrompts()) & 0x1) != 0);
+ }
+
+ FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
+ final AtomicInteger genCount = new AtomicInteger(0);
+ final AtomicInteger authCount = new AtomicInteger(0);
+ sshd.setKeyboardInteractiveAuthenticator(new KeyboardInteractiveAuthenticator() {
+ @Override
+ public InteractiveChallenge generateChallenge(ServerSession session, String username, String lang, String subMethods) {
+ assertEquals("Unexpected challenge call", 1, genCount.incrementAndGet());
+ return challenge;
+ }
+
+ @Override
+ public boolean authenticate(ServerSession session, String username, List<String> responses) throws Exception {
+ assertEquals("Unexpected authenticate call", 1, authCount.incrementAndGet());
+ assertEquals("Mismatched number of responses", GenericUtils.size(rspMap), GenericUtils.size(responses));
+
+ int index = 0;
+ for (Map.Entry<String,String> re : rspMap.entrySet()) {
+ String prompt = re.getKey();
+ String expected = re.getValue();
+ String actual = responses.get(index);
+ assertEquals("Mismatched response for prompt=" + prompt, expected, actual);
+ index++;
+ }
+ return true;
+ }
+ });
+ FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
+
+ try (SshClient client = setupTestClient()) {
+ final AtomicInteger interactiveCount = new AtomicInteger(0);
+ client.setUserInteraction(new UserInteraction() {
+ @Override
+ public void welcome(ClientSession session, String banner, String lang) {
+ // ignored
+ }
+
+ @Override
+ public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+ assertEquals("Unexpected multiple calls", 1, interactiveCount.incrementAndGet());
+ assertEquals("Mismatched name", challenge.getInteractionName(), name);
+ assertEquals("Mismatched instruction", challenge.getInteractionInstruction(), instruction);
+ assertEquals("Mismatched language", challenge.getLanguageTag(), lang);
+
+ List<PromptEntry> entries = challenge.getPrompts();
+ assertEquals("Mismatched prompts count", GenericUtils.size(entries), GenericUtils.length(prompt));
+
+ String[] responses = new String[prompt.length];
+ for (int index = 0; index < prompt.length; index++) {
+ PromptEntry e = entries.get(index);
+ String key = e.getPrompt();
+ assertEquals("Mismatched prompt at index=" + index, key, prompt[index]);
+ assertEquals("Mismatched echo at index=" + index, e.isEcho(), echo[index]);
+ responses[index] = ValidateUtils.checkNotNull(rspMap.get(key), "No value for prompt=%s", key);
+ }
+
+ return responses;
+ }
+
+ @Override
+ public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
+ throw new UnsupportedOperationException("Unexpected call");
+ }
+ });
+ FactoryManagerUtils.updateProperty(client, ServerFactoryManager.AUTH_METHODS, UserAuthKeyboardInteractiveFactory.NAME);
+
+ client.start();
+
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.auth().verify(11L, TimeUnit.SECONDS);
+ assertEquals("Bad generated challenge count", 1, genCount.get());
+ assertEquals("Bad authentication count", 1, authCount.get());
+ assertEquals("Bad interactive count", 1, interactiveCount.get());
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ @Test // see SSHD-196
+ public void testAuthPasswordChangeRequest() throws Exception {
+ final PasswordAuthenticator delegate = ValidateUtils.checkNotNull(sshd.getPasswordAuthenticator(), "No password authenticator");
+ final AtomicInteger attemptsCount = new AtomicInteger(0);
+ sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
+ @Override
+ public boolean authenticate(String username, String password, ServerSession session)
+ throws PasswordChangeRequiredException {
+ if (attemptsCount.incrementAndGet() == 1) {
+ throw new PasswordChangeRequiredException(attemptsCount.toString(), getCurrentTestName(), ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
+ }
+
+ return delegate.authenticate(username, password, session);
+ }
+ });
+ FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+
+ try (SshClient client = setupTestClient()) {
+ final AtomicInteger updatesCount = new AtomicInteger(0);
+ client.setUserInteraction(new UserInteraction() {
+ @Override
+ public void welcome(ClientSession session, String banner, String lang) {
+ // ignored
+ }
+
+ @Override
+ public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+ throw new UnsupportedOperationException("Unexpected call");
+ }
+
+ @Override
+ public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
+ assertEquals("Mismatched prompt", getCurrentTestName(), prompt);
+ assertEquals("Mismatched language", ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE, lang);
+ assertEquals("Unexpected repeated call", 1, updatesCount.incrementAndGet());
+ return getCurrentTestName();
+ }
+ });
+ FactoryManagerUtils.updateProperty(client, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
+
+ client.start();
+
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ s.addPasswordIdentity(getCurrentTestName());
+ s.auth().verify(11L, TimeUnit.SECONDS);
+ assertEquals("No password change request generated", 2, attemptsCount.get());
+ assertEquals("No user interaction invoked", 1, updatesCount.get());
+ } finally {
+ client.stop();
+ }
+ }
+ }
+
+ private static void assertAuthenticationResult(String message, AuthFuture future, boolean expected) throws IOException {
+ assertTrue(message + ": failed to get result on time", future.await(5L, TimeUnit.SECONDS));
+ assertEquals(message + ": mismatched authentication result", expected, future.isSuccess());
+ }
+
+ private AuthFuture authPassword(ClientSession s, String user, String pswd) throws IOException {
+ s.setUsername(user);
+ return s.getService(ClientUserAuthServiceOld.class)
+ .auth(new UserAuthPassword(s, "ssh-connection", pswd));
+ }
+
+ private AuthFuture authInteractive(ClientSession s, String user, String pswd) throws IOException {
+ s.setUsername(user);
+ return s.getService(ClientUserAuthServiceOld.class)
+ .auth(new UserAuthKeyboardInteractive(s, "ssh-connection", pswd));
+ }
+
+ private AuthFuture authPublicKey(ClientSession s, String user, KeyPair pair) throws IOException {
+ s.setUsername(user);
+ return s.getService(ClientUserAuthServiceOld.class)
+ .auth(new UserAuthPublicKey(s, "ssh-connection", pair));
+ }
+
+ public static class TestSession extends ServerSessionImpl {
+ public TestSession(ServerFactoryManager server, IoSession ioSession) throws Exception {
+ super(server, ioSession);
+ }
+
+ @Override
+ public void handleMessage(Buffer buffer) throws Exception {
+ super.handleMessage(buffer); // debug breakpoint
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
index 62f8a0c..72237c3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
@@ -105,7 +105,7 @@ public class CipherTest extends BaseTestSupport {
Assume.assumeTrue("No internal support for " + builtInCipher.getName(), builtInCipher.isSupported() && checkCipher(jschCipher.getName()));
try (SshServer sshd = setupTestServer()) {
- sshd.setCipherFactories(Arrays.<NamedFactory<org.apache.sshd.common.cipher.Cipher>>asList(builtInCipher));
+ sshd.setCipherFactories(Collections.<NamedFactory<org.apache.sshd.common.cipher.Cipher>>singletonList(builtInCipher));
sshd.start();
try {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index ef7d199..bfe983d 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -159,13 +159,13 @@ public class ServerTest extends BaseTestSupport {
AuthFuture authFuture;
do {
nbTrials++;
- assertTrue(nbTrials < 100);
+ assertTrue("Number of trials below max.", nbTrials < 100);
authFuture = s.getService(ClientUserAuthServiceOld.class)
.auth(new org.apache.sshd.deprecated.UserAuthPassword(s, "ssh-connection", "buggy"));
- assertTrue("Authentication wait failed", authFuture.await(5000));
+ assertTrue("Authentication wait failed", authFuture.await(5L, TimeUnit.SECONDS));
assertTrue("Authentication not done", authFuture.isDone());
assertFalse("Authentication unexpectedly successful", authFuture.isSuccess());
- } while (authFuture.isFailure());
+ } while (authFuture.getException() == null);
assertNotNull("Missing auth future exception", authFuture.getException());
assertTrue("Number trials (" + nbTrials + ") below min.=" + MAX_AUTH_REQUESTS, nbTrials > MAX_AUTH_REQUESTS);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
index c20029f..777ea52 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
@@ -74,7 +74,7 @@ public class GitPackCommandTest extends BaseTestSupport {
Path localDir = localRootDir.resolve(serverDir.getFileName());
Utils.deleteRecursive(localDir);
Git.cloneRepository()
- .setURI("ssh://" + getCurrentTestName() + "@localhost:" + port + "/" + serverDir.getFileName())
+ .setURI("ssh://" + getCurrentTestName() + "@" + TEST_LOCALHOST + ":" + port + "/" + serverDir.getFileName())
.setDirectory(localDir.toFile())
.call();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
index c8ea484..677ead5 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
@@ -48,7 +48,7 @@ public class GitPgmCommandTest extends BaseTestSupport {
}
@Test
- public void testGitpgm() throws Exception {
+ public void testGitPgm() throws Exception {
Path targetParent = detectTargetFolder().getParent();
Path serverDir = getTempTargetRelativeFile(getClass().getSimpleName());
[2/3] mina-sshd git commit: [SSHD-563] Provide server-side
KeyboardInteractiveAuthenticator
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/DefaultKeyboardInteractiveAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/DefaultKeyboardInteractiveAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/DefaultKeyboardInteractiveAuthenticator.java
new file mode 100644
index 0000000..71283ff
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/DefaultKeyboardInteractiveAuthenticator.java
@@ -0,0 +1,117 @@
+/*
+ * 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.auth.keyboard;
+
+import java.util.List;
+
+import org.apache.sshd.common.FactoryManagerUtils;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.auth.password.PasswordAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * Provides a default implementation for {@link KeyboardInteractiveAuthenticator}
+ * where it prompts for the password.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultKeyboardInteractiveAuthenticator
+ extends AbstractLoggingBean
+ implements KeyboardInteractiveAuthenticator {
+ // configuration parameters on the FactoryManager to configure the message values
+ public static final String KB_INTERACTIVE_NAME_PROP = "kb-server-interactive-name";
+ public static final String DEFAULT_KB_INTERACTIVE_NAME = "Password authentication";
+ public static final String KB_INTERACTIVE_INSTRUCTION_PROP = "kb-server-interactive-instruction";
+ public static final String DEFAULT_KB_INTERACTIVE_INSTRUCTION = "";
+ public static final String KB_INTERACTIVE_LANG_PROP = "kb-server-interactive-language";
+ public static final String DEFAULT_KB_INTERACTIVE_LANG = "en-US";
+ public static final String KB_INTERACTIVE_PROMPT_PROP = "kb-server-interactive-prompt";
+ public static final String DEFAULT_KB_INTERACTIVE_PROMPT = "Password: ";
+ public static final String KB_INTERACTIVE_ECHO_PROMPT_PROP = "kb-server-interactive-echo-prompt";
+ public static final boolean DEFAULT_KB_INTERACTIVE_ECHO_PROMPT = false;
+
+ public static final DefaultKeyboardInteractiveAuthenticator INSTANCE = new DefaultKeyboardInteractiveAuthenticator();
+
+ public DefaultKeyboardInteractiveAuthenticator() {
+ super();
+ }
+
+ @Override
+ public InteractiveChallenge generateChallenge(ServerSession session, String username, String lang, String subMethods) {
+ ServerFactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
+ PasswordAuthenticator auth = manager.getPasswordAuthenticator();
+ if (auth == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("generateChallenge({}) no password authenticator", session);
+ }
+ return null;
+ }
+
+ InteractiveChallenge challenge = new InteractiveChallenge();
+ challenge.setInteractionName(getInteractionName(session));
+ challenge.setInteractionInstruction(getInteractionInstruction(session));
+ challenge.setLanguageTag(getInteractionLanguage(session));
+ challenge.addPrompt(getInteractionPrompt(session), isInteractionPromptEchoEnabled(session));
+ return challenge;
+ }
+
+ @Override
+ public boolean authenticate(ServerSession session, String username, List<String> responses) throws Exception {
+ ServerFactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
+ PasswordAuthenticator auth = manager.getPasswordAuthenticator();
+ if (auth == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("authenticate({}) no password authenticator", session);
+ }
+ return false;
+ }
+
+ int numResp = GenericUtils.size(responses);
+ if (numResp != 1) {
+ throw new SshException("Mismatched number of responses");
+ }
+
+ return auth.authenticate(username, responses.get(0), session);
+ }
+
+ protected String getInteractionName(ServerSession session) {
+ return FactoryManagerUtils.getStringProperty(session, KB_INTERACTIVE_NAME_PROP, DEFAULT_KB_INTERACTIVE_NAME);
+ }
+
+ protected String getInteractionInstruction(ServerSession session) {
+ return FactoryManagerUtils.getStringProperty(session, KB_INTERACTIVE_INSTRUCTION_PROP, DEFAULT_KB_INTERACTIVE_INSTRUCTION);
+ }
+
+ protected String getInteractionLanguage(ServerSession session) {
+ return FactoryManagerUtils.getStringProperty(session, KB_INTERACTIVE_LANG_PROP, DEFAULT_KB_INTERACTIVE_LANG);
+ }
+
+ protected String getInteractionPrompt(ServerSession session) {
+ return FactoryManagerUtils.getStringProperty(session, KB_INTERACTIVE_PROMPT_PROP, DEFAULT_KB_INTERACTIVE_PROMPT);
+ }
+
+ protected boolean isInteractionPromptEchoEnabled(ServerSession session) {
+ return FactoryManagerUtils.getBooleanProperty(session, KB_INTERACTIVE_ECHO_PROMPT_PROP, DEFAULT_KB_INTERACTIVE_ECHO_PROMPT);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java
new file mode 100644
index 0000000..1bbe2fc
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java
@@ -0,0 +1,130 @@
+/*
+ * 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.auth.keyboard;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Represents a server "challenge" as per
+ * <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC-4256</A>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class InteractiveChallenge implements Cloneable {
+ private String interactionName;
+ private String interactionInstruction;
+ private String languageTag;
+ private List<PromptEntry> prompts = new ArrayList<>();
+
+ public InteractiveChallenge() {
+ super();
+ }
+
+ public String getInteractionName() {
+ return interactionName;
+ }
+
+ public void setInteractionName(String interactionName) {
+ this.interactionName = interactionName;
+ }
+
+ public String getInteractionInstruction() {
+ return interactionInstruction;
+ }
+
+ public void setInteractionInstruction(String interactionInstruction) {
+ this.interactionInstruction = interactionInstruction;
+ }
+
+ public String getLanguageTag() {
+ return languageTag;
+ }
+
+ public void setLanguageTag(String languageTag) {
+ this.languageTag = languageTag;
+ }
+
+ public void addPrompt(String prompt, boolean echo) {
+ addPrompt(new PromptEntry(prompt, echo));
+ }
+
+ public void addPrompt(PromptEntry entry) {
+ this.prompts.add(ValidateUtils.checkNotNull(entry, "No entry"));
+ }
+
+ public List<PromptEntry> getPrompts() {
+ return prompts;
+ }
+
+ // NOTE: prompts are COPIED to the local one
+ public void setPrompts(Collection<? extends PromptEntry> prompts) {
+ clearPrompts();
+
+ if (GenericUtils.size(prompts) > 0) {
+ this.prompts.addAll(prompts);
+ }
+ }
+
+ public void clearPrompts() {
+ this.prompts.clear();
+ }
+
+ public <B extends Buffer> B append(B buffer) {
+ buffer.putString(getInteractionName());
+ buffer.putString(getInteractionInstruction());
+ buffer.putString(getLanguageTag());
+
+ List<PromptEntry> entries = getPrompts();
+ int numEntries = GenericUtils.size(entries);
+ buffer.putInt(numEntries);
+
+ for (int index = 0; index < numEntries; index++) {
+ PromptEntry e = entries.get(index);
+ e.append(buffer);
+ }
+
+ return buffer;
+ }
+
+ @Override
+ public InteractiveChallenge clone() {
+ try {
+ InteractiveChallenge other = getClass().cast(super.clone());
+ other.prompts = new ArrayList<>();
+ for (PromptEntry entry : getPrompts()) {
+ other.addPrompt(entry.clone());
+ }
+ return other;
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Failed to clone " + toString() + ": " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getInteractionName() + "[" + getInteractionInstruction() + "](" + getLanguageTag() + "): " + getPrompts();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/KeyboardInteractiveAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/KeyboardInteractiveAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/KeyboardInteractiveAuthenticator.java
new file mode 100644
index 0000000..80c94f1
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/KeyboardInteractiveAuthenticator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.auth.keyboard;
+
+import java.util.List;
+
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * Provides pluggable authentication using the "keyboard-interactive"
+ * method as specified by <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC-4256</A>?
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyboardInteractiveAuthenticator {
+ /**
+ * Generates the interactive "challenge" to send to the client
+ *
+ * @param session The {@link ServerSession} through which the request was received
+ * @param username The username
+ * @param lang The language tag
+ * @param subMethods Sub-methods hints sent by the client
+ * @return The {@link InteractiveChallenge} - if {@code null} then authentication
+ * attempt via "keyboard-interactive" method is rejected
+ */
+ InteractiveChallenge generateChallenge(ServerSession session, String username, String lang, String subMethods);
+
+ /**
+ * Called to authenticate the response to the challenge(s) sent previously
+ *
+ * @param session The {@link ServerSession} through which the response was received
+ * @param username The username
+ * @param responses The received responses - <B>Note:</B> it is up to the authenticator
+ * to make sure that the number of responses matches the number of prompts sent in
+ * the initial challenge. The <U>order</U> of the responses matches the order of the
+ * prompts sent to the client
+ * @return {@code true} if responses have been validated
+ * @throws Exception if bad responses and server should terminate the connection
+ */
+ boolean authenticate(ServerSession session, String username, List<String> responses) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/PromptEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/PromptEntry.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/PromptEntry.java
new file mode 100644
index 0000000..f8dab48
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/PromptEntry.java
@@ -0,0 +1,101 @@
+/*
+ * 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.auth.keyboard;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PromptEntry implements Serializable, Cloneable {
+ private static final long serialVersionUID = 8206049800536373640L;
+
+ private String prompt;
+ private boolean echo;
+
+ public PromptEntry() {
+ super();
+ }
+
+ public PromptEntry(String prompt, boolean echo) {
+ this.prompt = prompt;
+ this.echo = echo;
+ }
+
+ public String getPrompt() {
+ return prompt;
+ }
+
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ public boolean isEcho() {
+ return echo;
+ }
+
+ public void setEcho(boolean echo) {
+ this.echo = echo;
+ }
+
+ public <B extends Buffer> B append(B buffer) {
+ buffer.putString(getPrompt());
+ buffer.putBoolean(isEcho());
+ return buffer;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(getPrompt()) + (isEcho() ? 1 : 0);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ PromptEntry other = (PromptEntry) obj;
+ return Objects.equals(getPrompt(), other.getPrompt()) && (isEcho() == other.isEcho());
+ }
+
+ @Override
+ public PromptEntry clone() {
+ try {
+ return getClass().cast(super.clone());
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Failed to clone " + toString() + ": " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getPrompt() + "(echo=" + isEcho() + ")";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordChangeRequiredException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordChangeRequiredException.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordChangeRequiredException.java
index e12e0ff..a0fc27d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordChangeRequiredException.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordChangeRequiredException.java
@@ -41,19 +41,19 @@ public class PasswordChangeRequiredException extends RuntimeException {
public PasswordChangeRequiredException(String message, String prompt, String lang, Throwable cause) {
super(message, cause);
- this.prompt = (prompt == null) ? "" : prompt;
- this.lang = (lang == null) ? "" : lang;
+ this.prompt = prompt;
+ this.lang = lang;
}
/**
- * @return The prompt to show to the user - may be empty
+ * @return The prompt to show to the user - may be {@code null}/empty
*/
public final String getPrompt() {
return prompt;
}
/**
- * @return The language code for the prompt - may be empty
+ * @return The language code for the prompt - may be {@code null}/empty
*/
public final String getLanguage() {
return lang;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
new file mode 100644
index 0000000..61667b1
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.auth.pubkey;
+
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * Caches the result per session
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, SessionListener {
+
+ protected final PublickeyAuthenticator authenticator;
+ protected final Map<ServerSession, Map<PublicKey, Boolean>> cache = new ConcurrentHashMap<ServerSession, Map<PublicKey, Boolean>>();
+
+ public CachingPublicKeyAuthenticator(PublickeyAuthenticator authenticator) {
+ this.authenticator = authenticator;
+ }
+
+ @Override
+ public boolean authenticate(String username, PublicKey key, ServerSession session) {
+ Map<PublicKey, Boolean> map = cache.get(session);
+ if (map == null) {
+ map = new ConcurrentHashMap<>();
+ cache.put(session, map);
+ session.addSessionListener(this);
+ }
+
+ Boolean result = map.get(key);
+ if (result == null) {
+ result = authenticator.authenticate(username, key, session);
+ map.put(key, result);
+ }
+
+ return result;
+ }
+
+ @Override
+ public void sessionCreated(Session session) {
+ // ignored
+ }
+
+ @Override
+ public void sessionEvent(Session session, Event event) {
+ // ignored
+ }
+
+ @Override
+ public void sessionClosed(Session session) {
+ cache.remove(session);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
index fc5bea5..d0b1a01 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
@@ -27,6 +27,7 @@ import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Collection;
+import org.apache.sshd.common.auth.UsernameHolder;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.io.IoUtils;
@@ -40,7 +41,7 @@ import org.apache.sshd.server.session.ServerSession;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class DefaultAuthorizedKeysAuthenticator extends AuthorizedKeysAuthenticator {
+public class DefaultAuthorizedKeysAuthenticator extends AuthorizedKeysAuthenticator implements UsernameHolder {
/**
* The default instance that enforces the same permissions regime as {@code OpenSSH}
@@ -81,6 +82,7 @@ public class DefaultAuthorizedKeysAuthenticator extends AuthorizedKeysAuthentica
this.strict = strict;
}
+ @Override
public final String getUsername() {
return user;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/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 db2c7ec..6c7a096 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
@@ -182,14 +182,14 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
}
protected void handleAuthenticationInProgress(int cmd, Buffer buffer) throws Exception {
- String username = (currentAuth == null) ? null : currentAuth.getUserName();
+ String username = (currentAuth == null) ? null : currentAuth.getUsername();
if (log.isDebugEnabled()) {
log.debug("handleAuthenticationInProgress({}@{})", username, session);
}
}
protected void handleAuthenticationSuccess(int cmd, Buffer buffer) throws Exception {
- String username = ValidateUtils.checkNotNull(currentAuth, "No current auth").getUserName();
+ String username = ValidateUtils.checkNotNull(currentAuth, "No current auth").getUsername();
if (log.isDebugEnabled()) {
log.debug("handleAuthenticationSuccess({}@{})", username, session);
}
@@ -264,7 +264,7 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
}
protected void handleAuthenticationFailure(int cmd, Buffer buffer) throws Exception {
- String username = (currentAuth == null) ? null : currentAuth.getUserName();
+ String username = (currentAuth == null) ? null : currentAuth.getUsername();
if (log.isDebugEnabled()) {
log.debug("handleAuthenticationFailure({}@{})", username, session);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
deleted file mode 100644
index 4e00e85..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.auth.UserInteraction;
-import org.apache.sshd.client.future.AuthFuture;
-import org.apache.sshd.client.session.ClientConnectionServiceFactory;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.FactoryManagerUtils;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.auth.UserAuthMethodFactory;
-import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.deprecated.ClientUserAuthServiceOld;
-import org.apache.sshd.deprecated.UserAuthKeyboardInteractive;
-import org.apache.sshd.deprecated.UserAuthPassword;
-import org.apache.sshd.deprecated.UserAuthPublicKey;
-import org.apache.sshd.server.ServerFactoryManager;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.auth.UserAuthPasswordFactory;
-import org.apache.sshd.server.auth.password.PasswordAuthenticator;
-import org.apache.sshd.server.auth.password.PasswordChangeRequiredException;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.server.session.ServerSessionImpl;
-import org.apache.sshd.server.session.SessionFactory;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AuthenticationTest extends BaseTestSupport {
-
- private static final String WELCOME = "Welcome to SSHD";
-
- private SshServer sshd;
- private int port;
-
- public AuthenticationTest() {
- super();
- }
-
- @Before
- public void setUp() throws Exception {
- sshd = setupTestServer();
- FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.WELCOME_BANNER, WELCOME);
- FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, "publickey,password publickey,keyboard-interactive");
- sshd.setSessionFactory(new SessionFactory(sshd) {
- @Override
- protected ServerSessionImpl doCreateSession(IoSession ioSession) throws Exception {
- return new TestSession(getServer(), ioSession);
- }
- });
- sshd.start();
- port = sshd.getPort();
- }
-
- @After
- public void tearDown() throws Exception {
- if (sshd != null) {
- sshd.stop(true);
- }
- }
-
- @Test
- public void testWrongPassword() throws Exception {
- try (SshClient client = setupTestClient()) {
- client.start();
- try (ClientSession s = client.connect("user", TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.addPasswordIdentity("bad password");
- assertAuthenticationResult(getCurrentTestName(), s.auth(), false);
- }
- }
- }
-
- @Test
- public void testChangeUser() throws Exception {
- try (SshClient client = setupTestClient()) {
- client.setServiceFactories(Arrays.asList(
- new ClientUserAuthServiceOld.Factory(),
- ClientConnectionServiceFactory.INSTANCE
- ));
-
- client.start();
-
- try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
-
- for (String username : new String[]{"user1", "user2"}) {
- assertAuthenticationResult(username, authPassword(s, username, "the-password"), false);
- }
-
- // Note that WAIT_AUTH flag should be false, but since the internal
- // authentication future is not updated, it's still returned
- assertEquals("Mismatched client session close mask", ClientSession.CLOSED | ClientSession.WAIT_AUTH, s.waitFor(ClientSession.CLOSED, 1000));
- } finally {
- client.stop();
- }
- }
- }
-
- @Test
- public void testChangePassword() throws Exception {
- final PasswordAuthenticator delegate = sshd.getPasswordAuthenticator();
- final AtomicInteger attemptsCount = new AtomicInteger(0);
- sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
- @Override
- public boolean authenticate(String username, String password, ServerSession session)
- throws PasswordChangeRequiredException {
- if (attemptsCount.incrementAndGet() == 1) {
- throw new PasswordChangeRequiredException(attemptsCount.toString(), getCurrentTestName(), ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
- }
-
- return delegate.authenticate(username, password, session);
- }
- });
-
- final AtomicInteger changesCount = new AtomicInteger(0);
- sshd.setUserAuthFactories(Collections.<NamedFactory<org.apache.sshd.server.auth.UserAuth>>singletonList(
- new org.apache.sshd.server.auth.UserAuthPasswordFactory() {
- @Override
- public org.apache.sshd.server.auth.UserAuth create() {
- return new org.apache.sshd.server.auth.UserAuthPassword() {
- @Override
- protected Boolean handleClientPasswordChangeRequest(
- Buffer buffer, ServerSession session, String username, String oldPassword, String newPassword)
- throws Exception {
- if (changesCount.incrementAndGet() == 1) {
- assertNotEquals("Non-different passwords", oldPassword, newPassword);
- return checkPassword(buffer, session, username, newPassword);
- } else {
- return super.handleClientPasswordChangeRequest(buffer, session, username, oldPassword, newPassword);
- }
- }
- };
- }
- }
- ));
- FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
-
- try (SshClient client = setupTestClient()) {
- final AtomicInteger updatesCount = new AtomicInteger(0);
- client.setUserInteraction(new UserInteraction() {
- @Override
- public void welcome(ClientSession session, String banner, String lang) {
- // ignored
- }
-
- @Override
- public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
- throw new UnsupportedOperationException("Unexpected call");
- }
-
- @Override
- public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
- assertEquals("Mismatched prompt", getCurrentTestName(), prompt);
- assertEquals("Mismatched language", ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE, lang);
- assertEquals("Unexpected repeated call", 1, updatesCount.incrementAndGet());
- return getCurrentTestName();
- }
- });
-
- final AtomicInteger sentCount = new AtomicInteger(0);
- client.setUserAuthFactories(Collections.<NamedFactory<org.apache.sshd.client.auth.UserAuth>>singletonList(
- new org.apache.sshd.client.auth.UserAuthPasswordFactory() {
- @Override
- public org.apache.sshd.client.auth.UserAuth create() {
- return new org.apache.sshd.client.auth.UserAuthPassword() {
- @Override
- protected void sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
- int count = sentCount.incrementAndGet();
- // 1st one is the original one (which is denied by the server)
- // 2nd one is the updated one retrieved from the user interaction
- if (count == 2) {
- super.sendPassword(buffer, session, getClass().getName(), newPassword);
- } else {
- super.sendPassword(buffer, session, oldPassword, newPassword);
- }
- }
- };
- }
- }));
- FactoryManagerUtils.updateProperty(client, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
-
- client.start();
-
- try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.addPasswordIdentity(getCurrentTestName());
- s.auth().verify(11L, TimeUnit.SECONDS);
- assertEquals("No password change request generated", 2, attemptsCount.get());
- assertEquals("No password change handled", 1, changesCount.get());
- assertEquals("No user interaction invoked", 1, updatesCount.get());
- } finally {
- client.stop();
- }
- }
- }
-
- @Test
- public void testAuthPasswordOnly() throws Exception {
- try (SshClient client = setupTestClient()) {
- client.setServiceFactories(Arrays.asList(
- new ClientUserAuthServiceOld.Factory(),
- ClientConnectionServiceFactory.INSTANCE
- ));
- client.start();
-
- try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
- assertAuthenticationResult(getCurrentTestName(), authPassword(s, getCurrentTestName(), getCurrentTestName()), false);
- s.close(true);
- } finally {
- client.stop();
- }
- }
- }
-
- @Test
- public void testAuthKeyPassword() throws Exception {
- try (SshClient client = setupTestClient()) {
- client.setServiceFactories(Arrays.asList(
- new ClientUserAuthServiceOld.Factory(),
- ClientConnectionServiceFactory.INSTANCE
- ));
- client.start();
-
- try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
-
- KeyPair pair = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
- assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
- assertAuthenticationResult(UserAuthMethodFactory.PASSWORD, authPassword(s, getCurrentTestName(), getCurrentTestName()), true);
- s.close(true);
- } finally {
- client.stop();
- }
- }
- }
-
- @Test
- public void testAuthKeyInteractive() throws Exception {
- try (SshClient client = setupTestClient()) {
- client.setServiceFactories(Arrays.asList(
- new ClientUserAuthServiceOld.Factory(),
- ClientConnectionServiceFactory.INSTANCE
- ));
- client.start();
-
- try (ClientSession s = client.connect(null, TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
-
- KeyPair pair = createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
- assertAuthenticationResult(UserAuthMethodFactory.PUBLIC_KEY, authPublicKey(s, getCurrentTestName(), pair), false);
- assertAuthenticationResult(UserAuthMethodFactory.KB_INTERACTIVE, authInteractive(s, getCurrentTestName(), getCurrentTestName()), true);
-
- s.close(true);
- } finally {
- client.stop();
- }
- }
- }
-
- @Test
- public void testAuthPasswordChangeRequest() throws Exception {
- final PasswordAuthenticator delegate = ValidateUtils.checkNotNull(sshd.getPasswordAuthenticator(), "No password authenticator");
- final AtomicInteger attemptsCount = new AtomicInteger(0);
- sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
- @Override
- public boolean authenticate(String username, String password, ServerSession session)
- throws PasswordChangeRequiredException {
- if (attemptsCount.incrementAndGet() == 1) {
- throw new PasswordChangeRequiredException(attemptsCount.toString(), getCurrentTestName(), ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE);
- }
-
- return delegate.authenticate(username, password, session);
- }
- });
- FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
-
- try (SshClient client = setupTestClient()) {
- final AtomicInteger updatesCount = new AtomicInteger(0);
- client.setUserInteraction(new UserInteraction() {
- @Override
- public void welcome(ClientSession session, String banner, String lang) {
- // ignored
- }
-
- @Override
- public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
- throw new UnsupportedOperationException("Unexpected call");
- }
-
- @Override
- public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
- assertEquals("Mismatched prompt", getCurrentTestName(), prompt);
- assertEquals("Mismatched language", ServerFactoryManager.DEFAULT_WELCOME_BANNER_LANGUAGE, lang);
- assertEquals("Unexpected repeated call", 1, updatesCount.incrementAndGet());
- return getCurrentTestName();
- }
- });
- FactoryManagerUtils.updateProperty(client, ServerFactoryManager.AUTH_METHODS, UserAuthPasswordFactory.NAME);
-
- client.start();
-
- try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- s.addPasswordIdentity(getCurrentTestName());
- s.auth().verify(11L, TimeUnit.SECONDS);
- assertEquals("No password change request generated", 2, attemptsCount.get());
- assertEquals("No user interaction invoked", 1, updatesCount.get());
- } finally {
- client.stop();
- }
- }
- }
-
- private static void assertAuthenticationResult(String message, AuthFuture future, boolean expected) throws IOException {
- assertTrue(message + ": failed to get result on time", future.await(5L, TimeUnit.SECONDS));
- assertEquals(message + ": mismatched authentication result", expected, future.isSuccess());
- }
-
- private AuthFuture authPassword(ClientSession s, String user, String pswd) throws IOException {
- s.setUsername(user);
- return s.getService(ClientUserAuthServiceOld.class)
- .auth(new UserAuthPassword(s, "ssh-connection", pswd));
- }
-
- private AuthFuture authInteractive(ClientSession s, String user, String pswd) throws IOException {
- s.setUsername(user);
- return s.getService(ClientUserAuthServiceOld.class)
- .auth(new UserAuthKeyboardInteractive(s, "ssh-connection", pswd));
- }
-
- private AuthFuture authPublicKey(ClientSession s, String user, KeyPair pair) throws IOException {
- s.setUsername(user);
- return s.getService(ClientUserAuthServiceOld.class)
- .auth(new UserAuthPublicKey(s, "ssh-connection", pair));
- }
-
- public static class TestSession extends ServerSessionImpl {
- public TestSession(ServerFactoryManager server, IoSession ioSession) throws Exception {
- super(server, ioSession);
- }
-
- @Override
- public void handleMessage(Buffer buffer) throws Exception {
- super.handleMessage(buffer); // debug breakpoint
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java b/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
index ae882e2..308d23b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
@@ -87,6 +87,10 @@ public class PortForwardingTest extends BaseTestSupport {
private IoAcceptor acceptor;
private SshClient client;
+ public PortForwardingTest() {
+ super();
+ }
+
@Before
public void setUp() throws Exception {
sshd = setupTestServer();
@@ -211,9 +215,9 @@ public class PortForwardingTest extends BaseTestSupport {
int n = input.read(buf);
String res = new String(buf, 0, n, StandardCharsets.UTF_8);
assertEquals("Mismatched data", expected, res);
+ } finally {
+ session.delPortForwardingR(forwardedPort);
}
-
- session.delPortForwardingR(forwardedPort);
} finally {
session.disconnect();
}
@@ -248,8 +252,9 @@ public class PortForwardingTest extends BaseTestSupport {
int n = input.read(buf);
String res = new String(buf, 0, n, StandardCharsets.UTF_8);
assertEquals("Mismatched data", expected, res);
+ } finally {
+ session.delPortForwardingR(TEST_LOCALHOST, forwardedPort);
}
- session.delPortForwardingR(TEST_LOCALHOST, forwardedPort);
} finally {
session.disconnect();
}
@@ -277,10 +282,9 @@ public class PortForwardingTest extends BaseTestSupport {
int n = input.read(buf);
String res = new String(buf, 0, n);
assertEquals("Mismatched data", expected, res);
+ } finally {
+ session.stopRemotePortForwarding(remote);
}
-
- session.stopRemotePortForwarding(remote);
- session.close(false).await();
}
}
@@ -309,10 +313,9 @@ public class PortForwardingTest extends BaseTestSupport {
String res = new String(buf, 0, n);
assertEquals("Mismatched data at iteration #" + i, expected, res);
}
+ } finally {
+ session.stopRemotePortForwarding(remote);
}
-
- session.stopRemotePortForwarding(remote);
- session.close(false).await();
}
}
@@ -339,9 +342,9 @@ public class PortForwardingTest extends BaseTestSupport {
int n = input.read(buf);
String res = new String(buf, 0, n);
assertEquals("Mismatched data", expected, res);
+ } finally {
+ session.delPortForwardingL(forwardedPort);
}
-
- session.delPortForwardingL(forwardedPort);
} finally {
session.disconnect();
}
@@ -370,10 +373,9 @@ public class PortForwardingTest extends BaseTestSupport {
int n = input.read(buf);
String res = new String(buf, 0, n);
assertEquals("Mismatched data", expected, res);
+ } finally {
+ session.stopLocalPortForwarding(bound);
}
-
- session.stopLocalPortForwarding(bound);
- session.close(false).await();
}
}
@@ -388,21 +390,19 @@ public class PortForwardingTest extends BaseTestSupport {
SshdSocketAddress bound2 = session.startLocalPortForwarding(local, remote);
session.stopLocalPortForwarding(bound2);
-
- session.close(false).await();
}
}
@Test
public void testLocalForwardingNativeBigPayload() throws Exception {
try (ClientSession session = createNativeSession()) {
- SshdSocketAddress local = new SshdSocketAddress("", 0);
- SshdSocketAddress remote = new SshdSocketAddress(TEST_LOCALHOST, echoPort);
- SshdSocketAddress bound = session.startLocalPortForwarding(local, remote);
-
String expected = getCurrentTestName();
byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
byte[] buf = new byte[bytes.length + Long.SIZE];
+
+ SshdSocketAddress local = new SshdSocketAddress("", 0);
+ SshdSocketAddress remote = new SshdSocketAddress(TEST_LOCALHOST, echoPort);
+ SshdSocketAddress bound = session.startLocalPortForwarding(local, remote);
try (Socket s = new Socket(bound.getHostName(), bound.getPort());
OutputStream output = s.getOutputStream();
InputStream input = s.getInputStream()) {
@@ -417,10 +417,9 @@ public class PortForwardingTest extends BaseTestSupport {
String res = new String(buf, 0, n);
assertEquals("Mismatched data at iteration #" + i, expected, res);
}
+ } finally {
+ session.stopLocalPortForwarding(bound);
}
-
- session.stopLocalPortForwarding(bound);
- session.close(false).await();
}
}
@@ -448,12 +447,10 @@ public class PortForwardingTest extends BaseTestSupport {
}
channel.close(false);
}
-
- session.close(false).await();
}
}
- @Test(timeout = 30000)
+ @Test(timeout = 45000)
public void testRemoteForwardingWithDisconnect() throws Exception {
Session session = createSession();
try {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java b/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
index 1e789f3..0ec059c 100644
--- a/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
@@ -99,30 +99,32 @@ public class ProxyTest extends BaseTestSupport {
@Test
public void testSocksProxy() throws Exception {
try (ClientSession session = createNativeSession()) {
- SshdSocketAddress dynamic = session.startDynamicPortForwarding(new SshdSocketAddress(TEST_LOCALHOST, 0));
-
String expected = getCurrentTestName();
byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
byte[] buf = new byte[bytes.length + Long.SIZE];
- for (int i = 0; i < 10; i++) {
- try (Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(TEST_LOCALHOST, dynamic.getPort())))) {
- s.connect(new InetSocketAddress(TEST_LOCALHOST, echoPort));
- s.setSoTimeout((int) TimeUnit.SECONDS.toMillis(10L));
+ SshdSocketAddress dynamic = session.startDynamicPortForwarding(new SshdSocketAddress(TEST_LOCALHOST, 0));
+
+ try {
+ for (int i = 0; i < 10; i++) {
+ try (Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(TEST_LOCALHOST, dynamic.getPort())))) {
+ s.connect(new InetSocketAddress(TEST_LOCALHOST, echoPort));
+ s.setSoTimeout((int) TimeUnit.SECONDS.toMillis(10L));
- try (OutputStream sockOut = s.getOutputStream();
- InputStream sockIn = s.getInputStream()) {
+ try (OutputStream sockOut = s.getOutputStream();
+ InputStream sockIn = s.getInputStream()) {
- sockOut.write(bytes);
- sockOut.flush();
+ sockOut.write(bytes);
+ sockOut.flush();
- int l = sockIn.read(buf);
- assertEquals("Mismatched data at iteration " + i, expected, new String(buf, 0, l));
+ int l = sockIn.read(buf);
+ assertEquals("Mismatched data at iteration " + i, expected, new String(buf, 0, l));
+ }
}
}
+ } finally {
+ session.stopDynamicPortForwarding(dynamic);
}
- session.stopDynamicPortForwarding(dynamic);
-
try {
try (Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(TEST_LOCALHOST, dynamic.getPort())))) {
s.connect(new InetSocketAddress(TEST_LOCALHOST, echoPort));
@@ -133,8 +135,6 @@ public class ProxyTest extends BaseTestSupport {
} catch (IOException e) {
// expected
}
-
- session.close(false).await();
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java b/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
index e87d425..3db63b8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
@@ -33,8 +33,8 @@ import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.server.ServerFactoryManager;
import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.auth.CachingPublicKeyAuthenticator;
import org.apache.sshd.server.auth.UserAuthPublicKeyFactory;
+import org.apache.sshd.server.auth.pubkey.CachingPublicKeyAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
@@ -94,8 +94,9 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
@SuppressWarnings("synthetic-access")
@Override
public boolean authenticate(String username, PublicKey key, ServerSession session) {
- count.putIfAbsent(KeyUtils.getFingerPrint(key), new AtomicInteger());
- count.get(KeyUtils.getFingerPrint(key)).incrementAndGet();
+ String fp = KeyUtils.getFingerPrint(key);
+ count.putIfAbsent(fp, new AtomicInteger());
+ count.get(fp).incrementAndGet();
return key.equals(pairRsa.getPublic());
}
});
@@ -109,19 +110,21 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
session.addPublicKeyIdentity(pairRsa);
session.auth().verify(5L, TimeUnit.SECONDS);
- assertEquals(2, count.size());
- assertTrue(count.containsKey(KeyUtils.getFingerPrint(pairRsaBad.getPublic())));
- assertTrue(count.containsKey(KeyUtils.getFingerPrint(pairRsa.getPublic())));
- assertEquals(1, count.get(KeyUtils.getFingerPrint(pairRsaBad.getPublic())).get());
- assertEquals(1, count.get(KeyUtils.getFingerPrint(pairRsa.getPublic())).get());
- client.close(false).await();
+ assertEquals("Mismatched authentication invocations count", 2, count.size());
+
+ String fpBad = KeyUtils.getFingerPrint(pairRsaBad.getPublic());
+ String fpGood = KeyUtils.getFingerPrint(pairRsa.getPublic());
+ assertTrue("Missing bad public key", count.containsKey(fpBad));
+ assertTrue("Missing good public key", count.containsKey(fpGood));
+ assertEquals("Mismatched bad key authentication attempts", 1, count.get(fpBad).get());
+ assertEquals("Mismatched good key authentication attempts", 1, count.get(fpGood).get());
} finally {
client.stop();
}
}
- Thread.sleep(100);
- assertTrue(auth.getCache().isEmpty());
+ Thread.sleep(100L);
+ assertTrue("Cache not empty", auth.getCache().isEmpty());
}
@Test
@@ -131,8 +134,9 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
@SuppressWarnings("synthetic-access")
@Override
public boolean authenticate(String username, PublicKey key, ServerSession session) {
- count.putIfAbsent(KeyUtils.getFingerPrint(key), new AtomicInteger());
- count.get(KeyUtils.getFingerPrint(key)).incrementAndGet();
+ String fp = KeyUtils.getFingerPrint(key);
+ count.putIfAbsent(fp, new AtomicInteger());
+ count.get(fp).incrementAndGet();
return key.equals(pairRsa.getPublic());
}
};
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
index 451eb31..f861ca1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
@@ -96,6 +96,7 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.io.NoCloseOutputStream;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.channel.ChannelSessionFactory;
@@ -503,7 +504,7 @@ public class ClientTest extends BaseTestSupport {
// That's ok, the channel is being closed by the other side
}
assertEquals(ClientChannel.CLOSED, channel.waitFor(ClientChannel.CLOSED, 0) & ClientChannel.CLOSED);
- session.close(false).await();
+ assertTrue("Failed to close session on time", session.close(false).await(7L, TimeUnit.SECONDS));
} finally {
client.stop();
}
@@ -614,7 +615,7 @@ public class ClientTest extends BaseTestSupport {
channel.setOut(out);
channel.setErr(err);
channel.open().verify(5L, TimeUnit.SECONDS);
- channel.close(false).await();
+ assertTrue("Failed to close channel on time", channel.close(false).await(7L, TimeUnit.SECONDS));
} finally {
client.stop();
}
@@ -938,7 +939,7 @@ public class ClientTest extends BaseTestSupport {
}
@Test // see SSHD-504
- public void testKeyboardInteractivePasswordPromptLocationIndependence() throws Exception {
+ public void testDefaultKeyboardInteractivePasswordPromptLocationIndependence() throws Exception {
final Collection<String> mismatchedPrompts = new LinkedList<String>();
client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthKeyboardInteractiveFactory() {
@Override
@@ -991,29 +992,21 @@ public class ClientTest extends BaseTestSupport {
}
}
));
- sshd.setUserAuthFactories(Arrays.<NamedFactory<org.apache.sshd.server.auth.UserAuth>>asList(
- new org.apache.sshd.server.auth.UserAuthKeyboardInteractiveFactory() {
- private int xformerIndex;
+ sshd.setKeyboardInteractiveAuthenticator(new DefaultKeyboardInteractiveAuthenticator() {
+ private int xformerIndex;
- @Override
- public org.apache.sshd.server.auth.UserAuth create() {
- return new org.apache.sshd.server.auth.UserAuthKeyboardInteractive() {
-
- @SuppressWarnings("synthetic-access")
- @Override
- protected String getInteractionPrompt() {
- String original = super.getInteractionPrompt();
- if (xformerIndex < xformers.size()) {
- Transformer<String, String> x = xformers.get(xformerIndex);
- xformerIndex++;
- return x.transform(original);
- } else {
- return original;
- }
- }
- };
- }
- }));
+ @Override
+ protected String getInteractionPrompt(ServerSession session) {
+ String original = super.getInteractionPrompt(session);
+ if (xformerIndex < xformers.size()) {
+ Transformer<String, String> x = xformers.get(xformerIndex);
+ xformerIndex++;
+ return x.transform(original);
+ } else {
+ return original;
+ }
+ }
+ });
try {
for (int index = 0; index < xformers.size(); index++) {
@@ -1042,15 +1035,14 @@ public class ClientTest extends BaseTestSupport {
}
@Test
- public void testKeyboardInteractiveWithFailures() throws Exception {
- final AtomicInteger count = new AtomicInteger();
- final int MAX_PROMPTS = 3;
- FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
-
+ public void testDefaultKeyboardInteractiveWithFailures() throws Exception {
client.setUserAuthFactories(Collections.<NamedFactory<UserAuth>>singletonList(UserAuthKeyboardInteractiveFactory.INSTANCE));
+ final AtomicInteger count = new AtomicInteger();
final AtomicReference<ClientSession> interactionSessionHolder = new AtomicReference<>(null);
client.setUserInteraction(new UserInteraction() {
+ private final String[] BAD_RESPONSE = { "bad" };
+
@Override
public void welcome(ClientSession session, String banner, String lang) {
validateSession("welcome", session);
@@ -1060,7 +1052,7 @@ public class ClientTest extends BaseTestSupport {
public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
validateSession("interactive", session);
count.incrementAndGet();
- return new String[]{"bad"};
+ return BAD_RESPONSE;
}
@Override
@@ -1075,13 +1067,17 @@ public class ClientTest extends BaseTestSupport {
}
}
});
+
+ final int MAX_PROMPTS = 3;
+ FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
+
client.start();
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
assertNotNull("Client session creation not signalled", clientSessionHolder.get());
AuthFuture future = session.auth();
- future.await();
+ assertTrue("Failed to complete authentication on time", future.await(15L, TimeUnit.SECONDS));
assertTrue("Unexpected authentication success", future.isFailure());
assertEquals("Mismatched authentication retry count", MAX_PROMPTS, count.get());
} finally {
@@ -1092,7 +1088,7 @@ public class ClientTest extends BaseTestSupport {
}
@Test
- public void testKeyboardInteractiveInSessionUserInteractive() throws Exception {
+ public void testDefaultKeyboardInteractiveInSessionUserInteractive() throws Exception {
final AtomicInteger count = new AtomicInteger();
final int MAX_PROMPTS = 3;
FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
@@ -1122,7 +1118,7 @@ public class ClientTest extends BaseTestSupport {
});
AuthFuture future = session.auth();
- future.await();
+ assertTrue("Failed to complete authentication on time", future.await(15L, TimeUnit.SECONDS));
assertTrue("Authentication not marked as success", future.isSuccess());
assertFalse("Authentication marked as failure", future.isFailure());
assertEquals("Mismatched authentication attempts count", 1, count.get());
[3/3] mina-sshd git commit: [SSHD-563] Provide server-side
KeyboardInteractiveAuthenticator
Posted by lg...@apache.org.
[SSHD-563] Provide server-side KeyboardInteractiveAuthenticator
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/75643fcb
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/75643fcb
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/75643fcb
Branch: refs/heads/master
Commit: 75643fcb1ce1316ea1b63815731ca5ee885db52e
Parents: d19f5ec
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Thu Sep 17 08:59:46 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Thu Sep 17 08:59:46 2015 +0300
----------------------------------------------------------------------
pom.xml | 2 -
.../sshd/client/ClientFactoryManager.java | 2 +-
.../sshd/client/auth/AbstractUserAuth.java | 27 +-
.../org/apache/sshd/client/auth/UserAuth.java | 4 +-
.../auth/UserAuthKeyboardInteractive.java | 102 +++-
.../sshd/client/auth/UserAuthPassword.java | 12 +-
.../sshd/client/auth/UserAuthPublicKey.java | 83 +--
.../client/auth/pubkey/KeyAgentIdentity.java | 49 ++
.../client/auth/pubkey/KeyPairIdentity.java | 60 +++
.../client/auth/pubkey/PublicKeyIdentity.java | 42 ++
.../client/config/hosts/HostConfigEntry.java | 4 +-
.../apache/sshd/client/future/AuthFuture.java | 7 +
.../sshd/client/future/DefaultAuthFuture.java | 6 +-
.../sshd/client/session/ClientSessionImpl.java | 2 +-
.../sshd/common/auth/UserAuthInstance.java | 40 ++
.../apache/sshd/common/auth/UsernameHolder.java | 31 ++
.../org/apache/sshd/common/session/Session.java | 14 +-
.../org/apache/sshd/server/ServerBuilder.java | 14 +
.../sshd/server/ServerFactoryManager.java | 11 +
.../java/org/apache/sshd/server/SshServer.java | 11 +
.../sshd/server/auth/AbstractUserAuth.java | 24 +-
.../server/auth/AbstractUserAuthPassword.java | 91 ----
.../auth/CachingPublicKeyAuthenticator.java | 76 ---
.../org/apache/sshd/server/auth/UserAuth.java | 14 +-
.../auth/UserAuthKeyboardInteractive.java | 93 ++--
.../apache/sshd/server/auth/UserAuthNone.java | 4 +-
.../sshd/server/auth/UserAuthPassword.java | 83 ++-
.../sshd/server/auth/UserAuthPublicKey.java | 33 +-
.../sshd/server/auth/gss/UserAuthGSS.java | 39 +-
...DefaultKeyboardInteractiveAuthenticator.java | 117 +++++
.../auth/keyboard/InteractiveChallenge.java | 130 +++++
.../KeyboardInteractiveAuthenticator.java | 58 ++
.../sshd/server/auth/keyboard/PromptEntry.java | 101 ++++
.../PasswordChangeRequiredException.java | 8 +-
.../pubkey/CachingPublicKeyAuthenticator.java | 75 +++
.../DefaultAuthorizedKeysAuthenticator.java | 4 +-
.../server/session/ServerUserAuthService.java | 6 +-
.../org/apache/sshd/AuthenticationTest.java | 377 -------------
.../org/apache/sshd/PortForwardingTest.java | 49 +-
.../test/java/org/apache/sshd/ProxyTest.java | 32 +-
.../apache/sshd/SinglePublicKeyAuthTest.java | 30 +-
.../java/org/apache/sshd/client/ClientTest.java | 64 ++-
.../sshd/common/auth/AuthenticationTest.java | 523 +++++++++++++++++++
.../apache/sshd/common/cipher/CipherTest.java | 2 +-
.../java/org/apache/sshd/server/ServerTest.java | 6 +-
.../sshd/git/pack/GitPackCommandTest.java | 2 +-
.../apache/sshd/git/pgm/GitPgmCommandTest.java | 2 +-
47 files changed, 1684 insertions(+), 882 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index d48cd3d..055e923 100644
--- a/pom.xml
+++ b/pom.xml
@@ -385,8 +385,6 @@
<exclude>src/test/resources/**</exclude>
<exclude>**/stty-output-*.txt</exclude>
<exclude>**/big-msg.txt</exclude>
- <exclude>sshd-sftp/**</exclude>
- <exclude>sshd-pam/**</exclude>
<!-- Eclipse files -->
<exclude>.metadata/**</exclude>
<exclude>.recommenders/**</exclude>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
index 5fcc068..11000d2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
@@ -64,7 +64,7 @@ public interface ClientFactoryManager extends FactoryManager {
String PREFERRED_AUTHS = "preferred-auths";
/**
- * Specifies the number of password prompts before giving up.
+ * Specifies the number of interactive prompts before giving up.
* The argument to this keyword must be an integer.
*/
String PASSWORD_PROMPTS = "password-prompts";
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
index 783c5f2..119310f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/AbstractUserAuth.java
@@ -22,21 +22,19 @@ package org.apache.sshd.client.auth;
import java.util.Collection;
import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.client.session.ClientSessionHolder;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public abstract class AbstractUserAuth
- extends AbstractLoggingBean
- implements UserAuth, ClientSessionHolder {
+public abstract class AbstractUserAuth extends AbstractLoggingBean implements UserAuth {
+ private final String name;
private ClientSession session;
private String service;
- protected AbstractUserAuth() {
- super();
+ protected AbstractUserAuth(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
}
@Override
@@ -44,6 +42,16 @@ public abstract class AbstractUserAuth
return session;
}
+ @Override
+ public ClientSession getSession() {
+ return getClientSession();
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
public String getService() {
return service;
}
@@ -51,6 +59,11 @@ public abstract class AbstractUserAuth
@Override
public void init(ClientSession session, String service, Collection<?> identities) throws Exception {
this.session = ValidateUtils.checkNotNull(session, "No client session");
- this.service = service;
+ this.service = ValidateUtils.checkNotNullAndNotEmpty(service, "No service");
+ }
+
+ @Override
+ public String toString() {
+ return getName() + ": " + getSession() + "[" + getService() + "]";
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
index c699077..5bf1037 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuth.java
@@ -21,6 +21,8 @@ package org.apache.sshd.client.auth;
import java.util.Collection;
import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.ClientSessionHolder;
+import org.apache.sshd.common.auth.UserAuthInstance;
import org.apache.sshd.common.util.buffer.Buffer;
/**
@@ -28,7 +30,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface UserAuth {
+public interface UserAuth extends ClientSessionHolder, UserAuthInstance<ClientSession> {
void init(ClientSession session, String service, Collection<?> identities) throws Exception;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
index 160cdb6..30f4e45 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
@@ -26,8 +26,10 @@ import java.util.List;
import org.apache.sshd.client.ClientFactoryManager;
import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.FactoryManagerUtils;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -38,14 +40,47 @@ import org.apache.sshd.common.util.buffer.BufferUtils;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthKeyboardInteractive extends AbstractUserAuth {
+ public static final String NAME = UserAuthKeyboardInteractiveFactory.NAME;
+
+ public static final String INTERACTIVE_LANGUAGE_TAG = "kb-client-interactive-language-tag";
+
+ /*
+ * As per RFC-4256:
+ *
+ * The language tag is deprecated and SHOULD be the empty string. It
+ * may be removed in a future revision of this specification. Instead,
+ * the server SHOULD select the language to be used based on the tags
+ * communicated during key exchange
+ */
+ public static final String DEFAULT_INTERACTIVE_LANGUAGE_TAG = "";
+
+ public static final String INTERACTIVE_SUBMETHODS = "kb-client-interactive-sub-methods";
+
+ /*
+ * As per RFC-4256:
+ *
+ * The submethods field is included so the user can give a hint of which
+ * actual methods he wants to use. It is a comma-separated list of
+ * authentication submethods (software or hardware) that the user
+ * prefers. If the client has knowledge of the submethods preferred by
+ * the user, presumably through a configuration setting, it MAY use the
+ * submethods field to pass this information to the server. Otherwise,
+ * it MUST send the empty string.
+ *
+ * The actual names of the submethods is something the user and the
+ * server need to agree upon.
+ *
+ * Server interpretation of the submethods field is implementation-
+ * dependent.
+ */
+ public static final String DEFAULT_INTERACTIVE_SUBMETHODS = "";
private Iterator<String> passwords;
- private String current;
- private int nbTrials;
private int maxTrials;
+ private int nbTrials;
public UserAuthKeyboardInteractive() {
- super();
+ super(NAME);
}
@Override
@@ -60,6 +95,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
}
passwords = pwds.iterator();
maxTrials = session.getIntProperty(ClientFactoryManager.PASSWORD_PROMPTS, ClientFactoryManager.DEFAULT_PASSWORD_PROMPTS);
+ ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
}
@Override
@@ -69,42 +105,43 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
String service = getService();
if (buffer == null) {
- if (passwords.hasNext()) {
- current = passwords.next();
- } else if (nbTrials++ < maxTrials) {
- current = null;
- } else {
- return false;
- }
-
+ String name = getName();
if (log.isDebugEnabled()) {
- log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST for keyboard-interactive",
- username, session, service);
+ log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST for {}",
+ username, session, service, name);
}
buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
- username.length() + service.length() + UserAuthKeyboardInteractiveFactory.NAME.length() + Integer.SIZE);
+ username.length() + service.length() + name.length() + Integer.SIZE);
buffer.putString(username);
buffer.putString(service);
- buffer.putString(UserAuthKeyboardInteractiveFactory.NAME);
- buffer.putString("");
- buffer.putString("");
+ buffer.putString(name);
+ buffer.putString(getExchangeLanguageTag(session));
+ buffer.putString(getExchangeSubMethods(session));
session.writePacket(buffer);
return true;
}
int cmd = buffer.getUByte();
if (cmd == SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST) {
+ nbTrials++;
+ if (nbTrials > maxTrials) {
+ if (log.isDebugEnabled()) {
+ log.debug("process({})[{}] Reject SSH_MSG_USERAUTH_INFO_REQUEST for {} num. trials ({}) exceeds max({})",
+ session, service, getName(), nbTrials, maxTrials);
+ }
+ return false;
+ }
+
if (log.isDebugEnabled()) {
- log.debug("process({}@{})[{}] Received SSH_MSG_USERAUTH_INFO_REQUEST for keyboard-interactive",
- username, session, service);
+ log.debug("process({})[{}] Received SSH_MSG_USERAUTH_INFO_REQUEST for {}", session, service, getName());
}
String name = buffer.getString();
String instruction = buffer.getString();
String language_tag = buffer.getString();
if (log.isTraceEnabled()) {
- log.trace("process({}@{})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={} instruction={} language={}",
- username, session, service, name, instruction, language_tag);
+ log.trace("process({})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={} instruction={} language={}",
+ session, service, name, instruction, language_tag);
}
int num = buffer.getInt();
@@ -117,8 +154,8 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
}
if (log.isTraceEnabled()) {
- log.trace("process({}@{})[{}] Prompt: {}", username, session, service, Arrays.toString(prompt));
- log.trace("process({}@{})[{}] Echo: {}", username, session, service, Arrays.toString(echo));
+ log.trace("process({})[{}] Prompt: {}", session, service, Arrays.toString(prompt));
+ log.trace("process({})[{}] Echo: {}", session, service, Arrays.toString(echo));
}
String[] rep = getUserResponses(name, instruction, language_tag, prompt, echo);
@@ -136,8 +173,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
* However it is the server's (!) responsibility to fail, so we only warn...
*/
if (num != rep.length) {
- log.warn("process({}@{}) Mismatched prompts ({}) vs. responses count ({})",
- username, session, num, rep.length);
+ log.warn("process({}) Mismatched prompts ({}) vs. responses count ({})", session, num, rep.length);
}
buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, BufferUtils.clear(buffer));
@@ -148,11 +184,23 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
session.writePacket(buffer);
return true;
}
- throw new IllegalStateException("process(" + username + "@" + session + ")[" + service + ") received unknown packet: cmd=" + cmd);
+ throw new IllegalStateException("process(" + session + ")[" + service + ") received unknown packet: cmd=" + cmd);
+ }
+
+ protected String getExchangeLanguageTag(ClientSession session) {
+ return FactoryManagerUtils.getStringProperty(session, INTERACTIVE_LANGUAGE_TAG, DEFAULT_INTERACTIVE_LANGUAGE_TAG);
+ }
+
+ protected String getExchangeSubMethods(ClientSession session) {
+ return FactoryManagerUtils.getStringProperty(session, INTERACTIVE_SUBMETHODS, DEFAULT_INTERACTIVE_SUBMETHODS);
}
protected String getCurrentPasswordCandidate() {
- return current;
+ if ((passwords != null) && passwords.hasNext()) {
+ return passwords.next();
+ } else {
+ return null;
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
index 0214bc6..4924f0c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
@@ -37,12 +37,13 @@ import org.apache.sshd.common.util.buffer.BufferUtils;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthPassword extends AbstractUserAuth {
+ public static final String NAME = UserAuthPasswordFactory.NAME;
private Iterator<String> passwords;
private String current;
public UserAuthPassword() {
- super();
+ super(NAME);
}
@Override
@@ -69,7 +70,7 @@ public class UserAuthPassword extends AbstractUserAuth {
if (passwords.hasNext()) {
current = passwords.next();
buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
- username.length() + service.length() + UserAuthPasswordFactory.NAME.length() + current.length() + Integer.SIZE);
+ username.length() + service.length() + getName().length() + current.length() + Integer.SIZE);
sendPassword(buffer, session, current, current);
return true;
}
@@ -121,16 +122,17 @@ public class UserAuthPassword extends AbstractUserAuth {
protected void sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
String username = session.getUsername();
String service = getService();
+ String name = getName();
boolean modified = !Objects.equals(oldPassword, newPassword);
if (log.isDebugEnabled()) {
- log.debug("sendPassword({}@{})[{}] send SSH_MSG_USERAUTH_REQUEST for password - modified={}",
- username, session, service, modified);
+ log.debug("sendPassword({}@{})[{}] send SSH_MSG_USERAUTH_REQUEST for {} - modified={}",
+ username, session, service, name, modified);
}
buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
buffer.putString(username);
buffer.putString(service);
- buffer.putString(UserAuthPasswordFactory.NAME);
+ buffer.putString(name);
buffer.putBoolean(modified);
// see RFC-4252 section 8
buffer.putString(oldPassword);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
index 6a6f366..525eb43 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
@@ -28,15 +28,16 @@ import java.util.List;
import org.apache.sshd.agent.SshAgent;
import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
+import org.apache.sshd.client.auth.pubkey.KeyPairIdentity;
+import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.signature.Signature;
import org.apache.sshd.common.util.Pair;
-import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -47,13 +48,14 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthPublicKey extends AbstractUserAuth {
+ public static final String NAME = UserAuthPublicKeyFactory.NAME;
private SshAgent agent;
private Iterator<PublicKeyIdentity> keys;
private PublicKeyIdentity current;
public UserAuthPublicKey() {
- super();
+ super(NAME);
}
@Override
@@ -99,15 +101,16 @@ public class UserAuthPublicKey extends AbstractUserAuth {
current = keys.next();
PublicKey key = current.getPublicKey();
String algo = KeyUtils.getKeyType(key);
+ String name = getName();
if (log.isDebugEnabled()) {
- log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST request publickey algo={}",
- username, session, service, algo);
+ log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST request {} algo={}",
+ username, session, service, name, algo);
}
buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
buffer.putString(username);
buffer.putString(service);
- buffer.putString(UserAuthPublicKeyFactory.NAME);
+ buffer.putString(name);
buffer.putBoolean(false);
buffer.putString(algo);
buffer.putPublicKey(key);
@@ -125,25 +128,27 @@ public class UserAuthPublicKey extends AbstractUserAuth {
if (cmd == SshConstants.SSH_MSG_USERAUTH_PK_OK) {
PublicKey key = current.getPublicKey();
String algo = KeyUtils.getKeyType(key);
+ String name = getName();
if (log.isDebugEnabled()) {
- log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST reply publickey algo={}",
- username, session, service, algo);
+ log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST reply {} algo={}",
+ username, session, service, name, algo);
}
buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
buffer.putString(username);
buffer.putString(service);
- buffer.putString(UserAuthPublicKeyFactory.NAME);
+ buffer.putString(name);
buffer.putBoolean(true);
buffer.putString(algo);
buffer.putPublicKey(key);
Buffer bs = new ByteArrayBuffer();
- bs.putBytes(session.getKex().getH());
+ KeyExchange kex = session.getKex();
+ bs.putBytes(kex.getH());
bs.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
bs.putString(username);
bs.putString(service);
- bs.putString(UserAuthPublicKeyFactory.NAME);
+ bs.putString(name);
bs.putBoolean(true);
bs.putString(algo);
bs.putPublicKey(key);
@@ -171,58 +176,4 @@ public class UserAuthPublicKey extends AbstractUserAuth {
}
}
}
-
- interface PublicKeyIdentity {
- PublicKey getPublicKey();
-
- byte[] sign(byte[] data) throws Exception;
- }
-
- static class KeyAgentIdentity implements PublicKeyIdentity {
- private final SshAgent agent;
- private final PublicKey key;
-
- KeyAgentIdentity(SshAgent agent, PublicKey key) {
- this.agent = agent;
- this.key = key;
- }
-
- @Override
- public PublicKey getPublicKey() {
- return key;
- }
-
- @Override
- public byte[] sign(byte[] data) throws Exception {
- return agent.sign(key, data);
- }
- }
-
- static class KeyPairIdentity implements PublicKeyIdentity {
- private final KeyPair pair;
- private final FactoryManager manager;
-
- KeyPairIdentity(FactoryManager manager, KeyPair pair) {
- this.manager = manager;
- this.pair = pair;
- }
-
- @Override
- public PublicKey getPublicKey() {
- return pair.getPublic();
- }
-
- @Override
- public byte[] sign(byte[] data) throws Exception {
- String keyType = KeyUtils.getKeyType(pair);
- Signature verif = ValidateUtils.checkNotNull(
- NamedFactory.Utils.create(manager.getSignatureFactories(), keyType),
- "No signer could be located for key type=%s",
- keyType);
- verif.initSigner(pair.getPrivate());
- verif.update(data, 0, data.length);
- return verif.sign();
- }
- }
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
new file mode 100644
index 0000000..e8f09a3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyAgentIdentity.java
@@ -0,0 +1,49 @@
+/*
+ * 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.pubkey;
+
+import java.security.PublicKey;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Uses an {@link SshAgent} to generate the identity signature
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class KeyAgentIdentity implements PublicKeyIdentity {
+ private final SshAgent agent;
+ private final PublicKey key;
+
+ public KeyAgentIdentity(SshAgent agent, PublicKey key) {
+ this.agent = ValidateUtils.checkNotNull(agent, "No signing agent");
+ this.key = ValidateUtils.checkNotNull(key, "No public key");
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return key;
+ }
+
+ @Override
+ public byte[] sign(byte[] data) throws Exception {
+ return agent.sign(key, data);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
new file mode 100644
index 0000000..40b3837
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
@@ -0,0 +1,60 @@
+/*
+ * 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.pubkey;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Uses a {@link KeyPair} to generate the identity signature
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class KeyPairIdentity implements PublicKeyIdentity {
+ private final KeyPair pair;
+ private final FactoryManager manager;
+
+ public KeyPairIdentity(FactoryManager manager, KeyPair pair) {
+ this.manager = ValidateUtils.checkNotNull(manager, "No manager");
+ this.pair = ValidateUtils.checkNotNull(pair, "No key pair");
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return pair.getPublic();
+ }
+
+ @Override
+ public byte[] sign(byte[] data) throws Exception {
+ String keyType = KeyUtils.getKeyType(pair);
+ Signature verifier = ValidateUtils.checkNotNull(
+ NamedFactory.Utils.create(manager.getSignatureFactories(), keyType),
+ "No signer could be located for key type=%s",
+ keyType);
+ verifier.initSigner(pair.getPrivate());
+ verifier.update(data, 0, data.length);
+ return verifier.sign();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
new file mode 100644
index 0000000..51444ae
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.pubkey;
+
+import java.security.PublicKey;
+
+/**
+ * Represents a public key identity
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PublicKeyIdentity {
+ /**
+ * @return The {@link PublicKey} identity value
+ */
+ PublicKey getPublicKey();
+
+ /**
+ * Proves the public key identity by signing the given data
+ *
+ * @param data Data to sign
+ * @return Signed data - using the identity
+ * @throws Exception If failed to sign the data
+ */
+ byte[] sign(byte[] data) throws Exception;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
index 7272a34..7593ae8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
@@ -49,6 +49,7 @@ import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.apache.sshd.common.auth.UsernameHolder;
import org.apache.sshd.common.config.SshConfigFileReader;
import org.apache.sshd.common.config.keys.IdentityUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -65,7 +66,7 @@ import org.apache.sshd.common.util.io.NoCloseReader;
* file format</A>
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class HostConfigEntry implements Cloneable {
+public class HostConfigEntry implements Cloneable, UsernameHolder {
/**
* Used in a host pattern to denote zero or more consecutive characters
@@ -218,6 +219,7 @@ public class HostConfigEntry implements Cloneable {
/**
* @return A username override - if not {@code null}/empty
*/
+ @Override
public String getUsername() {
return username;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
index 6859966..cf9f802 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
@@ -38,11 +38,18 @@ public interface AuthFuture extends SshFuture<AuthFuture>, VerifiableFuture<Auth
/**
* @return <code>true</code> if the authentication operation is finished successfully.
+ * <B>Note:</B> calling this method while the operation is in progress
+ * returns {@code false}. Should check {@link #isDone()} in order to
+ * ensure that the result is valid.
*/
boolean isSuccess();
/**
* @return <code>false</code> if the authentication operation failed.
+ * <B>Note:</B> the operation is considered failed if an exception
+ * is received instead of a success/fail response code or the operation
+ * is in progress. Should check {@link #isDone()} in order to
+ * ensure that the result is valid.
*/
boolean isFailure();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
index ca83826..f42b94c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
@@ -64,7 +64,11 @@ public class DefaultAuthFuture extends DefaultVerifiableSshFuture<AuthFuture> im
@Override // TODO for JDK-8 make this a default method
public boolean isFailure() {
Object v = getValue();
- return (v instanceof Boolean) && (!((Boolean) v).booleanValue());
+ if (v instanceof Boolean) {
+ return !((Boolean) v).booleanValue();
+ } else {
+ return true;
+ }
}
@Override // TODO for JDK-8 make this a default method
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/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 96dcbd6..29c2879 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
@@ -523,7 +523,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
for (;;) {
int cond = 0;
if (closeFuture.isClosed()) {
- cond |= ClientSession.CLOSED;
+ cond |= CLOSED;
}
if (authed) { // authFuture.isSuccess()
cond |= AUTHED;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/common/auth/UserAuthInstance.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/auth/UserAuthInstance.java b/sshd-core/src/main/java/org/apache/sshd/common/auth/UserAuthInstance.java
new file mode 100644
index 0000000..f7448e6
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/auth/UserAuthInstance.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.auth;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.session.Session;
+
+/**
+ * Represents an authentication-in-progress tracker for a specific session
+ *
+ * @param <S> The type of session being tracked by the instance
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+//CHECKSTYLE:OFF
+public interface UserAuthInstance<S extends Session> extends NamedResource {
+ /**
+ * @return The current session for which the authentication is being
+ * tracked. <B>Note:</B> may be {@code null} if the instance has not
+ * been initialized yet
+ */
+ S getSession();
+}
+//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
new file mode 100644
index 0000000..b49c84b
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
@@ -0,0 +1,31 @@
+/*
+ * 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.common.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface UsernameHolder {
+ /**
+ * @return The attached username - may be {@code null}/empty if holder
+ * not yet initialized
+ */
+ String getUsername();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 04e65ea..bd60783 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.Service;
+import org.apache.sshd.common.auth.UsernameHolder;
import org.apache.sshd.common.channel.ChannelListenerManager;
import org.apache.sshd.common.future.KeyExchangeFuture;
import org.apache.sshd.common.io.IoSession;
@@ -33,11 +34,12 @@ import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.util.buffer.Buffer;
/**
- * Represents an SSH session
+ * Represents an SSH session. <B>Note:</B> the associated username for the session
+ * may be {@code null}/empty if the session is not yet authenticated
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface Session extends SessionListenerManager, ChannelListenerManager, Closeable {
+public interface Session extends SessionListenerManager, ChannelListenerManager, Closeable, UsernameHolder {
/**
* Timeout status.
@@ -68,14 +70,6 @@ public interface Session extends SessionListenerManager, ChannelListenerManager,
*/
<T, E extends T> T setAttribute(AttributeKey<T> key, E value);
- /**
- * Retrieve the name of the user authenticated on this session
- * or null if the session has not been authenticated yet.
- *
- * @return the user name.
- */
- String getUsername();
-
void setUsername(String username);
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
index a932449..d2fd0ab 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
@@ -31,6 +31,8 @@ import org.apache.sshd.common.kex.DHFactory;
import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.session.ConnectionService;
import org.apache.sshd.common.util.Transformer;
+import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator;
+import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
import org.apache.sshd.server.channel.ChannelSessionFactory;
import org.apache.sshd.server.config.keys.DefaultAuthorizedKeysAuthenticator;
@@ -76,13 +78,20 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
));
public static final PublickeyAuthenticator DEFAULT_PUBLIC_KEY_AUTHENTICATOR = DefaultAuthorizedKeysAuthenticator.INSTANCE;
+ public static final KeyboardInteractiveAuthenticator DEFAULT_INTERACTIVE_AUTHENTICATOR = DefaultKeyboardInteractiveAuthenticator.INSTANCE;
protected PublickeyAuthenticator pubkeyAuthenticator;
+ protected KeyboardInteractiveAuthenticator interactiveAuthenticator;
public ServerBuilder() {
super();
}
+ public ServerBuilder interactiveAuthenticator(KeyboardInteractiveAuthenticator auth) {
+ interactiveAuthenticator = auth;
+ return this;
+ }
+
public ServerBuilder publickeyAuthenticator(PublickeyAuthenticator auth) {
pubkeyAuthenticator = auth;
return this;
@@ -108,6 +117,10 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
pubkeyAuthenticator = DEFAULT_PUBLIC_KEY_AUTHENTICATOR;
}
+ if (interactiveAuthenticator == null) {
+ interactiveAuthenticator = DEFAULT_INTERACTIVE_AUTHENTICATOR;
+ }
+
if (factory == null) {
factory = SshServer.DEFAULT_SSH_SERVER_FACTORY;
}
@@ -119,6 +132,7 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
public SshServer build(boolean isFillWithDefaultValues) {
SshServer server = super.build(isFillWithDefaultValues);
server.setPublickeyAuthenticator(pubkeyAuthenticator);
+ server.setKeyboardInteractiveAuthenticator(interactiveAuthenticator);
return server;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
index 9bcc8bb..29b03ea 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
@@ -26,6 +26,7 @@ import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
import org.apache.sshd.server.auth.UserAuth;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
+import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
@@ -146,6 +147,16 @@ public interface ServerFactoryManager extends FactoryManager, KeyPairProviderHol
PasswordAuthenticator getPasswordAuthenticator();
/**
+ * Retrieve the <code>KeyboardInteractiveAuthenticator</code> to be used by
+ * the SSH server. If no authenticator has been configured (i.e. this method returns
+ * {@code null}), then client authentication requests based on this method
+ * will be rejected.
+ *
+ * @return The {@link KeyboardInteractiveAuthenticator} or {@code null}
+ */
+ KeyboardInteractiveAuthenticator getKeyboardInteractiveAuthenticator();
+
+ /**
* Retrieve the <code>GSSAuthenticator</code> to be used by the SSH server.
* If no authenticator has been configured (i.e. this method returns
* {@code null}), then client authentication requests based on gssapi
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index 6baf31b..8d1d152 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -50,6 +50,7 @@ import org.apache.sshd.server.auth.UserAuthPasswordFactory;
import org.apache.sshd.server.auth.UserAuthPublicKeyFactory;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.auth.gss.UserAuthGSSFactory;
+import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
@@ -129,6 +130,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
protected List<NamedFactory<Command>> subsystemFactories;
protected PasswordAuthenticator passwordAuthenticator;
protected PublickeyAuthenticator publickeyAuthenticator;
+ protected KeyboardInteractiveAuthenticator interactiveAuthenticator;
protected GSSAuthenticator gssAuthenticator;
public SshServer() {
@@ -219,6 +221,15 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
}
@Override
+ public KeyboardInteractiveAuthenticator getKeyboardInteractiveAuthenticator() {
+ return interactiveAuthenticator;
+ }
+
+ public void setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator interactiveAuthenticator) {
+ this.interactiveAuthenticator = interactiveAuthenticator;
+ }
+
+ @Override
public GSSAuthenticator getGSSAuthenticator() {
return gssAuthenticator;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
index b2618ea..578e1d1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
@@ -22,21 +22,26 @@ import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.server.session.ServerSessionHolder;
/**
*/
-public abstract class AbstractUserAuth extends AbstractLoggingBean implements UserAuth, ServerSessionHolder {
+public abstract class AbstractUserAuth extends AbstractLoggingBean implements UserAuth {
+ private final String name;
private ServerSession session;
private String service;
private String username;
- protected AbstractUserAuth() {
- super();
+ protected AbstractUserAuth(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
}
@Override
- public String getUserName() {
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public String getUsername() {
return username;
}
@@ -50,6 +55,11 @@ public abstract class AbstractUserAuth extends AbstractLoggingBean implements Us
}
@Override
+ public ServerSession getSession() {
+ return getServerSession();
+ }
+
+ @Override
public Boolean auth(ServerSession session, String username, String service, Buffer buffer) throws Exception {
this.session = ValidateUtils.checkNotNull(session, "No server session");
this.username = username;
@@ -69,4 +79,8 @@ public abstract class AbstractUserAuth extends AbstractLoggingBean implements Us
protected abstract Boolean doAuth(Buffer buffer, boolean init) throws Exception;
+ @Override
+ public String toString() {
+ return getName() + ": " + getSession() + "[" + getService() + "]";
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuthPassword.java
deleted file mode 100644
index b673bf7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuthPassword.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.auth;
-
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.server.ServerFactoryManager;
-import org.apache.sshd.server.auth.password.PasswordAuthenticator;
-import org.apache.sshd.server.auth.password.PasswordChangeRequiredException;
-import org.apache.sshd.server.session.ServerSession;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractUserAuthPassword extends AbstractUserAuth {
- protected AbstractUserAuthPassword() {
- super();
- }
-
- /**
- * Invokes the configured {@link PasswordAuthenticator} and returns the result.
- * If {@link PasswordChangeRequiredException} thrown by the authenticator then
- * {@link #handleServerPasswordChangeRequest(Buffer, ServerSession, String, String, PasswordChangeRequiredException)}
- * is invoked
- *
- * @param buffer The received {@link Buffer} to be re-used if need to send
- * a password change request
- * @param session The {@link ServerSession} through which the request was received
- * @param username The username
- * @param password The password
- * @return The authentication result - if {@code null} then exception was handled
- * internally and authentication is still in progress
- * @throws Exception If internal error during authentication (exception for
- * {@link PasswordChangeRequiredException} which is handled internally)
- * @see #handleServerPasswordChangeRequest(Buffer, ServerSession, String, String, PasswordChangeRequiredException)
- */
- protected Boolean checkPassword(Buffer buffer, ServerSession session, String username, String password) throws Exception {
- ServerFactoryManager manager = ValidateUtils.checkNotNull(
- session.getFactoryManager(),
- "No ServerFactoryManager configured");
- PasswordAuthenticator auth = ValidateUtils.checkNotNull(
- manager.getPasswordAuthenticator(),
- "No PasswordAuthenticator configured");
- try {
- return auth.authenticate(username, password, session);
- } catch (PasswordChangeRequiredException e) {
- if (log.isDebugEnabled()) {
- log.debug("checkPassword({}@{}) password change required: {}",
- username, session, e.getMessage());
- }
- return handleServerPasswordChangeRequest(buffer, session, username, password, e);
- }
- }
-
- /**
- * Invoked by {@link #checkPassword(Buffer, ServerSession, String, String)}
- * when a {@link PasswordChangeRequiredException} was thrown by the authenticator.
- * By default it re-throws the original exception.
- *
- * @param buffer The received {@link Buffer} to be re-used if need to send
- * a password change request
- * @param session The {@link ServerSession} through which the request was received
- * @param username The username
- * @param password The (rejected) password
- * @param e The original thrown exception
- * @return {@code null} by default to indicate incomplete authentication
- * @throws Exception If failed to dispatch the message
- */
- protected Boolean handleServerPasswordChangeRequest(
- Buffer buffer, ServerSession session, String username, String password, PasswordChangeRequiredException e)
- throws Exception {
- throw e;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/CachingPublicKeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/CachingPublicKeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/CachingPublicKeyAuthenticator.java
deleted file mode 100644
index 998813c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/CachingPublicKeyAuthenticator.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.auth;
-
-import java.security.PublicKey;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionListener;
-import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
-import org.apache.sshd.server.session.ServerSession;
-
-/**
- * Caches the result per session
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, SessionListener {
-
- protected final PublickeyAuthenticator authenticator;
- protected final Map<ServerSession, Map<PublicKey, Boolean>> cache = new ConcurrentHashMap<ServerSession, Map<PublicKey, Boolean>>();
-
- public CachingPublicKeyAuthenticator(PublickeyAuthenticator authenticator) {
- this.authenticator = authenticator;
- }
-
- @Override
- public boolean authenticate(String username, PublicKey key, ServerSession session) {
- Map<PublicKey, Boolean> map = cache.get(session);
- if (map == null) {
- map = new ConcurrentHashMap<>();
- cache.put(session, map);
- session.addSessionListener(this);
- }
-
- Boolean result = map.get(key);
- if (result == null) {
- result = authenticator.authenticate(username, key, session);
- map.put(key, result);
- }
-
- return result;
- }
-
- @Override
- public void sessionCreated(Session session) {
- // ignored
- }
-
- @Override
- public void sessionEvent(Session session, Event event) {
- // ignored
- }
-
- @Override
- public void sessionClosed(Session session) {
- cache.remove(session);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
index 96730bb..9892324 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
@@ -18,15 +18,18 @@
*/
package org.apache.sshd.server.auth;
+import org.apache.sshd.common.auth.UserAuthInstance;
+import org.apache.sshd.common.auth.UsernameHolder;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.session.ServerSessionHolder;
/**
* Server side authentication mechanism.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface UserAuth {
+public interface UserAuth extends ServerSessionHolder, UserAuthInstance<ServerSession>, UsernameHolder {
/**
* Try to authenticate the user.
@@ -56,15 +59,6 @@ public interface UserAuth {
Boolean next(Buffer buffer) throws Exception;
/**
- * Get a user name which has been derived from the handshaking process, or the initial name if
- * nothing has been found.
- *
- * @return The user name
- * @throws Exception if the request fails
- */
- String getUserName() throws Exception;
-
- /**
* Free any system resources used by the module.
*/
void destroy();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/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 22ceed8..f9b8737 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
@@ -18,11 +18,18 @@
*/
package org.apache.sshd.server.auth;
-import org.apache.sshd.common.FactoryManagerUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.auth.keyboard.InteractiveChallenge;
+import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
import org.apache.sshd.server.session.ServerSession;
/**
@@ -30,35 +37,39 @@ import org.apache.sshd.server.session.ServerSession;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class UserAuthKeyboardInteractive extends AbstractUserAuthPassword {
- // configuration parameters on the FactoryManager to configure the message values
- public static final String KB_INTERACTIVE_NAME_PROP = "kb-interactive-name";
- public static final String DEFAULT_KB_INTERACTIVE_NAME = "Password authentication";
- public static final String KB_INTERACTIVE_INSTRUCTION_PROP = "kb-interactive-instruction";
- public static final String DEFAULT_KB_INTERACTIVE_INSTRUCTION = "";
- public static final String KB_INTERACTIVE_LANG_PROP = "kb-interactive-language";
- public static final String DEFAULT_KB_INTERACTIVE_LANG = "en-US";
- public static final String KB_INTERACTIVE_PROMPT_PROP = "kb-interactive-prompt";
- public static final String DEFAULT_KB_INTERACTIVE_PROMPT = "Password: ";
- public static final String KB_INTERACTIVE_ECHO_PROMPT_PROP = "kb-interactive-echo-prompt";
- public static final boolean DEFAULT_KB_INTERACTIVE_ECHO_PROMPT = false;
+public class UserAuthKeyboardInteractive extends AbstractUserAuth {
+ public static final String NAME = UserAuthKeyboardInteractiveFactory.NAME;
public UserAuthKeyboardInteractive() {
- super();
+ super(NAME);
}
@Override
protected Boolean doAuth(Buffer buffer, boolean init) throws Exception {
ServerSession session = getServerSession();
+ ServerFactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
+ KeyboardInteractiveAuthenticator auth = manager.getKeyboardInteractiveAuthenticator();
if (init) {
+ String lang = buffer.getString();
+ String subMethods = buffer.getString();
+ if (auth == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("doAuth({}) no interactive authenticator to generate challenge", session);
+ }
+ return false;
+ }
+
+ InteractiveChallenge challenge = auth.generateChallenge(session, getUsername(), lang, subMethods);
+ if (challenge == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("doAuth({}) no interactive challenge generated", session);
+ }
+ return false;
+ }
+
// Prompt for password
buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST, BufferUtils.clear(buffer));
- buffer.putString(getInteractionName());
- buffer.putString(getInteractionInstruction());
- buffer.putString(getInteractionLanguage());
- buffer.putInt(1);
- buffer.putString(getInteractionPrompt());
- buffer.putBoolean(isInteractionPromptEchoEnabled());
+ challenge.append(buffer);
session.writePacket(buffer);
return null;
} else {
@@ -66,39 +77,21 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuthPassword {
if (cmd != SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE) {
throw new SshException("Received unexpected message: " + cmd);
}
+
int num = buffer.getInt();
- /*
- * According to RFC4256:
- *
- * If the num-responses field does not match the num-prompts
- * field in the request message, the server MUST send a failure
- * message.
- */
- if (num != 1) {
- throw new SshException("Expected 1 response from user but received " + num);
+ List<String> responses = (num <= 0) ? Collections.<String>emptyList() : new ArrayList<String>(num);
+ for (int index = 0; index < num; index++) {
+ responses.add(buffer.getString());
}
- String password = buffer.getString();
- return checkPassword(buffer, session, getUserName(), password);
- }
- }
- protected String getInteractionName() {
- return FactoryManagerUtils.getStringProperty(getServerSession(), KB_INTERACTIVE_NAME_PROP, DEFAULT_KB_INTERACTIVE_NAME);
- }
-
- protected String getInteractionInstruction() {
- return FactoryManagerUtils.getStringProperty(getServerSession(), KB_INTERACTIVE_INSTRUCTION_PROP, DEFAULT_KB_INTERACTIVE_INSTRUCTION);
- }
-
- protected String getInteractionLanguage() {
- return FactoryManagerUtils.getStringProperty(getServerSession(), KB_INTERACTIVE_LANG_PROP, DEFAULT_KB_INTERACTIVE_LANG);
- }
-
- protected String getInteractionPrompt() {
- return FactoryManagerUtils.getStringProperty(getServerSession(), KB_INTERACTIVE_PROMPT_PROP, DEFAULT_KB_INTERACTIVE_PROMPT);
- }
+ if (auth == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("doAuth({}) no interactive authenticator to validate responses", session);
+ }
+ return false;
+ }
- protected boolean isInteractionPromptEchoEnabled() {
- return FactoryManagerUtils.getBooleanProperty(getServerSession(), KB_INTERACTIVE_ECHO_PROMPT_PROP, DEFAULT_KB_INTERACTIVE_ECHO_PROMPT);
+ return auth.authenticate(session, getUsername(), responses);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
index a3a62e0..8cc708e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
@@ -26,14 +26,14 @@ import org.apache.sshd.common.util.buffer.Buffer;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthNone extends AbstractUserAuth {
+ public static final String NAME = UserAuthNoneFactory.NAME;
public UserAuthNone() {
- super();
+ super(NAME);
}
@Override
public Boolean doAuth(Buffer buffer, boolean init) {
return Boolean.TRUE;
}
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/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 9d37581..3dc7fd5 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
@@ -19,7 +19,10 @@
package org.apache.sshd.server.auth;
import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.password.PasswordChangeRequiredException;
import org.apache.sshd.server.session.ServerSession;
@@ -28,23 +31,62 @@ import org.apache.sshd.server.session.ServerSession;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class UserAuthPassword extends AbstractUserAuthPassword {
+public class UserAuthPassword extends AbstractUserAuth {
+ public static final String NAME = UserAuthPasswordFactory.NAME;
+
public UserAuthPassword() {
- super();
+ super(NAME);
}
@Override
public Boolean doAuth(Buffer buffer, boolean init) throws Exception {
- if (!init) {
- throw new IllegalStateException("Incomplete initialization");
- }
+ ValidateUtils.checkTrue(init, "Instance not initialized");
boolean newPassword = buffer.getBoolean();
String password = buffer.getString();
if (newPassword) {
- return handleClientPasswordChangeRequest(buffer, getServerSession(), getUserName(), password, buffer.getString());
+ return handleClientPasswordChangeRequest(buffer, getServerSession(), getUsername(), password, buffer.getString());
} else {
- return checkPassword(buffer, getServerSession(), getUserName(), password);
+ return checkPassword(buffer, getServerSession(), getUsername(), password);
+ }
+ }
+
+ /**
+ * Invokes the configured {@link PasswordAuthenticator} and returns the result.
+ * If {@link PasswordChangeRequiredException} thrown by the authenticator then
+ * {@link #handleServerPasswordChangeRequest(Buffer, ServerSession, String, String, PasswordChangeRequiredException)}
+ * is invoked
+ *
+ * @param buffer The received {@link Buffer} to be re-used if need to send
+ * a password change request
+ * @param session The {@link ServerSession} through which the request was received
+ * @param username The username
+ * @param password The password
+ * @return The authentication result - if {@code null} then exception was handled
+ * internally and authentication is still in progress
+ * @throws Exception If internal error during authentication (exception for
+ * {@link PasswordChangeRequiredException} which is handled internally)
+ * @see #handleServerPasswordChangeRequest(Buffer, ServerSession, String, String, PasswordChangeRequiredException)
+ */
+ protected Boolean checkPassword(Buffer buffer, ServerSession session, String username, String password) throws Exception {
+ ServerFactoryManager manager = ValidateUtils.checkNotNull(
+ session.getFactoryManager(),
+ "No ServerFactoryManager configured");
+ PasswordAuthenticator auth = manager.getPasswordAuthenticator();
+ if (auth == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("checkPassword({}) no password authenticator", session);
+ }
+ return false;
+ }
+
+ try {
+ return auth.authenticate(username, password, session);
+ } catch (PasswordChangeRequiredException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("checkPassword({}) password change required: {}", session, e.getMessage());
+ }
+ return handleServerPasswordChangeRequest(buffer, session, username, password, e);
}
}
@@ -67,23 +109,34 @@ public class UserAuthPassword extends AbstractUserAuthPassword {
throw new UnsupportedOperationException("Password change not supported");
}
- @Override
+ /**
+ * Invoked by {@link #checkPassword(Buffer, ServerSession, String, String)}
+ * when a {@link PasswordChangeRequiredException} was thrown by the authenticator.
+ * By default it re-throws the original exception.
+ *
+ * @param buffer The received {@link Buffer} to be re-used if need to send
+ * a password change request
+ * @param session The {@link ServerSession} through which the request was received
+ * @param username The username
+ * @param password The (rejected) password
+ * @param e The original thrown exception
+ * @return {@code null} by default to indicate incomplete authentication
+ * @throws Exception If failed to dispatch the message
+ */
protected Boolean handleServerPasswordChangeRequest(
Buffer buffer, ServerSession session, String username, String password, PasswordChangeRequiredException e)
- throws Exception {
+ throws Exception {
String prompt = e.getPrompt();
String lang = e.getLanguage();
if (log.isDebugEnabled()) {
- log.debug("handlePasswordChangeRequest({}@{}) password change required - prompt={}, lang={}",
- username, session, prompt, lang);
+ log.debug("handlePasswordChangeRequest({}) password change required - prompt={}, lang={}",
+ session, prompt, lang);
}
buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, buffer);
- buffer.putString((prompt == null) ? "" : prompt);
- buffer.putString((lang == null) ? "" : lang);
+ buffer.putString(prompt);
+ buffer.putString(lang);
session.writePacket(buffer);
return null; // authentication incomplete
-
}
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/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 fa56c9d..5285e40 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
@@ -37,36 +37,43 @@ import org.apache.sshd.server.session.ServerSession;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class UserAuthPublicKey extends AbstractUserAuth {
+ public static final String NAME = UserAuthPublicKeyFactory.NAME;
public UserAuthPublicKey() {
- super();
+ super(NAME);
}
@Override
public Boolean doAuth(Buffer buffer, boolean init) throws Exception {
ValidateUtils.checkTrue(init, "Instance not initialized");
+
boolean hasSig = buffer.getBoolean();
String alg = buffer.getString();
-
int oldLim = buffer.wpos();
int oldPos = buffer.rpos();
int len = buffer.getInt();
buffer.wpos(buffer.rpos() + len);
- PublicKey key = buffer.getRawPublicKey();
+
ServerSession session = getServerSession();
- ServerFactoryManager manager = session.getFactoryManager();
- Signature verif = ValidateUtils.checkNotNull(
+ ServerFactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
+ PublicKey key = buffer.getRawPublicKey();
+ Signature verifier = ValidateUtils.checkNotNull(
NamedFactory.Utils.create(manager.getSignatureFactories(), alg),
"No verifier located for algorithm=%s",
alg);
- verif.initVerifier(key);
+ verifier.initVerifier(key);
buffer.wpos(oldLim);
byte[] sig = hasSig ? buffer.getBytes() : null;
+ PublickeyAuthenticator authenticator = manager.getPublickeyAuthenticator();
+ if (authenticator == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("doAuth({}) no authenticator", session);
+ }
+ return false;
+ }
- PublickeyAuthenticator authenticator =
- ValidateUtils.checkNotNull(manager.getPublickeyAuthenticator(), "No PublickeyAuthenticator configured");
- if (!authenticator.authenticate(getUserName(), key, session)) {
+ if (!authenticator.authenticate(getUsername(), key, session)) {
return Boolean.FALSE;
}
@@ -80,16 +87,16 @@ public class UserAuthPublicKey extends AbstractUserAuth {
Buffer buf = new ByteArrayBuffer();
buf.putBytes(session.getKex().getH());
buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
- buf.putString(getUserName());
+ buf.putString(getUsername());
buf.putString(getService());
- buf.putString(UserAuthPublicKeyFactory.NAME);
+ buf.putString(getName());
buf.putBoolean(true);
buf.putString(alg);
buffer.rpos(oldPos);
buffer.wpos(oldPos + 4 + len);
buf.putBuffer(buffer);
- verif.update(buf.array(), buf.rpos(), buf.available());
- if (!verif.verify(sig)) {
+ verifier.update(buf.array(), buf.rpos(), buf.available());
+ if (!verifier.verify(sig)) {
throw new Exception("Key verification failed");
}
return Boolean.TRUE;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/75643fcb/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
index 9f55d4a..b9d6a22 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
@@ -41,7 +41,7 @@ import org.ietf.jgss.Oid;
* <p>Several methods are available for overriding in specific circumstances.</p>
*/
public class UserAuthGSS extends AbstractUserAuth {
-
+ public static final String NAME = UserAuthGSSFactory.NAME;
// Oids for the Kerberos 5 mechanism and principal
public static final Oid KRB5_MECH = createOID("1.2.840.113554.1.2.2");
@@ -53,6 +53,10 @@ public class UserAuthGSS extends AbstractUserAuth {
// Identity from context
private String identity;
+ public UserAuthGSS() {
+ super(NAME);
+ }
+
/**
* Handle the first authentication step.
*/
@@ -74,7 +78,7 @@ public class UserAuthGSS extends AbstractUserAuth {
// Validate initial user before proceeding
- if (!auth.validateInitialUser(session, getUserName())) {
+ if (!auth.validateInitialUser(session, getUsername())) {
return Boolean.FALSE;
}
@@ -109,7 +113,9 @@ public class UserAuthGSS extends AbstractUserAuth {
"Packet not supported by user authentication method: " + msg);
}
- log.debug("In krb5.next: msg = " + msg);
+ if (log.isDebugEnabled()) {
+ log.debug("doAuth({}@{}) In krb5.next: msg = {}", getUsername(), session, msg);
+ }
// If the context is established, this must be a MIC message
@@ -125,22 +131,23 @@ public class UserAuthGSS extends AbstractUserAuth {
msgbuf.putBytes(ValidateUtils.checkNotNullAndNotEmpty(session.getSessionId(), "No current session ID"));
msgbuf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
- msgbuf.putString(getUserName());
+ msgbuf.putString(getUsername());
msgbuf.putString(getService());
- msgbuf.putString(UserAuthGSSFactory.NAME);
+ msgbuf.putString(getName());
byte[] msgbytes = msgbuf.getCompactData();
byte[] inmic = buffer.getBytes();
try {
context.verifyMIC(inmic, 0, inmic.length, msgbytes, 0, msgbytes.length, new MessageProp(false));
- log.debug("MIC verified");
+ if (log.isDebugEnabled()) {
+ log.debug("doAuth({}@{}) MIC verified", getUsername(), session);
+ }
return Boolean.TRUE;
} catch (GSSException e) {
- log.info("GSS verification error: {}", e.toString());
+ log.info("doAuth({}@{}) GSS verification error: {}", getUsername(), session, e.toString());
return Boolean.FALSE;
}
-
} else {
// Not established - new token to process
@@ -151,7 +158,7 @@ public class UserAuthGSS extends AbstractUserAuth {
// Validate identity if context is now established
- if (established && identity == null) {
+ if (established && (identity == null)) {
identity = context.getSrcName().toString();
log.info("GSS identity is {}", identity);
@@ -175,15 +182,9 @@ public class UserAuthGSS extends AbstractUserAuth {
}
}
- /**
- * Get a user name which has been derived from the handshaking process, or the initial name if
- * nothing has been found.
- *
- * @return The user name
- */
@Override
- public String getUserName() {
- return identity != null ? identity : super.getUserName();
+ public String getUsername() {
+ return identity != null ? identity : super.getUsername();
}
/**
@@ -195,7 +196,9 @@ public class UserAuthGSS extends AbstractUserAuth {
try {
context.dispose();
} catch (GSSException e) {
- // ignore
+ if (log.isDebugEnabled()) {
+ log.debug("Failed ({}) to dispose of context: {}", e.getClass().getSimpleName(), e.getMessage());
+ }
} finally {
context = null;
}