[Python-Dev] capability-mediated modules (was: python-dev Summary for 2003-03-01 through 2003-03-15)
Zooko
zooko@zooko.com
Tue, 18 Mar 2003 07:41:23 -0500
brett@python.org wrote:
>
> Security with capabilities is done based on possession; if you hold a
> reference to an object you can use that object.
Note that you can use capabilities as your sole access control mechanism if
every resource that you want to protect is identifiable with a Python reference.
For example, suppose you want to control the ability to listen on sockets for
network traffic. If there is a reference (e.g., to an object) that represents
the privilege of listening on sockets, then you can give such a reference to one
object, allowing that object it to listen on sockets, while withholding it from
another object, thus preventing that one from listening on sockets.
The only part of Python that isn't already well-matched with capabilities is the
way that authority is gained by importing modules, and you can load a module
even when you weren't given a reference to it. Reconciling Python modules with
capabilities would be the challenging part. Python objects, bound methods,
functions, and suchlike are already well-matched to capabilities.
In case that isn't clear, I'll write a quick example. Recalling my "tic-tac-toe
game" example from [1], I wrote code which allowed or denied the tic-tac-toe
game to paint a window on the screen and to write to a file. This "allow-or-
deny" enforcement was unified with the designation of which window and which
file. That is: by passing a reference to a certain window, I simultaneously
told the tic-tac-toe game which window to draw in *and* extended to it the
privilege of drawing to the screen.
This is a central motto of the capability security crowd: "Unify designation
with authority."
In a capability system *all* authority -- everything that you could ever want to
prevent -- is mediated by capabilities. Code that is loaded and run, but to
which no capabilities are extended, must be incapable of doing anything
dangerous.
Now, what about modules? In current Python, some code can "import os" and gain
all kinds of authority. In the rexec scheme, as I understand it, there was a
handler function which could be overridden to determine what happens when I try
"import os". This is effectively a "policy mini-language", such as in the
hypothetical "restricted Python v2" [1].
(Guido has pointed out that this overridable policy handler could be used to
implement capabilities as well as other regimes. I think I agree in principle,
but what I am advocating here is having the core language implement capabilities
so that the programmer-visible part is as minimal and unified as possible.)
Now what I would *like* is that instead of doing "import os" to load code,
instead the caller provides, or doesn't provide the os module as part of the
construction/invocation of A.
I don't have a clear idea yet of how that could be implemented in a Pythonic,
compatible way.
Just to help me think about it I'll suggest a non-Pythonic and incompatible way:
there is no "import" keyword. When you invoke a constructor, function, method,
etc., you have to pass as arguments references to everything that the code will
need to do its job. So, assuming the tic-tac-toe game requires the "math"
module and the "string" module, I would have to write:
# restricted Python v3+modules
game = TicTacToeGame()
game.display(open("/tmp/tttgame.out","w"), math, string)
The burden of typing in dozens of module names with each invocation can be eased
by: 1. bundling modules together (put math, string, and some other stuff into
one object/module/package named "standardstuff" and pass that as an argument),
2. "safe" modules that nobody could ever wish to prevent could be globally
available (via the resurrected "import" keyword, I guess).
If math and string are both "safe", then the example goes back to:
# restricted Python v3+modules
game = TicTacToeGame()
# a game against itself that writes results to a file
game.display(open("/tmp/tttgame.out","w"))
# a game against remote, listening on a socket
game.display(open("/tmp/tttgame.out","w"), socket)
(Note that there is a bootstrapping problem -- *some* code has to receive a
reference to the os module ex nihilo. That code should be "trusted" code -- the
Python interpreter, basically.)
Ah, but this last line shows another problem -- the game now has the socket
module, and the ability to open sockets to remote hosts and more. I just wanted
to allow it to listen on a particular port! The code would be safer if I didn't
pass the large-grained module and instead passed a specific object:
# a game against remote, listening on a socket
listensocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listensocket.bind(('daring.cwi.nl', 8901,))
game.display(open("/tmp/tttgame.out","w"), listensocket)
Regards,
Zooko
http://zooko.com/
^-- under re-construction: some new stuff, some broken links
[1] http://mail.python.org/pipermail/python-dev/2003-March/033938.html