[cap-talk] ... enforcement - hope? Capabilities as clumsy, not

David Wagner daw at cs.berkeley.edu
Tue Sep 28 19:09:16 EDT 2004


Jed Donnelley writes:
>David Wagner wrote:
>>Sure.  The prototype for main() says that the caller must pass in a list
>>of strings.  If the caller wants to pass a file to the program, the caller
>>does so by passing a string that holds the file's name.  That violates
>>capability discipline.  There is no way to arrange that argv[1] holds
>>a capability to the file, instead of a filename represented as a string.
>
>I don't see the problem.

The problem is that it violates capability discipline.  Are you asking
"what is capability discipline?", or are you asking "why is capability
discipline important for security?".

Capability discipline is a principle intended to guide interface design.
In its simplest form, it says that you should manipulate references to
objects (capabilities), not names (strings), when given the choice.  For
instance, don't path a filename; pass a file descriptor or a java.io.File
object.  It is particularly important to use capability discipline for
any interface that traverses a trust boundary.  At least, that is my
understanding/interpretation of the term.  I hope others will correct me
if this is in any way inaccurate.

Ok, that was easy.  Why should we bother with capability discipline?
I'm going to argue that capability discipline has two benefits.

1. Capability discipline prevents confused deputy attacks.  Suppose Foo
calls Bar, asking Bar to operate on some file f on Foo's behalf.  If Foo
passes a string holding the filename of f to Bar and Bar then opens that
file, then the file permissions get checked according to Bar's rights
rather than according to Foo's rights -- which often leads to security
vulnerabilities.  Norm Hardy's paper on confused deputy attacks has more
to say about this.  A safer thing to do is to have Foo open the file f,
then pass a capability to this file to Bar, and let Bar use the capability.

2. Capability discipline can be used to defend against TOCTTOU bugs.
(This benefit will only apply if the object referred to is immutable.)
Suppose we have a lookup function
    Capability lookup(String name);
that takes a name, looks it up in some shared mutable table, and returns
a reference to the object bound to that name.  Many TOCTTOU bugs amount
to something like
    if (testProperty(name))
        doOperation(name);
Oops, wait, I guess I better give the definition of those two calls before
you can spot the bug.  A typical pattern is that they might look like:
    boolean testProperty(String name) {
        Capability c = lookup(name);
        return c.hasProperty();
    }
    boolean doOperation(String name) {
        Capability c = lookup(name);
        return c.doOperation();
    }
The TOCTTOU bug in the above is now apparent.  Because the shared table
is mutable, there is a race condition: the two calls to lookup() might
return different results.  The problem is that programmers often fail to
recognize this possibility.  This might sound more plausible if I say that
testOperation() and doOperation() are often library functions or operating
system functionality whose implementation is not readily visible to the
programmer, so the programmer might not realize that the existence of a
call to lookup() using this globally mutable shared state.  As a result,
it is easy for the programmer to end up with "stale assumptions" (to borrow
some language from MarkM).

How might capability discipline help?  Well, capability discipline says
that you shouldn't be operating on names (strings); you should be using
references.  Thus, capability discipline would have you modify the
interface to the library or OS so that you pass a reference rather than
a name, and this would force you to push the call to lookup() further up
the control flow and into a place visible to the programmer.  The code
becomes something like:
    Capability c = lookup(name);
    if (c.hasProperty())
        c.doOperation();
If we can now assume that the object referred to by this capability is
immutable -- or, at least, the truth value of c.hashProperty() is
invariant and will not change -- then the problem with "stale assumptions"
goes away, and the TOCTTOU vulnerability disappears.  Capability discipline
alone is not enough; you still need the end object to be immutable; but
with capability-style programming, at least you don't have to worry about
races that arise from the mutability of the lookup table.

And, the above considerations say that the interface to main() ought to
be a list of references (capabilities), not strings (names).  The current
interface violates capability discipline and thereby exposes applications
to confused deputy and TOCTTOU vulnerabilities.  That's my summary of the
argument, anyway.

By the way, you might be tempted to consider the following idea.  As soon
as main() starts running, it immediately attempts to turn each of the names
passed to it into capabilities.  For instance, maybe it calls open() on each
of the filenames it receives, or lookup(), or whatever.  Then, we make sure
that main() always uses those capabilities thereafter and never again looks
at the names passed to it.  This would be a partial improvement, but it is
only partial.  You are still vulnerable to a confused deputy attack where
Foo calls Bar, passing a filename that Foo could not have opened in hopes
that Bar can be tricked into opening it using Bar's privileges.  And, you
still have some non-zero risk of TOCTTOU bugs when stale assumptions are
inherited across process boundaries.  It's an improvement over doing nothing,
but it's still a violation of capability discipline and thus has problems.

Note that the idea in the previous paragraph still requires the program
to play by a special set of rules.  If you're given legacy code, you might
be tempted to consider a weaker variant on the above idea.  The library
(or OS) could maintain a per-process table associating to each filename
that has ever been mentioned before a file descriptor associated with it.
When the library sees a new filename for the first time, it could immediately
open that file, add it to the table, and from then on always use the file
descriptor.  When the program tries to perform some operation on a filename,
the library could intercept that operation, check whether the filename is
in the table, and if so replace that operation with one referencing the
associated file descriptor.

However, this approach is problematic.  First, it changes the semantics
of the legacy program, so one would have to worry about the possibility that
it might break existing code.  Second, it violates capability discipline
and thereby inherits all the confused deputy and TOCTTOU risks of the idea
mentioned two paragraphs up.  Third, it introduces the possibility of
confused deputy attacks that go beyond what might occur with the idea two
paragraphs up.

>I see the issue of using capabilities for rights communication between
>domains as independent of such mapping issues above.

I'm puzzled by the focus on rights communication.  The majority of apps
I use just run with whatever rights they are given and never communicate
their rights to anyone other process.  If your focus is on coarse-grained
protection of processes from each other, why such a focus on rights
communication?

Worrying about rights communication makes a lot more sense when we talk
about decomposing an app down into smaller pieces that we protect from
each other, for purposes of least privilege.  But, if you're just going
to run the application as one big monolithic thing sitting inside a single
address space, why worry about rights communication?


More information about the cap-talk mailing list