|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.security.sasl.ntlm; |
|
|
|
import com.sun.security.ntlm.Client; |
|
import com.sun.security.ntlm.NTLMException; |
|
import java.io.IOException; |
|
import java.net.InetAddress; |
|
import java.net.UnknownHostException; |
|
import java.util.Map; |
|
import java.util.Random; |
|
import javax.security.auth.callback.Callback; |
|
|
|
|
|
import javax.security.sasl.*; |
|
import javax.security.auth.callback.CallbackHandler; |
|
import javax.security.auth.callback.NameCallback; |
|
import javax.security.auth.callback.PasswordCallback; |
|
import javax.security.auth.callback.UnsupportedCallbackException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class NTLMClient implements SaslClient { |
|
|
|
private static final String NTLM_VERSION = |
|
"com.sun.security.sasl.ntlm.version"; |
|
private static final String NTLM_RANDOM = |
|
"com.sun.security.sasl.ntlm.random"; |
|
private final static String NTLM_DOMAIN = |
|
"com.sun.security.sasl.ntlm.domain"; |
|
private final static String NTLM_HOSTNAME = |
|
"com.sun.security.sasl.ntlm.hostname"; |
|
|
|
private final Client client; |
|
private final String mech; |
|
private final Random random; |
|
|
|
private int step = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
NTLMClient(String mech, String authzid, String protocol, String serverName, |
|
Map<String, ?> props, CallbackHandler cbh) throws SaslException { |
|
|
|
this.mech = mech; |
|
String version = null; |
|
Random rtmp = null; |
|
String hostname = null; |
|
|
|
if (props != null) { |
|
String qop = (String)props.get(Sasl.QOP); |
|
if (qop != null && !qop.equals("auth")) { |
|
throw new SaslException("NTLM only support auth"); |
|
} |
|
version = (String)props.get(NTLM_VERSION); |
|
rtmp = (Random)props.get(NTLM_RANDOM); |
|
hostname = (String)props.get(NTLM_HOSTNAME); |
|
} |
|
this.random = rtmp != null ? rtmp : new Random(); |
|
|
|
if (version == null) { |
|
version = System.getProperty("ntlm.version"); |
|
} |
|
|
|
RealmCallback dcb = (serverName != null && !serverName.isEmpty())? |
|
new RealmCallback("Realm: ", serverName) : |
|
new RealmCallback("Realm: "); |
|
NameCallback ncb = (authzid != null && !authzid.isEmpty()) ? |
|
new NameCallback("User name: ", authzid) : |
|
new NameCallback("User name: "); |
|
PasswordCallback pcb = |
|
new PasswordCallback("Password: ", false); |
|
|
|
try { |
|
cbh.handle(new Callback[] {dcb, ncb, pcb}); |
|
} catch (UnsupportedCallbackException e) { |
|
throw new SaslException("NTLM: Cannot perform callback to " + |
|
"acquire realm, username or password", e); |
|
} catch (IOException e) { |
|
throw new SaslException( |
|
"NTLM: Error acquiring realm, username or password", e); |
|
} |
|
|
|
if (hostname == null) { |
|
try { |
|
hostname = InetAddress.getLocalHost().getCanonicalHostName(); |
|
} catch (UnknownHostException e) { |
|
hostname = "localhost"; |
|
} |
|
} |
|
try { |
|
String name = ncb.getName(); |
|
if (name == null) { |
|
name = authzid; |
|
} |
|
String domain = dcb.getText(); |
|
if (domain == null) { |
|
domain = serverName; |
|
} |
|
client = new Client(version, hostname, |
|
name, |
|
domain, |
|
pcb.getPassword()); |
|
} catch (NTLMException ne) { |
|
throw new SaslException( |
|
"NTLM: client creation failure", ne); |
|
} |
|
} |
|
|
|
@Override |
|
public String getMechanismName() { |
|
return mech; |
|
} |
|
|
|
@Override |
|
public boolean isComplete() { |
|
return step >= 2; |
|
} |
|
|
|
@Override |
|
public byte[] unwrap(byte[] incoming, int offset, int len) |
|
throws SaslException { |
|
throw new IllegalStateException("Not supported."); |
|
} |
|
|
|
@Override |
|
public byte[] wrap(byte[] outgoing, int offset, int len) |
|
throws SaslException { |
|
throw new IllegalStateException("Not supported."); |
|
} |
|
|
|
@Override |
|
public Object getNegotiatedProperty(String propName) { |
|
if (!isComplete()) { |
|
throw new IllegalStateException("authentication not complete"); |
|
} |
|
switch (propName) { |
|
case Sasl.QOP: |
|
return "auth"; |
|
case NTLM_DOMAIN: |
|
return client.getDomain(); |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
@Override |
|
public void dispose() throws SaslException { |
|
client.dispose(); |
|
} |
|
|
|
@Override |
|
public boolean hasInitialResponse() { |
|
return true; |
|
} |
|
|
|
@Override |
|
public byte[] evaluateChallenge(byte[] challenge) throws SaslException { |
|
step++; |
|
if (step == 1) { |
|
return client.type1(); |
|
} else { |
|
try { |
|
byte[] nonce = new byte[8]; |
|
random.nextBytes(nonce); |
|
return client.type3(challenge, nonce); |
|
} catch (NTLMException ex) { |
|
throw new SaslException("Type3 creation failed", ex); |
|
} |
|
} |
|
} |
|
} |