diff --git a/utils/test/common_test.py b/utils/test/common_test.py index a4ddc04a9..a6506d192 100755 --- a/utils/test/common_test.py +++ b/utils/test/common_test.py @@ -12,6 +12,7 @@ # # ---------------------------------------------------------------------- import unittest +import os import re import apparmor.common @@ -55,6 +56,13 @@ def setup_regex_tests(test_class): stub_test.__doc__ = "test '%s': %s" % (line, desc) setattr(test_class, 'test_%d' % (i), stub_test) +def write_file(directory, file, contents): + '''construct path, write contents to it, and return the constructed path''' + path = os.path.join(directory, file) + with open(path, 'w+') as f: + f.write(contents) + return path + if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.test_RegexParser'] unittest.main() diff --git a/utils/test/severity_test.py b/utils/test/severity_test.py index d1379a97f..6d1eceecb 100755 --- a/utils/test/severity_test.py +++ b/utils/test/severity_test.py @@ -1,5 +1,7 @@ +#!/usr/bin/env python # ---------------------------------------------------------------------- # Copyright (C) 2013 Kshitij Gupta +# Copyright (C) 2014 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 @@ -13,77 +15,199 @@ # ---------------------------------------------------------------------- import os import shutil +import tempfile import unittest import apparmor.severity as severity from apparmor.common import AppArmorException +from common_test import write_file -class Test(unittest.TestCase): +class SeverityBaseTest(unittest.TestCase): def setUp(self): - #copy the local profiles to the test directory - if os.path.exists('./profiles'): - shutil.rmtree('./profiles') - shutil.copytree('../../profiles/apparmor.d/', './profiles/', symlinks=True) + self.sev_db = severity.Severity('severity.db') def tearDown(self): - #Wipe the local profiles from the test directory - shutil.rmtree('./profiles') + pass - def testRank_Test(self): - sev_db = severity.Severity('severity.db') - rank = sev_db.rank('/usr/bin/whatis', 'x') - self.assertEqual(rank, 5, 'Wrong rank') - rank = sev_db.rank('/etc', 'x') - self.assertEqual(rank, 10, 'Wrong rank') - rank = sev_db.rank('/dev/doublehit', 'x') - self.assertEqual(rank, 0, 'Wrong rank') - rank = sev_db.rank('/dev/doublehit', 'rx') - self.assertEqual(rank, 4, 'Wrong rank') - rank = sev_db.rank('/dev/doublehit', 'rwx') - self.assertEqual(rank, 8, 'Wrong rank') - rank = sev_db.rank('/dev/tty10', 'rwx') - self.assertEqual(rank, 9, 'Wrong rank') - rank = sev_db.rank('/var/adm/foo/**', 'rx') - self.assertEqual(rank, 3, 'Wrong rank') - rank = sev_db.rank('CAP_KILL') - self.assertEqual(rank, 8, 'Wrong rank') - rank = sev_db.rank('CAP_SETPCAP') - self.assertEqual(rank, 9, 'Wrong rank') - self.assertEqual(sev_db.rank('/etc/apparmor/**', 'r') , 6, 'Invalid Rank') - self.assertEqual(sev_db.rank('/etc/**', 'r') , 10, 'Invalid Rank') - self.assertEqual(sev_db.rank('/usr/foo@bar', 'r') , 10, 'Invalid Rank') ## filename containing @ - self.assertEqual(sev_db.rank('/home/foo@bar', 'rw') , 6, 'Invalid Rank') ## filename containing @ + def _simple_severity_test(self, path, expected_rank): + rank = self.sev_db.rank(path) + self.assertEqual(rank, expected_rank, + 'expected rank %d, got %d' % (expected_rank, rank)) - # Load all variables for /sbin/klogd and test them - sev_db.load_variables('profiles/sbin.klogd') - self.assertEqual(sev_db.rank('@{PROC}/sys/vm/overcommit_memory', 'r'), 6, 'Invalid Rank') - self.assertEqual(sev_db.rank('@{HOME}/sys/@{PROC}/overcommit_memory', 'r'), 10, 'Invalid Rank') - self.assertEqual(sev_db.rank('/overco@{multiarch}mmit_memory', 'r'), 10, 'Invalid Rank') + def _simple_severity_w_perm(self, path, perm, expected_rank): + rank = self.sev_db.rank(path, perm) + self.assertEqual(rank, expected_rank, + 'expected rank %d, got %d' % (expected_rank, rank)) - sev_db.unload_variables() +class SeverityTest(SeverityBaseTest): + def test_perm_x(self): + self._simple_severity_w_perm('/usr/bin/whatis', 'x', 5) - sev_db.load_variables('profiles/usr.sbin.dnsmasq') - self.assertEqual(sev_db.rank('@{PROC}/sys/@{TFTP_DIR}/overcommit_memory', 'r'), 6, 'Invalid Rank') - self.assertEqual(sev_db.rank('@{PROC}/sys/vm/overcommit_memory', 'r'), 6, 'Invalid Rank') - self.assertEqual(sev_db.rank('@{HOME}/sys/@{PROC}/overcommit_memory', 'r'), 10, 'Invalid Rank') - self.assertEqual(sev_db.rank('/overco@{multiarch}mmit_memory', 'r'), 10, 'Invalid Rank') + def test_perm_etc_x(self): + self._simple_severity_w_perm('/etc', 'x', 10) - #self.assertEqual(sev_db.rank('/proc/@{PID}/maps', 'rw'), 9, 'Invalid Rank') + def test_perm_dev_x(self): + self._simple_severity_w_perm('/dev/doublehit', 'x', 0) - def testInvalid(self): - sev_db = severity.Severity('severity.db') - rank = sev_db.rank('/dev/doublehit', 'i') - self.assertEqual(rank, 10, 'Wrong') - try: - severity.Severity('severity_broken.db') - except AppArmorException: - pass - rank = sev_db.rank('CAP_UNKOWN') - rank = sev_db.rank('CAP_K*') + def test_perm_dev_rx(self): + self._simple_severity_w_perm('/dev/doublehit', 'rx', 4) + def test_perm_dev_rwx(self): + self._simple_severity_w_perm('/dev/doublehit', 'rwx', 8) + def test_perm_tty_rwx(self): + self._simple_severity_w_perm('/dev/tty10', 'rwx', 9) + + def test_perm_glob_1(self): + self._simple_severity_w_perm('/var/adm/foo/**', 'rx', 3) + + def test_cap_kill(self): + self._simple_severity_test('CAP_KILL', 8) + + def test_cap_setpcap(self): + self._simple_severity_test('CAP_SETPCAP', 9) + + def test_cap_unknown_1(self): + self._simple_severity_test('CAP_UNKNOWN', 10) + + def test_cap_unknown_2(self): + self._simple_severity_test('CAP_K*', 10) + + def test_perm_apparmor_glob(self): + self._simple_severity_w_perm('/etc/apparmor/**', 'r' , 6) + + def test_perm_etc_glob(self): + self._simple_severity_w_perm('/etc/**', 'r' , 10) + + def test_perm_filename_w_at_r(self): + self._simple_severity_w_perm('/usr/foo@bar', 'r' , 10) ## filename containing @ + + def test_perm_filename_w_at_rw(self): + self._simple_severity_w_perm('/home/foo@bar', 'rw', 6) ## filename containing @ + + def test_invalid_rank(self): + with self.assertRaises(AppArmorException): + self._simple_severity_w_perm('unexpected_unput', 'rw', 6) + +class SeverityVarsTest(SeverityBaseTest): + + VARIABLE_DEFINITIONS = ''' +@{HOME}=@{HOMEDIRS}/*/ /root/ +@{HOMEDIRS}=/home/ +@{multiarch}=*-linux-gnu* +@{TFTP_DIR}=/var/tftp /srv/tftpboot +@{PROC}=/proc/ +@{pid}={[1-9],[1-9][0-9],[1-9][0-9][0-9],[1-9][0-9][0-9][0-9],[1-9][0-9][0-9][0-9][0-9],[1-9][0-9][0-9][0-9][0-9][0-9]} +@{tid}=@{pid} +@{pids}=@{pid} +''' + + def setUp(self): + super(SeverityVarsTest, self).setUp() + self.tmpdir = tempfile.mkdtemp(prefix='aa-severity-') + rules_file = write_file(self.tmpdir, 'tunables', self.VARIABLE_DEFINITIONS) + + self.sev_db.load_variables(rules_file) + + def tearDown(self): + self.sev_db.unload_variables() + if os.path.exists(self.tmpdir): + shutil.rmtree(self.tmpdir) + + super(SeverityVarsTest, self).tearDown() + + def test_proc_var(self): + self._simple_severity_w_perm('@{PROC}/sys/vm/overcommit_memory', 'r', 6) + + def test_home_var(self): + self._simple_severity_w_perm('@{HOME}/sys/@{PROC}/overcommit_memory', 'r', 10) + + def test_multiarch_var(self): + self._simple_severity_w_perm('/overco@{multiarch}mmit_memory', 'r', 10) + + def test_proc_tftp_vars(self): + self._simple_severity_w_perm('@{PROC}/sys/@{TFTP_DIR}/overcommit_memory', 'r', 6) + +class SeverityDBTest(unittest.TestCase): + + def setUp(self): + self.tmpdir = tempfile.mkdtemp(prefix='aa-severity-db-') + + def tearDown(self): + if os.path.exists(self.tmpdir): + shutil.rmtree(self.tmpdir) + + def _test_db(self, contents): + self.db_file = write_file(self.tmpdir, 'severity.db', contents) + self.sev_db = severity.Severity(self.db_file) + return self.sev_db + + def test_simple_db(self): + db = self._test_db(''' + CAP_LEASE 8 + /etc/passwd* 4 8 0 +''') + + def test_cap_val_max_range(self): + db = self._test_db("CAP_LEASE 10\n") + + def test_cap_val_min_range(self): + db = self._test_db("CAP_LEASE 0\n") + + def test_cap_val_out_of_range(self): + with self.assertRaises(AppArmorException): + db = self._test_db("CAP_LEASE 18\n") + + def test_cap_val_out_of_range(self): + with self.assertRaises(AppArmorException): + db = self._test_db("CAP_LEASE -1\n") + + def test_path_insufficient_vals(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd* 0 4\n") + + def test_path_too_many_vals(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd* 0 4 5 6\n") + + def test_path_outside_range_1(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd* -2 4 6\n") + + def test_path_outside_range_2(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd* 12 4 6\n") + + def test_path_outside_range_3(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd* 2 -4 6\n") + + def test_path_outside_range_4(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd 2 14 6\n") + + def test_path_outside_range_5(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd 2 4 -12\n") + + def test_path_outside_range_6(self): + with self.assertRaises(AppArmorException): + db = self._test_db("/etc/passwd 2 4 4294967297\n") + + def test_garbage_line(self): + with self.assertRaises(AppArmorException): + db = self._test_db("garbage line\n") + + def test_invalid_db(self): + self.assertRaises(AppArmorException, severity.Severity, 'severity_broken.db') + + def test_nonexistent_db(self): + self.assertRaises(IOError, severity.Severity, 'severity.db.does.not.exist') + + def test_no_arg_to_severity(self): + sev_db = severity.Severity() + self.assertIsNone(sev_db, 'expected None, got %s' % (sev_db)) if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() + unittest.main(verbosity=2)