[Python-Dev] rexec.py unuseable (original) (raw)

Luke Kenneth Casson Leighton lkcl at lkcl.net
Thu Dec 18 17:57:48 EST 2003


On Thu, Dec 18, 2003 at 10:33:52PM +0100, Martin v. L?wis wrote:

Luke Kenneth Casson Leighton <lkcl at lkcl.net> writes:

> so, with the correct codebase reordering, a simple capabilities > based system can be added, the problem goes away. > > yes? I still don't see how it could,

neither do i: i believe that the best person you should address that concern to is to greg ewing, who raised this point:

 "The spirit behind my suggestion was to start thinking about
 ways in which functionality could be separated out so that
 this kind of special-casing for security purposes isn't
 needed."

in other words, i think he means that by restructuring the python codebase [and libraries? and its design?] it may be possible to avoid a need for adding ACLs at all.

and i believe he may be thinking along the lines of being able to permanently remove at run-time, under python-programmer-control, access to class from objects.

somehow.

and I believe I understand pretty well what kind of feature you propose. Restricted execution is just something completely different.

if you believe that, then i have evidently not explained myself clearly enough.

i'll give this one more shot. i'll be honest with you. if it's not obvious enough at the end of this message, i have to give up and stop, and say i tried but did not succeed in explaining this clearly enough to you, which is no failing on your part, but on mine.

anyway.

first i should outline the pseudo-code modifications needed to Python/ceval.c which are necessary, then after that i will outline what ACLs are needed.

what i can say that i haven't gone into detail on [in this message] is how the acls are obtained from the function object. e.g. in GetACLforFunction.

but it is worth mentioning that how the acls are associated with the function object (or any other object) is an implementation-specific issue NOT a design / specification issue.

static PyObject * call_function(PyObject ***pp_stack, int oparg) {

 PyObject **pfunc = (*pp_stack) - n - 1;
 PyObject *func = *pfunc;


/* caller function name on stack below?? probably not,
   but the principle is at least demonstrable */
PyObject *calling_func = GetCallerFunction(*pp_stack) - n - 2;
PyACL *acl_for_func = GetACLforFunction(func);

...
if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func,
                             PERMS_EXECUTE)
{
    return Exception("access denied");
}

...

}

also, to satisfy the requirements of "capabilities", a specific check to make it look like the object doesn't exist is added.

it's probably possible to have a look-up table on a per-opcode basis to look up the permissions to be checked against, which alleviates the need to put a call to check_permissions in every darn switch statement.

note the check for ACL being NULL: if it's NULL, that's assumed to mean "anything goes".

it also means that there's not much performance hit involved unless you actually add permissions.

case STORE_ATTR: if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func, PERMS_CAN_SEE) { return Exception("attrib doesn't exist"); } if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func, PERMS_WRITE) { return Exception("access denied"); } case DELETE_ATTR: if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func, PERMS_CAN_SEE) { return Exception("attrib doesn't exist"); } if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func, PERMS_WRITE+PERMS_DELETE) { return Exception("access denied"); } case LOAD_ATTR:

if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func,
                PERMS_CAN_SEE)
{
    return Exception("attrib doesn't exist");
}
if (acl_for_func != NULL && !check_permissions(calling_func, acl_for_func,
                PERMS_READ)
{
    return Exception("access denied");
}

basically, as long as code is only executed through eval_frame(), then it's likely to be the only place where access control checks are needed to be added.

basically, therefore, once calls to restrict operations are added, it's a matter of dropping the ACLs in at the right places.

that's how you achieve the same job as the rexec.py code.

you place an acl on builtins to restrict "all functions", DENY, EVERYTHING.

you place an acl on open() to restrict "all functions", DENY, FILE_WRITE.

you place an acl on open.class "all functions", DENY, CAN_SEE+FILE_WRITE

in fact you might even be able to get away with putting an acl on EVERY function right at the top-level.

but that, as described previously, means that it'd be necessary to get the "ACL-inheritance" to work properly, in order to avoid having to add a DENY FILE_WRITE acl to every single file operation capable of writing, for example.

so like i said, i believe it to be a relatively simple job to spec out how to add the means by which ACLs can be usefully evaluated.

what i CAN'T tell you is exactly WHERE every single ACL [needed to achieve the same results as rexec] have to be added [such that they will be usefully evaluated]: i simply don't have enough knowledge of the python codebase to do that on my own.

[unless someone was happy to pay me for long enough to find out, of course].

is this making sense at all?

in some ways, the longer this is left, the harder it is going to be to retrospectively bolt on.

there's an adage that says security cannot be easily added in, it has to be designed in from the start.

fortunately, i think there are a lot of smart people about :)

sincerely,

l.



More information about the Python-Dev mailing list