Re: updated proposal for cap in elf

Albert D. Cahalan (acahalan@cs.uml.edu)
Sun, 18 Apr 1999 18:59:34 -0400 (EDT)


David L. Parsley writes:
> On Sun, 18 Apr 1999, Albert D. Cahalan wrote:
>> David L. Parsley writes:
>>> On Sat, 17 Apr 1999, Albert D. Cahalan wrote:

[word-wrapped and generally cleaned up]

>>>> No, we need a set of bits (I'll call it "pm") that are always
>>>> added to pP. The set would be 0 for maximum security or normal
>>>> users, and ~0 for an admin on a more normal system. This might
>>>> help kill off the ugly hack in exec.c that pretends an executable
>>>> has fI and fE full if UID 0 runs it.
>>>
>>> Well, we should certainly get rid of all uid=0=special code;
>>> treatment for fI and fE should be configurable (under /proc
>>> somewhere?), as you'll want differnt defaults during development
>>> of a new, capability-enhanced distro.
>>
>> Just how configurable? You would need to have:
>>
>> 1. some bits that are 0
>> 2. some bits that are 1
>> 3. some bits that are 1 for root and 0 for others
>
> I'm just thinking of two options for default inheritance on
> a non-cap file:
>
> - inherit everything from parent process (compatible; root login
> must get all pI set)
> - inherit nothing from parent process (paranoid, takes work to
> set fI on all needed files)

Forgetting old_pm for the moment, that gives:

new_pP = fP | (fI & old_pI) | (old_pP & persistent_pP_config);

>> Ugh. You still don't kill the special treatment for root. This won't do.
>
> No, I think on login root should have all pI set, and root magic should be
> removed from the kernel.

You can and should remove the root magic from exec.c, but it has to stay
in other places. For example, a UID change using standard system calls
would have to drop some bits. We could have a Linux-specific system call
that would change UID without dropping bits.

>>>> new_pP = fP | (fI & old_pI) | old_pm;
>>>> if(new_pP & ~old_pM) return -EPERM; /* getting prohibited caps? */
>>>> if((new_pP & fm) != fm) return -EPERM; /* not enough? */
>>>
>>> OK, I think the min and max values are already implicit in the fI and fP,
>>
>> Please explain how to do this:
>>
>> 1. there exists a program called a.out that everyone can run
>> 2. there are two admins, avatar and super
>> 3. there a capability called CAP_X
>> 4. avatar can run everything with CAP_X
> so avatar has CAP_X in pI
>
>> 5. super only gets CAP_X when running a.out
> and super gets it from fP

That might be good enough in a courtroom, but I certainly don't want
to give CAP_X to every user on the system. Remember, super is an admin.
Requirement 0, the implied one, is that you can't just give away CAP_X
to everyone by putting it in fP. Normal users don't get special power
when they run the executable.

What this could be: the executable is a "safe rm" tool with the right
to move any file. One admin can always move files, while the other admin
only gets that power when using this one tool. Normal users are free to
use the tool of course.

>> Now add:
>>
>> 6. there is a restricted user that must _never_ get CAP_X
>> (the normal users get CAP_X via fP, but this user can't do that)
>
> OK, so you're wanting to add a mechanism whereby certain users _never_ get
> a certain cap, irrespective of fP and fI. I just don't see wide
> application for this; if you can give me some good concrete examples, that
> would help. Off hand, though, I think it would be most appropriate to
> control this in the filesystem as currently. I.e., CAP_X is obviously
> dangerous in the wrong hands; so most files w/ CAP_X in fP should be in
> some group to which only certain users belong, and world execute bit
> should be removed.

This is a major security problem. You now have to search the whole
filesystem and add ACLs to every file that might grant CAP_X.
(oh, we don't support ACLs anyway) If you encounter a file that
restricted users need to run without special power, you have a problem.
You could hack around the problem by keeping two copies of the executable
and using a script wrapper or $PATH difference maybe. This gross
solution would be very hard to maintain.

>> Add this too:
>>
>> 7. there is a buggy executable called b.out that must never get CAP_X
>> (avatar wants to run this executable)
>
> I don't understand this part; I would think just leave CAP_X out
> of fP and fI.

This part won't make sense until you solve the first problem correctly.

>>> [...] For the minimum, it's possible the binary will get caps
>>> from fP and fI that still won't be sufficient; I say just let it fail,
>>> much as 'chown bin myfile' always failed in the past.
>>
>> This is very bad. When 'chown bin myfile' always failed in the past,
>> every single privileged operation failed. With the new system, you
>> can have partial failures. This can really make a mess with programs
>> that expect to perform multiple operations.
>
> Again, I'd be interested in real-world examples of this.

Mostly this is a source of DoS attacks AFAIK. There have been security
problems caused by resource limits that kill a setuid process after it
has done only part of what it was going to do. I think that these exploits
will occur by accident when pI is partially full.

Also, I'm sure most people have an opinion about this:
Option 1: permission denied
Option 2: crashes and other confusing failures

> It seems like, in light of what I'm asking for, a better explanation of my
> fM proposal would be in order. So far, it seems like we've pretty much
> agreed that the 'shell' of an admin will have a pretty full pI. Now,
> let's say this admin uses some random mail reader to read his/her mail.
> The mail program has: fP=0, fI=0, fE=0; since all it needs are file
> rights. So obviously pP for the mail prog is 0. BUT, pI' = pI; so the
> mail program _does_ have a full pI. So somebody could send the admin a
> message with a buffer overflow exploit, which could then exec something
> with a bigger fI and do some damage.
>
> The same also applies to admin starting named(8) by hand. named(8) only
> inherits, say CAP_NET_BIND_SERVICE, but now has a full pI; so the named
> exploit is now nearly as potent as before. (assuming /bin/sh has a fairly
> large inheritable set)
>
> So basically what I'm saying is, I think it would be really useful to have
> a simple mechanism for constraining the inheritable set of the exec'ed
> process. Even though it's pP is small, pI can still do a lot of damage.

I mostly agree. I think that this mechanism should also restrict the
new pP and pE values.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/