1#!/usr/bin/env python 2# 3# Authors: Cristina Moraru <cristina.moraru@intel.com> 4# Alexandru Cornea <alexandru.cornea@intel.com> 5 6import string 7from time import sleep 8from oeqa.runtime.case import OERuntimeTestCase 9from oeqa.core.decorator.depends import OETestDepends 10from oeqa.runtime.decorator.package import OEHasPackage 11from oeqa.core.decorator.data import skipIfNotFeature 12from oeqa.core.decorator.data import skipIfDataVar, skipIfNotDataVar 13import bb 14blacklist = ["/usr/bin/uz", "/bin/su.shadow"] 15 16class IMACheck(OERuntimeTestCase): 17 18 @classmethod 19 def setUpClass(cls): 20 locations = ["/bin", "/usr/bin"] 21 cls.binaries = [] 22 for l in locations: 23 status, output = cls.tc.target.run("find %s -type f" % l) 24 cls.binaries.extend(output.split("\n")) 25 26 cls.total = len(cls.binaries) 27 28 29 @OETestDepends(['ssh.SSHTest.test_ssh']) 30 def test_ima_enabled(self): 31 ''' Test if IMA policy is loaded before systemd starts''' 32 33 ima_search = "ima: " 34 systemd_search = "systemd .* running" 35 status, output = self.target.run("dmesg | grep -n '%s'" % ima_search) 36 self.assertEqual( status, 0, "Did not find '%s' in dmesg" % ima_search) 37 38 39 @skipIfNotFeature('systemd', 40 'Test requires systemd to be in DISTRO_FEATURES') 41 @skipIfNotDataVar('VIRTUAL-RUNTIME_init_manager', 'systemd', 42 'systemd is not the init manager for this image') 43 @OETestDepends(['ima.IMACheck.test_ima_enabled']) 44 def test_ima_before_systemd(self): 45 ''' Test if IMA policy is loaded before systemd starts''' 46 ima_search = "ima: " 47 systemd_search = "systemd .* running" 48 status, output = self.target.run("dmesg | grep -n '%s'" % ima_search) 49 self.assertEqual( status, 0, "Did not find '%s' in dmesg" % ima_search) 50 ima_id = int(output.split(":")[0]) 51 status, output = self.target.run("dmesg | grep -n '%s'" % systemd_search) 52 self.assertEqual(status, 0, "Did not find '%s' in dmesg" % systemd_search) 53 init_id = int(output.split(":")[0]) 54 if ima_id > init_id: 55 self.fail("IMA does not start before systemd") 56 57 58 @OETestDepends(['ima.IMACheck.test_ima_enabled']) 59 def test_ima_hash(self): 60 ''' Test if IMA stores correct file hash ''' 61 filename = "/etc/filetest" 62 ima_measure_file = "/sys/kernel/security/ima/ascii_runtime_measurements" 63 status, output = self.target.run("echo test > %s" % filename) 64 self.assertEqual(status, 0, "Cannot create file %s on target" % filename) 65 66 # wait for the IMA system to update the entry 67 maximum_tries = 30 68 tries = 0 69 status, output = self.target.run("sha1sum %s" %filename) 70 sleep(2) 71 current_hash = output.split()[0] 72 ima_hash = "" 73 74 while tries < maximum_tries: 75 status, output = self.target.run("cat %s | grep %s" \ 76 % (ima_measure_file, filename)) 77 # get last entry, 4th field 78 if status == 0: 79 tokens = output.split("\n")[-1].split()[3] 80 ima_hash = tokens.split(":")[1] 81 if ima_hash == current_hash: 82 break 83 84 tries += 1 85 sleep(1) 86 87 # clean target 88 self.target.run("rm %s" % filename) 89 if ima_hash != current_hash: 90 self.fail("Hash stored by IMA does not match actual hash") 91 92 93 @OETestDepends(['ima.IMACheck.test_ima_enabled']) 94 def test_ima_signature(self): 95 ''' Test if IMA stores correct signature for system binaries''' 96 passed = 0 97 failed = 0 98 for b in self.binaries: 99 if b in blacklist: 100 continue 101 status, output = self.target.run("evmctl ima_verify %s" % b) 102 if status != 0: 103 failed += 1 104 else: 105 passed += 1 106 107 if failed == self.total: 108 self.fail("Signature verifications failed (%s)" % self.total) 109 110 #bb.warn("pass: %s, fail: %s, Total: %s" % (passed, failed, total)) 111 112 @OETestDepends(['ima.IMACheck.test_ima_enabled']) 113 def test_ima_overwrite(self): 114 ''' Test if IMA prevents overwriting signed files ''' 115 passed = 0 116 failed = 0 117 for b in self.binaries: 118 if b in blacklist: 119 continue 120 self.target.run("echo 'foo' >> %s" % b ) 121 status, output = self.target.run("evmctl ima_verify %s" % b) 122 123 if status != 0: 124 failed += 1 125 else: 126 passed += 1 127 128 if failed == self.total: 129 self.fail("Overwritting verifications failed (%s)" % self.total) 130