# HG changeset patch # User michaelm # Date 1542205401 0 # Node ID 5f1ca46703f9cdd4f7476a8cbfce391c7b7517f6 # Parent 9ca9aa224c399111b4f654fc3fec933e1a6e489c 8213189: Make restricted headers in HTTP Client configurable and remove Date by default Reviewed-by: dfuchs diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.base/share/conf/net.properties --- a/src/java.base/share/conf/net.properties Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.base/share/conf/net.properties Wed Nov 14 14:23:21 2018 +0000 @@ -99,3 +99,21 @@ #jdk.http.auth.proxying.disabledSchemes= jdk.http.auth.tunneling.disabledSchemes=Basic +# +# Allow restricted HTTP request headers +# +# By default, the following request headers are not allowed to be set by user code +# in HttpRequests: "connection", "content-length", "expect", "host" and "upgrade". +# The 'jdk.httpclient.allowRestrictedHeaders' property allows one or more of these +# headers to be specified as a comma separated list to override the default restriction. +# The names are case-insensitive and white-space is ignored (removed before processing +# the list). Note, this capability is mostly intended for testing and isn't expected +# to be used in real deployments. Protocol errors or other undefined behavior is likely +# to occur when using them. The property is not set by default. +# Note also, that there may be other headers that are restricted from being set +# depending on the context. This includes the "Authorization" header when the +# relevant HttpClient has an authenticator set. These restrictions cannot be +# overridden by this property. +# +# jdk.httpclient.allowRestrictedHeaders=host +# diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java Wed Nov 14 14:23:21 2018 +0000 @@ -585,6 +585,19 @@ } catch (SecurityException e) { return e; } + String hostHeader = userHeaders.firstValue("Host").orElse(null); + if (hostHeader != null && !hostHeader.equalsIgnoreCase(u.getHost())) { + // user has set a Host header different to request URI + // must check that for URLPermission also + URI u1 = replaceHostInURI(u, hostHeader); + URLPermission p1 = permissionForServer(u1, method, userHeaders.map()); + try { + assert acc != null; + sm.checkPermission(p1, acc); + } catch (SecurityException e) { + return e; + } + } ProxySelector ps = client.proxySelector(); if (ps != null) { if (!method.equals("CONNECT")) { @@ -602,6 +615,15 @@ return null; } + private static URI replaceHostInURI(URI u, String hostPort) { + StringBuilder sb = new StringBuilder(); + sb.append(u.getScheme()) + .append("://") + .append(hostPort) + .append(u.getRawPath()); + return URI.create(sb.toString()); + } + HttpClient.Version version() { return multi.version(); } diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java Wed Nov 14 14:23:21 2018 +0000 @@ -29,6 +29,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Function; +import java.net.http.HttpClient; import java.net.http.HttpResponse; import jdk.internal.net.http.common.Logger; import jdk.internal.net.http.common.MinimalFuture; @@ -64,6 +65,9 @@ return exchange; } + HttpClient client() { + return exchange.client(); + } /** * Returns the {@link HttpConnection} instance to which this exchange is diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java Wed Nov 14 14:23:21 2018 +0000 @@ -27,6 +27,7 @@ import java.io.IOException; import java.net.InetSocketAddress; +import java.net.http.HttpClient; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodySubscriber; import java.nio.ByteBuffer; @@ -706,6 +707,10 @@ } } + HttpClient client() { + return client; + } + String dbgString() { return "Http1Exchange"; } diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.net.http/share/classes/jdk/internal/net/http/Http1Request.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1Request.java Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Request.java Wed Nov 14 14:23:21 2018 +0000 @@ -27,6 +27,7 @@ import java.io.IOException; import java.net.URI; +import java.net.http.HttpClient; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -59,7 +60,7 @@ private final Http1Exchange http1Exchange; private final HttpConnection connection; private final HttpRequest.BodyPublisher requestPublisher; - private final HttpHeaders userHeaders; + private volatile HttpHeaders userHeaders; private final HttpHeadersBuilder systemHeadersBuilder; private volatile boolean streaming; private volatile long contentLength; @@ -91,7 +92,7 @@ } - private void collectHeaders0(StringBuilder sb) { + public void collectHeaders0(StringBuilder sb) { BiPredicate filter = connection.headerFilter(request); @@ -99,6 +100,15 @@ BiPredicate nocookies = NOCOOKIES.and(filter); HttpHeaders systemHeaders = systemHeadersBuilder.build(); + HttpClient client = http1Exchange.client(); + + // Filter overridable headers from userHeaders + userHeaders = HttpHeaders.of(userHeaders.map(), Utils.CONTEXT_RESTRICTED(client)); + + final HttpHeaders uh = userHeaders; + + // Filter any headers from systemHeaders that are set in userHeaders + systemHeaders = HttpHeaders.of(systemHeaders.map(), (k,v) -> uh.firstValue(k).isEmpty()); // If we're sending this request through a tunnel, // then don't send any preemptive proxy-* headers that diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.net.http/share/classes/jdk/internal/net/http/Stream.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java Wed Nov 14 14:23:21 2018 +0000 @@ -608,8 +608,20 @@ if (contentLength > 0) { h.setHeader("content-length", Long.toString(contentLength)); } + URI uri = request.uri(); + if (uri != null) { + h.setHeader("host", Utils.hostString(request)); + } HttpHeaders sysh = filterHeaders(h.build()); HttpHeaders userh = filterHeaders(request.getUserHeaders()); + // Filter context restricted from userHeaders + userh = HttpHeaders.of(userh.map(), Utils.CONTEXT_RESTRICTED(client())); + + final HttpHeaders uh = userh; + + // Filter any headers from systemHeaders that are set in userHeaders + sysh = HttpHeaders.of(sysh.map(), (k,v) -> uh.firstValue(k).isEmpty()); + OutgoingHeaders> f = new OutgoingHeaders<>(sysh, userh, this); if (contentLength == 0) { f.setFlag(HeadersFrame.END_STREAM); diff -r 9ca9aa224c39 -r 5f1ca46703f9 src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java Wed Nov 14 17:16:44 2018 +0530 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java Wed Nov 14 14:23:21 2018 +0000 @@ -45,6 +45,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.URLPermission; +import java.net.http.HttpClient; import java.net.http.HttpHeaders; import java.net.http.HttpTimeoutException; import java.nio.ByteBuffer; @@ -75,6 +76,7 @@ import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import jdk.internal.net.http.HttpRequestImpl; /** * Miscellaneous utilities @@ -127,14 +129,23 @@ public static final BiPredicate ACCEPT_ALL = (x,y) -> true; - private static final Set DISALLOWED_HEADERS_SET; + private static final Set DISALLOWED_HEADERS_SET = getDisallowedHeaders(); + + private static Set getDisallowedHeaders() { + Set headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + headers.addAll(Set.of("connection", "content-length", "expect", "host", "upgrade")); - static { - // A case insensitive TreeSet of strings. - TreeSet treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - treeSet.addAll(Set.of("connection", "content-length", - "date", "expect", "from", "host", "upgrade", "via", "warning")); - DISALLOWED_HEADERS_SET = Collections.unmodifiableSet(treeSet); + String v = getNetProperty("jdk.httpclient.allowRestrictedHeaders"); + if (v != null) { + // any headers found are removed from set. + String[] tokens = v.trim().split(","); + for (String token : tokens) { + headers.remove(token); + } + return Collections.unmodifiableSet(headers); + } else { + return Collections.unmodifiableSet(headers); + } } public static final BiPredicate @@ -156,6 +167,19 @@ return true; }; + // Headers that are not generally restricted, and can therefore be set by users, + // but can in some contexts be overridden by the implementation. + // Currently, only contains "Authorization" which will + // be overridden, when an Authenticator is set on the HttpClient. + // Needs to be BiPred to fit with general form of predicates + // used by caller. + + public static final BiPredicate CONTEXT_RESTRICTED(HttpClient client) { + return (k, v) -> client.authenticator() == null || + ! (k.equalsIgnoreCase("Authorization") + && k.equalsIgnoreCase("Proxy-Authorization")); + } + private static final Predicate IS_PROXY_HEADER = (k) -> k != null && k.length() > 6 && "proxy-".equalsIgnoreCase(k.substring(0,6)); private static final Predicate NO_PROXY_HEADER = @@ -323,8 +347,8 @@ Stream headers) { String urlString = new StringBuilder() .append(uri.getScheme()).append("://") - .append(uri.getAuthority()) - .append(uri.getPath()).toString(); + .append(uri.getRawAuthority()) + .append(uri.getRawPath()).toString(); StringBuilder actionStringBuilder = new StringBuilder(method); String collected = headers.collect(joining(",")); @@ -795,6 +819,33 @@ } /** + * Return the host string from a HttpRequestImpl + * + * @param request + * @return + */ + public static String hostString(HttpRequestImpl request) { + URI uri = request.uri(); + int port = uri.getPort(); + String host = uri.getHost(); + + boolean defaultPort; + if (port == -1) { + defaultPort = true; + } else if (uri.getScheme().equalsIgnoreCase("https")) { + defaultPort = port == 443; + } else { + defaultPort = port == 80; + } + + if (defaultPort) { + return host; + } else { + return host + ":" + Integer.toString(port); + } + } + + /** * Get a logger for debug HPACK traces.The logger should only be used * with levels whose severity is {@code <= DEBUG}. * diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/RequestBuilderTest.java --- a/test/jdk/java/net/httpclient/RequestBuilderTest.java Wed Nov 14 17:16:44 2018 +0530 +++ b/test/jdk/java/net/httpclient/RequestBuilderTest.java Wed Nov 14 14:23:21 2018 +0000 @@ -339,7 +339,7 @@ // headers that are allowed now, but weren't before private static final Set FORMERLY_RESTRICTED = Set.of("referer", "origin", - "OriGin", "Referer"); + "OriGin", "Referer", "Date", "via", "WarnIng"); @Test public void testFormerlyRestricted() throws URISyntaxException { @@ -354,14 +354,9 @@ } private static final Set RESTRICTED = Set.of("connection", "content-length", - "date", "expect", "from", "host", - "upgrade", "via", "warning", - "Connection", "Content-Length", - "DATE", "eXpect", "frOm", "hosT", - "upgradE", "vIa", "Warning", - "CONNection", "CONTENT-LENGTH", - "Date", "EXPECT", "From", "Host", - "Upgrade", "Via", "WARNING"); + "expect", "host", "upgrade", "Connection", "Content-Length", + "eXpect", "hosT", "upgradE", "CONNection", "CONTENT-LENGTH", + "EXPECT", "Host", "Upgrade"); interface WithHeader { HttpRequest.Builder withHeader(HttpRequest.Builder builder, String name, String value); diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/RestrictedHeadersTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/httpclient/RestrictedHeadersTest.java Wed Nov 14 14:23:21 2018 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 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 8178699 + * @modules java.net.http + * @run main/othervm RestrictedHeadersTest + * @run main/othervm -Djdk.httpclient.allowRestrictedHeaders=content-length,connection RestrictedHeadersTest content-length connection + * @run main/othervm -Djdk.httpclient.allowRestrictedHeaders=host,upgrade RestrictedHeadersTest host upgrade + * @run main/othervm -Djdk.httpclient.allowRestrictedHeaders=via RestrictedHeadersTest via + */ + +import java.net.URI; +import java.net.http.HttpRequest; +import java.util.Set; + +public class RestrictedHeadersTest { + public static void main(String[] args) { + if (args.length == 0) { + runDefaultTest(); + } else { + runTest(Set.of(args)); + } + } + + // This list must be same as impl + + static Set defaultRestrictedHeaders = + Set.of("connection", "content-length", "expect", "host", "upgrade"); + + private static void runDefaultTest() { + System.out.println("DEFAULT TEST: no property set"); + for (String header : defaultRestrictedHeaders) { + checkHeader(header, "foo", false); + } + // miscellaneous others that should succeed + checkHeader("foobar", "barfoo", true); + checkHeader("date", "today", true); + } + + private static void checkHeader(String name, String value, boolean succeed) { + try { + HttpRequest request = HttpRequest.newBuilder(URI.create("https://foo.com/")) + .header(name, value) + .GET() + .build(); + if (!succeed) { + String s = name+"/"+value+" should have failed"; + throw new RuntimeException(s); + } + System.out.printf("%s = %s succeeded as expected\n", name, value); + } catch (IllegalArgumentException iae) { + if (succeed) { + String s = name+"/"+value+" should have succeeded"; + throw new RuntimeException(s); + } + System.out.printf("%s = %s failed as expected\n", name, value); + } + } + + // args is the Set of allowed restricted headers + private static void runTest(Set args) { + System.out.print("RUNTEST: allowed headers set in property: "); + for (String arg : args) System.out.printf("%s ", arg); + System.out.println(""); + + for (String header : args) { + checkHeader(header, "val", true); + } + for (String header : defaultRestrictedHeaders) { + if (!args.contains(header)) { + checkHeader(header, "foo", false); + } + } + } +} diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/SpecialHeadersTest.java --- a/test/jdk/java/net/httpclient/SpecialHeadersTest.java Wed Nov 14 17:16:44 2018 +0530 +++ b/test/jdk/java/net/httpclient/SpecialHeadersTest.java Wed Nov 14 14:23:21 2018 +0000 @@ -38,6 +38,9 @@ * @run testng/othervm * -Djdk.httpclient.HttpClient.log=requests,headers,errors * SpecialHeadersTest + * @run testng/othervm -Djdk.httpclient.allowRestrictedHeaders=Host + * -Djdk.httpclient.HttpClient.log=requests,headers,errors + * SpecialHeadersTest */ import com.sun.net.httpserver.HttpServer; @@ -70,6 +73,7 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import static java.lang.System.err; import static java.lang.System.out; @@ -104,21 +108,33 @@ {"ORIGIN: upper"}, }; + // Needs net.property enabled for this part of test + static final String[][] headerNamesAndValues1 = new String[][]{ + {"Host: "}, + {"Host: camel-cased"}, + {"host: all-lower-case"}, + {"hoSt: mixed"} + }; + @DataProvider(name = "variants") public Object[][] variants() { + String prop = System.getProperty("jdk.httpclient.allowRestrictedHeaders"); + boolean hostTest = prop != null && prop.equalsIgnoreCase("host"); + final String[][] testInput = hostTest ? headerNamesAndValues1 : headerNamesAndValues; + List list = new ArrayList<>(); for (boolean sameClient : new boolean[] { false, true }) { - Arrays.asList(headerNamesAndValues).stream() + Arrays.asList(testInput).stream() .map(e -> new Object[] {httpURI, e[0], sameClient}) .forEach(list::add); - Arrays.asList(headerNamesAndValues).stream() + Arrays.asList(testInput).stream() .map(e -> new Object[] {httpsURI, e[0], sameClient}) .forEach(list::add); - Arrays.asList(headerNamesAndValues).stream() + Arrays.asList(testInput).stream() .map(e -> new Object[] {http2URI, e[0], sameClient}) .forEach(list::add); - Arrays.asList(headerNamesAndValues).stream() + Arrays.asList(testInput).stream() .map(e -> new Object[] {https2URI, e[0], sameClient}) .forEach(list::add); } @@ -131,7 +147,8 @@ return "Java-http-client/" + System.getProperty("java.version"); } - static final Map DEFAULTS = Map.of("USER-AGENT", userAgent()); + static final Map> DEFAULTS = Map.of( + "USER-AGENT", u -> userAgent(), "HOST", u -> u.getRawAuthority()); @Test(dataProvider = "variants") void test(String uriString, String headerNameAndValue, boolean sameClient) throws Exception { @@ -142,9 +159,9 @@ String v = headerNameAndValue.substring(index+1).trim(); String key = name.toUpperCase(Locale.ROOT); boolean useDefault = "".equals(v); - String value = useDefault ? DEFAULTS.get(key) : v; URI uri = URI.create(uriString+"?name="+key); + String value = useDefault ? DEFAULTS.get(key).apply(uri) : v; HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { @@ -210,7 +227,7 @@ return Optional.empty(); } @Override public HttpHeaders headers() { - Map> map = Map.of("via", List.of("http://foo.com")); + Map> map = Map.of("upgrade", List.of("http://foo.com")); return HttpHeaders.of(map, (x, y) -> true); } }; @@ -231,9 +248,9 @@ String v = headerNameAndValue.substring(index+1).trim(); String key = name.toUpperCase(Locale.ROOT); boolean useDefault = "".equals(v); - String value = useDefault ? DEFAULTS.get(key) : v; URI uri = URI.create(uriString+"?name="+key); + String value = useDefault ? DEFAULTS.get(key).apply(uri) : v; HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/security/16.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/httpclient/security/16.policy Wed Nov 14 14:23:21 2018 +0000 @@ -0,0 +1,45 @@ +// +// Copyright (c) 2016, 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. +// + +// Policy 16: Test tries to set Host header to localhost:123 but there is no permission + +grant { + // permissions common to all tests + permission java.util.PropertyPermission "*", "read"; + permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; + permission java.lang.RuntimePermission "modifyThread"; + permission java.util.logging.LoggingPermission "control", ""; + permission java.net.SocketPermission "localhost:1024-", "accept,listen"; + permission java.io.FilePermission "${test.src}${/}docs${/}-", "read"; + permission java.lang.RuntimePermission "createClassLoader"; + + + // permissions specific to this test + permission java.net.URLPermission "http://localhost:${port.number}/files/foo.txt", "GET:Host"; +}; + +// For proxy only. Not being tested +grant codebase "file:${test.classes}/proxydir/-" { + permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect"; + permission java.net.SocketPermission "localhost:1024-", "connect,resolve"; +}; diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/security/17.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/httpclient/security/17.policy Wed Nov 14 14:23:21 2018 +0000 @@ -0,0 +1,45 @@ +// +// Copyright (c) 2016, 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. +// + +// Policy 17. Grant permission to port 123 (no connect attempt is actually made) +grant { + // permissions common to all tests + permission java.util.PropertyPermission "*", "read"; + permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; + permission java.lang.RuntimePermission "modifyThread"; + permission java.util.logging.LoggingPermission "control", ""; + permission java.net.SocketPermission "localhost:1024-", "accept,listen"; + permission java.io.FilePermission "${test.src}${/}docs${/}-", "read"; + permission java.lang.RuntimePermission "createClassLoader"; + + + // permissions specific to this test + permission java.net.URLPermission "http://localhost:${port.number}/files/foo.txt", "GET:Host"; + permission java.net.URLPermission "http://foohost:123/files/foo.txt", "GET:Host"; +}; + +// For proxy only. Not being tested +grant codebase "file:${test.classes}/proxydir/-" { + permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect"; + permission java.net.SocketPermission "localhost:1024-", "connect,resolve"; +}; diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/security/Driver.java --- a/test/jdk/java/net/httpclient/security/Driver.java Wed Nov 14 17:16:44 2018 +0530 +++ b/test/jdk/java/net/httpclient/security/Driver.java Wed Nov 14 14:23:21 2018 +0000 @@ -70,6 +70,8 @@ runtest("10.policy", "10"); runtest("11.policy", "11"); runtest("12.policy", "12"); + runtest("16.policy", "16", "-Djdk.httpclient.allowRestrictedHeaders=Host"); + runtest("17.policy", "17", "-Djdk.httpclient.allowRestrictedHeaders=Host"); System.out.println("DONE"); } @@ -114,7 +116,11 @@ } public static void runtest(String policy, String testnum) throws Throwable { + runtest(policy, testnum, null); + } + + public static void runtest(String policy, String testnum, String addProp) throws Throwable { String testJdk = System.getProperty("test.jdk", "?"); String testSrc = System.getProperty("test.src", "?"); String testClassPath = System.getProperty("test.class.path", "?"); @@ -136,6 +142,9 @@ cmd.add("-Dport.number=" + Integer.toString(Utils.getFreePort())); cmd.add("-Dport.number1=" + Integer.toString(Utils.getFreePort())); cmd.add("-Djdk.httpclient.HttpClient.log=all,frames:all"); + if (addProp != null) { + cmd.add(addProp); + } cmd.add("-cp"); cmd.add(testClassPath); cmd.add("Security"); diff -r 9ca9aa224c39 -r 5f1ca46703f9 test/jdk/java/net/httpclient/security/Security.java --- a/test/jdk/java/net/httpclient/security/Security.java Wed Nov 14 17:16:44 2018 +0530 +++ b/test/jdk/java/net/httpclient/security/Security.java Wed Nov 14 14:23:21 2018 +0000 @@ -377,6 +377,24 @@ else throw new RuntimeException(t); } + }), + // (16) allowed to set Host header but does not have permission + TestAndResult.of(true, () -> { //Policy 16 + URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); + HttpRequest request = HttpRequest.newBuilder(u) + .header("Host", "foohost:123") + .GET().build(); + HttpResponse response = client.send(request, ofString()); + System.out.println("Received response:" + response); + }), + // (17) allowed to set Host header and does have permission + TestAndResult.of(false, () -> { //Policy 17 + URI u = URI.create("http://localhost:" + port + "/files/foo.txt"); + HttpRequest request = HttpRequest.newBuilder(u) + .header("Host", "foohost:123") + .GET().build(); + HttpResponse response = client.send(request, ofString()); + System.out.println("Received response:" + response); }) }; }