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

From: Crispin Cowan (crispin@private)
Date: Fri Dec 03 2004 - 15:13:26 PST

Valdis.Kletnieks@private wrote:

>On Tue, 30 Nov 2004 14:38:07 PST, Crispin Cowan said:
>>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.. ;)
I also think it is important that the Stacker facility not degenerate 
into the "Stacks with SELinux" facility, for 2 reasons:

   1. There are several modules that probably will never stack with
      SELinux (LIDS, SubDomain, the thing-whose-name-I-can't-remember
      from Erikson Research Canada, etc.) but that would be nice to
      stack with. SubDomain currently handles this by just sucking the
      Capabilities functionality into the SubDomain module; being able
      to just stack with the Capabilities module would be nice.
   2. "Stacks with SELinux", if that's all it is, should be implemented
      as a feature enhancement in the SELinux module, and not in the LSM

>>    * 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).
This semantic freak-out is happening because the capable() hooks are not 
restrictive, they are permissive. That inverts all the logic (everyone 
go look up De Morgan if you haven't seen it before :)

I propose 2 possible solutions for stacking modules that use the capable 

   1. The Draconian Solution: don't stack capable. Only permit one
      capable() hooking module at a time.
   2. The Flexible Solution: recognize that capable() hooks are
      permissive rather than restrictive, and invert the stacking logic.
      A capable() call is permitted if *any* module permits it, rather
      than if *all* modules permit it (which is what we are doing for
      the restrictive hooks).

>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..)
IMHO, the above scenario exceeds the plausible range of automatic module 
composition: here you are trying to do a restrictive override on a 
permissive hook. This is effectively an ad hoc chain of custom logic. I 
believe that modules that want to do that kind of thing should either 
subsume or replace the Capabilities module and completely take over the 
capable() hooks, or they should indirectly manipulate POSIX Capabilities 
in some other way. But expecting Stacker to handle this case gracefully 
and not turn into a massive bowl of special-case spaghetti is unrealistic.


Crispin Cowan, Ph.D.
CTO, Immunix

This archive was generated by hypermail 2.1.3 : Fri Dec 03 2004 - 22:22:28 PST