Well, I'm wondering what the best solution might be for granting Permission's to proxy's that we'd later want to revoke. In effect we haven't decided to trust the proxy code. The concept is, to provide supervised access to privileged functions for proxy's, not unsupervised trusted access.

Summary of observations so far:

   * References to sensitive objects, must not be allowed to escape
     into untrusted proxy code.
   * Existing JVM Permission's like SocketPermission cannot be granted
     to untrusted proxy code, they let references escape.
   * Security Delegates, provide a means to encapsulate sensitive
     objects, every method invocation must be checked.  Micro Managed
     ;)  This can have some obvious performance implications.
* The delegate must hold any required JVM permission's. * The delegate holds a reference to a sensitive object, and controls
     access to it's methods.
   * Delegates may be nested, eg: a Socket Delegate, wraps an
     OuputStream in another delegate when returning it to a caller.
   * A fine grained Permission, is very specific, eg: only grant
     permission to contact a particular network address.  Fined grained
     permission's have a  performance cost as well as increased
     application developer complexity cost.
   * A broad, coarse grained Permission is simple, eg: you either have
     permission to use the network or you don't.
   * A delegate can close and release all resources held, when
     permission checks fail, if it catches the AccessControlException,
     otherwise it must let the AccessControlException bubble up the
     call stack, which still allows other calling threads to continue
     to utilise the delegate.  EG an OutputStream or Channel must
     close, while a Socket may remain open.

I can see there are two choices, using networking as an example (The Permissions below exclude JVM Permission's unless otherwise stated):

   * For reasonable performance, a delegate's permission check has to
     be non blocking and fast for the majority of invocations.
   * OPTION 1: To use fine grained Permission's, that duplicate
     existing JVM permissions, for grants to untrusted proxy's. Because
     this approach uses more resources and takes longer to check, we
     must cache the result for each AccessControlContext and don't
     check it again until revocation occurs.  An
     ExecutionContextManager looks after the permission check on behalf
     of the delegate, while the delegate (our micro manager) thinks
     it's checking the permission on every invocation.OR
   * OPTION 2: Use fine grained JVM Permission grant's for delegates
     only, and new course grained permissions for proxy's, which the
     delegate uses to supervise access.  The delegates are responsible
     for preventing references from escaping and checking Proxy
     Permission's on every method invocation.
         o EG: Grant a SocketPermission to the DELEGATE'S CodeSource,
           restricting construction of sockets, to particular address
           ranges, because the socket won't change it's connection
           after construction.  (If we limit the permission's we give
           to our micro manager, he can't give a proxy something he
           doesn't have).
         o The delegate controls access to OutputStream and InputStream
           methods, using a fast and simple course grained network
           Permission check.
         o To stop access to the network, I revoke the network
           permission on that proxy.
         o If I'm a proxy that doesn't have network permission, I can't
           access the network at all.
         o If I'm a proxy with network permission, I can only do what
           the delegates are allowed to do.

OPTION 1 is the most pedantic and perhaps more flexibile, since it fully controls and restricts access with finely grained permissions, however it increases complexity on behalf of the application developer, who has to understand and know all the new permissions.

OPTION 2, allows simplicity on the part of the application developer: "My proxy needs network access permission." The permission check executes very quickly on every invocation, (non blocking PermissionCollection required), so the result doesn't need to be cached for the AccessControlContext and we can simply use AccessController.checkPermission calls. The policy's, by limiting the the Permission's delegates have with traditional JVM Permissions, can control what networks can be accessed by proxy's. A default security policy for delegates can be provided with River.

I've given both options quite a lot of thought, but find myself drawn to option 2.

Option 2 requires a new class, ProxyPermission extends Permission.

So for another example, disk access, we can limit the disk area the proxy's can read and write, via the delegates, so these untrusted proxy's could have a public shared writeable disk area, on a particular hard drive partition, or capacity restricted file system directory, so they can't take down the system. The ProxyPermission would simply be, new ProxyPermission("File"). These proxy's could read each others files and do some nasty stuff to each other. But then if we're clever with our delegates implementations, each could create and assign specific directories for files on that filesystem, so if each proxy has it's own file handle delegate instances, the proxy itself can decide who else it does or doesn't share disk access with, by not sharing it's file handle delegate references. A file delegate could place some restrictions on the total number of writes, this might be system wide configurable, so one proxy can't fill up the filesystem. There are other things we'd need to consider, such as clean up remove files, freeing up resources etc.

Option 2 also requires a River jar archive called delegates.jar, containing all our delegate classes, which is then used to define any policy restrictions imposed on security delegates, which will restrict what untrusted, but supervised smart proxy's can do.

Then an application developer only has to think about, disk, network, etc, whatever simple permission the service's proxy requires and this is only granted while the proxy is in use. The administrator controls what untrusted proxy's have access to, with a policy file for the delegate.jar codesource. Delegate implementations can be hidden from application developers, via River's existing API's such as JERI.

These ProxyPermission's are then used to ensure revocation of other Permission's. Other permission's can be dynamically added and removed from the policy, but only a ProxyPermission removal, can effect full revocation.

But what if we want the greater flexibility that fine grained JVM permissions offer?

We can add Principals to the permission grants for the delegate.jar codesource, these can also be dynamically added and removed, so in effect we can control what we allow the proxy's to do, by what we grant to our delegates, delegates prevent the references from escaping and perform coarse grained method access permission checks, to ensure full revocation occurs when we want it to.

So if the application developer wants to:

  1. Perform Authentication of service and client
  2. Authorise, by granting delegates with specific Principal[], any
     Permission's requested via proxy constraints (excluding
     ProxyPermission's), to delegates.jar.
  3. Verify Proxy
  4. Grant ProxyPermission, "network", "file", etc to the proxy.
  5. Execute methods on proxy as a Subject.
  6. Revoke ProxyPermissions "network", "file", etc from proxy (dispose
     of proxy)
  7. We can also revoke the Principal and CodeSource delegate.jar
     permission grant, if we want, no references these permissions
     guard, have escaped into untrusted proxy code, the delegates
     prevented it, so they too are effectively fully revoked.
  8. We can now keep any objects we obtained from the service, knowing
     full well that a trojan cannot perform some action when a thread
     access the object, because it has no permissions.

But we're not restricted to using JAAS, you can utilise whatever user authentication system you like, the Delegates just ensure we don't give out references for security sensitive objects to untrusted code.

So if we manage to do this properly, we have the basis for cooperation between different companies or entities with disparate systems, where trust is based around authentication, authorisation, privacy and agreed common ServiceAPI, but doesn't extend to codebase trust.

If we can eliminate protocols as the required means of communication between disparate systems, we can open up new found abilities for communication.

How's that sound?

Anyone willing to assist?

Regards,

Peter.

Reply via email to