2
Subject: fix aa-decode stdin handling
3
From: Christian Boltz <apparmor@cboltz.de>, Steve Beattie <steve.beattie@canonical.com>
5
This patch for stdin handling
6
- fixes the pattern to match the current log format (name= is NOT the
7
last part in the log entry)
8
- uses bash replacement to avoid some sed calls (which also means the
9
script now needs an explicit "#!/bin/bash")
10
- prints decoded filenames in double instead of single quotes to be
11
consistent with filenames that were not encoded
12
- also prints lines that do not contain an encoded filename (instead
13
of grepping them away)
14
- decode encoded profile names
17
utils/aa-decode | 32 ++++++++++++++++++++++++--------
18
utils/test/test-aa-decode.py | 214 +++++++++++++++++++++++++++++++++++++++++++
19
1 file changed, 24 insertions(+), 8 deletions(-)
21
Index: b/utils/aa-decode
22
===================================================================
29
-# Copyright (C) 2009-2010 Canonical Ltd.
30
+# Copyright (C) 2009-2010, 2012 Canonical Ltd.
31
+# Copyright (C) 2012 Christian Boltz
33
# This program is free software; you can redistribute it and/or
34
# modify it under the terms of version 2 of the GNU General Public
35
@@ -63,13 +64,28 @@ if [ -n "$1" ]; then
39
-# For now just look at 'name=...' which is usually the last in the log entry,
40
+# For now just look at 'name=...' and 'profile=...',
41
# so validate input against this and output based on it.
42
# TODO: better handle other cases too
43
-egrep ' name=2[fF][0-9a-fA-F]*$' | while read line ; do
44
- e=`echo "$line" | sed 's/.* name=\(.*\)/\\1/g' | tr -s '[:lower:]' '[:upper:]'`
46
- echo -n "$line" | sed "s/\(.*\) name=.*/\1 name=/g"
50
+ # check if line contains encoded name= or profile=
51
+ if echo "$line" | egrep ' (name|profile)=[0-9a-fA-F]*' >/dev/null ; then
53
+ # cut the encoded filename out of the line and decode it
54
+ ne=`echo "$line" | sed 's/.* name=\([^ ]*\).*$/\\1/g' | tr -s '[:lower:]' '[:upper:]'`
55
+ nd="\"$(decode ${ne})\""
57
+ pe=`echo "$line" | sed 's/.* profile=\([^ ]*\).*$/\\1/g' | tr -s '[:lower:]' '[:upper:]'`
58
+ pd="\"`decode ${pe}`\""
60
+ # replace encoded name and profile with its decoded counterparts
61
+ echo "$line" | sed -e "s^name=${ne}^name=${nd/^/\^}^g" -e "s^profile=${pe}^profile=${pd/^/\^}^g"
64
+ # line does not contain encoded name= - no need to decode, print unchanged line
70
Index: b/utils/test/test-aa-decode.py
71
===================================================================
73
+++ b/utils/test/test-aa-decode.py
75
+#! /usr/bin/env python
76
+# ------------------------------------------------------------------
78
+# Copyright (C) 2011-2012 Canonical Ltd.
80
+# This program is free software; you can redistribute it and/or
81
+# modify it under the terms of version 2 of the GNU General Public
82
+# License published by the Free Software Foundation.
84
+# ------------------------------------------------------------------
92
+aadecode_bin = "./aa-decode"
95
+# http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html
96
+# This is needed so that the subprocesses that produce endless output
97
+# actually quit when the reader goes away.
98
+def subprocess_setup():
99
+ # Python installs a SIGPIPE handler by default. This is usually not what
100
+ # non-Python subprocesses expect.
101
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
103
+def cmd(command, input = None, stderr = subprocess.STDOUT, stdout = subprocess.PIPE, stdin = None, timeout = None):
104
+ '''Try to execute given command (array) and return its stdout, or return
105
+ a textual error if it failed.'''
108
+ sp = subprocess.Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, close_fds=True, preexec_fn=subprocess_setup)
109
+ except OSError as e:
110
+ return [127, str(e)]
112
+ out, outerr = sp.communicate(input)
113
+ # Handle redirection of stdout
116
+ # Handle redirection of stderr
119
+ return [sp.returncode, str(out) + str(outerr)]
122
+def mkstemp_fill(contents, suffix='', prefix='tst-aadecode-', dir=None):
123
+ '''As tempfile.mkstemp does, return a (file, name) pair, but with prefilled contents.'''
125
+ handle, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
127
+ handle = open(name, "w+")
128
+ handle.write(contents)
132
+ return handle, name
134
+class AADecodeTest(unittest.TestCase):
137
+ self.tmpfile = None
139
+ def tearDown(self):
140
+ if self.tmpfile and os.path.exists(self.tmpfile):
141
+ os.remove(self.tmpfile)
143
+ def test_help(self):
144
+ '''Test --help argument'''
147
+ rc, report = cmd([aadecode_bin, "--help"])
148
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
149
+ self.assertEqual(expected, rc, result + report)
151
+ def test_simple_decode(self):
152
+ '''Test simple decode on command line'''
155
+ expected_output = 'Decoded: /tmp/foo bar'
156
+ test_code = '2F746D702F666F6F20626172'
158
+ rc, report = cmd([aadecode_bin, test_code])
159
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
160
+ self.assertEqual(expected, rc, result + report)
161
+ result = 'Got output "%s", expected "%s"\n' % (report, expected_output)
162
+ self.assertIn(expected_output, report, result + report)
164
+ def test_simple_filter(self):
165
+ '''test simple decoding of the name argument'''
168
+ expected_string = 'name="/tmp/foo bar"'
170
+'''type=AVC msg=audit(1348982151.183:2934): apparmor="DENIED" operation="open" parent=30751 profile="/usr/lib/firefox/firefox{,*[^s] [^h]}" name=2F746D702F666F6F20626172 pid=30833 comm="plugin-containe" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
172
+ (f, self.tmpfile) = mkstemp_fill(content)
174
+ rc, report = cmd([aadecode_bin], stdin=f)
175
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
176
+ self.assertEqual(expected, rc, result + report)
177
+ result = 'could not find expected %s in output:\n' % (expected_string)
178
+ self.assertIn(expected_string, report, result + report)
181
+ def test_simple_multiline(self):
182
+ '''test simple multiline decoding of the name argument'''
185
+ expected_strings = ['ses=4294967295 new ses=2762',
186
+ 'name="/tmp/foo bar"',
187
+ 'name="/home/steve/tmp/my test file"']
189
+''' type=LOGIN msg=audit(1348980001.155:2925): login pid=17875 uid=0 old auid=4294967295 new auid=0 old ses=4294967295 new ses=2762
190
+type=AVC msg=audit(1348982151.183:2934): apparmor="DENIED" operation="open" parent=30751 profile="/usr/lib/firefox/firefox{,*[^s] [^h]}" name=2F746D702F666F6F20626172 pid=30833 comm="plugin-containe" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
191
+type=AVC msg=audit(1348982148.195:2933): apparmor="DENIED" operation="file_lock" parent=5490 profile="/usr/lib/firefox/firefox{,*[^s][^h]}" name=2F686F6D652F73746576652F746D702F6D7920746573742066696C65 pid=30737 comm="firefox" requested_mask="k" denied_mask="k" fsuid=1000 ouid=1000
193
+ (f, self.tmpfile) = mkstemp_fill(content)
195
+ rc, report = cmd([aadecode_bin], stdin=f)
196
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
197
+ self.assertEqual(expected, rc, result + report)
198
+ for string in expected_strings:
199
+ result = 'could not find expected %s in output:\n' % (string)
200
+ self.assertIn(string, report, result + report)
203
+ def test_simple_profile(self):
204
+ '''test simple decoding of the profile argument'''
206
+ '''Example take from LP: #897957'''
208
+ expected_strings = ['name="/lib/x86_64-linux-gnu/libdl-2.13.so"',
209
+ 'profile="/test space"']
211
+'''[289763.843292] type=1400 audit(1322614912.304:857): apparmor="ALLOWED" operation="getattr" parent=16001 profile=2F74657374207370616365 name="/lib/x86_64-linux-gnu/libdl-2.13.so" pid=17011 comm="bash" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
214
+ (f, self.tmpfile) = mkstemp_fill(content)
216
+ rc, report = cmd([aadecode_bin], stdin=f)
217
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
218
+ self.assertEqual(expected, rc, result + report)
219
+ for string in expected_strings:
220
+ result = 'could not find expected %s in output:\n' % (string)
221
+ self.assertIn(string, report, result + report)
224
+ def test_simple_profile2(self):
225
+ '''test simple decoding of name and profile argument'''
227
+ '''Example take from LP: #897957'''
229
+ expected_strings = ['name="/home/steve/tmp/my test file"',
230
+ 'profile="/home/steve/tmp/my prog.sh"']
232
+'''type=AVC msg=audit(1349805073.402:6857): apparmor="DENIED" operation="mknod" parent=5890 profile=2F686F6D652F73746576652F746D702F6D792070726F672E7368 name=2F686F6D652F73746576652F746D702F6D7920746573742066696C65 pid=5891 comm="touch" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
235
+ (f, self.tmpfile) = mkstemp_fill(content)
237
+ rc, report = cmd([aadecode_bin], stdin=f)
238
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
239
+ self.assertEqual(expected, rc, result + report)
240
+ for string in expected_strings:
241
+ result = 'could not find expected %s in output:\n' % (string)
242
+ self.assertIn(string, report, result + report)
245
+ def test_simple_embedded_carats(self):
246
+ '''test simple decoding of embedded ^ in files'''
249
+ expected_strings = ['name="/home/steve/tmp/my test ^file"']
251
+'''type=AVC msg=audit(1349805073.402:6857): apparmor="DENIED" operation="mknod" parent=5890 profile="/usr/bin/test_profile" name=2F686F6D652F73746576652F746D702F6D792074657374205E66696C65 pid=5891 comm="touch" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
254
+ (f, self.tmpfile) = mkstemp_fill(content)
256
+ rc, report = cmd([aadecode_bin], stdin=f)
257
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
258
+ self.assertEqual(expected, rc, result + report)
259
+ for string in expected_strings:
260
+ result = 'could not find expected %s in output:\n' % (string)
261
+ self.assertIn(string, report, result + report)
264
+ def test_simple_encoded_nonpath_profiles(self):
265
+ '''test simple decoding of nonpath profiles'''
268
+ expected_strings = ['name="/lib/x86_64-linux-gnu/libdl-2.13.so"',
269
+ 'profile="test space"']
271
+'''[289763.843292] type=1400 audit(1322614912.304:857): apparmor="ALLOWED" operation="getattr" parent=16001 profile=74657374207370616365 name="/lib/x86_64-linux-gnu/libdl-2.13.so" pid=17011 comm="bash" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
274
+ (f, self.tmpfile) = mkstemp_fill(content)
276
+ rc, report = cmd([aadecode_bin], stdin=f)
277
+ result = 'Got exit code %d, expected %d\n' % (rc, expected)
278
+ self.assertEqual(expected, rc, result + report)
279
+ for string in expected_strings:
280
+ result = 'could not find expected %s in output:\n' % (string)
281
+ self.assertIn(string, report, result + report)
287
+if __name__ == '__main__':