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