move selinux tools to cts/tools/selinux
CTS shouldn't be depending on packages/experimental. Move
all the SELinux scripts/code from that directory to CTS
proper.
Bug: 17301255
Bug: 17593625
Change-Id: If43efc6aab803d1089adc03bafe8621955778730
diff --git a/tools/selinux/test/testrunner.py b/tools/selinux/test/testrunner.py
new file mode 100755
index 0000000..bc424e9
--- /dev/null
+++ b/tools/selinux/test/testrunner.py
@@ -0,0 +1,442 @@
+#!/usr/bin/python
+import sys
+sys.path.append('../src')
+import unittest
+import SELinux_CTS
+from SELinux_CTS import SELinuxPolicy
+
+policy_file_name = 'policy_test.conf'
+types = set([
+ 'bluetooth',
+ 'healthd',
+ 'healthd_exec',
+ 'testTYPE' ]) #testTYPE added for neverallow rule to make sense
+attributes = {
+ 'domain': set(['bluetooth', 'healthd', 'testTYPE']),
+ 'unconfineddomain': set(['bluetooth']),
+ 'appdomain': set(['bluetooth', 'testTYPE']),
+ 'file_type': set(['healthd_exec']),
+ 'exec_type': set(['healthd_exec']) }
+common_classes = {
+ 'file': set([
+ 'ioctl',
+ 'read',
+ 'write',
+ 'create',
+ 'getattr',
+ 'setattr',
+ 'lock',
+ 'relabelfrom',
+ 'relabelto',
+ 'append',
+ 'unlink',
+ 'link',
+ 'rename',
+ 'execute',
+ 'swapon',
+ 'quotaon',
+ 'mounton' ]) }
+classes = {
+ 'capability': set([
+ 'chown',
+ 'dac_override',
+ 'dac_read_search',
+ 'fowner',
+ 'fsetid',
+ 'kill',
+ 'setgid',
+ 'setuid',
+ 'setpcap',
+ 'linux_immutable',
+ 'net_bind_service',
+ 'net_broadcast',
+ 'net_admin',
+ 'net_raw',
+ 'ipc_lock',
+ 'ipc_owner',
+ 'sys_module',
+ 'sys_rawio',
+ 'sys_chroot',
+ 'sys_ptrace',
+ 'sys_pacct',
+ 'sys_admin',
+ 'sys_boot',
+ 'sys_nice',
+ 'sys_resource',
+ 'sys_time',
+ 'sys_tty_config',
+ 'mknod',
+ 'lease',
+ 'audit_write',
+ 'audit_control',
+ 'setfcap' ]),
+ 'file': (set([
+ 'execute_no_trans',
+ 'entrypoint',
+ 'execmod',
+ 'open',
+ 'audit_access' ]) | common_classes['file']) }
+
+# allow healthd healthd_exec:file { entrypoint read execute };
+allow_rules = [
+ { 'source_types': {
+ 'set': set([
+ 'healthd']),
+ 'flags': { 'complement': False } },
+ 'target_types': {
+ 'set': set([
+ 'healthd_exec']),
+ 'flags': { 'complement': False } },
+ 'classes': {
+ 'set': set([
+ 'file']),
+ 'flags': { 'complement': False } },
+ 'permissions': {
+ 'set': set([
+ 'entrypoint',
+ 'read',
+ 'execute' ]),
+ 'flags': { 'complement': False } } } ]
+
+# neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
+neverallow_rules = [
+ { 'source_types': {
+ 'set': set([
+ 'appdomain',
+ '-unconfineddomain',
+ '-bluetooth' ]),
+ 'flags': { 'complement': False } },
+ 'target_types': {
+ 'set': set([
+ 'self']),
+ 'flags': { 'complement': False } },
+ 'classes': {
+ 'set': set([
+ 'capability']),
+ 'flags': { 'complement': False } },
+ 'permissions': {
+ 'set': set([
+ '*' ]),
+ 'flags': { 'complement': False } } } ]
+
+expected_final_allow_list = [
+ [ ('healthd', 'healthd_exec', 'file', 'entrypoint'),
+ ('healthd', 'healthd_exec', 'file', 'read'),
+ ('healthd', 'healthd_exec', 'file', 'execute') ] ]
+
+expected_final_neverallow_list = [
+ [ ('testTYPE', 'testTYPE', 'capability', 'chown'),
+ ('testTYPE', 'testTYPE', 'capability', 'dac_override'),
+ ('testTYPE', 'testTYPE', 'capability', 'dac_read_search'),
+ ('testTYPE', 'testTYPE', 'capability', 'fowner'),
+ ('testTYPE', 'testTYPE', 'capability', 'fsetid'),
+ ('testTYPE', 'testTYPE', 'capability', 'kill'),
+ ('testTYPE', 'testTYPE', 'capability', 'setgid'),
+ ('testTYPE', 'testTYPE', 'capability', 'setuid'),
+ ('testTYPE', 'testTYPE', 'capability', 'setpcap'),
+ ('testTYPE', 'testTYPE', 'capability', 'linux_immutable'),
+ ('testTYPE', 'testTYPE', 'capability', 'net_bind_service'),
+ ('testTYPE', 'testTYPE', 'capability', 'net_broadcast'),
+ ('testTYPE', 'testTYPE', 'capability', 'net_admin'),
+ ('testTYPE', 'testTYPE', 'capability', 'net_raw'),
+ ('testTYPE', 'testTYPE', 'capability', 'ipc_lock'),
+ ('testTYPE', 'testTYPE', 'capability', 'ipc_owner'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_module'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_rawio'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_chroot'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_ptrace'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_pacct'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_admin'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_boot'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_nice'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_resource'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_time'),
+ ('testTYPE', 'testTYPE', 'capability', 'sys_tty_config'),
+ ('testTYPE', 'testTYPE', 'capability', 'mknod'),
+ ('testTYPE', 'testTYPE', 'capability', 'lease'),
+ ('testTYPE', 'testTYPE', 'capability', 'audit_write'),
+ ('testTYPE', 'testTYPE', 'capability', 'audit_control'),
+ ('testTYPE', 'testTYPE', 'capability', 'setfcap') ] ]
+
+
+class SELinuxPolicyTests(unittest.TestCase):
+
+
+ def setUp(self):
+ self.test_policy = SELinuxPolicy()
+ self.test_file = open(policy_file_name, 'r')
+ self.test_policy.types = types
+ self.test_policy.attributes = attributes
+ self.test_policy.common_classes = common_classes
+ self.test_policy.classes = classes
+ self.test_policy.allow_rules = allow_rules
+ self.test_policy.neverallow_rules = neverallow_rules
+ return
+
+ def testExpandAvcRule(self):
+ #TODO: add more examples here to cover different cases
+ expanded_allow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.allow_rules[0])
+ for a in expected_final_allow_list[0]:
+ self.failUnless(a in expanded_allow_list)
+ expanded_neverallow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.neverallow_rules[0])
+ for n in expected_final_neverallow_list[0]:
+ self.failUnless(n in expanded_neverallow_list)
+
+ def testExpandBrackets(self):
+ #test position without bracket:
+ self.test_file.seek(279)
+ self.failIf(SELinux_CTS.expand_brackets(self.test_file))
+
+ #test position with bracket:
+ self.test_file.seek(26123)
+ self.failUnless(SELinux_CTS.expand_brackets(self.test_file) == " entrypoint read execute ")
+
+ #test position with nested brackets:
+ self.test_file.seek(26873)
+ self.failUnless(SELinux_CTS.expand_brackets(self.test_file)
+ == " dir chr_file blk_file file lnk_file sock_file fifo_file ")
+
+ def testGetAvcRuleComponent(self):
+ #test against normal ('allow healthd healthd_exec:file ...)
+ self.test_file.seek(26096)
+ normal_src = { 'flags': { 'complement': False },
+ 'set': set(['healthd']) }
+ normal_tgt = { 'flags': { 'complement': False },
+ 'set': set(['healthd_exec']) }
+ normal_class = { 'flags': { 'complement': False },
+ 'set': set(['file']) }
+ normal_perm = { 'flags': { 'complement': False },
+ 'set': set(['entrypoint', 'read', 'execute']) }
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == normal_src)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == normal_tgt)
+ c = SELinux_CTS.advance_past_whitespace(self.test_file)
+ if c == ':':
+ self.test_file.read(1)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == normal_class)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == normal_perm)
+
+ #test against 'hard' ('init {fs_type ...' )
+ self.test_file.seek(26838)
+ hard_src = { 'flags': { 'complement': False },
+ 'set': set(['init']) }
+ hard_tgt = { 'flags': { 'complement': False },
+ 'set': set(['fs_type', 'dev_type', 'file_type']) }
+ hard_class = { 'flags': { 'complement': False },
+ 'set': set(['dir', 'chr_file', 'blk_file', 'file', 'lnk_file', 'sock_file', 'fifo_file']) }
+ hard_perm = { 'flags': { 'complement': False },
+ 'set': set(['relabelto']) }
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == hard_src)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == hard_tgt)
+ #mimic ':' check:
+ c = SELinux_CTS.advance_past_whitespace(self.test_file)
+ if c == ':':
+ self.test_file.read(1)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == hard_class)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == hard_perm)
+
+ #test against 'multi-line' ('init {fs_type ...' )
+ self.test_file.seek(26967)
+ multi_src = { 'flags': { 'complement': False },
+ 'set': set(['appdomain', '-unconfineddomain']) }
+ multi_tgt = { 'flags': { 'complement': False },
+ 'set': set(['audio_device', 'camera_device', 'dm_device', 'radio_device', 'gps_device', 'rpmsg_device']) }
+ multi_class = { 'flags': { 'complement': False },
+ 'set': set(['chr_file']) }
+ multi_perm = { 'flags': { 'complement': False },
+ 'set': set(['read', 'write']) }
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == multi_src)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == multi_tgt)
+ c = SELinux_CTS.advance_past_whitespace(self.test_file)
+ if c == ':':
+ self.test_file.read(1)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == multi_class)
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == multi_perm)
+
+ #test against 'complement'
+ self.test_file.seek(26806)
+ complement = { 'flags': { 'complement': True },
+ 'set': set(['entrypoint', 'relabelto']) }
+ self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
+ == complement)
+
+ def testGetLineType(self):
+ self.failUnless(SELinux_CTS.get_line_type('type bluetooth, domain;')
+ == SELinux_CTS.TYPE)
+ self.failUnless(SELinux_CTS.get_line_type('attribute unconfineddomain;')
+ == SELinux_CTS.ATTRIBUTE)
+ self.failUnless(SELinux_CTS.get_line_type('typeattribute bluetooth appdomain;')
+ == SELinux_CTS.TYPEATTRIBUTE)
+ self.failUnless(SELinux_CTS.get_line_type('class file')
+ == SELinux_CTS.CLASS)
+ self.failUnless(SELinux_CTS.get_line_type('common file')
+ == SELinux_CTS.COMMON)
+ self.failUnless(SELinux_CTS.get_line_type('allow healthd healthd_exec:file { entrypoint read execute };')
+ == SELinux_CTS.ALLOW_RULE)
+ self.failUnless(SELinux_CTS.get_line_type('neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;')
+ == SELinux_CTS.NEVERALLOW_RULE)
+ self.failUnless(SELinux_CTS.get_line_type('# FLASK')
+ == SELinux_CTS.OTHER)
+
+ def testIsMultiLine(self):
+ self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPE))
+ self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.ATTRIBUTE))
+ self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPEATTRIBUTE))
+ self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.CLASS))
+ self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.COMMON))
+ self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.ALLOW_RULE))
+ self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.NEVERALLOW_RULE))
+ self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.OTHER))
+
+ def testProcessInheritsSegment(self):
+ inherit_offset = 448 # needs changing if file changes
+ self.test_file.seek(inherit_offset, 0)
+ inherit_result = SELinux_CTS.process_inherits_segment(self.test_file)
+ self.failUnless(inherit_result == 'file')
+ return
+
+ def testFromFileName(self):
+ #using a special file, since the test_file has some lines which don't 'jive'
+ clean_policy_file = 'policy_clean_test.conf'
+ from_file_policy = SELinuxPolicy()
+ from_file_policy.from_file_name(clean_policy_file)
+ self.failUnless(from_file_policy.types == self.test_policy.types)
+ self.failUnless(from_file_policy.attributes == self.test_policy.attributes)
+ self.failUnless(from_file_policy.classes == self.test_policy.classes)
+ self.failUnless(from_file_policy.common_classes == self.test_policy.common_classes)
+ self.failUnless(from_file_policy.allow_rules == self.test_policy.allow_rules)
+ self.failUnless(from_file_policy.neverallow_rules == self.test_policy.neverallow_rules)
+
+ def testExpandPermissions(self):
+ #test general case
+ test_class_obj = 'file'
+ general_set = set(['read', 'write', 'execute'])
+ expanded_general_set = general_set
+ self.failUnless(self.test_policy.expand_permissions(test_class_obj, general_set)
+ == general_set)
+ star_set = set(['*'])
+ expanded_star_set = self.test_policy.classes['file'] #everything in the class
+ self.failUnless(self.test_policy.expand_permissions(test_class_obj, star_set)
+ == expanded_star_set)
+ complement_set = set(['*', '-open'])
+ expanded_complement_set = self.test_policy.classes['file'] - set(['open'])
+ self.failUnless(self.test_policy.expand_permissions(test_class_obj, complement_set)
+ == expanded_complement_set)
+
+ def testExpandTypes(self):
+
+ #test general case and '-' handling
+ test_source_set = set([
+ 'domain',
+ '-bluetooth' ])
+ expanded_test_source_set = set([
+ 'healthd', 'testTYPE' ])
+ self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set)
+
+ #test '*' handling
+ test_source_set = set([ '*' ])
+ expanded_test_source_set = set([
+ 'bluetooth', 'healthd', 'testTYPE' ])
+ self.failUnless(self.test_policy.expand_types(test_source_set) == types)
+ #test - handling
+ test_source_set = set([
+ '*',
+ '-bluetooth'])
+ expanded_test_source_set = set([
+ 'healthd', 'healthd_exec', 'testTYPE' ])
+ self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set)
+
+ def testProcessAttributeLine(self):
+ attribute_policy = SELinuxPolicy()
+ #test with 'normal input'
+ test_normal_string = 'attribute TEST_att;'
+ test_attribute = 'TEST_att'
+ attribute_policy.process_attribute_line(test_normal_string)
+ self.failUnless( test_attribute in attribute_policy.attributes)
+ #TODO: test on bogus inputs
+
+ def testProcessClassLine(self):
+ class_policy = SELinuxPolicy()
+ #offsets need changing if test file changes
+ common_offset = 279
+ class_initial_offset = 212
+ class_perm_offset = 437
+ self.test_file.seek(common_offset, 0)
+ line = self.test_file.readline()
+ class_policy.process_common_line(line, self.test_file)
+ self.test_file.seek(class_initial_offset, 0)
+ line = self.test_file.readline()
+ class_policy.process_class_line(line, self.test_file)
+ self.failUnless('file' in class_policy.classes)
+ self.test_file.seek(class_perm_offset, 0)
+ line = self.test_file.readline()
+ class_policy.process_class_line(line, self.test_file)
+ self.failUnless(class_policy.classes['file'] == classes['file'])
+
+ def testProcessCommonLine(self):
+ common_policy = SELinuxPolicy()
+ common_offset = 279 # needs changing if file changes
+ self.test_file.seek(common_offset, 0)
+ line = self.test_file.readline()
+ common_policy.process_common_line(line, self.test_file)
+ self.failUnless('file' in common_policy.common_classes )
+ self.failUnless(common_policy.common_classes['file'] == common_classes['file'])
+
+ def testProcessAvcRuleLine(self):
+ avc_policy = SELinuxPolicy()
+ allow_offset = 26091 # needs changing if file changes
+ neverallow_offset = 26311 # needs changing if file changes
+ self.test_file.seek(allow_offset, 0)
+ line = self.test_file.readline()
+ avc_policy.process_avc_rule_line(line, self.test_file)
+ self.failUnless(avc_policy.allow_rules[0] == allow_rules[0] ) # always '0'?
+ self.test_file.seek(neverallow_offset, 0)
+ line = self.test_file.readline()
+ avc_policy.process_avc_rule_line(line, self.test_file)
+ self.failUnless(avc_policy.neverallow_rules[0] == neverallow_rules[0] ) # always '0'?
+
+ def testProcessTypeLine(self):
+ type_policy = SELinuxPolicy()
+ test_normal_string = 'type TEST_type, TEST_att1, TEST_att2;'
+ test_type = 'TEST_type'
+ test_atts = ['TEST_att1', 'TEST_att2']
+ #test with 'normal input'
+ type_policy.process_type_line(test_normal_string)
+ self.failUnless(test_type in type_policy.types)
+ for a in test_atts:
+ self.failUnless(a in type_policy.attributes)
+ self.failUnless(test_type in type_policy.attributes[a])
+ #TODO: test with domain only, no attributes
+ # and test on bogus inputs
+
+ def testProcessTypeattributeLine(self):
+ typ_att_policy = SELinuxPolicy()
+ test_normal_string = 'typeattribute TEST_type TEST_att1, TEST_att2;'
+ test_type = 'TEST_type'
+ test_atts = ['TEST_att1', 'TEST_att2']
+ #test with 'normal input' (type should already be declared)
+ typ_att_policy.process_type_line('type ' + test_type + ';')
+ typ_att_policy.process_typeattribute_line(test_normal_string)
+ self.failUnless(test_type in typ_att_policy.types)
+ for a in test_atts:
+ self.failUnless(a in typ_att_policy.attributes)
+ self.failUnless(test_type in typ_att_policy.attributes[a])
+ #TODO: test with domain only, no attributes
+ # and test on bogus inputs
+
+def main():
+ unittest.main()
+
+if __name__ == '__main__':
+ main()