Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Significant complexity added in Java 8, by AccessController::doPrivileged methods with three parameter arguments. #50

Open
pfirmstone opened this issue Jan 25, 2025 · 4 comments

Comments

@pfirmstone
Copy link
Owner

pfirmstone commented Jan 25, 2025

Three argument AccessController::doPrivileged methods were added in Java 8, c0c2397 their implementation has added significant complexity to AccessControlContext.

@pfirmstone
Copy link
Owner Author

pfirmstone commented Jan 25, 2025

Criticisms:

  1. The permissions are included in the state of AccessControlContext, and influence the outcome of the permission check.
  2. Equals and hashCode methods ignore these additional permissions. Equals is called on permissions, this significantly impacts performance, if the permission limited feature is used.
  3. The three parameter methods appear to be designed to run with reduced privileges.
  4. This could be simply achieved previously by copying the existing AccessControlContext, adding a ProtectionDomain using the two-parameter constructor with a null CodeSource and static permissions parameter. This would have the same effect.
  5. It appears the knowledge of using ProtectionDomain's two parameter argument with a null codesource has been lost, with such a ProtectionDomain on the stack, policy cannot grant additional permissions, the permissions granted are restricted to the static permissions passed to the ProtectionDomain at construction and may be limited further by less privileged domains on the call stack.

Links:
https://docs.oracle.com/javase/8/docs/api/java/security/AccessController.html#doPrivileged-java.security.PrivilegedAction-java.security.AccessControlContext-java.security.Permission...-
https://docs.oracle.com/javase/8/docs/technotes/guides/security/doprivileged.html#asserting_a_subset_of_privileges

@pfirmstone
Copy link
Owner Author

Asserting a Subset of Privileges

As of JDK 8, a variant of AccessController.doPrivileged is available that enables code to assert a subset of its privileges, without preventing the full traversal of the stack to check for other permissions. This variant of the doPrivileged variant has three parameters, one of which you use to specify this subset of privileges. For example, the following excerpt asserts a privilege to retrieve system properties:

// Returns the value of the specified property. All code
// is allowed to read the app.version and app.vendor
// properties.

public String getProperty(final String prop) {
    return AccessController.doPrivileged(
        (PrivilegedAction<String>) () -> System.getProperty(prop),
        null,
        new java.util.PropertyPermission("app.version", "read"),
        new java.util.PropertyPermission("app.vendor", "read")
    );
}

The first parameter of this version of doPrivileged is of type java.security.PrivilegedAction. In this example, the first parameter is a lambda expression that implements the functional interface PrivilegedAction whose run method returns the value of the system property specified by the parameter prop.

The second parameter of this version of doPrivileged is of type AccessControlContext. Sometimes you need to perform an additional security check within a different context, such as a worker thread. You can obtain an AccessControlContext instance from a particular calling context with the method AccessControlContext.getContext. If you specify null for this parameter (as in this example), then the invocation of doPrivileged does not perform any additional security checks.

The third parameter of this version of doPrivileged is of type Permission..., which is a varargs parameter. This means that you can specify one or more Permission parameters or an array of Permission objects, as in Permission[]. In this example, the invocation of doPrivileged can retrieve the properties app.version and app.vendor.

You can use this three parameter variant of doPrivileged in a mode of least privilege or a mode of more privilege.

Least Privilege

The typical use case of the doPrivileged method is to enable the method that invokes it to perform one or more actions that require permission checks without requiring the callers of the current method to have all the necessary permissions. For example, the current method might need to open a file or make a network request for its own internal implementation purposes.

Before JDK 8, calls to doPrivileged methods had only two parameters. They worked by granting temporary privileges to the calling method and stopping the normal full traversal of the stack for access checking when it reached that class, rather than continuing up the call stack where it might reach a method whose defining class does not have the required permission. Typically, the class that is calling doPrivileged might have additional permissions that are not required in that code path and which might also be missing from some caller classes.

Normally, these extra permissions are not exercised at runtime. Not elevating them through use of doPrivileged helps to block exploitation of any incorrect code that could perform unintended actions. This is especially true when the PrivilegedAction is more complex than usual, or when it calls code outside the class or package boundary that might evolve independently over time.

The three-parameter variant of doPrivileged is generally safer to use because it avoids unnecessarily elevating permissions that are not intended to be required. However, it executes less efficiently so simple or performance-critical code paths might choose not to use it.

More Privilege

Sometimes when coding the current method, you want to temporarily extend the permission of the calling method to perform an action. For example, a framework I/O API might have a general purpose method for opening files of a particular data format. This API would take a normal file path parameter and use it to open an underlying FileInputStream using the calling code's permissions. However, this might also allow any caller to open the data files in a special directory that contains some standard demonstration samples.

The callers of this API could be directly granted a FilePermission for read access. However, it might not be convenient or possible for the security policy of the calling code to be updated. For example, the calling code could be a sandboxed applet.

One way to implement this is for the code to check the incoming path and determine if it refers to a file in the special directory. If it does, then it would call doPrivileged, enabling all permissions, then open the file inside the PrivilegedAction. If the file was not in the special directory, the code would open the file without using doPrivileged.

This technique requires the implementation to carefully handle the requested file path to determine if it refers to the special shared directory. The file path must be canonicalized before calling doPrivileged so that any relative path will be processed (and permission to read the user.dir system property will be checked) prior to determining if the path refers to a file in the special directory. It must also prevent malicious "../" path elements meant to escape out of the special directory.

A simpler and better implementation would use the variant of doPrivileged with the third parameter. It would pass a FilePermission with read access to the special directory as the third parameter. Then any manipulation of the file would be inside the PrivilegedAction. This implementation is simpler and much less prone to contain a security flaw.

@pfirmstone pfirmstone changed the title Significant complexity added in Java 8, by additional AccessController::doPrivileged methods. Significant complexity added in Java 8, by AccessController::doPrivileged methods with three parameter arguments. Jan 25, 2025
pfirmstone added a commit that referenced this issue Jan 29, 2025
…eged methods with three parameter arguments. #50

Refactor AccessController, AccessControlContext, ProtectionDomain, Subject and SubjectDomainCombiner
pfirmstone added a commit that referenced this issue Jan 29, 2025
…umber of issues.

Significant complexity added in Java 8, by AccessController::doPrivileged methods with three parameter arguments. #50
Test failure in TEST='test/hotspot/jtreg/:tier1_common following SecurityManager & AccessController support for privileges and access control with VirtualThread's #46 #49
SecurityManager & AccessController support for privileges and access control with VirtualThread's #46
@pfirmstone
Copy link
Owner Author

These additional methods added to AccessController are a result of Java not placing a domain on the stack to represent an external data source, if that data source is authenticated, then it might have privilege, or if the data is parsed and verified, then it may be "untainted". The implementors of Java did not do this, instead, they provided these methods so code could reduce privileges, prior to parsing untrusted data for example, this would not have been necessary, were there a domain on the stack representing untrusted data.

@pfirmstone
Copy link
Owner Author

Although these methods added in Java 1.8, were focused on code, there is an opportunity to make use of these methods, to allow permission to be granted to authenticated Principal's by removing the privileges of privileged domain code, this is especially true in the case of Java serialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant