Security, Persistence, and Reflection

BRIAN SPILSBURY zhivago@iglou.com
Thu, 8 May 1997 21:57:01 -0400 (EDT)


>    It would be too bad that the slightest mistake from a lisper,
> whether at a console, or in a program, or through the network,
> would crash the system, destroy or modify important data, etc.
> The possibility of a malevolent (volontary or unvolontary) program
> propagating on the net is to be seriously considered.

Be careful you're reminding me of java's security though lobotomy
approach.
 
>    However, one of the main features of LispOS is that software components
> can seamlessly exchange objects without the programmer having to manually
> encode and decode them with a stubborn unsafe flat format.
> 
> My opinion is that the system have a reflective kernel,
> so it can virtualize execution contexts.
> An "execution context" is any metaobject
> that defines how objects can be executed.
> An example execution context is the initial state of the CMUCL repl,
> where objects being executed are in source form.
> An execution context involves more than just the "environment"
> (set of bindings in accessible namespaces),
> as it also involves the way memory is implicitly allocated, etc.

Well, it sounds like you're breaking things here.
Memory allocation belongs to GC-land, the environment is um, well
the environment. It sounds almost like you want to reinvent the
unix 'monolithic binary' idea, only a but squishier.

> Hence, Chris Garrigues asked:
> > what exactly is a "user" in a LispOS?
> 
> Well, with a reflective approach, a "user" would be just
> a particular execution context.
> Hence, to restrict the rights of a program,
> you just would need create an execution context with less "rights":
> some functions/variables would not be accessible in the environment,
> or would have been redefined so as to filter accesses;
> the very implementation of the language could be changed so
> as to limit or other control resource usage (quotas, etc).
> An implementation might involve modified (or isolated) memory mappings,
> much like unix processes; or Lisp-in-Lisp interpretation/compilation;
> or tweaking of the assembly-level implementation of Lisp; or whatever.

This is just nasty. Capabilities provides a nice solution to this problem
re-inventing the one-address-space one-program-thingy isn't going to
get you very far though.

>    The result is that we really have some kind of LispVM (which doesn't imply
> a canonical representation; much less bytecodes or virtual processor stuff),
> that can be reified and modified, so as to control at a very fine grain
> the behaviour of programs running inside.
> Hence, any user can in turn drop some rights when executing programs;
> typically, any new access to a global resource would be monitored by the user
> [thanks to persistency, you only need do it once per program*access];
> a human user would also virtualize the memory usage of sub-users,
> so that he always keep a minimal pool of resource in case he runs
> short of memory.
> 
>    All in all, you have dynamic fine-grained control of resource usage,
> instead of the unix pseudo-security where the user
> (with the horrible need to give allmighty root access to daemons
> having to impersonate users, such as sendmail,
> or to programs accessing some hardware, such as X).
> [David Gadbois perfectly summarized the issue against coarsed-grained
> "protection" as only providing pseudo-security]

Uh, this solution you have _is_ the unix solution just using ACLs,
effectively.

> Then comes the problem of having multiple address spaces or not.
 
> Again, Reflection would allow the user and/or system administrator
> to dynamically control all those things: by being able to specify
> and modify the execution environment of objects, I can
> 
>    Perhaps the CLOS MOP offers enough expressiveness to do that,
> but the way it is usually implemented, it seems that this would be
> a completely inefficient way to implement
> something like configurable persistence
> (a reified generic function call at every memory access? Ouch!);
> Allowing the underlying implementation details of objects,
> such as the store(s) they are bound to, to be *dynamically* modified
> seems even a worse task for the CLOS MOP.
> Conclusion: Reflection must be built deep into the system.
 
I'll agree there.
 
> [Side note: Chris Bitmead talked about authentification
> of binary code by the system. Indeed, only authorized code
> may be executed, and only authorized meta-objects may produce
> authorized code. Such meta-objects could include compilers,
> cryptographic signature verifiers, etc; but if we trust the persistent store,
> this only needs be done once per binary object]

Um, why don't we just set it up so that you can't produce unauthorized
code? Ok, this means you need to distribute in an intermediate or
source form, but word/byte-code distribution isn't terribly chunky.

What I don't see if why a simple elegant, and essentially lisp
security system is not being advocated. Capabilities, especially
non-forgable capabilities are realized in the underlying lisp
concept of a reference. If I do not have a reference then I cannot
access a given object at all.

If we then have objects keeping lists of references to 'key' objects
then we can improve this security, I can have a 'read-brians-files'
key, which I give to all my file objects and tell them to let people
with this key read them.

There are issues here, mainly with delegation and transport of keys,
but single-use keys redress this to a point, and active capabilities
claim to get around this - still have to read that paper.

But unless there is something horribly wrong with object-internal
infinitely flexible security using unforgable keys, then why are
we even considering doing it the 'not quite unix way'?

With capabilities we don't even need a notion of 'user'. We can
implement users as agents which hold access capabilities for a user and
which are accessable via a capabilities held by an object that wants
passwords in return for them.

Brian