Skip to content

Commit 40a055e

Browse files
committed
8344228: Revisit SecurityManager usage in java.net.http after JEP 486 integration
Reviewed-by: jpai
1 parent 84ffb64 commit 40a055e

25 files changed

+86
-759
lines changed

src/java.net.http/share/classes/java/net/http/HttpClient.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@
3737
import java.net.InetSocketAddress;
3838
import java.net.Proxy;
3939
import java.net.ProxySelector;
40-
import java.net.URLPermission;
41-
import java.security.AccessController;
42-
import java.security.PrivilegedAction;
4340
import java.time.Duration;
4441
import java.util.Objects;
4542
import java.util.Optional;

src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java

Lines changed: 3 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,15 @@
2626
package jdk.internal.net.http;
2727

2828
import java.io.IOException;
29-
import java.net.InetSocketAddress;
3029
import java.net.ProtocolException;
31-
import java.net.ProxySelector;
32-
import java.net.URI;
33-
import java.net.URISyntaxException;
34-
import java.net.URLPermission;
35-
import java.security.AccessControlContext;
3630
import java.time.Duration;
37-
import java.util.List;
38-
import java.util.Map;
3931
import java.util.Optional;
4032
import java.util.concurrent.CompletableFuture;
4133
import java.util.concurrent.Executor;
4234
import java.util.concurrent.TimeUnit;
43-
import java.util.concurrent.TimeoutException;
4435
import java.util.concurrent.atomic.AtomicInteger;
4536
import java.util.function.Function;
4637
import java.net.http.HttpClient;
47-
import java.net.http.HttpHeaders;
4838
import java.net.http.HttpResponse;
4939
import java.net.http.HttpTimeoutException;
5040

@@ -53,20 +43,11 @@
5343
import jdk.internal.net.http.common.Utils;
5444
import jdk.internal.net.http.common.Log;
5545

56-
import static jdk.internal.net.http.common.Utils.permissionForProxy;
57-
5846
/**
5947
* One request/response exchange (handles 100/101 intermediate response also).
6048
* depth field used to track number of times a new request is being sent
6149
* for a given API request. If limit exceeded exception is thrown.
6250
*
63-
* Security check is performed here:
64-
* - uses AccessControlContext captured at API level
65-
* - checks for appropriate URLPermission for request
66-
* - if permission allowed, grants equivalent SocketPermission to call
67-
* - in case of direct HTTP proxy, checks additionally for access to proxy
68-
* (CONNECT proxying uses its own Exchange, so check done there)
69-
*
7051
*/
7152
final class Exchange<T> {
7253

@@ -83,8 +64,6 @@ final class Exchange<T> {
8364
// used to record possible cancellation raised before the exchImpl
8465
// has been established.
8566
private volatile IOException failed;
86-
@SuppressWarnings("removal")
87-
final AccessControlContext acc;
8867
final MultiExchange<T> multi;
8968
final Executor parentExecutor;
9069
volatile boolean upgrading; // to HTTP/2
@@ -103,22 +82,6 @@ final class Exchange<T> {
10382
this.upgrading = false;
10483
this.client = multi.client();
10584
this.multi = multi;
106-
this.acc = multi.acc;
107-
this.parentExecutor = multi.executor;
108-
this.pushGroup = multi.pushGroup;
109-
this.dbgTag = "Exchange";
110-
}
111-
112-
/* If different AccessControlContext to be used */
113-
Exchange(HttpRequestImpl request,
114-
MultiExchange<T> multi,
115-
@SuppressWarnings("removal") AccessControlContext acc)
116-
{
117-
this.request = request;
118-
this.acc = acc;
119-
this.upgrading = false;
120-
this.client = multi.client();
121-
this.multi = multi;
12285
this.parentExecutor = multi.executor;
12386
this.pushGroup = multi.pushGroup;
12487
this.dbgTag = "Exchange";
@@ -338,7 +301,7 @@ private void checkCancelled() {
338301
}
339302
}
340303

341-
<T> CompletableFuture<T> checkCancelled(CompletableFuture<T> cf, HttpConnection connection) {
304+
<U> CompletableFuture<U> checkCancelled(CompletableFuture<U> cf, HttpConnection connection) {
342305
return cf.handle((r,t) -> {
343306
if (t == null) {
344307
if (multi.requestCancelled()) {
@@ -354,7 +317,7 @@ <T> CompletableFuture<T> checkCancelled(CompletableFuture<T> cf, HttpConnection
354317
} catch (Throwable x) {
355318
if (debug.on()) debug.log("Failed to close connection", x);
356319
}
357-
return MinimalFuture.<T>failedFuture(t);
320+
return MinimalFuture.<U>failedFuture(t);
358321
}
359322
}
360323
}
@@ -422,15 +385,6 @@ public CompletableFuture<Response> responseAsync() {
422385
return responseAsyncImpl(null);
423386
}
424387

425-
CompletableFuture<Response> responseAsyncImpl(HttpConnection connection) {
426-
SecurityException e = checkPermissions();
427-
if (e != null) {
428-
return MinimalFuture.failedFuture(e);
429-
} else {
430-
return responseAsyncImpl0(connection);
431-
}
432-
}
433-
434388
// check whether the headersSentCF was completed exceptionally with
435389
// ProxyAuthorizationRequired. If so the Response embedded in the
436390
// exception is returned. Otherwise we proceed.
@@ -584,7 +538,7 @@ private CompletableFuture<Response> ignore1xxResponse(final Response rsp) {
584538
}
585539
}
586540

587-
CompletableFuture<Response> responseAsyncImpl0(HttpConnection connection) {
541+
CompletableFuture<Response> responseAsyncImpl(HttpConnection connection) {
588542
Function<ExchangeImpl<T>, CompletableFuture<Response>> after407Check;
589543
bodyIgnored = null;
590544
if (request.expectContinue()) {
@@ -735,109 +689,6 @@ HttpResponse.BodySubscriber<T> ignoreBody(HttpResponse.ResponseInfo hdrs) {
735689
return MinimalFuture.completedFuture(resp);
736690
}
737691

738-
private URI getURIForSecurityCheck() {
739-
URI u;
740-
String method = request.method();
741-
InetSocketAddress authority = request.authority();
742-
URI uri = request.uri();
743-
744-
// CONNECT should be restricted at API level
745-
if (method.equalsIgnoreCase("CONNECT")) {
746-
try {
747-
u = new URI("socket",
748-
null,
749-
authority.getHostString(),
750-
authority.getPort(),
751-
null,
752-
null,
753-
null);
754-
} catch (URISyntaxException e) {
755-
throw new InternalError(e); // shouldn't happen
756-
}
757-
} else {
758-
u = uri;
759-
}
760-
return u;
761-
}
762-
763-
/**
764-
* Returns the security permission required for the given details.
765-
* If method is CONNECT, then uri must be of form "scheme://host:port"
766-
*/
767-
private static URLPermission permissionForServer(URI uri,
768-
String method,
769-
Map<String, List<String>> headers) {
770-
if (method.equals("CONNECT")) {
771-
return new URLPermission(uri.toString(), "CONNECT");
772-
} else {
773-
return Utils.permissionForServer(uri, method, headers.keySet().stream());
774-
}
775-
}
776-
777-
/**
778-
* Performs the necessary security permission checks required to retrieve
779-
* the response. Returns a security exception representing the denied
780-
* permission, or null if all checks pass or there is no security manager.
781-
*/
782-
private SecurityException checkPermissions() {
783-
String method = request.method();
784-
@SuppressWarnings("removal")
785-
SecurityManager sm = System.getSecurityManager();
786-
if (sm == null || method.equals("CONNECT")) {
787-
// tunneling will have a null acc, which is fine. The proxy
788-
// permission check will have already been preformed.
789-
return null;
790-
}
791-
792-
HttpHeaders userHeaders = request.getUserHeaders();
793-
URI u = getURIForSecurityCheck();
794-
URLPermission p = permissionForServer(u, method, userHeaders.map());
795-
796-
try {
797-
assert acc != null;
798-
sm.checkPermission(p, acc);
799-
} catch (SecurityException e) {
800-
return e;
801-
}
802-
String hostHeader = userHeaders.firstValue("Host").orElse(null);
803-
if (hostHeader != null && !hostHeader.equalsIgnoreCase(u.getHost())) {
804-
// user has set a Host header different to request URI
805-
// must check that for URLPermission also
806-
URI u1 = replaceHostInURI(u, hostHeader);
807-
URLPermission p1 = permissionForServer(u1, method, userHeaders.map());
808-
try {
809-
assert acc != null;
810-
sm.checkPermission(p1, acc);
811-
} catch (SecurityException e) {
812-
return e;
813-
}
814-
}
815-
ProxySelector ps = client.proxySelector();
816-
if (ps != null) {
817-
if (!method.equals("CONNECT")) {
818-
// a non-tunneling HTTP proxy. Need to check access
819-
URLPermission proxyPerm = permissionForProxy(request.proxy());
820-
if (proxyPerm != null) {
821-
try {
822-
sm.checkPermission(proxyPerm, acc);
823-
} catch (SecurityException e) {
824-
return e;
825-
}
826-
}
827-
}
828-
}
829-
return null;
830-
}
831-
832-
private static URI replaceHostInURI(URI u, String hostPort) {
833-
StringBuilder sb = new StringBuilder();
834-
sb.append(u.getScheme())
835-
.append("://")
836-
.append(hostPort)
837-
.append(u.getRawPath());
838-
return URI.create(sb.toString());
839-
}
840-
841692
HttpClient.Version version() {
842693
return multi.version();
843694
}

src/java.net.http/share/classes/jdk/internal/net/http/HttpClientBuilderImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -46,7 +46,6 @@ public class HttpClientBuilderImpl implements HttpClient.Builder {
4646
Authenticator authenticator;
4747
HttpClient.Version version;
4848
Executor executor;
49-
// Security parameters
5049
SSLContext sslContext;
5150
SSLParameters sslParams;
5251
int priority = -1;

src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -48,10 +48,7 @@
4848
import java.nio.channels.SelectableChannel;
4949
import java.nio.channels.SelectionKey;
5050
import java.nio.channels.Selector;
51-
import java.security.AccessControlContext;
52-
import java.security.AccessController;
5351
import java.security.NoSuchAlgorithmException;
54-
import java.security.PrivilegedAction;
5552
import java.time.Duration;
5653
import java.time.temporal.ChronoUnit;
5754
import java.util.ArrayList;
@@ -97,7 +94,6 @@
9794
import jdk.internal.net.http.common.OperationTrackers.Trackable;
9895
import jdk.internal.net.http.common.OperationTrackers.Tracker;
9996
import jdk.internal.net.http.websocket.BuilderImpl;
100-
import jdk.internal.misc.InnocuousThread;
10197

10298
/**
10399
* Client implementation. Contains all configuration information and also
@@ -131,16 +127,10 @@ private static final class DefaultThreadFactory implements ThreadFactory {
131127
namePrefix = "HttpClient-" + clientID + "-Worker-";
132128
}
133129

134-
@SuppressWarnings("removal")
135130
@Override
136131
public Thread newThread(Runnable r) {
137132
String name = namePrefix + nextId.getAndIncrement();
138-
Thread t;
139-
if (System.getSecurityManager() == null) {
140-
t = new Thread(null, r, name, 0, false);
141-
} else {
142-
t = InnocuousThread.newThread(name, r);
143-
}
133+
Thread t = new Thread(null, r, name, 0, false);
144134
t.setDaemon(true);
145135
return t;
146136
}
@@ -188,15 +178,9 @@ public void ensureExecutedAsync(Runnable command) {
188178
}
189179
}
190180

191-
@SuppressWarnings("removal")
192181
private void shutdown() {
193182
if (delegate instanceof ExecutorService service) {
194-
PrivilegedAction<?> action = () -> {
195-
service.shutdown();
196-
return null;
197-
};
198-
AccessController.doPrivileged(action, null,
199-
new RuntimePermission("modifyThread"));
183+
service.shutdown();
200184
}
201185
}
202186
}
@@ -336,7 +320,6 @@ static void abortPendingRequests(HttpClientImpl client, Throwable reason) {
336320
private final ConnectionPool connections;
337321
private final DelegatingExecutor delegatingExecutor;
338322
private final boolean isDefaultExecutor;
339-
// Security parameters
340323
private final SSLContext sslContext;
341324
private final SSLParameters sslParams;
342325
private final SelectorManager selmgr;
@@ -445,16 +428,6 @@ private HttpClientImpl(HttpClientBuilderImpl builder,
445428
SingleFacadeFactory facadeFactory) {
446429
id = CLIENT_IDS.incrementAndGet();
447430
dbgTag = "HttpClientImpl(" + id +")";
448-
@SuppressWarnings("removal")
449-
var sm = System.getSecurityManager();
450-
if (sm != null && builder.localAddr != null) {
451-
// when a specific local address is configured, it will eventually
452-
// lead to the SocketChannel.bind(...) call with an InetSocketAddress
453-
// whose InetAddress is the local address and the port is 0. That ultimately
454-
// leads to a SecurityManager.checkListen permission check for that port.
455-
// so we do that security manager check here with port 0.
456-
sm.checkListen(0);
457-
}
458431
localAddr = builder.localAddr;
459432
if (builder.sslContext == null) {
460433
try {
@@ -484,7 +457,7 @@ private HttpClientImpl(HttpClientBuilderImpl builder,
484457
Redirect.NEVER : builder.followRedirects;
485458
this.userProxySelector = builder.proxy;
486459
this.proxySelector = Optional.ofNullable(userProxySelector)
487-
.orElseGet(HttpClientImpl::getDefaultProxySelector);
460+
.orElseGet(ProxySelector::getDefault);
488461
if (debug.on())
489462
debug.log("proxySelector is %s (user-supplied=%s)",
490463
this.proxySelector, userProxySelector != null);
@@ -642,12 +615,6 @@ private static SSLParameters getDefaultParams(SSLContext ctx) {
642615
return params;
643616
}
644617

645-
@SuppressWarnings("removal")
646-
private static ProxySelector getDefaultProxySelector() {
647-
PrivilegedAction<ProxySelector> action = ProxySelector::getDefault;
648-
return AccessController.doPrivileged(action);
649-
}
650-
651618
// Returns the facade that was returned to the application code.
652619
// May be null if that facade is no longer referenced.
653620
final HttpClientFacade facade() {
@@ -992,7 +959,6 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) {
992959
return sendAsync(userRequest, responseHandler, pushPromiseHandler, delegatingExecutor.delegate);
993960
}
994961

995-
@SuppressWarnings("removal")
996962
private <T> CompletableFuture<HttpResponse<T>>
997963
sendAsync(HttpRequest userRequest,
998964
BodyHandler<T> responseHandler,
@@ -1012,11 +978,7 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) {
1012978
return MinimalFuture.failedFuture(selmgr.selectorClosedException());
1013979
}
1014980

1015-
AccessControlContext acc = null;
1016-
if (System.getSecurityManager() != null)
1017-
acc = AccessController.getContext();
1018-
1019-
// Clone the, possibly untrusted, HttpRequest
981+
// Clone the possibly untrusted HttpRequest
1020982
HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector);
1021983
if (requestImpl.method().equals("CONNECT"))
1022984
throw new IllegalArgumentException("Unsupported method CONNECT");
@@ -1049,8 +1011,7 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) {
10491011
requestImpl,
10501012
this,
10511013
responseHandler,
1052-
pushPromiseHandler,
1053-
acc);
1014+
pushPromiseHandler);
10541015
CompletableFuture<HttpResponse<T>> mexCf = mex.responseAsync(executor);
10551016
CompletableFuture<HttpResponse<T>> res = mexCf.whenComplete((b,t) -> requestUnreference());
10561017
if (DEBUGELAPSED) {

0 commit comments

Comments
 (0)