1 /*
   2  * Copyright (c) 2015, 2016, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  */
  24 
  25 /**
  26  * @test
  27  * @bug 8087112
  28  * @modules java.httpclient
  29  *          jdk.httpserver
  30  * @library /lib/testlibrary/
  31  * @build jdk.testlibrary.SimpleSSLContext
  32  * @compile ../../../../com/sun/net/httpserver/LogFilter.java
  33  * @compile ../../../../com/sun/net/httpserver/FileServerHandler.java
  34  * @compile ../ProxyServer.java
  35  *
  36  * @run main/othervm/secure=java.lang.SecurityManager/policy=0.policy Security 0
  37  * @run main/othervm/secure=java.lang.SecurityManager/policy=2.policy Security 2
  38  * @run main/othervm/secure=java.lang.SecurityManager/policy=3.policy Security 3
  39  * @run main/othervm/secure=java.lang.SecurityManager/policy=4.policy Security 4
  40  * @run main/othervm/secure=java.lang.SecurityManager/policy=5.policy Security 5
  41  * @run main/othervm/secure=java.lang.SecurityManager/policy=6.policy Security 6
  42  * @run main/othervm/secure=java.lang.SecurityManager/policy=7.policy Security 7
  43  * @run main/othervm/secure=java.lang.SecurityManager/policy=8.policy Security 8
  44  * @run main/othervm/secure=java.lang.SecurityManager/policy=9.policy Security 9
  45  * @run main/othervm/secure=java.lang.SecurityManager/policy=0.policy Security 13
  46  * @run main/othervm/secure=java.lang.SecurityManager/policy=14.policy Security 14
  47  * @run main/othervm/secure=java.lang.SecurityManager/policy=15.policy Security 15
  48  */
  49 
  50 // Tests 1, 10, 11 and 12 executed from Driver
  51 
  52 import com.sun.net.httpserver.*;
  53 import java.io.IOException;
  54 import java.io.InputStream;
  55 import java.io.File;
  56 import java.io.OutputStream;
  57 import java.lang.reflect.Constructor;
  58 import java.net.*;
  59 import java.net.http.*;
  60 import java.nio.charset.StandardCharsets;
  61 import java.nio.file.Files;
  62 import java.nio.file.Path;
  63 import java.nio.ByteBuffer;
  64 import java.nio.file.Paths;
  65 import java.nio.file.StandardCopyOption;
  66 import java.util.LinkedList;
  67 import java.util.List;
  68 import java.util.concurrent.*;
  69 import java.util.function.*;
  70 import java.util.logging.ConsoleHandler;
  71 import java.util.logging.Level;
  72 import java.util.logging.Logger;
  73 import java.lang.reflect.InvocationTargetException;
  74 import java.net.BindException;
  75 
  76 /**
  77  * Security checks test
  78  */
  79 public class Security {
  80 
  81     static HttpServer s1 = null;
  82     static ExecutorService executor=null;
  83     static int port, proxyPort;
  84     static HttpClient client;
  85     static String httproot, fileuri, fileroot, redirectroot;
  86     static List<HttpClient> clients = new LinkedList<>();
  87     static URI uri;
  88 
  89     interface Test {
  90         public void execute() throws IOException, InterruptedException;
  91     }
  92 
  93     static class TestAndResult {
  94         Test test;
  95         boolean result;
  96 
  97         TestAndResult (Test t, boolean result) {
  98             this.test = t;
  99             this.result = result;
 100         }
 101     }
 102 
 103     static TestAndResult test(boolean result, Test t) {
 104         return new TestAndResult(t, result);
 105     }
 106 
 107     static TestAndResult[] tests;
 108     static String testclasses;
 109     static File subdir;
 110 
 111     /**
 112      * The ProxyServer class is compiled by jtreg, but we want to
 113      * move it so it is not on the application claspath. We want to
 114      * load it through a separate classloader so that it has a separate
 115      * protection domain and security permissions.
 116      *
 117      * Its permissions are in the second grant block in each policy file
 118      */
 119     static void setupProxy() throws IOException, ClassNotFoundException, NoSuchMethodException {
 120         testclasses = System.getProperty("test.classes");
 121         subdir = new File (testclasses, "proxydir");
 122         subdir.mkdir();
 123 
 124         movefile("ProxyServer.class");
 125         movefile("ProxyServer$Connection.class");
 126         movefile("ProxyServer$1.class");
 127 
 128         URL url = subdir.toURL();
 129         System.out.println("URL for class loader = " + url);
 130         URLClassLoader urlc = new URLClassLoader(new URL[] {url});
 131         proxyClass = Class.forName("ProxyServer", true, urlc);
 132         proxyConstructor = proxyClass.getConstructor(Integer.class, Boolean.class);
 133     }
 134 
 135     static void movefile(String f) throws IOException {
 136         Path src = Paths.get(testclasses, f);
 137         Path dest = subdir.toPath().resolve(f);
 138         if (!dest.toFile().exists()) {
 139             System.out.printf("moving %s to %s\n", src.toString(), dest.toString());
 140             Files.move(src, dest,  StandardCopyOption.REPLACE_EXISTING);
 141         } else if (src.toFile().exists()) {
 142             System.out.printf("%s exists, deleting %s\n", dest.toString(), src.toString());
 143             Files.delete(src);
 144         } else {
 145             System.out.printf("NOT moving %s to %s\n", src.toString(), dest.toString());
 146         }
 147     }
 148 
 149     static Object getProxy(int port, boolean b) throws Throwable {
 150         try {
 151             return proxyConstructor.newInstance(port, b);
 152         } catch (InvocationTargetException e) {
 153             throw e.getTargetException();
 154         }
 155     }
 156 
 157     static Class<?> proxyClass;
 158     static Constructor<?> proxyConstructor;
 159 
 160     static void setupTests() {
 161         tests = new TestAndResult[]{
 162             // (0) policy does not have permission for file. Should fail
 163             test(false, () -> { // Policy 0
 164                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 165                 HttpRequest request = client.request(u)
 166                         .GET();
 167                 HttpResponse response = request.response();
 168             }),
 169             // (1) policy has permission for file URL
 170             test(true, () -> { //Policy 1
 171                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 172                 HttpRequest request = client.request(u)
 173                         .GET();
 174                 HttpResponse response = request.response();
 175             }),
 176             // (2) policy has permission for all file URLs under /files
 177             test(true, () -> { // Policy 2
 178                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 179                 HttpRequest request = client.request(u)
 180                         .GET();
 181                 HttpResponse response = request.response();
 182             }),
 183             // (3) policy has permission for first URL but not redirected URL
 184             test(false, () -> { // Policy 3
 185                 URI u = URI.create("http://127.0.0.1:" + port + "/redirect/foo.txt");
 186                 HttpRequest request = client.request(u)
 187                         .GET();
 188                 HttpResponse response = request.response();
 189             }),
 190             // (4) policy has permission for both first URL and redirected URL
 191             test(true, () -> { // Policy 4
 192                 URI u = URI.create("http://127.0.0.1:" + port + "/redirect/foo.txt");
 193                 HttpRequest request = client.request(u)
 194                         .GET();
 195                 HttpResponse response = request.response();
 196             }),
 197             // (5) policy has permission for redirected but not first URL
 198             test(false, () -> { // Policy 5
 199                 URI u = URI.create("http://127.0.0.1:" + port + "/redirect/foo.txt");
 200                 HttpRequest request = client.request(u)
 201                         .GET();
 202                 HttpResponse response = request.response();
 203             }),
 204             // (6) policy has permission for file URL, but not method
 205             test(false, () -> { //Policy 6
 206                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 207                 HttpRequest request = client.request(u)
 208                         .GET();
 209                 HttpResponse response = request.response();
 210             }),
 211             // (7) policy has permission for file URL, method, but not header
 212             test(false, () -> { //Policy 7
 213                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 214                 HttpRequest request = client.request(u)
 215                         .header("X-Foo", "bar")
 216                         .GET();
 217                 HttpResponse response = request.response();
 218             }),
 219             // (8) policy has permission for file URL, method and header
 220             test(true, () -> { //Policy 8
 221                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 222                 HttpRequest request = client.request(u)
 223                         .header("X-Foo", "bar")
 224                         .GET();
 225                 HttpResponse response = request.response();
 226             }),
 227             // (9) policy has permission for file URL, method and header
 228             test(true, () -> { //Policy 9
 229                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 230                 HttpRequest request = client.request(u)
 231                         .headers("X-Foo", "bar", "X-Bar", "foo")
 232                         .GET();
 233                 HttpResponse response = request.response();
 234             }),
 235             // (10) policy has permission for destination URL but not for proxy
 236             test(false, () -> { //Policy 10
 237                 directProxyTest(proxyPort, true);
 238             }),
 239             // (11) policy has permission for both destination URL and proxy
 240             test(true, () -> { //Policy 11
 241                 directProxyTest(proxyPort, true);
 242             }),
 243             // (12) policy has permission for both destination URL and proxy
 244             test(false, () -> { //Policy 11
 245                 directProxyTest(proxyPort, false);
 246             }),
 247             // (13) async version of test 0
 248             test(false, () -> { // Policy 0
 249                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 250                 HttpRequest request = client.request(u)
 251                         .GET();
 252                 try {
 253                     HttpResponse response = request.responseAsync().get();
 254                 } catch (ExecutionException e) {
 255                     if (e.getCause() instanceof SecurityException) {
 256                         throw (SecurityException)e.getCause();
 257                     } else {
 258                         throw new RuntimeException(e);
 259                     }
 260                 }
 261             }),
 262             // (14) async version of test 1
 263             test(true, () -> { //Policy 1
 264                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 265                 HttpRequest request = client.request(u)
 266                         .GET();
 267                 try {
 268                     HttpResponse response = request.responseAsync().get();
 269                 } catch (ExecutionException e) {
 270                     if (e.getCause() instanceof SecurityException) {
 271                         throw (SecurityException)e.getCause();
 272                     } else {
 273                         throw new RuntimeException(e);
 274                     }
 275                 }
 276             }),
 277             // (15) check that user provided unprivileged code running on a worker
 278             //      thread does not gain ungranted privileges.
 279             test(false, () -> { //Policy 12
 280                 URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 281                 HttpRequest request = client.request(u)
 282                         .GET();
 283                 HttpResponse response = request.response();
 284                 HttpResponse.BodyProcessor<String> stproc = HttpResponse.asString();
 285 
 286                 CompletableFuture<String> cf;
 287                 cf = response.bodyAsync(new HttpResponse.BodyProcessor<String>() {
 288                         public void onResponseBodyChunk(ByteBuffer b) throws IOException {
 289                             // do some mischief here
 290                             SecurityManager sm = System.getSecurityManager();
 291                             System.setSecurityManager(null);
 292                             System.setSecurityManager(sm);
 293                             // problem if we get this far
 294                             stproc.onResponseBodyChunk(b);
 295                         }
 296                         public String onResponseBodyStart(long contentLength,
 297                                         HttpHeaders responseHeaders,
 298                                         LongConsumer fc) throws IOException {
 299 
 300                             SecurityManager sm = System.getSecurityManager();
 301                             // should succeed.
 302                             sm.checkPermission(new RuntimePermission("foobar"));
 303                             return stproc.onResponseBodyStart(contentLength,responseHeaders, fc);
 304                         }
 305                         public String onResponseComplete() throws IOException {
 306                             return stproc.onResponseComplete();
 307                         }
 308                         public void onResponseError(Throwable t) {
 309                             stproc.onResponseError(t);
 310                         }
 311                     }
 312                 );
 313                 try {
 314                     System.out.println("Body = " + cf.get());// should not reach here
 315                 } catch (ExecutionException e) {
 316                     if (e.getCause() instanceof SecurityException) {
 317                         throw (SecurityException)e.getCause();
 318                     } else {
 319                         throw new RuntimeException(e);
 320                     }
 321                 }
 322             })
 323         };
 324     }
 325 
 326     private static void directProxyTest(int proxyPort, boolean samePort) throws IOException, InterruptedException {
 327         Object proxy = null;
 328         try {
 329             proxy = getProxy(proxyPort, true);
 330         } catch (BindException e) {
 331             System.out.println("Bind failed");
 332             throw e;
 333         } catch (Throwable ee) {
 334             throw new RuntimeException(ee);
 335         }
 336         System.out.println("Proxy port = " + proxyPort);
 337         if (!samePort)
 338             proxyPort++;
 339 
 340         HttpClient cl = HttpClient.create()
 341                 .proxy(ProxySelector.of(
 342                                 new InetSocketAddress("127.0.0.1", proxyPort)))
 343                 .build();
 344         clients.add(cl);
 345 
 346         URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
 347         HttpRequest request = cl.request(u)
 348                 .headers("X-Foo", "bar", "X-Bar", "foo")
 349                 .GET();
 350         HttpResponse response = request.response();
 351     }
 352 
 353     static void runtest(Test r, String policy, boolean succeeds) {
 354         System.out.println("Using policy file: " + policy);
 355         try {
 356             r.execute();
 357             if (!succeeds) {
 358                 System.out.println("FAILED: expected security exception");
 359                 throw new RuntimeException("Failed");
 360             }
 361             System.out.println (policy + " succeeded as expected");
 362         } catch (BindException e) {
 363             System.exit(10);
 364         } catch (SecurityException e) {
 365             if (succeeds) {
 366                 System.out.println("FAILED");
 367                 throw new RuntimeException(e);
 368             }
 369             System.out.println (policy + " threw exception as expected");
 370         } catch (IOException | InterruptedException ee) {
 371             throw new RuntimeException(ee);
 372         }
 373     }
 374 
 375     public static void main(String[] args) throws Exception {
 376         try {
 377             initServer();
 378             setupProxy();
 379         } catch (BindException e) {
 380             System.exit(10);
 381         }
 382         fileroot = System.getProperty ("test.src")+ "/docs";
 383         int testnum = Integer.parseInt(args[0]);
 384         String policy = args[0];
 385 
 386         client = HttpClient
 387             .create()
 388             .followRedirects(HttpClient.Redirect.ALWAYS)
 389             .build();
 390 
 391         clients.add(HttpClient.getDefault());
 392         clients.add(client);
 393 
 394         try {
 395             setupTests();
 396             TestAndResult tr = tests[testnum];
 397             runtest(tr.test, policy, tr.result);
 398         } finally {
 399             s1.stop(0);
 400             executor.shutdownNow();
 401             for (HttpClient client : clients)
 402                 client.executorService().shutdownNow();
 403         }
 404     }
 405 
 406     public static void initServer() throws Exception {
 407         String portstring = System.getProperty("port.number");
 408         port = portstring != null ? Integer.parseInt(portstring) : 0;
 409         portstring = System.getProperty("port.number1");
 410         proxyPort = portstring != null ? Integer.parseInt(portstring) : 0;
 411 
 412         Logger logger = Logger.getLogger("com.sun.net.httpserver");
 413         ConsoleHandler ch = new ConsoleHandler();
 414         logger.setLevel(Level.ALL);
 415         ch.setLevel(Level.ALL);
 416         logger.addHandler(ch);
 417         String root = System.getProperty ("test.src")+ "/docs";
 418         InetSocketAddress addr = new InetSocketAddress (port);
 419         s1 = HttpServer.create (addr, 0);
 420         if (s1 instanceof HttpsServer) {
 421             throw new RuntimeException ("should not be httpsserver");
 422         }
 423         HttpHandler h = new FileServerHandler (root);
 424         HttpContext c = s1.createContext ("/files", h);
 425 
 426         HttpHandler h1 = new RedirectHandler ("/redirect");
 427         HttpContext c1 = s1.createContext ("/redirect", h1);
 428 
 429         executor = Executors.newCachedThreadPool();
 430         s1.setExecutor (executor);
 431         s1.start();
 432 
 433         if (port == 0)
 434             port = s1.getAddress().getPort();
 435         else {
 436             if (s1.getAddress().getPort() != port)
 437                 throw new RuntimeException("Error wrong port");
 438             System.out.println("Port was assigned by Driver");
 439         }
 440         System.out.println("HTTP server port = " + port);
 441         httproot = "http://127.0.0.1:" + port + "/files/";
 442         redirectroot = "http://127.0.0.1:" + port + "/redirect/";
 443         uri = new URI(httproot);
 444         fileuri = httproot + "foo.txt";
 445     }
 446 
 447     static class RedirectHandler implements HttpHandler {
 448 
 449         String root;
 450         int count = 0;
 451 
 452         RedirectHandler(String root) {
 453             this.root = root;
 454         }
 455 
 456         synchronized int count() {
 457             return count;
 458         }
 459 
 460         synchronized void increment() {
 461             count++;
 462         }
 463 
 464         @Override
 465         public synchronized void handle(HttpExchange t)
 466                 throws IOException {
 467             byte[] buf = new byte[2048];
 468             System.out.println("Server: " + t.getRequestURI());
 469             try (InputStream is = t.getRequestBody()) {
 470                 while (is.read(buf) != -1) ;
 471             }
 472             increment();
 473             if (count() == 1) {
 474                 Headers map = t.getResponseHeaders();
 475                 String redirect = "/redirect/bar.txt";
 476                 map.add("Location", redirect);
 477                 t.sendResponseHeaders(301, -1);
 478                 t.close();
 479             } else {
 480                 String response = "Hello world";
 481                 t.sendResponseHeaders(200, response.length());
 482                 OutputStream os = t.getResponseBody();
 483                 os.write(response.getBytes(StandardCharsets.ISO_8859_1));
 484                 t.close();
 485             }
 486         }
 487     }
 488 }