212 lines · 8.0 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.ntlm; import java.math.BigInteger;import java.util.Arrays;import java.util.Date;import java.util.Locale; /** * The NTLM client. Not multi-thread enabled.<p> * Example: * <pre> * Client client = new Client(null, "host", "dummy", *       "REALM", "t0pSeCr3t".toCharArray()); * byte[] type1 = client.type1(); * // Send type1 to server and receive response as type2 * byte[] type3 = client.type3(type2, nonce); * // Send type3 to server * </pre> */public final class Client extends NTLM {    final private String hostname;    final private String username;     private String domain;    // might be updated by Type 2 msg    private byte[] pw1, pw2;     /**     * Creates an NTLM Client instance.     * @param version the NTLM version to use, which can be:     * <ul>     * <li>LM/NTLM: Original NTLM v1     * <li>LM: Original NTLM v1, LM only     * <li>NTLM: Original NTLM v1, NTLM only     * <li>NTLM2: NTLM v1 with Client Challenge     * <li>LMv2/NTLMv2: NTLM v2     * <li>LMv2: NTLM v2, LM only     * <li>NTLMv2: NTLM v2, NTLM only     * </ul>     * If null, "LMv2/NTLMv2" will be used.     * @param hostname hostname of the client, can be null     * @param username username to be authenticated, must not be null     * @param domain domain of {@code username}, can be null     * @param password password for {@code username}, must not be not null.     * This method does not make any modification to this parameter, it neither     * needs to access the content of this parameter after this method call,     * so you are free to modify or nullify this parameter after this call.     * @throws NullPointerException if {@code username} or {@code password} is null.     * @throws NTLMException if {@code version} is illegal     */    public Client(String version, String hostname, String username,            String domain, char[] password) throws NTLMException {        super(version);        if ((username == null || password == null)) {            throw new NullPointerException("username/password cannot be null");        }        this.hostname = hostname;        this.username = username;        this.domain = domain;        this.pw1 = getP1(password);        this.pw2 = getP2(password);        debug("NTLM Client: (h,u,t,version(v)) = (%s,%s,%s,%s(%s))\n",                    hostname, username, domain, version, v.toString());    }     /**     * Generates the Type 1 message     * @return the message generated     */    public byte[] type1() {        Writer p = new Writer(1, 32);        int flags = 0x8203;        if (hostname != null) {            flags |= 0x2000;        }        if (domain != null) {            flags |= 0x1000;        }        if (v != Version.NTLM) {            flags |= 0x80000;        }        p.writeInt(12, flags);        p.writeSecurityBuffer(24, hostname, false);        p.writeSecurityBuffer(16, domain, false);        debug("NTLM Client: Type 1 created\n");        debug(p.getBytes());        return p.getBytes();    }     /**     * Generates the Type 3 message     * @param type2 the responding Type 2 message from server, must not be null     * @param nonce random 8-byte array to be used in message generation,     * must not be null except for original NTLM v1     * @return the message generated     * @throws NullPointerException if {@code type2} or {@code nonce} is null     * for NTLM v1.     * @throws NTLMException if the incoming message is invalid     */    public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException {        if (type2 == null || (v != Version.NTLM && nonce == null)) {            throw new NullPointerException("type2 and nonce cannot be null");        }        debug("NTLM Client: Type 2 received\n");        debug(type2);        Reader r = new Reader(type2);        byte[] challenge = r.readBytes(24, 8);        int inputFlags = r.readInt(20);        boolean unicode = (inputFlags & 1) == 1;        String domainFromServer = r.readSecurityBuffer(12, unicode);        if (domainFromServer != null) {            domain = domainFromServer;        }        if (domain == null) {            throw new NTLMException(NTLMException.NO_DOMAIN_INFO,                    "No domain info");        }         int flags = 0x88200 | (inputFlags & 3);        Writer p = new Writer(3, 64);        byte[] lm = null, ntlm = null;         p.writeSecurityBuffer(28, domain, unicode);        p.writeSecurityBuffer(36, username, unicode);        p.writeSecurityBuffer(44, hostname, unicode);         if (v == Version.NTLM) {            byte[] lmhash = calcLMHash(pw1);            byte[] nthash = calcNTHash(pw2);            if (writeLM) lm = calcResponse (lmhash, challenge);            if (writeNTLM) ntlm = calcResponse (nthash, challenge);        } else if (v == Version.NTLM2) {            byte[] nthash = calcNTHash(pw2);            lm = ntlm2LM(nonce);            ntlm = ntlm2NTLM(nthash, nonce, challenge);        } else {            byte[] nthash = calcNTHash(pw2);            if (writeLM) lm = calcV2(nthash,                    username.toUpperCase(Locale.US)+domain, nonce, challenge);            if (writeNTLM) {                byte[] alist = type2.length > 48 ?                    r.readSecurityBuffer(40) : new byte[0];                byte[] blob = new byte[32+alist.length];                System.arraycopy(new byte[]{1,1,0,0,0,0,0,0}, 0, blob, 0, 8);                // TS                byte[] time = BigInteger.valueOf(new Date().getTime())                        .add(new BigInteger("11644473600000"))                        .multiply(BigInteger.valueOf(10000))                        .toByteArray();                for (int i=0; i<time.length; i++) {                    blob[8+time.length-i-1] = time[i];                }                System.arraycopy(nonce, 0, blob, 16, 8);                System.arraycopy(new byte[]{0,0,0,0}, 0, blob, 24, 4);                System.arraycopy(alist, 0, blob, 28, alist.length);                System.arraycopy(new byte[]{0,0,0,0}, 0,                        blob, 28+alist.length, 4);                ntlm = calcV2(nthash, username.toUpperCase(Locale.US)+domain,                        blob, challenge);            }        }        p.writeSecurityBuffer(12, lm);        p.writeSecurityBuffer(20, ntlm);        p.writeSecurityBuffer(52, new byte[0]);         p.writeInt(60, flags);        debug("NTLM Client: Type 3 created\n");        debug(p.getBytes());        return p.getBytes();    }     /**     * Returns the domain value provided by server after the authentication     * is complete, or the domain value provided by the client before it.     * @return the domain     */    public String getDomain() {        return domain;    }     /**     * Disposes any password-derived information.     */    public void dispose() {        Arrays.fill(pw1, (byte)0);        Arrays.fill(pw2, (byte)0);    }}