| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.security.sasl.ntlm;  | 
 | 
 | 
 | 
import com.sun.security.ntlm.NTLMException;  | 
 | 
import com.sun.security.ntlm.Server;  | 
 | 
import java.io.IOException;  | 
 | 
import java.security.GeneralSecurityException;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.Random;  | 
 | 
import javax.security.auth.callback.Callback;  | 
 | 
import javax.security.auth.callback.CallbackHandler;  | 
 | 
import javax.security.auth.callback.NameCallback;  | 
 | 
import javax.security.auth.callback.PasswordCallback;  | 
 | 
import javax.security.auth.callback.UnsupportedCallbackException;  | 
 | 
import javax.security.sasl.*;  | 
 | 
 | 
 | 
/**  | 
 | 
  * Required callbacks:  | 
 | 
  * - RealmCallback  | 
 | 
  *      used as key by handler to fetch password, optional  | 
 | 
  * - NameCallback  | 
 | 
  *      used as key by handler to fetch password  | 
 | 
  * - PasswordCallback  | 
 | 
  *      handler must enter password for username/realm supplied  | 
 | 
  *  | 
 | 
  * Environment properties that affect the implementation:  | 
 | 
  *  | 
 | 
  * javax.security.sasl.qop  | 
 | 
  *    String, quality of protection; only "auth" is accepted, default "auth"  | 
 | 
  *  | 
 | 
  * com.sun.security.sasl.ntlm.version  | 
 | 
  *    String, name a specific version to accept:  | 
 | 
  *      LM/NTLM: Original NTLM v1  | 
 | 
  *      LM: Original NTLM v1, LM only  | 
 | 
  *      NTLM: Original NTLM v1, NTLM only  | 
 | 
  *      NTLM2: NTLM v1 with Client Challenge  | 
 | 
  *      LMv2/NTLMv2: NTLM v2  | 
 | 
  *      LMv2: NTLM v2, LM only  | 
 | 
  *      NTLMv2: NTLM v2, NTLM only  | 
 | 
  *    If not specified, use system property "ntlm.version". If also  | 
 | 
  *    not specified, all versions are accepted.  | 
 | 
  *  | 
 | 
  * com.sun.security.sasl.ntlm.domain  | 
 | 
  *    String, the domain of the server, default is server name (fqdn parameter)  | 
 | 
  *  | 
 | 
  * com.sun.security.sasl.ntlm.random  | 
 | 
  *    java.util.Random, the nonce source. Default null, an internal  | 
 | 
  *    java.util.Random object will be used  | 
 | 
  *  | 
 | 
  * Negotiated Properties:  | 
 | 
  *  | 
 | 
  * javax.security.sasl.qop  | 
 | 
  *    Always "auth"  | 
 | 
  *  | 
 | 
  * com.sun.security.sasl.ntlm.hostname  | 
 | 
  *    The hostname for the user, provided by the client  | 
 | 
  *  | 
 | 
  */  | 
 | 
 | 
 | 
final class NTLMServer implements SaslServer { | 
 | 
 | 
 | 
    private final static String NTLM_VERSION =  | 
 | 
            "com.sun.security.sasl.ntlm.version";  | 
 | 
    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 static final String NTLM_RANDOM =  | 
 | 
            "com.sun.security.sasl.ntlm.random";  | 
 | 
 | 
 | 
    private final Random random;  | 
 | 
    private final Server server;  | 
 | 
    private byte[] nonce;  | 
 | 
    private int step = 0;  | 
 | 
    private String authzId;  | 
 | 
    private final String mech;  | 
 | 
    private String hostname;  | 
 | 
    private String target;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    NTLMServer(String mech, String protocol, String serverName,  | 
 | 
            Map<String, ?> props, final CallbackHandler cbh)  | 
 | 
            throws SaslException { | 
 | 
 | 
 | 
        this.mech = mech;  | 
 | 
        String version = null;  | 
 | 
        String domain = null;  | 
 | 
        Random rtmp = null;  | 
 | 
 | 
 | 
        if (props != null) { | 
 | 
            domain = (String) props.get(NTLM_DOMAIN);  | 
 | 
            version = (String)props.get(NTLM_VERSION);  | 
 | 
            rtmp = (Random)props.get(NTLM_RANDOM);  | 
 | 
        }  | 
 | 
        random = rtmp != null ? rtmp : new Random();  | 
 | 
 | 
 | 
        if (version == null) { | 
 | 
            version = System.getProperty("ntlm.version"); | 
 | 
        }  | 
 | 
        if (domain == null) { | 
 | 
            domain = serverName;  | 
 | 
        }  | 
 | 
        if (domain == null) { | 
 | 
            throw new SaslException("Domain must be provided as" | 
 | 
                    + " the serverName argument or in props");  | 
 | 
        }  | 
 | 
 | 
 | 
        try { | 
 | 
            server = new Server(version, domain) { | 
 | 
                public char[] getPassword(String ntdomain, String username) { | 
 | 
                    try { | 
 | 
                        RealmCallback rcb =  | 
 | 
                                (ntdomain == null || ntdomain.isEmpty())  | 
 | 
                                    ? new RealmCallback("Domain: ") | 
 | 
                                    : new RealmCallback("Domain: ", ntdomain); | 
 | 
                        NameCallback ncb = new NameCallback(  | 
 | 
                                "Name: ", username);  | 
 | 
                        PasswordCallback pcb = new PasswordCallback(  | 
 | 
                                "Password: ", false);  | 
 | 
                        cbh.handle(new Callback[] { rcb, ncb, pcb }); | 
 | 
                        char[] passwd = pcb.getPassword();  | 
 | 
                        pcb.clearPassword();  | 
 | 
                        return passwd;  | 
 | 
                    } catch (IOException ioe) { | 
 | 
                        return null;  | 
 | 
                    } catch (UnsupportedCallbackException uce) { | 
 | 
                        return null;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            };  | 
 | 
        } catch (NTLMException ne) { | 
 | 
            throw new SaslException(  | 
 | 
                    "NTLM: server creation failure", ne);  | 
 | 
        }  | 
 | 
        nonce = new byte[8];  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public String getMechanismName() { | 
 | 
        return mech;  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public byte[] evaluateResponse(byte[] response) throws SaslException { | 
 | 
        try { | 
 | 
            step++;  | 
 | 
            if (step == 1) { | 
 | 
                random.nextBytes(nonce);  | 
 | 
                return server.type2(response, nonce);  | 
 | 
            } else { | 
 | 
                String[] out = server.verify(response, nonce);  | 
 | 
                authzId = out[0];  | 
 | 
                hostname = out[1];  | 
 | 
                target = out[2];  | 
 | 
                return null;  | 
 | 
            }  | 
 | 
        } catch (NTLMException ex) { | 
 | 
            throw new SaslException("NTLM: generate response failure", ex); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public boolean isComplete() { | 
 | 
        return step >= 2;  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public String getAuthorizationID() { | 
 | 
        if (!isComplete()) { | 
 | 
            throw new IllegalStateException("authentication not complete"); | 
 | 
        }  | 
 | 
        return authzId;  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public byte[] unwrap(byte[] incoming, int offset, int len)  | 
 | 
            throws SaslException { | 
 | 
        throw new IllegalStateException("Not supported yet."); | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public byte[] wrap(byte[] outgoing, int offset, int len)  | 
 | 
            throws SaslException { | 
 | 
        throw new IllegalStateException("Not supported yet."); | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public Object getNegotiatedProperty(String propName) { | 
 | 
        if (!isComplete()) { | 
 | 
            throw new IllegalStateException("authentication not complete"); | 
 | 
        }  | 
 | 
        switch (propName) { | 
 | 
            case Sasl.QOP:  | 
 | 
                return "auth";  | 
 | 
            case Sasl.BOUND_SERVER_NAME:  | 
 | 
                return target;  | 
 | 
            case NTLM_HOSTNAME:  | 
 | 
                return hostname;  | 
 | 
            default:  | 
 | 
                return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public void dispose() throws SaslException { | 
 | 
        return;  | 
 | 
    }  | 
 | 
}  |