226 lines · 7.5 KB · Java
RawDownload
/* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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 specfied, 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;     /**     * @param mech not null     * @param protocol not null for Sasl, ignored in NTLM     * @param serverName not null for Sasl, can be null in NTLM. If non-null,     * might be used as domain if not provided in props     * @param props can be null     * @param cbh can be null for Sasl, but will throw NPE in auth for NTLM     * @throws SaslException     */    NTLMServer(String mech, String protocol, String serverName,            Map 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 NullPointerException("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 = 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: Invalid version string: " + version, 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];                return null;            }        } catch (GeneralSecurityException ex) {            throw new SaslException("", ex);        }    }     @Override    public boolean isComplete() {        return step >= 2;    }     @Override    public String getAuthorizationID() {        return authzId;    }     @Override    public byte[] unwrap(byte[] incoming, int offset, int len)            throws SaslException {        throw new UnsupportedOperationException("Not supported yet.");    }     @Override    public byte[] wrap(byte[] outgoing, int offset, int len)            throws SaslException {        throw new UnsupportedOperationException("Not supported yet.");    }     @Override    public Object getNegotiatedProperty(String propName) {        if (propName.equals(Sasl.QOP)) {            return "auth";        } else if (propName.equals(NTLM_HOSTNAME)) {            return hostname;        } else {            return null;        }    }     @Override    public void dispose() throws SaslException {        return;    }}