[Python-Dev] Re: Capabilities

Zooko zooko@zooko.com
Mon, 10 Mar 2003 16:15:18 -0500


(I, Zooko, wrote the lines prepended with "> > ".)

 Guido wrote:
>
> > The [Principle-of-Least-Privilege approach to securing a standard library]
> > is to separate the tools so that dangerous ones don't come tied together
> > with common ones.  The security policy, then, is expressed by code that
> > grants or withholds capabilities (== references) rather than by code that
> > toggles the "restricted" bit.
>
> This sounds interesting, but I'm not sure I follow it.  Can you
> elaborate by giving a couple of examples?

First let me say that "capability access control" [1] is a theoretical 
construct, comparable to "access control lists" [2] and "Trust Management" [3].
Each is a formal model for specifying access control rules -- who is allowed to 
do what.

But in the context of Python we are interested not only in the theoretical 
model but also in a specific way of implementing it -- by making object 
references unforgeable and binding all authorities to object references.

So in this discussion it may not be clear whether a claimed advantage of
"capabilities" flows from the formal model or from the practice of unifying
security programming with object oriented programming.  I don't think it is 
important to differentiate in this discussion.

Now for examples...

Hm, well first of all, where are rexec and Zope proxies currently used?  
I believe that a "cap-Python" would support those uses, implementing the same 
security policies, but more cleanly since access control would be a first-class 
part of the language.

I don't know Zope very well, and rather than guess, I'd like to ask someone who
does know Zope to give a typical example of how proxies are used in workaday 
Zope.  I suspect that capabilities are quite similar to Zope proxies.


Now for a quick made-up example to demonstrate what I meant about expressing
security policy above, consider a tic-tac-toe game that is supposed to draw to
the screen.

In "restricted Python v1", certain modules have been flagged as "safe" and others 
"unsafe".  Code can execute other code with a "restricted" flag set, something 
like this:

# restricted Python v1
game = eval(TicTacToeGame, restricted=True)
game.display()

Unfortunately, in "restricted Python v1", all of the modules that allow drawing 
to the screen are marked as "unsafe", so the tic-tac-toe-game immediately dies 
with an exception.

In "restricted Python v2", an arbitrary security policy can be implemented:

# restricted Python v2
games=[]
def securitypolicy(subject, action, object):
    if ((subject in games) and (action == "import") and (object == "wxPython")) or
        (subject in games) and (action == "execute") and (object == "wxPython.Window") or
        (subject in games) and (action == "execute") and (object == "wxPython.Window.paint")):
        return True
    # ...
    return False

game = eval(TicTacToeGame, policy=securitypolicy)
gameobjh.append(game)
game.display()

I think that the "rexec" design was along the lines of "restricted Python v2", 
but I apologize if this simple analogy insults anyone.

I'm not sure whether "restricted Python v2" is expressive enough to implement 
the capability security access control model or not, but I don't care, because 
I don't like "restricted Python v2".  I like restricted Python v3:

# restricted Python v3
game = TicTacToeGame()
game.display(wxPython.wxWindow())

Now the game object has a reference to the window object, and it can use that 
reference to draw the pictures.  If I later change this design and decide that 
instead of drawing to a window, I want the game to write to a file, then I'll 
change the implementation of the TicTacToeGame class, and then'll I'll come back 
here to this code and change it from passing a wxWindows to:

# restricted Python v3
game = TicTacToeGame()
game.display(open("/tmp/tttgame.out","w"))

Now if I were writing in "restricted Python v2", then in addition to those two 
changes I would also have to make a third change, which is to edit my 
securitypolicy function in order to allow this particular game object to access 
a file named "/tmp/tttgame.out", and to disallow it access to wxPython:

# restricted Python v2
def securitypolicy(subject, action, object):
    if (subject in games) and (action in ("read", "write",)) and (object == "file:/tmp/tttgame.out"):
        return True
    # ...
    return False

game = TicTacToeGame()
game.display("/tmp/tttgame.out")

This is what I meant by saying that the security policy is expressed in Python 
instead of by twiddling access bits in an embedded policy language.  In a 
capability-secure language, the change (which the programmer has to make anyway), 
from "wxPython.wxWindows()" to "open('/tmp/tttgame.out', 'w')" is necessary and 
sufficient to enforce the programmer's intended security policy, so there is no 
need for the redundant and brittle "policy" function.

I find this unification access control and application logic to resonate deeply 
with the Zen of Python.

Regards,

Zooko

[1] http://www.eros-os.org/papers/shap-thesis.ps
[2] http://www.research.microsoft.com/~lampson/09-Protection/Acrobat.pdf
[3] http://citeseer.nj.nec.com/blaze96decentralized.html