Server.java revision 12745:f068a4ffddd2
1 2/* 3 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27package com.sun.security.ntlm; 28 29import java.util.Arrays; 30import java.util.Locale; 31 32/** 33 * The NTLM server, not multi-thread enabled.<p> 34 * Example: 35 * <pre> 36 * Server server = new Server(null, "REALM") { 37 * public char[] getPassword(String ntdomain, String username) { 38 * switch (username) { 39 * case "dummy": return "t0pSeCr3t".toCharArray(); 40 * case "guest": return "".toCharArray(); 41 * default: return null; 42 * } 43 * } 44 * }; 45 * // Receive client request as type1 46 * byte[] type2 = server.type2(type1, nonce); 47 * // Send type2 to client and receive type3 48 * verify(type3, nonce); 49 * </pre> 50 */ 51public abstract class Server extends NTLM { 52 private final String domain; 53 private final boolean allVersion; 54 /** 55 * Creates a Server instance. 56 * @param version the NTLM version to use, which can be: 57 * <ul> 58 * <li>NTLM: Original NTLM v1 59 * <li>NTLM2: NTLM v1 with Client Challenge 60 * <li>NTLMv2: NTLM v2 61 * </ul> 62 * If null, all versions will be supported. Please note that unless NTLM2 63 * is selected, authentication succeeds if one of LM (or LMv2) or 64 * NTLM (or NTLMv2) is verified. 65 * @param domain the domain, must not be null 66 * @throws NTLMException if {@code domain} is null. 67 */ 68 public Server(String version, String domain) throws NTLMException { 69 super(version); 70 if (domain == null) { 71 throw new NTLMException(NTLMException.PROTOCOL, 72 "domain cannot be null"); 73 } 74 this.allVersion = (version == null); 75 this.domain = domain; 76 debug("NTLM Server: (t,version) = (%s,%s)\n", domain, version); 77 } 78 79 /** 80 * Generates the Type 2 message 81 * @param type1 the Type1 message received, must not be null 82 * @param nonce the random 8-byte array to be used in message generation, 83 * must not be null 84 * @return the message generated 85 * @throws NTLMException if the incoming message is invalid, or 86 * {@code nonce} is null. 87 */ 88 public byte[] type2(byte[] type1, byte[] nonce) throws NTLMException { 89 if (nonce == null) { 90 throw new NTLMException(NTLMException.PROTOCOL, 91 "nonce cannot be null"); 92 } 93 debug("NTLM Server: Type 1 received\n"); 94 if (type1 != null) debug(type1); 95 Writer p = new Writer(2, 32); 96 // Negotiate NTLM2 Key, Target Type Domain, 97 // Negotiate NTLM, Request Target, Negotiate unicode 98 int flags = 0x90205; 99 p.writeSecurityBuffer(12, domain, true); 100 p.writeInt(20, flags); 101 p.writeBytes(24, nonce); 102 debug("NTLM Server: Type 2 created\n"); 103 debug(p.getBytes()); 104 return p.getBytes(); 105 } 106 107 /** 108 * Verifies the Type3 message received from client and returns 109 * various negotiated information. 110 * @param type3 the incoming Type3 message from client, must not be null 111 * @param nonce the same nonce provided in {@link #type2}, must not be null 112 * @return client username, client hostname, and the request target 113 * @throws NTLMException if the incoming message is invalid, or 114 * {@code nonce} is null. 115 */ 116 public String[] verify(byte[] type3, byte[] nonce) 117 throws NTLMException { 118 if (type3 == null || nonce == null) { 119 throw new NTLMException(NTLMException.PROTOCOL, 120 "type1 or nonce cannot be null"); 121 } 122 debug("NTLM Server: Type 3 received\n"); 123 if (type3 != null) debug(type3); 124 Reader r = new Reader(type3); 125 String username = r.readSecurityBuffer(36, true); 126 String hostname = r.readSecurityBuffer(44, true); 127 String incomingDomain = r.readSecurityBuffer(28, true); 128 /*if (incomingDomain != null && !incomingDomain.equals(domain)) { 129 throw new NTLMException(NTLMException.DOMAIN_UNMATCH, 130 "Wrong domain: " + incomingDomain + 131 " vs " + domain); // Needed? 132 }*/ 133 134 boolean verified = false; 135 char[] password = getPassword(incomingDomain, username); 136 if (password == null) { 137 throw new NTLMException(NTLMException.USER_UNKNOWN, 138 "Unknown user"); 139 } 140 byte[] incomingLM = r.readSecurityBuffer(12); 141 byte[] incomingNTLM = r.readSecurityBuffer(20); 142 143 if (!verified && (allVersion || v == Version.NTLM)) { 144 if (incomingLM.length > 0) { 145 byte[] pw1 = getP1(password); 146 byte[] lmhash = calcLMHash(pw1); 147 byte[] lmresponse = calcResponse (lmhash, nonce); 148 if (Arrays.equals(lmresponse, incomingLM)) { 149 verified = true; 150 } 151 } 152 if (incomingNTLM.length > 0) { 153 byte[] pw2 = getP2(password); 154 byte[] nthash = calcNTHash(pw2); 155 byte[] ntresponse = calcResponse (nthash, nonce); 156 if (Arrays.equals(ntresponse, incomingNTLM)) { 157 verified = true; 158 } 159 } 160 debug("NTLM Server: verify using NTLM: " + verified + "\n"); 161 } 162 if (!verified && (allVersion || v == Version.NTLM2)) { 163 byte[] pw2 = getP2(password); 164 byte[] nthash = calcNTHash(pw2); 165 byte[] clientNonce = Arrays.copyOf(incomingLM, 8); 166 byte[] ntlmresponse = ntlm2NTLM(nthash, clientNonce, nonce); 167 if (Arrays.equals(incomingNTLM, ntlmresponse)) { 168 verified = true; 169 } 170 debug("NTLM Server: verify using NTLM2: " + verified + "\n"); 171 } 172 if (!verified && (allVersion || v == Version.NTLMv2)) { 173 byte[] pw2 = getP2(password); 174 byte[] nthash = calcNTHash(pw2); 175 if (incomingLM.length > 0) { 176 byte[] clientNonce = Arrays.copyOfRange( 177 incomingLM, 16, incomingLM.length); 178 byte[] lmresponse = calcV2(nthash, 179 username.toUpperCase(Locale.US)+incomingDomain, 180 clientNonce, nonce); 181 if (Arrays.equals(lmresponse, incomingLM)) { 182 verified = true; 183 } 184 } 185 if (incomingNTLM.length > 0) { 186 // We didn't sent alist in type2(), so there 187 // is nothing to check here. 188 byte[] clientBlob = Arrays.copyOfRange( 189 incomingNTLM, 16, incomingNTLM.length); 190 byte[] ntlmresponse = calcV2(nthash, 191 username.toUpperCase(Locale.US)+incomingDomain, 192 clientBlob, nonce); 193 if (Arrays.equals(ntlmresponse, incomingNTLM)) { 194 verified = true; 195 } 196 } 197 debug("NTLM Server: verify using NTLMv2: " + verified + "\n"); 198 } 199 if (!verified) { 200 throw new NTLMException(NTLMException.AUTH_FAILED, 201 "None of LM and NTLM verified"); 202 } 203 return new String[] {username, hostname, incomingDomain}; 204 } 205 206 /** 207 * Retrieves the password for a given user. This method should be 208 * overridden in a concrete class. 209 * @param domain can be null 210 * @param username must not be null 211 * @return the password for the user, or null if unknown 212 */ 213 public abstract char[] getPassword(String domain, String username); 214} 215