8344228: Revisit SecurityManager usage in java.net.http after JEP 486 integration

Reviewed-by: jpai
This commit is contained in:
Daniel Fuchs 2024-11-15 15:05:33 +00:00
parent 84ffb64cd7
commit 40a055ebd2
25 changed files with 86 additions and 759 deletions

View File

@ -37,9 +37,6 @@ import java.net.CookieHandler;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URLPermission;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;

View File

@ -26,25 +26,15 @@
package jdk.internal.net.http;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLPermission;
import java.security.AccessControlContext;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpResponse;
import java.net.http.HttpTimeoutException;
@ -53,20 +43,11 @@ import jdk.internal.net.http.common.MinimalFuture;
import jdk.internal.net.http.common.Utils;
import jdk.internal.net.http.common.Log;
import static jdk.internal.net.http.common.Utils.permissionForProxy;
/**
* One request/response exchange (handles 100/101 intermediate response also).
* depth field used to track number of times a new request is being sent
* for a given API request. If limit exceeded exception is thrown.
*
* Security check is performed here:
* - uses AccessControlContext captured at API level
* - checks for appropriate URLPermission for request
* - if permission allowed, grants equivalent SocketPermission to call
* - in case of direct HTTP proxy, checks additionally for access to proxy
* (CONNECT proxying uses its own Exchange, so check done there)
*
*/
final class Exchange<T> {
@ -83,8 +64,6 @@ final class Exchange<T> {
// used to record possible cancellation raised before the exchImpl
// has been established.
private volatile IOException failed;
@SuppressWarnings("removal")
final AccessControlContext acc;
final MultiExchange<T> multi;
final Executor parentExecutor;
volatile boolean upgrading; // to HTTP/2
@ -103,22 +82,6 @@ final class Exchange<T> {
this.upgrading = false;
this.client = multi.client();
this.multi = multi;
this.acc = multi.acc;
this.parentExecutor = multi.executor;
this.pushGroup = multi.pushGroup;
this.dbgTag = "Exchange";
}
/* If different AccessControlContext to be used */
Exchange(HttpRequestImpl request,
MultiExchange<T> multi,
@SuppressWarnings("removal") AccessControlContext acc)
{
this.request = request;
this.acc = acc;
this.upgrading = false;
this.client = multi.client();
this.multi = multi;
this.parentExecutor = multi.executor;
this.pushGroup = multi.pushGroup;
this.dbgTag = "Exchange";
@ -338,7 +301,7 @@ final class Exchange<T> {
}
}
<T> CompletableFuture<T> checkCancelled(CompletableFuture<T> cf, HttpConnection connection) {
<U> CompletableFuture<U> checkCancelled(CompletableFuture<U> cf, HttpConnection connection) {
return cf.handle((r,t) -> {
if (t == null) {
if (multi.requestCancelled()) {
@ -354,7 +317,7 @@ final class Exchange<T> {
} catch (Throwable x) {
if (debug.on()) debug.log("Failed to close connection", x);
}
return MinimalFuture.<T>failedFuture(t);
return MinimalFuture.<U>failedFuture(t);
}
}
}
@ -422,15 +385,6 @@ final class Exchange<T> {
return responseAsyncImpl(null);
}
CompletableFuture<Response> responseAsyncImpl(HttpConnection connection) {
SecurityException e = checkPermissions();
if (e != null) {
return MinimalFuture.failedFuture(e);
} else {
return responseAsyncImpl0(connection);
}
}
// check whether the headersSentCF was completed exceptionally with
// ProxyAuthorizationRequired. If so the Response embedded in the
// exception is returned. Otherwise we proceed.
@ -584,7 +538,7 @@ final class Exchange<T> {
}
}
CompletableFuture<Response> responseAsyncImpl0(HttpConnection connection) {
CompletableFuture<Response> responseAsyncImpl(HttpConnection connection) {
Function<ExchangeImpl<T>, CompletableFuture<Response>> after407Check;
bodyIgnored = null;
if (request.expectContinue()) {
@ -735,109 +689,6 @@ final class Exchange<T> {
return MinimalFuture.completedFuture(resp);
}
private URI getURIForSecurityCheck() {
URI u;
String method = request.method();
InetSocketAddress authority = request.authority();
URI uri = request.uri();
// CONNECT should be restricted at API level
if (method.equalsIgnoreCase("CONNECT")) {
try {
u = new URI("socket",
null,
authority.getHostString(),
authority.getPort(),
null,
null,
null);
} catch (URISyntaxException e) {
throw new InternalError(e); // shouldn't happen
}
} else {
u = uri;
}
return u;
}
/**
* Returns the security permission required for the given details.
* If method is CONNECT, then uri must be of form "scheme://host:port"
*/
private static URLPermission permissionForServer(URI uri,
String method,
Map<String, List<String>> headers) {
if (method.equals("CONNECT")) {
return new URLPermission(uri.toString(), "CONNECT");
} else {
return Utils.permissionForServer(uri, method, headers.keySet().stream());
}
}
/**
* Performs the necessary security permission checks required to retrieve
* the response. Returns a security exception representing the denied
* permission, or null if all checks pass or there is no security manager.
*/
private SecurityException checkPermissions() {
String method = request.method();
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm == null || method.equals("CONNECT")) {
// tunneling will have a null acc, which is fine. The proxy
// permission check will have already been preformed.
return null;
}
HttpHeaders userHeaders = request.getUserHeaders();
URI u = getURIForSecurityCheck();
URLPermission p = permissionForServer(u, method, userHeaders.map());
try {
assert acc != null;
sm.checkPermission(p, acc);
} 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")) {
// a non-tunneling HTTP proxy. Need to check access
URLPermission proxyPerm = permissionForProxy(request.proxy());
if (proxyPerm != null) {
try {
sm.checkPermission(proxyPerm, acc);
} catch (SecurityException e) {
return e;
}
}
}
}
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();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, 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
@ -46,7 +46,6 @@ public class HttpClientBuilderImpl implements HttpClient.Builder {
Authenticator authenticator;
HttpClient.Version version;
Executor executor;
// Security parameters
SSLContext sslContext;
SSLParameters sslParams;
int priority = -1;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, 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
@ -48,10 +48,7 @@ import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@ -97,7 +94,6 @@ import jdk.internal.net.http.common.Utils;
import jdk.internal.net.http.common.OperationTrackers.Trackable;
import jdk.internal.net.http.common.OperationTrackers.Tracker;
import jdk.internal.net.http.websocket.BuilderImpl;
import jdk.internal.misc.InnocuousThread;
/**
* Client implementation. Contains all configuration information and also
@ -131,16 +127,10 @@ final class HttpClientImpl extends HttpClient implements Trackable {
namePrefix = "HttpClient-" + clientID + "-Worker-";
}
@SuppressWarnings("removal")
@Override
public Thread newThread(Runnable r) {
String name = namePrefix + nextId.getAndIncrement();
Thread t;
if (System.getSecurityManager() == null) {
t = new Thread(null, r, name, 0, false);
} else {
t = InnocuousThread.newThread(name, r);
}
Thread t = new Thread(null, r, name, 0, false);
t.setDaemon(true);
return t;
}
@ -188,15 +178,9 @@ final class HttpClientImpl extends HttpClient implements Trackable {
}
}
@SuppressWarnings("removal")
private void shutdown() {
if (delegate instanceof ExecutorService service) {
PrivilegedAction<?> action = () -> {
service.shutdown();
return null;
};
AccessController.doPrivileged(action, null,
new RuntimePermission("modifyThread"));
service.shutdown();
}
}
}
@ -336,7 +320,6 @@ final class HttpClientImpl extends HttpClient implements Trackable {
private final ConnectionPool connections;
private final DelegatingExecutor delegatingExecutor;
private final boolean isDefaultExecutor;
// Security parameters
private final SSLContext sslContext;
private final SSLParameters sslParams;
private final SelectorManager selmgr;
@ -445,16 +428,6 @@ final class HttpClientImpl extends HttpClient implements Trackable {
SingleFacadeFactory facadeFactory) {
id = CLIENT_IDS.incrementAndGet();
dbgTag = "HttpClientImpl(" + id +")";
@SuppressWarnings("removal")
var sm = System.getSecurityManager();
if (sm != null && builder.localAddr != null) {
// when a specific local address is configured, it will eventually
// lead to the SocketChannel.bind(...) call with an InetSocketAddress
// whose InetAddress is the local address and the port is 0. That ultimately
// leads to a SecurityManager.checkListen permission check for that port.
// so we do that security manager check here with port 0.
sm.checkListen(0);
}
localAddr = builder.localAddr;
if (builder.sslContext == null) {
try {
@ -484,7 +457,7 @@ final class HttpClientImpl extends HttpClient implements Trackable {
Redirect.NEVER : builder.followRedirects;
this.userProxySelector = builder.proxy;
this.proxySelector = Optional.ofNullable(userProxySelector)
.orElseGet(HttpClientImpl::getDefaultProxySelector);
.orElseGet(ProxySelector::getDefault);
if (debug.on())
debug.log("proxySelector is %s (user-supplied=%s)",
this.proxySelector, userProxySelector != null);
@ -642,12 +615,6 @@ final class HttpClientImpl extends HttpClient implements Trackable {
return params;
}
@SuppressWarnings("removal")
private static ProxySelector getDefaultProxySelector() {
PrivilegedAction<ProxySelector> action = ProxySelector::getDefault;
return AccessController.doPrivileged(action);
}
// Returns the facade that was returned to the application code.
// May be null if that facade is no longer referenced.
final HttpClientFacade facade() {
@ -992,7 +959,6 @@ final class HttpClientImpl extends HttpClient implements Trackable {
return sendAsync(userRequest, responseHandler, pushPromiseHandler, delegatingExecutor.delegate);
}
@SuppressWarnings("removal")
private <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest userRequest,
BodyHandler<T> responseHandler,
@ -1012,11 +978,7 @@ final class HttpClientImpl extends HttpClient implements Trackable {
return MinimalFuture.failedFuture(selmgr.selectorClosedException());
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null)
acc = AccessController.getContext();
// Clone the, possibly untrusted, HttpRequest
// Clone the possibly untrusted HttpRequest
HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector);
if (requestImpl.method().equals("CONNECT"))
throw new IllegalArgumentException("Unsupported method CONNECT");
@ -1049,8 +1011,7 @@ final class HttpClientImpl extends HttpClient implements Trackable {
requestImpl,
this,
responseHandler,
pushPromiseHandler,
acc);
pushPromiseHandler);
CompletableFuture<HttpResponse<T>> mexCf = mex.responseAsync(executor);
CompletableFuture<HttpResponse<T>> res = mexCf.whenComplete((b,t) -> requestUnreference());
if (DEBUGELAPSED) {

View File

@ -31,9 +31,6 @@ import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.util.List;
import java.util.Locale;
@ -65,17 +62,13 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
final boolean secure;
final boolean expectContinue;
private volatile boolean isWebSocket;
@SuppressWarnings("removal")
private volatile AccessControlContext acc;
private final Duration timeout; // may be null
private final Optional<HttpClient.Version> version;
private volatile boolean userSetAuthorization;
private volatile boolean userSetProxyAuthorization;
private static String userAgent() {
PrivilegedAction<String> pa = () -> System.getProperty("java.version");
@SuppressWarnings("removal")
String version = AccessController.doPrivileged(pa);
String version = System.getProperty("java.version");
return "Java-http-client/" + version;
}
@ -196,7 +189,6 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
this.expectContinue = other.expectContinue;
this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
this.requestPublisher = mayHaveBody ? publisher(other) : null; // may be null
this.acc = other.acc;
this.timeout = other.timeout;
this.version = other.version();
this.authority = null;
@ -274,7 +266,6 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
this.expectContinue = parent.expectContinue;
this.secure = parent.secure;
this.requestPublisher = parent.requestPublisher;
this.acc = parent.acc;
this.timeout = parent.timeout;
this.version = parent.version;
this.authority = null;
@ -395,7 +386,6 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
systemHeadersBuilder.setHeader(name, value);
}
@SuppressWarnings("removal")
InetSocketAddress getAddress() {
URI uri = uri();
if (uri == null) {
@ -412,8 +402,7 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
final String host = uri.getHost();
final int port = p;
if (proxy() == null) {
PrivilegedAction<InetSocketAddress> pa = () -> new InetSocketAddress(host, port);
return AccessController.doPrivileged(pa);
return new InetSocketAddress(host, port);
} else {
return InetSocketAddress.createUnresolved(host, port);
}

View File

@ -30,7 +30,6 @@ import java.lang.ref.WeakReference;
import java.net.ConnectException;
import java.net.http.HttpConnectTimeoutException;
import java.time.Duration;
import java.security.AccessControlContext;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
@ -79,8 +78,6 @@ class MultiExchange<T> implements Cancelable {
private final HttpRequest userRequest; // the user request
private final HttpRequestImpl request; // a copy of the user request
private final ConnectTimeoutTracker connectTimeout; // null if no timeout
@SuppressWarnings("removal")
final AccessControlContext acc;
final HttpClientImpl client;
final HttpResponse.BodyHandler<T> responseHandler;
final HttpClientImpl.DelegatingExecutor executor;
@ -155,8 +152,7 @@ class MultiExchange<T> implements Cancelable {
HttpRequestImpl requestImpl,
HttpClientImpl client,
HttpResponse.BodyHandler<T> responseHandler,
PushPromiseHandler<T> pushPromiseHandler,
@SuppressWarnings("removal") AccessControlContext acc) {
PushPromiseHandler<T> pushPromiseHandler) {
this.previous = null;
this.userRequest = userRequest;
this.request = requestImpl;
@ -164,15 +160,11 @@ class MultiExchange<T> implements Cancelable {
this.previousreq = null;
this.client = client;
this.filters = client.filterChain();
this.acc = acc;
this.executor = client.theExecutor();
this.responseHandler = responseHandler;
if (pushPromiseHandler != null) {
Executor ensureExecutedAsync = this.executor::ensureExecutedAsync;
Executor executor = acc == null
? ensureExecutedAsync
: new PrivilegedExecutor(ensureExecutedAsync, acc);
Executor executor = this.executor::ensureExecutedAsync;
this.pushGroup = new PushGroup<>(pushPromiseHandler, request, executor);
} else {
pushGroup = null;
@ -470,7 +462,7 @@ class MultiExchange<T> implements Cancelable {
previousreq = currentreq;
currentreq = newrequest;
retriedOnce = false;
setExchange(new Exchange<>(currentreq, this, acc));
setExchange(new Exchange<>(currentreq, this));
return responseAsyncImpl();
}).thenCompose(Function.identity());
} })

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, 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
@ -32,9 +32,6 @@ import java.net.StandardSocketOptions;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
@ -167,7 +164,6 @@ class PlainHttpConnection extends HttpConnection {
}
}
@SuppressWarnings("removal")
@Override
public CompletableFuture<Void> connectAsync(Exchange<?> exchange) {
CompletableFuture<ConnectState> cf = new MinimalFuture<>();
@ -191,14 +187,12 @@ class PlainHttpConnection extends HttpConnection {
debug.log("binding to configured local address " + localAddr);
}
var sockAddr = new InetSocketAddress(localAddr, 0);
PrivilegedExceptionAction<SocketChannel> pa = () -> chan.bind(sockAddr);
try {
AccessController.doPrivileged(pa);
chan.bind(sockAddr);
if (debug.on()) {
debug.log("bind completed " + localAddr);
}
} catch (PrivilegedActionException e) {
var cause = e.getCause();
} catch (IOException cause) {
if (debug.on()) {
debug.log("bind to " + localAddr + " failed: " + cause.getMessage());
}
@ -206,13 +200,7 @@ class PlainHttpConnection extends HttpConnection {
}
}
PrivilegedExceptionAction<Boolean> pa =
() -> chan.connect(Utils.resolveAddress(address));
try {
finished = AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw e.getCause();
}
finished = chan.connect(Utils.resolveAddress(address));
if (finished) {
if (debug.on()) debug.log("connect finished without blocking");
if (connectionOpened()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, 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
@ -72,7 +72,7 @@ final class PlainTunnelingConnection extends HttpConnection {
assert client != null;
HttpRequestImpl req = new HttpRequestImpl("CONNECT", address, proxyHeaders);
MultiExchange<Void> mulEx = new MultiExchange<>(null, req,
client, discarding(), null, null);
client, discarding(), null);
Exchange<Void> connectExchange = mulEx.getExchange();
return connectExchange

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2015, 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.internal.net.http;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Executes tasks within a given access control context, and by a given executor.
*/
class PrivilegedExecutor implements Executor {
/** The underlying executor. May be provided by the user. */
final Executor executor;
/** The ACC to execute the tasks within. */
@SuppressWarnings("removal")
final AccessControlContext acc;
public PrivilegedExecutor(Executor executor, @SuppressWarnings("removal") AccessControlContext acc) {
Objects.requireNonNull(executor);
Objects.requireNonNull(acc);
this.executor = executor;
this.acc = acc;
}
private static class PrivilegedRunnable implements Runnable {
private final Runnable r;
@SuppressWarnings("removal")
private final AccessControlContext acc;
PrivilegedRunnable(Runnable r, @SuppressWarnings("removal") AccessControlContext acc) {
this.r = r;
this.acc = acc;
}
@SuppressWarnings("removal")
@Override
public void run() {
PrivilegedAction<Void> pa = () -> { r.run(); return null; };
AccessController.doPrivileged(pa, acc);
}
}
@Override
public void execute(Runnable r) {
executor.execute(new PrivilegedRunnable(r, acc));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2024, 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
@ -27,7 +27,6 @@ package jdk.internal.net.http;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
@ -37,11 +36,6 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -229,11 +223,6 @@ public final class RequestPublishers {
/**
* Publishes the content of a given file.
* <p>
* Privileged actions are performed within a limited doPrivileged that only
* asserts the specific, read, file permission that was checked during the
* construction of this FilePublisher. This only applies if the file system
* that created the file provides interoperability with {@code java.io.File}.
*/
public static class FilePublisher implements BodyPublisher {
@ -241,62 +230,27 @@ public final class RequestPublishers {
private final long length;
private final Function<Path, InputStream> inputStreamSupplier;
private static String pathForSecurityCheck(Path path) {
return path.toFile().getPath();
}
/**
* Factory for creating FilePublisher.
*
* Permission checks are performed here before construction of the
* FilePublisher. Permission checking and construction are deliberately
* and tightly co-located.
*/
public static FilePublisher create(Path path)
throws FileNotFoundException {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
FilePermission filePermission = null;
boolean defaultFS = true;
try {
String fn = pathForSecurityCheck(path);
if (sm != null) {
FilePermission readPermission = new FilePermission(fn, "read");
sm.checkPermission(readPermission);
filePermission = readPermission;
}
path.toFile().getPath();
} catch (UnsupportedOperationException uoe) {
// path not associated with the default file system provider
defaultFS = false;
// Path not associated with the default file system
// Test early if an input stream can still be obtained
try {
if (sm != null) {
Files.newInputStream(path).close();
}
} catch (IOException ioe) {
if (ioe instanceof FileNotFoundException) {
throw (FileNotFoundException) ioe;
} else {
var ex = new FileNotFoundException(ioe.getMessage());
ex.initCause(ioe);
throw ex;
}
}
}
// existence check must be after permission checks
// existence check must be after FS checks
if (Files.notExists(path))
throw new FileNotFoundException(path + " not found");
Permission perm = filePermission;
assert perm == null || perm.getActions().equals("read");
@SuppressWarnings("removal")
AccessControlContext acc = sm != null ?
AccessController.getContext() : null;
boolean finalDefaultFS = defaultFS;
Function<Path, InputStream> inputStreamSupplier = (p) ->
createInputStream(p, acc, perm, finalDefaultFS);
createInputStream(p, finalDefaultFS);
long length;
try {
@ -308,41 +262,17 @@ public final class RequestPublishers {
return new FilePublisher(path, length, inputStreamSupplier);
}
@SuppressWarnings("removal")
private static InputStream createInputStream(Path path,
AccessControlContext acc,
Permission perm,
boolean defaultFS) {
try {
if (acc != null) {
PrivilegedExceptionAction<InputStream> pa = defaultFS
? () -> new FileInputStream(path.toFile())
: () -> Files.newInputStream(path);
return perm != null
? AccessController.doPrivileged(pa, acc, perm)
: AccessController.doPrivileged(pa, acc);
} else {
return defaultFS
return defaultFS
? new FileInputStream(path.toFile())
: Files.newInputStream(path);
}
} catch (PrivilegedActionException pae) {
throw toUncheckedException(pae.getCause());
} catch (IOException io) {
throw new UncheckedIOException(io);
}
}
private static RuntimeException toUncheckedException(Throwable t) {
if (t instanceof RuntimeException)
throw (RuntimeException) t;
if (t instanceof Error)
throw (Error) t;
if (t instanceof IOException)
throw new UncheckedIOException((IOException) t);
throw new UndeclaredThrowableException(t);
}
private FilePublisher(Path name,
long length,
Function<Path, InputStream> inputStreamSupplier) {

View File

@ -25,8 +25,6 @@
package jdk.internal.net.http;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
@ -34,10 +32,8 @@ import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@ -56,62 +52,31 @@ public final class ResponseBodyHandlers {
private ResponseBodyHandlers() { }
private static final String pathForSecurityCheck(Path path) {
return path.toFile().getPath();
}
/**
* A Path body handler.
*/
public static class PathBodyHandler implements BodyHandler<Path>{
private final Path file;
private final List<OpenOption> openOptions; // immutable list
@SuppressWarnings("removal")
private final AccessControlContext acc;
private final FilePermission filePermission;
/**
* Factory for creating PathBodyHandler.
*
* Permission checks are performed here before construction of the
* PathBodyHandler. Permission checking and construction are
* deliberately and tightly co-located.
*/
public static PathBodyHandler create(Path file,
List<OpenOption> openOptions) {
FilePermission filePermission = null;
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
String fn = pathForSecurityCheck(file);
FilePermission writePermission = new FilePermission(fn, "write");
sm.checkPermission(writePermission);
filePermission = writePermission;
} catch (UnsupportedOperationException ignored) {
// path not associated with the default file system provider
}
}
assert filePermission == null || filePermission.getActions().equals("write");
@SuppressWarnings("removal")
var acc = sm != null ? AccessController.getContext() : null;
return new PathBodyHandler(file, openOptions, acc, filePermission);
return new PathBodyHandler(file, openOptions);
}
private PathBodyHandler(Path file,
List<OpenOption> openOptions,
@SuppressWarnings("removal") AccessControlContext acc,
FilePermission filePermission) {
List<OpenOption> openOptions) {
this.file = file;
this.openOptions = openOptions;
this.acc = acc;
this.filePermission = filePermission;
}
@Override
public BodySubscriber<Path> apply(ResponseInfo responseInfo) {
return new PathSubscriber(file, openOptions, acc, filePermission);
return new PathSubscriber(file, openOptions);
}
}
@ -170,44 +135,20 @@ public final class ResponseBodyHandlers {
public static class FileDownloadBodyHandler implements BodyHandler<Path> {
private final Path directory;
private final List<OpenOption> openOptions;
@SuppressWarnings("removal")
private final AccessControlContext acc;
private final FilePermission[] filePermissions; // may be null
/**
* Factory for creating FileDownloadBodyHandler.
*
* Permission checks are performed here before construction of the
* FileDownloadBodyHandler. Permission checking and construction are
* deliberately and tightly co-located.
*/
public static FileDownloadBodyHandler create(Path directory,
List<OpenOption> openOptions) {
String fn;
try {
fn = pathForSecurityCheck(directory);
directory.toFile().getPath();
} catch (UnsupportedOperationException uoe) {
// directory not associated with the default file system provider
throw new IllegalArgumentException("invalid path: " + directory, uoe);
}
FilePermission filePermissions[] = null;
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
FilePermission writePermission = new FilePermission(fn, "write");
String writePathPerm = fn + File.separatorChar + "*";
FilePermission writeInDirPermission = new FilePermission(writePathPerm, "write");
sm.checkPermission(writeInDirPermission);
FilePermission readPermission = new FilePermission(fn, "read");
sm.checkPermission(readPermission);
// read permission is only needed before determine the below checks
// only write permission is required when downloading to the file
filePermissions = new FilePermission[] { writePermission, writeInDirPermission };
}
// existence, etc, checks must be after permission checks
// existence, etc, checks must be after FS checks
if (Files.notExists(directory))
throw new IllegalArgumentException("non-existent directory: " + directory);
if (!Files.isDirectory(directory))
@ -215,21 +156,13 @@ public final class ResponseBodyHandlers {
if (!Files.isWritable(directory))
throw new IllegalArgumentException("non-writable directory: " + directory);
assert filePermissions == null || (filePermissions[0].getActions().equals("write")
&& filePermissions[1].getActions().equals("write"));
@SuppressWarnings("removal")
var acc = sm != null ? AccessController.getContext() : null;
return new FileDownloadBodyHandler(directory, openOptions, acc, filePermissions);
return new FileDownloadBodyHandler(directory, openOptions);
}
private FileDownloadBodyHandler(Path directory,
List<OpenOption> openOptions,
@SuppressWarnings("removal") AccessControlContext acc,
FilePermission... filePermissions) {
List<OpenOption> openOptions) {
this.directory = directory;
this.openOptions = openOptions;
this.acc = acc;
this.filePermissions = filePermissions;
}
/** The "attachment" disposition-type and separator. */
@ -394,7 +327,7 @@ public final class ResponseBodyHandlers {
"Resulting file, " + file.toString() + ", outside of given directory");
}
return new PathSubscriber(file, openOptions, acc, filePermissions);
return new PathSubscriber(file, openOptions);
}
}
}

View File

@ -26,7 +26,6 @@
package jdk.internal.net.http;
import java.io.BufferedReader;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -35,11 +34,6 @@ import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -164,83 +158,31 @@ public class ResponseSubscribers {
/**
* A Subscriber that writes the flow of data to a given file.
*
* Privileged actions are performed within a limited doPrivileged that only
* asserts the specific, write, file permissions that were checked during
* the construction of this PathSubscriber.
*/
public static class PathSubscriber implements TrustedSubscriber<Path> {
private static final FilePermission[] EMPTY_FILE_PERMISSIONS = new FilePermission[0];
private final Path file;
private final OpenOption[] options;
@SuppressWarnings("removal")
private final AccessControlContext acc;
private final FilePermission[] filePermissions;
private final boolean isDefaultFS;
private final CompletableFuture<Path> result = new MinimalFuture<>();
private final AtomicBoolean subscribed = new AtomicBoolean();
private volatile Flow.Subscription subscription;
private volatile FileChannel out;
private static final String pathForSecurityCheck(Path path) {
return path.toFile().getPath();
}
/**
* Factory for creating PathSubscriber.
*
* Permission checks are performed here before construction of the
* PathSubscriber. Permission checking and construction are deliberately
* and tightly co-located.
*/
public static PathSubscriber create(Path file,
List<OpenOption> options) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
FilePermission filePermission = null;
if (sm != null) {
try {
String fn = pathForSecurityCheck(file);
FilePermission writePermission = new FilePermission(fn, "write");
sm.checkPermission(writePermission);
filePermission = writePermission;
} catch (UnsupportedOperationException ignored) {
// path not associated with the default file system provider
}
}
assert filePermission == null || filePermission.getActions().equals("write");
@SuppressWarnings("removal")
AccessControlContext acc = sm != null ? AccessController.getContext() : null;
return new PathSubscriber(file, options, acc, filePermission);
return new PathSubscriber(file, options);
}
// pp so handler implementations in the same package can construct
/*package-private*/ PathSubscriber(Path file,
List<OpenOption> options,
@SuppressWarnings("removal") AccessControlContext acc,
FilePermission... filePermissions) {
List<OpenOption> options) {
this.file = file;
this.options = options.stream().toArray(OpenOption[]::new);
this.acc = acc;
this.filePermissions = filePermissions == null || filePermissions[0] == null
? EMPTY_FILE_PERMISSIONS : filePermissions;
this.isDefaultFS = isDefaultFS(file);
}
private static boolean isDefaultFS(Path file) {
try {
file.toFile();
return true;
} catch (UnsupportedOperationException uoe) {
return false;
}
}
@SuppressWarnings("removal")
@Override
public void onSubscribe(Flow.Subscription subscription) {
Objects.requireNonNull(subscription);
@ -250,31 +192,12 @@ public class ResponseSubscribers {
}
this.subscription = subscription;
if (acc == null) {
try {
out = FileChannel.open(file, options);
} catch (IOException ioe) {
result.completeExceptionally(ioe);
subscription.cancel();
return;
}
} else {
try {
PrivilegedExceptionAction<FileChannel> pa =
() -> FileChannel.open(file, options);
out = isDefaultFS
? AccessController.doPrivileged(pa, acc, filePermissions)
: AccessController.doPrivileged(pa, acc);
} catch (PrivilegedActionException pae) {
Throwable t = pae.getCause() != null ? pae.getCause() : pae;
result.completeExceptionally(t);
subscription.cancel();
return;
} catch (Exception e) {
result.completeExceptionally(e);
subscription.cancel();
return;
}
try {
out = FileChannel.open(file, options);
} catch (IOException ioe) {
result.completeExceptionally(ioe);
subscription.cancel();
return;
}
subscription.request(1);
}
@ -311,21 +234,8 @@ public class ResponseSubscribers {
return result;
}
@SuppressWarnings("removal")
private void close() {
if (acc == null) {
Utils.close(out);
} else {
PrivilegedAction<Void> pa = () -> {
Utils.close(out);
return null;
};
if (isDefaultFS) {
AccessController.doPrivileged(pa, acc, filePermissions);
} else {
AccessController.doPrivileged(pa, acc);
}
}
Utils.close(out);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, 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
@ -27,7 +27,6 @@ package jdk.internal.net.http.common;
import java.security.Principal;
import java.util.List;
import javax.net.ssl.SSLSession;
import javax.net.ssl.ExtendedSSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLPeerUnverifiedException;

View File

@ -39,8 +39,6 @@ import java.lang.System.Logger.Level;
import java.net.ConnectException;
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;
@ -51,8 +49,6 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.Collection;
@ -100,10 +96,7 @@ public final class Utils {
// public static final boolean TESTING;
// static {
// if (ASSERTIONSENABLED) {
// PrivilegedAction<String> action = () -> System.getProperty("test.src");
// TESTING = AccessController.doPrivileged(action) != null;
// } else TESTING = false;
// TESTING = ASSERTIONSENABLED ? System.getProperty("test.src") != null : false;
// }
public static final LoggerConfig DEBUG_CONFIG =
getLoggerConfig(DebugLogger.HTTP_NAME, LoggerConfig.OFF);
@ -120,9 +113,7 @@ public final class Utils {
hostnameVerificationDisabledValue();
private static LoggerConfig getLoggerConfig(String loggerName, LoggerConfig def) {
PrivilegedAction<String> action = () -> System.getProperty(loggerName);
@SuppressWarnings("removal")
var prop = AccessController.doPrivileged(action);
var prop = System.getProperty(loggerName);
if (prop == null) return def;
var config = LoggerConfig.OFF;
for (var s : prop.split(",")) {
@ -449,41 +440,6 @@ public final class Utils {
private Utils() { }
/**
* Returns the security permissions required to connect to the proxy, or
* {@code null} if none is required or applicable.
*/
public static URLPermission permissionForProxy(InetSocketAddress proxyAddress) {
if (proxyAddress == null)
return null;
StringBuilder sb = new StringBuilder();
sb.append("socket://")
.append(proxyAddress.getHostString()).append(":")
.append(proxyAddress.getPort());
String urlString = sb.toString();
return new URLPermission(urlString, "CONNECT");
}
/**
* Returns the security permission required for the given details.
*/
public static URLPermission permissionForServer(URI uri,
String method,
Stream<String> headers) {
String urlString = new StringBuilder()
.append(uri.getScheme()).append("://")
.append(uri.getRawAuthority())
.append(uri.getRawPath()).toString();
StringBuilder actionStringBuilder = new StringBuilder(method);
String collected = headers.collect(joining(","));
if (!collected.isEmpty()) {
actionStringBuilder.append(":").append(collected);
}
return new URLPermission(urlString, actionStringBuilder.toString());
}
private static final boolean[] LOWER_CASE_CHARS = new boolean[128];
// ABNF primitives defined in RFC 7230
@ -587,34 +543,24 @@ public final class Utils {
return true;
}
@SuppressWarnings("removal")
public static int getIntegerNetProperty(String name, int defaultValue) {
return AccessController.doPrivileged((PrivilegedAction<Integer>) () ->
NetProperties.getInteger(name, defaultValue));
return NetProperties.getInteger(name, defaultValue);
}
@SuppressWarnings("removal")
public static String getNetProperty(String name) {
return AccessController.doPrivileged((PrivilegedAction<String>) () ->
NetProperties.get(name));
return NetProperties.get(name);
}
@SuppressWarnings("removal")
public static boolean getBooleanProperty(String name, boolean def) {
return AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
Boolean.parseBoolean(System.getProperty(name, String.valueOf(def))));
return Boolean.parseBoolean(System.getProperty(name, String.valueOf(def)));
}
@SuppressWarnings("removal")
public static String getProperty(String name) {
return AccessController.doPrivileged((PrivilegedAction<String>) () ->
System.getProperty(name));
return System.getProperty(name);
}
@SuppressWarnings("removal")
public static int getIntegerProperty(String name, int defaultValue) {
return AccessController.doPrivileged((PrivilegedAction<Integer>) () ->
Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue))));
return Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue)));
}
public static int getIntegerNetProperty(String property, int min, int max, int defaultValue, boolean log) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024, 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
@ -28,8 +28,6 @@ import jdk.internal.net.http.common.Utils;
import jdk.internal.net.http.hpack.HPACK.Logger.Level;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.function.Supplier;
@ -52,9 +50,7 @@ public final class HPACK {
static {
String PROPERTY = "jdk.internal.httpclient.hpack.log.level";
@SuppressWarnings("removal")
String value = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty(PROPERTY));
String value = System.getProperty(PROPERTY);
if (value == null) {
LOGGER = new RootLogger(NONE);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, 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
@ -39,17 +39,13 @@ import jdk.internal.net.http.common.Pair;
import jdk.internal.net.http.common.Utils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLPermission;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.Base64;
@ -61,11 +57,9 @@ import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import static java.lang.String.format;
import static jdk.internal.net.http.common.Utils.isValidName;
import static jdk.internal.net.http.common.Utils.permissionForProxy;
import static jdk.internal.net.http.common.Utils.stringOf;
public class OpeningHandshake {
@ -112,7 +106,6 @@ public class OpeningHandshake {
public OpeningHandshake(BuilderImpl b) {
checkURI(b.getUri());
Proxy proxy = proxyFor(b.getProxySelector(), b.getUri());
checkPermissions(b, proxy);
this.client = b.getClient();
URI httpURI = createRequestURI(b.getUri());
HttpRequestBuilderImpl requestBuilder = new HttpRequestBuilderImpl(httpURI);
@ -185,12 +178,9 @@ public class OpeningHandshake {
}
}
@SuppressWarnings("removal")
public CompletableFuture<Result> send() {
PrivilegedAction<CompletableFuture<Result>> pa = () ->
client.sendAsync(this.request, BodyHandlers.ofString())
return client.sendAsync(this.request, BodyHandlers.ofString())
.thenCompose(this::resultFrom);
return AccessController.doPrivileged(pa);
}
/*
@ -376,27 +366,4 @@ public class OpeningHandshake {
return proxy;
}
/**
* Performs the necessary security permissions checks to connect ( possibly
* through a proxy ) to the builders WebSocket URI.
*
* @throws SecurityException if the security manager denies access
*/
static void checkPermissions(BuilderImpl b, Proxy proxy) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return;
}
Stream<String> headers = b.getHeaders().stream().map(p -> p.first).distinct();
URLPermission perm1 = Utils.permissionForServer(b.getUri(), "", headers);
sm.checkPermission(perm1);
if (proxy == null) {
return;
}
URLPermission perm2 = permissionForProxy((InetSocketAddress) proxy.address());
if (perm2 != null) {
sm.checkPermission(perm2);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2024, 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
@ -213,7 +213,7 @@ public class DebugLoggerTest {
public void flush() {
}
@Override
public void close() throws SecurityException {
public void close() {
}
}

View File

@ -40,7 +40,6 @@ import org.testng.annotations.Test;
import javax.net.ssl.SSLContext;
import java.io.FileNotFoundException;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -211,9 +210,6 @@ public class FilePublisherPermsTest implements HttpServerAdapters {
@BeforeTest
public void setup() throws Exception {
policyFile = System.getProperty("java.security.policy");
out.println(policyFile);
sslContext = new SimpleSSLContext().get();
if (sslContext == null)
throw new AssertionError("Unexpected null sslContext");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -92,13 +92,6 @@ public class SecureZipFSProvider extends FileSystemProvider {
public InputStream newInputStream(Path path, OpenOption... options)
throws IOException {
Path p = toTestPath(path).unwrap();
// Added permission checks before opening the file
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("customPermission"));
sm.checkRead(p.toString());
}
return defaultProvider.newInputStream(p, options);
}

View File

@ -27,10 +27,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.security.*;
import java.util.concurrent.CopyOnWriteArrayList;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.util.Arrays.asList;
@ -49,9 +47,9 @@ public class ProxyServer extends Thread implements Closeable {
// build it. Let's keep it simple.
static final boolean IS_WINDOWS;
static {
PrivilegedAction<String> action =
() -> System.getProperty("os.name", "unknown");
String osName = AccessController.doPrivileged(action);
// Parses os.name directly in order to avoid depending on test
// libraries in an auxiliary test class
String osName = System.getProperty("os.name", "unknown");
IS_WINDOWS = osName.toLowerCase(Locale.ROOT).startsWith("win");
}
@ -151,20 +149,6 @@ public class ProxyServer extends Thread implements Closeable {
volatile boolean done;
public void run() {
if (System.getSecurityManager() == null) {
execute();
} else {
// so calling domain does not need to have socket permission
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
execute();
return null;
}
});
}
}
public void execute() {
int id = 0;
try {
while (!done) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2024, 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
@ -23,8 +23,6 @@
package jdk.httpclient.test.lib.common;
import java.net.InetAddress;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import javax.net.ssl.SNIMatcher;
@ -59,14 +57,8 @@ public final class TestServerConfigurator extends HttpsConfigurator {
@Override
public void configure(final HttpsParameters params) {
final SSLParameters sslParams = getSSLContext().getDefaultSSLParameters();
@SuppressWarnings("removal") final SecurityManager sm = System.getSecurityManager();
final String hostname;
if (sm == null) {
hostname = serverAddr.getHostName();
} else {
final PrivilegedAction<String> action = () -> serverAddr.getHostName();
hostname = AccessController.doPrivileged(action);
}
final String hostname = serverAddr.getHostName();
final List<SNIMatcher> sniMatchers = List.of(new ServerNameMatcher(hostname));
sslParams.setSNIMatchers(sniMatchers);
// configure the server with these custom SSLParameters

View File

@ -29,7 +29,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
@ -43,11 +42,14 @@ public class FileProcessorPermissionTest {
static final Path fromFilePath = Paths.get(testSrc, "FileProcessorPermissionTest.java");
static final Path asFilePath = Paths.get(testSrc, "asFile.txt");
static final Path CWD = Paths.get(".");
static final Class<SecurityException> SE = SecurityException.class;
interface ExceptionAction<T> {
T run() throws Exception;
}
@Test
public void test() throws Exception {
List<PrivilegedExceptionAction<?>> list = List.of(
List<ExceptionAction<?>> list = List.of(
() -> HttpRequest.BodyPublishers.ofFile(fromFilePath),
() -> BodyHandlers.ofFile(asFilePath),
@ -59,7 +61,7 @@ public class FileProcessorPermissionTest {
() -> BodyHandlers.ofFileDownload(CWD, CREATE, WRITE)
);
for (PrivilegedExceptionAction pa : list) {
for (ExceptionAction<?> pa : list) {
pa.run();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, 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
@ -38,7 +38,6 @@ import java.net.URL;
import java.net.http.HttpHeaders;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.security.AccessController;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
@ -196,8 +195,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq = new HttpRequestImpl(reqBuilder);
HttpRequestImpl req = new HttpRequestImpl(origReq, ps);
MultiExchange<?> multi = new MultiExchange<Void>(origReq, req, client,
BodyHandlers.replacing(null),
null, AccessController.getContext());
BodyHandlers.replacing(null), null);
Exchange<?> exchange = new Exchange<>(req, multi);
out.println("\nSimulating unauthenticated request to " + uri);
filter.request(req, multi);
@ -266,8 +264,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq2 = new HttpRequestImpl(reqBuilder2);
HttpRequestImpl req2 = new HttpRequestImpl(origReq2, ps);
MultiExchange<?> multi2 = new MultiExchange<Void>(origReq2, req2, client,
HttpResponse.BodyHandlers.replacing(null),
null, AccessController.getContext());
HttpResponse.BodyHandlers.replacing(null), null);
filter.request(req2, multi2);
out.println("Check that filter has added credentials from cache for " + reqURI2
+ " with proxy " + req2.proxy());
@ -298,8 +295,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq3 = new HttpRequestImpl(reqBuilder3);
HttpRequestImpl req3 = new HttpRequestImpl(origReq3, ps);
MultiExchange<?> multi3 = new MultiExchange<Void>(origReq3, req3, client,
HttpResponse.BodyHandlers.replacing(null),
null, AccessController.getContext());
HttpResponse.BodyHandlers.replacing(null), null);
filter.request(req3, multi3);
HttpHeaders h3 = req3.getSystemHeadersBuilder().build();
if (proxy == null) {
@ -342,8 +338,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq4 = new HttpRequestImpl(reqBuilder4);
HttpRequestImpl req4 = new HttpRequestImpl(origReq4, fakeProxy);
MultiExchange<?> multi4 = new MultiExchange<Void>(origReq4, req4, client,
HttpResponse.BodyHandlers.replacing(null), null,
AccessController.getContext());
HttpResponse.BodyHandlers.replacing(null), null);
out.println("Simulating new request to " + reqURI4 + " with a proxy " + req4.proxy());
assertTrue((req4.proxy() == null) == (proxy != null),
"(req4.proxy() == null) == (proxy != null) should be true");
@ -383,8 +378,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq5 = new HttpRequestImpl(reqBuilder5);
HttpRequestImpl req5 = new HttpRequestImpl(origReq5, NO_PROXY);
MultiExchange<?> multi5 = new MultiExchange<Void>(origReq5, req5, client,
HttpResponse.BodyHandlers.replacing(null), null,
AccessController.getContext());
HttpResponse.BodyHandlers.replacing(null), null);
out.println("Simulating new request to " + reqURI + " with a proxy " + req5.proxy());
assertTrue(req5.proxy() == null, "req5.proxy() should be null");
Exchange<?> exchange5 = new Exchange<>(req5, multi5);
@ -437,8 +431,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq6 = new HttpRequestImpl(reqBuilder6);
HttpRequestImpl req6 = new HttpRequestImpl(origReq6, ps);
MultiExchange<?> multi6 = new MultiExchange<Void>(origReq6, req6, client,
HttpResponse.BodyHandlers.replacing(null), null,
AccessController.getContext());
HttpResponse.BodyHandlers.replacing(null), null);
out.println("Simulating new request to " + reqURI + " with a proxy " + req6.proxy());
assertTrue(req6.proxy() != null, "req6.proxy() should not be null");
Exchange<?> exchange6 = new Exchange<>(req6, multi6);
@ -461,8 +454,7 @@ public class AuthenticationFilterTest {
HttpRequestImpl origReq7 = new HttpRequestImpl(reqBuilder7);
HttpRequestImpl req7 = new HttpRequestImpl(origReq7, ps);
MultiExchange<?> multi7 = new MultiExchange<Void>(origReq7, req7, client,
HttpResponse.BodyHandlers.replacing(null), null,
AccessController.getContext());
HttpResponse.BodyHandlers.replacing(null), null);
out.println("Simulating new request to " + reqURI7 + " with a proxy " + req7.proxy());
assertTrue(req7.proxy() == null, "req7.proxy() should be null");
Exchange<?> exchange7 = new Exchange<>(req7, multi7);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024, 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
@ -221,7 +221,6 @@ public class RawChannelTest {
requestImpl,
clientImpl,
discarding(),
null,
null);
HttpResponse<?> r = mex.responseAsync(clientImpl.theExecutor())
.get(30, SECONDS);

View File

@ -42,13 +42,6 @@ import javax.net.ssl.TrustManagerFactory;
/**
* Creates a simple usable SSLContext for SSLSocketFactory
* or a HttpsServer using a default keystore in the test tree.
* <p>
* Using this class with a security manager requires the following
* permissions to be granted:
* <p>
* permission "java.util.PropertyPermission" "test.src.path", "read";
* permission java.io.FilePermission "/path/to/test/lib/jdk/test/lib/testkeys", "read";
* The exact path above depends on the location of the test.
*/
public class SimpleSSLContext {
@ -60,27 +53,17 @@ public class SimpleSSLContext {
public SimpleSSLContext() throws IOException {
String paths = System.getProperty("test.src.path");
StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
boolean securityExceptions = false;
SSLContext sslContext = null;
while (st.hasMoreTokens()) {
String path = st.nextToken();
try {
File f = new File(path, "../../../../../lib/jdk/test/lib/net/testkeys");
if (f.exists()) {
try (FileInputStream fis = new FileInputStream(f)) {
sslContext = init(fis);
break;
}
File f = new File(path, "../../../../../lib/jdk/test/lib/net/testkeys");
if (f.exists()) {
try (FileInputStream fis = new FileInputStream(f)) {
sslContext = init(fis);
break;
}
} catch (SecurityException e) {
// catch and ignore because permission only required
// for one entry on path (at most)
securityExceptions = true;
}
}
if (securityExceptions) {
System.out.println("SecurityExceptions thrown on loading testkeys");
}
ssl = sslContext;
}