2728N/A/*
2728N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2728N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2728N/A *
2728N/A * This code is free software; you can redistribute it and/or modify it
2728N/A * under the terms of the GNU General Public License version 2 only, as
2728N/A * published by the Free Software Foundation.
2728N/A *
2728N/A * This code is distributed in the hope that it will be useful, but WITHOUT
2728N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2728N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2728N/A * version 2 for more details (a copy is included in the LICENSE file that
2728N/A * accompanied this code).
2728N/A *
2728N/A * You should have received a copy of the GNU General Public License version
2728N/A * 2 along with this work; if not, write to the Free Software Foundation,
2728N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2728N/A *
2728N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2728N/A * or visit www.oracle.com if you need additional information or have any
2728N/A * questions.
2728N/A */
2728N/A
2728N/A/*
2728N/A * @test
2728N/A * @bug 6911951
2728N/A * @summary NTLM should be a supported Java SASL mechanism
2728N/A */
2728N/Aimport java.io.IOException;
2728N/Aimport javax.security.sasl.*;
2728N/Aimport javax.security.auth.callback.*;
2728N/Aimport java.util.*;
2728N/A
2728N/Apublic class NTLMTest {
2728N/A
2728N/A private static final String MECH = "NTLM";
2728N/A private static final String REALM = "REALM";
2728N/A private static final String PROTOCOL = "jmx";
2728N/A private static final byte[] EMPTY = new byte[0];
2728N/A
2728N/A private static final String USER1 = "dummy";
2728N/A private static final char[] PASS1 = "bogus".toCharArray();
2728N/A private static final String USER2 = "foo";
2728N/A private static final char[] PASS2 = "bar".toCharArray();
2728N/A
2728N/A private static final Map<String,char[]> maps =
2728N/A new HashMap<String,char[]>();
2728N/A static {
2728N/A maps.put(USER1, PASS1);
2728N/A maps.put(USER2, PASS2);
2728N/A }
2728N/A
2728N/A static char[] getPass(String d, String u) {
2728N/A if (!d.equals(REALM)) return null;
2728N/A return maps.get(u);
2728N/A }
2728N/A
2728N/A public static void main(String[] args) throws Exception {
2728N/A
2728N/A checkAuthOnly();
2728N/A checkClientNameOverride();
2728N/A checkServerDomainOverride();
2728N/A checkClientDomainOverride();
2728N/A checkVersions();
2728N/A checkClientHostname();
2728N/A }
2728N/A
2728N/A static void checkVersions() throws Exception {
2728N/A // Server accepts all version
2728N/A checkVersion(null, null);
2728N/A checkVersion("LM/NTLM", null);
2728N/A checkVersion("LM", null);
2728N/A checkVersion("NTLM", null);
2728N/A checkVersion("NTLM2", null);
2728N/A checkVersion("LMv2/NTLMv2", null);
2728N/A checkVersion("LMv2", null);
2728N/A checkVersion("NTLMv2", null);
2728N/A
2728N/A // Client's default version is LMv2
2728N/A checkVersion(null, "LMv2");
2728N/A
2728N/A // Also works if they specified identical versions
2728N/A checkVersion("LM/NTLM", "LM");
2728N/A checkVersion("LM", "LM");
2728N/A checkVersion("NTLM", "LM");
2728N/A checkVersion("NTLM2", "NTLM2");
2728N/A checkVersion("LMv2/NTLMv2", "LMv2");
2728N/A checkVersion("LMv2", "LMv2");
2728N/A checkVersion("NTLMv2", "LMv2");
2728N/A
2728N/A // But should not work if different
2728N/A try {
2728N/A checkVersion("LM/NTLM", "LMv2");
2728N/A throw new Exception("Should not succeed");
2728N/A } catch (SaslException se) {
2809N/A // OK
2728N/A }
2728N/A try {
2728N/A checkVersion("LMv2/NTLMv2", "LM");
2728N/A throw new Exception("Should not succeed");
2728N/A } catch (SaslException se) {
2809N/A // OK
2728N/A }
2728N/A
2728N/A }
2728N/A
2728N/A /**
2728N/A * A test on version matching
2728N/A * @param vc ntlm version specified for client
2728N/A * @param vs ntlm version specified for server
2728N/A * @throws Exception
2728N/A */
2728N/A private static void checkVersion(String vc, String vs) throws Exception {
2728N/A Map<String,Object> pc = new HashMap<>();
2728N/A pc.put("com.sun.security.sasl.ntlm.version", vc);
2728N/A Map<String,Object> ps = new HashMap<>();
2728N/A ps.put("com.sun.security.sasl.ntlm.version", vs);
2728N/A SaslClient clnt = Sasl.createSaslClient(
2728N/A new String[]{MECH}, USER1, PROTOCOL, null, pc,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A NameCallback ncb = (NameCallback)cb;
2728N/A ncb.setName(ncb.getDefaultName());
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A ((PasswordCallback)cb).setPassword(PASS1);
2728N/A }
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, ps,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A String domain = null, name = null;
2728N/A PasswordCallback pcb = null;
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A name = ((NameCallback)cb).getDefaultName();
2728N/A } else if (cb instanceof RealmCallback) {
2728N/A domain = ((RealmCallback)cb).getDefaultText();
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A pcb = (PasswordCallback)cb;
2728N/A }
2728N/A }
2728N/A if (pcb != null) {
2728N/A pcb.setPassword(getPass(domain, name));
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A handshake(clnt, srv);
2728N/A }
2728N/A
2728N/A private static void checkClientHostname() throws Exception {
2728N/A Map<String,Object> pc = new HashMap<>();
2728N/A pc.put("com.sun.security.sasl.ntlm.hostname", "this.is.com");
2728N/A SaslClient clnt = Sasl.createSaslClient(
2728N/A new String[]{MECH}, USER1, PROTOCOL, null, pc,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A NameCallback ncb = (NameCallback)cb;
2728N/A ncb.setName(ncb.getDefaultName());
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A ((PasswordCallback)cb).setPassword(PASS1);
2728N/A }
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, null,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A String domain = null, name = null;
2728N/A PasswordCallback pcb = null;
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A name = ((NameCallback)cb).getDefaultName();
2728N/A } else if (cb instanceof RealmCallback) {
2728N/A domain = ((RealmCallback)cb).getDefaultText();
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A pcb = (PasswordCallback)cb;
2728N/A }
2728N/A }
2728N/A if (pcb != null) {
2728N/A pcb.setPassword(getPass(domain, name));
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A handshake(clnt, srv);
2728N/A if (!"this.is.com".equals(
2728N/A srv.getNegotiatedProperty("com.sun.security.sasl.ntlm.hostname"))) {
2728N/A throw new Exception("Hostname not trasmitted to server");
2728N/A }
2728N/A }
2728N/A
2728N/A /**
2728N/A * Client realm override, but finally overridden by server response
2728N/A */
2728N/A private static void checkClientDomainOverride() throws Exception {
2728N/A SaslClient clnt = Sasl.createSaslClient(
2728N/A new String[]{MECH}, USER1, PROTOCOL, "ANOTHERREALM", null,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A NameCallback ncb = (NameCallback)cb;
2728N/A ncb.setName(ncb.getDefaultName());
2728N/A } else if(cb instanceof RealmCallback) {
2728N/A RealmCallback dcb = (RealmCallback)cb;
2728N/A dcb.setText("THIRDDOMAIN");
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A ((PasswordCallback)cb).setPassword(PASS1);
2728N/A }
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, null,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A String domain = null, name = null;
2728N/A PasswordCallback pcb = null;
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A name = ((NameCallback)cb).getDefaultName();
2728N/A } else if (cb instanceof RealmCallback) {
2728N/A domain = ((RealmCallback)cb).getDefaultText();
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A pcb = (PasswordCallback)cb;
2728N/A }
2728N/A }
2728N/A if (pcb != null) {
2728N/A pcb.setPassword(getPass(domain, name));
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A handshake(clnt, srv);
2728N/A }
2728N/A
2728N/A /**
2728N/A * Client side user name provided in callback.
2728N/A * @throws Exception
2728N/A */
2728N/A private static void checkClientNameOverride() throws Exception {
2728N/A SaslClient clnt = Sasl.createSaslClient(
2728N/A new String[]{MECH}, null, PROTOCOL, null, null,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A NameCallback ncb = (NameCallback)cb;
2728N/A ncb.setName(USER1);
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A ((PasswordCallback)cb).setPassword(PASS1);
2728N/A }
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, REALM, null,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A String domain = null, name = null;
2728N/A PasswordCallback pcb = null;
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A name = ((NameCallback)cb).getDefaultName();
2728N/A } else if (cb instanceof RealmCallback) {
2728N/A domain = ((RealmCallback)cb).getDefaultText();
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A pcb = (PasswordCallback)cb;
2728N/A }
2728N/A }
2728N/A if (pcb != null) {
2728N/A pcb.setPassword(getPass(domain, name));
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A handshake(clnt, srv);
2728N/A }
2728N/A
2728N/A /**
2728N/A * server side domain provided in props.
2728N/A * @throws Exception
2728N/A */
2728N/A private static void checkServerDomainOverride() throws Exception {
2728N/A SaslClient clnt = Sasl.createSaslClient(
2728N/A new String[]{MECH}, USER1, PROTOCOL, null, null,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A NameCallback ncb = (NameCallback)cb;
2728N/A ncb.setName(ncb.getDefaultName());
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A ((PasswordCallback)cb).setPassword(PASS1);
2728N/A }
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A Map<String,Object> ps = new HashMap<>();
2728N/A ps.put("com.sun.security.sasl.ntlm.domain", REALM);
2728N/A SaslServer srv = Sasl.createSaslServer(MECH, PROTOCOL, null, ps,
2728N/A new CallbackHandler() {
2728N/A public void handle(Callback[] callbacks)
2728N/A throws IOException, UnsupportedCallbackException {
2728N/A String domain = null, name = null;
2728N/A PasswordCallback pcb = null;
2728N/A for (Callback cb: callbacks) {
2728N/A if (cb instanceof NameCallback) {
2728N/A name = ((NameCallback)cb).getDefaultName();
2728N/A } else if (cb instanceof RealmCallback) {
2728N/A domain = ((RealmCallback)cb).getDefaultText();
2728N/A } else if (cb instanceof PasswordCallback) {
2728N/A pcb = (PasswordCallback)cb;
2728N/A }
2728N/A }
2728N/A if (pcb != null) {
2728N/A pcb.setPassword(getPass(domain, name));
2728N/A }
2728N/A }
2728N/A });
2728N/A
2728N/A handshake(clnt, srv);
2728N/A }
2728N/A
2728N/A private static void checkAuthOnly() throws Exception {
2728N/A Map<String,Object> props = new HashMap<>();
2728N/A props.put(Sasl.QOP, "auth-conf");
2728N/A try {
2728N/A Sasl.createSaslClient(
2728N/A new String[]{MECH}, USER2, PROTOCOL, REALM, props, null);
2728N/A throw new Exception("NTLM should not support auth-conf");
2728N/A } catch (SaslException se) {
2728N/A // Normal
2728N/A }
2728N/A }
2728N/A
2728N/A private static void handshake(SaslClient clnt, SaslServer srv)
2728N/A throws Exception {
2728N/A if (clnt == null) {
2728N/A throw new IllegalStateException(
2728N/A "Unable to find client impl for " + MECH);
2728N/A }
2728N/A if (srv == null) {
2728N/A throw new IllegalStateException(
2728N/A "Unable to find server impl for " + MECH);
2728N/A }
2728N/A
2728N/A byte[] response = (clnt.hasInitialResponse()
2728N/A ? clnt.evaluateChallenge(EMPTY) : EMPTY);
2728N/A System.out.println("Initial:");
2728N/A new sun.misc.HexDumpEncoder().encodeBuffer(response, System.out);
2728N/A byte[] challenge;
2728N/A
2728N/A while (!clnt.isComplete() || !srv.isComplete()) {
2728N/A challenge = srv.evaluateResponse(response);
2728N/A response = null;
2728N/A if (challenge != null) {
2728N/A System.out.println("Challenge:");
2728N/A new sun.misc.HexDumpEncoder().encodeBuffer(challenge, System.out);
2728N/A response = clnt.evaluateChallenge(challenge);
2728N/A }
2728N/A if (response != null) {
2728N/A System.out.println("Response:");
2728N/A new sun.misc.HexDumpEncoder().encodeBuffer(response, System.out);
2728N/A }
2728N/A }
2728N/A
2728N/A if (clnt.isComplete() && srv.isComplete()) {
2728N/A System.out.println("SUCCESS");
2728N/A if (!srv.getAuthorizationID().equals(USER1)) {
2728N/A throw new Exception("Not correct user");
2728N/A }
2728N/A } else {
2728N/A throw new IllegalStateException(
2728N/A "FAILURE: mismatched state:"
2728N/A + " client complete? " + clnt.isComplete()
2728N/A + " server complete? " + srv.isComplete());
2728N/A }
2728N/A
2728N/A if (!clnt.getNegotiatedProperty(Sasl.QOP).equals("auth") ||
2728N/A !srv.getNegotiatedProperty(Sasl.QOP).equals("auth") ||
2728N/A !clnt.getNegotiatedProperty(
2728N/A "com.sun.security.sasl.ntlm.domain").equals(REALM)) {
2728N/A throw new Exception("Negotiated property error");
2728N/A }
2728N/A clnt.dispose();
2728N/A srv.dispose();
2728N/A }
2728N/A}