I'm going to raise this as an issue on Jira, however I want to be sure I
haven't overlooked something first, so I've cc'd this to a number of
you, I know many of you are busy, so won't be offended if you're unable
to weigh in, if there are other reasons for not responding to the list
please feel free to correct any of my assumptions directly.
In a djinn (Jini network environment) You can limit untrusted code from
gaining permission by ensuring policy permission grants, include both
the Principal && ( CodeSource URL || Signer) and by only granting
trusted code AuthPermission("doAs").
But what if I want to run as a Subject with untrusted code?
To run as a Subject, you have to trust the code and it must have
AuthPermission("doAs"), unfortunately, this allows the code to run as
ANY subject, which may be undesirable, if "doAs" is granted, there are
no restrictions as to which Subject the code may run as, including an
administrator if it finds itself on the stack at the right time, this
might allow code to perform a privilege escalation attack.
To allow a user to execute some task via a smart proxy service,
AuthPermission("doAs") must be granted to the smart proxy after
verifying proxy trust.
But what if we want to use a service with a smart proxy without granting
trust? So I can use it while running as my Subject, allowing me to use
my public credentials for authorisation to run as my Subject on the
services server (with another set of Principals assigned by the service
server), without granting smart proxy code my local Principal
Permissions from my local domain. I want to allow Subjects to cross
authority domains (personal, company and country network boundaries),
where trust relations aren't implicit.
Under certain circumstances in a dynamic distributed environment like
River; we don't always know in advance who the code Signers will be or
which CodeSource URL will be utilised (which is why we have
DynamicPolicy; the server Subject vouches for the proxy code).
For example, if you provide a collaboration service (via the internet),
which utilises handback objects from clients (a handback object could be
another service proxy), when client Subjects log in to use the service,
there is a risk that another clients code is on the stack at the time
Subject.doAs is called, enabling it to access information that is only
intended for the user that has just logged in, by Subject.doAs injecting
it with the another users principals and running on the another users
thread, in doing so the ProtectionDomain on the new user thread call
stack no longer contains the original Subject's Principals, but now only
the new user / Subject's Principals.
This issue is specific to Jini / River because of the power of dynamic code.
Although this power isn't fully utilised now, it may be in the future,
if Jini services are made available on the internet.
One solution might be to add methods similar to Subject's static methods
to net.jini.security.Security.
As I mentioned earlier (see Re: Distributed Network Security), we could
extend and modify SubjectDomainCombiner to add a SubjectProtectionDomain
to the call stack, instead of adding Principals (privileges) to all
ProtectionDomains on the stack at that point in time. This way we no
longer need to check for AuthPermission("doAs") and untrusted code is
prevented from performing a privilege escalation attack if it exists on
the call stack at the time another user Security.doAs(Subject,
PrivilegedAction) is called.
There are other subtle kinks with SubjectDomainCombiner (implementation):
When an administrator adds or removes a Principal from a Subject, the
ProtectionDomains on the stack are not updated, this requires a user to
complete the PrivilegedAction before changes are effected.
If we have SubjectProtectionDomain and add it to the call stack, we
don't need to add Principals to every ProtectionDomain, instead the
SubjectProtectionDomain asks the Subject for the Principals. This
allows dynamic updates to be effected immediately, without requiring the
user to log out and in again. Implementing equals and hashCode in
SubjectProtectionDomain would no longer require caching of
ProtectionDomain instances too.
Each domain is responsible for assigning Principals to Subjects,
Subjects may have different Principals in different domains.
It's worth noting that Oracle has been fixing deserialisation privilege
escalation attacks, when downloaded code doesn't yet appear on the call
stack and privilege escalation attacks by classes that extend trusted
classes, but don't override their methods allowing them to avoid
appearing on the call stack.
We can use similar methods to limit privileges during proxy class
initialisation during unmarshalling in MarshalledInstance.
I propose adding the following static methods to net.jini.security.Security:
public static <T> T doAs(final Subject subject,
final PrivilegedAction<T> action) {
throw new UnsupportedOperationException("not implemented");
}
public static <T> T doAs(final Subject subject,
final PrivilegedExceptionAction<T> action)
throws PrivilegedActionException {
throw new UnsupportedOperationException("not implemented");
}
public static <T> T doAsPrivileged(final Subject subject,
final PrivilegedAction<T> action,
final SecurityContext context) {
throw new UnsupportedOperationException("not implemented");
}
public static <T> T doAsPrivileged(final Subject subject,
final PrivilegedExceptionAction<T> action,
final SecurityContext acc)
throws PrivilegedActionException {
throw new UnsupportedOperationException("not implemented");
}
Where SecurityContext is determined by calling Security.getContext().
SecurityContext contains the AccessControlContext in addition to any
other context settings, like the context ClassLoader.
We would require a Security Property to be set to allow these methods to
behave identical to existing JAAS Subject static methods OR to behave as
proposed.
// Security Property
String subjectBehaviourProperty =
"net.jini.security.Security.distributed_domain_subjects";
DynamicPolicy grants would also need to subtly change the way
Security.grant methods operate when the property is true, both the proxy
ClassLoader and the Principal must be granted necessary permissions
separately as they will appear in separate ProtectionDomains in the
active AccessControlContext.
DistributedSubjectDomainCombiner would extend SubjectDomainCombiner and
effect the required changes, only one SubjectProtectionDomain would
exist on the stack at any time.
Compatibility would be maintained with existing JAAS providers etc.
Policy files may need to be written a little differently to reflect the
separation of concerns of Principal and CodeSource permissions.
Penny for your thoughts?
Best Regards,
Peter Firmstone.