/* | 
|
 * Copyright (c) 2010, 2013, 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;  | 
|
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 NTLMException if {@code username} or {@code password} is null, | 
|
     * or {@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 NTLMException(NTLMException.PROTOCOL,  | 
|
                    "username/password cannot be null"); | 
|
}  | 
|
this.hostname = hostname;  | 
|
this.username = username;  | 
|
this.domain = domain == null ? "" : 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);  | 
|
// Negotiate always sign, Negotiate NTLM,  | 
|
        // Request Target, Negotiate OEM, Negotiate unicode | 
|
int flags = 0x8207;  | 
|
        if (v != Version.NTLM) { | 
|
flags |= 0x80000;  | 
|
}  | 
|
p.writeInt(12, flags);  | 
|
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 NTLMException if the incoming message is invalid, or | 
|
     * {@code nonce} is null for NTLM v1. | 
|
*/  | 
|
public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException {  | 
|
if (type2 == null || (v != Version.NTLM && nonce == null)) {  | 
|
throw new NTLMException(NTLMException.PROTOCOL,  | 
|
                    "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;  | 
|
// IE uses domainFromServer to generate an alist if server has not  | 
|
// provided one. Firefox/WebKit do not. Neither do we.  | 
|
//String domainFromServer = r.readSecurityBuffer(12, unicode);  | 
|
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) { | 
|
// Some client create a alist even if server does not send  | 
|
                // one: (i16)2 (i16)len target_in_unicode (i16)0 (i16) 0 | 
|
byte[] alist = ((inputFlags & 0x800000) != 0) ?  | 
|
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);  | 
|
}  | 
|
}  |