#!/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()