5 from oeqa.oetest import oeRuntimeTest, skipModule
6 from oeqa.utils.decorators import *
9 """Get directory of supporting files"""
10 pkgarch = oeRuntimeTest.tc.d.getVar('MACHINE', True)
11 deploydir = oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True)
12 return os.path.join(deploydir, "files", "target", pkgarch)
15 LABEL = "a" * MAX_LABEL_LEN
18 if not oeRuntimeTest.hasFeature('smack'):
20 "smack module skipped: "
21 "target doesn't have smack in DISTRO_FEATURES")
23 class SmackBasicTest(oeRuntimeTest):
24 ''' base smack test '''
26 status, output = self.target.run(
27 "grep smack /proc/mounts | awk '{print $2}'")
28 self.smack_path = output
29 self.files_dir = os.path.join(
30 os.path.abspath(os.path.dirname(__file__)), 'files')
32 status,output = self.target.run("cat /proc/self/attr/current")
33 self.current_label = output.strip()
35 class SmackAccessLabel(SmackBasicTest):
37 @skipUnlessPassed('test_ssh')
38 def test_add_access_label(self):
39 ''' Test if chsmack can correctly set a SMACK label '''
40 filename = "/tmp/test_access_label"
41 self.target.run("touch %s" %filename)
42 status, output = self.target.run("chsmack -a %s %s" %(LABEL, filename))
45 "Cannot set smack access label. "
46 "Status and output: %d %s" %(status, output))
47 status, output = self.target.run("chsmack %s" %filename)
48 self.target.run("rm %s" %filename)
49 m = re.search('(?<=access=")\S+(?=")', output)
51 self.fail("Did not find access attribute")
53 label_retrieved = m .group(0)
55 LABEL, label_retrieved,
56 "label not set correctly. expected and gotten: "
57 "%s %s" %(LABEL,label_retrieved))
59 class SmackExecLabel(SmackBasicTest):
61 @skipUnlessPassed('test_ssh')
62 def test_add_exec_label(self):
63 '''Test if chsmack can correctly set a SMACK Exec label'''
64 filename = "/tmp/test_exec_label"
65 self.target.run("touch %s" %filename)
66 status, output = self.target.run("chsmack -e %s %s" %(LABEL, filename))
69 "Cannot set smack exec label. "
70 "Status and output: %d %s" %(status, output))
71 status, output = self.target.run("chsmack %s" %filename)
72 self.target.run("rm %s" %filename)
73 m= re.search('(?<=execute=")\S+(?=")', output)
75 self.fail("Did not find execute attribute")
77 label_retrieved = m.group(0)
79 LABEL, label_retrieved,
80 "label not set correctly. expected and gotten: " +
81 "%s %s" %(LABEL,label_retrieved))
83 class SmackMmapLabel(SmackBasicTest):
85 @skipUnlessPassed('test_ssh')
86 def test_add_mmap_label(self):
87 '''Test if chsmack can correctly set a SMACK mmap label'''
88 filename = "/tmp/test_exec_label"
89 self.target.run("touch %s" %filename)
90 status, output = self.target.run("chsmack -m %s %s" %(LABEL, filename))
93 "Cannot set smack mmap label. "
94 "Status and output: %d %s" %(status, output))
95 status, output = self.target.run("chsmack %s" %filename)
96 self.target.run("rm %s" %filename)
97 m = re.search('(?<=mmap=")\S+(?=")', output)
99 self.fail("Did not find mmap attribute")
101 label_retrieved = m.group(0)
103 LABEL, label_retrieved,
104 "label not set correctly. expected and gotten: " +
105 "%s %s" %(LABEL,label_retrieved))
107 class SmackTransmutable(SmackBasicTest):
109 @skipUnlessPassed('test_ssh')
110 def test_add_transmutable(self):
111 '''Test if chsmack can correctly set a SMACK transmutable mode'''
113 directory = "~/test_transmutable"
114 self.target.run("mkdir -p %s" %directory)
115 status, output = self.target.run("chsmack -t %s" %directory)
116 self.assertEqual(status, 0, "Cannot set smack transmutable. "
117 "Status and output: %d %s" %(status, output))
118 status, output = self.target.run("chsmack %s" %directory)
119 self.target.run("rmdir %s" %directory)
120 m = re.search('(?<=transmute=")\S+(?=")', output)
122 self.fail("Did not find transmute attribute")
124 label_retrieved = m.group(0)
126 "TRUE", label_retrieved,
127 "label not set correctly. expected and gotten: " +
128 "%s %s" %(LABEL,label_retrieved))
130 class SmackChangeSelfLabelPrivilege(SmackBasicTest):
132 @skipUnlessPassed('test_ssh')
133 def test_privileged_change_self_label(self):
134 '''Test if privileged process (with CAP_MAC_ADMIN privilege)
135 can change its label.
138 status, output = self.target.run("ls /tmp/notroot.py")
141 os.path.join(self.files_dir, 'notroot.py'),
144 labelf = "/proc/self/attr/current"
145 command = "/bin/sh -c 'echo PRIVILEGED >%s; cat %s'" %(labelf, labelf)
147 status, output = self.target.run(
148 "python /tmp/notroot.py 0 %s %s" %(self.current_label, command))
150 self.assertIn("PRIVILEGED", output,
151 "Privilege process did not change label.Output: %s" %output)
153 class SmackChangeSelfLabelUnprivilege(SmackBasicTest):
155 @skipUnlessPassed('test_ssh')
156 def test_unprivileged_change_self_label(self):
157 '''Test if unprivileged process (without CAP_MAC_ADMIN privilege)
158 cannot change its label'''
160 status, output = self.target.run("ls /tmp/notroot.py")
163 os.path.join(self.files_dir, 'notroot.py'),
166 command = "/bin/sh -c 'echo %s >/proc/self/attr/current'" %LABEL
167 status, output = self.target.run(
168 "python /tmp/notroot.py %d %s %s"
169 %(self.uid, self.current_label, command) +
170 " 2>&1 | grep 'Operation not permitted'" )
174 "Unprivileged process should not be able to change its label")
177 class SmackChangeFileLabelPrivilege(SmackBasicTest):
179 @skipUnlessPassed('test_ssh')
180 def test_unprivileged_change_file_label(self):
181 '''Test if unprivileged process cannot change file labels'''
183 status, chsmack = self.target.run("which chsmack")
184 status, touch = self.target.run("which touch")
185 filename = "/tmp/test_unprivileged_change_file_label"
187 status, output = self.target.run("ls /tmp/notroot.py")
190 os.path.join(self.files_dir, 'notroot.py'),
193 self.target.run("python /tmp/notroot.py %d %s %s %s" %(self.uid, self.current_label, touch, filename))
194 status, output = self.target.run(
195 "python /tmp/notroot.py " +
196 "%d unprivileged %s -a %s %s 2>&1 " %(self.uid, chsmack, LABEL, filename) +
197 "| grep 'Operation not permitted'" )
199 self.target.run("rm %s" %filename)
202 "Unprivileged process changed label for %s" %filename)
204 class SmackLoadRule(SmackBasicTest):
206 @skipUnlessPassed('test_ssh')
207 def test_load_smack_rule(self):
208 '''Test if new smack access rules can be loaded'''
210 # old 23 character format requires special spaces formatting
211 # 12345678901234567890123456789012345678901234567890123
212 ruleA="TheOne TheOther rwxat"
213 ruleB="TheOne TheOther r----"
214 clean="TheOne TheOther -----"
218 status, output = self.target.run(
219 'echo -n "%s" > %s/load' %(ruleA, self.smack_path))
220 status, output = self.target.run(
221 'cat %s/load | grep "^TheOne" | grep " TheOther "' %self.smack_path)
222 self.assertEqual(status, 0, "Rule A was not added")
223 mode = list(filter(bool, output.split(" ")))[2].strip()
226 "Mode A was not set correctly; mode: %s" %mode)
228 status, output = self.target.run(
229 'echo -n "%s" > %s/load' %(ruleB, self.smack_path))
230 status, output = self.target.run(
231 'cat %s/load | grep "^TheOne" | grep " TheOther "' %self.smack_path)
232 mode = list(filter(bool, output.split(" ")))[2].strip()
235 "Mode B was not set correctly; mode: %s" %mode)
237 self.target.run('echo -n "%s" > %s/load' %(clean, self.smack_path))
240 class SmackOnlycap(SmackBasicTest):
242 @skipUnlessPassed('test_ssh')
243 def test_smack_onlycap(self):
244 '''Test if smack onlycap label can be set
246 test needs to change the running label of the current process,
247 so whole test takes places on image
249 status, output = self.target.run("ls /tmp/test_smack_onlycap.sh")
252 os.path.join(self.files_dir, 'test_smack_onlycap.sh'),
253 "/tmp/test_smack_onlycap.sh")
255 status, output = self.target.run("sh /tmp/test_smack_onlycap.sh")
256 self.assertEqual(status, 0, output)
258 class SmackNetlabel(SmackBasicTest):
259 @skipUnlessPassed('test_ssh')
260 def test_smack_netlabel(self):
262 test_label="191.191.191.191 TheOne"
263 expected_label="191.191.191.191/32 TheOne"
265 status, output = self.target.run(
266 "echo -n '%s' > %s/netlabel" %(test_label, self.smack_path))
269 "Netlabel /32 could not be set. Output: %s" %output)
271 status, output = self.target.run("cat %s/netlabel" %self.smack_path)
273 expected_label, output,
274 "Did not find expected label in output: %s" %output)
276 test_label="253.253.253.0/24 TheOther"
277 status, output = self.target.run(
278 "echo -n '%s' > %s/netlabel" %(test_label, self.smack_path))
281 "Netlabel /24 could not be set. Output: %s" %output)
283 status, output = self.target.run("cat %s/netlabel" %self.smack_path)
286 "Did not find expected label in output: %s" %output)
288 class SmackCipso(SmackBasicTest):
289 @skipUnlessPassed('test_ssh')
290 def test_smack_cipso(self):
291 '''Test if smack cipso rules can be set'''
292 # 12345678901234567890123456789012345678901234567890123456
294 ruleB="TheOneB 3 1 55 "
295 ruleC="TheOneC 4 2 17 33 "
297 status, output = self.target.run(
298 "echo -n '%s' > %s/cipso" %(ruleA, self.smack_path))
299 self.assertEqual(status, 0,
300 "Could not set cipso label A. Ouput: %s" %output)
302 status, output = self.target.run(
303 "cat %s/cipso | grep '^TheOneA'" %self.smack_path)
304 self.assertEqual(status, 0, "Cipso rule A was not set")
305 self.assertIn(" 2", output, "Rule A was not set correctly")
307 status, output = self.target.run(
308 "echo -n '%s' > %s/cipso" %(ruleB, self.smack_path))
309 self.assertEqual(status, 0,
310 "Could not set cipso label B. Ouput: %s" %output)
312 status, output = self.target.run(
313 "cat %s/cipso | grep '^TheOneB'" %self.smack_path)
314 self.assertEqual(status, 0, "Cipso rule B was not set")
315 self.assertIn("/55", output, "Rule B was not set correctly")
317 status, output = self.target.run(
318 "echo -n '%s' > %s/cipso" %(ruleC, self.smack_path))
321 "Could not set cipso label C. Ouput: %s" %output)
323 status, output = self.target.run(
324 "cat %s/cipso | grep '^TheOneC'" %self.smack_path)
325 self.assertEqual(status, 0, "Cipso rule C was not set")
326 self.assertIn("/17,33", output, "Rule C was not set correctly")
328 class SmackDirect(SmackBasicTest):
329 @skipUnlessPassed('test_ssh')
330 def test_smack_direct(self):
331 status, initial_direct = self.target.run(
332 "cat %s/direct" %self.smack_path)
335 status, output = self.target.run(
336 "echo '%s' > %s/direct" %(test_direct, self.smack_path))
337 self.assertEqual(status, 0 ,
338 "Could not set smack direct. Output: %s" %output)
339 status, new_direct = self.target.run("cat %s/direct" %self.smack_path)
340 # initial label before checking
341 status, output = self.target.run(
342 "echo '%s' > %s/direct" %(initial_direct, self.smack_path))
344 test_direct, new_direct.strip(),
345 "Smack direct label does not match.")
348 class SmackAmbient(SmackBasicTest):
349 @skipUnlessPassed('test_ssh')
350 def test_smack_ambient(self):
351 test_ambient = "test_ambient"
352 status, initial_ambient = self.target.run("cat %s/ambient" %self.smack_path)
353 status, output = self.target.run(
354 "echo '%s' > %s/ambient" %(test_ambient, self.smack_path))
355 self.assertEqual(status, 0,
356 "Could not set smack ambient. Output: %s" %output)
358 status, output = self.target.run("cat %s/ambient" %self.smack_path)
359 # Filter '\x00', which is sometimes added to the ambient label
360 new_ambient = ''.join(filter(lambda x: x in string.printable, output))
361 initial_ambient = ''.join(filter(lambda x: x in string.printable, initial_ambient))
362 status, output = self.target.run(
363 "echo '%s' > %s/ambient" %(initial_ambient, self.smack_path))
365 test_ambient, new_ambient.strip(),
366 "Ambient label does not match")
369 class SmackloadBinary(SmackBasicTest):
370 @skipUnlessPassed('test_ssh')
371 def test_smackload(self):
372 '''Test if smackload command works'''
373 rule="testobject testsubject rwx"
375 status, output = self.target.run("echo -n '%s' > /tmp/rules" %rule)
376 status, output = self.target.run("smackload /tmp/rules")
379 "Smackload failed to load rule. Output: %s" %output)
381 status, output = self.target.run(
382 "cat %s/load | grep '%s'" %(self.smack_path, rule))
383 self.assertEqual(status, 0, "Smackload rule was loaded correctly")
385 class SmackcipsoBinary(SmackBasicTest):
387 @skipUnlessPassed('test_ssh')
388 def test_smackcipso(self):
389 '''Test if smackcipso command works'''
390 # 12345678901234567890123456789012345678901234567890123456
391 rule="cipsolabel 2 2 "
393 status, output = self.target.run("echo '%s' | smackcipso" %rule)
396 "Smackcipso failed to load rule. Output: %s" %output)
398 status, output = self.target.run(
399 "cat %s/cipso | grep 'cipsolabel'" %self.smack_path)
400 self.assertEqual(status, 0, "Smackload rule was loaded correctly")
403 "Rule was not set correctly. Got: %s" %output)
405 class SmackEnforceFileAccess(SmackBasicTest):
406 @skipUnlessPassed('test_ssh')
407 def test_smack_enforce_file_access(self):
408 '''Test if smack file access is enforced (rwx)
410 test needs to change the running label of the current process,
411 so whole test takes places on image
413 status, output = self.target.run("ls /tmp/smack_test_file_access.sh")
416 os.path.join(self.files_dir, 'smack_test_file_access.sh'),
417 "/tmp/smack_test_file_access.sh")
419 status, output = self.target.run("sh /tmp/smack_test_file_access.sh")
420 self.assertEqual(status, 0, output)
422 class SmackEnforceMmap(SmackBasicTest):
424 @skipUnlessPassed('test_ssh')
425 def test_smack_mmap_enforced(self):
426 '''Test if smack mmap access is enforced'''
427 raise unittest.SkipTest("Depends on mmap_test, which was removed from the layer while investigating its license.")
429 # 12345678901234567890123456789012345678901234567890123456
430 delr1="mmap_label mmap_test_label1 -----"
431 delr2="mmap_label mmap_test_label2 -----"
432 delr3="mmap_file_label mmap_test_label1 -----"
433 delr4="mmap_file_label mmap_test_label2 -----"
435 RuleA="mmap_label mmap_test_label1 rw---"
436 RuleB="mmap_label mmap_test_label2 r--at"
437 RuleC="mmap_file_label mmap_test_label1 rw---"
438 RuleD="mmap_file_label mmap_test_label2 rwxat"
440 mmap_label="mmap_label"
441 file_label="mmap_file_label"
442 test_file = "/tmp/smack_test_mmap"
443 self.target.copy_to(os.path.join(get_files_dir(), "mmap_test"), "/tmp/")
444 mmap_exe = "/tmp/mmap_test"
445 status, echo = self.target.run("which echo")
446 status, output = self.target.run("ls /tmp/notroot.py")
449 os.path.join(self.files_dir, 'notroot.py'),
451 status, output = self.target.run(
452 "python /tmp/notroot.py %d %s %s 'test' > %s" \
453 %(self.uid, self.current_label, echo, test_file))
454 status, output = self.target.run("ls %s" %test_file)
455 self.assertEqual(status, 0, "Could not create mmap test file")
456 self.target.run("chsmack -m %s %s" %(file_label, test_file))
457 self.target.run("chsmack -e %s %s" %(mmap_label, mmap_exe))
459 # test with no rules with mmap label or exec label as subject
460 # access should be granted
461 self.target.run('echo -n "%s" > %s/load' %(delr1, self.smack_path))
462 self.target.run('echo -n "%s" > %s/load' %(delr2, self.smack_path))
463 self.target.run('echo -n "%s" > %s/load' %(delr3, self.smack_path))
464 self.target.run('echo -n "%s" > %s/load' %(delr4, self.smack_path))
465 status, output = self.target.run("%s %s 0 2" % (mmap_exe, test_file))
468 "Should have mmap access without rules. Output: %s" %output)
470 # add rules that do not match access required
471 self.target.run('echo -n "%s" > %s/load' %(RuleA, self.smack_path))
472 self.target.run('echo -n "%s" > %s/load' %(RuleB, self.smack_path))
473 status, output = self.target.run("%s %s 0 2" % (mmap_exe, test_file))
476 "Should not have mmap access with unmatching rules. " +
477 "Output: %s" %output)
479 "Permission denied", output,
480 "Mmap access should be denied with unmatching rules")
482 # add rule to match only partially (one way)
483 self.target.run('echo -n "%s" > %s/load' %(RuleC, self.smack_path))
484 status, output = self.target.run("%s %s 0 2" %(mmap_exe, test_file))
487 "Should not have mmap access with partial matching rules. " +
488 "Output: %s" %output)
490 "Permission denied", output,
491 "Mmap access should be denied with partial matching rules")
493 # add rule to match fully
494 self.target.run('echo -n "%s" > %s/load' %(RuleD, self.smack_path))
495 status, output = self.target.run("%s %s 0 2" %(mmap_exe, test_file))
498 "Should have mmap access with full matching rules." +
499 "Output: %s" %output)
501 class SmackEnforceTransmutable(SmackBasicTest):
503 def test_smack_transmute_dir(self):
504 '''Test if smack transmute attribute works
506 test needs to change the running label of the current process,
507 so whole test takes places on image
509 test_dir = "/tmp/smack_transmute_dir"
510 label="transmute_label"
511 status, initial_label = self.target.run("cat /proc/self/attr/current")
513 self.target.run("mkdir -p %s" %test_dir)
514 self.target.run("chsmack -a %s %s" %(label, test_dir))
515 self.target.run("chsmack -t %s" %test_dir)
517 "echo -n '%s %s rwxat' | smackload" %(initial_label, label) )
519 self.target.run("touch %s/test" %test_dir)
520 status, output = self.target.run("chsmack %s/test" %test_dir)
522 'access="%s"' %label, output,
523 "Did not get expected label. Output: %s" %output)
526 class SmackTcpSockets(SmackBasicTest):
527 def test_smack_tcp_sockets(self):
528 '''Test if smack is enforced on tcp sockets
530 whole test takes places on image, depends on tcp_server/tcp_client'''
532 self.target.copy_to(os.path.join(get_files_dir(), "tcp_client"), "/tmp/")
533 self.target.copy_to(os.path.join(get_files_dir(), "tcp_server"), "/tmp/")
534 status, output = self.target.run("ls /tmp/test_smack_tcp_sockets.sh")
537 os.path.join(self.files_dir, 'test_smack_tcp_sockets.sh'),
538 "/tmp/test_smack_tcp_sockets.sh")
540 status, output = self.target.run("sh /tmp/test_smack_tcp_sockets.sh")
541 self.assertEqual(status, 0, output)
543 class SmackUdpSockets(SmackBasicTest):
544 def test_smack_udp_sockets(self):
545 '''Test if smack is enforced on udp sockets
547 whole test takes places on image, depends on udp_server/udp_client'''
549 self.target.copy_to(os.path.join(get_files_dir(), "udp_client"), "/tmp/")
550 self.target.copy_to(os.path.join(get_files_dir(), "udp_server"), "/tmp/")
551 status, output = self.target.run("ls /tmp/test_smack_udp_sockets.sh")
554 os.path.join(self.files_dir, 'test_smack_udp_sockets.sh'),
555 "/tmp/test_smack_udp_sockets.sh")
557 status, output = self.target.run("sh /tmp/test_smack_udp_sockets.sh")
558 self.assertEqual(status, 0, output)
560 class SmackFileLabels(SmackBasicTest):
562 @skipUnlessPassed('test_ssh')
563 def test_smack_labels(self):
564 '''Check for correct Smack labels.'''
567 /etc/ access="System::Shared" transmute="TRUE"
568 /etc/passwd access="System::Shared"
569 /etc/terminfo access="System::Shared" transmute="TRUE"
570 /etc/skel/ access="System::Shared" transmute="TRUE"
571 /etc/skel/.profile access="System::Shared"
572 /var/log/ access="System::Log" transmute="TRUE"
575 files = ' '.join([x.split()[0] for x in expected.split('\n') if x])
576 files_wildcard = ' '.join([x + '/*' for x in files.split()])
577 # Auxiliary information.
578 status, output = self.target.run(
579 'set -x; mount; ls -l -d %s; find %s | xargs ls -d -l; find %s | xargs chsmack' % (
580 ' '.join([x.rstrip('/') for x in files.split()]), files, files
583 msg = "File status:\n" + output
584 status, output = self.target.run('chsmack %s' % files)
586 status, 0, msg="status and output: %s and %s\n%s" % (status,output, msg))
587 self.longMessage = True
589 self.assertEqual(output.strip().split('\n'), expected.strip().split('\n'), msg=msg)