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 2016/01/05 07:05:48 UTC
[3/3] mina-sshd git commit: [SSHD-620] Add support for 'hostbased'
authentication
[SSHD-620] Add support for 'hostbased' authentication
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/18335089
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/18335089
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/18335089
Branch: refs/heads/master
Commit: 18335089d97a76c1aec74b4c082f9a3a2bfb3517
Parents: cbff09d
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Jan 5 08:05:18 2016 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Jan 5 08:05:18 2016 +0200
----------------------------------------------------------------------
.../client/ClientAuthenticationManager.java | 4 +-
.../java/org/apache/sshd/client/SshClient.java | 13 +-
.../auth/AuthenticationIdentitiesProvider.java | 1 +
.../client/auth/PasswordIdentityProvider.java | 146 ---------
.../auth/UserAuthKeyboardInteractive.java | 304 ------------------
.../UserAuthKeyboardInteractiveFactory.java | 36 ---
.../sshd/client/auth/UserAuthPassword.java | 135 --------
.../client/auth/UserAuthPasswordFactory.java | 36 ---
.../sshd/client/auth/UserAuthPublicKey.java | 179 -----------
.../client/auth/UserAuthPublicKeyFactory.java | 72 -----
.../sshd/client/auth/UserInteraction.java | 110 -------
.../auth/hostbased/HostKeyIdentityProvider.java | 93 ++++++
.../auth/hostbased/UserAuthHostBased.java | 202 ++++++++++++
.../hostbased/UserAuthHostBasedFactory.java | 86 ++++++
.../keyboard/UserAuthKeyboardInteractive.java | 306 +++++++++++++++++++
.../UserAuthKeyboardInteractiveFactory.java | 38 +++
.../client/auth/keyboard/UserInteraction.java | 110 +++++++
.../auth/password/PasswordIdentityProvider.java | 146 +++++++++
.../client/auth/password/UserAuthPassword.java | 137 +++++++++
.../auth/password/UserAuthPasswordFactory.java | 38 +++
.../client/auth/pubkey/UserAuthPublicKey.java | 178 +++++++++++
.../auth/pubkey/UserAuthPublicKeyFactory.java | 73 +++++
.../client/session/AbstractClientSession.java | 4 +-
.../client/session/ClientUserAuthService.java | 2 +-
.../apache/sshd/common/SshdSocketAddress.java | 299 ++++++++++++++++++
.../sshd/common/auth/UserAuthMethodFactory.java | 5 +
.../apache/sshd/common/util/NumberUtils.java | 32 +-
.../apache/sshd/common/util/SecurityUtils.java | 12 +
.../server/ServerAuthenticationManager.java | 23 +-
.../java/org/apache/sshd/server/SshServer.java | 12 +
.../auth/UserAuthKeyboardInteractive.java | 116 -------
.../UserAuthKeyboardInteractiveFactory.java | 36 ---
.../sshd/server/auth/UserAuthNoneFactory.java | 2 +-
.../sshd/server/auth/UserAuthPassword.java | 142 ---------
.../server/auth/UserAuthPasswordFactory.java | 36 ---
.../sshd/server/auth/UserAuthPublicKey.java | 161 ----------
.../server/auth/UserAuthPublicKeyFactory.java | 72 -----
.../AcceptAllHostBasedAuthenticator.java | 31 ++
.../auth/hostbased/HostBasedAuthenticator.java | 45 +++
.../RejectAllHostBasedAuthenticator.java | 31 ++
.../hostbased/StaticHostBasedAuthenticator.java | 72 +++++
.../auth/hostbased/UserAuthHostBased.java | 173 +++++++++++
.../hostbased/UserAuthHostBasedFactory.java | 74 +++++
.../keyboard/UserAuthKeyboardInteractive.java | 115 +++++++
.../UserAuthKeyboardInteractiveFactory.java | 38 +++
.../server/auth/password/UserAuthPassword.java | 141 +++++++++
.../auth/password/UserAuthPasswordFactory.java | 38 +++
.../server/auth/pubkey/UserAuthPublicKey.java | 166 ++++++++++
.../auth/pubkey/UserAuthPublicKeyFactory.java | 73 +++++
.../server/session/AbstractServerSession.java | 12 +
.../apache/sshd/SinglePublicKeyAuthTest.java | 2 +-
.../java/org/apache/sshd/WelcomeBannerTest.java | 2 +-
.../client/ClientAuthenticationManagerTest.java | 5 +-
.../sshd/client/ClientSessionListenerTest.java | 2 +-
.../java/org/apache/sshd/client/ClientTest.java | 35 +--
.../auth/PasswordIdentityProviderTest.java | 1 +
.../sshd/common/auth/AuthenticationTest.java | 73 ++++-
.../deprecated/ClientUserAuthServiceOld.java | 2 +-
.../apache/sshd/deprecated/UserAuthAgent.java | 2 +-
.../deprecated/UserAuthKeyboardInteractive.java | 2 +-
.../sshd/deprecated/UserAuthPublicKey.java | 2 +-
.../java/org/apache/sshd/server/ServerTest.java | 2 +-
62 files changed, 2887 insertions(+), 1649 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
index ab1f154..c48701b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
@@ -23,9 +23,9 @@ import java.security.KeyPair;
import java.util.List;
import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
-import org.apache.sshd.client.auth.PasswordIdentityProvider;
import org.apache.sshd.client.auth.UserAuth;
-import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.auth.keyboard.UserInteraction;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index e8da138..8375c89 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -56,12 +56,12 @@ import java.util.logging.Logger;
import org.apache.sshd.agent.SshAgentFactory;
import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
-import org.apache.sshd.client.auth.PasswordIdentityProvider;
import org.apache.sshd.client.auth.UserAuth;
-import org.apache.sshd.client.auth.UserAuthKeyboardInteractiveFactory;
-import org.apache.sshd.client.auth.UserAuthPasswordFactory;
-import org.apache.sshd.client.auth.UserAuthPublicKeyFactory;
-import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
+import org.apache.sshd.client.auth.keyboard.UserInteraction;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.client.auth.password.UserAuthPasswordFactory;
+import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
@@ -1029,6 +1029,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
break;
}
+ i++;
continue;
}
@@ -1109,7 +1110,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
try {
if (socksPort >= 0) {
- session.startDynamicPortForwarding(new SshdSocketAddress("localhost", socksPort));
+ session.startDynamicPortForwarding(new SshdSocketAddress(SshdSocketAddress.LOCALHOST_NAME, socksPort));
Thread.sleep(Long.MAX_VALUE);
} else {
ClientChannel channel;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
index 2d0637b..56ef102 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -27,6 +27,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
import org.apache.sshd.common.util.GenericUtils;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
deleted file mode 100644
index 26811db..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
+++ /dev/null
@@ -1,146 +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.client.auth;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.Supplier;
-import org.apache.sshd.common.util.Transformer;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PasswordIdentityProvider {
- PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
- @Override
- public Iterable<String> loadPasswords() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * @return The currently available passwords - never {@code null}
- */
- Iterable<String> loadPasswords();
-
- /**
- * A helper class for password identity provider related operations
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
- // CHECKSTYLE:OFF
- final class Utils {
- // CHECKSTYLE:ON
- public static final Transformer<PasswordIdentityProvider, Iterable<String>> LOADER =
- new Transformer<PasswordIdentityProvider, Iterable<String>>() {
- @Override
- public Iterable<String> transform(PasswordIdentityProvider p) {
- return (p == null) ? null : p.loadPasswords();
- }
- };
-
- private Utils() {
- throw new UnsupportedOperationException("No instance allowed");
- }
-
- public static Iterator<String> iteratorOf(ClientSession session) {
- ValidateUtils.checkNotNull(session, "No session");
- return iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
- }
-
- public static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
- return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
- }
-
- /**
- * Resolves a non-{@code null} iterator of the available passwords
- *
- * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null}
- * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
- */
- public static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
- return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
- }
-
- public static PasswordIdentityProvider resolvePasswordIdentityProvider(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
- if ((passwords == null) || (identities == passwords)) {
- return identities;
- } else if (identities == null) {
- return passwords;
- } else {
- return multiProvider(identities, passwords);
- }
- }
-
- public static PasswordIdentityProvider multiProvider(PasswordIdentityProvider ... providers) {
- return multiProvider(GenericUtils.isEmpty(providers) ? Collections.<PasswordIdentityProvider>emptyList() : Arrays.asList(providers));
- }
-
- public static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
- return wrap(iterableOf(providers));
- }
-
- public static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
- if (GenericUtils.isEmpty(providers)) {
- return Collections.emptyList();
- }
-
- Collection<Supplier<Iterable<String>>> suppliers = new ArrayList<Supplier<Iterable<String>>>(providers.size());
- for (final PasswordIdentityProvider p : providers) {
- if (p == null) {
- continue;
- }
-
- suppliers.add(new Supplier<Iterable<String>>() {
- @Override
- public Iterable<String> get() {
- return p.loadPasswords();
- }
- });
- }
-
- if (GenericUtils.isEmpty(suppliers)) {
- return Collections.emptyList();
- }
-
- return GenericUtils.multiIterableSuppliers(suppliers);
- }
-
- public static PasswordIdentityProvider wrap(final Iterable<String> passwords) {
- return new PasswordIdentityProvider() {
- @Override
- public Iterable<String> loadPasswords() {
- return passwords;
- }
- };
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index 9327236..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
+++ /dev/null
@@ -1,304 +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.client.auth;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.client.ClientAuthenticationManager;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.PropertyResolverUtils;
-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;
-
-/**
- * Manages a "keyboard-interactive" exchange according to
- * <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
- *
- * @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 final AtomicBoolean requestPending = new AtomicBoolean(false);
- private final AtomicInteger trialsCount = new AtomicInteger(0);
- private Iterator<String> passwords;
- private int maxTrials;
-
- public UserAuthKeyboardInteractive() {
- super(NAME);
- }
-
- @Override
- public void init(ClientSession session, String service) throws Exception {
- super.init(session, service);
- passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
- maxTrials = PropertyResolverUtils.getIntProperty(session, ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
- ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
- }
-
- @Override
- protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
- String name = getName();
- if (requestPending.get()) {
- if (log.isDebugEnabled()) {
- log.debug("sendAuthDataRequest({})[{}] no reply for previous request for {}",
- session, service, name);
- }
- return false;
- }
-
- if (!verifyTrialsCount(session, service, SshConstants.SSH_MSG_USERAUTH_REQUEST, trialsCount.get(), maxTrials)) {
- return false;
- }
-
- String username = session.getUsername();
- String lang = getExchangeLanguageTag(session);
- String subMethods = getExchangeSubMethods(session);
- if (log.isDebugEnabled()) {
- log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST for {}: lang={}, methods={}",
- session, service, name, lang, subMethods);
- }
-
- Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
- username.length() + service.length() + name.length()
- + GenericUtils.length(lang) + GenericUtils.length(subMethods)
- + Long.SIZE /* a bit extra for the lengths */);
- buffer.putString(username);
- buffer.putString(service);
- buffer.putString(name);
- buffer.putString(lang);
- buffer.putString(subMethods);
- requestPending.set(true);
- session.writePacket(buffer);
- return true;
- }
-
- @Override
- protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
- int cmd = buffer.getUByte();
- if (cmd != SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST) {
- throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
- + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
- }
-
- requestPending.set(false);
-
- if (!verifyTrialsCount(session, service, cmd, trialsCount.incrementAndGet(), maxTrials)) {
- return false;
- }
-
- String name = buffer.getString();
- String instruction = buffer.getString();
- String lang = buffer.getString();
- int num = buffer.getInt();
- if (log.isDebugEnabled()) {
- log.debug("processAuthDataRequest({})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={}, instruction={}, language={}, num-prompts={}",
- session, service, name, instruction, lang, num);
- }
-
- String[] prompt = new String[num];
- boolean[] echo = new boolean[num];
- for (int i = 0; i < num; i++) {
- // according to RFC4256: "The prompt field(s) MUST NOT be empty strings."
- prompt[i] = buffer.getString();
- echo[i] = buffer.getBoolean();
- }
-
- if (log.isTraceEnabled()) {
- log.trace("processAuthDataRequest({})[{}] Prompt: {}", session, service, Arrays.toString(prompt));
- log.trace("processAuthDataRequest({})[{}] Echo: {}", session, service, Arrays.toString(echo));
- }
-
- String[] rep = getUserResponses(name, instruction, lang, prompt, echo);
- if (rep == null) {
- if (log.isDebugEnabled()) {
- log.debug("processAuthDataRequest({})[{}] no responses for {}", session, service, name);
- }
- return false;
- }
-
- /*
- * 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.
- *
- * However it is the server's (!) responsibility to fail, so we only warn...
- */
- if (num != rep.length) {
- log.warn("processAuthDataRequest({})[{}] Mismatched prompts ({}) vs. responses count ({})",
- session, service, num, rep.length);
- }
-
- buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, BufferUtils.clear(buffer));
- buffer.putInt(rep.length);
- for (int index = 0; index < rep.length; index++) {
- String r = rep[index];
- if (log.isTraceEnabled()) {
- log.trace("processAuthDataRequest({})[{}] response #{}: {}", session, service, index + 1, r);
- }
- buffer.putString(r);
- }
-
- session.writePacket(buffer);
- return true;
- }
-
- protected String getExchangeLanguageTag(ClientSession session) {
- return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_LANGUAGE_TAG, DEFAULT_INTERACTIVE_LANGUAGE_TAG);
- }
-
- protected String getExchangeSubMethods(ClientSession session) {
- return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_SUBMETHODS, DEFAULT_INTERACTIVE_SUBMETHODS);
- }
-
- protected String getCurrentPasswordCandidate() {
- if ((passwords != null) && passwords.hasNext()) {
- return passwords.next();
- } else {
- return null;
- }
- }
-
- protected boolean verifyTrialsCount(ClientSession session, String service, int cmd, int nbTrials, int maxAllowed) {
- if (log.isDebugEnabled()) {
- log.debug("verifyTrialsCount({})[{}] cmd={} - {} out of {}",
- session, service, getAuthCommandName(cmd), nbTrials, maxAllowed);
- }
-
- return nbTrials <= maxAllowed;
- }
-
- /**
- * @param name The interaction name - may be empty
- * @param instruction The instruction - may be empty
- * @param lang The language tag - may be empty
- * @param prompt The prompts - may be empty
- * @param echo Whether to echo the response for the prompt or not - same
- * length as the prompts
- * @return The response for each prompt - if {@code null} then the assumption
- * is that some internal error occurred and no response is sent. <B>Note:</B>
- * according to <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
- * the number of responses should be <U>exactly</U> the same as the number
- * of prompts. However, since it is the <U>server's</U> responsibility to
- * enforce this we do not validate the response (other than logging it as
- * a warning...)
- */
- protected String[] getUserResponses(String name, String instruction, String lang, String[] prompt, boolean[] echo) {
- ClientSession session = getClientSession();
- int num = GenericUtils.length(prompt);
- if (num == 0) {
- if (log.isDebugEnabled()) {
- log.debug("getUserResponses({}) no prompts for interaction={}", session, name);
- }
- return GenericUtils.EMPTY_STRING_ARRAY;
- }
-
- String candidate = getCurrentPasswordCandidate();
- if (useCurrentPassword(candidate, name, instruction, lang, prompt, echo)) {
- if (log.isDebugEnabled()) {
- log.debug("getUserResponses({}) use password candidate for interaction={}", session, name);
- }
- return new String[]{candidate};
- }
-
- UserInteraction ui = session.getUserInteraction();
- if ((ui != null) && ui.isInteractionAllowed(session)) {
- return ui.interactive(session, name, instruction, lang, prompt, echo);
- }
-
- if (log.isDebugEnabled()) {
- log.debug("getUserResponses({}) no user interaction for name={}", session, name);
- }
-
- return null;
- }
-
- protected boolean useCurrentPassword(String password, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
- int num = GenericUtils.length(prompt);
- if ((num != 1) || (password == null) || echo[0]) {
- return false;
- }
-
- // check that prompt is something like "XXX password YYY:"
- String value = GenericUtils.trimToEmpty(prompt[0]).toLowerCase();
- int passPos = value.lastIndexOf("password");
- if (passPos < 0) { // no password keyword in prompt
- return false;
- }
-
- int sepPos = value.lastIndexOf(':');
- if (sepPos <= passPos) { // no prompt separator or separator before the password keyword
- return false;
- }
-
- return true;
- }
-
- public static String getAuthCommandName(int cmd) {
- switch(cmd) {
- case SshConstants.SSH_MSG_USERAUTH_REQUEST:
- return "SSH_MSG_USERAUTH_REQUEST";
- case SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST:
- return "SSH_MSG_USERAUTH_INFO_REQUEST";
- default:
- return SshConstants.getCommandMessageName(cmd);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java
deleted file mode 100644
index 5cf1068..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java
+++ /dev/null
@@ -1,36 +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.client.auth;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthKeyboardInteractiveFactory extends AbstractUserAuthFactory {
- public static final String NAME = KB_INTERACTIVE;
- public static final UserAuthKeyboardInteractiveFactory INSTANCE = new UserAuthKeyboardInteractiveFactory();
-
- public UserAuthKeyboardInteractiveFactory() {
- super(NAME);
- }
-
- @Override
- public UserAuth create() {
- return new UserAuthKeyboardInteractive();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index d4ed6f7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
+++ /dev/null
@@ -1,135 +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.client.auth;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Objects;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * Implements the "password" authentication mechanism
- * @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(NAME);
- }
-
- @Override
- public void init(ClientSession session, String service) throws Exception {
- super.init(session, service);
- passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
- }
-
- @Override
- protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
- if ((passwords == null) || (!passwords.hasNext())) {
- if (log.isDebugEnabled()) {
- log.debug("sendAuthDataRequest({})[{}] no more passwords to send", session, service);
- }
-
- return false;
- }
-
- current = passwords.next();
- String username = session.getUsername();
- Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
- username.length() + service.length() + getName().length() + current.length() + Integer.SIZE);
- sendPassword(buffer, session, current, current);
- return true;
- }
-
- @Override
- protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
- int cmd = buffer.getUByte();
- if (cmd != SshConstants.SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) {
- throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
- + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
- }
-
- String prompt = buffer.getString();
- String lang = buffer.getString();
- UserInteraction ui = session.getUserInteraction();
- if ((ui != null) && ui.isInteractionAllowed(session)) {
- String password = ui.getUpdatedPassword(session, prompt, lang);
- if (GenericUtils.isEmpty(password)) {
- if (log.isDebugEnabled()) {
- log.debug("processAuthDataRequest({})[{}] No updated password for prompt={}, lang={}",
- session, service, prompt, lang);
- }
- } else {
- sendPassword(buffer, session, password, password);
- return true;
- }
- } else {
- if (log.isDebugEnabled()) {
- log.debug("processAuthDataRequest({})[{}] no UI for password change request for prompt={}, lang={}",
- session, service, prompt, lang);
- }
- }
-
- return false;
- }
-
- /**
- * Sends the password via a {@code SSH_MSG_USERAUTH_REQUEST} message.
- * If old and new password are not the same then it requests a password
- * modification from the server (which may be denied if the server does
- * not support this feature).
- *
- * @param buffer The {@link Buffer} to re-use for sending the message
- * @param session The target {@link ClientSession}
- * @param oldPassword The previous password
- * @param newPassword The new password
- * @throws IOException If failed to send the message.
- */
- 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 {} - modified={}",
- session, service, name, modified);
- }
-
- buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
- buffer.putString(username);
- buffer.putString(service);
- buffer.putString(name);
- buffer.putBoolean(modified);
- // see RFC-4252 section 8
- buffer.putString(oldPassword);
- if (modified) {
- buffer.putString(newPassword);
- }
- session.writePacket(buffer);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java
deleted file mode 100644
index 14ecfb0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java
+++ /dev/null
@@ -1,36 +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.client.auth;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPasswordFactory extends AbstractUserAuthFactory {
- public static final String NAME = PASSWORD;
- public static final UserAuthPasswordFactory INSTANCE = new UserAuthPasswordFactory();
-
- public UserAuthPasswordFactory() {
- super(NAME);
- }
-
- @Override
- public UserAuth create() {
- return new UserAuthPassword();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index f800cad..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
+++ /dev/null
@@ -1,179 +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.client.auth;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.security.PublicKey;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
-import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator;
-import org.apache.sshd.client.session.ClientSession;
-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.signature.Signature;
-import org.apache.sshd.common.signature.SignatureFactoriesManager;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-
-/**
- * Implements the "publickey" authentication mechanism
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFactoriesManager {
- public static final String NAME = UserAuthPublicKeyFactory.NAME;
-
- private Iterator<PublicKeyIdentity> keys;
- private PublicKeyIdentity current;
- private List<NamedFactory<Signature>> factories;
-
- public UserAuthPublicKey() {
- this(null);
- }
-
- public UserAuthPublicKey(List<NamedFactory<Signature>> factories) {
- super(NAME);
- this.factories = factories; // OK if null/empty
- }
-
- @Override
- public List<NamedFactory<Signature>> getSignatureFactories() {
- return factories;
- }
-
- @Override
- public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
- this.factories = factories;
- }
-
- @Override
- public void init(ClientSession session, String service) throws Exception {
- super.init(session, service);
- releaseKeys(); // just making sure in case multiple calls to the method
- keys = new UserAuthPublicKeyIterator(session, this);
- }
-
- @Override
- protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
- if ((keys == null) || (!keys.hasNext())) {
- if (log.isDebugEnabled()) {
- log.debug("sendAuthDataRequest({})[{}] no more keys to send", session, service);
- }
-
- return false;
- }
-
- current = keys.next();
- if (log.isTraceEnabled()) {
- log.trace("sendAuthDataRequest({})[{}] current key details: {}", session, service, current);
- }
-
- PublicKey key = current.getPublicKey();
- String algo = KeyUtils.getKeyType(key);
- String name = getName();
- if (log.isDebugEnabled()) {
- log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}",
- session, service, name, algo, KeyUtils.getFingerPrint(key));
- }
- Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
- buffer.putString(session.getUsername());
- buffer.putString(service);
- buffer.putString(name);
- buffer.putBoolean(false);
- buffer.putString(algo);
- buffer.putPublicKey(key);
- session.writePacket(buffer);
- return true;
- }
-
- @Override
- protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
- int cmd = buffer.getUByte();
- if (cmd != SshConstants.SSH_MSG_USERAUTH_PK_OK) {
- throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
- + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
- }
-
- PublicKey key = current.getPublicKey();
- String algo = KeyUtils.getKeyType(key);
- String name = getName();
- if (log.isDebugEnabled()) {
- log.debug("processAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_PK_OK reply {} type={} - fingerprint={}",
- session, service, name, algo, KeyUtils.getFingerPrint(key));
- }
-
- String username = session.getUsername();
- buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
- buffer.putString(username);
- buffer.putString(service);
- buffer.putString(name);
- buffer.putBoolean(true);
- buffer.putString(algo);
- buffer.putPublicKey(key);
-
- Buffer bs = new ByteArrayBuffer();
- KeyExchange kex = session.getKex();
- bs.putBytes(kex.getH());
- bs.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
- bs.putString(username);
- bs.putString(service);
- bs.putString(name);
- bs.putBoolean(true);
- bs.putString(algo);
- bs.putPublicKey(key);
-
- byte[] sig = current.sign(bs.getCompactData());
- bs = new ByteArrayBuffer(algo.length() + sig.length + Long.SIZE, false);
- bs.putString(algo);
- bs.putBytes(sig);
- buffer.putBytes(bs.array(), bs.rpos(), bs.available());
-
- session.writePacket(buffer);
- return true;
- }
-
- @Override
- public void destroy() {
- try {
- releaseKeys();
- } catch (IOException e) {
- throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ") to close agent: " + e.getMessage(), e);
- }
-
- super.destroy(); // for logging
- }
-
- protected void releaseKeys() throws IOException {
- try {
- if (keys instanceof Closeable) {
- if (log.isTraceEnabled()) {
- log.trace("releaseKeys({}) closing {}", getClientSession(), keys);
- }
- ((Closeable) keys).close();
- }
- } finally {
- keys = null;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java
deleted file mode 100644
index f7ccd54..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java
+++ /dev/null
@@ -1,72 +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.client.auth;
-
-import java.util.List;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.signature.Signature;
-import org.apache.sshd.common.signature.SignatureFactoriesManager;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPublicKeyFactory extends AbstractUserAuthFactory implements SignatureFactoriesManager {
- public static final String NAME = PUBLIC_KEY;
- public static final UserAuthPublicKeyFactory INSTANCE = new UserAuthPublicKeyFactory() {
- @Override
- public List<NamedFactory<Signature>> getSignatureFactories() {
- return null;
- }
-
- @Override
- public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
- if (!GenericUtils.isEmpty(factories)) {
- throw new UnsupportedOperationException("Not allowed to change default instance signature factories");
- }
- }
- };
-
- private List<NamedFactory<Signature>> factories;
-
- public UserAuthPublicKeyFactory() {
- this(null);
- }
-
- public UserAuthPublicKeyFactory(List<NamedFactory<Signature>> factories) {
- super(NAME);
- this.factories = factories; // OK if null/empty
- }
-
- @Override
- public List<NamedFactory<Signature>> getSignatureFactories() {
- return factories;
- }
-
- @Override
- public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
- this.factories = factories;
- }
-
- @Override
- public UserAuth create() {
- return new UserAuthPublicKey(getSignatureFactories());
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
deleted file mode 100644
index 700055a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
+++ /dev/null
@@ -1,110 +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.client.auth;
-
-import org.apache.sshd.client.session.ClientSession;
-
-/**
- * Interface used by the ssh client to communicate with the end user.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <a href="https://www.ietf.org/rfc/rfc4256.txt">RFC 4256</A>
- */
-public interface UserInteraction {
- /**
- * A useful "placeholder" that indicates that no interaction is expected.
- * <B>Note:</B> throws {@link IllegalStateException} is any of the interaction
- * methods is called
- */
- UserInteraction NONE = new UserInteraction() {
- @Override
- public boolean isInteractionAllowed(ClientSession session) {
- return false;
- }
-
- @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 IllegalStateException("interactive(" + session + ")[" + name + "] unexpected call");
- }
-
- @Override
- public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
- throw new IllegalStateException("getUpdatedPassword(" + session + ")[" + prompt + "] unexpected call");
- }
-
- @Override
- public String toString() {
- return "NONE";
- }
- };
-
- /**
- *
- * @param session The {@link ClientSession}
- * @return {@code true} if user interaction allowed for this session
- */
- boolean isInteractionAllowed(ClientSession session);
-
- /**
- * Displays the welcome banner to the user.
- *
- * @param session The {@link ClientSession} through which the banner was received
- * @param banner The welcome banner
- * @param lang The banner language code - may be empty
- */
- void welcome(ClientSession session, String banner, String lang);
-
- /**
- * Invoked when "keyboard-interactive" authentication mechanism
- * is used in order to provide responses for the server's challenges
- * (a.k.a. prompts)
- *
- * @param session The {@link ClientSession} through which the request was received
- * @param name The interaction name (may be empty)
- * @param instruction The instruction (may be empty)
- * @param lang The language for the data (may be empty)
- * @param prompt The prompts to be displayed (may be empty)
- * @param echo For each prompt whether to echo the user's response
- * @return The replies - <B>Note:</B> the protocol states that the number
- * of replies should be <U>exactly</U> the same as the number of prompts,
- * however we do not enforce it since it is defined as the <U>server's</U>
- * job to check and manage this violation.
- */
- String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo);
-
- /**
- * Invoked when the server returns an {@code SSH_MSG_USERAUTH_PASSWD_CHANGEREQ}
- * response indicating that the password should be changed - e.g., expired or
- * not strong enough (as per the server's policy).
- *
- * @param session The {@link ClientSession} through which the request was received
- * @param prompt The server's prompt (may be empty)
- * @param lang The prompt's language (may be empty)
- * @return The password to use - if {@code null}/empty then no updated
- * password was provided - thus failing the authentication via passwords
- * (<B>Note:</B> authentication might still succeed via some other means -
- * be it other passwords, public keys, etc...)
- */
- String getUpdatedPassword(ClientSession session, String prompt, String lang);
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
new file mode 100644
index 0000000..7819422
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
@@ -0,0 +1,93 @@
+/*
+ * 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.hostbased;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Pair;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface HostKeyIdentityProvider {
+ /**
+ * @return The host keys as a {@link Pair} of key + certificates (which can be {@code null}/empty)
+ */
+ Iterable<Pair<KeyPair, List<X509Certificate>>> loadHostKeys();
+
+ /**
+ * A helper class for key identity provider related operations
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ // CHECKSTYLE:OFF
+ final class Utils {
+ // CHECKSTYLE:ON
+ private Utils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ public static Iterator<Pair<KeyPair, List<X509Certificate>>> iteratorOf(HostKeyIdentityProvider provider) {
+ return GenericUtils.iteratorOf((provider == null) ? null : provider.loadHostKeys());
+ }
+
+ public static HostKeyIdentityProvider wrap(KeyPair ... pairs) {
+ return wrap(GenericUtils.isEmpty(pairs) ? Collections.<KeyPair>emptyList() : Arrays.asList(pairs));
+ }
+
+ public static HostKeyIdentityProvider wrap(final Iterable<? extends KeyPair> pairs) {
+ return new HostKeyIdentityProvider() {
+ @Override
+ public Iterable<Pair<KeyPair, List<X509Certificate>>> loadHostKeys() {
+ return new Iterable<Pair<KeyPair, List<X509Certificate>>>() {
+ @Override
+ public Iterator<Pair<KeyPair, List<X509Certificate>>> iterator() {
+ final Iterator<? extends KeyPair> iter = GenericUtils.iteratorOf(pairs);
+ return new Iterator<Pair<KeyPair, List<X509Certificate>>>() {
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public Pair<KeyPair, List<X509Certificate>> next() {
+ KeyPair kp = iter.next();
+ return new Pair<KeyPair, List<X509Certificate>>(kp, Collections.<X509Certificate>emptyList());
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("No removal allowed");
+ }
+ };
+ }
+ };
+ }
+
+ };
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java
new file mode 100644
index 0000000..a8313f3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java
@@ -0,0 +1,202 @@
+/*
+ * 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.hostbased;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.sshd.client.auth.AbstractUserAuth;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshdSocketAddress;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesManager;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthHostBased extends AbstractUserAuth implements SignatureFactoriesManager {
+ public static final String NAME = UserAuthHostBasedFactory.NAME;
+
+ private Iterator<Pair<KeyPair, List<X509Certificate>>> keys;
+ private final HostKeyIdentityProvider clientHostKeys;
+ private List<NamedFactory<Signature>> factories;
+ private String clientUsername;
+ private String clientHostname;
+
+ public UserAuthHostBased(HostKeyIdentityProvider clientHostKeys) {
+ super(NAME);
+ this.clientHostKeys = clientHostKeys; // OK if null
+ }
+
+ @Override
+ public void init(ClientSession session, String service) throws Exception {
+ super.init(session, service);
+ keys = HostKeyIdentityProvider.Utils.iteratorOf(clientHostKeys); // in case multiple calls to the method
+ }
+
+ @Override
+ public List<NamedFactory<Signature>> getSignatureFactories() {
+ return factories;
+ }
+
+ @Override
+ public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
+ this.factories = factories;
+ }
+
+ public String getClientUsername() {
+ return clientUsername;
+ }
+
+ public void setClientUsername(String clientUsername) {
+ this.clientUsername = clientUsername;
+ }
+
+ public String getClientHostname() {
+ return clientHostname;
+ }
+
+ public void setClientHostname(String clientHostname) {
+ this.clientHostname = clientHostname;
+ }
+
+ @Override
+ protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
+ if ((keys == null) || (!keys.hasNext())) {
+ if (log.isDebugEnabled()) {
+ log.debug("sendAuthDataRequest({})[{}] no more keys to send", session, service);
+ }
+
+ return false;
+ }
+
+ Pair<KeyPair, List<X509Certificate>> keyInfo = keys.next();
+ KeyPair kp = keyInfo.getFirst();
+ PublicKey pub = kp.getPublic();
+ String keyType = KeyUtils.getKeyType(pub);
+ if (log.isTraceEnabled()) {
+ log.trace("sendAuthDataRequest({})[{}] current key details: type={}, fingerprint={}",
+ session, service, keyType, KeyUtils.getFingerPrint(pub));
+ }
+
+ Collection<NamedFactory<Signature>> factories =
+ ValidateUtils.checkNotNullAndNotEmpty(
+ SignatureFactoriesManager.Utils.resolveSignatureFactories(this, session),
+ "No signature factories for session=%s",
+ session);
+ Signature verifier = ValidateUtils.checkNotNull(
+ NamedFactory.Utils.create(factories, keyType),
+ "No signer could be located for key type=%s",
+ keyType);
+
+ byte[] id = session.getSessionId();
+ String username = session.getUsername();
+ String clientUsername = resolveClientUsername();
+ String clientHostname = resolveClientHostname();
+ if (log.isDebugEnabled()) {
+ log.debug("sendAuthDataRequest({})[{}] client={}@{}",
+ session, service, clientUsername, clientHostname);
+ }
+
+ Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
+ id.length + username.length() + service.length() + clientUsername.length() + clientHostname.length()
+ + keyType.length() + ByteArrayBuffer.DEFAULT_SIZE + Long.SIZE);
+ buffer.clear();
+
+ buffer.putRawPublicKey(pub);
+
+ List<X509Certificate> certs = keyInfo.getSecond();
+ if (GenericUtils.size(certs) > 0) {
+ for (X509Certificate c : certs) {
+ // TODO make sure this yields DER encoding
+ buffer.putRawBytes(c.getEncoded());
+ }
+ }
+ byte[] keyBytes = buffer.getCompactData();
+
+ buffer.clear();
+ buffer.putBytes(id);
+ buffer.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
+ buffer.putString(username);
+ buffer.putString(getService());
+ buffer.putString(getName());
+ buffer.putString(keyType);
+ buffer.putBytes(keyBytes);
+ buffer.putString(clientHostname);
+ buffer.putString(clientUsername);
+
+ verifier.initSigner(kp.getPrivate());
+ verifier.update(buffer.array(), buffer.rpos(), buffer.available());
+ byte[] signature = verifier.sign();
+ if (log.isTraceEnabled()) {
+ log.trace("sendAuthDataRequest({})[{}] type={}, fingerprint={}, client={}@{}: signature={}",
+ session, service, keyType, KeyUtils.getFingerPrint(pub),
+ clientUsername, clientHostname, BufferUtils.printHex(signature));
+ }
+
+ buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, buffer);
+ buffer.putString(username);
+ buffer.putString(getService());
+ buffer.putString(getName());
+ buffer.putString(keyType);
+ buffer.putBytes(keyBytes);
+ buffer.putString(clientHostname);
+ buffer.putString(clientUsername);
+ buffer.putBytes(signature);
+
+ session.writePacket(buffer);
+ return true;
+ }
+
+ @Override
+ protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
+ int cmd = buffer.getUByte();
+ throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
+ + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
+ }
+
+ protected String resolveClientUsername() {
+ String value = getClientUsername();
+ return GenericUtils.isEmpty(value) ? OsUtils.getCurrentUser() : value;
+ }
+
+ protected String resolveClientHostname() {
+ String value = getClientHostname();
+ if (GenericUtils.isEmpty(value)) {
+ value = SshdSocketAddress.toAddressString(SshdSocketAddress.getFirstExternalNetwork4Address());
+ }
+
+ return GenericUtils.isEmpty(value) ? SshdSocketAddress.LOCALHOST_IP : value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java
new file mode 100644
index 0000000..8ead888
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java
@@ -0,0 +1,86 @@
+/*
+ * 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.hostbased;
+
+import java.util.List;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesManager;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthHostBasedFactory extends AbstractUserAuthFactory implements SignatureFactoriesManager {
+ public static final String NAME = HOST_BASED;
+
+ private List<NamedFactory<Signature>> factories;
+ private HostKeyIdentityProvider clientHostKeys;
+ private String clientUsername;
+ private String clientHostname;
+
+ public UserAuthHostBasedFactory() {
+ super(NAME);
+ }
+
+ @Override
+ public List<NamedFactory<Signature>> getSignatureFactories() {
+ return factories;
+ }
+
+ @Override
+ public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
+ this.factories = factories;
+ }
+
+ public HostKeyIdentityProvider getClientHostKeys() {
+ return clientHostKeys;
+ }
+
+ public void setClientHostKeys(HostKeyIdentityProvider clientHostKeys) {
+ this.clientHostKeys = clientHostKeys;
+ }
+
+ public String getClientUsername() {
+ return clientUsername;
+ }
+
+ public void setClientUsername(String clientUsername) {
+ this.clientUsername = clientUsername;
+ }
+
+ public String getClientHostname() {
+ return clientHostname;
+ }
+
+ public void setClientHostname(String clientHostname) {
+ this.clientHostname = clientHostname;
+ }
+
+ @Override
+ public UserAuthHostBased create() {
+ UserAuthHostBased auth = new UserAuthHostBased(getClientHostKeys());
+ auth.setClientHostname(getClientHostname());
+ auth.setClientUsername(getClientUsername());
+ auth.setSignatureFactories(getSignatureFactories());
+ return auth;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
new file mode 100644
index 0000000..608f451
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
@@ -0,0 +1,306 @@
+/*
+ * 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.keyboard;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.client.ClientAuthenticationManager;
+import org.apache.sshd.client.auth.AbstractUserAuth;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.PropertyResolverUtils;
+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;
+
+/**
+ * Manages a "keyboard-interactive" exchange according to
+ * <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
+ *
+ * @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 final AtomicBoolean requestPending = new AtomicBoolean(false);
+ private final AtomicInteger trialsCount = new AtomicInteger(0);
+ private Iterator<String> passwords;
+ private int maxTrials;
+
+ public UserAuthKeyboardInteractive() {
+ super(NAME);
+ }
+
+ @Override
+ public void init(ClientSession session, String service) throws Exception {
+ super.init(session, service);
+ passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
+ maxTrials = PropertyResolverUtils.getIntProperty(session, ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
+ ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
+ }
+
+ @Override
+ protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
+ String name = getName();
+ if (requestPending.get()) {
+ if (log.isDebugEnabled()) {
+ log.debug("sendAuthDataRequest({})[{}] no reply for previous request for {}",
+ session, service, name);
+ }
+ return false;
+ }
+
+ if (!verifyTrialsCount(session, service, SshConstants.SSH_MSG_USERAUTH_REQUEST, trialsCount.get(), maxTrials)) {
+ return false;
+ }
+
+ String username = session.getUsername();
+ String lang = getExchangeLanguageTag(session);
+ String subMethods = getExchangeSubMethods(session);
+ if (log.isDebugEnabled()) {
+ log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST for {}: lang={}, methods={}",
+ session, service, name, lang, subMethods);
+ }
+
+ Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
+ username.length() + service.length() + name.length()
+ + GenericUtils.length(lang) + GenericUtils.length(subMethods)
+ + Long.SIZE /* a bit extra for the lengths */);
+ buffer.putString(username);
+ buffer.putString(service);
+ buffer.putString(name);
+ buffer.putString(lang);
+ buffer.putString(subMethods);
+ requestPending.set(true);
+ session.writePacket(buffer);
+ return true;
+ }
+
+ @Override
+ protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
+ int cmd = buffer.getUByte();
+ if (cmd != SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST) {
+ throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
+ + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
+ }
+
+ requestPending.set(false);
+
+ if (!verifyTrialsCount(session, service, cmd, trialsCount.incrementAndGet(), maxTrials)) {
+ return false;
+ }
+
+ String name = buffer.getString();
+ String instruction = buffer.getString();
+ String lang = buffer.getString();
+ int num = buffer.getInt();
+ if (log.isDebugEnabled()) {
+ log.debug("processAuthDataRequest({})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={}, instruction={}, language={}, num-prompts={}",
+ session, service, name, instruction, lang, num);
+ }
+
+ String[] prompt = new String[num];
+ boolean[] echo = new boolean[num];
+ for (int i = 0; i < num; i++) {
+ // according to RFC4256: "The prompt field(s) MUST NOT be empty strings."
+ prompt[i] = buffer.getString();
+ echo[i] = buffer.getBoolean();
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("processAuthDataRequest({})[{}] Prompt: {}", session, service, Arrays.toString(prompt));
+ log.trace("processAuthDataRequest({})[{}] Echo: {}", session, service, Arrays.toString(echo));
+ }
+
+ String[] rep = getUserResponses(name, instruction, lang, prompt, echo);
+ if (rep == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("processAuthDataRequest({})[{}] no responses for {}", session, service, name);
+ }
+ return false;
+ }
+
+ /*
+ * 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.
+ *
+ * However it is the server's (!) responsibility to fail, so we only warn...
+ */
+ if (num != rep.length) {
+ log.warn("processAuthDataRequest({})[{}] Mismatched prompts ({}) vs. responses count ({})",
+ session, service, num, rep.length);
+ }
+
+ buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, BufferUtils.clear(buffer));
+ buffer.putInt(rep.length);
+ for (int index = 0; index < rep.length; index++) {
+ String r = rep[index];
+ if (log.isTraceEnabled()) {
+ log.trace("processAuthDataRequest({})[{}] response #{}: {}", session, service, index + 1, r);
+ }
+ buffer.putString(r);
+ }
+
+ session.writePacket(buffer);
+ return true;
+ }
+
+ protected String getExchangeLanguageTag(ClientSession session) {
+ return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_LANGUAGE_TAG, DEFAULT_INTERACTIVE_LANGUAGE_TAG);
+ }
+
+ protected String getExchangeSubMethods(ClientSession session) {
+ return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_SUBMETHODS, DEFAULT_INTERACTIVE_SUBMETHODS);
+ }
+
+ protected String getCurrentPasswordCandidate() {
+ if ((passwords != null) && passwords.hasNext()) {
+ return passwords.next();
+ } else {
+ return null;
+ }
+ }
+
+ protected boolean verifyTrialsCount(ClientSession session, String service, int cmd, int nbTrials, int maxAllowed) {
+ if (log.isDebugEnabled()) {
+ log.debug("verifyTrialsCount({})[{}] cmd={} - {} out of {}",
+ session, service, getAuthCommandName(cmd), nbTrials, maxAllowed);
+ }
+
+ return nbTrials <= maxAllowed;
+ }
+
+ /**
+ * @param name The interaction name - may be empty
+ * @param instruction The instruction - may be empty
+ * @param lang The language tag - may be empty
+ * @param prompt The prompts - may be empty
+ * @param echo Whether to echo the response for the prompt or not - same
+ * length as the prompts
+ * @return The response for each prompt - if {@code null} then the assumption
+ * is that some internal error occurred and no response is sent. <B>Note:</B>
+ * according to <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
+ * the number of responses should be <U>exactly</U> the same as the number
+ * of prompts. However, since it is the <U>server's</U> responsibility to
+ * enforce this we do not validate the response (other than logging it as
+ * a warning...)
+ */
+ protected String[] getUserResponses(String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+ ClientSession session = getClientSession();
+ int num = GenericUtils.length(prompt);
+ if (num == 0) {
+ if (log.isDebugEnabled()) {
+ log.debug("getUserResponses({}) no prompts for interaction={}", session, name);
+ }
+ return GenericUtils.EMPTY_STRING_ARRAY;
+ }
+
+ String candidate = getCurrentPasswordCandidate();
+ if (useCurrentPassword(candidate, name, instruction, lang, prompt, echo)) {
+ if (log.isDebugEnabled()) {
+ log.debug("getUserResponses({}) use password candidate for interaction={}", session, name);
+ }
+ return new String[]{candidate};
+ }
+
+ UserInteraction ui = session.getUserInteraction();
+ if ((ui != null) && ui.isInteractionAllowed(session)) {
+ return ui.interactive(session, name, instruction, lang, prompt, echo);
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("getUserResponses({}) no user interaction for name={}", session, name);
+ }
+
+ return null;
+ }
+
+ protected boolean useCurrentPassword(String password, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+ int num = GenericUtils.length(prompt);
+ if ((num != 1) || (password == null) || echo[0]) {
+ return false;
+ }
+
+ // check that prompt is something like "XXX password YYY:"
+ String value = GenericUtils.trimToEmpty(prompt[0]).toLowerCase();
+ int passPos = value.lastIndexOf("password");
+ if (passPos < 0) { // no password keyword in prompt
+ return false;
+ }
+
+ int sepPos = value.lastIndexOf(':');
+ if (sepPos <= passPos) { // no prompt separator or separator before the password keyword
+ return false;
+ }
+
+ return true;
+ }
+
+ public static String getAuthCommandName(int cmd) {
+ switch(cmd) {
+ case SshConstants.SSH_MSG_USERAUTH_REQUEST:
+ return "SSH_MSG_USERAUTH_REQUEST";
+ case SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST:
+ return "SSH_MSG_USERAUTH_INFO_REQUEST";
+ default:
+ return SshConstants.getCommandMessageName(cmd);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java
new file mode 100644
index 0000000..86bf005
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.keyboard;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthKeyboardInteractiveFactory extends AbstractUserAuthFactory {
+ public static final String NAME = KB_INTERACTIVE;
+ public static final UserAuthKeyboardInteractiveFactory INSTANCE = new UserAuthKeyboardInteractiveFactory();
+
+ public UserAuthKeyboardInteractiveFactory() {
+ super(NAME);
+ }
+
+ @Override
+ public UserAuthKeyboardInteractive create() {
+ return new UserAuthKeyboardInteractive();
+ }
+}
\ No newline at end of file