<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p>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.</p>
<p>Requirements:</p>
<ol>
<li>Ability to perform authorization checks on code and Subjects.</li>
<li>Compatible with all current Java LTS releases.<br>
</li>
<li>Will need to use Java API's, nothing platform specific.</li>
</ol>
<p>Uncertainty:</p>
<ol>
<li>New JAAS API's are unknown, will they be suitable for our
application? Unclear.<br>
</li>
<li>Will hooks be provided in OpenJDK for Guard checks or not?
Unclear.<br>
</li>
<li>Lack of support for new features with existing API's going
forward, eg virtual threads.</li>
<li>How to authenticate TLS and Kerberos connections without using
deprecated API's?</li>
<li>Avoid other API's that may be removed in future due to
under-utilisation.</li>
</ol>
<p>What can we do with what we know, how can we do it better?</p>
<ol>
<li>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).<br>
</li>
<li>Methods:</li>
<ul>
<li>Callable<T> privilegedCall(Callable<T> c) //
Always preserves subject, if any.<br>
</li>
<li>Callable<T> privilegedCall(Subject s,
Callable<T> c) // Uses provided Subject, from
LoginModule, or executing in the context of an authenticated
client over a secure connection.<br>
</li>
<li>Callable<T> privilegedCall(AuthorizationContext ac,
Callable<T> c) // Uses provided AuthorizationContext
combined with context of the privileged caller.<br>
</li>
<li>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.<br>
</li>
</ul>
</ol>
<ol start="3">
<li>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.<br>
</li>
<li>Callable returned can be submitted to an Executor, eg as a
privileged task.<br>
</li>
<li>Create a new AuthorizationContext class</li>
</ol>
<ol start="4">
<ul>
<li>Encapsulates the current Subject</li>
<li>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.<br>
</li>
<li>Methods:</li>
<ul>
<li>Subject getSubject() // For secure connections.<br>
</li>
<li>void checkEach(Consumer<ProtectionDomain> 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.<br>
</li>
</ul>
</ul>
</ol>
<ol start="4">
<li>Use Agents to instrument Java Public API until hooks are
provided by OpenJDK.<br>
<ul>
<li>*ONLY* target LTS releases to minimize API analysis
required.</li>
<li>Use static analysis to located methods in Java API's that
return sensitive classes, eg ClassLoader, ProtectionDomain.</li>
<li>We could use some hooks here OpenJDK?<br>
</li>
</ul>
</li>
<li>How to capture domains and privileged scope?</li>
</ol>
<blockquote>
<ul>
<li>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.<br>
</li>
<li>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.<br>
</li>
<li>If an inherited ThreadLocal AuthorizationContext isn't
present, when checked, then an unprivileged domain will
represent the entire stack, with the following exception:</li>
<ul>
<li>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.<br>
</li>
<li>For bootloader system code construct ProtectionDomain,
with CodeSource URL[] containing a jrt:/$MODULE , if Module
isNamed(), retrieved from Class.getModule(). <br>
</li>
<li>Authorization agents will be considered platform code.</li>
</ul>
<li>At the time Authorization.getContext() is called, the
inherited context will be combined with the stack walk from
the current thread and the all domains will include the
Subject's principals.</li>
</ul>
</blockquote>
<ol start="6">
<li>GuardFactory and GuardFactorySpi, Authorization agents will
need to call GuardFactory, to obtain Guard instances, the Guard
implementation can call:</li>
<ul>
<li>Authorization::getContext()checkEach(domain ->
doSomething(domain));<br>
</li>
</ul>
</ol>
<pre class="moz-signature" cols="72">--
Regards,
Peter Firmstone</pre>
<div class="moz-cite-prefix">On 26/06/2021 10:05 pm, Peter Firmstone
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:fc593c92-d4cc-0cb7-c85b-fcc7cb5520c6@zeus.net.au">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<p><br>
</p>
<div class="moz-cite-prefix">On 26/06/2021 3:41 pm, Peter
Firmstone wrote:<br>
</div>
<blockquote type="cite"
cite="mid:e0436c20-41b6-811a-8026-59da7c5e3a7e@zeus.net.au">
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8">
<p>Apologies for multiple earlier emails, please ignore and read
this instead.</p>
<p>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.<br>
</p>
<p>Summary of Proposed Changes:</p>
<ol>
<li>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.<br>
</li>
<li>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.<br>
</li>
<li>JDK code to no longer call Permission implementations
directly, instances obtained using GuardFactory, when
enabled in the java.security configuration file.<br>
</li>
<li>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.</li>
<li>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.<br>
</li>
<li>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. <br>
</li>
<li>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.<br>
</li>
<li>Deprecate for removal the CodeSource::implies method.</li>
<li>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.</li>
<li>Deprecate for removal AccessController::checkPermission
and AccessControlContext::checkPermission methods.</li>
</ol>
</blockquote>
<p>AccessController.checkPermission calls
AccessControlContext.optimize, which invokes the DomainCombiner,
prior to calling AccessControlContext.checkPermission <br>
</p>
<p>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.<br>
</p>
<p>I think it would be simpler however, to create a new method in
AccessController to replace checkPermission which also calls
optimize.</p>
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.<br>
<p>Eg:</p>
<p>AccessController.stream(AccessControlContext
context).forEach(domain -> Check::domain)</p>
<p>Or <br>
</p>
<p>AccessController.checkDomains(AccessControlContext context,
Consumer<ProtectionDomain>)</p>
<p>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.<br>
</p>
<blockquote type="cite"
cite="mid:e0436c20-41b6-811a-8026-59da7c5e3a7e@zeus.net.au">
<ol start="11">
<li>Undeprecate AccessController, AccessControlContext,
DomainCombiner, SubjectDomainCombiner and Subject::doAs
methods, while deprecating for removal methods stated in
items above.</li>
<li>Deprecate for removal ProtectionDomain::implies,
ProtectionDomain::getPermissions and
ProtectionDomain::staticPermissionsOnly</li>
<li>Replace PermissionCollection type argument with Object in
ProtectionDomain constructors, ignore the permissions
parameter, and deprecate existing constructors. Deprecate
PermissionCollection for removal.<br>
</li>
<li>Create a new constructor: ProtectionDomain(CodeSource cs,
ClassLoader cl, Principal[] p).</li>
<li>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.<br>
</li>
</ol>
</blockquote>
<p>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:</p>
<ul>
<li>Cache can be utilised by other implementations.</li>
<li>Simplification of SubjectDomainCombiner.</li>
<li>Responsibility of ProtectionDomain.</li>
</ul>
<p>It's not an essential item, however, just seems like an
opportunity for a little refactoring.<br>
</p>
<blockquote type="cite"
cite="mid:e0436c20-41b6-811a-8026-59da7c5e3a7e@zeus.net.au">
<ol start="11">
</ol>
<p>To clarify what an *unprivileged* AccessControlContext is:</p>
<blockquote>
<p>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.<br>
</p>
<p>So as to distinguish between what is traditionally a JDK
bootstrap ProtectionDomain and unprivileged domain after
ProtectionDomain::getPermissions is removed.<br>
</p>
</blockquote>
<blockquote> </blockquote>
<p>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.<br>
</p>
<p>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.</p>
<p>Regards,</p>
<p>Peter Firmstone.<br>
</p>
<div class="moz-cite-prefix">On 25/06/2021 3:59 pm, Peter
Firmstone wrote:<br>
</div>
<blockquote type="cite"
cite="mid:2f315680-1cdb-1694-34a3-95312bf42ca7@zeus.net.au">Thanks
Dalibor, <br>
<br>
Would targeting Java 18 be practical? <br>
<br>
Also it won't take long to code a prototype, just not sure of
the process. <br>
<br>
Cheers, <br>
<br>
Peter. <br>
<br>
<br>
On 24/06/2021 9:30 pm, Dalibor Topic wrote: <br>
<blockquote type="cite">On 24.06.2021 04:24, Peter Firmstone
wrote: <br>
<blockquote type="cite">Thanks Andrew, <br>
<br>
For the simple case, of replacing the SecurityManager
stack walk, one could use reflection. <br>
<br>
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. <br>
</blockquote>
<br>
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. <br>
<br>
cheers, <br>
dalibor topic <br>
<br>
</blockquote>
</blockquote>
</blockquote>
<pre class="moz-signature" cols="72">--
Regards,
Peter Firmstone
</pre>
</blockquote>
<pre class="moz-signature" cols="72">
</pre>
</body>
</html>