Fred Oliver wrote:
Peter,
I'm not clear on the scope of the proposal. Do you depend on
enumerating all of the classes for which delegates would be needed?
How would you interpose the delegate?
Revokeability is intended for downloaded code. I have some different
thoughts about possible implementations. The aim is to force re checking of
a Permission if a reduction of trust has occurred. To never allow references
to objects with privileged functionality to escape into unprivileged code.
The best place is to contain the privileged functionality within the
platform, the second best place, the client implementation or remotely in
the Server's service provider implementation.
Many Java classes are subject to Permission checks only in their
constructors, or obtained from checked methods or GuardedObjects, these
Permission's are not safe to grant if they are to be later revoked, object
references may still exist, enabling privileged functionality in untrusted
client code. What is needed are new Permission's that protect the methods
and creation of these classes by using a delegate to wrap the class,
depriving access via existing unchecked methods. I think a builder class
that returns OutputStreams (InputStreams, or any other etc), could build
many different types of OutputStreams but utilise the same delegate. This
could be provided as part of the Apache River platform.
What I want to do, is enable a client to provide Permission's temporarily
for a period of time to a ProtectionDomain, then revoke that Permission
later. I would want to do this if I utilise services from different servers
that utilise the same proxy code, so I can re-use the ClassLoader and
ProtectionDomain, to avoid having to use a new ClassLoader with re
verification of the same bytecode. This is based around provisioning of
proxy codebases, using Entry's to advertise the required codebase, version
and Permission's required.
I had thought about requiring all delegates register with the revokeable
policy, however now I'm having a change of heart, and think instead a class
or delegate could use a MethodAccessController, with two methods
checkPermission() and getPermission(). The RevokeableDynamicPolicy
implementer can provide the implementation for the MethodAccessController
interface. Each delegate gets it's own MethodAccessController object
(during construction) which has been designated a Permission to check (at
construction time), from the policy. The MethodAccessController
implementation will have to keep a static set internally of all objects, the
policy will require a static method to Enumerate over these and set them to
check when the Permission Class matches that of a revoked Permission,
probably by passing a Set of Classes.
This Permission check would be optimised, to call
AccessController.checkPermission(Permission) only under the following
conditions.
1. If this is the first time the current thread has called a method
(or constructor) on the delegate object.
2. If revocation has occurred for a Permission with the same Class in
any ProtectionDomain, since the last permission check performed on
that thread. The MethodAccessController keeps track of the
threads with an object weak hash map.
The $1,000,000.00 question: Is the optimisation of only checking a thread
once, on the assumption that if the ProtectionDomain's on that thread's
stack are trustworthy to access the method, the code involved in this thread
can still be trusted until a Permission that implies it has become revoked?
Remembering that the code was originally trusted, the object constructed
with the Permission check performed, utilised by that code for a period of
time, but now now that Permission has been revoked any method called on the
delegate throws an AccessControlException.
The only way the code could still have the Permission is if the
ProtectionDomain doesn't exist on the thread's stack, at the time of the
permission check, the check field is cleared for that thread and later, the
thread invokes the method with the ProtectionDomain in question on it's
stack, however to do so, the code in question must already hold a reference,
but not be involved in the current operations, it seems the probability
would be very low, the reference would have to be passed from another
thread, if an attacker could place this in the code, they'd be better off
attacking during the privileged period. The Permission revoked code would
stop privileged operations during re verification of trust for another proxy
object and server.
Static fields within the code could be used to store information between
service proxy's, this has a parallel to applets, where this was fixed with
separate ClassLoader, the client has that option however.
Publicly shared proxy and service implementations can have their source
audited.
As an example, I think you propose that if code in a domain has a
socket open on a port for which access is later revoked, the code
should be denied further read/write access to the socket.
-Should the socket be left open or closed?
Closed, to free the port. If somehow it is shared between threads, the
other thread will receive an IOException.
-Was a delegate introduced and where?
The best place is in a builder or factory method, since constructors create
a dependency link. I'm thinking of a implementing new Platform class or
builder for the various Stream classes, along with new Permission's
-Can the code use reflection to bypass the delegate?
Yes, if it has Permission.
-Is reflection denied generally?
Yes, it should not be granted to code that will have Permission's revoked.
Thanks,
Your welcome, & thanks for asking too ;)
Cheers,
Peter.
Fred
On Sat, Aug 7, 2010 at 2:10 AM, Peter Firmstone <[email protected]> wrote:
Please help identify any fallacies or oversights in the following
arguments.
A Permission may be revoked, at any point in time after a revocation,
untrusted code may hold a reference to a privileged object.
Some Permission's protect methods, such as Thread.interrupt(), these are
effectively revoked with the existing Java security model, however other
objects are only protected in their constructor, the responsibility being
on
the trusted code, not to let their references escape, such as
FileOutputStream.
The moment code holding a reference becomes untrusted, the reference has
escaped.
Instead of using a GuardedObject, or checking permission in constructors,
to
deal with Permission's that can be revoked, we need to encapsulate the
object that needs protection with a SecurityDelegate.
During a checkPermission call, the current Thread's AccessControlContext
is
obtained, and (gross simplification) is asked to checkPermission. The
AccessControlContext contains all the ProtectionDomain's on the stack,
all
ProtectionDomains on the stack must have the Permission for it to
succeed.
The ProtectionDomain's contained by the AccessControlContext are related
to
the class and object methods called and returned, the ProtectionDomain's
are
dynamically added or removed.
So the thinking behind the SecurityDelegate's private check method is
that
an object must be protected in a dynamically changing environment:
1. Has the RevokeableDynamicPolicy advised that a check must be
performed?
2. Is this the same thread that the last security check was made
against? If we haven't been advised that there is a reduction of
trust in our dynamic Security environment and the last
checkPermission call succeeded on this thread, then we can assume
that this Tread is still safe.
3. If this thread is different or new, then we must checkPermission,
regardless of whether trust has changed recently or not.
The costs:
1. Multi-threading is penalised (although a WeakMap could be
utilised, with threads as keys, and boolean check values, where
all are set true by the notify() call).
2. The three "if" calls on every method invocation, check, null and
== Thread.
3. Replicating the check method on all implementers (this will
require a helper class to implement the check).
4. The RevokeableDynamicPolicy will need to notify all
SecurityDelegate's every time a reduction in trust occurs, it will
rely on GC to clean up and remove SecurityDelegates.
The assumption is if the current Thread was trustworthy last call and the
environment hasn't experienced a reduction of trust, we can still trust
this
thread. There is of course a risk that a Thread may have a new
ProtectionDomain on it's stack that isn't trusted, however this could
still
happen in the case of the guarded object, where the environment doesn't
experience a reduction of trust and the trusted code must be trusted not
to
let the reference escape it's own ProtectionDomain. Any code that
experiences a reduction of trust will receive an AccessControlException.
Cheers,
Peter.