xref: /openbmc/openbmc/meta-security/meta-integrity/lib/oeqa/runtime/cases/ima.py (revision 96e4b4e121e0e2da1535d7d537d6a982a6ff5bc0)
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    @skipIfNotFeature('integrity','Test requires "integrity" in DISTRO_FEATURES')
31    def test_ima_enabled(self):
32        ''' Test if IMA policy is loaded before systemd starts'''
33
34        ima_search = "ima: "
35        systemd_search = "systemd .* running"
36        status, output = self.target.run("dmesg | grep -n '%s'" % ima_search)
37        self.assertEqual( status, 0, "Did not find '%s' in dmesg" % ima_search)
38
39
40    @skipIfNotFeature('systemd',
41                      'Test requires systemd to be in DISTRO_FEATURES')
42    @skipIfNotDataVar('VIRTUAL-RUNTIME_init_manager', 'systemd',
43                      'systemd is not the init manager for this image')
44    @OETestDepends(['ima.IMACheck.test_ima_enabled'])
45    def test_ima_before_systemd(self):
46        ''' Test if IMA policy is loaded before systemd starts'''
47        ima_search = "ima: "
48        systemd_search = "systemd .* running"
49        status, output = self.target.run("dmesg | grep -n '%s'" % ima_search)
50        self.assertEqual( status, 0, "Did not find '%s' in dmesg" % ima_search)
51        ima_id = int(output.split(":")[0])
52        status, output = self.target.run("dmesg | grep -n '%s'" % systemd_search)
53        self.assertEqual(status, 0, "Did not find '%s' in dmesg" % systemd_search)
54        init_id = int(output.split(":")[0])
55        if ima_id > init_id:
56            self.fail("IMA does not start before systemd")
57
58
59    @OETestDepends(['ima.IMACheck.test_ima_enabled'])
60    def test_ima_hash(self):
61        ''' Test if IMA stores correct file hash '''
62        filename = "/etc/ld.so.cache"
63        ima_measure_file = "/sys/kernel/security/ima/ascii_runtime_measurements"
64
65        # wait for the IMA system to update the entry
66        maximum_tries = 3
67        tries = 0
68        status, output = self.target.run("sha256sum %s" %filename)
69        sleep(2)
70        current_hash = output.split()[0]
71        ima_hash = ""
72
73        while tries < maximum_tries:
74            status, output = self.target.run("cat %s | grep -e '%s'" \
75                % (ima_measure_file, filename))
76            # get last entry, 4th field
77            if status == 0:
78                tokens = output.split("\n")[-1].split()[3]
79                ima_hash = tokens.split(":")[1]
80                if ima_hash == current_hash:
81                    break
82
83            tries += 1
84            sleep(1)
85
86        # clean target
87        self.target.run("rm %s" % filename)
88        if ima_hash != current_hash:
89            self.fail("Hash stored by IMA does not match actual hash")
90
91
92    @OETestDepends(['ima.IMACheck.test_ima_enabled'])
93    def test_ima_signature(self):
94        ''' Test if IMA stores correct signature for system binaries'''
95        passed = 0
96        failed = 0
97        for b in self.binaries:
98            if b in blacklist:
99                continue
100            status, output = self.target.run("evmctl ima_verify %s" % b)
101            if status != 0:
102                failed += 1
103            else:
104                passed += 1
105
106        if failed == self.total:
107             self.fail("Signature verifications failed (%s)" % self.total)
108
109        #bb.warn("pass: %s, fail: %s, Total: %s" % (passed, failed, total))
110
111    @OETestDepends(['ima.IMACheck.test_ima_enabled'])
112    def test_ima_overwrite(self):
113        ''' Test if IMA prevents overwriting signed files '''
114        passed = 0
115        failed = 0
116        for b in self.binaries:
117            if b in blacklist:
118                continue
119            self.target.run("echo 'foo' >> %s" % b )
120            status, output = self.target.run("evmctl ima_verify %s" % b)
121
122            if status != 0:
123                failed += 1
124            else:
125                passed += 1
126
127        if failed == self.total:
128             self.fail("Overwritting verifications failed (%s)" % self.total)
129