Re: file effective and process inheritable mask

David L. Parsley (kparse@salem.k12.va.us)
Sat, 24 Apr 1999 08:34:13 -0400 (EDT)


Hi Albert,

On Fri, 23 Apr 1999, Albert D. Cahalan wrote:
> David L. Parsley writes:
> > On Fri, 23 Apr 1999, Albert D. Cahalan wrote:
> >> David L. Parsley writes:
>
> >>> don't see why we need fE (file effective).
> >>
> >> Consider these types of marked executables:
> >
> > Note: pE and pP are what I'm concerned with...
> >
> >> dumb, without privilege fE should be empty
> > or pE'=pP'=0
> >
> >> dumb, with privilege fE should be equal to fP
> > or pE'=pP'=fP || (fI && pI)=(something); in other words, whatever this
> > program can get in it's permitted needs to be raised in effective; a
> > raised permitted bit which isn't effective is useless (for a 'dumb' prog)
> >
> >> smart fE should be empty
> > why not pE=pP initially, and let the 'smart' program continue to
> > manipulate pE as it desires?
>
> This is a poor design because the smart program must clear pE as soon
> as possible. If C++ is used, main() might not even be the first code
> to execute. There may be security holes in global constructors.

Aarrgghh. Yes, I see your point; it just seems like a shame to tack this
extra set on all cap-enabled binaries just to support your odd C++
cap-enabled. Since a set is 128 bits, and I hope we never use them all,
maybe we could steal a high-order bit to say raise/lower all effective?

> >> We really only need one bit to indicate if an executable is aware,
> >> but there is no harm in using a whole set of bits.

Except for the 99% of progs that don't need it...

> > I just want to keep our implementation fairly spartan, without much
> > clutter. fE just doesn't look very useful to me, and especially not for
> > security purposes. As Andrej Presern pointed out, an exploit can
> > trivially raise all effective to match permitted anyway.
>
> He is wrong. Only a buffer overflow exploit has that kind of power.
> There are many other kinds of exploits that could be stopped.

OK, yes... this is only for buffer overflows; still the program _will_
have to be cap-aware to manipulate it's pE properly and avoid other
exploits.

> >>> Two, (and I'm thinking mostly here about non-cap-aware binaries),
> >>> it seems like it would be nice to be able to constrain the passage of the
> >>> inheritable set using our cap-elf model. I'm referring to an fM, where it
> >>> masks off bits in the inheritable by the formula pI' = pI && fM. This
> >>> occurs to me after thinking about an admin account starting a program by
> >>> hand, where the shell might have a mostly full inheritable set. This
> >>> seems like a bad security issue, since that program can exec() another
> >>> program with a more potent inheritable set.
> >>
> >> You are supposed to be able to do that.
> >
> > Certainly! That's the whole point of the way inheritable passes between
> > processes. I'm just saying that, in looking at designing a secure system
> > around caps, there are many cases where I _don't_ want inheritance to pass
> > in the usual fashion. Some programs just have no business exec'ing
> > another process and giving it elevated privs. Running named, mountd,
> > imapd, portmap, etc with pI=(mostly full) makes them just as vulnerable to
> > buffer overflow exploits as before; i.e., they could exec /bin/sh and get
> > a shell with mostly raised pI=='root shell'. So by including an fM, I can
> > prevent programs from getting a full pI when they don't need it. This,
> > IMHO, is perfectly in line with the principle of least priviledge.
>
> I don't think this is useful. Consider this:
>
> 1. If an attacker can cause an exec(), the attacker most likely
> has gained full control via a buffer overflow.

Exactly. Now ask yourself; if the attacker decides to 'exec /sbin/sh', do
you want pI=(mostly full)=='root shell' or pI=(only what the program
needed)=rather crippled. One useful bit of information from the POSIX
spec: a process cannot raise a bit in the inheritable set that is not
already set in permitted. So, my extension would allow, in many cases,
pI=pP=(one or two caps) - still dangerous, but much less than before.

> 2. If an attacker has gained full control via a buffer overflow,
> the attacker can load arbitrary code without an exec().

Yes, in which case they are constrained to pP. If pI is full, though,
they are much better exec'ing something that will inherit a lot of power,
like 'cp /etc/shadow /home/ftp/pub/ua412'. Note I'm not referring to the
uid fs rights, but the capability to read any file which a 'root shell'
would have in pI, and cp might have in fI (so admin can do admin things).
If pI is constrained with a mask, this sort of thing can be mostly
prevented.

> In other words, an attacker that can do an exec() has other ways to
> get the job done. You might as well not worry about the exec.

But I am, because with pI mostly full, that's where the attacker can gain
priviledge! I'm trying to say that, if pI is _full_ and a script kiddie
downloads a standard /bin/sh exec'ing buffer overflow exploit, then the
shell they get will be the current equivalent of a root shell! I'm trying
to avoid that, as well as provide the sysadmin a general means of
constricting the passage of pI.

> >> Think of it being a bit like the
> >> ability to run a normal setuid-root executable. Normal users are allowed
> >> to do that. It would be silly to only let already-privileged users run
> >> privileged executables.
> >>
> >> The fI vs. fP distinction lets you have capabilities that are activated
> >> for a subset of the users, those who already have the bits in pI.
> >
> > Right, but 'more' has no business having CAP_SETFCAP in it's pI under
> > _any_ circumstances. (well, none legitimate that I can think of) So
> > having CAP_SETFCAP in it's pI is a pretty big priviledge that we currently
> > have no good method for removing.
>
> Huh? Unless you enable some sort of backward compatibility hack, there
> is no way that 'more' will get CAP_SETFCAP without it being listed as
> something that 'more' should have. Remember that pP does not survive
> across an exec() in any way. Only pI lives on.

Read again. pI is what I was referring to.

> >> Besides confusion and tech support trouble, crashes can allow DoS attacks
> >> and possibly worse.
> >>
> >> The two most important extensions IMHO are:
> >>
> >> 1. The ability to mark an executable with the minimum capabilities
> >> needed to properly execute. This helps avoid crashes.
> >
> > Well, I've tried to illustrate a broad class of problems that appear to
> > require the solution I've suggested. I can certainly _see_ what you're
> > talking about here, but I can't think of any good examples that would
> > cause any real problems. If this is really a corner case you're talking
> > about here, it may be best solved in other ways (patching the program,
> > since not error-checking is an obvious bug). I'm just thinking it's not
> > efficient to store this extra info if <1% of cap-enabled programs really
>
> I don't want to find out if this is a corner case, do you? We'd know
> it is not a corner case if we see a security alert go out.
>
> I could think up some odd kind of DoS attack, but it would be better
> if someone would post a real example. There have been attacks that
> used the CPU time and file size limits.

I'm just not willing to entertain extensions to prevent a measly DoS
attack (which I grant can be bad, but less catastrophic that root shells)
that adds extra space and calculation overhead for everbody when we
currently can't think of a good example. And if you _do_ think of one or
two examples, we'll have to decide whether they are general enough to
warrant the extension, or whether the code should just be patched.

cheers,
David

- --
David L. Parsley
Network Specialist
City of Salem Schools

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