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 }