Re: READ 1ST: Re: Authorization layer API and low level access checks

2021-06-27 Thread Peter Firmstone
Since I need to implement an authorization layer, and move past the 
current uncertainty surrounding authorization and authentication in 
Java, I think I'll start small and completely independent and learn from 
history.


Requirements:

1. Ability to perform authorization checks on code and Subjects.
2. Compatible with all current Java LTS releases.
3. Will need to use Java API's, nothing platform specific.

Uncertainty:

1. New JAAS API's are unknown, will they be suitable for our
   application? Unclear.
2. Will hooks be provided in OpenJDK for Guard checks or not? Unclear.
3. Lack of support for new features with existing API's going forward,
   eg virtual threads.
4. How to authenticate TLS and Kerberos connections without using
   deprecated API's?
5. Avoid other API's that may be removed in future due to
   under-utilisation.

What can we do with what we know, how can we do it better?

1. Create a new Authorization.class with methods that return Callable
   objects that can be submitted to Executors, including virtual
   threads (assuming virtual threads support StackWalker).
2. Methods:
 * Callable privilegedCall(Callable c) // Always preserves
   subject, if any.
 * Callable privilegedCall(Subject s, Callable c) // Uses
   provided Subject, from LoginModule, or executing in the context
   of an authenticated client over a secure connection.
 * Callable privilegedCall(AuthorizationContext ac, Callable
   c) // Uses provided AuthorizationContext combined with context
   of the privileged caller.
 * AuthorizationContext  getContext(); // Returns an optimized
   context (combines the inherited and calculated contexts, injects
   Principal[]'s from the Subject into each domain. Stored for
   later use when making privilegedCall's eg for TLS connection.

3. The Authorization.class implementation will enure that the inherited
   context is stored in a ThreadLocal variable which is restored to
   it's original value using a try finally block to ensure the
   inherited context is only present during the wrapped Callable's
   call.   It's possible that privilegedCall's are nested in one
   thread, in which case the ThreadLocal value will be changed after
   each privilegedCall to the outer calls context, until it is set to
   null, by the outermost privilegedCall.
4. Callable returned can be submitted to an Executor, eg as a
   privileged task.
5. Create a new AuthorizationContext class

 * Encapsulates the current Subject
 * Contains a snapshot of the ProtectionDomains when the Callable
   was passed to privilegedCall and includes the domain of the
   class that called the Authorization.privileged method as well as
   any domains of Callable implementation parameter classes, this
   is the inherited context.
 * Methods:
 o Subject getSubject() // For secure connections.
 o void checkEach(Consumer consumer) throws
   AuthorizationException // Consumer::andThen allows for
   debugging information to be printed to error, such as print
   the privileges of a domain, or to record the required
   privileges of each domain, without throwing a
   SecurityException (when recording allowed operations).
   AuthorizationContext implementation determines how to
   execute.  No mutable shared state.

4. Use Agents to instrument Java Public API until hooks are provided by
   OpenJDK.
 * *ONLY* target LTS releases to minimize API analysis required.
 * Use static analysis to located methods in Java API's that return
   sensitive classes, eg ClassLoader, ProtectionDomain.
 * We could use some hooks here OpenJDK?
5. How to capture domains and privileged scope?

 * It is not possible to inherit a call stack from a previous
   thread, so either the thread executes only platform code and is
   checked, assuming the platform code can be trusted to not allow
   sensitive object references to escape, or if application code is
   present, then it is unprivileged unless a privilegedCall is
   made.  It will be known by the presence of a ThreadLocal
   inherited context whether a privilegedCall is being executed.
 * The stack context is only captured after a privilegedCall is
   made on a Thread's call stack, with the exception of a call
   stack that contains *only* platform code.
 * If an inherited ThreadLocal AuthorizationContext isn't present,
   when checked, then an unprivileged domain will represent the
   entire stack, with the following exception:
 o Unless all code on the thread stack is platform code, in
   this case, capture all domains on the call stack since the
   thread started.   This is a code only check, as no Subject
   will be present.
 o For bootloader system code construct ProtectionDomain, with
   CodeSource URL[] containing a jrt:/$MODULE , if 

Re: READ 1ST: Re: Authorization layer API and low level access checks

2021-06-26 Thread Peter Firmstone


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)


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, 

READ 1ST: Re: Authorization layer API and low level access checks

2021-06-25 Thread Peter Firmstone

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.
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.

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