blob: bc424e9193ff6b7f53a771a4bc8fc13a52716dba [file] [log] [blame]
Nick Kralevichc7624852014-10-01 11:23:51 -07001#!/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()