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

Yep.

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

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

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

At some point we have to trust something.  If you control the constructor, you shouldn't have to worry about anything getting out since you won't be able to make anything out of it.

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.

Yep.  I would like to minimize the impact on Python code and what abilities they have.

(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 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 only way to enforce this restriction is to re-wrap the 'real' file
handle - in other words, replace the 'file-like object' wrapper with a
'config-like object' wrapper.

Yep.  But that is just a general issue that you are going to have with any security system.  It all comes down to where you put your foot down in terms or restricting access to a resource.

Merely passing the poisoned file handle to 'config' doesn't work,
because 'config' doesn't know how to safely handle it (only the C
gateway code can shift the interpreter into a state where poisoned
objects can be handled safely.)

Passing the file-like proxy to 'config' doesn't work either, because the
proxy doesn't allow arbitrary writes.

The only thing that you really can do is write another wrapper - which
is exactly what you would have to do in the non-poison case.

Yep.

-Brett

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

Reply via email to