On 10/23/2015 11:11 AM, Christian Boltz wrote: > Hello, > > this patch adds support for signal log events to aa-logprof. > > In other words: this is the first new feature in aa-logprof since I'm > working on the rule classes :-) > > In detail, this means: > - handle signal events in logparser.py > - "translate" those events in aa.py - from log (logparser.py readlog()) > to prelog (handle_children()) to log_dict (collapse_log()) to > log_obj (ask_the_questions()) > (yes, really! :-/ - needless to say that this is ugly...)
you weren't kidding > - finally ask the user about the signal in ask_the_questions() > > Also add a logparser test to test-signal.py to ensure the logparser step > works as expected. > > Note that the aa.py changes are not covered by tests, however they > worked in a manual test. > > I'm not fond of this. The translation is really ugly and the dict stuff and hand_children() make me want to cry (bad memories of the perl code). That said I didn't see anything wrong Acked-by: John Johansen <[email protected]> > > [ 12-logprof-signal.diff ] > > === modified file ./utils/apparmor/aa.py > --- utils/apparmor/aa.py 2015-10-23 17:27:29.601410816 +0200 > +++ utils/apparmor/aa.py 2015-10-23 19:34:14.770163154 +0200 > @@ -1125,6 +1126,16 @@ > continue > prelog[aamode][profile][hat]['capability'][capability] = True > > + elif typ == 'signal': > + # If signal then we (should) have pid, profile, hat, > program, mode, access, signal and peer > + pid, p, h, prog, aamode, access, signal, peer = entry > + if not regex_nullcomplain.search(p) and not > regex_nullcomplain.search(h): > + profile = p > + hat = h > + if not profile or not hat: > + continue > + prelog[aamode][profile][hat]['signal'][peer][access][signal] > = True > + > elif typ == 'path' or typ == 'exec': > # If path or exec then we (should) have pid, profile, hat, > program, mode, details and to_name > pid, p, h, prog, aamode, mode, detail, to_name = entry[:8] > @@ -1629,7 +1640,14 @@ > network_obj = NetworkRule(family, sock_type, > log_event=aamode) > log_obj[profile][hat]['network'].add(network_obj) > > - for ruletype in ['capability', 'network']: > + > + for peer in > sorted(log_dict[aamode][profile][hat]['signal'].keys()): > + for access in > sorted(log_dict[aamode][profile][hat]['signal'][peer].keys()): > + for signal in > sorted(log_dict[aamode][profile][hat]['signal'][peer][access].keys()): > + signal_obj = SignalRule(access, signal, peer, > log_event=aamode) > + log_obj[profile][hat]['signal'].add(signal_obj) > + > + for ruletype in ['capability', 'network', 'signal']: > # XXX aa-mergeprof also has this code - if you change > it, keep aa-mergeprof in sync! > for rule_obj in log_obj[profile][hat][ruletype].rules: > > @@ -2456,6 +2474,14 @@ > if not is_known_rule(aa[profile][hat], 'network', > NetworkRule(family, sock_type)): > > log_dict[aamode][profile][hat]['netdomain'][family][sock_type] = True > > + sig = prelog[aamode][profile][hat]['signal'] > + for peer in sig.keys(): > + for access in sig[peer].keys(): > + for signal in sig[peer][access].keys(): > + if not is_known_rule(aa[profile][hat], 'signal', > SignalRule(access, signal, peer)): > + > log_dict[aamode][profile][hat]['signal'][peer][access][signal] = True > + > + > PROFILE_MODE_RE = > re.compile('^(r|w|l|m|k|a|ix|ux|px|pux|cx|pix|cix|cux|Ux|Px|PUx|Cx|Pix|Cix|CUx)+$') > PROFILE_MODE_DENY_RE = re.compile('^(r|w|l|m|k|a|x)+$') > > === modified file ./utils/apparmor/logparser.py > --- utils/apparmor/logparser.py 2015-10-23 19:44:41.344399036 +0200 > +++ utils/apparmor/logparser.py 2015-10-23 19:37:12.788718122 +0200 > @@ -137,6 +137,10 @@ > ev['family'] = event.net_family > ev['protocol'] = event.net_protocol > ev['sock_type'] = event.net_sock_type > + elif ev['operation'] and ev['operation'] == 'signal': > + ev['signal'] = event.signal > + ev['peer'] = event.peer > + > LibAppArmor.free_record(event) > > if not ev['time']: > @@ -356,6 +360,9 @@ > elif e['operation'] == 'change_hat': > return(e['pid'], e['parent'], 'unknown_hat', > [profile, hat, aamode, hat]) > + elif e['operation'] == 'signal': > + return(e['pid'], e['parent'], 'signal', > + [profile, hat, prog, aamode, e['denied_mask'], > e['signal'], e['peer']]) > else: > self.debug_logger.debug('UNHANDLED: %s' % e) > > === modified file ./utils/test/test-signal.py > --- utils/test/test-signal.py 2015-10-23 19:44:41.344399036 +0200 > +++ utils/test/test-signal.py 2015-10-23 19:40:17.146900973 +0200 > @@ -20,7 +20,7 @@ > from apparmor.rule.signal import SignalRule, SignalRuleset > from apparmor.rule import BaseRule > from apparmor.common import AppArmorException, AppArmorBug > -#from apparmor.logparser import ReadLog > +from apparmor.logparser import ReadLog > from apparmor.translations import init_translation > _ = init_translation() > > @@ -82,44 +84,43 @@ > with self.assertRaises(expected): > SignalRule.parse(rawrule) > > -#class SignalTestParseFromLog(SignalTest): > -# def test_net_from_log(self): > -# parser = ReadLog('', '', '', '', '') > -# event = 'type=AVC msg=audit(1428699242.551:386): apparmor="DENIED" > operation="create" profile="/bin/ping" pid=10589 comm="ping" family="send" > sock_type="raw" protocol=1' > - > -# parsed_event = parser.parse_event(event) > - > -# self.assertEqual(parsed_event, { > -# 'request_mask': None, > -# 'denied_mask': None, > -# 'error_code': 0, > -# 'family': 'send', > -# 'magic_token': 0, > -# 'parent': 0, > -# 'profile': '/bin/ping', > -# 'protocol': 'icmp', > -# 'sock_type': 'raw', > -# 'operation': 'create', > -# 'resource': None, > -# 'info': None, > -# 'aamode': 'REJECTING', > -# 'time': 1428699242, > -# 'active_hat': None, > -# 'pid': 10589, > -# 'task': 0, > -# 'attr': None, > -# 'name2': None, > -# 'name': None, > -# }) > +class SignalTestParseFromLog(SignalTest): > + def test_net_from_log(self): > + parser = ReadLog('', '', '', '', '') > + event = 'type=AVC msg=audit(1409438250.564:201): apparmor="DENIED" > operation="signal" profile="/usr/bin/pulseaudio" pid=2531 comm="pulseaudio" > requested_mask="send" denied_mask="send" signal=term > peer="/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper"' > + > + parsed_event = parser.parse_event(event) > + > + self.assertEqual(parsed_event, { > + 'request_mask': 'send', > + 'denied_mask': 'send', > + 'error_code': 0, > + 'magic_token': 0, > + 'parent': 0, > + 'profile': '/usr/bin/pulseaudio', > + 'signal': 'term', > + 'peer': > '/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper', > + 'operation': 'signal', > + 'resource': None, > + 'info': None, > + 'aamode': 'REJECTING', > + 'time': 1409438250, > + 'active_hat': None, > + 'pid': 2531, > + 'task': 0, > + 'attr': None, > + 'name2': None, > + 'name': None, > + }) > > -# obj = SignalRule(parsed_event['family'], parsed_event['sock_type'], > log_event=parsed_event) > + obj = SignalRule(parsed_event['denied_mask'], > parsed_event['signal'], parsed_event['peer'], log_event=parsed_event) > > -# # audit allow deny comment access all? > type/proto all? > -# expected = exp(False, False, False, '' , 'send', False, > 'raw' , False) > + # audit allow deny comment access all? > signal all? peer > all? > + expected = exp(False, False, False, '', {'send'}, False, > {'term'}, False, > '/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper', False) > > -# self._compare_obj(obj, expected) > + self._compare_obj(obj, expected) > > -# self.assertEqual(obj.get_raw(1), ' signal send raw,') > + self.assertEqual(obj.get_raw(1), ' signal send set=term > peer=/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper,') > > > class SignalFromInit(SignalTest): > > > > Regards, > > Christian Boltz > -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
