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