userauth/ reformat

This commit is contained in:
Shikhar Bhushan
2010-03-02 00:04:18 +01:00
parent a1ab7bdc8f
commit 10067c7baa
18 changed files with 103 additions and 81 deletions

View File

@@ -41,15 +41,11 @@ import net.schmizz.sshj.userauth.method.AuthMethod;
import java.util.Deque; import java.util.Deque;
/** /** User authentication API. See RFC 4252. */
* User authentication API
*
* @see rfc4252
*/
public interface UserAuth { public interface UserAuth {
/** /**
* Attempt to authenticate {@code username} using each of {@link methods} in order. {@code nextService} is the * Attempt to authenticate {@code username} using each of {@code methods} in order. {@code nextService} is the
* {@link net.schmizz.sshj.Service} that will be enabled on successful authentication. * {@link net.schmizz.sshj.Service} that will be enabled on successful authentication.
* <p/> * <p/>
* Authentication fails if there are no method available, i.e. if all the method failed or there were method * Authentication fails if there are no method available, i.e. if all the method failed or there were method
@@ -66,8 +62,8 @@ public interface UserAuth {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
void authenticate(String username, Service nextService, Iterable<AuthMethod> methods) throws UserAuthException, void authenticate(String username, Service nextService, Iterable<AuthMethod> methods)
TransportException; throws UserAuthException, TransportException;
/** /**
* Returns the authentication banner (if any). In some cases this is available even before the first authentication * Returns the authentication banner (if any). In some cases this is available even before the first authentication
@@ -77,15 +73,15 @@ public interface UserAuth {
*/ */
String getBanner(); String getBanner();
/** Returns saved exceptions that might have been ignored because there were more authentication method available. */ /** @return Saved exceptions that might have been ignored because there were more authentication method available. */
Deque<UserAuthException> getSavedExceptions(); Deque<UserAuthException> getSavedExceptions();
/** Returns the {@code timeout} for a method to successfully authenticate before it is abandoned. */ /** @return The {@code timeout} for a method to successfully authenticate before it is abandoned. */
int getTimeout(); int getTimeout();
/** /**
* Returns whether authentication was partially successful. Some server's may be configured to require multiple * @return Whether authentication was partially successful. Some server's may be configured to require multiple
* authentications; and this value will be {@code true} if at least one of the method supplied succeeded. * authentications; and this value will be {@code true} if at least one of the method supplied succeeded.
*/ */
boolean hadPartialSuccess(); boolean hadPartialSuccess();

View File

@@ -40,7 +40,8 @@ import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.SSHException; import net.schmizz.sshj.common.SSHException;
/** User authentication exception */ /** User authentication exception */
public class UserAuthException extends SSHException { public class UserAuthException
extends SSHException {
public static final ExceptionChainer<UserAuthException> chainer = new ExceptionChainer<UserAuthException>() { public static final ExceptionChainer<UserAuthException> chainer = new ExceptionChainer<UserAuthException>() {

View File

@@ -12,26 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* 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 net.schmizz.sshj.userauth; package net.schmizz.sshj.userauth;
@@ -54,14 +34,16 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** {@link UserAuth} implementation. */ /** {@link UserAuth} implementation. */
public class UserAuthImpl extends AbstractService implements UserAuth, AuthParams { public class UserAuthImpl
extends AbstractService
implements UserAuth, AuthParams {
private final Set<String> allowed = new HashSet<String>(); private final Set<String> allowed = new HashSet<String>();
private final Deque<UserAuthException> savedEx = new ArrayDeque<UserAuthException>(); private final Deque<UserAuthException> savedEx = new ArrayDeque<UserAuthException>();
private final Event<UserAuthException> result = new Event<UserAuthException>("userauth result", private final Event<UserAuthException> result = new Event<UserAuthException>("userauth result",
UserAuthException.chainer); UserAuthException.chainer);
private String username; private String username;
private AuthMethod currentMethod; private AuthMethod currentMethod;
@@ -158,7 +140,8 @@ public class UserAuthImpl extends AbstractService implements UserAuth, AuthParam
} }
@Override @Override
public void handle(Message msg, SSHPacket buf) throws SSHException { public void handle(Message msg, SSHPacket buf)
throws SSHException {
if (!msg.in(50, 80)) // ssh-userauth packets have message numbers between 50-80 if (!msg.in(50, 80)) // ssh-userauth packets have message numbers between 50-80
throw new TransportException(DisconnectReason.PROTOCOL_ERROR); throw new TransportException(DisconnectReason.PROTOCOL_ERROR);
@@ -198,7 +181,8 @@ public class UserAuthImpl extends AbstractService implements UserAuth, AuthParam
banner = buf.readString(); banner = buf.readString();
} }
private void gotFailure(SSHPacket buf) throws UserAuthException, TransportException { private void gotFailure(SSHPacket buf)
throws UserAuthException, TransportException {
allowed.clear(); allowed.clear();
allowed.addAll(Arrays.<String>asList(buf.readString().split(","))); allowed.addAll(Arrays.<String>asList(buf.readString().split(",")));
partialSuccess |= buf.readBoolean(); partialSuccess |= buf.readBoolean();
@@ -216,7 +200,8 @@ public class UserAuthImpl extends AbstractService implements UserAuth, AuthParam
result.set(true); result.set(true);
} }
private void gotUnknown(Message msg, SSHPacket buf) throws SSHException { private void gotUnknown(Message msg, SSHPacket buf)
throws SSHException {
if (currentMethod == null || result == null) { if (currentMethod == null || result == null) {
trans.sendUnimplemented(); trans.sendUnimplemented();
return; return;
@@ -239,7 +224,8 @@ public class UserAuthImpl extends AbstractService implements UserAuth, AuthParam
savedEx.push(e); savedEx.push(e);
} }
private boolean tryWith(AuthMethod meth) throws UserAuthException, TransportException { private boolean tryWith(AuthMethod meth)
throws UserAuthException, TransportException {
currentMethod = meth; currentMethod = meth;
result.clear(); result.clear();
meth.init(this); meth.init(this);

View File

@@ -40,10 +40,13 @@ import net.schmizz.sshj.userauth.password.PasswordFinder;
import java.io.File; import java.io.File;
/** A file key provider is initialized with a location of */ /** A file key provider is initialized with a location of */
public interface FileKeyProvider extends KeyProvider { public interface FileKeyProvider
extends KeyProvider {
enum Format { enum Format {
PKCS8, OpenSSH, Unknown PKCS8,
OpenSSH,
Unknown
} }
void init(File location); void init(File location);

View File

@@ -42,7 +42,8 @@ import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
/** A {@link KeyProvider} wrapper around {@link java.security.KeyPair} */ /** A {@link KeyProvider} wrapper around {@link java.security.KeyPair} */
public class KeyPairWrapper implements KeyProvider { public class KeyPairWrapper
implements KeyProvider {
private final KeyPair kp; private final KeyPair kp;
private final KeyType type; private final KeyType type;

View File

@@ -45,12 +45,15 @@ import java.security.PublicKey;
public interface KeyProvider { public interface KeyProvider {
/** Returns the private key. */ /** Returns the private key. */
PrivateKey getPrivate() throws IOException; PrivateKey getPrivate()
throws IOException;
/** Returns the public key. */ /** Returns the public key. */
PublicKey getPublic() throws IOException; PublicKey getPublic()
throws IOException;
/** Returns the {@link KeyType}. */ /** Returns the {@link KeyType}. */
KeyType getType() throws IOException; KeyType getType()
throws IOException;
} }

View File

@@ -35,7 +35,8 @@ public class KeyProviderUtil {
* *
* @throws java.io.IOException * @throws java.io.IOException
*/ */
public static FileKeyProvider.Format detectKeyFileFormat(File location) throws IOException { public static FileKeyProvider.Format detectKeyFileFormat(File location)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(location)); BufferedReader br = new BufferedReader(new FileReader(location));
String firstLine = br.readLine(); String firstLine = br.readLine();
IOUtils.closeQuietly(br); IOUtils.closeQuietly(br);
@@ -51,7 +52,7 @@ public class KeyProviderUtil {
/* /*
* TODO: Tectia, PuTTY (.ppk) ... * TODO: Tectia, PuTTY (.ppk) ...
*/ */
return FileKeyProvider.Format.OpenSSH; return FileKeyProvider.Format.Unknown;
} }
} }

View File

@@ -52,9 +52,11 @@ import java.security.PublicKey;
* *
* @see PKCS8KeyFile * @see PKCS8KeyFile
*/ */
public class OpenSSHKeyFile extends PKCS8KeyFile { public class OpenSSHKeyFile
extends PKCS8KeyFile {
public static class Factory implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> { public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
public FileKeyProvider create() { public FileKeyProvider create() {
return new OpenSSHKeyFile(); return new OpenSSHKeyFile();
@@ -68,7 +70,8 @@ public class OpenSSHKeyFile extends PKCS8KeyFile {
private PublicKey pubKey; private PublicKey pubKey;
@Override @Override
public PublicKey getPublic() throws IOException { public PublicKey getPublic()
throws IOException {
return pubKey != null ? pubKey : super.getPublic(); return pubKey != null ? pubKey : super.getPublic();
} }

View File

@@ -55,9 +55,11 @@ import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
/** Represents a PKCS8-encoded key file. This is the format used by OpenSSH and OpenSSL. */ /** Represents a PKCS8-encoded key file. This is the format used by OpenSSH and OpenSSL. */
public class PKCS8KeyFile implements FileKeyProvider { public class PKCS8KeyFile
implements FileKeyProvider {
public static class Factory implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> { public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
public FileKeyProvider create() { public FileKeyProvider create() {
return new PKCS8KeyFile(); return new PKCS8KeyFile();
} }
@@ -77,15 +79,18 @@ public class PKCS8KeyFile implements FileKeyProvider {
protected char[] passphrase; // for blanking out protected char[] passphrase; // for blanking out
public PrivateKey getPrivate() throws IOException { public PrivateKey getPrivate()
throws IOException {
return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate(); return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate();
} }
public PublicKey getPublic() throws IOException { public PublicKey getPublic()
throws IOException {
return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic(); return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic();
} }
public KeyType getType() throws IOException { public KeyType getType()
throws IOException {
return type != null ? type : (type = KeyType.fromKey(getPublic())); return type != null ? type : (type = KeyType.fromKey(getPublic()));
} }
@@ -111,7 +116,8 @@ public class PKCS8KeyFile implements FileKeyProvider {
}; };
} }
protected KeyPair readKeyPair() throws IOException { protected KeyPair readKeyPair()
throws IOException {
KeyPair kp = null; KeyPair kp = null;
org.bouncycastle.openssl.PasswordFinder pFinder = makeBouncyPasswordFinder(); org.bouncycastle.openssl.PasswordFinder pFinder = makeBouncyPasswordFinder();
PEMReader r = null; PEMReader r = null;

View File

@@ -44,14 +44,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** This abstract class for {@link AuthMethod} implements common or default functionality. */ /** This abstract class for {@link AuthMethod} implements common or default functionality. */
public abstract class AbstractAuthMethod implements AuthMethod { public abstract class AbstractAuthMethod
implements AuthMethod {
/** Logger */ /** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass()); protected final Logger log = LoggerFactory.getLogger(getClass());
private final String name; private final String name;
/** {@link net.schmizz.sshj.userauth.AuthParams} useful for building request. */ /** {@link AuthParams} useful for building request. */
protected AuthParams params; protected AuthParams params;
/** Create with the {@code name} of this authentication method. */ /** Create with the {@code name} of this authentication method. */
@@ -63,7 +64,8 @@ public abstract class AbstractAuthMethod implements AuthMethod {
return name; return name;
} }
public void handle(Message msg, SSHPacket buf) throws UserAuthException, TransportException { public void handle(Message msg, SSHPacket buf)
throws UserAuthException, TransportException {
throw new UserAuthException("Unknown packet received during " + getName() + " auth: " + msg); throw new UserAuthException("Unknown packet received during " + getName() + " auth: " + msg);
} }
@@ -71,7 +73,8 @@ public abstract class AbstractAuthMethod implements AuthMethod {
this.params = params; this.params = params;
} }
public void request() throws UserAuthException, TransportException { public void request()
throws UserAuthException, TransportException {
params.getTransport().write(buildReq()); params.getTransport().write(buildReq());
} }
@@ -80,10 +83,11 @@ public abstract class AbstractAuthMethod implements AuthMethod {
} }
/** /**
* Builds a {@link net.schmizz.sshj.common.SSHPacket} containing the fields common to all authentication method. * Builds a {@link SSHPacket} containing the fields common to all authentication method. Method-specific fields can
* Method-specific fields can further be put into this buffer. * further be put into this buffer.
*/ */
protected SSHPacket buildReq() throws UserAuthException { protected SSHPacket buildReq()
throws UserAuthException {
return new SSHPacket(Message.USERAUTH_REQUEST) // SSH_MSG_USERAUTH_REQUEST return new SSHPacket(Message.USERAUTH_REQUEST) // SSH_MSG_USERAUTH_REQUEST
.putString(params.getUsername()) // username goes first .putString(params.getUsername()) // username goes first
.putString(params.getNextServiceName()) // the service that we'd like on success .putString(params.getNextServiceName()) // the service that we'd like on success

View File

@@ -42,7 +42,8 @@ import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
// TODO check if this even works...! // TODO check if this even works...!
/** Implements the {@code hostbased} SSH authentication method. */ /** Implements the {@code hostbased} SSH authentication method. */
public class AuthHostbased extends KeyedAuthMethod { public class AuthHostbased
extends KeyedAuthMethod {
protected final String hostname; protected final String hostname;
protected final String hostuser; protected final String hostuser;
@@ -59,7 +60,8 @@ public class AuthHostbased extends KeyedAuthMethod {
} }
@Override @Override
protected SSHPacket buildReq() throws UserAuthException { protected SSHPacket buildReq()
throws UserAuthException {
SSHPacket req = putPubKey(super.buildReq()); SSHPacket req = putPubKey(super.buildReq());
req.putString(hostname == null ? params.getTransport().getRemoteHost() : hostname) // req.putString(hostname == null ? params.getTransport().getRemoteHost() : hostname) //
.putString(hostuser); .putString(hostuser);

View File

@@ -45,7 +45,8 @@ import net.schmizz.sshj.userauth.UserAuthException;
* *
* @see net.schmizz.sshj.userauth.UserAuth * @see net.schmizz.sshj.userauth.UserAuth
*/ */
public interface AuthMethod extends SSHPacketHandler { public interface AuthMethod
extends SSHPacketHandler {
/** Returns assigned name of this authentication method */ /** Returns assigned name of this authentication method */
String getName(); String getName();
@@ -61,7 +62,8 @@ public interface AuthMethod extends SSHPacketHandler {
* *
* @throws TransportException * @throws TransportException
*/ */
void request() throws UserAuthException, TransportException; void request()
throws UserAuthException, TransportException;
/** Returns whether authentication should be reattempted if it failed. */ /** Returns whether authentication should be reattempted if it failed. */
boolean shouldRetry(); boolean shouldRetry();

View File

@@ -40,7 +40,8 @@ package net.schmizz.sshj.userauth.method;
* service requested. This method generally fails and is typically used to find out the method allowed by an SSH server * service requested. This method generally fails and is typically used to find out the method allowed by an SSH server
* (sent as part of the {@code SSH_MSG_USERAUTH_FAILURE} packet) * (sent as part of the {@code SSH_MSG_USERAUTH_FAILURE} packet)
*/ */
public class AuthNone extends AbstractAuthMethod { public class AuthNone
extends AbstractAuthMethod {
AuthNone() { AuthNone() {
super("none"); super("none");

View File

@@ -45,7 +45,8 @@ import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.Resource; import net.schmizz.sshj.userauth.password.Resource;
/** Implements the {@code password} authentication method. Password-change request handling is not currently supported. */ /** Implements the {@code password} authentication method. Password-change request handling is not currently supported. */
public class AuthPassword extends AbstractAuthMethod { public class AuthPassword
extends AbstractAuthMethod {
private final PasswordFinder pwdf; private final PasswordFinder pwdf;
private Resource resource; private Resource resource;
@@ -63,7 +64,8 @@ public class AuthPassword extends AbstractAuthMethod {
} }
@Override @Override
public SSHPacket buildReq() throws UserAuthException { public SSHPacket buildReq()
throws UserAuthException {
log.info("Requesting password for " + resource); log.info("Requesting password for " + resource);
char[] password = pwdf.reqPassword(resource); char[] password = pwdf.reqPassword(resource);
if (password == null) if (password == null)
@@ -75,7 +77,8 @@ public class AuthPassword extends AbstractAuthMethod {
} }
@Override @Override
public void handle(Message cmd, SSHPacket buf) throws UserAuthException, TransportException { public void handle(Message cmd, SSHPacket buf)
throws UserAuthException, TransportException {
if (cmd == Message.USERAUTH_60) if (cmd == Message.USERAUTH_60)
throw new UserAuthException("Password change request received; unsupported operation"); throw new UserAuthException("Password change request received; unsupported operation");
else else

View File

@@ -51,7 +51,8 @@ import java.io.IOException;
* request signed with the private key. Therefore, private keys are not requested from the associated {@link * request signed with the private key. Therefore, private keys are not requested from the associated {@link
* KeyProvider} unless needed. * KeyProvider} unless needed.
*/ */
public class AuthPublickey extends KeyedAuthMethod { public class AuthPublickey
extends KeyedAuthMethod {
/** Initialize this method with the provider for public and private key. */ /** Initialize this method with the provider for public and private key. */
public AuthPublickey(KeyProvider kProv) { public AuthPublickey(KeyProvider kProv) {
@@ -60,7 +61,8 @@ public class AuthPublickey extends KeyedAuthMethod {
/** Internal use. */ /** Internal use. */
@Override @Override
public void handle(Message cmd, SSHPacket buf) throws UserAuthException, TransportException { public void handle(Message cmd, SSHPacket buf)
throws UserAuthException, TransportException {
if (cmd == Message.USERAUTH_60) if (cmd == Message.USERAUTH_60)
sendSignedReq(); sendSignedReq();
else else
@@ -76,7 +78,8 @@ public class AuthPublickey extends KeyedAuthMethod {
* *
* @throws UserAuthException * @throws UserAuthException
*/ */
private SSHPacket buildReq(boolean signed) throws UserAuthException { private SSHPacket buildReq(boolean signed)
throws UserAuthException {
try { try {
kProv.getPublic(); kProv.getPublic();
} catch (IOException ioe) { } catch (IOException ioe) {
@@ -91,14 +94,16 @@ public class AuthPublickey extends KeyedAuthMethod {
* @throws UserAuthException * @throws UserAuthException
* @throws TransportException * @throws TransportException
*/ */
private void sendSignedReq() throws UserAuthException, TransportException { private void sendSignedReq()
throws UserAuthException, TransportException {
log.debug("Sending signed request"); log.debug("Sending signed request");
params.getTransport().write(putSig(buildReq(true))); params.getTransport().write(putSig(buildReq(true)));
} }
/** Builds a feeler request (sans signature). */ /** Builds a feeler request (sans signature). */
@Override @Override
protected SSHPacket buildReq() throws UserAuthException { protected SSHPacket buildReq()
throws UserAuthException {
return buildReq(false); return buildReq(false);
} }

View File

@@ -47,7 +47,8 @@ import java.io.IOException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
public abstract class KeyedAuthMethod extends AbstractAuthMethod { public abstract class KeyedAuthMethod
extends AbstractAuthMethod {
protected KeyProvider kProv; protected KeyProvider kProv;
public KeyedAuthMethod(String name, KeyProvider kProv) { public KeyedAuthMethod(String name, KeyProvider kProv) {
@@ -55,7 +56,8 @@ public abstract class KeyedAuthMethod extends AbstractAuthMethod {
this.kProv = kProv; this.kProv = kProv;
} }
protected SSHPacket putPubKey(SSHPacket reqBuf) throws UserAuthException { protected SSHPacket putPubKey(SSHPacket reqBuf)
throws UserAuthException {
PublicKey key; PublicKey key;
try { try {
key = kProv.getPublic(); key = kProv.getPublic();
@@ -70,7 +72,8 @@ public abstract class KeyedAuthMethod extends AbstractAuthMethod {
return reqBuf; return reqBuf;
} }
protected SSHPacket putSig(SSHPacket reqBuf) throws UserAuthException { protected SSHPacket putSig(SSHPacket reqBuf)
throws UserAuthException {
PrivateKey key; PrivateKey key;
try { try {
key = kProv.getPrivate(); key = kProv.getPrivate();

View File

@@ -15,7 +15,8 @@
*/ */
package net.schmizz.sshj.userauth.password; package net.schmizz.sshj.userauth.password;
public class AccountResource extends Resource<String> { public class AccountResource
extends Resource<String> {
public AccountResource(String user, String host) { public AccountResource(String user, String host) {
super(user + "@" + host); super(user + "@" + host);

View File

@@ -15,7 +15,8 @@
*/ */
package net.schmizz.sshj.userauth.password; package net.schmizz.sshj.userauth.password;
public class PrivateKeyFileResource extends Resource<String> { public class PrivateKeyFileResource
extends Resource<String> {
public PrivateKeyFileResource(String path) { public PrivateKeyFileResource(String path) {
super(path); super(path);