At this time, Lilith security is in the design and prototype stage,
and is designed to work with the Lilly classes.
The goal of the current
security architecture is to provide a proof of concept under which
a machine owner can ensure that users running a LillyLilim on his host are
subject to constraints that he defines. These constraints can
vary on a per-user basis. The ultimate goal of the security
architecture is to
provide flexible security on a per-user, per-invocation basis. This
security will also include mechanisms for authentication (veryifying the
users identity) and message integrity.
We presuppose a state in which the LilithHost owner has the user's public key
in his .keystore file. When the user wants to run a LillyLilim on a remote host,
he sends his identity over with the LillyLilim. The LilithHost running on that node
provides a unique
class loader for each user that associates all classes loaded through
the loader with a Code Source associated with the user's key.
The LilithHost loads a user's LillyLilim and all classes subsequently required by
the LillyLilim through the user specific class loader. In this way, instances
of a class are associated with a particular user. The multiple
class loader configuration is necessary as Java is inherently
class-centric and provides no straightforward route to associate a
particular instance of a class with a user.
Constraints are placed upon a user's class by allowing or disallowing it
Permissions. The set of possible Permissions consists of Java defined
defined Permissions, and any other special case Permissions the LilithHost
owner (or other) chooses to define. Java defined
Permissions include, for example, network and file access. Lilith defined
Permissions are those associated with the calls buildtree, sendlilim,
and runlilim. An example of special case Permissions would be those that
the LilithHost owner might define for his own classes he allows the user to access.
The LilithHost owner grants users' classes specific Permissions in the
.java.policy file. Whenever calls associated with a Permission
are invoked, Java performs a security check.
Java allows a call to take place if all classes in the execution
stack have the required Permissions (or if a class in the stack has such
permission and is specifically written to take the
responsibility of invoking a call on behalf of other classes higher up
in the stack.) Thus, the .java.policy file and Lilith's user-specific class
loader scenario together link users, classes, and Permissions.
The LilithHost owner's .java.policy file should give Lilith classes
java.security.AllPermission. This allows
the Lilith classes to make, for example, network and file accesses
where necessary. The LilithHost owner can apply specific contraints on
specific users' classes by allowing or disallowing individual
Permissions on a per-user basis.
The design of the Lilith-LillyLilim interactions makes it especially easy to
permit Lilith necessary accesses, deny specific users specific accesses,
and be assured that the users' classes can not abuse the greater
Lilith accesss for their
- No conflicts between Lilith-only calls and LillyLilim calls.
Lilith's interactions with the user's LillyLilim code are defined in such
a way that mechanisms
necessary to the basic functionality of Lilith (e.g., network accesses)
never include the users code in the execution stack. Thus any
constraints on a given user will not constrain Lilith. For example,
dissallowing a user network access denies a user the ability to open his
own sockets, but still allows Lilith to open sockets and send messages in the
desired manner. This makes it easy to add contraints to the .java.
policy file as the LilithHost owner knows the constraints will pertain only
to the user defined code and will not impede Lilith.
- Users cannot get rouge access to Lilith to attempt to abuse security checks.
Security checks are innately done by checking the permissions
of all classes within the execution stack. Classes with greater permissions
can perform accesses on behalf of classes with lesser permissions only
by explicitly providing such functionality in the code of the lesser constrained class. No Lilith code
provides such a service, so there is no way a user can trick the Lilith
code into providing access for him. Furthermore, the user's LillyLilim code
has no reference to the Lilith code and therefore cannot get a handle to the
Lilith classes to even make such an attempt.
- User can't hide his code's presence in the execution stack.
If the user's LillyLilim makes a call, it will appear in the execution
stack. Invoking other classes will not mask the LillyLilim. Often such classes
would have been loaded through the user specific class loader and therefore
be subject to the same constraints. In the event that the user can
somehow gain reference to a class that is defined to access on behalf
of others, that class would have been loaded with some constraints
that cannot be escaped.
scalability - accessing a single .java.policy file and .keystore is a bottleneck