blob: b8255c781cc75dd4d3f8af7bad4d025c1f39e02c [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001import unittest
2import re
3import os
4import string
5from oeqa.runtime.case import OERuntimeTestCase
6from oeqa.core.decorator.depends import OETestDepends
7from oeqa.runtime.decorator.package import OEHasPackage
8from oeqa.core.decorator.data import skipIfNotFeature
9
10MAX_LABEL_LEN = 255
11LABEL = "a" * MAX_LABEL_LEN
12
13class SmackBasicTest(OERuntimeTestCase):
14 ''' base smack test '''
15
16 @classmethod
17 def setUpClass(cls):
18 cls.smack_path = ""
19 cls.current_label = ""
20 cls.uid = 1000
21
22 @skipIfNotFeature('smack',
23 'Test requires smack to be in DISTRO_FEATURES')
24 @OEHasPackage(['smack-test'])
25 @OETestDepends(['ssh.SSHTest.test_ssh'])
26 def test_smack_basic(self):
27 status, output = self.target.run("grep smack /proc/mounts | awk '{print $2}'")
28 self.smack_path = output
29 status,output = self.target.run("cat /proc/self/attr/current")
30 self.current_label = output.strip()
31
Brad Bishopc342db32019-05-15 21:57:59 -040032 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
33 def test_add_access_label(self):
34 ''' Test if chsmack can correctly set a SMACK label '''
35 filename = "/tmp/test_access_label"
36 self.target.run("touch %s" %filename)
37 status, output = self.target.run("chsmack -a %s %s" %(LABEL, filename))
38 self.assertEqual(
39 status, 0,
40 "Cannot set smack access label. "
41 "Status and output: %d %s" %(status, output))
42 status, output = self.target.run("chsmack %s" %filename)
43 self.target.run("rm %s" %filename)
44 m = re.search('(?<=access=")\S+(?=")', output)
45 if m is None:
46 self.fail("Did not find access attribute")
47 else:
48 label_retrieved = m .group(0)
49 self.assertEqual(
50 LABEL, label_retrieved,
51 "label not set correctly. expected and gotten: "
52 "%s %s" %(LABEL,label_retrieved))
53
54
Brad Bishopc342db32019-05-15 21:57:59 -040055 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
56 def test_add_exec_label(self):
57 '''Test if chsmack can correctly set a SMACK Exec label'''
58 filename = "/tmp/test_exec_label"
59 self.target.run("touch %s" %filename)
60 status, output = self.target.run("chsmack -e %s %s" %(LABEL, filename))
61 self.assertEqual(
62 status, 0,
63 "Cannot set smack exec label. "
64 "Status and output: %d %s" %(status, output))
65 status, output = self.target.run("chsmack %s" %filename)
66 self.target.run("rm %s" %filename)
67 m= re.search('(?<=execute=")\S+(?=")', output)
68 if m is None:
69 self.fail("Did not find execute attribute")
70 else:
71 label_retrieved = m.group(0)
72 self.assertEqual(
73 LABEL, label_retrieved,
74 "label not set correctly. expected and gotten: " +
75 "%s %s" %(LABEL,label_retrieved))
76
77
Brad Bishopc342db32019-05-15 21:57:59 -040078 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
79 def test_add_mmap_label(self):
80 '''Test if chsmack can correctly set a SMACK mmap label'''
81 filename = "/tmp/test_exec_label"
82 self.target.run("touch %s" %filename)
83 status, output = self.target.run("chsmack -m %s %s" %(LABEL, filename))
84 self.assertEqual(
85 status, 0,
86 "Cannot set smack mmap label. "
87 "Status and output: %d %s" %(status, output))
88 status, output = self.target.run("chsmack %s" %filename)
89 self.target.run("rm %s" %filename)
90 m = re.search('(?<=mmap=")\S+(?=")', output)
91 if m is None:
92 self.fail("Did not find mmap attribute")
93 else:
94 label_retrieved = m.group(0)
95 self.assertEqual(
96 LABEL, label_retrieved,
97 "label not set correctly. expected and gotten: " +
98 "%s %s" %(LABEL,label_retrieved))
99
100
Brad Bishopc342db32019-05-15 21:57:59 -0400101 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
102 def test_add_transmutable(self):
103 '''Test if chsmack can correctly set a SMACK transmutable mode'''
104
105 directory = "~/test_transmutable"
106 self.target.run("mkdir -p %s" %directory)
107 status, output = self.target.run("chsmack -t %s" %directory)
108 self.assertEqual(status, 0, "Cannot set smack transmutable. "
109 "Status and output: %d %s" %(status, output))
110 status, output = self.target.run("chsmack %s" %directory)
111 self.target.run("rmdir %s" %directory)
112 m = re.search('(?<=transmute=")\S+(?=")', output)
113 if m is None:
114 self.fail("Did not find transmute attribute")
115 else:
116 label_retrieved = m.group(0)
117 self.assertEqual(
118 "TRUE", label_retrieved,
119 "label not set correctly. expected and gotten: " +
120 "%s %s" %(LABEL,label_retrieved))
121
122
Brad Bishopc342db32019-05-15 21:57:59 -0400123 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
124 def test_privileged_change_self_label(self):
125 '''Test if privileged process (with CAP_MAC_ADMIN privilege)
126 can change its label.
127 '''
128
129 labelf = "/proc/self/attr/current"
130 command = "/bin/sh -c 'echo PRIVILEGED >%s; cat %s'" %(labelf, labelf)
131
132 status, output = self.target.run(
133 "notroot.py 0 %s %s" %(self.current_label, command))
134
135 self.assertIn("PRIVILEGED", output,
136 "Privilege process did not change label.Output: %s" %output)
137
Brad Bishopc342db32019-05-15 21:57:59 -0400138 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
139 def test_unprivileged_change_self_label(self):
140 '''Test if unprivileged process (without CAP_MAC_ADMIN privilege)
141 cannot change its label'''
142
143 command = "/bin/sh -c 'echo %s >/proc/self/attr/current'" %LABEL
144 status, output = self.target.run(
145 "notroot.py %d %s %s"
146 %(self.uid, self.current_label, command) +
147 " 2>&1 | grep 'Operation not permitted'" )
148
149 self.assertEqual(
150 status, 0,
151 "Unprivileged process should not be able to change its label")
152
153
Brad Bishopc342db32019-05-15 21:57:59 -0400154 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
155 def test_unprivileged_change_file_label(self):
156 '''Test if unprivileged process cannot change file labels'''
157
158 status, chsmack = self.target.run("which chsmack")
159 status, touch = self.target.run("which touch")
160 filename = "/tmp/test_unprivileged_change_file_label"
161
162 self.target.run("touch %s" % filename)
163 self.target.run("notroot.py %d %s" %(self.uid, self.current_label))
164 status, output = self.target.run(
165 "notroot.py " +
166 "%d unprivileged %s -a %s %s 2>&1 " %(self.uid, chsmack, LABEL, filename) +
167 "| grep 'Operation not permitted'" )
168
169 self.target.run("rm %s" % filename)
170 self.assertEqual( status, 0, "Unprivileged process changed label for %s" %filename)
171
Brad Bishopc342db32019-05-15 21:57:59 -0400172 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
173 def test_load_smack_rule(self):
174 '''Test if new smack access rules can be loaded'''
175
176 # old 23 character format requires special spaces formatting
177 # 12345678901234567890123456789012345678901234567890123
178 ruleA="TheOne TheOther rwxat"
179 ruleB="TheOne TheOther r----"
180 clean="TheOne TheOther -----"
181 modeA = "rwxat"
182 modeB = "r"
183
184 status, output = self.target.run('echo -n "%s" > %s/load' %(ruleA, self.smack_path))
185 status, output = self.target.run( 'cat %s/load | grep "^TheOne" | grep " TheOther "' %self.smack_path)
186 self.assertEqual(status, 0, "Rule A was not added")
187 mode = list(filter(bool, output.split(" ")))[2].strip()
188 self.assertEqual( mode, modeA, "Mode A was not set correctly; mode: %s" %mode)
189
190 status, output = self.target.run( 'echo -n "%s" > %s/load' %(ruleB, self.smack_path))
191 status, output = self.target.run( 'cat %s/load | grep "^TheOne" | grep " TheOther "' %self.smack_path)
192 mode = list(filter(bool, output.split(" ")))[2].strip()
193 self.assertEqual( mode, modeB, "Mode B was not set correctly; mode: %s" %mode)
194
195 self.target.run('echo -n "%s" > %s/load' %(clean, self.smack_path))
196
197
Brad Bishopc342db32019-05-15 21:57:59 -0400198 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
199 def test_smack_onlycap(self):
200 '''Test if smack onlycap label can be set
201
202 test needs to change the running label of the current process,
203 so whole test takes places on image
204 '''
205 status, output = self.target.run("sh /usr/sbin/test_smack_onlycap.sh")
206 self.assertEqual(status, 0, output)
207
Brad Bishopc342db32019-05-15 21:57:59 -0400208
209 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
210 def test_smack_netlabel(self):
211
212 test_label="191.191.191.191 TheOne"
213 expected_label="191.191.191.191/32 TheOne"
214
215 status, output = self.target.run( "echo -n '%s' > %s/netlabel" %(test_label, self.smack_path))
216 self.assertEqual( status, 0, "Netlabel /32 could not be set. Output: %s" %output)
217
218 status, output = self.target.run("cat %s/netlabel" %self.smack_path)
219 self.assertIn( expected_label, output, "Did not find expected label in output: %s" %output)
220
221 test_label="253.253.253.0/24 TheOther"
222 status, output = self.target.run( "echo -n '%s' > %s/netlabel" %(test_label, self.smack_path))
223 self.assertEqual( status, 0, "Netlabel /24 could not be set. Output: %s" %output)
224
225 status, output = self.target.run("cat %s/netlabel" %self.smack_path)
226 self.assertIn(
227 test_label, output,
228 "Did not find expected label in output: %s" %output)
229
Brad Bishopc342db32019-05-15 21:57:59 -0400230
231 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
232 def test_smack_cipso(self):
233 '''Test if smack cipso rules can be set'''
234 # 12345678901234567890123456789012345678901234567890123456
235 ruleA="TheOneA 2 0 "
236 ruleB="TheOneB 3 1 55 "
237 ruleC="TheOneC 4 2 17 33 "
238
239 status, output = self.target.run(
240 "echo -n '%s' > %s/cipso" %(ruleA, self.smack_path))
241 self.assertEqual(status, 0,
242 "Could not set cipso label A. Ouput: %s" %output)
243
244 status, output = self.target.run(
245 "cat %s/cipso | grep '^TheOneA'" %self.smack_path)
246 self.assertEqual(status, 0, "Cipso rule A was not set")
247 self.assertIn(" 2", output, "Rule A was not set correctly")
248
249 status, output = self.target.run(
250 "echo -n '%s' > %s/cipso" %(ruleB, self.smack_path))
251 self.assertEqual(status, 0,
252 "Could not set cipso label B. Ouput: %s" %output)
253
254 status, output = self.target.run(
255 "cat %s/cipso | grep '^TheOneB'" %self.smack_path)
256 self.assertEqual(status, 0, "Cipso rule B was not set")
257 self.assertIn("/55", output, "Rule B was not set correctly")
258
259 status, output = self.target.run(
260 "echo -n '%s' > %s/cipso" %(ruleC, self.smack_path))
261 self.assertEqual(
262 status, 0,
263 "Could not set cipso label C. Ouput: %s" %output)
264
265 status, output = self.target.run(
266 "cat %s/cipso | grep '^TheOneC'" %self.smack_path)
267 self.assertEqual(status, 0, "Cipso rule C was not set")
268 self.assertIn("/17,33", output, "Rule C was not set correctly")
269
Brad Bishopc342db32019-05-15 21:57:59 -0400270
271 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
272 def test_smack_direct(self):
273 status, initial_direct = self.target.run(
274 "cat %s/direct" %self.smack_path)
275
276 test_direct="17"
277 status, output = self.target.run(
278 "echo '%s' > %s/direct" %(test_direct, self.smack_path))
279 self.assertEqual(status, 0 ,
280 "Could not set smack direct. Output: %s" %output)
281 status, new_direct = self.target.run("cat %s/direct" %self.smack_path)
282 # initial label before checking
283 status, output = self.target.run(
284 "echo '%s' > %s/direct" %(initial_direct, self.smack_path))
285 self.assertEqual(
286 test_direct, new_direct.strip(),
287 "Smack direct label does not match.")
288
289
Brad Bishopc342db32019-05-15 21:57:59 -0400290 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
291 def test_smack_ambient(self):
292 test_ambient = "test_ambient"
293 status, initial_ambient = self.target.run("cat %s/ambient" %self.smack_path)
294 status, output = self.target.run(
295 "echo '%s' > %s/ambient" %(test_ambient, self.smack_path))
296 self.assertEqual(status, 0,
297 "Could not set smack ambient. Output: %s" %output)
298
299 status, output = self.target.run("cat %s/ambient" %self.smack_path)
300 # Filter '\x00', which is sometimes added to the ambient label
301 new_ambient = ''.join(filter(lambda x: x in string.printable, output))
302 initial_ambient = ''.join(filter(lambda x: x in string.printable, initial_ambient))
303 status, output = self.target.run(
304 "echo '%s' > %s/ambient" %(initial_ambient, self.smack_path))
305 self.assertEqual(
306 test_ambient, new_ambient.strip(),
307 "Ambient label does not match")
308
309
Brad Bishopc342db32019-05-15 21:57:59 -0400310 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
311 def test_smackload(self):
312 '''Test if smackload command works'''
313 rule="testobject testsubject rwx"
314
315 status, output = self.target.run("echo -n '%s' > /tmp/rules" %rule)
316 status, output = self.target.run("smackload /tmp/rules")
317 self.assertEqual( status, 0, "Smackload failed to load rule. Output: %s" %output)
318
319 status, output = self.target.run( "cat %s/load | grep '%s'" %(self.smack_path, rule))
320 self.assertEqual(status, 0, "Smackload rule was loaded correctly")
321
322
Brad Bishopc342db32019-05-15 21:57:59 -0400323 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
324 def test_smackcipso(self):
325 '''Test if smackcipso command works'''
326 # 12345678901234567890123456789012345678901234567890123456
327 rule="cipsolabel 2 2 "
328
329 status, output = self.target.run("echo '%s' | smackcipso" %rule)
330 self.assertEqual( status, 0, "Smackcipso failed to load rule. Output: %s" %output)
331
332 status, output = self.target.run(
333 "cat %s/cipso | grep 'cipsolabel'" %self.smack_path)
334 self.assertEqual(status, 0, "smackcipso rule was loaded correctly")
335 self.assertIn( "2/2", output, "Rule was not set correctly. Got: %s" %output)
336
337
Brad Bishopc342db32019-05-15 21:57:59 -0400338 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
339 def test_smack_enforce_file_access(self):
340 '''Test if smack file access is enforced (rwx)
341
342 test needs to change the running label of the current process,
343 so whole test takes places on image
344 '''
345 status, output = self.target.run("sh /usr/sbin/smack_test_file_access.sh")
346 self.assertEqual(status, 0, output)
347
348
Brad Bishopc342db32019-05-15 21:57:59 -0400349 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
350 def test_smack_mmap_enforced(self):
351 '''Test if smack mmap access is enforced'''
352 raise unittest.SkipTest("Depends on mmap_test, which was removed from the layer while investigating its license.")
353
354 # 12345678901234567890123456789012345678901234567890123456
355 delr1="mmap_label mmap_test_label1 -----"
356 delr2="mmap_label mmap_test_label2 -----"
357 delr3="mmap_file_label mmap_test_label1 -----"
358 delr4="mmap_file_label mmap_test_label2 -----"
359
360 RuleA="mmap_label mmap_test_label1 rw---"
361 RuleB="mmap_label mmap_test_label2 r--at"
362 RuleC="mmap_file_label mmap_test_label1 rw---"
363 RuleD="mmap_file_label mmap_test_label2 rwxat"
364
365 mmap_label="mmap_label"
366 file_label="mmap_file_label"
367 test_file = "/usr/sbin/smack_test_mmap"
368 mmap_exe = "/tmp/mmap_test"
369 status, echo = self.target.run("which echo")
370 status, output = self.target.run(
371 "notroot.py %d %s %s 'test' > %s" \
372 %(self.uid, self.current_label, echo, test_file))
373 status, output = self.target.run("ls %s" %test_file)
374 self.assertEqual(status, 0, "Could not create mmap test file")
375 self.target.run("chsmack -m %s %s" %(file_label, test_file))
376 self.target.run("chsmack -e %s %s" %(mmap_label, mmap_exe))
377
378 # test with no rules with mmap label or exec label as subject
379 # access should be granted
380 self.target.run('echo -n "%s" > %s/load' %(delr1, self.smack_path))
381 self.target.run('echo -n "%s" > %s/load' %(delr2, self.smack_path))
382 self.target.run('echo -n "%s" > %s/load' %(delr3, self.smack_path))
383 self.target.run('echo -n "%s" > %s/load' %(delr4, self.smack_path))
384 status, output = self.target.run("%s %s 0 2" % (mmap_exe, test_file))
385 self.assertEqual(
386 status, 0,
387 "Should have mmap access without rules. Output: %s" %output)
388
389 # add rules that do not match access required
390 self.target.run('echo -n "%s" > %s/load' %(RuleA, self.smack_path))
391 self.target.run('echo -n "%s" > %s/load' %(RuleB, self.smack_path))
392 status, output = self.target.run("%s %s 0 2" % (mmap_exe, test_file))
393 self.assertNotEqual(
394 status, 0,
395 "Should not have mmap access with unmatching rules. " +
396 "Output: %s" %output)
397 self.assertIn(
398 "Permission denied", output,
399 "Mmap access should be denied with unmatching rules")
400
401 # add rule to match only partially (one way)
402 self.target.run('echo -n "%s" > %s/load' %(RuleC, self.smack_path))
403 status, output = self.target.run("%s %s 0 2" %(mmap_exe, test_file))
404 self.assertNotEqual(
405 status, 0,
406 "Should not have mmap access with partial matching rules. " +
407 "Output: %s" %output)
408 self.assertIn(
409 "Permission denied", output,
410 "Mmap access should be denied with partial matching rules")
411
412 # add rule to match fully
413 self.target.run('echo -n "%s" > %s/load' %(RuleD, self.smack_path))
414 status, output = self.target.run("%s %s 0 2" %(mmap_exe, test_file))
415 self.assertEqual(
416 status, 0,
417 "Should have mmap access with full matching rules." +
418 "Output: %s" %output)
419
420
Brad Bishopc342db32019-05-15 21:57:59 -0400421 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
422 def test_smack_transmute_dir(self):
423 '''Test if smack transmute attribute works
424
425 test needs to change the running label of the current process,
426 so whole test takes places on image
427 '''
428 test_dir = "/tmp/smack_transmute_dir"
429 label="transmute_label"
430 status, initial_label = self.target.run("cat /proc/self/attr/current")
431
432 self.target.run("mkdir -p %s" % test_dir)
433 self.target.run("chsmack -a %s %s" % (label, test_dir))
434 self.target.run("chsmack -t %s" % test_dir)
435 self.target.run("echo -n '%s %s rwxat' | smackload" %(initial_label, label) )
436
437 self.target.run("touch %s/test" % test_dir)
438 status, output = self.target.run("chsmack %s/test" % test_dir)
439 self.assertIn( 'access="%s"' %label, output,
440 "Did not get expected label. Output: %s" % output)
441
442
Brad Bishopc342db32019-05-15 21:57:59 -0400443 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
444 def test_smack_tcp_sockets(self):
445 '''Test if smack is enforced on tcp sockets
446
447 whole test takes places on image, depends on tcp_server/tcp_client'''
448
449 status, output = self.target.run("sh /usr/sbin/test_smack_tcp_sockets.sh")
450 self.assertEqual(status, 0, output)
451
452
Brad Bishopc342db32019-05-15 21:57:59 -0400453 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
454 def test_smack_udp_sockets(self):
455 '''Test if smack is enforced on udp sockets
456
457 whole test takes places on image, depends on udp_server/udp_client'''
458
459 status, output = self.target.run("sh /usr/sbin/test_smack_udp_sockets.sh")
460 self.assertEqual(status, 0, output)
461
462
Brad Bishopc342db32019-05-15 21:57:59 -0400463 @OETestDepends(['smack.SmackBasicTest.test_smack_basic'])
464 def test_smack_labels(self):
465 '''Check for correct Smack labels.'''
466 expected = '''
467/tmp/ access="*"
468/etc/ access="System::Shared" transmute="TRUE"
469/etc/passwd access="System::Shared"
470/etc/terminfo access="System::Shared" transmute="TRUE"
471/etc/skel/ access="System::Shared" transmute="TRUE"
472/etc/skel/.profile access="System::Shared"
473/var/log/ access="System::Log" transmute="TRUE"
474/var/tmp/ access="*"
475'''
476 files = ' '.join([x.split()[0] for x in expected.split('\n') if x])
477 files_wildcard = ' '.join([x + '/*' for x in files.split()])
478 # Auxiliary information.
479 status, output = self.target.run(
480 'set -x; mount; ls -l -d %s; find %s | xargs ls -d -l; find %s | xargs chsmack' % (
481 ' '.join([x.rstrip('/') for x in files.split()]), files, files
482 )
483 )
484 msg = "File status:\n" + output
485 status, output = self.target.run('chsmack %s' % files)
486 self.assertEqual(
487 status, 0, msg="status and output: %s and %s\n%s" % (status,output, msg))
488 self.longMessage = True
489 self.maxDiff = None
490 self.assertEqual(output.strip().split('\n'), expected.strip().split('\n'), msg=msg)