[apparmor] [patch 3/3] use capability rule class in aa.py and cleanprof.py

Christian Boltz apparmor at cboltz.de
Wed Dec 3 18:19:11 UTC 2014


Hello,

Am Mittwoch, 3. Dezember 2014 schrieb Steve Beattie:
> On Sun, Nov 30, 2014 at 12:45:49AM +0100, Christian Boltz wrote:

> I agree with that; however, I think the difference between the two
> situations (raw_rule not matching the passed cap_list versus calling
> set_param() twice) is that you have to go out of your way to set
> raw_rule to be different from the capability that represents in
> the same constructor call, whereas with some convoluted logic (and
> really, aa.py excels at convoluted logic), 

Indeed ;-) - but it has its roots in the Immunix::AppArmor module, so 
that is what you really should blame *g*

> it would be possible to
> become confused about whether set_param() had already been called on
> a partially initialized object.

OK, good argument ;-)

> > So if someone wants to shoot himself in the foot - hey, it's his
> > foot, not mine *eg*
> 
> That said, with your followup email, I think it's worth trying to do
> better with raw_rule handling. My intent is to make it easy to do
> the easy and correct things, and make you go out of your way to do
> the things that will likely cause bugs.

Ideally we would make it impossible to do things that cause bugs. 
However that would include making the internal variables really internal 
(aka "not available for external access") which isn't possible in python 
AFAIK (praise PHP for allowing that!). However, such private variables 
would make testing some corner cases (where I intentionally break the 
internal variables) harder ;-)

> > > Log events have an
> > > explicit object, and thus an is_covered() test needs to only see
> > > if a
> > > given string is matched by a regex, not whether two regexs cover
> > > the
> > > same set of strings, or that one is a subset of the other.  Thus,
> > > I've left a bit of the code and the tests for log matching in but
> > > commented out.
> > 
> > OK, when we come to log handling, we'll see which way works the
> > best.
> 
> I can see a relatively simple algorithm for handling matching log
> events with regex patterns: convert apparmor regex to python regex
> (caching if necessary) and then ask the python regex if the log event
> matches. With two regexs, I'd have to do some algorithm research
> (probably John has pointers).

aa-mergeprof already somehow[tm] has to compare two profiles (including 
file rules), which means doing exactly that. We should have a look at it 
when we come to implementing file rules - either we find the perfect way 
to do it in aa-mergeprof, or we find a (possible) bug in it ;-)  
(no, I did not check the code before writing this mail ;-)
 
> > > - use standard pythonic notions of using a superclass'
> > > __init__ function.
> > 
> > I know it's more pythonic, but it's also harder to read ;-)
> 
> It would be less annoying to read if we were python3 only, and could
> just do super().__init__(blah blah).

Argh, py version fun again. At least the py2 code still works with py3 
this time, which already sounds like a surprise ;-)

Anyway - having a function with another name in BaseRule would solve 
this problem in a less cryptic^Wpythonic way.

> > Minor nitpicking (CapabilityRule __init__):
> > 
> > If cap_list is a string and contains multiple capabilities ("chown
> > chmod"), you should split() it.
> 
> That's... not really an interface use case I'd considered; my thinking
> was if you want to give the constructor a group of capabilities, you
> give it a list.

That's the theory, yes. In practise someone gives you a string, and you 
don't know exactly what this string contains. Maybe he just parsed it 
with "capability (.*),"

My untested proposal should cover this without adding much code (same 
line count if you ignore the blank line) or overhead, so there's no 
reason not to split the string ;-)

> > > - converted get_raw() and get_clean() to use depth=0 by default
> > > for
> > > 
> > >   convenience
> > 
> > Good idea, even if it comes with the risk of "forgetting" to specify
> > the depth parameter. Ack
> 
> I'm really of two minds about get_raw and get_clean. On the one hand,
> the rules themselves know best how to format themselves.

Not really - at the moment, CapabilityRule and CapabilityRuleset don't 
know if they live inside a profile, a hat or an include (which all have 
different whitespace levels), so they don't know which depth is needed.

> On the other,
> this leads to the difficulty of sorting multi-line rules.

Not really, as long as you use the same depth for all rules you want to 
sort.

On the long(er) term, we could think about setting/changing the 
whitespace only for get_clean() and keep it unchanged in get_raw(). 
Let me warn you that this would mean doing quite some changes in aa.py 
(which does strip() all lines by default), so let's just keep it 
somewhere in the not-too-important section of the TODO list ;-)
 
> > > - move the implementation of set_raw to
> > > parser.py:parse_capability()
> > > 
> > >   which returns a completed CapabilityRule object.
> > 
> > IMHO it would make sense to keep all capability-specific code in one
> > file - rule/capability.py. I don't see a problem with having it
> > outside the class, but I doubt having a parser.py with code for all
> > rule types is a good idea.
> > 
> > My experience from rewriting PostfixAdmin is: having everything
> > specific to mailboxes in MailboxHandler turned out to be the best
> > way, and allows to (ab?)use the basic infrastructure to manage
> > anything, not only mailboxes and aliases. You could easily write a
> > module to manage your collection of $whatever in some hours ;-))
> 
> When I first started re-arranging your patches, I originally had the
> parsing functions in __init__.py and capability.py, 


Funny enough that this is basically what we then discussed on IRC (see 
my follow-up mail)

> but then pushed them to the separate parser.py file.  
> I don't have a strong feeling one
> way or the other, but I'd note that one of the drawbacks of doing so
> was that the references to the functions were somewhat awkward; we'd
> either need to import each of the individual parsing functions, or do
> what I'd tried, which was to import capability into the name space
> (and reference capability.parse_raw()), but then that collided with a
> few of the local loop variables. A third way of coping with this I'd
> thought of was to hang the parse_raw function as a class method off
> of the Rule classes; I think I'll give that a try to see how it
> looks.

See the IRC discussion quoted in my follow-up mail:
Integrating the parse_raw function in CapabilityRule.__init__() is 
probably the best way - either directly or by calling an internal 
function.

Yes, that means some more imports are needed in capability.py - but it 
has the big advantage of having everything capability-related together 
in one file. (Well, except the regex, but let's ignore this detail for 
now ;-)

> I'll send out version 5 of the patch set shortly, with my changes
> flattened into yours.

Sounds good :-)

The remaining question is if we want to split the commit to know who 
broke which part *g* or if we just commit the merged patch with two 
authors mentioned.

Oh, and is one of us still allowed to send an Ack for a merged patch 
(which would mean acking partially self-written code), or do we need to 
force someone else to review it? ;-)


Regards,

Christian Boltz
-- 
Funktionieren Windows 98-Witze auch unter Windows 2000
oder braucht man da ein Update?




More information about the AppArmor mailing list