Re: [Python-Dev] In defense of Capabilities [was: doc for new restricted execution design for Python]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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