Re: Bug in how capability inheritance is handled in "fs/exec.c", 2.3.99

From: Linda Walsh (law@sgi.com)
Date: Mon May 29 2000 - 14:47:40 EST


"Theodore Y. Ts'o" wrote:
>
> From: "Linda Walsh" <law@sgi.com>
> Date: Sun, 28 May 2000 18:28:34 -0700
>
> Initially I have something like 'UID=backup' that only starts with the
> ability to do CAP_DAC_READ_SEARCH in their permitted set, so lets
> assume that is bit 0 and we use an 8 bit mask.
>
> Part of the problem here really is a disconnect about what the overall
> model should be for capabilities. Now, I wasn't on the POSIX.1e
> committee (but Casey Schaufler apparently was, and I'd love to get his
> take on the situation), but my understanding of the POSIX.1e design
> goals was that they explicitly tried to decouple UID's and
> capaibilities.

---
	They *allow* the decouple -- for those sites that want to decouple
(all users get no permissions).  
They also *allow* the coupling for
those sites that want that policy.  Casey's my boss and designed the
IRIX system based on POXIX, so the formula I sent is directly from the
POSIX compliant IRIX implementation.  The idea is to leave the choice
of security policy to the end-user site.

> When you consider the bad habits of many Unix system administrators --- > running arbitrary commands as make: arbitrary Mail programs, GUI's, > downloading unknown and even downloading possibly untrusted .tar.gz > files from the net and running "compile and make" as root (shades of the > Love E-mail virus!), it seems reasonable to consider designs that > explicitly make this sort of thing impossible. --- The system would be configurable to make it possible. Again -- this is not something to be *forced* on the user, but the user can choose or not. If capabilities don't meet the end-customer's needs, they will just forgoe any capability usage and continue to use the root model -- obviously an even less secure choice. Trusted IRIX can be run with no capabitilities, but just with MAC. In fact 4.0 was cert'ed w/o capabitilities -- still had a root user. Even ACL's aren't required at the B1 (LSPP) level. I've been told they become required at the B3 level (currently no Common Criteria Protection Profiles have been written for the B2 or B3 levels).

> > That is, the whole concept of a "root shell" is prohibited by the POSIX > 1.e Draft 17 rules. In fact the shell's executable runs with (P,I,E) = > (0,0,0), so by default you don't run with any privileges whatever. > Instead, the backup program would have CAP_DAC_READ_SEARCH (and thus be > considered part of the TCB), and could enforce its own access control > policies about who was allowed to execute the program. --- I don't remember reading this as a requirement in the standard. It is certainly 1 valid configuration, but it may not be flexible for all users. For example, I might configure a user law-root on my home system (behind a corporate firewall) to be permitted all capabitilities but require a voice print or thumbprint and only accessible on the console ( in my bedroom) to use them. Default (effective) capabilties would always be 'none', so anyone obtaining access to that signon through other means wouldn't have those caps unless they could also be at the console where 'su' or 'login' can call my thumb-scan PAM module. That is still very secure for my situation.

> > It's a very different model --- almost VMS-like. It is however, much > more secure, especially in the presence of less-than-clueful human > operators. To use your example, of running 'UID=backup', you can be > sure just as the night follows the day that backup operators will run > all sorts of things that you never anticipated with CAP_DAC_READ_SEARCH > rasied. Including nethack, irc, gnapster, etc...... And trust me, you > probably don't want your backup operators running gnapster with > CAP_DAC_READ_SEARCH raised! :-) --- But none of those files should be in the trusted computing base (TCB), so all should have a CAPSET of (0,0,0) which would drop all capabilities automatically so it isn't a problem. The algorithm I propose solves this problem. Yet allows my machine, safe behind my firewall to be run in a more 'convenient' manner' -- meaning I can have all caps when running files in the TCB and just blindly set their CAPS to PIE=(0,all,all). Still complete safety against trojans.

> Right. And given how the Draft 17 doesn't allow this, I believe this > was intentional. --- It *does* allow it. The formula referenced in section 2 doesn't agree with the detailed description in section section 3. Furthermore, show me the where in POSIX it says that the I-set must be limited by the P-set. The code in setuid is wrong. It adds a limitation that, according to your reading, does not exist in POSIX. If that is true, then compute_cred() is wrong as it doesn't do that limitation. Either way there is a bug because it is *inconsistent*.

> The idea is that if a process wants to a capability to > be inherited across an exec, it needs to take explicit action to do so. > Note that if you have a capability set in the permitted capmask, you're > allowed to set that flag in the inherited capmask (Posix.1eD17 > 25.1.1) --- Correct. By the existing model we have: pP = some vector, say "0001", then PIE=(1,1,1) (by code in suid). Now when I execute file backup that has PIE=(0000, 1111, 0010), we have:

Example1: File inherited sets and Effective sets allowed to be > Permitted

I_1 = I_0 ;I_1=0001 P_1 = (P_f && X) | (I_F && I_0) ;P_1=0000 | 1111 & 0001 ;=> P_1 = 0001 E_1 = E_F && P_1 ;E_1= 0010 & 0001 ;E_1 = 0000 (broken,wrong -- you've ; lost all permissions)

Example2: File I-sets and E-sets must be equal to P-set (PIE=10,10,10)

I_1 = I_0 ;I_1=0001 P_1 = (P_f && X) | (I_F && I_0) ;P_1 = 0010 | (0010 & 0001) ;P_1 = 0011 E_1 = E_F && P_1 ;E_1 = 0010 & 0011 ;E_1 = 0010 (again broken & wrong, ; you've lost perm "0001"

In both examples, using the formula in section 2 results in incorrect behavior.

> > So in this model, you start with a completely unprivileged shell: > > P = 0 > I = 0 > E = 0 > > Now you run the backup program, which has (among possibly other bits) > the CAP_DAC_READ_SEARCH capability in its Permitted capmask: > > P = CAP_DAC_READ_SEARCH > I = 0 --- NO -- backup only needs CAP_RAW_IO to write to its tapedrive device. Normal users as well as user=backup can use the program to backup either their personal files or the system. That's the intent of the security policy. Otherwise *every* program that wants to run with privileges has to be *modified* to play with CAPS. This is *BAD*.

> > The backup program now does its own access controls to see whether or > not the person currently running the program is allowed to run it. If > it doesn't, it will abort. --- I don't want to modify tar or dump -- I'll just set the device permission bits on the tape drive to permit anyone read/write access and let DAC control access. I have DAC setup on my system to prohibit access by unauthorized users to other devices.

The existing paradigm is creating *alot* of work in rewriting existing code. This is *bad*.

> > Let's suppose that it needs read access to check some file; it can then > raise it explicitly: > > P = CAP_DAC_READ_SEARCH > I = 0 > E = CAP_DAC_READ_SEARCH > > Now, suppose the backup program runs tar, and wants tar to > inherit the CAP_DAC_READ_SEARCH capbility. Then the tar executable must > have the CAP_DAC_READ_SEARCH set in the inherited capability --- and > presumably the system administrator has made the decision that tar is > allowed to be trusted with this capability --- and then the backup > program explicitly raises that capability the inheritable capmask: > > P = CAP_DAC_READ_SEARCH | > I = CAP_DAC_READ_SEARCH }- = SET A > E = CAP_DAC_READ_SEARCH | --- Ahh...but now tar always runs with the ability to access everyone's files -- not just the administrator. This is *BAD*.

> > Now when the tar process executes, it will have the following > permissions: > > P = CAP_DAC_READ_SEARCH > I = CAP_DAC_READ_SEARCH > E = 0 --- It does? For pPIE=(1,0,1), f=(1,1,1), Example 3:

I_1 = I_0 ;I_1 = 0000 P_1 = (P_f && X) | (I_F && I_0) ;P_1 = 0001 | (0001 & 0000) ;P_1 = 0001 E_1 = E_F && P_1 ;E_1 = 0001 & 0001 ;E_1 = 0001 --- I assert that if someone can so easily make a mistake in their computations, the current security implementation is poorly designed.

> > Oops! But tar is a capability dumb program, which doesn't know how to > raise capabilities on its own. So if the system administrator sets > CAP_DAC_READ_SEARCH in the executable's effective capmask as well, then > when it is started, it will automatically be given CAP_DAC_READ_SEARCH: > > P = CAP_DAC_READ_SEARCH > I = CAP_DAC_READ_SEARCH > E = CAP_DAC_READ_SEARCH --- This is the same as "SET A" above.

> > It's a different model than the one which you were working with, but it > *does* work, and it is self-consistent. --- It's also, obviously, easily confused and it doesn't work when I want the progam to inherit capabilities unless you use inconsistent rules for the relationships between I,E and P. Inconsistent = more complex = bad.

> No, it's not a bug; it's spec'ed that way. See 25.1.1.4. You can > always clear a capability flag, but you're only supposed to be able to > set a capability flag (in any of the bitmasks) if you have that > capability flag in the permitted bitmask. --- But this says nothing about how 'init' or 'login' can set caps.

login, running with PIE=(all,all,all) now wants to "log in" user 'backup'. It forks and sets PIE=(01,all,01) (using 01 for DAC_READ_SEARCH). This works. So pPIE=(01,all,01) (note that Inheritable set is not a sub set of the Permitted set -- this is inconsistent). Now tar only wants to be able to access raw tape devices but wants to allow other bits like "DAC_READ_SEARCH) to be inherited in from the previous process fPIE(tar)=(10,1111,10), so it's inherited mask is also set to 'not a subset' of the Permitted or Effective sets).

So we have example 4: I_1 = I_0 ;I_1 = 1111 P_1 = (P_f && X) | (I_F && I_0) ;P_1 = 0010 | (1111 & 1111) ;P_1 = 1111 E_1 = E_F && P_1 ;E_1 = 0010 & 1111 ;E_1 = 0010

So now you have a Permitted set of "1111" (BAD -- another bug), and E=0010. Again -- not what was desired which was 0011.

Another broken example under the current system (the 4th so far).

> > Your proposed inheritance scheme (repeated above) does make a certain > amount of sense. Note, though, that if a program explicitly sets its > Inhertiable capmask to be equal to its Permitted capmask, then the Draft > 17 rules becomes almost identical to what you've asked for. --- Your terminology is unclear -- by "program explicity sets", do you mean the current process, the new process, or the PIE vector stored on disk for the file? The first choice has insufficient information to compute the new PIE vectors. The 2nd choice doesn't affect inheritance because it is done after inheritance was applied by exec, so assuming the third case:

Example 5 pPIE=(01,01,01), fPIE=(10,10,10):

I_1 = I_0 ;I_1 = 0001 P_1 = (P_f && X) | (I_F && I_0) ;P_1 = 0010 | (1111 & 01) ;P_1 = 0011 E_1 = E_F && P_1 ;E_1 = 0010 & 0011 ;E_1 = 0010 -- Still wrong, != 0011

> > The big difference is that the program has to explicitly allow the > inheritance by manually setting pI = pP. This harmonizes with the > section which you quoted from 25.1.1.2: --- Nope -- see Ex. 5 > > > The section says the resulting set depends on the cap-states of > > both. It then gets more specific, saying, each cap in the new _permitted_ > > (it is italicized in the original) may have been *forced* (my emphasis) 'on' > > by [1] the program (file) *or* [2] *inherited* *from* *the* *previous* > > *process* [meaning they had to be in the previous process's permitted > > and inherited set otherwise there would be nothing to inherit and...] > > ([3] if the capability attributes of the program allow the inheritance > > (i.e program's 'I' vector). > > Again, I think you're operating under a different model than what the > designers of Posix.1e envisioned, and that's why you're finding it > "broken". It's not broken, just different. (And hopefully Casey can > confirm this, since I Wasn't There. :-) --- The way I envision is the way Casey designed it into IRIX. He, I and other team members had considerable discussion on this point a few weeks ago -- and over the meaning of a file having no capability set. Out of that we came up with files with no set, have '0,0,0'. Thus files in the Trusted Computing Base (TCB) can be marked with 0,1111,0 unless they need privilege -- like, say, tar. So 'user=backup' runs with (01,all,01), tar has (10,all,10) (or 10,all,10), doesn't matter.

Under the current paradigm we have example 6:

I_1 = I_0 ;I_1 = 0001 P_1 = (P_f && X) | (I_F && I_0) ;P_1 = 0010 | (10 & 01) ;P_1 = 0010 E_1 = E_F && P_1 ;E_1 = 0010 & 0010 ;E_1 = 0010 -- still wrong

I've just given 6 examples of how the current implementation produces incorrect results -- even in cases you thought it would work, it didn't. I assert this is a ugly misimplementation.

-linda

-- Linda A Walsh | Trust Technology, Core Linux, SGI law@sgi.com | Voice: (650) 933-5338

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



This archive was generated by hypermail 2b29 : Wed May 31 2000 - 21:00:22 EST