| /* |
| * Copyright (c) 2015, 2018, 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 8087112 |
| * @modules java.net.http |
| * java.logging |
| * jdk.httpserver |
| * @library /lib/testlibrary/ |
| * @build jdk.testlibrary.SimpleSSLContext |
| * @compile ../../../../com/sun/net/httpserver/LogFilter.java |
| * @compile ../../../../com/sun/net/httpserver/FileServerHandler.java |
| * @compile ../ProxyServer.java |
| * |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=0.policy Security 0 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=2.policy Security 2 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=3.policy Security 3 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=4.policy Security 4 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=5.policy Security 5 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=6.policy Security 6 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=7.policy Security 7 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=8.policy Security 8 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=9.policy Security 9 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=0.policy Security 13 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=14.policy Security 14 |
| * @run main/othervm/secure=java.lang.SecurityManager/policy=15.policy -Djava.security.debug=access:domain,failure Security 15 |
| */ |
| |
| // Tests 1, 10, 11 and 12 executed from Driver |
| |
| import com.sun.net.httpserver.Headers; |
| import com.sun.net.httpserver.HttpExchange; |
| import com.sun.net.httpserver.HttpHandler; |
| import com.sun.net.httpserver.HttpServer; |
| import com.sun.net.httpserver.HttpsServer; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.File; |
| import java.io.OutputStream; |
| import java.lang.reflect.Constructor; |
| import java.net.BindException; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.ProxySelector; |
| import java.net.URI; |
| import java.net.URLClassLoader; |
| import java.net.URL; |
| import java.net.http.HttpHeaders; |
| import java.net.http.HttpClient; |
| import java.net.http.HttpRequest; |
| import java.net.http.HttpResponse; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.ByteBuffer; |
| import java.nio.file.Paths; |
| import java.nio.file.StandardCopyOption; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.CompletionException; |
| import java.util.concurrent.CompletionStage; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Flow; |
| import java.util.logging.ConsoleHandler; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.lang.reflect.InvocationTargetException; |
| import static java.net.http.HttpResponse.BodyHandlers.ofString; |
| |
| /** |
| * Security checks test |
| */ |
| public class Security { |
| |
| static HttpServer s1; |
| static ExecutorService executor; |
| static int port, proxyPort; |
| static HttpClient client; |
| static String httproot, fileuri, fileroot, redirectroot; |
| static List<HttpClient> clients = new LinkedList<>(); |
| static URI uri; |
| |
| interface ThrowingRunnable { void run() throws Throwable; } |
| |
| static class TestAndResult { |
| private final ThrowingRunnable runnable; |
| private final boolean expectSecurityException; |
| |
| TestAndResult(boolean expectSecurityException, ThrowingRunnable runnable) { |
| this.expectSecurityException = expectSecurityException; |
| this.runnable = runnable; |
| } |
| |
| static TestAndResult of(boolean expectSecurityException, |
| ThrowingRunnable runnable) { |
| return new TestAndResult(expectSecurityException, runnable); |
| } |
| |
| void runWithPolicy(String policy) { |
| System.out.println("Using policy file: " + policy); |
| try { |
| runnable.run(); |
| if (expectSecurityException) { |
| String msg = "FAILED: expected security exception not thrown"; |
| System.out.println(msg); |
| throw new RuntimeException(msg); |
| } |
| System.out.println (policy + " succeeded as expected"); |
| } catch (BindException e) { |
| System.exit(10); |
| } catch (SecurityException e) { |
| if (!expectSecurityException) { |
| System.out.println("UNEXPECTED security Exception: " + e); |
| throw new RuntimeException("UNEXPECTED security Exception", e); |
| } |
| System.out.println(policy + " threw SecurityException as expected: " + e); |
| } catch (Throwable t) { |
| throw new AssertionError(t); |
| } |
| } |
| } |
| |
| static TestAndResult[] tests = createTests(); |
| static String testclasses; |
| static File subdir; |
| |
| /** |
| * The ProxyServer class is compiled by jtreg, but we want to |
| * move it so it is not on the application claspath. We want to |
| * load it through a separate classloader so that it has a separate |
| * protection domain and security permissions. |
| * |
| * Its permissions are in the second grant block in each policy file |
| */ |
| static void setupProxy() throws IOException, ClassNotFoundException, NoSuchMethodException { |
| testclasses = System.getProperty("test.classes"); |
| subdir = new File (testclasses, "proxydir"); |
| subdir.mkdir(); |
| |
| movefile("ProxyServer.class"); |
| movefile("ProxyServer$Connection.class"); |
| movefile("ProxyServer$1.class"); |
| |
| URL url = subdir.toURI().toURL(); |
| System.out.println("URL for class loader = " + url); |
| URLClassLoader urlc = new URLClassLoader(new URL[] {url}); |
| proxyClass = Class.forName("ProxyServer", true, urlc); |
| proxyConstructor = proxyClass.getConstructor(Integer.class, Boolean.class); |
| } |
| |
| static void movefile(String f) throws IOException { |
| Path src = Paths.get(testclasses, f); |
| Path dest = subdir.toPath().resolve(f); |
| if (!dest.toFile().exists()) { |
| System.out.printf("moving %s to %s\n", src.toString(), dest.toString()); |
| Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING); |
| } else if (src.toFile().exists()) { |
| System.out.printf("%s exists, deleting %s\n", dest.toString(), src.toString()); |
| Files.delete(src); |
| } else { |
| System.out.printf("NOT moving %s to %s\n", src.toString(), dest.toString()); |
| } |
| } |
| |
| static Object createProxy(int port, boolean b) throws Throwable { |
| try { |
| return proxyConstructor.newInstance(port, b); |
| } catch (InvocationTargetException e) { |
| throw e.getTargetException(); |
| } |
| } |
| |
| static Class<?> proxyClass; |
| static Constructor<?> proxyConstructor; |
| |
| static TestAndResult[] createTests() { |
| return new TestAndResult[] { |
| // (0) policy does not have permission for file. Should fail |
| TestAndResult.of(true, () -> { // Policy 0 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (1) policy has permission for file URL |
| TestAndResult.of(false, () -> { //Policy 1 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (2) policy has permission for all file URLs under /files |
| TestAndResult.of(false, () -> { // Policy 2 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (3) policy has permission for first URL but not redirected URL |
| TestAndResult.of(true, () -> { // Policy 3 |
| URI u = URI.create("http://localhost:" + port + "/redirect/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (4) policy has permission for both first URL and redirected URL |
| TestAndResult.of(false, () -> { // Policy 4 |
| URI u = URI.create("http://localhost:" + port + "/redirect/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (5) policy has permission for redirected but not first URL |
| TestAndResult.of(true, () -> { // Policy 5 |
| URI u = URI.create("http://localhost:" + port + "/redirect/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (6) policy has permission for file URL, but not method |
| TestAndResult.of(true, () -> { //Policy 6 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (7) policy has permission for file URL, method, but not header |
| TestAndResult.of(true, () -> { //Policy 7 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u) |
| .header("X-Foo", "bar") |
| .GET() |
| .build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (8) policy has permission for file URL, method and header |
| TestAndResult.of(false, () -> { //Policy 8 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u) |
| .header("X-Foo", "bar") |
| .GET() |
| .build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (9) policy has permission for file URL, method and header |
| TestAndResult.of(false, () -> { //Policy 9 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u) |
| .headers("X-Foo", "bar", "X-Bar", "foo") |
| .GET() |
| .build(); |
| HttpResponse<?> response = client.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| }), |
| // (10) policy has permission for destination URL but not for proxy |
| TestAndResult.of(true, () -> { //Policy 10 |
| directProxyTest(proxyPort, true); |
| }), |
| // (11) policy has permission for both destination URL and proxy |
| TestAndResult.of(false, () -> { //Policy 11 |
| directProxyTest(proxyPort, true); |
| }), |
| // (12) policy has permission for both destination URL and proxy |
| TestAndResult.of(true, () -> { //Policy 12 ( 11 & 12 are the same ) |
| directProxyTest(proxyPort, false); |
| }), |
| // (13) async version of test 0 |
| TestAndResult.of(true, () -> { // Policy 0 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| try { |
| HttpResponse<?> response = client.sendAsync(request, ofString()).get(); |
| System.out.println("Received response:" + response); |
| } catch (ExecutionException e) { |
| if (e.getCause() instanceof SecurityException) { |
| throw (SecurityException)e.getCause(); |
| } else { |
| throw new RuntimeException(e); |
| } |
| } |
| }), |
| // (14) async version of test 1 |
| TestAndResult.of(false, () -> { //Policy 1 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| try { |
| HttpResponse<?> response = client.sendAsync(request, ofString()).get(); |
| System.out.println("Received response:" + response); |
| } catch (ExecutionException e) { |
| if (e.getCause() instanceof SecurityException) { |
| throw (SecurityException)e.getCause(); |
| } else { |
| throw new RuntimeException(e); |
| } |
| } |
| }), |
| // (15) check that user provided unprivileged code running on a worker |
| // thread does not gain ungranted privileges. |
| TestAndResult.of(true, () -> { //Policy 12 |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u).GET().build(); |
| HttpResponse.BodyHandler<String> sth = ofString(); |
| |
| CompletableFuture<HttpResponse<String>> cf = |
| client.sendAsync(request, new HttpResponse.BodyHandler<String>() { |
| @Override |
| public HttpResponse.BodySubscriber<String> apply(HttpResponse.ResponseInfo rinfo) { |
| final HttpResponse.BodySubscriber<String> stproc = sth.apply(rinfo); |
| return new HttpResponse.BodySubscriber<String>() { |
| @Override |
| public CompletionStage<String> getBody() { |
| return stproc.getBody(); |
| } |
| @Override |
| public void onNext(List<ByteBuffer> item) { |
| SecurityManager sm = System.getSecurityManager(); |
| // should succeed. |
| sm.checkPermission(new RuntimePermission("foobar")); |
| // do some mischief here |
| System.setSecurityManager(null); |
| System.setSecurityManager(sm); |
| // problem if we get this far |
| stproc.onNext(item); |
| } |
| @Override |
| public void onSubscribe(Flow.Subscription subscription) { |
| stproc.onSubscribe(subscription); |
| } |
| @Override |
| public void onError(Throwable throwable) { |
| stproc.onError(throwable); |
| } |
| @Override |
| public void onComplete() { |
| stproc.onComplete(); |
| } |
| }; |
| } |
| } |
| ); |
| try { |
| HttpResponse<String> response = cf.join(); |
| System.out.println("Received response:" + response); |
| } catch (CompletionException e) { |
| Throwable t = e.getCause(); |
| if (t instanceof SecurityException) |
| throw (SecurityException)t; |
| else if ((t instanceof IOException) |
| && (t.getCause() instanceof SecurityException)) |
| throw ((SecurityException)t.getCause()); |
| else |
| throw new RuntimeException(t); |
| } |
| }) |
| }; |
| } |
| |
| private static void directProxyTest(int proxyPort, boolean samePort) |
| throws IOException, InterruptedException |
| { |
| System.out.println("proxyPort:" + proxyPort + ", samePort:" + samePort); |
| |
| int p = proxyPort; |
| if (samePort) { |
| Object proxy; |
| try { |
| proxy = createProxy(p, true); |
| } catch (BindException e) { |
| System.out.println("Bind failed"); |
| throw e; |
| } catch (Throwable ee) { |
| throw new RuntimeException(ee); |
| } |
| } else { |
| while (p == proxyPort || p == port) { |
| // avoid ports that may be granted permission |
| p++; |
| if (p > 65535) { |
| p = 32000; // overflow |
| } |
| } |
| } |
| System.out.println("Proxy port, p:" + p); |
| |
| InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), |
| p); |
| HttpClient cl = HttpClient.newBuilder() |
| .proxy(ProxySelector.of(addr)) |
| .build(); |
| clients.add(cl); |
| |
| URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); |
| HttpRequest request = HttpRequest.newBuilder(u) |
| .headers("X-Foo", "bar", "X-Bar", "foo") |
| .build(); |
| HttpResponse<?> response = cl.send(request, ofString()); |
| System.out.println("Received response:" + response); |
| } |
| |
| public static void main(String[] args) throws Exception { |
| try { |
| initServer(); |
| setupProxy(); |
| } catch (BindException e) { |
| System.exit(10); |
| } |
| fileroot = System.getProperty("test.src")+ "/docs"; |
| int testnum = Integer.parseInt(args[0]); |
| String policy = args[0]; |
| |
| client = HttpClient.newBuilder() |
| .followRedirects(HttpClient.Redirect.ALWAYS) |
| .build(); |
| |
| clients.add(client); |
| |
| try { |
| TestAndResult tr = tests[testnum]; |
| tr.runWithPolicy(policy); |
| } finally { |
| s1.stop(0); |
| executor.shutdownNow(); |
| } |
| } |
| |
| public static void initServer() throws Exception { |
| String portstring = System.getProperty("port.number"); |
| port = portstring != null ? Integer.parseInt(portstring) : 0; |
| portstring = System.getProperty("port.number1"); |
| proxyPort = portstring != null ? Integer.parseInt(portstring) : 0; |
| |
| Logger logger = Logger.getLogger("com.sun.net.httpserver"); |
| ConsoleHandler ch = new ConsoleHandler(); |
| logger.setLevel(Level.ALL); |
| ch.setLevel(Level.ALL); |
| logger.addHandler(ch); |
| String root = System.getProperty("test.src")+ "/docs"; |
| InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), port); |
| s1 = HttpServer.create (addr, 0); |
| if (s1 instanceof HttpsServer) { |
| throw new RuntimeException("should not be httpsserver"); |
| } |
| s1.createContext("/files", new FileServerHandler(root)); |
| s1.createContext("/redirect", new RedirectHandler("/redirect")); |
| |
| executor = Executors.newCachedThreadPool(); |
| s1.setExecutor(executor); |
| s1.start(); |
| |
| if (port == 0) |
| port = s1.getAddress().getPort(); |
| else { |
| if (s1.getAddress().getPort() != port) |
| throw new RuntimeException("Error wrong port"); |
| System.out.println("Port was assigned by Driver"); |
| } |
| System.out.println("HTTP server port = " + port); |
| httproot = "http://localhost:" + port + "/files/"; |
| redirectroot = "http://localhost:" + port + "/redirect/"; |
| uri = new URI(httproot); |
| fileuri = httproot + "foo.txt"; |
| } |
| |
| static class RedirectHandler implements HttpHandler { |
| |
| String root; |
| int count = 0; |
| |
| RedirectHandler(String root) { |
| this.root = root; |
| } |
| |
| synchronized int count() { |
| return count; |
| } |
| |
| synchronized void increment() { |
| count++; |
| } |
| |
| @Override |
| public synchronized void handle(HttpExchange t) throws IOException { |
| System.out.println("Server: " + t.getRequestURI()); |
| try (InputStream is = t.getRequestBody()) { |
| is.readAllBytes(); |
| } |
| increment(); |
| if (count() == 1) { |
| Headers map = t.getResponseHeaders(); |
| String redirect = "/redirect/bar.txt"; |
| map.add("Location", redirect); |
| t.sendResponseHeaders(301, -1); |
| t.close(); |
| } else { |
| String response = "Hello world"; |
| t.sendResponseHeaders(200, response.length()); |
| OutputStream os = t.getResponseBody(); |
| os.write(response.getBytes(StandardCharsets.ISO_8859_1)); |
| t.close(); |
| } |
| } |
| } |
| } |