mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00

Defining 'stderr = subprocess.STDOUT' as a default value for function did not work and the 'stderr' was always empty, thus also 'outerr' was always empty and not standard error contents was ever considered in any way. Best in fact was to remove excess function arguments as they were not even used and replace it with a simpler and less error prone structure. Even after reading 'stderr' correctly it did not help much as all tests used 'assertIn' which ignored excess output. Better replace the normal output with the error output if there ever was something, since stderr is most likely a serious thing and tests should stop on it.
213 lines
8.8 KiB
Python
Executable file
213 lines
8.8 KiB
Python
Executable file
#! /usr/bin/python3
|
|
# ------------------------------------------------------------------
|
|
#
|
|
# Copyright (C) 2011-2012 Canonical Ltd.
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of version 2 of the GNU General Public
|
|
# License published by the Free Software Foundation.
|
|
#
|
|
# ------------------------------------------------------------------
|
|
|
|
import os
|
|
import signal
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
# The locationg of the aa-decode utility can be overridden by setting
|
|
# the APPARMOR_DECODE environment variable; this is useful for running
|
|
# these tests in an installed environment
|
|
aadecode_bin = "../aa-decode"
|
|
|
|
# http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html
|
|
# This is needed so that the subprocesses that produce endless output
|
|
# actually quit when the reader goes away.
|
|
def subprocess_setup():
|
|
# Python installs a SIGPIPE handler by default. This is usually not what
|
|
# non-Python subprocesses expect.
|
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
|
|
# Define only arguments that are actually ever used: command and stdin
|
|
def cmd(command, stdin=None):
|
|
'''Try to execute given command (array) and return its stdout, or return
|
|
a textual error if it failed.'''
|
|
|
|
try:
|
|
sp = subprocess.Popen(
|
|
command,
|
|
stdin=stdin,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
close_fds=True,
|
|
preexec_fn=subprocess_setup
|
|
)
|
|
except OSError as e:
|
|
return [127, str(e)]
|
|
|
|
stdout, stderr = sp.communicate(input)
|
|
|
|
# If there was some error output, show that instead of stdout to ensure
|
|
# test fails and does not mask potentially major warnings and errors.
|
|
if stderr:
|
|
out = stderr
|
|
else:
|
|
out = stdout
|
|
|
|
return [sp.returncode, out.decode('utf-8')]
|
|
|
|
|
|
def mkstemp_fill(contents, suffix='', prefix='tst-aadecode-', dir=None):
|
|
'''As tempfile.mkstemp does, return a (file, name) pair, but with prefilled contents.'''
|
|
|
|
handle, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
|
|
os.close(handle)
|
|
handle = open(name, "w+")
|
|
handle.write(contents)
|
|
handle.flush()
|
|
handle.seek(0)
|
|
|
|
return handle, name
|
|
|
|
class AADecodeTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.tmpfile = None
|
|
|
|
def tearDown(self):
|
|
if self.tmpfile and os.path.exists(self.tmpfile):
|
|
os.remove(self.tmpfile)
|
|
|
|
def test_help(self):
|
|
'''Test --help argument'''
|
|
|
|
expected = 0
|
|
rc, report = cmd([aadecode_bin, "--help"])
|
|
result = 'Got exit code %d, expected %d\n' % (rc, expected)
|
|
self.assertEqual(expected, rc, result + report)
|
|
|
|
def _run_file_test(self, content, expected_list):
|
|
'''test case helper function; takes log content and a list of
|
|
expected strings as arguments'''
|
|
|
|
expected = 0
|
|
|
|
(f, self.tmpfile) = mkstemp_fill(content)
|
|
|
|
rc, report = cmd([aadecode_bin], stdin=f)
|
|
result = 'Got exit code %d, expected %d\n' % (rc, expected)
|
|
self.assertEqual(expected, rc, result + report)
|
|
for expected_string in expected_list:
|
|
result = 'could not find expected %s in output:\n' % (expected_string)
|
|
self.assertIn(expected_string, report, result + report)
|
|
f.close()
|
|
|
|
def test_simple_decode(self):
|
|
'''Test simple decode on command line'''
|
|
|
|
expected = 0
|
|
expected_output = 'Decoded: /tmp/foo bar'
|
|
test_code = '2F746D702F666F6F20626172'
|
|
|
|
rc, report = cmd([aadecode_bin, test_code])
|
|
result = 'Got exit code %d, expected %d\n' % (rc, expected)
|
|
self.assertEqual(expected, rc, result + report)
|
|
result = 'Got output "%s", expected "%s"\n' % (report, expected_output)
|
|
self.assertIn(expected_output, report, result + report)
|
|
|
|
def test_simple_filter(self):
|
|
'''test simple decoding of the name argument'''
|
|
|
|
expected_string = 'name="/tmp/foo bar"'
|
|
content = \
|
|
'''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
|
|
'''
|
|
|
|
self._run_file_test(content, [expected_string])
|
|
|
|
def test_simple_multiline(self):
|
|
'''test simple multiline decoding of the name argument'''
|
|
|
|
expected_strings = ['ses=4294967295 new ses=2762',
|
|
'name="/tmp/foo bar"',
|
|
'name="/home/steve/tmp/my test file"']
|
|
content = \
|
|
''' type=LOGIN msg=audit(1348980001.155:2925): login pid=17875 uid=0 old auid=4294967295 new auid=0 old ses=4294967295 new ses=2762
|
|
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
|
|
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
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
def test_simple_profile(self):
|
|
'''test simple decoding of the profile argument'''
|
|
|
|
'''Example take from LP: #897957'''
|
|
expected_strings = ['name="/lib/x86_64-linux-gnu/libdl-2.13.so"',
|
|
'profile="/test space"']
|
|
content = \
|
|
'''[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
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
def test_simple_profile2(self):
|
|
'''test simple decoding of name and profile argument'''
|
|
|
|
'''Example take from LP: #897957'''
|
|
expected_strings = ['name="/home/steve/tmp/my test file"',
|
|
'profile="/home/steve/tmp/my prog.sh"']
|
|
content = \
|
|
'''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
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
def test_simple_embedded_carat(self):
|
|
'''test simple decoding of embedded ^ in files'''
|
|
|
|
expected_strings = ['name="/home/steve/tmp/my test ^file"']
|
|
content = \
|
|
'''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
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
def test_simple_embedded_backslash_carat(self):
|
|
'''test simple decoding of embedded \^ in files'''
|
|
|
|
expected_strings = ['name="/home/steve/tmp/my test \^file"']
|
|
content = \
|
|
'''type=AVC msg=audit(1349805073.402:6857): apparmor="DENIED" operation="mknod" parent=5890 profile="/usr/bin/test_profile" name=2F686F6D652F73746576652F746D702F6D792074657374205C5E66696C65 pid=5891 comm="touch" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
def test_simple_embedded_singlequote(self):
|
|
'''test simple decoding of embedded \' in files'''
|
|
|
|
expected_strings = ['name="/home/steve/tmp/my test \'file"']
|
|
content = \
|
|
'''type=AVC msg=audit(1349805073.402:6857): apparmor="DENIED" operation="mknod" parent=5890 profile="/usr/bin/test_profile" name=2F686F6D652F73746576652F746D702F6D792074657374202766696C65 pid=5891 comm="touch" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
def test_simple_encoded_nonpath_profiles(self):
|
|
'''test simple decoding of nonpath profiles'''
|
|
|
|
expected_strings = ['name="/lib/x86_64-linux-gnu/libdl-2.13.so"',
|
|
'profile="test space"']
|
|
content = \
|
|
'''[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
|
|
'''
|
|
|
|
self._run_file_test(content, expected_strings)
|
|
|
|
#
|
|
# Main
|
|
#
|
|
if __name__ == '__main__':
|
|
if 'APPARMOR_DECODE' in os.environ:
|
|
aadecode_bin = os.environ['APPARMOR_DECODE']
|
|
unittest.main(verbosity=1)
|