Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ static boolean fileExists(final File file, Permission permission) {
if (sm == null) {
return file.exists();
}
sm.checkPermission(permission);
// Move the permission check inside doPrivileged so the stack walk stops
// at the doPrivileged boundary. Checking outside doPrivileged walks all
// the way up to user-deployment code, which must not be required to hold
// a CXF-internal permission. Inside doPrivileged only CXF's own privilege
// context is checked, preserving the confused-deputy guard.
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
sm.checkPermission(permission);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ffang I do understand the motivation for this change but I believe it significantly changes the (intended?) security posture and behavior. Essentially, any caller will be granted the permission assuming cxf-core has it, why do we have to change it now? Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@reta You're absolutely right — thank you for catching this. Moving sm.checkPermission() inside doPrivileged does weaken the caller-check semantics and is not a good fix.

The root cause of Issues 2 & 3 is that PR #3157's ACCESS_EXTERNAL_SCHEMA="" change routes schema resolution through SchemaLSResourceResolver → ExtendedURIResolver → URIResolver.tryFileSystem() — a code path that was never reached in this context before. When the JAXP schema validator (JDK code) drives that resolution under a SecurityManager, sm.checkPermission(RESOLVE_URI) walks all the way up through the JDK validator frames, which don't hold a CXF-internal permission, causing the failure.

The correct fix is to leave SecurityActions.fileExists() untouched — worth noting that SecurityActions is a package-private class so only CXF's own code in org.apache.cxf.resource can call into it directly(So any caller in your concern is restricted in CXF code, though we still should avoid it). Instead we wrap the resolver.resolve() call in SchemaLSResourceResolver.resolveResource() with AccessController.doPrivileged(). That establishes the boundary at CXF's own code: the sm.checkPermission(RESOLVE_URI) stack walk stops there, finds that CXF's JARs hold the permission, and passes — without granting any privilege to the JAXP/user frames above.

Will push a revised commit shortly.

return file.exists();
}
});
Expand Down
13 changes: 12 additions & 1 deletion core/src/main/java/org/apache/cxf/resource/URIResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.nio.file.Files;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -271,7 +273,16 @@ private static boolean followRedirect(int status) {

private HttpURLConnection createInputStream() throws IOException {
checkAllowedScheme(url);
HttpURLConnection huc = (HttpURLConnection)url.openConnection();
// Wrap the network connection in doPrivileged so that callers (including
// user deployments) do not need SocketPermission for the target host.
final HttpURLConnection huc;
try {
huc = AccessController.doPrivileged(
(PrivilegedExceptionAction<HttpURLConnection>) () ->
(HttpURLConnection)url.openConnection());
} catch (PrivilegedActionException e) {
throw (IOException) e.getException();
}

String host = SystemPropertyAction.getPropertyOrNull("http.proxyHost");
if (host != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -50,7 +52,8 @@ public Proxy createProxy(HTTPClientPolicy policy, URI currentUrl) {
* are honoured rather than bypassed.
*/
private Proxy getSystemProxy(URI uri) {
ProxySelector selector = ProxySelector.getDefault();
ProxySelector selector = AccessController.doPrivileged(
(PrivilegedAction<ProxySelector>) ProxySelector::getDefault);
if (selector == null) {
return null;
}
Expand Down