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

From: Theodore Y. Ts'o (tytso@MIT.EDU)
Date: Mon May 29 2000 - 08:04:07 EST


   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.

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.

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.

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! :-)

> The basic idea seems to be that an executable should never
> have permissions shoved down its throat that it's not prepared to
> handle.
   ---
           True, but if the previous process's Permitted set aren't factored
   in, then neither is it possible for an executable to inherit capabilities
   that both the previous process and the file have specified as inheritable.
   Somewhere you have to be "and"ing the previous process's Permitted set
   with the new Inheritable mask (pP & pI').

Right. And given how the Draft 17 doesn't allow this, I believe this
was intentional. 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.4).

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
E = 0

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.

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
E = CAP_DAC_READ_SEARCH

Now when the tar process executes, it will have the following
permissions:

P = CAP_DAC_READ_SEARCH
I = CAP_DAC_READ_SEARCH
E = 0

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

It's a different model than the one which you were working with, but it
*does* work, and it is self-consistent.

           Naw. Linux already does what I am asking, but does so in an
   inconsistent and error-prone manner. Currently, the setcap system call
   improperly constrains pI to be a subset of pP. This is a bug. That's
   not part of the POSIX spec -- but it is *needed* because how inheritance
   in fs/exec.c is broken. So the limit in kernel/capability.c is a kludge
   to solve the inaccurate inheritance model in fs/exec.c.

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.

   1) pI' = pI & fI
   2) pP' = fp | (pI' & pP)
   3) pE' = fE & pP'

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.

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:

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

                                                        - Ted

P.S. My apologies in advance for denigrating those competent sysadmins
out there. But if you've ever seen the way mainframe datashops are
often run, where there is a strict division between those are system
programmers, and those who mount tapes, know how to press the reboot
button and who have to do tasks like monitor screens that say "ED IS OK"
in 72 point type --- and who are instructed to page a system programmer
when the screen turns red and displays "ED IS DOWN" (ED being a cray
supercomputer that used to be at MIT) --- you'll understand a bit more
of the design concerns behind wanting to design a system which is secure
even in the presence of relatively clueless third shift system
operators. (3rd shift gernally runs from midnight to 8a.m., and often
you don't have your best and brightest manning the data center at that
hour.)

-
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:21 EST