Authorization layer API and low level access checks.
Peter Firmstone
peter.firmstone at zeus.net.au
Fri Jun 25 08:01:41 UTC 2021
The more I think about it, allowing Thread to use a singleton immutable
unprivileged AccessControlContext instead of the inherited context is
the right thing to do, it achieves the original goal of avoiding
privilege escalation, limits the the size of the context that needs to
be checked and allows simple support for virtual threads. The
AccessController.doPrivileged method allows code to make privileged actions.
The way to implement it, for compatible transition would be:
1. Implement it first in virtual threads.
2. When stubbing out SecurityManager, change system threads to also use
the singleton unprivileged context, instead of the inherited
context, which must be calculated for each thread at creation time.
3. Alternative option to item 2, is to make generic grants in policy
files for affected threads (which inherited privileged context).
I recently generated some principle of least privilege policy files for
tests:
https://github.com/pfirmstone/JGDMS/blob/trunk/qa/harness/policy/defaultsecuresharedvm.policy.new
https://github.com/pfirmstone/JGDMS/blob/trunk/qa/harness/policy/defaultsecuretest.policy.new
https://github.com/pfirmstone/JGDMS/blob/trunk/qa/harness/policy/defaultsecurephoenix.policy.new
For the generation of these policy files, a properties file was
provided, to populate the policy files with properties, to replace local
and platform specific paths and file names.
Note how many permissions are granted to code and principal's. This
ensures that other code cannot use the principal's thread for privileged
escalation, and code cannot perform certain tasks without a logged in
Subject.
This is how we prevent deserialization (not Java deserialization) of
untrusted data in our case, we have DeserializationPermission. So not
only do we ensure there's a logged in Subject, that's providing the
data, but we are also restricting the code that is allowed to parse
it. Our deserialization uses constructors to validate invariants but,
we still avoid using it to process untrusted data.
One pain point is SocketPermission, which doesn't allow IP Address
subnet wild cards, hence the use of unlimited IP Address wild cards.
It's generally preferable not to use domain names in SocketPermission,
due to blocking DNS calls, personally I'd like to replace that with
RFC3986 normalization.
Note that the JGDMS SecurityManager and Policy implementations are
performant and scalable, all hotspots in JGDMS are JDK native methods
(Socket's basically). The use of virtual threads, would provide a
significant scalability improvement for JGDMS.
If we could get the proposed GuardBuilder & GuardBuilderSpi happening
(so developers are freed from the current Permission implementations) as
well as the proposed changes to thread AccessControlContext below, we
would have the best authorization layer available.
We can still stub out SecurityManager and remove the Policy and
Permission implementations, to reduce the maintenance burden for OpenJDK
developers.
The reality is, the overall result for us, will be much better, if we
can retain AccessController and AccessControlContext for the following
reasons:
1. Allowing grants to be made to code and principals, to prevent
parsing of untrusted data, while limiting the scope of those grants
(refer to 3).
2. Preserving current JAAS functionality, to authenticate and secure
connections.
3. Limiting or preventing viral authorization checks from spreading to
an excessive number of ProtectionDomains (viral Permissions). For
the libraries and JVM code that use doPrivileged will continue to
function using common API's.
4. To enable developers to implement an authorization layer. While this
may be a small proportion of overall projects, the projects that do
are usually significant.
We don't require SecurityManager, a Policy or Permission implementations
to implement an authorization layer and these components are the
majority of the maintenance burden for OpenJDK, as far as I can tell at
least.
Basic components required for effective authorization layer implementations:
1. Guard, GuardBuilder and GuardBuilderSpi (or equivalent).
2. AccessController, AccessControlContext and DomainCombiner (These are
difficult to re-implement in a Java version compatible manner, and
re-implementations would not have the benefits of JDK support for
AccessController.doPrivileged, or Thread context, which limits viral
authorization checks).
3. ProtectionDomain, CodeSource and Principal
4. JAAS, Subject and LoginModule.
5. GSS-API/Kerberos, JCA, JCE and JSSE.
--
Regards,
Peter Firmstone
On 24/06/2021 11:50 am, Peter Firmstone wrote:
> Clarification inline below.
>
> On 24/06/2021 11:03 am, Peter Firmstone wrote:
>> Hi Alan,
>>
>> It is important to understand the reason for the inherited
>> AccessControlContext, in order to consider alternatives.
>>
>> The motivation for inherited context, was simply to avoid privilege
>> escalation, prior to Executors.
>>
>> Whenever a permission check is made, the DomainCombiner, combines the
>> inherited context, with the thread's current context, in case there
>> are any less privileged domains in the inherited context.
>>
>> But there is an alternative, higher performance option, that avoids
>> privilege escalation for executors as well.
>>
>> A ProtectionDomain with a null CodeSource has AllPermission, while a
>> ProtectionDomain that contains a CodeSource with a null URL has only
>> the Permission's given to it when created, or to blanket grant
>> statements in policy files.
>>
>> Rather than inherit context from the calling thread, all threads upon
>> creation could be initialized with one shared immutable unprivileged
>> AccessControlContext, containing a single element array, with a
>> ProtectionDomain, containing a CodeSource with a null URL.
>>
>> Code cannot assume that calling code is privileged, hence the
>> AccessController.doPrivileged method, so an unprivileged context
>> could replace system threads inherited context as well. There will
>> be some minor impacts in older code where developers create a system
>> thread for cleanup tasks or other things, but nothing that couldn't
>> be worked around, until it can be addressed properly. This is an
>> existential moment for Java authorization, as a developer with
>> extensive use of Java authorization, I would most certainly welcome
>> this change.
>>
>> This would be a simplification that enhances security. This is far
>> more preferable than an inherited AccessControlContext as it
>> eliminates any risk that Executor tasks present, where domains in the
>> context that creates Callable or Runnable, may not be in the
>> inherited thread context. JEP 411, presents an opportunity to
>> address it.
>>
>> A use case:
>>
>> I would like to use virtual threads, in executors, to make blocking
>> secure network connections, so I don't consume too many system
>> threads. When network failures occur, the number of threads created
>> increase significantly, as blocked threads waiting on network are no
>> longer available to the executor.
>>
>> All our executor tasks are wrapped, with AccessControlContext, using
>> Executors::callable(PrivilegedAction), we do this to capture the
>> Subject, and to grant SocketPermission (to Principles and CodeSource)
>> to make secure network calls from one node to another. Across the
>> network, the user Subject's Principals are preserved, from the thread
>> in the client to the thread in the server during authentication.
>> DeserializationPermission is granted to the user Principal's and
>> CodeSource in the server, so that the code cannot perform
>> deserialization (not to be confused with Java serialization) without
>> an authenticated user. The authenticated user represents the domain
>> from which data to be deserialized originates.
>>
>> Personally I would like to see AccessController and
>> AccessControlContext retained, and all threads modified to be
>> initialized with a single shared immutable unprivileged
>> AccessControlContext, rather than an inherited AccessControlContext
>> in system threads and virtual threads that do not support any
>> permissions at all.
> All threads except bootstrap threads in the JVM, obviously they would
> need to be privileged.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/security-dev/attachments/20210625/39d6835a/attachment.htm>
More information about the security-dev
mailing list