1import 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.current_label = "" 19 cls.uid = 1000 20 status, output = cls.tc.target.run("grep smack /proc/mounts | awk '{print $2}'") 21 cls.smack_path = output 22 23 @skipIfNotFeature('smack', 24 'Test requires smack to be in DISTRO_FEATURES') 25 @OEHasPackage(['smack-test']) 26 @OETestDepends(['ssh.SSHTest.test_ssh']) 27 def test_smack_basic(self): 28 status,output = self.target.run("cat /proc/self/attr/current") 29 self.current_label = output.strip() 30 31 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 32 def test_add_access_label(self): 33 ''' Test if chsmack can correctly set a SMACK label ''' 34 filename = "/tmp/test_access_label" 35 self.target.run("touch %s" %filename) 36 status, output = self.target.run("chsmack -a %s %s" %(LABEL, filename)) 37 self.assertEqual( 38 status, 0, 39 "Cannot set smack access label. " 40 "Status and output: %d %s" %(status, output)) 41 status, output = self.target.run("chsmack %s" %filename) 42 self.target.run("rm %s" %filename) 43 m = re.search('(access=")\S+(?=")', output) 44 if m is None: 45 self.fail("Did not find access attribute") 46 else: 47 label_retrieved = re.split("access=\"", output)[1][:-1] 48 self.assertEqual( 49 LABEL, label_retrieved, 50 "label not set correctly. expected and gotten: " 51 "%s %s" %(LABEL,label_retrieved)) 52 53 54 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 55 def test_add_exec_label(self): 56 '''Test if chsmack can correctly set a SMACK Exec label''' 57 filename = "/tmp/test_exec_label" 58 self.target.run("touch %s" %filename) 59 status, output = self.target.run("chsmack -e %s %s" %(LABEL, filename)) 60 self.assertEqual( 61 status, 0, 62 "Cannot set smack exec label. " 63 "Status and output: %d %s" %(status, output)) 64 status, output = self.target.run("chsmack %s" %filename) 65 self.target.run("rm %s" %filename) 66 m= re.search('(execute=")\S+(?=")', output) 67 if m is None: 68 self.fail("Did not find execute attribute") 69 else: 70 label_retrieved = re.split("execute=\"", output)[1][:-1] 71 self.assertEqual( 72 LABEL, label_retrieved, 73 "label not set correctly. expected and gotten: " + 74 "%s %s" %(LABEL,label_retrieved)) 75 76 77 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 78 def test_add_mmap_label(self): 79 '''Test if chsmack can correctly set a SMACK mmap label''' 80 filename = "/tmp/test_exec_label" 81 self.target.run("touch %s" %filename) 82 status, output = self.target.run("chsmack -m %s %s" %(LABEL, filename)) 83 self.assertEqual( 84 status, 0, 85 "Cannot set smack mmap label. " 86 "Status and output: %d %s" %(status, output)) 87 status, output = self.target.run("chsmack %s" %filename) 88 self.target.run("rm %s" %filename) 89 m = re.search('(mmap=")\S+(?=")', output) 90 if m is None: 91 self.fail("Did not find mmap attribute") 92 else: 93 label_retrieved = re.split("mmap=\"", output)[1][:-1] 94 self.assertEqual( 95 LABEL, label_retrieved, 96 "label not set correctly. expected and gotten: " + 97 "%s %s" %(LABEL,label_retrieved)) 98 99 100 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 101 def test_add_transmutable(self): 102 '''Test if chsmack can correctly set a SMACK transmutable mode''' 103 104 directory = "~/test_transmutable" 105 self.target.run("mkdir -p %s" %directory) 106 status, output = self.target.run("chsmack -t %s" %directory) 107 self.assertEqual(status, 0, "Cannot set smack transmutable. " 108 "Status and output: %d %s" %(status, output)) 109 status, output = self.target.run("chsmack %s" %directory) 110 self.target.run("rmdir %s" %directory) 111 m = re.search('(transmute=")\S+(?=")', output) 112 if m is None: 113 self.fail("Did not find transmute attribute") 114 else: 115 label_retrieved = re.split("transmute=\"", output)[1][:-1] 116 self.assertEqual( 117 "TRUE", label_retrieved, 118 "label not set correctly. expected and gotten: " + 119 "%s %s" %(LABEL,label_retrieved)) 120 121 122 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 123 def test_privileged_change_self_label(self): 124 '''Test if privileged process (with CAP_MAC_ADMIN privilege) 125 can change its label. 126 ''' 127 128 labelf = "/proc/self/attr/current" 129 command = "/bin/sh -c 'echo PRIVILEGED >%s'; cat %s" %(labelf, labelf) 130 131 status, output = self.target.run( 132 "/usr/sbin/notroot.py 0 %s %s" %(self.current_label, command)) 133 134 self.assertIn("PRIVILEGED", output, 135 "Privilege process did not change label.Output: %s" %output) 136 137 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 138 def test_unprivileged_change_self_label(self): 139 '''Test if unprivileged process (without CAP_MAC_ADMIN privilege) 140 cannot change its label''' 141 142 command = "/bin/sh -c 'echo %s >/proc/self/attr/current'" %LABEL 143 status, output = self.target.run( 144 "/usr/sbin/notroot.py %d %s %s" 145 %(self.uid, self.current_label, command) + 146 " 2>&1 | grep 'Operation not permitted'" ) 147 148 self.assertEqual( 149 status, 0, 150 "Unprivileged process should not be able to change its label") 151 152 153 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 154 def test_unprivileged_change_file_label(self): 155 '''Test if unprivileged process cannot change file labels''' 156 157 status, chsmack = self.target.run("which chsmack") 158 status, touch = self.target.run("which touch") 159 filename = "/tmp/test_unprivileged_change_file_label" 160 161 self.target.run("touch %s" % filename) 162 self.target.run("/usr/sbin/notroot.py %d %s" %(self.uid, self.current_label)) 163 status, output = self.target.run( 164 "/usr/sbin/notroot.py " + 165 "%d unprivileged %s -a %s %s 2>&1 " %(self.uid, chsmack, LABEL, filename) + 166 "| grep 'Operation not permitted'" ) 167 168 self.target.run("rm %s" % filename) 169 self.assertEqual( status, 0, "Unprivileged process changed label for %s" %filename) 170 171 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 172 def test_load_smack_rule(self): 173 '''Test if new smack access rules can be loaded''' 174 175 # old 23 character format requires special spaces formatting 176 # 12345678901234567890123456789012345678901234567890123 177 ruleA="TheOne TheOther rwxat" 178 ruleB="TheOne TheOther r----" 179 clean="TheOne TheOther -----" 180 modeA = "rwxat" 181 modeB = "r" 182 183 status, output = self.target.run('echo -n "%s" > %s/load' %(ruleA, self.smack_path)) 184 status, output = self.target.run( 'cat %s/load | grep "^TheOne" | grep " TheOther "' %self.smack_path) 185 self.assertEqual(status, 0, "Rule A was not added") 186 mode = list(filter(bool, output.split(" ")))[2].strip() 187 self.assertEqual( mode, modeA, "Mode A was not set correctly; mode: %s" %mode) 188 189 status, output = self.target.run( 'echo -n "%s" > %s/load' %(ruleB, self.smack_path)) 190 status, output = self.target.run( 'cat %s/load | grep "^TheOne" | grep " TheOther "' %self.smack_path) 191 mode = list(filter(bool, output.split(" ")))[2].strip() 192 self.assertEqual( mode, modeB, "Mode B was not set correctly; mode: %s" %mode) 193 194 self.target.run('echo -n "%s" > %s/load' %(clean, self.smack_path)) 195 196 197 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 198 def test_smack_onlycap(self): 199 '''Test if smack onlycap label can be set 200 201 test needs to change the running label of the current process, 202 so whole test takes places on image 203 ''' 204 status, output = self.target.run("sh /usr/sbin/test_smack_onlycap.sh") 205 self.assertEqual(status, 0, output) 206 207 208 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 209 def test_smack_netlabel(self): 210 211 test_label="191.191.191.191 TheOne" 212 expected_label="191.191.191.191/32 TheOne" 213 214 status, output = self.target.run( "echo -n '%s' > %s/netlabel" %(test_label, self.smack_path)) 215 self.assertEqual( status, 0, "Netlabel /32 could not be set. Output: %s" %output) 216 217 status, output = self.target.run("cat %s/netlabel" %self.smack_path) 218 self.assertIn( expected_label, output, "Did not find expected label in output: %s" %output) 219 220 test_label="253.253.253.0/24 TheOther" 221 status, output = self.target.run( "echo -n '%s' > %s/netlabel" %(test_label, self.smack_path)) 222 self.assertEqual( status, 0, "Netlabel /24 could not be set. Output: %s" %output) 223 224 status, output = self.target.run("cat %s/netlabel" %self.smack_path) 225 self.assertIn( 226 test_label, output, 227 "Did not find expected label in output: %s" %output) 228 229 230 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 231 def test_smack_cipso(self): 232 '''Test if smack cipso rules can be set''' 233 # 12345678901234567890123456789012345678901234567890123456 234 ruleA="TheOneA 2 0 " 235 ruleB="TheOneB 3 1 55 " 236 ruleC="TheOneC 4 2 17 33 " 237 238 status, output = self.target.run( 239 "echo -n '%s' > %s/cipso" %(ruleA, self.smack_path)) 240 self.assertEqual(status, 0, 241 "Could not set cipso label A. Ouput: %s" %output) 242 243 status, output = self.target.run( 244 "cat %s/cipso | grep '^TheOneA'" %self.smack_path) 245 self.assertEqual(status, 0, "Cipso rule A was not set") 246 self.assertIn(" 2", output, "Rule A was not set correctly") 247 248 status, output = self.target.run( 249 "echo -n '%s' > %s/cipso" %(ruleB, self.smack_path)) 250 self.assertEqual(status, 0, 251 "Could not set cipso label B. Ouput: %s" %output) 252 253 status, output = self.target.run( 254 "cat %s/cipso | grep '^TheOneB'" %self.smack_path) 255 self.assertEqual(status, 0, "Cipso rule B was not set") 256 self.assertIn("/55", output, "Rule B was not set correctly") 257 258 status, output = self.target.run( 259 "echo -n '%s' > %s/cipso" %(ruleC, self.smack_path)) 260 self.assertEqual( 261 status, 0, 262 "Could not set cipso label C. Ouput: %s" %output) 263 264 status, output = self.target.run( 265 "cat %s/cipso | grep '^TheOneC'" %self.smack_path) 266 self.assertEqual(status, 0, "Cipso rule C was not set") 267 self.assertIn("/17,33", output, "Rule C was not set correctly") 268 269 270 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 271 def test_smack_direct(self): 272 status, initial_direct = self.target.run( 273 "cat %s/direct" %self.smack_path) 274 275 test_direct="17" 276 status, output = self.target.run( 277 "echo '%s' > %s/direct" %(test_direct, self.smack_path)) 278 self.assertEqual(status, 0 , 279 "Could not set smack direct. Output: %s" %output) 280 status, new_direct = self.target.run("cat %s/direct" %self.smack_path) 281 # initial label before checking 282 status, output = self.target.run( 283 "echo '%s' > %s/direct" %(initial_direct, self.smack_path)) 284 self.assertEqual( 285 test_direct, new_direct.strip(), 286 "Smack direct label does not match.") 287 288 289 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 290 def test_smack_ambient(self): 291 test_ambient = "test_ambient" 292 status, initial_ambient = self.target.run("cat %s/ambient" %self.smack_path) 293 status, output = self.target.run( 294 "echo '%s' > %s/ambient" %(test_ambient, self.smack_path)) 295 self.assertEqual(status, 0, 296 "Could not set smack ambient. Output: %s" %output) 297 298 status, output = self.target.run("cat %s/ambient" %self.smack_path) 299 # Filter '\x00', which is sometimes added to the ambient label 300 new_ambient = ''.join(filter(lambda x: x in string.printable, output)) 301 initial_ambient = ''.join(filter(lambda x: x in string.printable, initial_ambient)) 302 status, output = self.target.run( 303 "echo '%s' > %s/ambient" %(initial_ambient, self.smack_path)) 304 self.assertEqual( 305 test_ambient, new_ambient.strip(), 306 "Ambient label does not match") 307 308 309 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 310 def test_smackload(self): 311 '''Test if smackload command works''' 312 rule="testobject testsubject rwx" 313 314 status, output = self.target.run("echo -n '%s' > /tmp/rules" %rule) 315 status, output = self.target.run("smackload /tmp/rules") 316 self.assertEqual( status, 0, "Smackload failed to load rule. Output: %s" %output) 317 318 status, output = self.target.run( "cat %s/load | grep '%s'" %(self.smack_path, rule)) 319 self.assertEqual(status, 0, "Smackload rule was loaded correctly") 320 321 322 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 323 def test_smackcipso(self): 324 '''Test if smackcipso command works''' 325 # 12345678901234567890123456789012345678901234567890123456 326 rule="cipsolabel 2 2 " 327 328 status, output = self.target.run("echo '%s' | smackcipso" %rule) 329 self.assertEqual( status, 0, "Smackcipso failed to load rule. Output: %s" %output) 330 331 status, output = self.target.run( 332 "cat %s/cipso | grep 'cipsolabel'" %self.smack_path) 333 self.assertEqual(status, 0, "smackcipso rule was loaded correctly") 334 self.assertIn( "2/2", output, "Rule was not set correctly. Got: %s" %output) 335 336 337 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 338 def test_smack_enforce_file_access(self): 339 '''Test if smack file access is enforced (rwx) 340 341 test needs to change the running label of the current process, 342 so whole test takes places on image 343 ''' 344 status, output = self.target.run("sh /usr/sbin/smack_test_file_access.sh") 345 self.assertEqual(status, 0, output) 346 347 348 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 349 def test_smack_transmute_dir(self): 350 '''Test if smack transmute attribute works 351 352 test needs to change the running label of the current process, 353 so whole test takes places on image 354 ''' 355 test_dir = "/tmp/smack_transmute_dir" 356 label="transmute_label" 357 status, initial_label = self.target.run("cat /proc/self/attr/current") 358 359 self.target.run("mkdir -p %s" % test_dir) 360 self.target.run("chsmack -a %s %s" % (label, test_dir)) 361 self.target.run("chsmack -t %s" % test_dir) 362 self.target.run("echo -n '%s %s rwxat' | smackload" %(initial_label, label) ) 363 364 self.target.run("touch %s/test" % test_dir) 365 status, output = self.target.run("chsmack %s/test" % test_dir) 366 self.assertIn( 'access="%s"' %label, output, 367 "Did not get expected label. Output: %s" % output) 368 369 370 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 371 def test_smack_tcp_sockets(self): 372 '''Test if smack is enforced on tcp sockets 373 374 whole test takes places on image, depends on tcp_server/tcp_client''' 375 376 status, output = self.target.run("sh /usr/sbin/test_smack_tcp_sockets.sh") 377 self.assertEqual(status, 0, output) 378 379 380 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 381 def test_smack_udp_sockets(self): 382 '''Test if smack is enforced on udp sockets 383 384 whole test takes places on image, depends on udp_server/udp_client''' 385 386 status, output = self.target.run("sh /usr/sbin/test_smack_udp_sockets.sh") 387 self.assertEqual(status, 0, output) 388 389 390 @OETestDepends(['smack.SmackBasicTest.test_smack_basic']) 391 def test_smack_labels(self): 392 '''Check for correct Smack labels.''' 393 expected = ''' 394/tmp/ access="*" 395/etc/ access="System::Shared" transmute="TRUE" 396/etc/passwd access="System::Shared" 397/etc/terminfo access="System::Shared" transmute="TRUE" 398/etc/skel/ access="System::Shared" transmute="TRUE" 399/etc/skel/.profile access="System::Shared" 400/var/log/ access="System::Log" transmute="TRUE" 401/var/tmp/ access="*" 402''' 403 files = ' '.join([x.split()[0] for x in expected.split('\n') if x]) 404 files_wildcard = ' '.join([x + '/*' for x in files.split()]) 405 # Auxiliary information. 406 status, output = self.target.run( 407 'set -x; mount; ls -l -d %s; find %s | xargs ls -d -l; find %s | xargs chsmack' % ( 408 ' '.join([x.rstrip('/') for x in files.split()]), files, files 409 ) 410 ) 411 msg = "File status:\n" + output 412 status, output = self.target.run('chsmack %s' % files) 413 self.assertEqual( 414 status, 0, msg="status and output: %s and %s\n%s" % (status,output, msg)) 415 self.longMessage = True 416 self.maxDiff = None 417 self.assertEqual(output.strip().split('\n'), expected.strip().split('\n'), msg=msg) 418