<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi Martin, <br>
    </p>
    <p>I'm curious, you sound like you arrived at this opinion from
      experience?  Rather than being an upper layer only concern, my
      opinion is that it requires lower layer intervention / controls,
      with upper layers providing the decision making context.<br>
    </p>
    <p>My reason for asking is, we're basically waiting for finalizers
      to be disabled, so that we can instrument the java api with access
      controls to replace SM.<br>
    </p>
    <p>In our implementation, we use the SM infrastructure like this:</p>
    <ol>
      <li>Distributed computing - protection domains and ClassLoaders
        are used for service identity and isolation at the client.
        (Simplification because a server can also be a client and vice
        versa).<br>
      </li>
      <li>All client subjects are authenticated over secure connections,
        threads on the server are run with the client subject, from the
        client jvm, for access control decisions, eg do I trust the data
        enough to parse it?   Callbacks for client listeners (services)
        are run with the server's subject at the client.<br>
      </li>
      <li>We re-implemented java de-serialization, with a public api
        that uses constructors.   Unlike perl taint mode, we rely on the
        authenticated subject's principals, to determine whether to
        allow permission to deserialize (parse data).  SM allows us to
        handle tainted data, because we put permission checks into our
        deserialization implementation, if there's no authenticated
        subject, or the remote end doesn't have the required principal,
        then deserialization doesn't proceed at all, because no one
        vouches for the data, it cannot be trusted.   Our
        deserialization implementation provides an atomic input
        validation api, to validate data (sanitize) from trusted
        sources, in theory it would allow us to parse untrusted data,
        but we use authentication to reduce our exposure.  Rather than a
        bolted on external kind of white listing filtering mechanism,
        it's a class level implementation concern.<br>
      </li>
      <li>Clients dynamically download requested proxy jar files,
        (streams are not annotated like RMI), prior to download, the
        client authenticates the service's server, after authentication,
        the client loads the jar files, and deserializes the proxy
        object state into a designated ClassLoader (unique to the
        service identity, services that share jar file URI will not
        share ClassLoader's and don't resolve to the same class type).
        After authentication, the service provides URI and advisory
        permissions and the client may dynamically grant the
        intersection of those permissions which it has permission to
        grant and those the service requests.</li>
      <li>Our services are discoverable over multicast IPv6 (globally
        and on local networks, usually the two are kept somewhat
        separate).<br>
      </li>
      <li>We have service constraints, these are upper layer controls
        that lower layers use to ensure connections use strongly
        encrypted TLS protocols for example, or that a connection can be
        authenticated with a particular principal.  If a service is
        obtained from another service, our lower layer communications
        ensure that the same constraints apply to the second service,
        the client may apply new constraints after receiving a service
        proxy.<br>
      </li>
    </ol>
    <p>JEP411's successor will remove or change the functionality of
      Java's access controls and will break all our TLS connections and
      our ability to have different levels of access control for
      different services.<br>
    </p>
    <p>We can of course just do some kind of no op on later versions of
      Java with missing api's via reflection, which will also disable
      encrypted connections, then we can allow services to communicate
      over trusted networks or VPN's, and allow deserialization and jar
      file downloads, all without any jvm layer security, but we lose
      our ability to dynamically discover services globally, they will
      need to be known in advance and the secure network connections
      established in advance.<br>
    </p>
    <p>We solved all problems with SM mentioned in JEP 411, with the
      exception of the maintenance cost for OpenJDK.  My understanding
      is it is company policy around security that makes it expensive to
      maintain.  We have a policy generation tool (based on principles
      of least privilege), our policy provider has a less than 1%
      performance impact.   We have a PermissionComparator that avoids
      equals calls on Permission's, we have a URI 3986 implementation,
      that also normalizes IPv6 addresses, uses bitshift operations for
      case conversions and is extremely fast, it's used by our
      ClassLoader and Policy implementations.<br>
    </p>
    <p>The only remaining irritations were the structures of the
      Permissions themselves, eg SocketPermission can't constrain
      communications to subnet IP address ranges.<br>
    </p>
    <p>What Li Gong provided was very well designed, Sun just never
      finished it, and pretty much let it rot on the vine, few people
      used it, because of the amount of work required to make it work
      properly, and the fact that security is a nice to have feature,
      but budget constraints and delivery deadlines, and now it's
      subject to defenestration.   Hand edited policy files?  Sorry,
      that's not a finished product.<br>
    </p>
    <p>The other mistake was the Java trusted computing base became too
      large, it needed to be restricted to core java language
      features.   There's too much trusted code in the JVM. 
      Deserialization and XML (data parsing), never required any
      permissions, so it couldn't be disabled by assigning the necessary
      permissions to the principal of the authenticating subject. 
      Serialization and XML shouldn't have been part of the trusted code
      base.  Even if Java deserialization was insecure, as it was for
      many years, if it required authentication of the data source prior
      to deserialization proceeding, well maybe history might have been
      different.  Also too many classes are Serializable.<br>
    </p>
    <p>So by removing SM, in effect we're just making the trusted
      codebase larger, now it will encompass all third party libraries
      and their dependencies, while also removing the only available
      mechanism to determine whether data from an external source can be
      trusted based on who it was provided by (authenticated).</p>
    <p>Of course there will be those of us who will re-implement an
      authorization layer, hopefully we'll learn from Java's mistakes,
      not repeat them, but make a bunch of new mistakes instead.<br>
    </p>
    <p>Regards,</p>
    <p>Peter.<br>
    </p>
    <p><br>
    </p>
    <div class="moz-cite-prefix">On 23/04/2022 12:58 pm, Martin Balao
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:ce5643f9-ab10-803b-e0ae-433cdf680d8e@redhat.com">
      <pre class="moz-quote-pre" wrap="">Hi,

On 4/8/22 11:13 AM, Sean Mullan wrote:
</pre>
      <blockquote type="cite">
        <pre class="moz-quote-pre" wrap="">In general, I think authorization is best done at a higher layer within
the application and not via low-level SM callouts. Authorize the subject
first and if not acceptable, prevent the operation or API from being
called in the first place. Once the operation is in motion, you have
already taken a greater risk that something might go wrong.
</pre>
      </blockquote>
      <pre class="moz-quote-pre" wrap="">
I completely agree with this vision, and also agree with the other
arguments that both Sean Mullan and Andrew Dinn mentioned before in this
thread. In my view, authorization decisions at higher layer generally
have better context, are more clear and less riskier. At a lower layer
there is more complexity and chances of both subtle combinations or
unseen paths that may lead to check bypasses. I lean towards not
splitting authorization responsibility through different layers, which
might create confusion or even a false sense of security in some cases.
To illustrate with a trivial example, if a subject is not supposed to
access to some information, it's the application that has enough context
to decide and block right away. Letting the attempt go though the call
stack down to the network or the file system might be the recipe for
missing a channel.

I won't enter the untrusted code case -which has been extensively
discussed already- but want to briefly mention something about the
"trusted code performing risky operations" case. My first point is that
vulnerabilities at the JVM level (i.e.: memory safety compromises) are
serious enough to potentially bypass a SecurityManager. My second point
is that the SecurityManager is really unable to deal with tainted data
flowing from low to high integrity domains, and sanitation must be
performed by the application or the library anyways because there are
infinite ways in which it can be harmful. Even when the data is obtained
at a low integrity domain, there will be a flow towards a high integrity
domain to perform a legitimate action (i.e.: SQL query, OS command
execution, etc). The OS and the DB engine, in this example, have the
knowledge, granularity and power to be the next level of enforcement
after data sanitation. Again, I wouldn't split this responsibility or
pass it to the JDK for the same reasons than before.

Best,
Martin.-

</pre>
    </blockquote>
    <pre class="moz-signature" cols="72">
</pre>
  </body>
</html>