| Brad Bishop | 15ae250 | 2019-06-18 21:44:24 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python | 
|  | 2 | # | 
|  | 3 | # Authors:  Cristina Moraru <cristina.moraru@intel.com> | 
|  | 4 | #           Alexandru Cornea <alexandru.cornea@intel.com> | 
|  | 5 |  | 
|  | 6 | import string | 
|  | 7 | from time import sleep | 
|  | 8 | from oeqa.runtime.case import OERuntimeTestCase | 
|  | 9 | from oeqa.core.decorator.depends import OETestDepends | 
|  | 10 | from oeqa.runtime.decorator.package import OEHasPackage | 
|  | 11 | from oeqa.core.decorator.data import skipIfNotFeature | 
|  | 12 | from oeqa.core.decorator.data import skipIfDataVar, skipIfNotDataVar | 
|  | 13 | import bb | 
|  | 14 | blacklist = ["/usr/bin/uz", "/bin/su.shadow"] | 
|  | 15 |  | 
|  | 16 | class 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) |