Add simple test for AuthGssApiWithMic

Mock enough of the JGSS API to avoid needing a real Kerberos
environment. I'm not sure how accurate this is, but it should test that
the client is sending the correct packets in the corect order.
This commit is contained in:
Billy Keyes
2015-06-11 11:39:13 -07:00
parent 4adc83b9df
commit b9d0a03cb3
7 changed files with 591 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
package net.schmizz.sshj.userauth;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Collections;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import net.schmizz.sshj.userauth.method.AuthGssApiWithMic;
import net.schmizz.sshj.util.BasicFixture;
import net.schmizz.sshj.util.gss.BogusGSSAuthenticator;
import net.schmizz.sshj.util.gss.BogusGSSManager;
public class GssApiTest {
private static final String LOGIN_CONTEXT_NAME = "TestLoginContext";
private static class TestAuthConfiguration extends Configuration {
private AppConfigurationEntry entry = new AppConfigurationEntry(
"testLoginModule",
LoginModuleControlFlag.REQUIRED,
Collections.<String, Object> emptyMap());
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
if (name.equals(LOGIN_CONTEXT_NAME)) {
return new AppConfigurationEntry[] { entry };
} else {
return new AppConfigurationEntry[0];
}
}
}
private final BasicFixture fixture = new BasicFixture();
@Before
public void setUp() throws Exception {
fixture.setGssAuthenticator(new BogusGSSAuthenticator());
fixture.init(false);
}
@After
public void tearDown() throws IOException, InterruptedException {
fixture.done();
}
@Test
public void authenticated() throws Exception {
AuthGssApiWithMic authMethod = new AuthGssApiWithMic(
new LoginContext(LOGIN_CONTEXT_NAME, null, null, new TestAuthConfiguration()),
Collections.singletonList(BogusGSSManager.KRB5_MECH),
new BogusGSSManager());
fixture.getClient().auth("user", authMethod);
assertTrue(fixture.getClient().isAuthenticated());
}
}

View File

@@ -33,9 +33,11 @@ package net.schmizz.sshj.util;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import java.io.IOException;
@@ -50,6 +52,8 @@ public class BasicFixture {
public static final String hostname = "localhost";
public final int port = gimmeAPort();
private GSSAuthenticator gssAuthenticator;
private SSHClient client;
private SshServer server;
@@ -99,6 +103,7 @@ public class BasicFixture {
return false;
}
});
server.setGSSAuthenticator(gssAuthenticator);
server.start();
serverRunning = true;
}
@@ -137,6 +142,10 @@ public class BasicFixture {
return client;
}
public void setGssAuthenticator(GSSAuthenticator gssAuthenticator) {
this.gssAuthenticator = gssAuthenticator;
}
public void dummyAuth()
throws UserAuthException, TransportException {
server.setPasswordAuthenticator(new BogusPasswordAuthenticator());

View File

@@ -0,0 +1,22 @@
package net.schmizz.sshj.util.gss;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
public class BogusGSSAuthenticator
extends GSSAuthenticator {
private final GSSManager manager = new BogusGSSManager();
@Override
public GSSManager getGSSManager() {
return manager;
}
@Override
public GSSCredential getGSSCredential(GSSManager mgr) throws GSSException {
return manager.createCredential(GSSCredential.ACCEPT_ONLY);
}
}

View File

@@ -0,0 +1,243 @@
package net.schmizz.sshj.util.gss;
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.ietf.jgss.ChannelBinding;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.Oid;
public class BogusGSSContext
implements GSSContext {
private static final byte[] INIT_TOKEN = fromString("INIT");
private static final byte[] ACCEPT_TOKEN = fromString("ACCEPT");
private static final byte[] MIC = fromString("LGTM");
private static byte[] fromString(String s) {
return s.getBytes(Charset.forName("UTF-8"));
}
private boolean initialized = false;
private boolean accepted = false;
private boolean integState = false;
private boolean mutualAuthState = false;
@Override
public byte[] initSecContext(byte[] inputBuf, int offset, int len) throws GSSException {
initialized = true;
return INIT_TOKEN;
}
@Override
public int initSecContext(InputStream inStream, OutputStream outStream) throws GSSException {
throw unavailable();
}
@Override
public byte[] acceptSecContext(byte[] inToken, int offset, int len) throws GSSException {
accepted = Arrays.equals(INIT_TOKEN, Arrays.copyOfRange(inToken, offset, offset + len));
return ACCEPT_TOKEN;
}
@Override
public void acceptSecContext(InputStream inStream, OutputStream outStream) throws GSSException {
throw unavailable();
}
@Override
public boolean isEstablished() {
return initialized || accepted;
}
@Override
public void dispose() throws GSSException {}
@Override
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokenSize) throws GSSException {
throw unavailable();
}
@Override
public byte[] wrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public void wrap(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public byte[] unwrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public void unwrap(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public byte[] getMIC(byte[] inMsg, int offset, int len, MessageProp msgProp) throws GSSException {
return MIC;
}
@Override
public void getMIC(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public void verifyMIC(byte[] inToken, int tokOffset, int tokLen, byte[] inMsg, int msgOffset, int msgLen, MessageProp msgProp) throws GSSException {
if (!Arrays.equals(MIC, Arrays.copyOfRange(inToken, tokOffset, tokOffset + tokLen))) {
throw new GSSException(GSSException.BAD_MIC);
}
}
@Override
public void verifyMIC(InputStream tokStream, InputStream msgStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public byte[] export() throws GSSException {
throw unavailable();
}
@Override
public void requestMutualAuth(boolean state) throws GSSException {
this.mutualAuthState = state;
}
@Override
public void requestInteg(boolean state) throws GSSException {
this.integState = state;
}
@Override
public void requestReplayDet(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestSequenceDet(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestCredDeleg(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestAnonymity(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestConf(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestLifetime(int lifetime) throws GSSException {
throw unavailable();
}
@Override
public void setChannelBinding(ChannelBinding cb) throws GSSException {
throw unavailable();
}
@Override
public boolean getMutualAuthState() {
return mutualAuthState;
}
@Override
public boolean getIntegState() {
return integState;
}
@Override
public boolean getCredDelegState() {
return false;
}
@Override
public boolean getReplayDetState() {
return false;
}
@Override
public boolean getSequenceDetState() {
return false;
}
@Override
public boolean getAnonymityState() {
return false;
}
@Override
public boolean isTransferable() throws GSSException {
return false;
}
@Override
public boolean isProtReady() {
return false;
}
@Override
public boolean getConfState() {
return false;
}
@Override
public int getLifetime() {
return INDEFINITE_LIFETIME;
}
@Override
public GSSName getSrcName() throws GSSException {
try {
String hostname = InetAddress.getLocalHost().getCanonicalHostName();
return new BogusGSSName("user@" + hostname, GSSName.NT_HOSTBASED_SERVICE);
} catch (UnknownHostException e) {
throw new IllegalStateException(e);
}
}
@Override
public GSSName getTargName() throws GSSException {
throw unavailable();
}
@Override
public Oid getMech() throws GSSException {
return BogusGSSManager.KRB5_MECH;
}
@Override
public GSSCredential getDelegCred() throws GSSException {
throw unavailable();
}
@Override
public boolean isInitiator() throws GSSException {
return false;
}
}

View File

@@ -0,0 +1,87 @@
package net.schmizz.sshj.util.gss;
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
public class BogusGSSCredential
implements GSSCredential {
private final GSSName name;
private final int usage;
public BogusGSSCredential(GSSName name, int usage) {
this.name = name;
this.usage = usage;
}
@Override
public void dispose() throws GSSException {}
@Override
public GSSName getName() throws GSSException {
return name;
}
@Override
public GSSName getName(Oid mech) throws GSSException {
return name.canonicalize(mech);
}
@Override
public int getRemainingLifetime() throws GSSException {
return INDEFINITE_LIFETIME;
}
@Override
public int getRemainingInitLifetime(Oid mech) throws GSSException {
return INDEFINITE_LIFETIME;
}
@Override
public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
return INDEFINITE_LIFETIME;
}
@Override
public int getUsage() throws GSSException {
return usage;
}
@Override
public int getUsage(Oid mech) throws GSSException {
return usage;
}
@Override
public Oid[] getMechs() throws GSSException {
return new Oid[] { BogusGSSManager.KRB5_MECH };
}
@Override
public void add(GSSName name, int initLifetime, int acceptLifetime, Oid mech, int usage) throws GSSException {
throw unavailable();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
return (name == null ? 0 : name.hashCode());
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BogusGSSCredential)) {
return false;
}
GSSName otherName = ((BogusGSSCredential) obj).name;
return name == null ? otherName == null : name.equals((Object) otherName);
}
}

View File

@@ -0,0 +1,106 @@
package net.schmizz.sshj.util.gss;
import java.security.Provider;
import org.apache.sshd.server.auth.gss.UserAuthGSS;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements a fake Kerberos 5 mechanism. MINA only supports Kerberos 5 over
* GSS-API, so we can't implement a separate mechanism.
*/
public class BogusGSSManager
extends GSSManager {
public static final Oid KRB5_MECH = UserAuthGSS.KRB5_MECH;
private static final Logger log = LoggerFactory.getLogger(BogusGSSManager.class);
@Override
public Oid[] getMechs() {
return new Oid[] { KRB5_MECH };
}
@Override
public Oid[] getNamesForMech(Oid mech) throws GSSException {
return new Oid[] { GSSName.NT_EXPORT_NAME, GSSName.NT_HOSTBASED_SERVICE };
}
@Override
public Oid[] getMechsForName(Oid nameType) {
return new Oid[] { KRB5_MECH };
}
@Override
public GSSName createName(String nameStr, Oid nameType) throws GSSException {
return new BogusGSSName(nameStr, nameType);
}
@Override
public GSSName createName(byte[] name, Oid nameType) throws GSSException {
throw unavailable();
}
@Override
public GSSName createName(String nameStr, Oid nameType, Oid mech) throws GSSException {
return this.createName(nameStr, nameType);
}
@Override
public GSSName createName(byte[] name, Oid nameType, Oid mech) throws GSSException {
throw unavailable();
}
@Override
public GSSCredential createCredential(int usage) throws GSSException {
return new BogusGSSCredential(null, usage);
}
@Override
public GSSCredential createCredential(GSSName name, int lifetime, Oid mech, int usage) throws GSSException {
return new BogusGSSCredential(name, usage);
}
@Override
public GSSCredential createCredential(GSSName name, int lifetime, Oid[] mechs, int usage) throws GSSException {
return new BogusGSSCredential(name, usage);
}
@Override
public GSSContext createContext(GSSName peer, Oid mech, GSSCredential myCred, int lifetime) throws GSSException {
return new BogusGSSContext();
}
@Override
public GSSContext createContext(GSSCredential myCred) throws GSSException {
return new BogusGSSContext();
}
@Override
public GSSContext createContext(byte[] interProcessToken) throws GSSException {
throw unavailable();
}
@Override
public void addProviderAtFront(Provider p, Oid mech) throws GSSException {
throw unavailable();
}
@Override
public void addProviderAtEnd(Provider p, Oid mech) throws GSSException {
throw unavailable();
}
static GSSException unavailable() throws GSSException {
GSSException e = new GSSException(GSSException.UNAVAILABLE);
log.error(e.getMessage(), e);
throw e;
}
}

View File

@@ -0,0 +1,58 @@
package net.schmizz.sshj.util.gss;
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
public class BogusGSSName
implements GSSName {
private final String name;
private final Oid oid;
public BogusGSSName(String name, Oid oid) {
this.name = name;
this.oid = oid;
}
@Override
public boolean equals(GSSName another) throws GSSException {
if (!(another instanceof BogusGSSName)) {
throw new GSSException(GSSException.BAD_NAMETYPE);
}
BogusGSSName otherName = (BogusGSSName) another;
return name.equals(otherName.name) && oid.equals(otherName.oid);
}
@Override
public GSSName canonicalize(Oid mech) throws GSSException {
return this;
}
@Override
public byte[] export() throws GSSException {
throw unavailable();
}
@Override
public Oid getStringNameType() throws GSSException {
return oid;
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public boolean isMN() {
return false;
}
@Override
public String toString() {
return name;
}
}