test/jdk/sun/net/www/protocol/http/NoNTLM.java
changeset 47216 71c04702a3d5
parent 42338 a60f280f803c
child 54314 46cf212cdcca
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/net/www/protocol/http/NoNTLM.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8004502
+ * @summary Sanity check that NTLM will not be selected by the http protocol
+ *    handler when running on a profile that does not support NTLM
+ * @modules java.base/sun.net.www
+ *          java.base/sun.net.www.protocol.http:open
+ * @run main/othervm NoNTLM
+ */
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.Authenticator;
+import java.net.HttpURLConnection;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import sun.net.www.MessageHeader;
+
+public class NoNTLM {
+
+    static final String CRLF = "\r\n";
+
+    static final String OKAY =
+        "HTTP/1.1 200" + CRLF +
+        "Content-Length: 0" + CRLF +
+        "Connection: close" + CRLF +
+        CRLF;
+
+    static class Client implements Runnable {
+        private final URL url;
+        private volatile IOException ioe;
+        private volatile int respCode;
+
+        Client(int port) throws IOException {
+            this.url = new URL("http://127.0.0.1:" + port + "/foo.html");
+        }
+
+        public void run() {
+            try {
+                HttpURLConnection uc =
+                    (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
+                try {
+                    uc.getInputStream();
+                } catch (IOException x) {
+                    respCode = uc.getResponseCode();
+                    throw x;
+                }
+                uc.disconnect();
+            } catch (IOException x) {
+                if (respCode == 0)
+                    respCode = -1;
+                ioe = x;
+            }
+        }
+
+        IOException ioException() {
+            return ioe;
+        }
+
+        int respCode() {
+            return respCode;
+        }
+
+        static void start(int port) throws IOException {
+            Client client = new Client(port);
+            new Thread(client).start();
+        }
+    }
+
+    /**
+     * Return the http response with WWW-Authenticate headers for the given
+     * authentication schemes.
+     */
+    static String authReplyFor(String... schemes) {
+        // construct the server reply
+        String reply = "HTTP/1.1 401 Unauthorized" + CRLF +
+                       "Content-Length: 0"+ CRLF +
+                       "Connection: close" + CRLF;
+        for (String s: schemes) {
+            switch (s) {
+                case "Basic" :
+                    reply += "WWW-Authenticate: Basic realm=\"wallyworld\"" + CRLF;
+                    break;
+                case "Digest" :
+                    reply += "WWW-Authenticate: Digest" +
+                             " realm=\"wallyworld\"" +
+                             " domain=/" +
+                             " nonce=\"abcdefghijklmnopqrstuvwxyz\"" +
+                             " qop=\"auth\"" + CRLF;
+                    break;
+                case "NTLM" :
+                    reply += "WWW-Authenticate: NTLM" + CRLF;
+                    break;
+                default :
+                    throw new RuntimeException("Should not get here");
+            }
+        }
+        reply += CRLF;
+        return reply;
+    }
+
+    /**
+     * Test the http protocol handler with the given authentication schemes
+     * in the WWW-Authenticate header.
+     */
+    static void test(String... schemes) throws IOException {
+
+        // the authentication scheme that the client is expected to choose
+        String expected = null;
+        for (String s: schemes) {
+            if (expected == null) {
+                expected = s;
+            } else if (s.equals("Digest")) {
+                expected = s;
+            }
+        }
+
+        // server reply
+        String reply = authReplyFor(schemes);
+
+        System.out.println("====================================");
+        System.out.println("Expect client to choose: " + expected);
+        System.out.println(reply);
+
+        try (ServerSocket ss = new ServerSocket(0)) {
+            Client.start(ss.getLocalPort());
+
+            // client ---- GET ---> server
+            // client <--- 401 ---- server
+            try (Socket s = ss.accept()) {
+                new MessageHeader().parseHeader(s.getInputStream());
+                s.getOutputStream().write(reply.getBytes("US-ASCII"));
+            }
+
+            // client ---- GET ---> server
+            // client <--- 200 ---- server
+            String auth;
+            try (Socket s = ss.accept()) {
+                MessageHeader mh = new MessageHeader();
+                mh.parseHeader(s.getInputStream());
+                s.getOutputStream().write(OKAY.getBytes("US-ASCII"));
+                auth = mh.findValue("Authorization");
+            }
+
+            // check Authorization header
+            if (auth == null)
+                throw new RuntimeException("Authorization header not found");
+            System.out.println("Server received Authorization header: " + auth);
+            String[] values = auth.split(" ");
+            if (!values[0].equals(expected))
+                throw new RuntimeException("Unexpected value");
+        }
+    }
+
+    /**
+     * Test the http protocol handler with one WWW-Authenticate header with
+     * the value "NTLM".
+     */
+    static void testNTLM() throws Exception {
+        // server reply
+        String reply = authReplyFor("NTLM");
+
+        System.out.println("====================================");
+        System.out.println("Expect client to fail with 401 Unauthorized");
+        System.out.println(reply);
+
+        try (ServerSocket ss = new ServerSocket(0)) {
+            Client client = new Client(ss.getLocalPort());
+            Thread thr = new Thread(client);
+            thr.start();
+
+            // client ---- GET ---> server
+            // client <--- 401 ---- client
+            try (Socket s = ss.accept()) {
+                new MessageHeader().parseHeader(s.getInputStream());
+                s.getOutputStream().write(reply.getBytes("US-ASCII"));
+            }
+
+            // the client should fail with 401
+            System.out.println("Waiting for client to terminate");
+            thr.join();
+            IOException ioe = client.ioException();
+            if (ioe != null)
+                System.out.println("Client failed: " + ioe);
+            int respCode = client.respCode();
+            if (respCode != 0 && respCode != -1)
+                System.out.println("Client received HTTP response code: " + respCode);
+            if (respCode != HttpURLConnection.HTTP_UNAUTHORIZED)
+                throw new RuntimeException("Unexpected response code");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        try {
+            Class<?> ntlmProxyClass = Class.forName("sun.net.www.protocol.http.NTLMAuthenticationProxy", true, NoNTLM.class.getClassLoader());
+            Field ntlmSupportedField = ntlmProxyClass.getDeclaredField("supported");
+            ntlmSupportedField.setAccessible(true);
+            if (ntlmSupportedField.getBoolean(null)) {
+                System.out.println("NTLM is supported. Nothing to do. Exiting.");
+                return;
+            }
+        } catch (ClassNotFoundException okay) { }
+
+        // setup Authenticator
+        Authenticator.setDefault(new Authenticator() {
+            @Override
+            protected PasswordAuthentication getPasswordAuthentication() {
+                return new PasswordAuthentication("user", "pass".toCharArray());
+            }
+        });
+
+        // test combinations of authentication schemes
+        test("Basic");
+        test("Digest");
+        test("Basic", "Digest");
+        test("Basic", "NTLM");
+        test("Digest", "NTLM");
+        test("Basic", "Digest", "NTLM");
+
+        // test NTLM only, this should fail with "401 Unauthorized"
+        testNTLM();
+
+        System.out.println();
+        System.out.println("TEST PASSED");
+    }
+}
+