Skip to contentSkip to conversation

posix capabilities inheritance

DISCUSSION Andy Lutomirski 3 participants 4 messages Oct 24, 2003 2 messages not in the archive

lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

#2 lore raw

Thanks to everyone for making me realize just how complex / screwed up all of this is. I think it's fair to say that something should be fixed/changed, but I don't think that a quick patch is a good idea -- currently capabilities are so limited that there is little userspace use, and the use that exists is pretty much limited to Ernie's example. Witness the fact that my patch, as well as others, appear to break _nothing_. That means that we have the opportunity (2.7 timeframe here) to overhaul the system _once_.

At the risk of premature discussion, here are some thoughts:

1. pI is currently almost useless. If a process really wants a capability to be dropped after exec, it can drop it itself. So redefine pI to mean "these are the only capabilities that this process or its children may _ever_ hold."

2. fE is useless. It doesn't seem to have much of a point, and it just adds complexity. (e.g. look at Windows privileges. they start unenabled, and programs have to jump through hoops to use them. I see no security benefit.) So remove fE entirely.

3. The current use of the capability bits is not as fine-grained as it could be, and lacks the ability to restrict normal users. Redefine it as:

 Capability range             Function
 ----------------             -----------------
 <0                           On by default
 0-31                         Legacy bounds (see below)
 >=32                         Off by default

All of the current capabilities retain their current values. New capabilities above 32 can have a "legacy bound" associated with them, like this:

_u32 legacy_bounds[] = {...};

bool capable(int cap)

{
	if(x<32) return (cap in effective set);
	else return ( (legacy_bounds[cap] & (bits 0-31 of effective set))
	 && (cap in effective set) );
}

This ensures that existing programs that think they have dropped capabilities (using the old numbering) really have dropped those capabilities. New programs can do whatever they want, using the new numbering. Some random thoughts of what the "new" capabilities might look like (where CAP_USER_ means <0 and CAP_ADM_ means >=32):

Completely new things:
CAP_USER_FSUID /* permission checks can use fsuid */ CAP_USER_FSGID /* permission checks can use fsgid */ CAP_USER_FSWRITE /* can write files */
Saner version of existing features (CAP_SYS_ADMIN, anyone?) CAP_ADM_MOUNT_BLOCK /* can mount block devices */ CAP_ADM_MOUNT_NONE /* can mount things that are not block devices */

This could be very nice for administrators. Maybe a large block of capability space could be reserved for userspace use. (Make capabilities 64 bits? Fun!) Albert -- this fixes one of your complaints.

Capability evolution might look like this:
pI' = pI & fI
pP' = ((fP & X) | pP) & pI'
pE' = pP' & (pE|fP) & (only caps <0 if uid==0 and setuid nonroot)

By the way, even if people are scared of programs mishandling fP!=0, this still seems very useful just to adjust "per-user" rights.

If there's enough interest, I might try to implement this in the next few weeks and get it ready for 2.7. There's the risk that if any new scheme goes into a mainline development kernel, then people will start using it, and it will be impossible to change it, which is why I'm floating this around now. I don't see any reason to keep the current system, and, given that there is no de-facto *nix capability standard, Linux might as well have the best capability system out there.

Any thoughts/suggestions/flames?

Andy

#3 lore raw
Quoted 5 lines · Andy Lutomirski

1. pI is currently almost useless. If a process really wants a capability to be dropped after exec, it can drop it itself. So redefine pI to mean "these are the only capabilities that this process or its children may _ever_ hold."

A lot of the reasons why it looks useless is because we don't have filesystem support. The intent behind the POSIX capabilities system is that system administrator can control which privileges are conferred upon a program when it is exec'ed, as well as which privileges it is allowed to inherit. (The idea behind this is that even if the exec'ing process wishes to bequeath some super-user capabilities across an exec, unless that program has been audited and cleared by the site security officer as being cleared to accept that additional capability, it shouldn't be given it.) In addition, of course, the exec'ing process ought to be allowed to control what capabilities it is willing to bequeth.

Hence the capabilities inheritance algorithm of:

	pP' = fP | (fI & pI)
Quoted 4 lines

2. fE is useless. It doesn't seem to have much of a point, and it just adds complexity. (e.g. look at Windows privileges. they start unenabled, and programs have to jump through hoops to use them. I see no security benefit.) So remove fE entirely.

It has a lot of point. It's there for the same reason why just because you have the root password, you don't want to run as root all the time. (At least, sane people don't...) You might make mistakes.

A very basic rule of security is that you only use the least amount of privileges to get the job done. That way, if you make a mistake, and get tricked into accessing the wrong file (say, because you failed to check for .. in pathnames supplied by an untrusted user), you limit the amount of damage done because of your bone-headed mistake.

Is this more complexity? Yes. But security generally means more complexity. Deal with it. If you don't like, it you can just always run with fE set to fP, just as you just login as root and run with root privileges all the time. As long as you're careful, and never accidentally type the command rm -rf /, why bother with non-root accounts on a single-user machine?

(And if you really need help answering that question, then let's just agree to disagree, and remind me to never hire you to be a system administrator on any of my machines....)

Quoted 2 lines

3. The current use of the capability bits is not as fine-grained as it could be, and lacks the ability to restrict normal users.

No. Bad, bad, bad, bad, bad idea.

A lot of the rules of when capabilities can be dropped and inherited assume that capabilities enhance privileges, and do not take privileges away. A very different calculus emerges when you start restricting what a normal user can do, especially when this mixes with setuid (non-root) or setgid programs. A lot of programs don't do error checking, and if you can change basic assumptions of what works and doesn't work, you can actually cause security breaches where none previously existed.

One of the reasons why fP, fE, and fI are all set to zero was because in a previous attempt to try to emulate capabilities, the old "backwards compatibility" algorithm of how to treat setuid root programs allowed the exec'ing process to control what privileges could be inherited by a setuid root program. This was really bad, since it meant that a setuid root program could have some capabilities (such as filesystem descretionary access controls override), but not others, and a failure to check error returns (since after all a setuid root program would *always* succeed, right?), caused a security hole.

It is for similar reasons that chroot requires root privileges. One might think that restricting what part of the filesystem can be seen by a process shouldn't require root privileges, except that by changing the environment, it can fool setuid or setgid programs into doing the wrong thing.

So any scheme that restricts normal user privileges **must** have different access controls and different semantics than schemes which allow a process with superuser privileges from dropping some or all of its capabilities. It's fundamentally a different thing, and should not be conflated together. That way only lies madness. (And security holes.)

- Ted

#4 lore raw
Quoted 5 lines · Theodore Ts'o
Quoted 1 line · Andy Lutomirski

1. pI is currently almost useless. If a process really wants a capability to be dropped after exec, it can drop it itself. So redefine pI to mean "these are the only capabilities that this process or its children may _ever_ hold."

Quoted 11 lines

A lot of the reasons why it looks useless is because we don't have filesystem support. The intent behind the POSIX capabilities system is that system administrator can control which privileges are conferred upon a program when it is exec'ed, as well as which privileges it is allowed to inherit. (The idea behind this is that even if the exec'ing process wishes to bequeath some super-user capabilities across an exec, unless that program has been audited and cleared by the site security officer as being cleared to accept that additional capability, it shouldn't be given it.) In addition, of course, the exec'ing process ought to be allowed to control what capabilities it is willing to bequeth.

I agree completely. I'm just suggesting a different way of doing it. If a process is actually not trusted to have some capability, then it is presumably not trusted to use helper applications that have that capability. So make it impossible to use or _regain_ caps that are not in pI and fI.

On the other hand, if the exec'ing process wants to remove a capability without this restriction, then it can just drop it and then call exec. Hence:

pP' = (fP | pP) & pI'; (note pI_'_ -- fI restrictions still take effect here)

Quoted 26 lines

Hence the capabilities inheritance algorithm of:

	pP' = fP | (fI & pI)
Quoted 1 line

2. fE is useless. It doesn't seem to have much of a point, and it just adds complexity. (e.g. look at Windows privileges. they start unenabled, and programs have to jump through hoops to use them. I see no security benefit.) So remove fE entirely.

It has a lot of point. It's there for the same reason why just because you have the root password, you don't want to run as root all the time. (At least, sane people don't...) You might make mistakes.

A very basic rule of security is that you only use the least amount of privileges to get the job done. That way, if you make a mistake, and get tricked into accessing the wrong file (say, because you failed to check for .. in pathnames supplied by an untrusted user), you limit the amount of damage done because of your bone-headed mistake.

Is this more complexity? Yes. But security generally means more complexity. Deal with it. If you don't like, it you can just always run with fE set to fP, just as you just login as root and run with root privileges all the time. As long as you're careful, and never accidentally type the command rm -rf /, why bother with non-root accounts on a single-user machine?

Quoted 3 lines

(And if you really need help answering that question, then let's just agree to disagree, and remind me to never hire you to be a system administrator on any of my machines....)

We are both talking about the _file_ effective mask, right? I absolutely agree that pI is needed.

I've been programming Windows for a long time, and windows has a capability system. Essectially, a "privilege" (capability) is either present or not present, and, if present, either enabled or disabled. This enabled state corresponds to effectiveness in POSIX -- programs can freely change it. All capabilities are disabled by default (almost -- there's a pointless exception, of course). The result is that every program that uses a privileged function (e.g. change the time, restart, etc.) wraps that call with something that turns enables the capability at first, then disables it. This has no benefit -- a hijacked privileged program can still enable them, and the admin never sees this, because everything enables them. I'm not saying that the ability to disable capabilities is bad (e.g. single-process file servers absolutely require it).

Now let's throw fE into the equation. Will admins keep a list of which programs prefer their capabilities enabled by default? If I'm an admin, and I don't trust a program, I won't give it the capability _at all_. Just telling the program "I don't trust you, so please to do such-and-such unless you remember to enable the capability first" seems useless. I think a sensible capability system will choose a sane default at leave it there.

Quoted 6 lines
Quoted 3 lines

3. The current use of the capability bits is not as fine-grained as it could be, and lacks the ability to restrict normal users.

No. Bad, bad, bad, bad, bad idea.

I imagined that admins or users would remove almost everything from the inheritable mask so that the untrusted program is not given the change to screw up. I'm perfectly willing to drop that part, though.

I think that my proposal for extending super-user capabilities still has a lot of value, though. Things like CAP_SYS_ADMIN are far to wide in scope, and I think that can be fixed without breaking old applications.

Andy

#5 lore raw
Quoted 8 lines · Andy Lutomirski

I've been programming Windows for a long time, and windows has a capability system. [...] All capabilities are disabled by default (almost -- there's a pointless exception, of course). The result is that every program that uses a privileged function (e.g. change the time, restart, etc.) wraps that call with something that turns enables the capability at first, then disables it. This has no benefit -- a hijacked privileged program can still enable them, and the admin never sees this, because everything enables them.

Actually, it does have some benefits. If I do an open() somewhere else in the code, I know that it is not going to unintentionally use my elevated privileges. That's useful. Least privilege, and all that.