Re: [RFC] [PATCH] Stacking through chaining (v3)

From: Valdis.Kletnieks@private
Date: Wed Dec 01 2004 - 09:21:14 PST


On Tue, 30 Nov 2004 14:38:07 PST, Crispin Cowan said:

> Right: stacking arbitrary security policies is intractable :)

Amen. ;)

> In a purely restrictive model, you can generalize the simple case of 
> "operation allowed if all modules allow it", but I don't believe you can 
> automate "A allows it if B is loaded/allows it/denies it/votes Republican".

Fortunately for my sanity, most of the things I've wanted to stack can be
categorized as either:

1) Another purely restrictive hook.
2) Another purely restrictive hook, but not needed if SELinux is active.

(I'm still not sure how best to do (2) - although just wrapping the code in
a '#ifndef CONFIG_SECURITY_SELINUX' and pushing appropriate policy upstream
to the SELinux crew is a viable option.. ;)

> Since I don't actually believe that stacking is all that useful in the 
> general case, I would actually rather have the permissive Capable hooks 
> than a powerful stacking composition capability. So what I would like to 
> see is:
> 
>     * keep the stacking mechanism as simple as possible, aiming at the
>       "allow if all modules allow" composition policy and not much else

I'm fine on that - supporting only a purely restrictive composition is
fairly sufficient for me, especially if we can use things like capabilities
to implement (for instance) the 'realtime' LSM..

(Although I still think the 'realtime' LSM could equally well be addressed
with support for set-capability binaries - more on that below)

>     * if there is a conflict between Capable and stacker, choose Capable

I'd feel a lot better about Capable if we had a clear plan for killing
CAP_SYS_ADMIN and fragmenting it into many little bits, within our lifetimes ;)

I'm not sure how I feel about that particular conflict resolution - we'd have
to be more specific about the calling order - would we:

a) Call capable() first, and if it fails, then run the restrictive chain
b) Call capable() first, and if it succeeds, run the restrictive chain
c) call Capable(), run the chain, and return (capable || chain)
d) Run the restrictive chain, and if it fails, call capable() as a last resort
e) Run the restrictive chain, and if it succeeds, call capable()
f) Run both, and return (chain || capable)

(Note the slight semantic difference for the 'run both' and 'call second if
first fails' cases - in one, we short-circuit, in the other we don't.  This
may matter for some chain entries that have side effects (saving state, etc).

Also, how does this fit in with the current capabilities and Andy Lutomirsky's
work on set-capability binaries (I remember some squirrelly corner cases in
there)?  Personally, I'd prefer a scenario where we first apply capabilities
to create a possibly-larger-than-generic set of permissions, and then use a
chain of strictly restrictive calls to limit it.  So, for instance, you could
make /usr/bin/ping have CAP_RAW_NETWORK so it starts off with the ability to
send a raw packet, but then SELinux or any other restrictive LSM could deny
the actual request. (And yes, I *know* there's subtle differences between this
and using SELinux to restrict a set-UID-root binary..)

Or did you have some other general direction to consider here?





This archive was generated by hypermail 2.1.3 : Wed Dec 01 2004 - 09:22:13 PST