Path: blob/master/src/java.base/share/classes/com/sun/security/ntlm/Client.java
41161 views
/*1* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.security.ntlm;2627import java.math.BigInteger;28import java.util.Arrays;29import java.util.Date;30import java.util.Locale;3132/**33* The NTLM client. Not multi-thread enabled.<p>34* Example:35* <pre>36* Client client = new Client(null, "host", "dummy",37* "REALM", "t0pSeCr3t".toCharArray());38* byte[] type1 = client.type1();39* // Send type1 to server and receive response as type240* byte[] type3 = client.type3(type2, nonce);41* // Send type3 to server42* </pre>43*/44public final class Client extends NTLM {45private final String hostname;46private final String username;4748private String domain;49private byte[] pw1, pw2;5051/**52* Creates an NTLM Client instance.53* @param version the NTLM version to use, which can be:54* <ul>55* <li>LM/NTLM: Original NTLM v156* <li>LM: Original NTLM v1, LM only57* <li>NTLM: Original NTLM v1, NTLM only58* <li>NTLM2: NTLM v1 with Client Challenge59* <li>LMv2/NTLMv2: NTLM v260* <li>LMv2: NTLM v2, LM only61* <li>NTLMv2: NTLM v2, NTLM only62* </ul>63* If null, "LMv2/NTLMv2" will be used.64* @param hostname hostname of the client, can be null65* @param username username to be authenticated, must not be null66* @param domain domain of {@code username}, can be null67* @param password password for {@code username}, must not be not null.68* This method does not make any modification to this parameter, it neither69* needs to access the content of this parameter after this method call,70* so you are free to modify or nullify this parameter after this call.71* @throws NTLMException if {@code username} or {@code password} is null,72* or {@code version} is illegal.73*74*/75public Client(String version, String hostname, String username,76String domain, char[] password) throws NTLMException {77super(version);78if ((username == null || password == null)) {79throw new NTLMException(NTLMException.PROTOCOL,80"username/password cannot be null");81}82this.hostname = hostname;83this.username = username;84this.domain = domain == null ? "" : domain;85this.pw1 = getP1(password);86this.pw2 = getP2(password);87debug("NTLM Client: (h,u,t,version(v)) = (%s,%s,%s,%s(%s))\n",88hostname, username, domain, version, v.toString());89}9091/**92* Generates the Type 1 message93* @return the message generated94*/95public byte[] type1() {96Writer p = new Writer(1, 32);97// Negotiate always sign, Negotiate NTLM,98// Request Target, Negotiate OEM, Negotiate unicode99int flags = 0x8207;100if (v != Version.NTLM) {101flags |= 0x80000;102}103p.writeInt(12, flags);104debug("NTLM Client: Type 1 created\n");105debug(p.getBytes());106return p.getBytes();107}108109/**110* Generates the Type 3 message111* @param type2 the responding Type 2 message from server, must not be null112* @param nonce random 8-byte array to be used in message generation,113* must not be null except for original NTLM v1114* @return the message generated115* @throws NTLMException if the incoming message is invalid, or116* {@code nonce} is null for NTLM v1.117*/118public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException {119if (type2 == null || (v != Version.NTLM && nonce == null)) {120throw new NTLMException(NTLMException.PROTOCOL,121"type2 and nonce cannot be null");122}123debug("NTLM Client: Type 2 received\n");124debug(type2);125Reader r = new Reader(type2);126byte[] challenge = r.readBytes(24, 8);127int inputFlags = r.readInt(20);128boolean unicode = (inputFlags & 1) == 1;129130// IE uses domainFromServer to generate an alist if server has not131// provided one. Firefox/WebKit do not. Neither do we.132//String domainFromServer = r.readSecurityBuffer(12, unicode);133134int flags = 0x88200 | (inputFlags & 3);135Writer p = new Writer(3, 64);136byte[] lm = null, ntlm = null;137138p.writeSecurityBuffer(28, domain, unicode);139p.writeSecurityBuffer(36, username, unicode);140p.writeSecurityBuffer(44, hostname, unicode);141142if (v == Version.NTLM) {143byte[] lmhash = calcLMHash(pw1);144byte[] nthash = calcNTHash(pw2);145if (writeLM) lm = calcResponse (lmhash, challenge);146if (writeNTLM) ntlm = calcResponse (nthash, challenge);147} else if (v == Version.NTLM2) {148byte[] nthash = calcNTHash(pw2);149lm = ntlm2LM(nonce);150ntlm = ntlm2NTLM(nthash, nonce, challenge);151} else {152byte[] nthash = calcNTHash(pw2);153if (writeLM) lm = calcV2(nthash,154username.toUpperCase(Locale.US)+domain, nonce, challenge);155if (writeNTLM) {156// Some client create a alist even if server does not send157// one: (i16)2 (i16)len target_in_unicode (i16)0 (i16) 0158byte[] alist = ((inputFlags & 0x800000) != 0) ?159r.readSecurityBuffer(40) : new byte[0];160byte[] blob = new byte[32+alist.length];161System.arraycopy(new byte[]{1,1,0,0,0,0,0,0}, 0, blob, 0, 8);162// TS163byte[] time = BigInteger.valueOf(new Date().getTime())164.add(new BigInteger("11644473600000"))165.multiply(BigInteger.valueOf(10000))166.toByteArray();167for (int i=0; i<time.length; i++) {168blob[8+time.length-i-1] = time[i];169}170System.arraycopy(nonce, 0, blob, 16, 8);171System.arraycopy(new byte[]{0,0,0,0}, 0, blob, 24, 4);172System.arraycopy(alist, 0, blob, 28, alist.length);173System.arraycopy(new byte[]{0,0,0,0}, 0,174blob, 28+alist.length, 4);175ntlm = calcV2(nthash, username.toUpperCase(Locale.US)+domain,176blob, challenge);177}178}179p.writeSecurityBuffer(12, lm);180p.writeSecurityBuffer(20, ntlm);181p.writeSecurityBuffer(52, new byte[0]);182183p.writeInt(60, flags);184debug("NTLM Client: Type 3 created\n");185debug(p.getBytes());186return p.getBytes();187}188189/**190* Returns the domain value provided by server after the authentication191* is complete, or the domain value provided by the client before it.192* @return the domain193*/194public String getDomain() {195return domain;196}197198/**199* Disposes any password-derived information.200*/201public void dispose() {202Arrays.fill(pw1, (byte)0);203Arrays.fill(pw2, (byte)0);204}205}206207208