If your interested, I could use some peer code review. I recently
created ConcurrentDynamicPolicyProvider, motivated by Gregg's
identification of a DynamicPolicyProvider concurrency bottleneck, here's
some background:
Previous Policy implementations, including DynamicPolicyProvider (the
original implementation was renamed to
org.apache.river.imp.security.cdc.DynamicPolicyProviderImp) are a
bottleneck to multi threaded performance. Every check via the
AccessController or SecurityManager results in checks of every
ProtectionDomain within the context of the current thread (or defined
AccessControlContext, which may of course be expanded or changed by the
DomainCombiner). implies(Permission) is called on each
ProtectionDomain, the ProtectionDomain (if created with the dynamic
constructor) then asks the Policy, if it implies(Permission), if it
does, it returns true, if false, it checks it's internal
PermissionCollection. This makes the Policy provider an obvious point
for lock contention between threads. And all this is just for one
security check!
The new ConcurrentDynamicPolicyProvider also implements a new interface
called RevokeableDynamicPolicy, which supersedes DynamicPolicy.
DynamicPolicy is an interface for Policy Providers to implement that
allow dynamic permission grants, or more accurately, the delay of
permission grants until runtime, it's used in the proxy verification
process (see net.jini.security.Security). DynamicPolicy grant
Permission's to a ClassLoader domain, this grant may cover multiple
ProtectionDomain's. Dynamic grant's could not be revoked, trust once
granted, couldn't be removed.
One of our goals for Apache River is to continue the push into untrusted
networks that Jini 2.0 started. One of the problems that Project
Neuromancer highlighted was that for a remote reference implementation
that utilised a distributed hashtable based object registry, there was
an issue with proxy trust, once a proxy's server has moved, a proxy can
no longer be trusted (for the remote reference proxy implementation that
is), but the proxy itself had to be given a thread of control to
discover the new location of it's referent (server).
This might seem peculiar, but the reason is that the server is involved
with trust verification; the client asks the server if it trusts the
proxy. The client can authenticate the server and the server can
authenticate the client, the connection having been severed requires the
process to repeat itself, this cannot be done without first revoking trust.
One could change the AccessControlContext, by adding a ProtectionDomain
that had limited Permission, then run a method on the proxy to allow it
to retrieve a new referent, but it would be a hack, it isn't the
solution if the proxy is used from another thread, it still has elevated
permissions.
Trust for the client can be broken down into two components:
* Do I trust the downloaded code? (Proxy Verification)
* Do I trust the Service's Principal? (Authentication)
The foundation of RevokeableDynamicPolicy, is that trust may change at
runtime, due to many factors, such as that mentioned above, or it may be
due to a vulnerability found in a CodeSource, but for whatever reason,
the ability to Grant some trust as well as Revoke it is important.
(Note in order to be able to revoke, the Policy must not be allowed to
provide the dynamic permission grant's PermissionCollection's to the
ProtectionDomain, otherwise these will become merged into the
ProtectionDomain's private PermissionCollection, resulting in implies()
returning true by the ProtectionDomain, even though the Policy may have
returned false).
Dynamic Grants, are currently applied to a ClassLoader domain, where a
proxy is loaded, so they may cover multiple ProtectionDomain's within
that ClassLoader space. I've decided to expand the scope of grant
possibilities to allow runtime Permission grant's to apply:
1. To a specific CodeSource, no matter what it's ClassLoader,
2. A ProtectionDomain (more accurately, a CodeSource within a
Particular ClassLoader **),
3. Or Certificate based grant's, which require certain developers
having signed the CodeSource, no matter what CodeSource.
The above grants can all be restricted to a set of Pricipal[]'s, for
instances where Authentication is also required.
**At the time of the AccessController Permission check, the
SubjectDomainCombiner creates copies of the ProtectionDomain's from the
stack of the current thread's context, so the actual ProtectionDomain
grant also applies to ProtectionDomain's with an identical CodeSource
and ClassLoader.
In a RevokeableDynamicPolicy, the dynamic grant is known as a
PermissionGrant, which is an interface for which many implementations
are permitted to exist. The code involved in the PermissionGrant must
also be able to satisfy Permission checks, before the grant will be
accepted by the Policy implementation for obvious security reasons.
It is the implementation of PermissionGrant which determines both the
scope and how the grant applies.
I created another Policy, called ConcurrentPolicyFile, to replace the
standard file based PolicyFile provided by the JRE. To make it easier I
borrowed some code from Harmony, used to parse Java policy files, an
interface called PolicyParser. DefaultPolicyParser, has been refactored
to generate PermissionGrant's from the standard java policy file syntax
which ConcurrentPolicyFile utilises. Custom Policy Parsers and Scanners
may be written by implementers also, for example to parse the OSGi
bundle Permission's text file syntax.
The PolicyParser may also retrieve policy files via URL from across the
network, enabling centralised deployment of dynamic grant security
policies if required.
This allows another new possibility, that of utilising the existing Java
policy file syntax, to store the Dynamic PermissionGrant's for the proxy
in a jar file. By permitting the standard java policy file syntax, it
also allows the proxy to utilise other third party jar files or
libraries and specify the Permissions required for each of those too.
Furthermore, PermissionGrant's could be utilised as Service Entry's to
advertise the Permission client's are expected to grant, a client can
then decide if the PermissionGrant is acceptable (by querying it's
Permission's) before unmarshalling a proxy ,using the new
StreamServiceRegistrar with delayed unmarshalling ( the implementation
of StreamServiceRegistrar pending interface peer review). We can
implement Apache River framework utility classes that create the Entry
from the jar file for Service deployment.
A client having decided that the Permission's required by a Service, can
then go through the process of Proxy Verification, knowing precisely the
grant's required, the client can create a new instance of the
PermissionGrant with additional Principals and make the Permission's
specific to the ClassLoader and CodeSources, the proxy will be using.
At any time, the client may revoke the permission, it may do this
directly after discarding the service, or it may revoke some permission
based on the discovery of a new security vulnerability, thereby
eliminating it.
In essence, the developer decides the required Permission's, the
deployment framework obtains the PermissionGrant's from the jar's policy
file and the client decides if they are acceptable.
I also recall Gregg needing a method of determining the Constraint's a
Service requires, perhaps Constraints could be advertised via a
Constraint Entry, to allow filtering of Services which have restraint's
that cannot be satisfied.
To summarise recent discussions regarding Entry's we could have standard:
Codebase Entry's (as per Patrick's suggestion for provisioning)
PermissionGrant Entry's
Constraint Entry's
Any suggestions for the Entry format's would be most welcome.
This renewed focus on Security is intended to make it easier to deploy
Services over the internet. If Apache River is to succeed on the
internet, it will need to be robust from a security perspective, to have
a significant advantage in order to replace current protocol based
internet applications.
Apache River won't restrict internet based applications to Java.
Perhaps if we can succeed in making River run on the internet, we could
create a River Service Browser Firefox Plugin.
There's plenty of new code in need of peer review, if you have some free
time to assist, please SVN the latest source and browse under
trunk/src/org/apache/river/
Best Regards,
Peter Firmstone.