On 26/06/2021 3:41 pm, Peter Firmstone wrote:
Apologies for multiple earlier emails, please ignore and read this
instead.
This proposal is about stripping out and simplifying as much of the
dilapidated and complex SecurityManager infrastructure as possible,
while retaining the ability for developers to implement a better high
scaling and performant Authorization layer, without prohibitively
preventing it.
Summary of Proposed Changes:
1. GuardFactory & GuardFactorySpi to provide hooks for authorization
checks without SecurityManager or Policy. (Note GuardFactory
should never return null and instead return a no-op Guard that
hotspot can optimize out.
2. Existing Permission implementations to be obtained using
GuardFactorySpi implementations, until their removal. Note that
when SecurityManager is stubbed out and Permission implementations
are deprecated for removal, these should no longer be provided by
default, but instead need to be enabled by entries in the
java.security config file, in preparation for their removal.
3. JDK code to no longer call Permission implementations directly,
instances obtained using GuardFactory, when enabled in the
java.security configuration file.
4. Threads (system and virtual) updated to use a singleton
*unprivileged* AccessControlContext, instead of inherited
AccessControlContext, this is more appropriate for Executors, the
original inherited context was designed before Executors were
introduced.
5. Deprecation for removal of all Permission implementations from the
JDK platform. The existing implementations of Permission
introduce unnecessary complexity; they lack sufficient flexibility
resulting in a proliferation of Permission grants required in
policy files and some make blocking network calls.
6. Introduce a system property to change AccessController's default
behaviour, disable the stack walk by default, but allow it to be
re-enabled with a system property, replace the stack walk array
result of ProtectionDomains with an *unprivileged*
AccessControlContext, the SubjectDomainCombiner can replace it
with a, AccessControlContext containing a single element array,
containing one ProtectionDomain with Principals.
7. AccessController::doPrivileged erases the DomainCombiner by
default, deprecate these methods for removal (make private),
retain doPrivilegedWithCombiner methods that preserve the
SubjectDomainCombiner. Developers should replace their
doPrivileged methods with doPrivilegedWithCombiner. Create a new
method AccessController::doUnprivileged, clear intent, to erase
the DomainCombiner, and use the *unprivileged*
AccessControlContext. Update
AccessController.AccHolder.innocuousAcc to refer to an
*unprivileged* context, as per the definition below.
8. Deprecate for removal the CodeSource::implies method.
9. Give unique ProtectionDomain's with a meaninful CodeSource to Java
modules mapped to the boot loader, rather than using a Shared
ProtectionDomain with a null CodeSource.
10. Deprecate for removal AccessController::checkPermission and
AccessControlContext::checkPermission methods.
AccessController.checkPermission calls AccessControlContext.optimize,
which invokes the DomainCombiner, prior to calling
AccessControlContext.checkPermission
In my implementation of SecurityManager, I call
AccessController.getContext from within a PrivilegedAction, to optimise
a newly created AccessControlContext, (AccessController.getContext also
calls AcessControlConext.optimize), prior to calling
AccessControlContext.checkPermission.
I think it would be simpler however, to create a new method in
AccessController to replace checkPermission which also calls optimize.
I think something could be done here with Stream and Consumer to perform
the function checking ProtectionDomain's. Needs a little more thought,
but basically we want to be able to check each ProtectionDomain, without
being restricted to Permission or Policy implementations.
Eg:
AccessController.stream(AccessControlContext context).forEach(domain ->
Check::domain)
Or
AccessController.checkDomains(AccessControlContext context,
Consumer<ProtectionDomain>)
This method would have a relevant Guard.check "RUNTIME"
"getProtectionDomain" prior to calling AccessControlContext.optimize and
the developer would need to make the call from a PrivilegedAction, and
remember pass the relevant guard check for it's own AccessControlContext.
11. Undeprecate AccessController, AccessControlContext,
DomainCombiner, SubjectDomainCombiner and Subject::doAs methods,
while deprecating for removal methods stated in items above.
12. Deprecate for removal ProtectionDomain::implies,
ProtectionDomain::getPermissions and
ProtectionDomain::staticPermissionsOnly
13. Replace PermissionCollection type argument with Object in
ProtectionDomain constructors, ignore the permissions parameter,
and deprecate existing constructors. Deprecate
PermissionCollection for removal.
14. Create a new constructor: ProtectionDomain(CodeSource cs,
ClassLoader cl, Principal[] p).
15. Create a new factory method
ProtectionDomain::newInstance(Principal[] p), to allow a weak
cache of ProtectionDomain instances for each Principal[], to be
utilised by SubjectDomainCombiner to avoid unnecessary duplication
of objects. This is an optimization for
AccessControlContext::equals and ::hashCode methods. Using a
cache of AccessControlContext, it is possible to avoid rechecking
authorization that has already been checked. For example, when
using an Executor with many tasks, all with the same
AccessControlContext, you only need to check once and return the
same result for subsequent checks. This is an optimization I
have used previously to great effect.
The ProtectionDomain::newInstance is just a nice to have,
SubjectDomainCombiner already caches PD's, just seems a better place to
cache for the following reasons:
* Cache can be utilised by other implementations.
* Simplification of SubjectDomainCombiner.
* Responsibility of ProtectionDomain.
It's not an essential item, however, just seems like an opportunity for
a little refactoring.
To clarify what an *unprivileged* AccessControlContext is:
An instance of AccessControlContext, that contains a single
element array, containing a ProtectionDomain, with a null
ClassLoader, null Principal[] and a *non-null* CodeSource,
containing a null URL.
So as to distinguish between what is traditionally a JDK bootstrap
ProtectionDomain and unprivileged domain after
ProtectionDomain::getPermissions is removed.
Stubbing of SecurityManager and Policy, for runtime backward
compatibility. Update ProtectionDomain::implies method, to *not*
consult with the Policy. Note it's possible to get access to the
ProtectionDomain array contained within AccessControlContext using a
DomainCombiner.
This is backward compatible with existing usages of JAAS and least
painful method of transition for all concerned as well as allowing
complete flexibility of implementation.
Regards,
Peter Firmstone.
On 25/06/2021 3:59 pm, Peter Firmstone wrote:
Thanks Dalibor,
Would targeting Java 18 be practical?
Also it won't take long to code a prototype, just not sure of the
process.
Cheers,
Peter.
On 24/06/2021 9:30 pm, Dalibor Topic wrote:
On 24.06.2021 04:24, Peter Firmstone wrote:
Thanks Andrew,
For the simple case, of replacing the SecurityManager stack walk,
one could use reflection.
Thank you for also confirming that is not possible (or at least
very unlikely) to add a GuardBuilder to Java 8, the proposal is for
JDK code to use a provider mechanism, to intercept permission
checks, so custom authentication layers can be implemented, this
could be accepted in future versions of Java, but not existing. As
it is said, there is no harm in asking.
Generally speaking, adding any public APIs to a platform release
after its specification has been published, is always going to be a
very tall order involving the JCP, among other things. It's not
really worth it, when other technical solutions, such as
multi-release JARs, already exist, that alleviate the necessity.
cheers,
dalibor topic
--
Regards,
Peter Firmstone