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 ''' |
Andrew Geissler | dc9d614 | 2023-05-19 09:38:37 -0500 | [diff] [blame] | 61 | filename = "/etc/ld.so.cache" |
Brad Bishop | 15ae250 | 2019-06-18 21:44:24 -0400 | [diff] [blame] | 62 | ima_measure_file = "/sys/kernel/security/ima/ascii_runtime_measurements" |
Brad Bishop | 15ae250 | 2019-06-18 21:44:24 -0400 | [diff] [blame] | 63 | |
| 64 | # wait for the IMA system to update the entry |
Andrew Geissler | dc9d614 | 2023-05-19 09:38:37 -0500 | [diff] [blame] | 65 | maximum_tries = 3 |
Brad Bishop | 15ae250 | 2019-06-18 21:44:24 -0400 | [diff] [blame] | 66 | tries = 0 |
Andrew Geissler | dc9d614 | 2023-05-19 09:38:37 -0500 | [diff] [blame] | 67 | status, output = self.target.run("sha256sum %s" %filename) |
Brad Bishop | 15ae250 | 2019-06-18 21:44:24 -0400 | [diff] [blame] | 68 | sleep(2) |
| 69 | current_hash = output.split()[0] |
| 70 | ima_hash = "" |
| 71 | |
| 72 | while tries < maximum_tries: |
Andrew Geissler | dc9d614 | 2023-05-19 09:38:37 -0500 | [diff] [blame] | 73 | status, output = self.target.run("cat %s | grep -e '%s'" \ |
Brad Bishop | 15ae250 | 2019-06-18 21:44:24 -0400 | [diff] [blame] | 74 | % (ima_measure_file, filename)) |
| 75 | # get last entry, 4th field |
| 76 | if status == 0: |
| 77 | tokens = output.split("\n")[-1].split()[3] |
| 78 | ima_hash = tokens.split(":")[1] |
| 79 | if ima_hash == current_hash: |
| 80 | break |
| 81 | |
| 82 | tries += 1 |
| 83 | sleep(1) |
| 84 | |
| 85 | # clean target |
| 86 | self.target.run("rm %s" % filename) |
| 87 | if ima_hash != current_hash: |
| 88 | self.fail("Hash stored by IMA does not match actual hash") |
| 89 | |
| 90 | |
| 91 | @OETestDepends(['ima.IMACheck.test_ima_enabled']) |
| 92 | def test_ima_signature(self): |
| 93 | ''' Test if IMA stores correct signature for system binaries''' |
| 94 | passed = 0 |
| 95 | failed = 0 |
| 96 | for b in self.binaries: |
| 97 | if b in blacklist: |
| 98 | continue |
| 99 | status, output = self.target.run("evmctl ima_verify %s" % b) |
| 100 | if status != 0: |
| 101 | failed += 1 |
| 102 | else: |
| 103 | passed += 1 |
| 104 | |
| 105 | if failed == self.total: |
| 106 | self.fail("Signature verifications failed (%s)" % self.total) |
| 107 | |
| 108 | #bb.warn("pass: %s, fail: %s, Total: %s" % (passed, failed, total)) |
| 109 | |
| 110 | @OETestDepends(['ima.IMACheck.test_ima_enabled']) |
| 111 | def test_ima_overwrite(self): |
| 112 | ''' Test if IMA prevents overwriting signed files ''' |
| 113 | passed = 0 |
| 114 | failed = 0 |
| 115 | for b in self.binaries: |
| 116 | if b in blacklist: |
| 117 | continue |
| 118 | self.target.run("echo 'foo' >> %s" % b ) |
| 119 | status, output = self.target.run("evmctl ima_verify %s" % b) |
| 120 | |
| 121 | if status != 0: |
| 122 | failed += 1 |
| 123 | else: |
| 124 | passed += 1 |
| 125 | |
| 126 | if failed == self.total: |
| 127 | self.fail("Overwritting verifications failed (%s)" % self.total) |