connectbot/sshlib

View on GitHub
src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java

Summary

Maintainability
F
3 days
Test Coverage
D
62%
File `AuthenticationManager.java` has 418 lines of code (exceeds 250 allowed). Consider refactoring.
 
package com.trilead.ssh2.auth;
 
import com.trilead.ssh2.crypto.keys.Ed25519PrivateKey;
import com.trilead.ssh2.crypto.keys.Ed25519PublicKey;
import com.trilead.ssh2.signature.RSASHA256Verify;
import com.trilead.ssh2.signature.RSASHA512Verify;
import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Set;
import java.util.Vector;
 
import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.PacketServiceAccept;
import com.trilead.ssh2.packets.PacketServiceRequest;
import com.trilead.ssh2.packets.PacketUserauthBanner;
import com.trilead.ssh2.packets.PacketUserauthFailure;
import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
import com.trilead.ssh2.packets.PacketUserauthRequestNone;
import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesWriter;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.ECDSASHA2Verify;
import com.trilead.ssh2.signature.Ed25519Verify;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.SSHSignature;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
 
 
/**
* AuthenticationManager.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class AuthenticationManager implements MessageHandler
{
TransportManager tm;
 
Vector packets = new Vector();
boolean connectionClosed = false;
 
String banner;
 
String[] remainingMethods = new String[0];
boolean isPartialSuccess = false;
 
boolean authenticated = false;
boolean initDone = false;
 
public AuthenticationManager(TransportManager tm)
{
this.tm = tm;
}
 
boolean methodPossible(String methName)
{
if (remainingMethods == null)
return false;
 
Identical blocks of code found in 2 locations. Consider refactoring.
for (int i = 0; i < remainingMethods.length; i++)
{
if (remainingMethods[i].compareTo(methName) == 0)
return true;
}
return false;
}
 
byte[] deQueue() throws IOException
{
synchronized (packets)
{
while (packets.size() == 0)
{
if (connectionClosed)
throw new IOException("The connection is closed.", tm.getReasonClosedCause());
 
try
{
packets.wait();
}
catch (InterruptedException ign)
{
}
}
/* This sequence works with J2ME */
byte[] res = (byte[]) packets.firstElement();
packets.removeElementAt(0);
return res;
}
}
 
byte[] getNextMessage() throws IOException
{
while (true)
{
byte[] msg = deQueue();
 
if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
return msg;
 
PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
 
banner = sb.getBanner();
}
}
 
public String[] getRemainingMethods(String user) throws IOException
{
initialize(user);
return remainingMethods;
}
 
public boolean getPartialSuccess()
{
return isPartialSuccess;
}
 
Method `initialize` has 27 lines of code (exceeds 25 allowed). Consider refactoring.
private boolean initialize(String user) throws IOException
{
if (!initDone)
{
tm.registerMessageHandler(this, 0, 255);
 
PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
tm.sendMessage(sr.getPayload());
 
PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
tm.sendMessage(urn.getPayload());
 
byte[] msg = getNextMessage();
new PacketServiceAccept(msg, 0, msg.length);
msg = getNextMessage();
 
initDone = true;
 
Identical blocks of code found in 2 locations. Consider refactoring.
if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
 
Identical blocks of code found in 2 locations. Consider refactoring.
if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
 
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
 
throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
}
return authenticated;
}
 
public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
throws IOException
{
KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password);
 
return authenticatePublicKey(user, pair, rnd);
}
 
public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd)
throws IOException
{
return authenticatePublicKey(user, pair, rnd, null);
}
 
public boolean authenticatePublicKey(String user, SignatureProxy signatureProxy)
throws IOException
{
return authenticatePublicKey(user, null, null, signatureProxy);
}
 
Method `authenticatePublicKey` has 136 lines of code (exceeds 25 allowed). Consider refactoring.
Method `authenticatePublicKey` has a Cognitive Complexity of 34 (exceeds 5 allowed). Consider refactoring.
public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd, SignatureProxy signatureProxy)
throws IOException
{
PrivateKey privateKey = null;
PublicKey publicKey = null;
if (pair != null)
{
privateKey = pair.getPrivate();
publicKey = pair.getPublic();
}
if (signatureProxy != null)
{
publicKey = signatureProxy.getPublicKey();
}
 
try
{
initialize(user);
 
if (!methodPossible("publickey"))
throw new IOException("Authentication method publickey not supported by the server at this stage.");
 
if (publicKey instanceof DSAPublicKey)
{
SSHSignature s = DSASHA1Verify.get();
byte[] pk_enc = s.encodePublicKey(publicKey);
 
byte[] msg = this.generatePublicKeyUserAuthenticationRequest(user, DSASHA1Verify.ID_SSH_DSS, pk_enc);
 
byte[] ds_enc;
Identical blocks of code found in 2 locations. Consider refactoring.
if (signatureProxy != null)
{
ds_enc = signatureProxy.sign(msg, SignatureProxy.SHA1);
}
else
{
ds_enc = s.generateSignature(msg, privateKey, rnd);
}
 
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
DSASHA1Verify.ID_SSH_DSS, pk_enc, ds_enc);
tm.sendMessage(ua.getPayload());
}
else if (publicKey instanceof RSAPublicKey)
{
byte[] pk_enc = RSASHA1Verify.get().encodePublicKey(publicKey);
String pk_algorithm;
 
 
// Servers support different hash algorithms for RSA keys
// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-12
Set<String> algsAccepted = tm.getExtensionInfo().getSignatureAlgorithmsAccepted();
final byte[] rsa_sig_enc;
 
if (algsAccepted.contains(RSASHA512Verify.get().getKeyFormat()))
{
SSHSignature s = RSASHA512Verify.get();
pk_algorithm = s.getKeyFormat();
byte[] msg = this.generatePublicKeyUserAuthenticationRequest(user, pk_algorithm, pk_enc);
Identical blocks of code found in 2 locations. Consider refactoring.
if (signatureProxy != null)
{
rsa_sig_enc = signatureProxy.sign(msg, SignatureProxy.SHA512);
}
else
{
rsa_sig_enc = s.generateSignature(msg, privateKey, rnd);
}
}
else if (algsAccepted.contains(RSASHA256Verify.ID_RSA_SHA_2_256))
{
pk_algorithm = RSASHA256Verify.ID_RSA_SHA_2_256;
byte[] msg = this.generatePublicKeyUserAuthenticationRequest(user, pk_algorithm, pk_enc);
 
Identical blocks of code found in 2 locations. Consider refactoring.
if (signatureProxy != null)
{
rsa_sig_enc = signatureProxy.sign(msg, SignatureProxy.SHA256);
}
else
{
rsa_sig_enc = RSASHA256Verify.get().generateSignature(msg, privateKey, rnd);
}
}
else
{
pk_algorithm = "ssh-rsa";
byte[] msg = this.generatePublicKeyUserAuthenticationRequest(user, pk_algorithm, pk_enc);
Identical blocks of code found in 2 locations. Consider refactoring.
if (signatureProxy != null)
{
rsa_sig_enc = signatureProxy.sign(msg, SignatureProxy.SHA1);
}
else
{
// Server always accepts RSA with SHA1
rsa_sig_enc = RSASHA1Verify.get().generateSignature(msg, privateKey, rnd);
}
}
 
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
pk_algorithm, pk_enc, rsa_sig_enc);
 
tm.sendMessage(ua.getPayload());
}
else if (publicKey instanceof ECPublicKey)
{
ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
 
ECDSASHA2Verify verifier = ECDSASHA2Verify.getVerifierForKey(ecPublicKey);
 
final String algo = verifier.getKeyFormat();
 
byte[] pk_enc = verifier.encodePublicKey(ecPublicKey);
 
byte[] msg = this.generatePublicKeyUserAuthenticationRequest(user, algo, pk_enc);
 
byte[] ec_sig_enc;
if (signatureProxy != null)
{
ec_sig_enc = signatureProxy.sign(msg, ECDSASHA2Verify.getDigestAlgorithmForParams(ecPublicKey));
}
else
{
ec_sig_enc = verifier.generateSignature(msg, privateKey, rnd);
}
 
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
algo, pk_enc, ec_sig_enc);
 
tm.sendMessage(ua.getPayload());
}
else if (publicKey instanceof Ed25519PublicKey)
{
final String algo = Ed25519Verify.ED25519_ID;
 
byte[] pk_enc = Ed25519Verify.get().encodePublicKey(publicKey);
 
byte[] msg = this.generatePublicKeyUserAuthenticationRequest(user, algo, pk_enc);
 
byte[] ed_sig_enc;
if (signatureProxy != null)
{
ed_sig_enc = signatureProxy.sign(msg, SignatureProxy.SHA512);
}
else
{
Ed25519PrivateKey pk = (Ed25519PrivateKey) privateKey;
ed_sig_enc = Ed25519Verify.get().generateSignature(msg, pk, rnd);
}
 
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
algo, pk_enc, ed_sig_enc);
 
tm.sendMessage(ua.getPayload());
}
else
{
throw new IOException("Unknown public key type.");
}
 
byte[] ar = getNextMessage();
 
return isAuthenticationSuccessful(ar);
}
catch (IOException e)
{
e.printStackTrace();
tm.close(e, false);
throw new IOException("Publickey authentication failed.", e);
}
}
 
public boolean authenticateNone(String user) throws IOException
{
try
{
initialize(user);
return authenticated;
}
catch (IOException e)
{
tm.close(e, false);
throw new IOException("None authentication failed.", e);
}
}
 
public boolean authenticatePassword(String user, String pass) throws IOException
{
try
{
initialize(user);
 
if (!methodPossible("password"))
throw new IOException("Authentication method password not supported by the server at this stage.");
 
PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
tm.sendMessage(ua.getPayload());
 
byte[] ar = getNextMessage();
 
return isAuthenticationSuccessful(ar);
}
catch (IOException e)
{
tm.close(e, false);
throw new IOException("Password authentication failed.", e);
}
}
 
Method `authenticateInteractive` has 41 lines of code (exceeds 25 allowed). Consider refactoring.
Method `authenticateInteractive` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.
public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
{
try
{
initialize(user);
 
if (!methodPossible("keyboard-interactive"))
throw new IOException(
"Authentication method keyboard-interactive not supported by the server at this stage.");
 
if (submethods == null)
submethods = new String[0];
 
PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
submethods);
 
tm.sendMessage(ua.getPayload());
 
while (true)
{
byte[] ar = getNextMessage();
 
if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
{
PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
 
String[] responses;
 
try
{
responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
.getPrompt(), pui.getEcho());
}
catch (Exception e)
{
throw new IOException("Exception in callback.", e);
}
 
if (responses == null)
throw new IOException("Your callback may not return NULL!");
 
PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
tm.sendMessage(puir.getPayload());
 
continue;
}
 
return isAuthenticationSuccessful(ar);
}
}
catch (IOException e)
{
tm.close(e, false);
throw new IOException("Keyboard-interactive authentication failed.", e);
}
}
 
public void handleMessage(byte[] msg, int msglen) throws IOException
{
synchronized (packets)
{
if (msg == null)
{
connectionClosed = true;
}
else
{
byte[] tmp = new byte[msglen];
System.arraycopy(msg, 0, tmp, 0, msglen);
packets.addElement(tmp);
}
 
packets.notifyAll();
 
if (packets.size() > 5)
{
connectionClosed = true;
throw new IOException("Error, peer is flooding us with authentication packets.");
}
}
}
 
private boolean isAuthenticationSuccessful(byte[] ar) throws IOException
{
Identical blocks of code found in 2 locations. Consider refactoring.
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
 
Identical blocks of code found in 2 locations. Consider refactoring.
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
 
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
 
return false;
}
 
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
 
private byte[] generatePublicKeyUserAuthenticationRequest(String user, String algorithm, byte[] publicKeyEncoded) {
TypesWriter tw = new TypesWriter();
{
byte[] H = tm.getSessionIdentifier();
 
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString(algorithm);
tw.writeString(publicKeyEncoded, 0, publicKeyEncoded.length);
}
 
return tw.getBytes();
}
}