8225425: java.lang.UnsatisfiedLinkError: net.dll: Can't find dependent libraries
authorchegar
Mon, 19 Aug 2019 14:28:43 +0100
changeset 57796 56df9a08ed9c
parent 57795 2e58f5d927a6
child 57797 d48a1703ad6e
8225425: java.lang.UnsatisfiedLinkError: net.dll: Can't find dependent libraries Reviewed-by: dfuchs, alanb, erikj
make/lib/Lib-java.base.gmk
src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java
src/java.base/windows/native/libnet/NTLMAuthentication.c
test/jdk/sun/net/www/protocol/http/TestTransparentNTLM.java
--- a/make/lib/Lib-java.base.gmk	Mon Aug 19 15:36:07 2019 +0300
+++ b/make/lib/Lib-java.base.gmk	Mon Aug 19 14:28:43 2019 +0100
@@ -56,7 +56,7 @@
     LIBS_solaris := -lnsl -lsocket $(LIBDL), \
     LIBS_aix := $(LIBDL),\
     LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib winhttp.lib \
-        urlmon.lib delayimp.lib $(WIN_JAVA_LIB) advapi32.lib, \
+        delayimp.lib $(WIN_JAVA_LIB) advapi32.lib, \
     LIBS_macosx := -framework CoreFoundation -framework CoreServices, \
 ))
 
--- a/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java	Mon Aug 19 15:36:07 2019 +0300
+++ b/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java	Mon Aug 19 14:28:43 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -202,7 +202,17 @@
         }
     }
 
-    static native boolean isTrustedSite(String url);
+    private static final boolean isTrustedSiteAvailable = isTrustedSiteAvailable();
+
+    private static native boolean isTrustedSiteAvailable();
+
+    private static boolean isTrustedSite(String url) {
+        if (isTrustedSiteAvailable)
+            return isTrustedSite0(url);
+        return false;
+    }
+
+    private static native boolean isTrustedSite0(String url);
 
     /**
      * Not supported. Must use the setHeaders() method
--- a/src/java.base/windows/native/libnet/NTLMAuthentication.c	Mon Aug 19 15:36:07 2019 +0300
+++ b/src/java.base/windows/native/libnet/NTLMAuthentication.c	Mon Aug 19 14:28:43 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -26,19 +26,44 @@
 #include <jni.h>
 #include <windows.h>
 #include "jni_util.h"
+#include "jdk_util.h"
 #include <urlmon.h>
 
-JNIEXPORT jboolean JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthentication_isTrustedSite(JNIEnv *env, jclass clazz, jstring url )
+typedef HRESULT (WINAPI *CoInternetCreateSecurityManagerType)
+        (IServiceProvider*,IInternetSecurityManager**,DWORD);
+
+static CoInternetCreateSecurityManagerType fn_CoInternetCreateSecurityManager;
+
+JNIEXPORT jboolean JNICALL
+Java_sun_net_www_protocol_http_ntlm_NTLMAuthentication_isTrustedSiteAvailable
+  (JNIEnv *env, jclass clazz)
 {
+    HMODULE libUrlmon = JDK_LoadSystemLibrary("urlmon.dll");
+    if (libUrlmon != NULL) {
+        fn_CoInternetCreateSecurityManager = (CoInternetCreateSecurityManagerType)
+                GetProcAddress(libUrlmon, "CoInternetCreateSecurityManager");
+        if (fn_CoInternetCreateSecurityManager != NULL) {
+            return JNI_TRUE;
+        }
+    }
+    return JNI_FALSE;
+}
 
+JNIEXPORT jboolean JNICALL
+Java_sun_net_www_protocol_http_ntlm_NTLMAuthentication_isTrustedSite0
+  (JNIEnv *env, jclass clazz, jstring url)
+{
     HRESULT hr;
     DWORD dwZone;
     DWORD  pPolicy = 0;
     IInternetSecurityManager *spSecurityManager;
     jboolean ret;
 
+    if (fn_CoInternetCreateSecurityManager == NULL)
+        return JNI_FALSE;
+
     // Create IInternetSecurityManager
-    hr = CoInternetCreateSecurityManager(NULL, &spSecurityManager, (DWORD)0);
+    hr = fn_CoInternetCreateSecurityManager(NULL, &spSecurityManager, (DWORD)0);
     if (FAILED(hr)) {
         return JNI_FALSE;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/net/www/protocol/http/TestTransparentNTLM.java	Mon Aug 19 14:28:43 2019 +0100
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018, 2019, 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 8225425
+ * @summary Verifies that transparent NTLM (on Windows) is not used by default,
+ *          and is used only when the relevant property is set.
+ * @requires os.family == "windows"
+ * @library /test/lib
+ * @run testng/othervm
+ *      -Dtest.auth.succeed=false
+ *      TestTransparentNTLM
+ * @run testng/othervm
+ *      -Djdk.http.ntlm.transparentAuth=allHosts
+ *      -Dtest.auth.succeed=true
+ *      TestTransparentNTLM
+ * @run testng/othervm
+ *      -Djdk.http.ntlm.transparentAuth=blahblah
+ *      -Dtest.auth.succeed=false
+ *      TestTransparentNTLM
+ * @run testng/othervm
+ *      -Djdk.http.ntlm.transparentAuth=trustedHosts
+ *      -Dtest.auth.succeed=false
+ *      TestTransparentNTLM
+ */
+
+// Run with `trustedHosts` to exercise the native code, nothing more.
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import jdk.test.lib.net.URIBuilder;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.testng.SkipException;
+import static java.lang.System.out;
+import static java.net.Proxy.NO_PROXY;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+public class TestTransparentNTLM {
+
+    boolean succeed;  // true if authentication is expected to succeed
+    Server server;
+    URL url;
+
+    @Test
+    public void testNTLM() throws IOException {
+        out.println("connecting to url: " + url);
+        HttpURLConnection uc = (HttpURLConnection)url.openConnection(NO_PROXY);
+        int respCode = uc.getResponseCode();
+        out.println("received: " + respCode);
+
+        if (succeed) {
+            assertEquals(respCode, HttpURLConnection.HTTP_OK);
+            String body = new String(uc.getInputStream().readAllBytes(), UTF_8);
+            out.println("received body: " + body);
+        } else {
+            assertEquals(respCode, HttpURLConnection.HTTP_UNAUTHORIZED);
+        }
+    }
+
+    static class Server extends Thread implements  Closeable {
+
+        static final InetAddress LOOPBACK = InetAddress.getLoopbackAddress();
+        final ServerSocket serverSocket;
+        final boolean expectAuthToSucceed;
+
+        Server(boolean expectAuthToSucceed) throws IOException {
+            super("TestTransparentNTLM-Server");
+            serverSocket = new ServerSocket();
+            serverSocket.bind(new InetSocketAddress(LOOPBACK, 0));
+            this.expectAuthToSucceed = expectAuthToSucceed;
+        }
+
+        int port() {
+            return serverSocket.getLocalPort();
+        }
+
+        static final String AUTH_REQUIRED =
+                "HTTP/1.1 401 Unauthorized\r\n" +
+                "Content-Length: 0\r\n" +
+                "Connection: close\r\n" +
+                "WWW-Authenticate: NTLM\r\n\r\n";
+
+        static final String AUTH_STAGE_TWO =
+                "HTTP/1.1 401 Unauthorized\r\n" +
+                "Content-Length: 0\r\n" +
+                "WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==\r\n\r\n";
+
+        static final String AUTH_SUCCESSFUL =
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-Length: 11\r\n\r\n" +
+                "Hello world";
+
+        @Override
+        public void run() {
+            try {
+                try (Socket s = serverSocket.accept()) {
+                    out.println("Server accepted connection - 1");
+                    readRequestHeaders(s.getInputStream());
+                    s.getOutputStream().write(AUTH_REQUIRED.getBytes(UTF_8));
+                }
+
+                if (expectAuthToSucceed) {
+                    // await the second follow up connection
+                    try (Socket s = serverSocket.accept()) {
+                        out.println("Server accepted connection - 2");
+                        readRequestHeaders(s.getInputStream());
+                        s.getOutputStream().write(AUTH_STAGE_TWO.getBytes(UTF_8));
+                        readRequestHeaders(s.getInputStream());
+                        s.getOutputStream().write(AUTH_SUCCESSFUL.getBytes(UTF_8));
+                    }
+                }
+            } catch (IOException e) {
+                fail("Unexpected exception", e);
+            }
+        }
+
+        @Override
+        public void close() throws IOException {
+            serverSocket.close();
+        }
+
+        static final byte[] REQUEST_END = new byte[] {'\r', '\n', '\r', '\n'};
+
+        // Read until the end of the HTTP request headers
+        static void readRequestHeaders(InputStream is) throws IOException {
+            int requestEndCount = 0, r;
+            while ((r = is.read()) != -1) {
+                if (r == REQUEST_END[requestEndCount]) {
+                    requestEndCount++;
+                    if (requestEndCount == 4) {
+                        break;
+                    }
+                } else {
+                    requestEndCount = 0;
+                }
+            }
+        }
+    }
+
+    @BeforeTest
+    public void setup() throws Exception {
+        succeed = System.getProperty("test.auth.succeed").equals("true");
+        if (succeed)
+            out.println("Expect client to succeed, with 200 Ok");
+        else
+            out.println("Expect client to fail, with 401 Unauthorized");
+
+        server = new Server(succeed);
+        server.start();
+        url = URIBuilder.newBuilder()
+                .scheme("http")
+                .loopback()
+                .port(server.port())
+                .path("/xxyyzz")
+                .toURL();
+    }
+
+    @AfterTest
+    public void teardown() throws Exception {
+        server.close();
+        server.join();
+    }
+}