Split Capabilities: Making Capabilities Scale
Mark S. Miller
markm@caplet.com
Sun, 09 Jul 2000 11:17:51 -0700
At 11:11 AM 7/6/00 , Karp, Alan wrote:
> > As an aside, I'ld argue that it is *rarely* the case that
> > users may wish to
> > share objects in bulk, and *never* the case that correctly
> > designed programs
> > should do so. Up to a point, the fact that capabilities means
> > that they
> > reinforce good design practices.
>
>Actually, I think the situation is quite common. Many times when I fork a
>process, I'd like the child process to have a substantial fraction of my
>privileges. Not every time, but many timmes. For example, a word processor
>I spawn will need access to fonts. A build will need access to source and
>object files and the required executables. Each of these requires transfer
>of a potentially large number of capabilities.
This is a defining issue of conventional capability style: The principle of
least authority. For non-legacy-oriented code, we believe the child process
(or whatever) should be given access to exactly those capabilities plausibly
needed for it to carry out its duties. A great example (from Dean Tribble)
is the difference between a file copy routine written in the Unix style vs
the capability style. In the Unix style, we understand
% cp foo.txt bar.txt
to be passing the strings "foo.txt" and "bar.txt" to the program designated
by "cp". The shell evaluates (sort of) the name "cp" to a program, but it
does not evaluate the arguments to files. Instead, it leaves it to the cp
program to do so. Therefore, the cp program needs access to the invoker's
entire accessible file system merely in order to look up these two names!
Any such system is begging for trojan horse viruses.
By contrast, the corresponding E command line
? cp ( <file:foo.txt> readOnly, <file:bar.txt> )
gives the "cp" function access only to the File objects that the two
arguments evaluate to. It would not have access to anything else. To
understand the authority needed to issue this command line, we need to
expand the URI syntactic sugar:
? cp ( file__uriGetter get("foo.txt") readOnly, file__uriGetter get("bar.txt"))
The outer scope for interactive commands or top-level *.e scripts (the E
equivalent of "main") contains powerful capabilities (like file__uriGetter)
that provide all the authority of their process. Under legacy OSes, this is
normally also all the authority of their "user". This is a dangerous
environment, and the E programmer should do very little in this scope other
than to invoke other services (like the above hypothetical "cp") with well
chosen narrow authorities derived from these broad authorities. I take it
this dangerous scope corresponds to the E-speak root name frame or the
KeyKOS root login directory?
Invokable services (like "cp") are defined in *.emaker files, whose outer
scope provides no authority whatsoever to effect the world outside of
itself, other than to use computational resources. (Note: this claim has
yet to be fully implemented.)
An E user that's sloppy about the principle of least authority might
leave out the "readOnly" above, in which case everything would still work if
"cp" operates correctly. If "cp" is buggy or malicious, the extra damage
would be limited to modifications to the file named "foo.txt", rather than,
as in the Unix case, all files modifiable by this user. Why is this safety
so free of notational cost? Because we have to designate to cp what files
we're interested in anyway. As long as these designations are evaluated in
the invoking context rather than the invoked context, as is the case in any
lexically scoped programming language, we're most of the way there.
From what I've read, E-speak has a similar perspective on names. So perhaps
it can dispense with granting authority en-masse to child processes?
Cheers,
--MarkM