Client.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.security.ntlm; 27 28import java.math.BigInteger; 29import java.util.Arrays; 30import java.util.Date; 31import java.util.Locale; 32 33/** 34 * The NTLM client. Not multi-thread enabled.<p> 35 * Example: 36 * <pre> 37 * Client client = new Client(null, "host", "dummy", 38 * "REALM", "t0pSeCr3t".toCharArray()); 39 * byte[] type1 = client.type1(); 40 * // Send type1 to server and receive response as type2 41 * byte[] type3 = client.type3(type2, nonce); 42 * // Send type3 to server 43 * </pre> 44 */ 45public final class Client extends NTLM { 46 private final String hostname; 47 private final String username; 48 49 private String domain; 50 private byte[] pw1, pw2; 51 52 /** 53 * Creates an NTLM Client instance. 54 * @param version the NTLM version to use, which can be: 55 * <ul> 56 * <li>LM/NTLM: Original NTLM v1 57 * <li>LM: Original NTLM v1, LM only 58 * <li>NTLM: Original NTLM v1, NTLM only 59 * <li>NTLM2: NTLM v1 with Client Challenge 60 * <li>LMv2/NTLMv2: NTLM v2 61 * <li>LMv2: NTLM v2, LM only 62 * <li>NTLMv2: NTLM v2, NTLM only 63 * </ul> 64 * If null, "LMv2/NTLMv2" will be used. 65 * @param hostname hostname of the client, can be null 66 * @param username username to be authenticated, must not be null 67 * @param domain domain of {@code username}, can be null 68 * @param password password for {@code username}, must not be not null. 69 * This method does not make any modification to this parameter, it neither 70 * needs to access the content of this parameter after this method call, 71 * so you are free to modify or nullify this parameter after this call. 72 * @throws NTLMException if {@code username} or {@code password} is null, 73 * or {@code version} is illegal. 74 * 75 */ 76 public Client(String version, String hostname, String username, 77 String domain, char[] password) throws NTLMException { 78 super(version); 79 if ((username == null || password == null)) { 80 throw new NTLMException(NTLMException.PROTOCOL, 81 "username/password cannot be null"); 82 } 83 this.hostname = hostname; 84 this.username = username; 85 this.domain = domain == null ? "" : domain; 86 this.pw1 = getP1(password); 87 this.pw2 = getP2(password); 88 debug("NTLM Client: (h,u,t,version(v)) = (%s,%s,%s,%s(%s))\n", 89 hostname, username, domain, version, v.toString()); 90 } 91 92 /** 93 * Generates the Type 1 message 94 * @return the message generated 95 */ 96 public byte[] type1() { 97 Writer p = new Writer(1, 32); 98 // Negotiate always sign, Negotiate NTLM, 99 // Request Target, Negotiate OEM, Negotiate unicode 100 int flags = 0x8207; 101 if (v != Version.NTLM) { 102 flags |= 0x80000; 103 } 104 p.writeInt(12, flags); 105 debug("NTLM Client: Type 1 created\n"); 106 debug(p.getBytes()); 107 return p.getBytes(); 108 } 109 110 /** 111 * Generates the Type 3 message 112 * @param type2 the responding Type 2 message from server, must not be null 113 * @param nonce random 8-byte array to be used in message generation, 114 * must not be null except for original NTLM v1 115 * @return the message generated 116 * @throws NTLMException if the incoming message is invalid, or 117 * {@code nonce} is null for NTLM v1. 118 */ 119 public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException { 120 if (type2 == null || (v != Version.NTLM && nonce == null)) { 121 throw new NTLMException(NTLMException.PROTOCOL, 122 "type2 and nonce cannot be null"); 123 } 124 debug("NTLM Client: Type 2 received\n"); 125 debug(type2); 126 Reader r = new Reader(type2); 127 byte[] challenge = r.readBytes(24, 8); 128 int inputFlags = r.readInt(20); 129 boolean unicode = (inputFlags & 1) == 1; 130 131 // IE uses domainFromServer to generate an alist if server has not 132 // provided one. Firefox/WebKit do not. Neither do we. 133 //String domainFromServer = r.readSecurityBuffer(12, unicode); 134 135 int flags = 0x88200 | (inputFlags & 3); 136 Writer p = new Writer(3, 64); 137 byte[] lm = null, ntlm = null; 138 139 p.writeSecurityBuffer(28, domain, unicode); 140 p.writeSecurityBuffer(36, username, unicode); 141 p.writeSecurityBuffer(44, hostname, unicode); 142 143 if (v == Version.NTLM) { 144 byte[] lmhash = calcLMHash(pw1); 145 byte[] nthash = calcNTHash(pw2); 146 if (writeLM) lm = calcResponse (lmhash, challenge); 147 if (writeNTLM) ntlm = calcResponse (nthash, challenge); 148 } else if (v == Version.NTLM2) { 149 byte[] nthash = calcNTHash(pw2); 150 lm = ntlm2LM(nonce); 151 ntlm = ntlm2NTLM(nthash, nonce, challenge); 152 } else { 153 byte[] nthash = calcNTHash(pw2); 154 if (writeLM) lm = calcV2(nthash, 155 username.toUpperCase(Locale.US)+domain, nonce, challenge); 156 if (writeNTLM) { 157 // Some client create a alist even if server does not send 158 // one: (i16)2 (i16)len target_in_unicode (i16)0 (i16) 0 159 byte[] alist = ((inputFlags & 0x800000) != 0) ? 160 r.readSecurityBuffer(40) : new byte[0]; 161 byte[] blob = new byte[32+alist.length]; 162 System.arraycopy(new byte[]{1,1,0,0,0,0,0,0}, 0, blob, 0, 8); 163 // TS 164 byte[] time = BigInteger.valueOf(new Date().getTime()) 165 .add(new BigInteger("11644473600000")) 166 .multiply(BigInteger.valueOf(10000)) 167 .toByteArray(); 168 for (int i=0; i<time.length; i++) { 169 blob[8+time.length-i-1] = time[i]; 170 } 171 System.arraycopy(nonce, 0, blob, 16, 8); 172 System.arraycopy(new byte[]{0,0,0,0}, 0, blob, 24, 4); 173 System.arraycopy(alist, 0, blob, 28, alist.length); 174 System.arraycopy(new byte[]{0,0,0,0}, 0, 175 blob, 28+alist.length, 4); 176 ntlm = calcV2(nthash, username.toUpperCase(Locale.US)+domain, 177 blob, challenge); 178 } 179 } 180 p.writeSecurityBuffer(12, lm); 181 p.writeSecurityBuffer(20, ntlm); 182 p.writeSecurityBuffer(52, new byte[0]); 183 184 p.writeInt(60, flags); 185 debug("NTLM Client: Type 3 created\n"); 186 debug(p.getBytes()); 187 return p.getBytes(); 188 } 189 190 /** 191 * Returns the domain value provided by server after the authentication 192 * is complete, or the domain value provided by the client before it. 193 * @return the domain 194 */ 195 public String getDomain() { 196 return domain; 197 } 198 199 /** 200 * Disposes any password-derived information. 201 */ 202 public void dispose() { 203 Arrays.fill(pw1, (byte)0); 204 Arrays.fill(pw2, (byte)0); 205 } 206} 207