1#!/usr/bin/python 2import sys 3sys.path.append('../src') 4import unittest 5import SELinux_CTS 6from SELinux_CTS import SELinuxPolicy 7 8policy_file_name = 'policy_test.conf' 9types = set([ 10 'bluetooth', 11 'healthd', 12 'healthd_exec', 13 'testTYPE' ]) #testTYPE added for neverallow rule to make sense 14attributes = { 15 'domain': set(['bluetooth', 'healthd', 'testTYPE']), 16 'unconfineddomain': set(['bluetooth']), 17 'appdomain': set(['bluetooth', 'testTYPE']), 18 'file_type': set(['healthd_exec']), 19 'exec_type': set(['healthd_exec']) } 20common_classes = { 21 'file': set([ 22 'ioctl', 23 'read', 24 'write', 25 'create', 26 'getattr', 27 'setattr', 28 'lock', 29 'relabelfrom', 30 'relabelto', 31 'append', 32 'unlink', 33 'link', 34 'rename', 35 'execute', 36 'swapon', 37 'quotaon', 38 'mounton' ]) } 39classes = { 40 'capability': set([ 41 'chown', 42 'dac_override', 43 'dac_read_search', 44 'fowner', 45 'fsetid', 46 'kill', 47 'setgid', 48 'setuid', 49 'setpcap', 50 'linux_immutable', 51 'net_bind_service', 52 'net_broadcast', 53 'net_admin', 54 'net_raw', 55 'ipc_lock', 56 'ipc_owner', 57 'sys_module', 58 'sys_rawio', 59 'sys_chroot', 60 'sys_ptrace', 61 'sys_pacct', 62 'sys_admin', 63 'sys_boot', 64 'sys_nice', 65 'sys_resource', 66 'sys_time', 67 'sys_tty_config', 68 'mknod', 69 'lease', 70 'audit_write', 71 'audit_control', 72 'setfcap' ]), 73 'file': (set([ 74 'execute_no_trans', 75 'entrypoint', 76 'execmod', 77 'open', 78 'audit_access' ]) | common_classes['file']) } 79 80# allow healthd healthd_exec:file { entrypoint read execute }; 81allow_rules = [ 82 { 'source_types': { 83 'set': set([ 84 'healthd']), 85 'flags': { 'complement': False } }, 86 'target_types': { 87 'set': set([ 88 'healthd_exec']), 89 'flags': { 'complement': False } }, 90 'classes': { 91 'set': set([ 92 'file']), 93 'flags': { 'complement': False } }, 94 'permissions': { 95 'set': set([ 96 'entrypoint', 97 'read', 98 'execute' ]), 99 'flags': { 'complement': False } } } ] 100 101# neverallow { appdomain -unconfineddomain -bluetooth } self:capability *; 102neverallow_rules = [ 103 { 'source_types': { 104 'set': set([ 105 'appdomain', 106 '-unconfineddomain', 107 '-bluetooth' ]), 108 'flags': { 'complement': False } }, 109 'target_types': { 110 'set': set([ 111 'self']), 112 'flags': { 'complement': False } }, 113 'classes': { 114 'set': set([ 115 'capability']), 116 'flags': { 'complement': False } }, 117 'permissions': { 118 'set': set([ 119 '*' ]), 120 'flags': { 'complement': False } } } ] 121 122expected_final_allow_list = [ 123 [ ('healthd', 'healthd_exec', 'file', 'entrypoint'), 124 ('healthd', 'healthd_exec', 'file', 'read'), 125 ('healthd', 'healthd_exec', 'file', 'execute') ] ] 126 127expected_final_neverallow_list = [ 128 [ ('testTYPE', 'testTYPE', 'capability', 'chown'), 129 ('testTYPE', 'testTYPE', 'capability', 'dac_override'), 130 ('testTYPE', 'testTYPE', 'capability', 'dac_read_search'), 131 ('testTYPE', 'testTYPE', 'capability', 'fowner'), 132 ('testTYPE', 'testTYPE', 'capability', 'fsetid'), 133 ('testTYPE', 'testTYPE', 'capability', 'kill'), 134 ('testTYPE', 'testTYPE', 'capability', 'setgid'), 135 ('testTYPE', 'testTYPE', 'capability', 'setuid'), 136 ('testTYPE', 'testTYPE', 'capability', 'setpcap'), 137 ('testTYPE', 'testTYPE', 'capability', 'linux_immutable'), 138 ('testTYPE', 'testTYPE', 'capability', 'net_bind_service'), 139 ('testTYPE', 'testTYPE', 'capability', 'net_broadcast'), 140 ('testTYPE', 'testTYPE', 'capability', 'net_admin'), 141 ('testTYPE', 'testTYPE', 'capability', 'net_raw'), 142 ('testTYPE', 'testTYPE', 'capability', 'ipc_lock'), 143 ('testTYPE', 'testTYPE', 'capability', 'ipc_owner'), 144 ('testTYPE', 'testTYPE', 'capability', 'sys_module'), 145 ('testTYPE', 'testTYPE', 'capability', 'sys_rawio'), 146 ('testTYPE', 'testTYPE', 'capability', 'sys_chroot'), 147 ('testTYPE', 'testTYPE', 'capability', 'sys_ptrace'), 148 ('testTYPE', 'testTYPE', 'capability', 'sys_pacct'), 149 ('testTYPE', 'testTYPE', 'capability', 'sys_admin'), 150 ('testTYPE', 'testTYPE', 'capability', 'sys_boot'), 151 ('testTYPE', 'testTYPE', 'capability', 'sys_nice'), 152 ('testTYPE', 'testTYPE', 'capability', 'sys_resource'), 153 ('testTYPE', 'testTYPE', 'capability', 'sys_time'), 154 ('testTYPE', 'testTYPE', 'capability', 'sys_tty_config'), 155 ('testTYPE', 'testTYPE', 'capability', 'mknod'), 156 ('testTYPE', 'testTYPE', 'capability', 'lease'), 157 ('testTYPE', 'testTYPE', 'capability', 'audit_write'), 158 ('testTYPE', 'testTYPE', 'capability', 'audit_control'), 159 ('testTYPE', 'testTYPE', 'capability', 'setfcap') ] ] 160 161 162class SELinuxPolicyTests(unittest.TestCase): 163 164 165 def setUp(self): 166 self.test_policy = SELinuxPolicy() 167 self.test_file = open(policy_file_name, 'r') 168 self.test_policy.types = types 169 self.test_policy.attributes = attributes 170 self.test_policy.common_classes = common_classes 171 self.test_policy.classes = classes 172 self.test_policy.allow_rules = allow_rules 173 self.test_policy.neverallow_rules = neverallow_rules 174 return 175 176 def testExpandAvcRule(self): 177 #TODO: add more examples here to cover different cases 178 expanded_allow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.allow_rules[0]) 179 for a in expected_final_allow_list[0]: 180 self.failUnless(a in expanded_allow_list) 181 expanded_neverallow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.neverallow_rules[0]) 182 for n in expected_final_neverallow_list[0]: 183 self.failUnless(n in expanded_neverallow_list) 184 185 def testExpandBrackets(self): 186 #test position without bracket: 187 self.test_file.seek(279) 188 self.failIf(SELinux_CTS.expand_brackets(self.test_file)) 189 190 #test position with bracket: 191 self.test_file.seek(26123) 192 self.failUnless(SELinux_CTS.expand_brackets(self.test_file) == " entrypoint read execute ") 193 194 #test position with nested brackets: 195 self.test_file.seek(26873) 196 self.failUnless(SELinux_CTS.expand_brackets(self.test_file) 197 == " dir chr_file blk_file file lnk_file sock_file fifo_file ") 198 199 def testGetAvcRuleComponent(self): 200 #test against normal ('allow healthd healthd_exec:file ...) 201 self.test_file.seek(26096) 202 normal_src = { 'flags': { 'complement': False }, 203 'set': set(['healthd']) } 204 normal_tgt = { 'flags': { 'complement': False }, 205 'set': set(['healthd_exec']) } 206 normal_class = { 'flags': { 'complement': False }, 207 'set': set(['file']) } 208 normal_perm = { 'flags': { 'complement': False }, 209 'set': set(['entrypoint', 'read', 'execute']) } 210 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 211 == normal_src) 212 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 213 == normal_tgt) 214 c = SELinux_CTS.advance_past_whitespace(self.test_file) 215 if c == ':': 216 self.test_file.read(1) 217 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 218 == normal_class) 219 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 220 == normal_perm) 221 222 #test against 'hard' ('init {fs_type ...' ) 223 self.test_file.seek(26838) 224 hard_src = { 'flags': { 'complement': False }, 225 'set': set(['init']) } 226 hard_tgt = { 'flags': { 'complement': False }, 227 'set': set(['fs_type', 'dev_type', 'file_type']) } 228 hard_class = { 'flags': { 'complement': False }, 229 'set': set(['dir', 'chr_file', 'blk_file', 'file', 'lnk_file', 'sock_file', 'fifo_file']) } 230 hard_perm = { 'flags': { 'complement': False }, 231 'set': set(['relabelto']) } 232 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 233 == hard_src) 234 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 235 == hard_tgt) 236 #mimic ':' check: 237 c = SELinux_CTS.advance_past_whitespace(self.test_file) 238 if c == ':': 239 self.test_file.read(1) 240 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 241 == hard_class) 242 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 243 == hard_perm) 244 245 #test against 'multi-line' ('init {fs_type ...' ) 246 self.test_file.seek(26967) 247 multi_src = { 'flags': { 'complement': False }, 248 'set': set(['appdomain', '-unconfineddomain']) } 249 multi_tgt = { 'flags': { 'complement': False }, 250 'set': set(['audio_device', 'camera_device', 'dm_device', 'radio_device', 'gps_device', 'rpmsg_device']) } 251 multi_class = { 'flags': { 'complement': False }, 252 'set': set(['chr_file']) } 253 multi_perm = { 'flags': { 'complement': False }, 254 'set': set(['read', 'write']) } 255 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 256 == multi_src) 257 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 258 == multi_tgt) 259 c = SELinux_CTS.advance_past_whitespace(self.test_file) 260 if c == ':': 261 self.test_file.read(1) 262 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 263 == multi_class) 264 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 265 == multi_perm) 266 267 #test against 'complement' 268 self.test_file.seek(26806) 269 complement = { 'flags': { 'complement': True }, 270 'set': set(['entrypoint', 'relabelto']) } 271 self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file) 272 == complement) 273 274 def testGetLineType(self): 275 self.failUnless(SELinux_CTS.get_line_type('type bluetooth, domain;') 276 == SELinux_CTS.TYPE) 277 self.failUnless(SELinux_CTS.get_line_type('attribute unconfineddomain;') 278 == SELinux_CTS.ATTRIBUTE) 279 self.failUnless(SELinux_CTS.get_line_type('typeattribute bluetooth appdomain;') 280 == SELinux_CTS.TYPEATTRIBUTE) 281 self.failUnless(SELinux_CTS.get_line_type('class file') 282 == SELinux_CTS.CLASS) 283 self.failUnless(SELinux_CTS.get_line_type('common file') 284 == SELinux_CTS.COMMON) 285 self.failUnless(SELinux_CTS.get_line_type('allow healthd healthd_exec:file { entrypoint read execute };') 286 == SELinux_CTS.ALLOW_RULE) 287 self.failUnless(SELinux_CTS.get_line_type('neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;') 288 == SELinux_CTS.NEVERALLOW_RULE) 289 self.failUnless(SELinux_CTS.get_line_type('# FLASK') 290 == SELinux_CTS.OTHER) 291 292 def testIsMultiLine(self): 293 self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPE)) 294 self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.ATTRIBUTE)) 295 self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPEATTRIBUTE)) 296 self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.CLASS)) 297 self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.COMMON)) 298 self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.ALLOW_RULE)) 299 self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.NEVERALLOW_RULE)) 300 self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.OTHER)) 301 302 def testProcessInheritsSegment(self): 303 inherit_offset = 448 # needs changing if file changes 304 self.test_file.seek(inherit_offset, 0) 305 inherit_result = SELinux_CTS.process_inherits_segment(self.test_file) 306 self.failUnless(inherit_result == 'file') 307 return 308 309 def testFromFileName(self): 310 #using a special file, since the test_file has some lines which don't 'jive' 311 clean_policy_file = 'policy_clean_test.conf' 312 from_file_policy = SELinuxPolicy() 313 from_file_policy.from_file_name(clean_policy_file) 314 self.failUnless(from_file_policy.types == self.test_policy.types) 315 self.failUnless(from_file_policy.attributes == self.test_policy.attributes) 316 self.failUnless(from_file_policy.classes == self.test_policy.classes) 317 self.failUnless(from_file_policy.common_classes == self.test_policy.common_classes) 318 self.failUnless(from_file_policy.allow_rules == self.test_policy.allow_rules) 319 self.failUnless(from_file_policy.neverallow_rules == self.test_policy.neverallow_rules) 320 321 def testExpandPermissions(self): 322 #test general case 323 test_class_obj = 'file' 324 general_set = set(['read', 'write', 'execute']) 325 expanded_general_set = general_set 326 self.failUnless(self.test_policy.expand_permissions(test_class_obj, general_set) 327 == general_set) 328 star_set = set(['*']) 329 expanded_star_set = self.test_policy.classes['file'] #everything in the class 330 self.failUnless(self.test_policy.expand_permissions(test_class_obj, star_set) 331 == expanded_star_set) 332 complement_set = set(['*', '-open']) 333 expanded_complement_set = self.test_policy.classes['file'] - set(['open']) 334 self.failUnless(self.test_policy.expand_permissions(test_class_obj, complement_set) 335 == expanded_complement_set) 336 337 def testExpandTypes(self): 338 339 #test general case and '-' handling 340 test_source_set = set([ 341 'domain', 342 '-bluetooth' ]) 343 expanded_test_source_set = set([ 344 'healthd', 'testTYPE' ]) 345 self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set) 346 347 #test '*' handling 348 test_source_set = set([ '*' ]) 349 expanded_test_source_set = set([ 350 'bluetooth', 'healthd', 'testTYPE' ]) 351 self.failUnless(self.test_policy.expand_types(test_source_set) == types) 352 #test - handling 353 test_source_set = set([ 354 '*', 355 '-bluetooth']) 356 expanded_test_source_set = set([ 357 'healthd', 'healthd_exec', 'testTYPE' ]) 358 self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set) 359 360 def testProcessAttributeLine(self): 361 attribute_policy = SELinuxPolicy() 362 #test with 'normal input' 363 test_normal_string = 'attribute TEST_att;' 364 test_attribute = 'TEST_att' 365 attribute_policy.process_attribute_line(test_normal_string) 366 self.failUnless( test_attribute in attribute_policy.attributes) 367 #TODO: test on bogus inputs 368 369 def testProcessClassLine(self): 370 class_policy = SELinuxPolicy() 371 #offsets need changing if test file changes 372 common_offset = 279 373 class_initial_offset = 212 374 class_perm_offset = 437 375 self.test_file.seek(common_offset, 0) 376 line = self.test_file.readline() 377 class_policy.process_common_line(line, self.test_file) 378 self.test_file.seek(class_initial_offset, 0) 379 line = self.test_file.readline() 380 class_policy.process_class_line(line, self.test_file) 381 self.failUnless('file' in class_policy.classes) 382 self.test_file.seek(class_perm_offset, 0) 383 line = self.test_file.readline() 384 class_policy.process_class_line(line, self.test_file) 385 self.failUnless(class_policy.classes['file'] == classes['file']) 386 387 def testProcessCommonLine(self): 388 common_policy = SELinuxPolicy() 389 common_offset = 279 # needs changing if file changes 390 self.test_file.seek(common_offset, 0) 391 line = self.test_file.readline() 392 common_policy.process_common_line(line, self.test_file) 393 self.failUnless('file' in common_policy.common_classes ) 394 self.failUnless(common_policy.common_classes['file'] == common_classes['file']) 395 396 def testProcessAvcRuleLine(self): 397 avc_policy = SELinuxPolicy() 398 allow_offset = 26091 # needs changing if file changes 399 neverallow_offset = 26311 # needs changing if file changes 400 self.test_file.seek(allow_offset, 0) 401 line = self.test_file.readline() 402 avc_policy.process_avc_rule_line(line, self.test_file) 403 self.failUnless(avc_policy.allow_rules[0] == allow_rules[0] ) # always '0'? 404 self.test_file.seek(neverallow_offset, 0) 405 line = self.test_file.readline() 406 avc_policy.process_avc_rule_line(line, self.test_file) 407 self.failUnless(avc_policy.neverallow_rules[0] == neverallow_rules[0] ) # always '0'? 408 409 def testProcessTypeLine(self): 410 type_policy = SELinuxPolicy() 411 test_normal_string = 'type TEST_type, TEST_att1, TEST_att2;' 412 test_type = 'TEST_type' 413 test_atts = ['TEST_att1', 'TEST_att2'] 414 #test with 'normal input' 415 type_policy.process_type_line(test_normal_string) 416 self.failUnless(test_type in type_policy.types) 417 for a in test_atts: 418 self.failUnless(a in type_policy.attributes) 419 self.failUnless(test_type in type_policy.attributes[a]) 420 #TODO: test with domain only, no attributes 421 # and test on bogus inputs 422 423 def testProcessTypeattributeLine(self): 424 typ_att_policy = SELinuxPolicy() 425 test_normal_string = 'typeattribute TEST_type TEST_att1, TEST_att2;' 426 test_type = 'TEST_type' 427 test_atts = ['TEST_att1', 'TEST_att2'] 428 #test with 'normal input' (type should already be declared) 429 typ_att_policy.process_type_line('type ' + test_type + ';') 430 typ_att_policy.process_typeattribute_line(test_normal_string) 431 self.failUnless(test_type in typ_att_policy.types) 432 for a in test_atts: 433 self.failUnless(a in typ_att_policy.attributes) 434 self.failUnless(test_type in typ_att_policy.attributes[a]) 435 #TODO: test with domain only, no attributes 436 # and test on bogus inputs 437 438def main(): 439 unittest.main() 440 441if __name__ == '__main__': 442 main() 443