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