[Python-Dev] In defense of Capabilities [was: doc for new restricted execution design for Python]

Brett Cannon brett at python.org
Thu Jul 6 18:59:37 CEST 2006


On 7/5/06, Talin <talin at acm.org> wrote:
>
> Brett Cannon wrote:
> > On 7/5/06, Michael Chermside <mcherm at mcherm.com> wrote:
> >> If you were using capabilities, you would need to ensure that
> >> restricted interpreters could only get the file object that they
> >> were given. But then _all_ of these fancy versions of the
> >> restrictions would be immediately supported: it would be up to the
> >> users to create secure wrappers implementing the specific
> >> restrictions desired.
> >
> > I agree.  I would prefer this way of doing it.  But as I have said,
> making
> > sure that 'file' does not get out into the wild is tough.
>
> I seem to recall someone mentioned earlier in this discussion the notion
> of somehow throwing an exception when sandboxed code attempts to push a
> file reference onto the interpreter stack.


That was AMK.

I'm not an expert in these matters, so perhaps what I am going to say
> will make no sense, but here goes:
>
> What if there were two copies of the evaluator function. One copy would
> be a slightly slower 'checked' function, that would test all objects for
> a 'check' bit. Any attempt to evaluate a reference to an object with a
> check bit set would throw an exception.


The other eval function would be the 'unchecked' version that would run
> at full speed, just like it does today.
>
> Transitioning from the checked to the unchecked state could only be done
> via C code. So the 'file' wrapper, for example, would switch over to the
> unchecked interpreter before calling the actual methods of 'file'. That
> C wrapper might also check the current permission state to see what
> operations were legal.


So add the proper checks in Python/ceval.c:call_function() to check for this
flag on every object passed in that is called?

Note that whenever a C function sets the interpreter state to
> 'unchecked', that fact is saved on the stack, so that when the function
> returns, the previous state is restored. The function for setting the
> interpreter state is something like PyCall_Unchecked( ... ), which
> restores the interpreter state back to where it was.
>
> Transitioning from unchecked to checked is trickier. Specifically, you
> don't want to ever run sandboxed code in the unchecked state - this is a
> problem for generators, callbacks, and so on. I can think of two
> approaches to handling this:
>
> First approach is to mark all sandboxed code with a bit indicating the
> code is untrusted. Any attempt to call or otherwise invoke a function
> that has this bit set would throw the interpreter into the 'checked'
> state. (Note that transitioning the other way is *not* automatic - i.e.
> calling trusted code does not automatically transition to unchecked
> state.)
>
> The approach is good because it means that if you have intermediary code
> between the wrapper and the sandboxed code, the interpreter still does
> the right thing - it sets the interpreter into checked state.
>
> One problem is how to restore the 'unchecked' state when a function call
> returns. Probably you would have to build this into the code that does
> the state transition.
>
> If marking the sandboxed code isn't feasible, then you'd have to have
> the wrapper objects wrap all of the callbacks with code that goes to
> checked state before calling the callbacks. This means finding all the
> possible holes - however, I suspect that there are far fewer such holes
> than trying to hide all possible 'file' methods. However, one advantage
> of doing this is that restoring the 'unchecked' state after the call
> returns is fairly straightforward.
>
> The advantage of the this whole approach is that once you set the
> 'check' bit on 'file', it doesn't matter whether 'file' gets loose or
> not - you can't do anything with it without throwing an exception.
> Moreover, it also doesn't matter what code path you go through to access
> it. Only C code that flips the interpreter into unchecked state can call
> methods on 'file' without blowing up.
>
> So essentially, what I propose is to define a simple security primitive
> - which essentially comes down to checking a single bit - and use that
> as a basis to create more complex and subtle security mechanisms.


Right, but it does require that the proper verification function be turned
on so that the permission bit on 'file' is checked.  It kind of seems like
'rexec' and its f_restricted flag it set on execution frames, except you are
adding an object-level flag as well.

Either way, the trick is not fouling up switching between the two checking
functions.

-Brett
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.python.org/pipermail/python-dev/attachments/20060706/72e33923/attachment-0001.html 


More information about the Python-Dev mailing list