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

2006-07-11 Thread Talin
Brett Cannon wrote:
 Using a factory method callback, one could store the PyCodeObject in a C
 proxy object that just acts as a complete delegate, forwarding all method
 calls to the internally stored PyCodeObject.  That would work.
 
 
 For this initial implementation, though, I am not going to try to support
 this.   We can always add support like this later since it doesn't
 fundamentally break or change anything that is already planned.  Let's 
 focus
 on getting even more basic stuff working before we start to get too fancy.
 
 -Brett

Thinking about this some more, I've come up with a simpler idea for a 
generic wrapper class. The wrapper consists of two parts: a decorator to 
indicate that a given method is 'public', and a C 'guard' wrapper that 
insures that only 'public' members can be accessed.

So for example:

from Sandbox import guard, public

class FileProxy:
   # Public method, no decorator
   @public
   def read( length ):
  ...

   @public
   def seek( offset ):
  ...

   # Private method, no decorator
   def open( path ):
  ...

# Construct an instance of FileProxy, and wrap a
# guard object around it. Any attempt to access a non-public
# attribute will raise an exception (or perhaps simply report
# that the attribute doesn't exist.)
fh = guard( FileProxy() )

Now, from my point of view this *is* 'the basic stuff'. In other words, 
this right here is the fundamental sandbox mechanism, and everything 
else is built on top of it.

Now, the C 'guard' function is only a low-level means to insure that 
no-one can mess with the object; It is not intended to be the actual 
restriction policy itself. Those are placed in the wrapper classes, just 
as in your proposed scheme.

(This goes back to my basic premise, that a simple yes/no security 
feature can be used to build up much more complex and subtle security 
features.)

The only real complexity of this, as I see it, is that methods to 
references will have to be themselves wrapped.

In other words, if I say 'fh.read' without the (), what I get back can't 
be the actual read function object - that would be too easy to fiddle 
with. What I'd have to get is a wrapped version of the method that is a 
callable.

One relatively simply way to deal with this is to have the 'public' 
decorator create a C wrapper for the function object, and store it as an 
attribute of the function. The class wrapper then simply looks up the 
attribute, and if it has an attribute wrapper, returns that, otherwise 
it fails.

(Although, I've often wished for Python to have a variant of __call__ 
that could be used to override individual methods, i.e.:

 __call_method__( self, methodname, *args )

This would make the guard wrapper much easier to construct, since we 
could restrict the methods only to being called, and not allow 
references to methods to be taken.)

-- Talin
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-11 Thread Brett Cannon
On 7/10/06, Talin [EMAIL PROTECTED] wrote:
Brett Cannon wrote: Using a factory method callback, one could store the PyCodeObject in a C proxy object that just acts as a complete delegate, forwarding all method calls to the internally stored PyCodeObject.That would work.
 For this initial implementation, though, I am not going to try to support this. We can always add support like this later since it doesn't fundamentally break or change anything that is already planned.Let's
 focus on getting even more basic stuff working before we start to get too fancy. -BrettThinking about this some more, I've come up with a simpler idea for ageneric wrapper class. The wrapper consists of two parts: a decorator to
indicate that a given method is 'public', and a C 'guard' wrapper thatinsures that only 'public' members can be accessed.So for example:from Sandbox import guard, publicclass FileProxy:
 # Public method, no decoratorIs that supposed to say no decorator, even though there is one? 
 @public def read( length ):... @public def seek( offset ):... # Private method, no decorator def open( path ):...
# Construct an instance of FileProxy, and wrap a# guard object around it. Any attempt to access a non-public# attribute will raise an exception (or perhaps simply report# that the attribute doesn't exist.)
fh = guard( FileProxy() )But wouldn't you still need to protect things like object.__metaclasses__() so that you can't get access to the class and mutate it? And if you use a custom metaclass to avoid having object expose the reference you then would need to protect the metaclass.
I guess I would need to see an implementation to see if there is any way to get around security protections since it is still Python code.
Now, from my point of view this *is* 'the basic stuff'. In other words,this right here is the fundamental sandbox mechanism, and everythingelse is built on top of it.Now, the C 'guard' function is only a low-level means to insure that
no-one can mess with the object; It is not intended to be the actualrestriction policy itself. Those are placed in the wrapper classes, justas in your proposed scheme.(This goes back to my basic premise, that a simple yes/no security
feature can be used to build up much more complex and subtle securityfeatures.)Yeah, this tends to be true.-Brett
The only real complexity of this, as I see it, is that methods toreferences will have to be themselves wrapped.In other words, if I say 'fh.read' without the (), what I get back can'tbe the actual read function object - that would be too easy to fiddle
with. What I'd have to get is a wrapped version of the method that is acallable.One relatively simply way to deal with this is to have the 'public'decorator create a C wrapper for the function object, and store it as an
attribute of the function. The class wrapper then simply looks up theattribute, and if it has an attribute wrapper, returns that, otherwiseit fails.(Although, I've often wished for Python to have a variant of __call__
that could be used to override individual methods, i.e.: __call_method__( self, methodname, *args )This would make the guard wrapper much easier to construct, since wecould restrict the methods only to being called, and not allow
references to methods to be taken.)-- Talin___Python-Dev mailing listPython-Dev@python.org
http://mail.python.org/mailman/listinfo/python-devUnsubscribe: http://mail.python.org/mailman/options/python-dev/brett%40python.org

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-11 Thread Brett Cannon
On 7/11/06, Phillip J. Eby [EMAIL PROTECTED] wrote:
At 10:56 AM 7/11/2006 -0700, Brett Cannon wrote:On 7/10/06, Talin mailto:[EMAIL PROTECTED][EMAIL PROTECTED] wrote:(Although, I've often wished for Python to have a variant of __call__
that could be used to override individual methods, i.e.:__call_method__( self, methodname, *args )As with so many other things in this discussion, this was already invented
by the Zope folks just shy of a decade ago.;-)__call_method__ isactually a feature of ExtensionClasses, although you can of courseimplement it yourself now atop new-style classes.For other things that Zope has already done in the area of restricted
execution RD, see:http://svn.zope.org/Zope3/trunk/src/zope/security/untrustedinterpreter.txt?view=auto
The surrounding directory has other documents regarding their approach.I haven't been following this discussion closely, but a lot of the thingsmentioned in this thread seem to have a lot in common with stuff the Zope
folks have had in production for untrusted Python execution for some timenow, working with current versions of Python.It would be a shame toreinvent all the same wheels, especially since their code is nicely
documented complete with extensive doctests and explanations of the approach.Taking a proxy approach is just being discussed; it has not been decided as the proper solution. I have been considering just preventing direct 'file' access and using open() to act as a delegate for opening files. That approach has nothing to do with proxies.
-Brett
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-11 Thread Phillip J. Eby
At 11:35 AM 7/11/2006 -0700, Brett Cannon wrote:
On 7/11/06, Phillip J. Eby 
mailto:[EMAIL PROTECTED][EMAIL PROTECTED] wrote:
At 10:56 AM 7/11/2006 -0700, Brett Cannon wrote:
 On 7/10/06, Talin 
 mailto:[EMAIL PROTECTED]mailto:[EMAIL PROTECTED][EMAIL PROTECTED] 
 wrote:
 (Although, I've often wished for Python to have a variant of __call__
 that could be used to override individual methods, i.e.:
 
   __call_method__( self, methodname, *args )

As with so many other things in this discussion, this was already invented
by the Zope folks just shy of a decade ago.  ;-)  __call_method__ is
actually a feature of ExtensionClasses, although you can of course
implement it yourself now atop new-style classes.

For other things that Zope has already done in the area of restricted
execution RD, see:

http://svn.zope.org/Zope3/trunk/src/zope/security/untrustedinterpreter.txt?view=autohttp://svn.zope.org/Zope3/trunk/src/zope/security/untrustedinterpreter.txt?view=auto

The surrounding directory has other documents regarding their approach.

I haven't been following this discussion closely, but a lot of the things
mentioned in this thread seem to have a lot in common with stuff the Zope
folks have had in production for untrusted Python execution for some time
now, working with current versions of Python.  It would be a shame to
reinvent all the same wheels, especially since their code is nicely
documented complete with extensive doctests and explanations of the approach.

Taking a proxy approach is just being discussed; it has not been decided 
as the proper solution.

My point is more to the study of the requirements for untrusted execution, 
and what options are open within the existing CPython architecture.  Many 
aspects of the Zope untrusted interpreter do *not* involve proxies; there 
are quite a few simple types that Zope's untrusted interpreter allows 
direct access to.  See the section on Basic objects in the link above.

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-09 Thread Brett Cannon
On 7/8/06, Talin [EMAIL PROTECTED] wrote:
Brett Cannon wrote: On 7/7/06, Guido van Rossum [EMAIL PROTECTED] wrote: On 7/8/06, Ka-Ping Yee [EMAIL PROTECTED]
 wrote:  I'd like the answer to be yes.It sounded for a while like this  was not part of Brett's plan, though.Now i'm not so sure.It  sounds like you're also interested in having the answer be yes?
   Let's keep talking about and playing with more examples -- i think  they'll help us understand what goals we should aim for and what  pitfalls to anticipate before we nail down too many details.
 I'd like the answer to be no, because I don't believe that we can trust the VM to provide sufficient barriers. The old pre-2.2 restricted execution mode tried to do this but 
2.2 punched a million holes in it. Python isn't designed for this (it doesn't even enforce private attributes). I guess this is also the main reason I'm skeptical about capabilities for Python.
 My plan is no.As Guido said, getting this right isfeasibly questionable.I do not plan on trying to have security proxies or such implemented in Python code; it will need to be in C.If someone comes
 along and manages to find a way to make Python work without significantly changing the languages, great, and we can toss out my security implementation for that. But as of right now, I am not planning on making Python code safe to run in
 Python code.It might be possible for the code *outside* the sandbox to create newsecurity policies written in Python.Lets start with the concept of a generic protection wrapper - its a C
proxy object which can wrap around any Python object, and which canrestrict access to a specific set of methods. So for example:protected_object = protect(myObject, methods=set('open','close'))
'protect' creates a C proxy which restricts access to the object,allowing only those methods listed to be called.Now, lets create a security policy, written in Python. The policy isessentially a factory which creates wrapped objects:
class MyPolicy: # Ask the policy to create a file object def file( path, perms ):if perms == 'r': # Trivial example, a real proxy would be more # sophisticated, and probably configurable.
 return protect( file( path, perms ), methods=set('open', 'read', 'close') ) raise SecurityExceptionNow, when we create our sandbox, we pass in the policy:
sb = Sandbox( MyPolicy() )The sandbox calls 'protect' on the policy object, preventing it frombeing inspected or called inappropriately.Using a factory method callback, one could store the PyCodeObject in a C proxy object that just acts as a complete delegate, forwarding all method calls to the internally stored PyCodeObject. That would work.
For this initial implementation, though, I am not going to try to support this. We can always add support like this later since it doesn't fundamentally break or change anything that is already planned. Let's focus on getting even more basic stuff working before we start to get too fancy.
-Brett
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-08 Thread Brett Cannon
On 7/7/06, Talin [EMAIL PROTECTED] wrote:
Brett Cannon wrote: On 7/7/06, Guido van Rossum [EMAIL PROTECTED] wrote: On 7/7/06, Brett Cannon [EMAIL PROTECTED]
 wrote:  I guess I am just not seeing where your approach is better than preventing  the constructor in 'file' and having open() return the 'file' object or  proxy object.With your approach 'file' would be flagged, but with the
  other you just put the same check in 'file's constructor.With both you  would probably also want open() to be a factory function anyway.So I don't  see where you gain simplicity or more security.It seems like you are
  pushing the check into the eval loop, but you still require the flagging of  objects as unsafe.Going with the other two proposals you don't burden the
  eval loop with the check but the objects that you would have flagged in the  first place.   It just seems like we are pushing around the flagging of unsafe stuff
 and  that doesn't feel like it buys us much. At the risk of repeating someone's point or making no sense (I'm only following this with half an ear) I would like to point out that as
 long as there's C code involved, we can have the equivalent of private constructors in C++/Java. This means classes that cannot be constructed by calling the class from Python. C code has to use some
 other API to create an instance, bypassing this check. It seems reasonable to me, even if most popular types *can* be constructed. There are other types that have this property, e.g. list iterators.
 Try type(iter(list()))(). Good point.C code could circumvent the bit check by doing all of the work behind the scenes without pushing the object on the stack.But if the
 check is in the C code for the object itself it is much harder to get around. -BrettI may be confused (I usually am), but I think you are misinterpretingGuido's point. I think what he is saying is not you should beware of C
code getting around the checks but rather he is pointing out that Ccode that gets around the checks can be a useful part of the system -thus the notion of private constructors, in other words methods for
creating an object that are normally inaccessible to Python code.As to your point: I think I am beginning to understand, so let mereiterate to see if I have it right.In the scenario you describe, the open() function is replaced by a
function that returns a proxy that has limits on what it is allowed todo. (Ideally, it would return one of several different types of proxiesbased on the input file path - so a file handle to /tmp would have a
different set of restrictions than a file handle to /home/me.)Yep. 
Somewhere inside this proxy is the 'real' file handle. We need to insurethat the proxy is air-tight, so that it can't 'leak' to the outsideworld. (The original motivation for my scheme was the belief thatair-tightness couldn't be achieved, however the point that Guido makes
above is beginning to make me believe otherwise.)The proxy is air-tight by being written in C and holding on to the reference to the file object in the struct of the proxy.
The proxy also has to be able to support all of the methods that theregular file handle can - because we're going to want to pass that proxyover to other subsystems that don't know that they are dealing with a
proxy - such as XML parsers or config file readers. Because thepermission checks are implemented in the proxy, this means that librarycode using the proxy has exactly the same access restrictions as thesandboxed code.
Yep. Or at least the methods that will be needed (e.g., a read-only proxy only needs the read()-like methods). 
If you want any library code to be able to have additional privilegesbeyond what the sandboxed code can do, you'll need to pass them the'real' file handle, something which can only be done by either theproxy, or by a C 'gateway' function. This is not a problem as long as
the library code doesn't attempt to hold on to the file handle(otherwise then the sandboxed code could potentially grab it from thelibrary's objects.) So for any case where a library needs additionalprivileges, you'll need to add additional methods to the proxy or create
the gateway functions to deal with that.Basically. A library shouldn't need additional abilities. If they do they are not following the security restrictions so it shouldn't work. It should be a rarity that a library really needs to sneak around the security of a proxy to the point of we don't even consider it. Duck typing should be enough for this not to be a problem.
So if it truly is the case that the file handle cannot leak into theoutside world, then you are correct - there's no need to put a check
into the interpreter. My motivation was based on the belief that therewould always be a way to get around that, so instead the notion was to'poison' these protected objects so that even if they did leak out,
attempting to use them in a restricted environment would fail.At some point we have to trust something. If you control the 

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

2006-07-08 Thread Brett Cannon
On 7/7/06, Guido van Rossum [EMAIL PROTECTED] wrote:
On 7/8/06, Ka-Ping Yee [EMAIL PROTECTED] wrote: The situation you're describing here is a classic case of one component keeping a closely held authority while using it to
 provide some limited capability to some other component.This comes up quite often when you're trying to write secure code. If you want to be able to write that subsystem in Python, then
 we will need a way to create airtight Python objects (i.e. objects that only leak what they explicitly choose to leak). So this goes back to the big question of goals: Do we want to be able to protect one piece of Python code
 from another piece of Python code? I'd like the answer to be yes.It sounded for a while like this was not part of Brett's plan, though.Now i'm not so sure.It sounds like you're also interested in having the answer be yes?
 Let's keep talking about and playing with more examples -- i think they'll help us understand what goals we should aim for and what pitfalls to anticipate before we nail down too many details.
I'd like the answer to be no, because I don't believe that we cantrust the VM to provide sufficient barriers. The old pre-2.2restricted execution mode tried to do this but 2.2 punched a millionholes in it. Python isn't designed for this (it doesn't even enforce
private attributes). I guess this is also the main reason I'mskeptical about capabilities for Python.My plan is no. As Guido said, getting this right is feasibly questionable. I do not plan on trying to have security proxies or such implemented in Python code; it will need to be in C. If someone comes along and manages to find a way to make Python work without significantly changing the languages, great, and we can toss out my security implementation for that.
But as of right now, I am not planning on making Python code safe to run in Python code.-Brett
Guido van Rossum (home page: http://www.python.org/~guido/)___Python-Dev mailing list
Python-Dev@python.orghttp://mail.python.org/mailman/listinfo/python-devUnsubscribe: 
http://mail.python.org/mailman/options/python-dev/brett%40python.org
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-08 Thread Talin
Brett Cannon wrote:
 On 7/7/06, Guido van Rossum [EMAIL PROTECTED] wrote:
 On 7/8/06, Ka-Ping Yee [EMAIL PROTECTED] wrote:
  I'd like the answer to be yes.  It sounded for a while like this
  was not part of Brett's plan, though.  Now i'm not so sure.  It
  sounds like you're also interested in having the answer be yes?
 
  Let's keep talking about and playing with more examples -- i think
  they'll help us understand what goals we should aim for and what
  pitfalls to anticipate before we nail down too many details.

 I'd like the answer to be no, because I don't believe that we can
 trust the VM to provide sufficient barriers. The old pre-2.2
 restricted execution mode tried to do this but 2.2 punched a million
 holes in it. Python isn't designed for this (it doesn't even enforce
 private attributes). I guess this is also the main reason I'm
 skeptical about capabilities for Python.
 
 My plan is no.  As Guido said, getting this right is  feasibly
 questionable.  I do not plan on trying to have security proxies or such
 implemented in Python code; it will need to be in C.  If someone comes 
 along
 and manages to find a way to make Python work without significantly 
 changing
 the languages, great, and we can toss out my security implementation for
 that.
 
 But as of right now, I am not planning on making Python code safe to run in
 Python code.

It might be possible for the code *outside* the sandbox to create new 
security policies written in Python.

Lets start with the concept of a generic protection wrapper - its a C 
proxy object which can wrap around any Python object, and which can 
restrict access to a specific set of methods. So for example:

protected_object = protect(myObject, methods=set('open','close'))

'protect' creates a C proxy which restricts access to the object, 
allowing only those methods listed to be called.

Now, lets create a security policy, written in Python. The policy is 
essentially a factory which creates wrapped objects:

class MyPolicy:
   # Ask the policy to create a file object
   def file( path, perms ):
  if perms == 'r':
 # Trivial example, a real proxy would be more
 # sophisticated, and probably configurable.
 return protect( file( path, perms ),
 methods=set('open', 'read', 'close') )
 raise SecurityException

Now, when we create our sandbox, we pass in the policy:

sb = Sandbox( MyPolicy() )

The sandbox calls 'protect' on the policy object, preventing it from 
being inspected or called inappropriately.

-- Talin
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Talin
Brett Cannon wrote:
 On 7/5/06, Talin [EMAIL PROTECTED] wrote:
 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?

Right. I also realized that you would need to add code that propagates 
the checked bit from the class to any instances of the class. So 
whenever you call a class to create an object, if the class has the 
checked bit, the instance will have it set as well.

 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

I wasn't aware of how rexec worked, but that seems correct to me.

Given a 'restricted' flag on a stack frame, and one on the object as 
well, then the code for checking for permission violations is nothing 
more than:

if (object.restricted  exec_frame.restricted)
raise SecurityException

In particular, there's no need to call a function to check a permission 
level or access rights or anything of the sort - all that stuff is 
implemented at a higher level.

By making the check very simple, it can also be made very fast. And by 
making it fast, we can afford to call it a lot - for every operation in 
fact.

And if we can call it for every operation, then we don't have to spend 
time hunting down all of the possible loopholes and ways in which 'file' 
or other restricted objects might be accessed.

Originally I had thought to simply add a check like the above into the 
interpreter. However, that would mean that *all* code, whether 
restricted or not, would have to pay the (slight) performance penalty of 
checking that flag. So instead, I thought it might be more efficient to 
have two different code paths, one with the check and one without. But 
all this is based on profound ignorance of the interpreter - there might 
be a hundred other, better ways to do this without having to create two 
versions of ceval.

Another interesting think about the check bit idea is that you can set 
it on any kind of object. For example, you could set it on individual 
methods of a class rather than the class as a whole. However, that's 
probably needlessly elaborate, since fine-grained access control will be 
much more elegantly achieved via trusted wrappers.

-- Talin
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Brett Cannon
On 7/6/06, Talin [EMAIL PROTECTED] wrote:
Brett Cannon wrote: On 7/5/06, Talin [EMAIL PROTECTED] wrote: 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?Right. I also realized that you would need to add code that propagatesthe checked bit from the class to any instances of the class. Sowhenever you call a class to create an object, if the class has the
checked bit, the instance will have it set as well. 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. -BrettI wasn't aware of how rexec worked, but that seems correct to me.Given a 'restricted' flag on a stack frame, and one on the object aswell, then the code for checking for permission violations is nothing
more than:if (object.restricted  exec_frame.restricted)raise SecurityExceptionIn particular, there's no need to call a function to check a permissionlevel or access rights or anything of the sort - all that stuff is
implemented at a higher level.By making the check very simple, it can also be made very fast. And bymaking it fast, we can afford to call it a lot - for every operation infact.And if we can call it for every operation, then we don't have to spend
time hunting down all of the possible loopholes and ways in which 'file'or other restricted objects might be accessed.Not true. You have to set this object restriction flag, right? What happens if you don't set it on all of the proper classes/types? You end up in the exact same situation you are with crippling; making sure you cover your ass with what you flag as unsafe else you risk having something get passed you.
Originally I had thought to simply add a check like the above into theinterpreter. However, that would mean that *all* code, whether
restricted or not, would have to pay the (slight) performance penalty ofchecking that flag. So instead, I thought it might be more efficient tohave two different code paths, one with the check and one without. But
all this is based on profound ignorance of the interpreter - there mightbe a hundred other, better ways to do this without having to create twoversions of ceval.Yeah, keep it simple, especially when it comes to 
ceval.c . Another interesting think about the check bit idea is that you can set
it on any kind of object. For example, you could set it on individualmethods of a class rather than the class as a whole. However, that'sprobably needlessly elaborate, since fine-grained access control will be
much more elegantly achieved via trusted wrappers.Yeah, that seems a bit extreme.-Brett
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Talin
Brett Cannon wrote:
 On 7/6/06, Talin [EMAIL PROTECTED] wrote:
 And if we can call it for every operation, then we don't have to spend
 time hunting down all of the possible loopholes and ways in which 'file'
 or other restricted objects might be accessed.
 
 Not true.  You have to set this object restriction flag, right?  What
 happens if you don't set it on all of the proper classes/types?  You end up
 in the exact same situation you are with crippling; making sure you cover
 your ass with what you flag as  unsafe else you risk having something get
 passed you.

But that's a much simpler problem.

With the restricted flag, it isn't just *your* code that is prevented 
from using 'file' - it's *all* code. Only approved gateways that remove 
the restriction (by setting the interpreter state) can perform 
operations on file objects without blowing up.

This means that if you call some random library function that attempts 
to open a file, it won't work, because the random library function is 
still running in restricted mode.

Similarly, if you have a reference to some externally created object 
that has a reference to a file (or the file class) somewhere in it's 
inheritance hierarchy, any attempt to access that object will fail.

Without this, you would have to chase down every bit of library code 
that opens file, or has a reference to a file.

What I am proposing shares some aspects of both the crippling and the 
capability model:

It's similar to crippling in the sense that you're protecting the object 
itself, not access to the object. So you avoid the problem of trying to 
figure out all of the possible ways an object can be accessed.

However, where it resembles capabilities is that its an 'all or nothing' 
approach - that is, you either have access to file, or you don't. Unlike 
the crippling model where fine-grained access control is implemented by 
modifying individual methods of the crippled object, in this scheme we 
cripple the object *entirely*, and then provide fine-grained access 
control via wrappers. Those wrappers, in turn, act just like 
capabilities - you can have different wrappers that have different sets 
of access permissions.

So it provides the advantage of the capability approach in that the set 
of restrictions can be extended or modified by writing new wrappers.

Thus, by providing an extremely simple but unbreakable check at the 
interpreter level, we can then write classes that build on top of that a 
richer and more sophisticated set of permissions, while still 
maintaining a strong barrier to unauthorized actions.

-- Talin
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Brett Cannon
On 7/7/06, Talin [EMAIL PROTECTED] wrote:
Brett Cannon wrote: On 7/6/06, Talin [EMAIL PROTECTED] wrote: And if we can call it for every operation, then we don't have to spend time hunting down all of the possible loopholes and ways in which 'file'
 or other restricted objects might be accessed. Not true.You have to set this object restriction flag, right?What happens if you don't set it on all of the proper classes/types?You end up
 in the exact same situation you are with crippling; making sure you cover your ass with what you flag asunsafe else you risk having something get passed you.But that's a much simpler problem.
With the restricted flag, it isn't just *your* code that is preventedfrom using 'file' - it's *all* code. Only approved gateways that removethe restriction (by setting the interpreter state) can perform
operations on file objects without blowing up.This means that if you call some random library function that attemptsto open a file, it won't work, because the random library function isstill running in restricted mode.
Right, but that happens with either approach being proposed anyway.
Similarly, if you have a reference to some externally created objectthat has a reference to a file (or the file class) somewhere in it'sinheritance hierarchy, any attempt to access that object will fail.Without this, you would have to chase down every bit of library code
that opens file, or has a reference to a file.Not true. With the library code running under a sandboxed interpreter the checks by either implementation will still be in effect. And if a proxy is returned by open() instead of 'file' with stuff crippled then the worries alleviated even further.
What I am proposing shares some aspects of both the crippling and thecapability model:
It's similar to crippling in the sense that you're protecting the objectitself, not access to the object. So you avoid the problem of trying tofigure out all of the possible ways an object can be accessed.
However, where it resembles capabilities is that its an 'all or nothing'approach - that is, you either have access to file, or you don't. Unlikethe crippling model where fine-grained access control is implemented by
modifying individual methods of the crippled object, in this scheme wecripple the object *entirely*, and then provide fine-grained accesscontrol via wrappers. Those wrappers, in turn, act just likecapabilities - you can have different wrappers that have different sets
of access permissions.So it provides the advantage of the capability approach in that the setof restrictions can be extended or modified by writing new wrappers.Thus, by providing an extremely simple but unbreakable check at the
interpreter level, we can then write classes that build on top of that aricher and more sophisticated set of permissions, while stillmaintaining a strong barrier to unauthorized actions.
I guess I am just not seeing where your approach is better than preventing the constructor in 'file' and having open() return the 'file' object or proxy object. With your approach 'file' would be flagged, but with the other you just put the same check in 'file's constructor. With both you would probably also want open() to be a factory function anyway. So I don't see where you gain simplicity or more security. It seems like you are pushing the check into the eval loop, but you still require the flagging of objects as unsafe. Going with the other two proposals you don't burden the eval loop with the check but the objects that you would have flagged in the first place.
It just seems like we are pushing around the flagging of unsafe stuff and that doesn't feel like it buys us much.-Brett
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Guido van Rossum
On 7/7/06, Brett Cannon [EMAIL PROTECTED] wrote:
 I guess I am just not seeing where your approach is better than preventing
 the constructor in 'file' and having open() return the 'file' object or
 proxy object.  With your approach 'file' would be flagged, but with the
 other you just put the same check in 'file's constructor.  With both you
 would probably also want open() to be a factory function anyway.  So I don't
 see where you gain simplicity or more security.  It seems like you are
 pushing the check into the eval loop, but you still require the flagging of
 objects as unsafe.  Going with the other two proposals you don't burden the
 eval loop with the check but the objects that you would have flagged in the
 first place.

 It just seems like we are pushing around the flagging of unsafe stuff and
 that doesn't feel like it buys us much.

At the risk of repeating someone's point or making no sense (I'm only
following this with half an ear) I would like to point out that as
long as there's C code involved, we can have the equivalent of private
constructors in C++/Java. This means classes that cannot be
constructed by calling the class from Python. C code has to use some
other API to create an instance, bypassing this check. It seems
reasonable to me, even if most popular types *can* be constructed.
There are other types that have this property, e.g. list iterators.
Try type(iter(list()))().

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Brett Cannon
On 7/7/06, Guido van Rossum [EMAIL PROTECTED] wrote:
On 7/7/06, Brett Cannon [EMAIL PROTECTED] wrote: I guess I am just not seeing where your approach is better than preventing the constructor in 'file' and having open() return the 'file' object or
 proxy object.With your approach 'file' would be flagged, but with the other you just put the same check in 'file's constructor.With both you would probably also want open() to be a factory function anyway.So I don't
 see where you gain simplicity or more security.It seems like you are pushing the check into the eval loop, but you still require the flagging of objects as unsafe.Going with the other two proposals you don't burden the
 eval loop with the check but the objects that you would have flagged in the first place. It just seems like we are pushing around the flagging of unsafe stuff and that doesn't feel like it buys us much.
At the risk of repeating someone's point or making no sense (I'm onlyfollowing this with half an ear) I would like to point out that aslong as there's C code involved, we can have the equivalent of private
constructors in C++/Java. This means classes that cannot beconstructed by calling the class from Python. C code has to use someother API to create an instance, bypassing this check. It seemsreasonable to me, even if most popular types *can* be constructed.
There are other types that have this property, e.g. list iterators.Try type(iter(list()))().Good point. C code could circumvent the bit check by doing all of the work behind the scenes without pushing the object on the stack. But if the check is in the C code for the object itself it is much harder to get around.
-Brett
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Nick Coghlan
Brett Cannon wrote:
  Good point.  C code could circumvent the bit check by doing all of the 
 work behind the scenes without pushing the object on the stack.  But if 
 the check is in the C code for the object itself it is much harder to 
 get around.

C code can circumvent the bit check by calling fopen() directly and pushing 
something onto the stack that isn't even recognised by the interpreter as a 
file object :)

You *have* to trust C code completely before importing it, because it has 
access to the platform C library and can do whatever the heck it wants.

Cheers,
Nick.

-- 
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---
 http://www.boredomandlaziness.org
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Brett Cannon
On 7/7/06, Nick Coghlan [EMAIL PROTECTED] wrote:
Brett Cannon wrote:Good point.C code could circumvent the bit check by doing all of the work behind the scenes without pushing the object on the stack.But if the check is in the C code for the object itself it is much harder to
 get around.C code can circumvent the bit check by calling fopen() directly and pushingsomething onto the stack that isn't even recognised by the interpreter as afile object :)
Right, but you can take measures to prevent accidental circumvention. You *have* to trust C code completely before importing it, because it has
access to the platform C library and can do whatever the heck it wants.Yep.-Brett 
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Talin
Brett Cannon wrote:
 On 7/7/06, Guido van Rossum [EMAIL PROTECTED] wrote:
 

 On 7/7/06, Brett Cannon [EMAIL PROTECTED] wrote:
  I guess I am just not seeing where your approach is better than
 preventing
  the constructor in 'file' and having open() return the 'file' object or
  proxy object.  With your approach 'file' would be flagged, but with the
  other you just put the same check in 'file's constructor.  With both 
 you
  would probably also want open() to be a factory function anyway.  So I
 don't
  see where you gain simplicity or more security.  It seems like you are
  pushing the check into the eval loop, but you still require the 
 flagging
 of
  objects as unsafe.  Going with the other two proposals you don't burden
 the
  eval loop with the check but the objects that you would have flagged in
 the
  first place.
 
  It just seems like we are pushing around the flagging of unsafe stuff
 and
  that doesn't feel like it buys us much.

 At the risk of repeating someone's point or making no sense (I'm only
 following this with half an ear) I would like to point out that as
 long as there's C code involved, we can have the equivalent of private
 constructors in C++/Java. This means classes that cannot be
 constructed by calling the class from Python. C code has to use some
 other API to create an instance, bypassing this check. It seems
 reasonable to me, even if most popular types *can* be constructed.
 There are other types that have this property, e.g. list iterators.
 Try type(iter(list()))().
 
 
 
 Good point.  C code could circumvent the bit check by doing all of the work
 behind the scenes without pushing the object on the stack.  But if the 
 check
 is in the C code for the object itself it is much harder to get around.
 
 -Brett

I may be confused (I usually am), but I think you are misinterpreting 
Guido's point. I think what he is saying is not you should beware of C 
code getting around the checks but rather he is pointing out that C 
code that gets around the checks can be a useful part of the system - 
thus the notion of private constructors, in other words methods for 
creating an object that are normally inaccessible to Python code.

As to your point: I think I am beginning to understand, so let me 
reiterate to see if I have it right.

In the scenario you describe, the open() function is replaced by a 
function that returns a proxy that has limits on what it is allowed to 
do. (Ideally, it would return one of several different types of proxies 
based on the input file path - so a file handle to /tmp would have a 
different set of restrictions than a file handle to /home/me.)

Somewhere inside this proxy is the 'real' file handle. We need to insure 
that the proxy is air-tight, so that it can't 'leak' to the outside 
world. (The original motivation for my scheme was the belief that 
air-tightness couldn't be achieved, however the point that Guido makes 
above is beginning to make me believe otherwise.)

The proxy also has to be able to support all of the methods that the 
regular file handle can - because we're going to want to pass that proxy 
over to other subsystems that don't know that they are dealing with a 
proxy - such as XML parsers or config file readers. Because the 
permission checks are implemented in the proxy, this means that library 
code using the proxy has exactly the same access restrictions as the 
sandboxed code.

If you want any library code to be able to have additional privileges 
beyond what the sandboxed code can do, you'll need to pass them the 
'real' file handle, something which can only be done by either the 
proxy, or by a C 'gateway' function. This is not a problem as long as 
the library code doesn't attempt to hold on to the file handle 
(otherwise then the sandboxed code could potentially grab it from the 
library's objects.) So for any case where a library needs additional 
privileges, you'll need to add additional methods to the proxy or create 
the gateway functions to deal with that.

So if it truly is the case that the file handle cannot leak into the 
outside world, then you are correct - there's no need to put a check 
into the interpreter. My motivation was based on the belief that there 
would always be a way to get around that, so instead the notion was to 
'poison' these protected objects so that even if they did leak out, 
attempting to use them in a restricted environment would fail.

Moreover, there would be no need to even bother preventing such leakage 
- you wouldn't have to change the behavior of __subclasses__ and so on - 
which means that the 'restricted' environment would look nearly 
identical to the normal Python programming environment, i.e. you are 
free to inspect and use __subclasses__ or any other inspection feature 
as long as the result doesn't contain any poisoned objects.

(BTW, I'm going to dub my idea the 'poisoned object' scheme, just so we 
have a label.)

While I was typing this, I did realize a drawback to 

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

2006-07-07 Thread Ka-Ping Yee
On Fri, 7 Jul 2006, Talin wrote:
 While I was typing this, I did realize a drawback to poisoned objects,
 which I will illustrate by the following example:

 Suppose we want to grant to the sandboxed program permission to read and
 write cofiguration properties. We don't want to give them arbitrary
 write access to the file, instead we want to force the sandbox code to
 only access that file by setting and getting properties.

 This is an example where a subsystem would require elevated privileges
 compared to the main program - the config file reader / writer needs to
 be able to read  write the file as a text stream, but we don't want to
 allow the sandboxed program to just write arbitrary data to it.

The situation you're describing here is a classic case of one
component keeping a closely held authority while using it to
provide some limited capability to some other component.  This
comes up quite often when you're trying to write secure code.

If you want to be able to write that subsystem in Python, then
we will need a way to create airtight Python objects (i.e. objects
that only leak what they explicitly choose to leak).

So this goes back to the big question of goals:

Do we want to be able to protect one piece of Python code
from another piece of Python code?

I'd like the answer to be yes.  It sounded for a while like this
was not part of Brett's plan, though.  Now i'm not so sure.  It
sounds like you're also interested in having the answer be yes?

Let's keep talking about and playing with more examples -- i think
they'll help us understand what goals we should aim for and what
pitfalls to anticipate before we nail down too many details.


-- ?!ng
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-07 Thread Guido van Rossum
On 7/8/06, Ka-Ping Yee [EMAIL PROTECTED] wrote:
 The situation you're describing here is a classic case of one
 component keeping a closely held authority while using it to
 provide some limited capability to some other component.  This
 comes up quite often when you're trying to write secure code.

 If you want to be able to write that subsystem in Python, then
 we will need a way to create airtight Python objects (i.e. objects
 that only leak what they explicitly choose to leak).

 So this goes back to the big question of goals:

 Do we want to be able to protect one piece of Python code
 from another piece of Python code?

 I'd like the answer to be yes.  It sounded for a while like this
 was not part of Brett's plan, though.  Now i'm not so sure.  It
 sounds like you're also interested in having the answer be yes?

 Let's keep talking about and playing with more examples -- i think
 they'll help us understand what goals we should aim for and what
 pitfalls to anticipate before we nail down too many details.

I'd like the answer to be no, because I don't believe that we can
trust the VM to provide sufficient barriers. The old pre-2.2
restricted execution mode tried to do this but 2.2 punched a million
holes in it. Python isn't designed for this (it doesn't even enforce
private attributes). I guess this is also the main reason I'm
skeptical about capabilities for Python.

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-06 Thread Brett Cannon
On 7/5/06, Talin [EMAIL PROTECTED] wrote:
Brett Cannon wrote: On 7/5/06, Michael Chermside [EMAIL PROTECTED] 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 afile reference onto the interpreter stack.That was AMK. 
I'm not an expert in these matters, so perhaps what I am going to saywill make no sense, but here goes:What if there were two copies of the evaluator function. One copy wouldbe a slightly slower 'checked' function, that would test all objects for
a 'check' bit. Any attempt to evaluate a reference to an object with acheck bit set would throw an exception.
The other eval function would be the 'unchecked' version that would runat full speed, just like it does today.Transitioning from the checked to the unchecked state could only be donevia C code. So the 'file' wrapper, for example, would switch over to the
unchecked interpreter before calling the actual methods of 'file'. ThatC wrapper might also check the current permission state to see whatoperations 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 theinterpreter state is something like PyCall_Unchecked( ... ), whichrestores 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 aproblem for generators, callbacks, and so on. I can think of twoapproaches 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 functionthat 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 codebetween 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 callreturns. 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 havethe wrapper objects wrap all of the callbacks with code that goes tochecked state before calling the callbacks. This means finding all the
possible holes - however, I suspect that there are far fewer such holesthan trying to hide all possible 'file' methods. However, one advantageof 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 ornot - you can't do anything with it without throwing an exception.
Moreover, it also doesn't matter what code path you go through to accessit. Only C code that flips the interpreter into unchecked state can callmethods 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 thatas 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
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-05 Thread Michael Chermside
In response to Ka-Ping's comments on the subject of Resource Hiding
vs Resource Crippling, Brett says:

 It seems that your criticisms are aimed at resource crippling
 being a plug holes as needed but if you foul up you are screwed
 with resource hiding being more fix the fundamental issues and
 just don't present access to resources you don't want to give
 access to (or wrap accordingly).  And in general I agree with
 this assessment.  But I also realize that Python was not designed
 for security in mind and there seems to be new ways to get access
 to 'file'.  If I felt confident that I could find and hide 'file'
 as needed, I would go that route immediately.  But I don't think I
 can (and Armin has said this as well).

I agree completely. Resource Hiding (specifically, Capabilities)
has the cleanest concept, yet there are valid reasons to worry
about implementing it in Python. However, I would like to point out
one other advantage that capabilities has over Resource Crippling.

Resource Crippling implements the restrictions as changes in the
underlying C-level objects capable of performing dangerous operations.
That means that the restrictions are implemented by the interpreter.
The advantage is obvious: we can trust the interpreter. But the
disadvantages are (1) it's slow to fix (requires a bugfix release
followed by everyone in the world upgrading), and (2) it cannot be
extended by the user.

With resource crippling, you will need to decide just what kind of
restrictions the file type will implement. I believe you are
planning to restrict to a list of known filenames and known
directories for reading and for writing. (Actually, you said mode
string, but I presume that you won't maintain separate lists for
'r' and 'rb' modes.) Then there was discussion of whether the
directories ought to be recursive, whether the total number of
files opened ought to be restricted, whether the total size written
should be restricted, and even whether the size should be measured
in bytes or blocks. Such conversations could go on for a long time,
and in the end you must make some compromises.

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 really like this feature of capabilities: that they can be
extended (well, restricted) by the user, not just by the language
implementer. That's a powerful feature, and I don't want to give
it up. But on the other hand, I don't quite see how to maintain
it -- here are my best ideas, perhaps they will help.

Python already has one essential ingredient for capabilities:
unforgable references. But it fails in two other ways: having
secure data, and having no auxiliary means of accessing
objects.

Python's powerful introspection and visible implementation
(eg: __dict__) make it impossible to encapsulate data in an
object in a way that prevents malicious users from accessing
it. But that is actually surprisingly easy to fix. Just create
a new type (maybe a new metaclass), implemented in C, which
contains private data and a means to control access to it. You
would provide a dict which would be stored privately without
access from Python, and then provide methods and attributes
along with a Python function for evaluating access to each. The
type would ensure that the access test was evaluated with
access to the private dict before any method or attribute was
accessed. Such a construct is simple enough that I believe we
could implement it and be reasonably confident that it was
reliably secure. (I have played with this in the past and been
reasonably pleased with the results.) Adding restrictions would
then incur some performance penalties, but that seems
unproblematic.

That leaves the other problem: auxiliary means of accessing
objects. There are things like gc.get_objects(). In the special
case of file, which is a type that's also dangerous, there are
tricks like object().__class__.__subclasses__(). I would love
to believe that we could plug all of these holes, but experience
(rexec) proves otherwise. For something like sockets, I am
fairly sure that there's a clear bottleneck (the low-level
socket module), but still numerous existing libraries that use
this low-level module without being handed a capability.

So this is where my alternative plan starts to fall apart. Your
(Brett's) plan to use resource crippling for these kinds of
restrictions involves putting C code around all direct access
to files, sockets, or whatever resource is being accessed.
Perhaps instead of your C code doing the security checks
directly, it could make sure that the objects returned were
contained within the correct secure wrappers. That's OK so far
as it goes, but the C checks are per interpreter instance, so
how do we get them 

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

2006-07-05 Thread Greg Ewing
Michael Chermside wrote:

 That leaves the other problem: auxiliary means of accessing
 objects. There are things like gc.get_objects(). In the special
 case of file, which is a type that's also dangerous, there are
 tricks like object().__class__.__subclasses__().

My approach to that would be to not provide access to
these kinds of things via attributes, but via builtin
functions. E.g there wouldn't be a __subclasses__
attribute, but a subclasses() function. Then that
capability can be denied by not providing that
function.

--
Greg
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2006-07-05 Thread Talin
Brett Cannon wrote:
 On 7/5/06, Michael Chermside [EMAIL PROTECTED] 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.

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.

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.

-- Talin
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com