Thanks Gregg,
The services you deploy are often unique, but relevant and it's apparent
you've been able to explore and delve deeply into complex problems. I'm
grateful that both you and Dan are finding some time to discuss this
issue, because to be quite honest, I'm not happy that I fully grasp the
issues myself and discussion helps to improve understanding.
You've also hit the nail on the head, it's about limiting permission
while executing as a Subject while further limiting permissions
available to the proxy, but still allowing the Subject to have more
permission when the proxy isn't on the call stack.
It was also about separating Principal and CodeSource concerns, so you
don't need to ensure that all your policy grant files include CodeSource
and Principals. - But it turns out that this isn't necessary there is
an alternative option that follows:
Dynamic grants made to proxy's usually include the CodeSource and
Principals, in the current case, the permission granted can be less than
with the same Principals executed only in the presence of signed trusted
code, the dynamic grant is very specific, to be granted permission the
code must have the proxy ClassLoader and be executed by a Subject with
the Principals specified in the grant.
If the proxy doesn't have AuthPermission("doAs"), get the current
context by calling AccessController.getContext() this will contain the
proxy's ProtectionDomain if it has already been unmarshalled, then use
AccessController.doPrivileged to execute Subject.doAsPrivileged(Subject,
PrivilegedAction, AccessControlContext), which injects the Subject
principals into the proxy codebase, without allowing the proxy to gain
the privileges the Principals have in the presence of trusted signed code.
Rather than modify SubjectDomainCombiner, the alternative option
described above works by signing all your local code (some additional
work for the developer) and all Principal based grants must also include
signed by, so those principal grant's can't be stolen by untrusted code,
because untrusted code it isn't signed with the Certificates stated in
the policy file. No grant should be made to a Principal alone, it must
include a CodeSource URL or a jar Signer. BTW, ConcurrentFilePolicy
uses CodeSource URI's not URL, a small change of semantics for a big
performance gain.
Another benefit of signed code is it protects local package private
security boundaries, of course this will create more errors if you
haven't got your preferred lists right.
So we could provide two additional methods in
net.jini.security.Security, but without modifying SubjectDomainCombiner,
while also preserving current policy behaviour, to ensure that the
AccessControlContext used in Subject.doAsPrivileged is not null.
I guess what I've also just shown is that granting to a combination of
CodeSource / Signers and Principals is determinate as Dan mentioned in a
previous post, so yes, Dan was right.
Do you think a practical way for an administrator to limit the
Permissions a Subject can grant to a service proxy is by limiting them
with GrantPermission? It allows the Subject to have more permission (in
the presence of trusted signed code) than it's capable of granting.
The proxy could make available a list of Permissions it needs within the
jar file as you've suggested.
My gut feel is permissions should be granted by the Subject to the proxy
(in the presence of trusted code), however since not all Subjects are
people, this mechanism needs to be flexible. Any ideas? Events?
We can also determine if a Subject can grant a subset of the Permissions
requested by the proxy, by creating a ProtectionDomain that contains the
Principals and required Signer Certificate (this won't work for
CodeSource URL's only signed code), then ask the Policy for it's
PermissionCollection, then check GrantPermission for each permission on
the list.
This is where things get a little interesting, since
Subject.doAsPrivileged or Subject.doAs can be used to perform the
dynamic grants, there are traps for the uneducated.
Privileged context must not be used as the context to communicate with
the proxy itself, so it's beneficial to developers if we provide the
methods to perform high risk tasks without requiring the proxy to have
AuthPermission("doAs"). Prior to receiving dynamic grants, the proxy,
even if injected with Principals does not have Principal privileges,
hence privilege escalation is impossible, provided all grants to
principals also require a trusted jar signer. We can even pass the
proxy class to these methods, so they can ensure the proxy's
ProtectionDomain is on the stack, prior to making any method calls to
avoid deserialisation attacks.
Some supporting information:
1. Deserialisation Attacks: During deserialisation, there is a small
time period during class loading where privileges must be
minimised. Deserialisation privilege escalation attacks rely on
deserialisation being performed in a privileged context while the
ProtectionDomain of the object being deserialised is not yet on
the stack. Google deserialisation attacks if you're interested.
We should also add a PD with restricted privileges to the stack
while MarshalledInstance performs unmarshalling, just to make sure
that all deserialised object have no more privileges than required.
2. Thankfully this bug has been recently fixed:
http://slightlyrandombrokenthoughts.blogspot.com.au/2010/04/java-trusted-method-chaining-cve-2010.html
3. In Jini security documentation I've seen on the web,
Subject.doAsPrivileged is called with a null AccessControlContext
executing the proxy, in doing so the proxy PD is no longer on the
stack, however it isn't clear when the proxy ProtectionDomain will
be added back onto the stack, will it be possible for an object to
be deserialised in a privileged context? It's highly possible,
because of delayed class loading.
4. N.B. I'm not talking about denial of service during unmarshalling,
where the attacker is simply of nuisance value, say running an
unending loop or using up all memory. In this case the user can
just kill the jvm and start again. No deserialisation attacks can
allow creating ClassLoaders or setting the SecurityManager in
privileged context.
Solution:
Always ensure a domain with minimal privileges exists on the stack
during deserialisation.
Cheers,
Peter.
Gregg Wonderly wrote:
On Jul 7, 2012, at 8:02 AM, Peter Firmstone wrote:
These doAs methods in this case cannot elevate Permission, they can reduce
Permission to that which the Subject has in common with other code on the
stack, but it cannot be used by code to gain privilege if it uses a custom
DomainCombiner that extends SubjectDomainCombiner.
Would this be an acceptable compromise?
In future, we can look at other tools to assist with simplifying security, such
as static bytecode analysis or FindBugs perhaps.
When I am using remote code, I either trust it by source, or don't, and can assert that trust by
granting AllPermission for the URL, or not. Adding local resource access to a proxies code base,
is usually limited to network access, but sometimes file access for certain UIs which, for example,
have images which my caching URLHandler will store on disk. That access to local resources,
whether through Subject grant, or blanket permission is what matters for using a services
ServiceUI. When I use a services proxy for interaction, I've always used JAAS login services to
gain access, via PAM login services on Linux and other PAM supporting OSes using JNI mechanisms.
Thus, there is a Subject created which has a set of Principals that my LoginModule creates in the
form of a "user" and any "groups" that the user is a member of.
In the services, I always use Subject.doAs with those subjects, and could use
user or group based permission grants in my policy. But, in the end, I never
found that to be needed, and I instead, grant permissions to the codebase at
the appropriate granularity (never granting to or using a client side codebase
jar).
The authorization framework that I've mention here, before, is then used to do
role based control of how the API is used on the server, by the calls into that
environment from the client. I have a InvocationHandler that I insert to do
the Subject.doAs() on the server side. So, when the user authenticates with
the server, I get a Subject. I do Subject.doAs() to create an instance of the
InvocationHandler (which holds a reference to the Subject), and the exported
smart proxy instance, which is returned to the client.
When a Subject asserts some kind of client local controls, it could provide
some new functionality as you illustrated. In the case of network access
control, or other local resource access, it could allow you to limit access to
those resources, or to extend access in particular ways with a dynamic policy
grant, on the client.
I crafted some code in that direction at one point. It allowed the jar to have
a list of permissions in it, that it wished to have granted, and I was trying
to decide what the right interface would be, to allowing the client software to
talk to the user about these permissions. In a sense, this was along the lines
of Java WebStart kinds of thoughts. My ServiceUI desktop has a code flow at
the point that the UI is activated, that it could obviously do a resource query
into the jar, find the requested accesses, and prompt the user to grant them.
Conversely, you want to limit permissions by asserting a domain controlled by
the Subject, that would provide whatever access you granted to the Subject in
that domain/policty. So, at the time that a client UI is first activated, you
could use information about it being an uncontrolled codebase to trigger the
assertion of the Subject controlled domain.
What I think we need to focus on, are these two mechanisms (grant to client
thread/Subject jar requested permissions and limit of client thread to already
asserted Subject based domain) and the obvious fact that it's really about
asserting control into the client execution environment. We need to work on
how we'd decide that needed to happen, and then think about whether it's just
Subjects we want to assert, and whether we need to include the Privileged forms
or not.
Gregg Wonderly