[External] : Re: PrivilegedAction et al and JEP411

Peter Firmstone peter.firmstone at zeus.net.au
Fri Jun 23 07:16:54 UTC 2023


On 23/06/2023 11:06 am, Ron Pressler wrote:
>
>> On 22 Jun 2023, at 23:50, Peter Firmstone <peter.firmstone at zeus.net.au> wrote:
>>
>>
>> If you are able to share, I'd be interested to learn about challenges you had with SM, if we one day have the opportunity to reimplement it, the lessons might be valuable, so we can avoid the same mistakes.
> Much of the effort has to do with maintaining the doPrivileged calls in the right places, which needs to be done across the board, and testing all those locations with and without an installed SM.

Of course, the very thing I was requesting to be preserved is the problem.

It must be an incredible amount of work without tooling, I can't begin 
to imagine how much time is wasted hand editing policy files.

We have to update all our test policy files for every release of Java.

For example, lets say you needed to write a test that  confirmed a 
SecurityException is thrown, first we would run the tests to generate 
the policy files, this would add all required permissions to all domains 
on the stack for privileges to be granted, which causes the test to 
fail, then we remove the relevant permissions from the test domain code, 
and voila, the test passes.

>
> Let me just be clear that even though it’s the cost that made SM harmful and justified the effort involved in removing it, we believe that the stack-based approach is fundamentally flawed for an ecosystem where deep and wide dependency hierarchies are common and configurations must be tailored for a specific architecture and dependency set rather than in a black box fashion. We would advocate for simpler designs that have shown greater effectiveness.

Ah yes, black box = encapsulation.

So we have a very dynamic environment, Java's standard static policy 
provider is no good for our application, we would have 
SecurityExceptions all the time and you'd be forever editing policy 
files, so we have dynamically granted permissions on top of static 
permissions for policy.   We took some inspiration from OSGi, and 
annotated jar files with their required permissions, these are remote 
micro service proxy's which are dynamically downloaded, each with their 
own ProtectionDomain, which is related to service identity, based on the 
authenticating service and it's CodeSource, it also has its own 
namespace, separate from other services with identical CodeSource's.

Example of required permissions included in a jar file:

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/services/reggie/reggie-dl/src/main/resources/META-INF/PERMISSIONS.LIST

Users are given a list of Permission's they are allowed to grant (in 
static policy files), called GrantPermission's.  Users and services 
authenticate each other, and users can dynamically grant Permission's to 
Service proxy's following authentication, the permissions required are 
declared, so it's kinda obvious, the Service's back end server runs 
threads with the authenticated users Subject, so we're not just using 
POLP on one jvm, we are distributed across many jvm's on different OS's 
or architectures.   When the local service proxy is no longer in use, 
dynamically granted permissions become weakly reachable and are garbage 
collected, so they don't hang around any longer than needed.   This is 
equivalent to you manually editing your policy files multiple times daily.

There are also other permissions which are granted during the 
authentication process, eg to allow opening a socket for authentication, 
(typically using Certificates over TLS, but Kerberos is also possible), 
during the service lookup process, the service provides a URI list of 
resources, required for our RFC3986URIClassLoader, which is provisioned 
following service authentication, DownloadPermission (incorrectly named, 
should be DynamicClassLoadPermission) is dynamically granted, prior to 
any class files being dynamically class loaded.   Any CodeSource 
certificates are transmitted with the URI list during authentication, so 
they don't need to be known in advance, alternatively we can also use 
SHA256 or SHA512 hash's to verify downloaded jar files, these policies 
are generated dynamically on the fly and only remain in memory for the 
period they apply.

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-pref-class-loader/src/main/java/net/jini/loader/pref/PreferredClassProvider.java

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/net/jini/loader/ProxyCodebaseSpi.java

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-pref-class-loader/src/main/java/net/jini/loader/pref/PreferredProxyCodebaseProvider.java

In our implementation, Permissions aren't stored in PermissionCollection 
instances, PermissionCollection instances are created on demand and are 
thread confined for high scalability, to avoid synchronization between 
threads, if there are many domains on a call stack, permission checks 
are sent as executor tasks, and executed concurrently, the first domain 
on the stack to fail a permission check throws SecurityException, and is 
returned to the permission check call, waiting permission check tasks 
from the same stack walk are cancelled, they're only all executed when a 
permission check passes, then the result of the permission check for 
that context is weakly cached, so we don't have to repeatedly check 
it.   We also use Comparator's to arrange permissions in the most 
efficient order for checking, to improve performance, also since we 
generate our policy files, they contain equal matches for permission's, 
which often avoids slow implies checks that occur within 
PermissionCollection's.

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/net/jini/security/policy/DynamicPolicyProvider.java

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/org/apache/river/api/security/CombinerSecurityManager.java

Honestly, it doesn't matter if we have 100's or even 1000's of 
ProtectionDomain's on a stack during permission checks, we could have 
100's of services participate in a transaction, as I mentioned the 
checks go to executors and get processed concurrently and dynamic policy 
is held temporarily in memory only.   Security is generally less than 3% 
of our system load.

Horribly antiquated implementations included in OpenJDK are based on 
late 1990's design philosophy, they're not even deployable in our 
environment, damn!  It blocks on DNS network calls?   I mean you can't 
compare a DNS network call performance wise with bitshift operations in 
RFC3986 URI normalization, or dynamic policy, with manual editing of 
policy files, there is no comparison, maybe you could compare it to an 
old man on a bicycle racing a top fueller at the drags, poor old bugger 
is gonna be at the line covered in rubber when the top fueller launches.

Everything runs with only the permissions required and no more under 
least privilege principles.

Anyway, it's been nice to think that what we achieved, was thought 
impossible.   It's also worth mentioning we know of users who are using 
this with OSGi, and have many more domains than typically used, due to 
OSGi's fine grained package level modularity.

Maybe it's our fault, for not widely promoting it, we just never 
expected JEP411, it came out of left field, we expected a core feature 
of Java would always be there and we were wrong.

When someone comes up with a simpler design, I'm all up for the 
effectiveness challenge, I'm pretty sure that whatever it is, we'll blow 
it away both on performance and effectiveness, we've had years to 
perfect it, but I would also happily be proven wrong and challenge 
OpenJDK to implement something that does.

Just be aware, that what you think you know, yours and your experts 
assumptions are based on theirs and your experiences with OpenJDK's 
implementation, that our experiences are worlds apart and are not the 
same.   The leg of your pants getting caught in your bicycle chain as 
you launch your bicycle off the line is not the same as tuning a top 
fueller.   Yes, I agree your bicycle is a piece of crap and you should 
get rid of it, it's just a shame you're removing the track too, since 
you determined that drag racing is fundamentally flawed based on your 
understanding of bicycles, were not going to be able to race top 
fueller's there now either, we need to find somewhere else to race.

I'm sorry that we didn't come to your aid a decade ago, I can only 
imagine all the time wasted manually editing policy files in test cases, 
time we could have saved you.

Anyway, it's time to go, I've said my piece, you all have work to do and 
so do I, the code's available for anyone who is interested, feel free to 
ask questions.

One final word, I would like to thank Alan for his directness and honesty.

Peter.


>
>> Serialization filters are lazily initialized, this is a performance optimisation (a security trade off), however if a program doesn't utilise serialization and has disabled serialization using a filter, this provides an opportunity for an attacker.  For the attack to be successful, the attacker needs to change the serialization filter properties, prior to initialization, if the attacker is successful, they will be able to disable the only security mechanism protecting de-serialization against attack.
> I’m not sure that’s right. Briefly looking at the code, it seems that if you set jdk.serialFilter on the command line, you cannot set it again from the program (you can set the property, but it looks to me like the new value will be ignored). But I’m not familiar with the implementation at all and I could be wrong. In any event, that’s an unrelated topic.

😇


>
> — Ron
>



More information about the security-dev mailing list