1/* 2 * Copyright (c) 2010, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 6911951 7150092 27 * @summary NTLM should be a supported Java SASL mechanism 28 * @modules java.base/sun.security.util 29 * java.security.sasl 30 */ 31import java.io.IOException; 32import javax.security.sasl.*; 33import javax.security.auth.callback.*; 34import java.util.*; 35import sun.security.util.HexDumpEncoder; 36 37public class NTLMTest { 38 39 private static final String MECH = "NTLM"; 40 private static final String REALM = "REALM"; 41 private static final String PROTOCOL = "jmx"; 42 private static final byte[] EMPTY = new byte[0]; 43 44 private static final String USER1 = "dummy"; 45 private static final char[] PASS1 = "bogus".toCharArray(); 46 private static final String USER2 = "foo"; 47 private static final char[] PASS2 = "bar".toCharArray(); 48 49 private static final Map<String,char[]> maps = 50 new HashMap<String,char[]>(); 51 static { 52 maps.put(USER1, PASS1); 53 maps.put(USER2, PASS2); 54 } 55 56 static char[] getPass(String d, String u) { 57 if (!d.equals(REALM)) return null; 58 return maps.get(u); 59 } 60 61 public static void main(String[] args) throws Exception { 62 63 checkAuthOnly(); 64 checkClientNameOverride(); 65 checkClientDomainOverride(); 66 checkVersions(); 67 checkClientHostname(); 68 } 69 70 static void checkVersions() throws Exception { 71 // Server accepts all version 72 checkVersion(null, null); 73 checkVersion("LM/NTLM", null); 74 checkVersion("LM", null); 75 checkVersion("NTLM", null); 76 checkVersion("NTLM2", null); 77 checkVersion("LMv2/NTLMv2", null); 78 checkVersion("LMv2", null); 79 checkVersion("NTLMv2", null); 80 81 // Client's default version is LMv2 82 checkVersion(null, "LMv2"); 83 84 // Also works if they specified identical versions 85 checkVersion("LM/NTLM", "LM"); 86 checkVersion("LM", "LM"); 87 checkVersion("NTLM", "LM"); 88 checkVersion("NTLM2", "NTLM2"); 89 checkVersion("LMv2/NTLMv2", "LMv2"); 90 checkVersion("LMv2", "LMv2"); 91 checkVersion("NTLMv2", "LMv2"); 92 93 // But should not work if different 94 try { 95 checkVersion("LM/NTLM", "LMv2"); 96 throw new Exception("Should not succeed"); 97 } catch (SaslException se) { 98 // OK 99 } 100 try { 101 checkVersion("LMv2/NTLMv2", "LM"); 102 throw new Exception("Should not succeed"); 103 } catch (SaslException se) { 104 // OK 105 } 106 107 } 108 109 /** 110 * A test on version matching 111 * @param vc ntlm version specified for client 112 * @param vs ntlm version specified for server 113 * @throws Exception 114 */ 115 private static void checkVersion(String vc, String vs) throws Exception { 116 Map<String,Object> pc = new HashMap<>(); 117 pc.put("com.sun.security.sasl.ntlm.version", vc); 118 Map<String,Object> ps = new HashMap<>(); 119 ps.put("com.sun.security.sasl.ntlm.version", vs); 120 SaslClient clnt = Sasl.createSaslClient( 121 new String[]{MECH}, USER1, PROTOCOL, REALM, pc, 122 new CallbackHandler() { 123 public void handle(Callback[] callbacks) 124 throws IOException, UnsupportedCallbackException { 125 for (Callback cb: callbacks) { 126 if (cb instanceof PasswordCallback) { 127 ((PasswordCallback)cb).setPassword(PASS1); 128 } 129 } 130 } 131 }); 132 133 SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, ps, 134 new CallbackHandler() { 135 public void handle(Callback[] callbacks) 136 throws IOException, UnsupportedCallbackException { 137 String domain = null, name = null; 138 PasswordCallback pcb = null; 139 for (Callback cb: callbacks) { 140 if (cb instanceof NameCallback) { 141 name = ((NameCallback)cb).getDefaultName(); 142 } else if (cb instanceof RealmCallback) { 143 domain = ((RealmCallback)cb).getDefaultText(); 144 } else if (cb instanceof PasswordCallback) { 145 pcb = (PasswordCallback)cb; 146 } 147 } 148 if (pcb != null) { 149 pcb.setPassword(getPass(domain, name)); 150 } 151 } 152 }); 153 154 handshake(clnt, srv); 155 } 156 157 private static void checkClientHostname() throws Exception { 158 Map<String,Object> pc = new HashMap<>(); 159 pc.put("com.sun.security.sasl.ntlm.hostname", "this.is.com"); 160 SaslClient clnt = Sasl.createSaslClient( 161 new String[]{MECH}, USER1, PROTOCOL, REALM, pc, 162 new CallbackHandler() { 163 public void handle(Callback[] callbacks) 164 throws IOException, UnsupportedCallbackException { 165 for (Callback cb: callbacks) { 166 if (cb instanceof PasswordCallback) { 167 ((PasswordCallback)cb).setPassword(PASS1); 168 } 169 } 170 } 171 }); 172 173 SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, null, 174 new CallbackHandler() { 175 public void handle(Callback[] callbacks) 176 throws IOException, UnsupportedCallbackException { 177 String domain = null, name = null; 178 PasswordCallback pcb = null; 179 for (Callback cb: callbacks) { 180 if (cb instanceof NameCallback) { 181 name = ((NameCallback)cb).getDefaultName(); 182 } else if (cb instanceof RealmCallback) { 183 domain = ((RealmCallback)cb).getDefaultText(); 184 } else if (cb instanceof PasswordCallback) { 185 pcb = (PasswordCallback)cb; 186 } 187 } 188 if (pcb != null) { 189 pcb.setPassword(getPass(domain, name)); 190 } 191 } 192 }); 193 194 handshake(clnt, srv); 195 if (!"this.is.com".equals( 196 srv.getNegotiatedProperty("com.sun.security.sasl.ntlm.hostname"))) { 197 throw new Exception("Hostname not trasmitted to server"); 198 } 199 } 200 201 /** 202 * Client realm override, but finally overridden by server response 203 */ 204 private static void checkClientDomainOverride() throws Exception { 205 SaslClient clnt = Sasl.createSaslClient( 206 new String[]{MECH}, USER1, PROTOCOL, "ANOTHERREALM", null, 207 new CallbackHandler() { 208 public void handle(Callback[] callbacks) 209 throws IOException, UnsupportedCallbackException { 210 for (Callback cb: callbacks) { 211 if (cb instanceof RealmCallback) { 212 ((RealmCallback)cb).setText(REALM); 213 } else if (cb instanceof PasswordCallback) { 214 ((PasswordCallback)cb).setPassword(PASS1); 215 } 216 } 217 } 218 }); 219 220 SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, null, 221 new CallbackHandler() { 222 public void handle(Callback[] callbacks) 223 throws IOException, UnsupportedCallbackException { 224 String domain = null, name = null; 225 PasswordCallback pcb = null; 226 for (Callback cb: callbacks) { 227 if (cb instanceof NameCallback) { 228 name = ((NameCallback)cb).getDefaultName(); 229 } else if (cb instanceof RealmCallback) { 230 domain = ((RealmCallback)cb).getDefaultText(); 231 } else if (cb instanceof PasswordCallback) { 232 pcb = (PasswordCallback)cb; 233 } 234 } 235 if (pcb != null) { 236 pcb.setPassword(getPass(domain, name)); 237 } 238 } 239 }); 240 241 handshake(clnt, srv); 242 } 243 244 /** 245 * Client side user name provided in callback. 246 * @throws Exception 247 */ 248 private static void checkClientNameOverride() throws Exception { 249 SaslClient clnt = Sasl.createSaslClient( 250 new String[]{MECH}, "someone", PROTOCOL, REALM, null, 251 new CallbackHandler() { 252 public void handle(Callback[] callbacks) 253 throws IOException, UnsupportedCallbackException { 254 for (Callback cb: callbacks) { 255 if (cb instanceof NameCallback) { 256 NameCallback ncb = (NameCallback) cb; 257 ncb.setName(USER1); 258 } else if (cb instanceof PasswordCallback) { 259 ((PasswordCallback)cb).setPassword(PASS1); 260 } 261 } 262 } 263 }); 264 265 SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, "FAKE", null, 266 new CallbackHandler() { 267 public void handle(Callback[] callbacks) 268 throws IOException, UnsupportedCallbackException { 269 String domain = null, name = null; 270 PasswordCallback pcb = null; 271 for (Callback cb: callbacks) { 272 if (cb instanceof NameCallback) { 273 name = ((NameCallback)cb).getDefaultName(); 274 } else if (cb instanceof RealmCallback) { 275 domain = ((RealmCallback)cb).getDefaultText(); 276 } else if (cb instanceof PasswordCallback) { 277 pcb = (PasswordCallback)cb; 278 } 279 } 280 if (pcb != null) { 281 pcb.setPassword(getPass(domain, name)); 282 } 283 } 284 }); 285 286 handshake(clnt, srv); 287 } 288 289 private static void checkAuthOnly() throws Exception { 290 Map<String,Object> props = new HashMap<>(); 291 props.put(Sasl.QOP, "auth-conf"); 292 try { 293 Sasl.createSaslClient( 294 new String[]{MECH}, USER2, PROTOCOL, REALM, props, null); 295 throw new Exception("NTLM should not support auth-conf"); 296 } catch (SaslException se) { 297 // Normal 298 } 299 } 300 301 private static void handshake(SaslClient clnt, SaslServer srv) 302 throws Exception { 303 if (clnt == null) { 304 throw new IllegalStateException( 305 "Unable to find client impl for " + MECH); 306 } 307 if (srv == null) { 308 throw new IllegalStateException( 309 "Unable to find server impl for " + MECH); 310 } 311 312 byte[] response = (clnt.hasInitialResponse() 313 ? clnt.evaluateChallenge(EMPTY) : EMPTY); 314 System.out.println("Initial:"); 315 new HexDumpEncoder().encodeBuffer(response, System.out); 316 byte[] challenge; 317 318 while (!clnt.isComplete() || !srv.isComplete()) { 319 challenge = srv.evaluateResponse(response); 320 response = null; 321 if (challenge != null) { 322 System.out.println("Challenge:"); 323 new HexDumpEncoder().encodeBuffer(challenge, System.out); 324 response = clnt.evaluateChallenge(challenge); 325 } 326 if (response != null) { 327 System.out.println("Response:"); 328 new HexDumpEncoder().encodeBuffer(response, System.out); 329 } 330 } 331 332 if (clnt.isComplete() && srv.isComplete()) { 333 System.out.println("SUCCESS"); 334 if (!srv.getAuthorizationID().equals(USER1)) { 335 throw new Exception("Not correct user"); 336 } 337 } else { 338 throw new IllegalStateException( 339 "FAILURE: mismatched state:" 340 + " client complete? " + clnt.isComplete() 341 + " server complete? " + srv.isComplete()); 342 } 343 344 if (!clnt.getNegotiatedProperty(Sasl.QOP).equals("auth") || 345 !srv.getNegotiatedProperty(Sasl.QOP).equals("auth") || 346 !clnt.getNegotiatedProperty( 347 "com.sun.security.sasl.ntlm.domain").equals(REALM)) { 348 throw new Exception("Negotiated property error"); 349 } 350 clnt.dispose(); 351 srv.dispose(); 352 } 353} 354