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...) - 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. [ 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 -- Working in a datacentre is the next best thing to being talented and popular. I get to spend hours surrounded by thousands of screaming fans [http://twitter.com/_gmh_/status/80741758358654977] -- AppArmor mailing list AppArmor@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor