[Python-Dev] Re: Capabilities

Guido van Rossum guido@python.org
Tue, 11 Mar 2003 11:04:54 -0500


(Zooko, would it kill you to keep your line lengths well below 79?)

[Zooko]
> > > 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.

[Guido]
> > This sounds interesting, but I'm not sure I follow it.  Can you
> > elaborate by giving a couple of examples?

[Zooko]
> 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.

Yes.

> 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.

Not really.  The rexec design gives you the tools to implement either
v1, v2 or v3.  Its basic features are more like v1, but it has a
concept of Zope-like proxies, named Bastions, and it allows you to use
functions or bound methods as capabilities.  Bastions are mostly a
convenience to allow a bunch of capabilities to be used like an
object.  The "security policy" you sketch as part of v2 would be
possible but there aren't really any hooks to implement this; you'd
have to craft it out of Bastions and capabilities.

> 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.

Me too.

> 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

--Guido van Rossum (home page: http://www.python.org/~guido/)