~sbeattie/apparmor/apparmor-quantal-update

« back to all changes in this revision

Viewing changes to debian/patches/0022-aa-decode-stdin.patch

  • Committer: Steve Beattie
  • Date: 2012-10-09 20:34:17 UTC
  • Revision ID: sbeattie@ubuntu.com-20121009203417-monryb1c4cn7drnj
debian/patches/0022-aa-decode-stdin.patch: fix aa-decode to process
stdin correctly and decode encoded profiles names

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Forwarded: yes
 
2
Subject: fix aa-decode stdin handling
 
3
From: Christian Boltz <apparmor@cboltz.de>, Steve Beattie <steve.beattie@canonical.com>
 
4
 
 
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
 
15
 
 
16
---
 
17
 utils/aa-decode |   32 ++++++++++++++++++++++++--------
 
18
 utils/test/test-aa-decode.py |  214 +++++++++++++++++++++++++++++++++++++++++++
 
19
 1 file changed, 24 insertions(+), 8 deletions(-)
 
20
 
 
21
Index: b/utils/aa-decode
 
22
===================================================================
 
23
--- a/utils/aa-decode
 
24
+++ b/utils/aa-decode
 
25
@@ -1,6 +1,7 @@
 
26
-#!/bin/sh
 
27
+#!/bin/bash
 
28
 #
 
29
-#    Copyright (C) 2009-2010 Canonical Ltd.
 
30
+#    Copyright (C) 2009-2010, 2012 Canonical Ltd.
 
31
+#    Copyright (C) 2012 Christian Boltz
 
32
 #
 
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
 
36
     exit 0
 
37
 fi
 
38
 
 
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:]'`
 
45
-    d=`decode $e`
 
46
-    echo -n "$line" | sed "s/\(.*\) name=.*/\1 name=/g"
 
47
-    echo "'$d'"
 
48
+while read line ; do
 
49
+
 
50
+    # check if line contains encoded name= or profile=
 
51
+    if echo "$line" | egrep ' (name|profile)=[0-9a-fA-F]*' >/dev/null ; then
 
52
+
 
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})\""
 
56
+
 
57
+        pe=`echo "$line" | sed 's/.* profile=\([^ ]*\).*$/\\1/g' | tr -s '[:lower:]' '[:upper:]'`
 
58
+        pd="\"`decode ${pe}`\""
 
59
+
 
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"
 
62
+
 
63
+    else
 
64
+        # line does not contain encoded name= - no need to decode, print unchanged line
 
65
+        echo "$line"
 
66
+    fi
 
67
+
 
68
 done
 
69
 
 
70
Index: b/utils/test/test-aa-decode.py
 
71
===================================================================
 
72
--- /dev/null
 
73
+++ b/utils/test/test-aa-decode.py
 
74
@@ -0,0 +1,214 @@
 
75
+#! /usr/bin/env python
 
76
+# ------------------------------------------------------------------
 
77
+#
 
78
+#    Copyright (C) 2011-2012 Canonical Ltd.
 
79
+#
 
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.
 
83
+#
 
84
+# ------------------------------------------------------------------
 
85
+
 
86
+import os
 
87
+import signal
 
88
+import subprocess
 
89
+import tempfile
 
90
+import unittest
 
91
+
 
92
+aadecode_bin = "./aa-decode"
 
93
+
 
94
+
 
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)
 
102
+
 
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.'''
 
106
+
 
107
+    try:
 
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)]
 
111
+
 
112
+    out, outerr = sp.communicate(input)
 
113
+    # Handle redirection of stdout
 
114
+    if out == None:
 
115
+        out = ''
 
116
+    # Handle redirection of stderr
 
117
+    if outerr == None:
 
118
+        outerr = ''
 
119
+    return [sp.returncode, str(out) + str(outerr)]
 
120
+
 
121
+
 
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.'''
 
124
+
 
125
+    handle, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
 
126
+    os.close(handle)
 
127
+    handle = open(name, "w+")
 
128
+    handle.write(contents)
 
129
+    handle.flush()
 
130
+    handle.seek(0)
 
131
+
 
132
+    return handle, name
 
133
+
 
134
+class AADecodeTest(unittest.TestCase):
 
135
+
 
136
+    def setUp(self):
 
137
+        self.tmpfile = None
 
138
+
 
139
+    def tearDown(self):
 
140
+        if self.tmpfile and os.path.exists(self.tmpfile):
 
141
+            os.remove(self.tmpfile)
 
142
+
 
143
+    def test_help(self):
 
144
+        '''Test --help argument'''
 
145
+
 
146
+        expected = 0
 
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)
 
150
+
 
151
+    def test_simple_decode(self):
 
152
+        '''Test simple decode on command line'''
 
153
+
 
154
+        expected = 0
 
155
+        expected_output = 'Decoded: /tmp/foo bar'
 
156
+        test_code = '2F746D702F666F6F20626172'
 
157
+
 
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)
 
163
+
 
164
+    def test_simple_filter(self):
 
165
+        '''test simple decoding of the name argument'''
 
166
+
 
167
+        expected = 0
 
168
+        expected_string = 'name="/tmp/foo bar"'
 
169
+        content = \
 
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
 
171
+'''
 
172
+        (f, self.tmpfile) = mkstemp_fill(content)
 
173
+
 
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)
 
179
+        f.close()
 
180
+
 
181
+    def test_simple_multiline(self):
 
182
+        '''test simple multiline decoding of the name argument'''
 
183
+
 
184
+        expected = 0
 
185
+        expected_strings = ['ses=4294967295 new ses=2762',
 
186
+                            'name="/tmp/foo bar"',
 
187
+                            'name="/home/steve/tmp/my test file"']
 
188
+        content = \
 
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
 
192
+'''
 
193
+        (f, self.tmpfile) = mkstemp_fill(content)
 
194
+
 
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)
 
201
+        f.close()
 
202
+
 
203
+    def test_simple_profile(self):
 
204
+        '''test simple decoding of the profile argument'''
 
205
+
 
206
+        '''Example take from LP: #897957'''
 
207
+        expected = 0
 
208
+        expected_strings = ['name="/lib/x86_64-linux-gnu/libdl-2.13.so"',
 
209
+                            'profile="/test space"']
 
210
+        content = \
 
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
 
212
+'''
 
213
+
 
214
+        (f, self.tmpfile) = mkstemp_fill(content)
 
215
+
 
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)
 
222
+        f.close()
 
223
+
 
224
+    def test_simple_profile2(self):
 
225
+        '''test simple decoding of name and profile argument'''
 
226
+
 
227
+        '''Example take from LP: #897957'''
 
228
+        expected = 0
 
229
+        expected_strings = ['name="/home/steve/tmp/my test file"',
 
230
+                            'profile="/home/steve/tmp/my prog.sh"']
 
231
+        content = \
 
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
 
233
+'''
 
234
+
 
235
+        (f, self.tmpfile) = mkstemp_fill(content)
 
236
+
 
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)
 
243
+        f.close()
 
244
+
 
245
+    def test_simple_embedded_carats(self):
 
246
+        '''test simple decoding of embedded ^ in files'''
 
247
+
 
248
+        expected = 0
 
249
+        expected_strings = ['name="/home/steve/tmp/my test ^file"']
 
250
+        content = \
 
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
 
252
+'''
 
253
+
 
254
+        (f, self.tmpfile) = mkstemp_fill(content)
 
255
+
 
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)
 
262
+        f.close()
 
263
+
 
264
+    def test_simple_encoded_nonpath_profiles(self):
 
265
+        '''test simple decoding of nonpath profiles'''
 
266
+
 
267
+        expected = 0
 
268
+        expected_strings = ['name="/lib/x86_64-linux-gnu/libdl-2.13.so"',
 
269
+                            'profile="test space"']
 
270
+        content = \
 
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
 
272
+'''
 
273
+
 
274
+        (f, self.tmpfile) = mkstemp_fill(content)
 
275
+
 
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)
 
282
+        f.close()
 
283
+
 
284
+#
 
285
+# Main
 
286
+#
 
287
+if __name__ == '__main__':
 
288
+    unittest.main()