Integrity Measurement Architecture

The Linux IMA subsystem is responsible for calculating the hashes of files and programs before they are loaded, and supports reporting on the hashes and validate if they adhere to a predefined list. In this guide, we introduce this technology and how it can be enabled in Gentoo Linux.

Introduction
The Linux IMA subsystem introduces hooks within the Linux kernel to support creating and collecting hashes of files when opened, before their contents are accessed for read or execute. The IMA subsystem has 2 subsystems within in it - measure and appraise. Measurement collects the hashes of files, while appraisal compares a collected hash to a stored hash and denies access in the event of a mismatch

To support proven integrity of the files, the IMA measurement subsystem can interact with the TPM chip within the system to protect the collected hashes from tampering by a rogue administrator or application.

The IMA measurement subsystem was added in linux-2.6.30. Appraisal came later, in linux-3.7.

Protection of the extended attribute itself is handled by EVM.

Trusted Computing Base
The Trusted Computing Base defines a set of rules that a properly, integrity-protected system should adhere to. Linux defines 2 policies for this: The tcb policy, which performs measurement, and tcb_appraise which performs appraisal.

Kernel configuration
First of all, enable the IMA subsystem in the Linux kernel configuration.

Kernel command line options
There are 2 important IMA command line options: ima_appraise= and ima_policy=

ima_appraise= can take 1 of 4 values:
 * enforce causes IMA to appraise files according to policy. Access is denied to the appraised file if the store hash is missing or does not match the collected value. IMA generates new stored hashes for newly created files, but not changed ones.
 * log is similar to enforce except access is not denied but only logged/
 * off disables all appraisal. The stored hashes aren't checked and new files don't get a stored hash
 * fix disabled all appraisal, but generates new stored hashes for any file opened for read or without one. Incorrect stored hashes are not fixed, however.

ima_policy= can take 1 of 3 values:
 * tcb measures all executables run, all mmap'd files for execution (such as shared libraries), all kernel modules loaded, and all firmware loaded. Additonally, a files opened for read by root are measured as well.
 * appraise_tcb appraises all files owned by root.
 * secure_boot appraises all loaded modules, firmware, kexec'd kernel, and IMA policies. It also requires them to have an IMA signature as well. This is normally used with the CONFIG_INTEGRITY_TRUSTED_KEYRING option in the kernel in "secure boot" scenario, with the public key obtained from the OEM in firmware or via the MOK (Machine Owner Key) in shim.

ima_policy= can be specified multiple times, and the result is the union of the policies.

Enable i_version mount option (optional)
Mount all file systems with the i_version support (which, sadly, means you need to mount it with  mount option - without the underscore). This is an optimization for IMA, allowing it to only recompute the hash of file when it actually changes instead of every time the file is opened.

For the root file system, you might want to enable it through the  kernel parameter as well so that it gets mounted immediately with i_version support when the Linux kernel mounts the root file system.

Note that the underscore needs to be present here, unlike in the fstab case.

Registering the file hashes for the system
First boot with the  boot option. This will allow the system to boot up even when no (or wrong) hashes are registered.

Next, we need to open all files that will be appraised for read

Its also possible to use the  utility which is part of

When done, the stored hash value should show as an extended attribute:

Finally, reboot with. The system should now run with appraisal enabled, causing the system to validate the hash against the stored value before using it. If it doesn't match, then the file is not loaded and any access towards it will be denied with a Permission denied error.

You can check if this works by booting with  and changing the contents of a root-owned file (or the value of the extended attribute) and reboot with , or by directly editing virtual guest images.

Using digital signatures for immutable files
The IMA appraisal code also supports immutable files. In this case, an RSA-key based signature is taken of the file and stored in the extended attribute. The private key is used to sign the files, whereas the public key is used to verify the signature. This provides additional protection against tampering as the private key does not need to be available on the system while its running (only during the initial marking).

To sign such immutable files (like kernel modules and application code), you need to use the  command provided by the  package. But first, setup the kernel keyring:

This allows the IMA subsystem to validate the signature (which is also needed when initially setting the signature) by loading the public key onto the IMA keyring. You will need to do this every time the system boots, so it makes sense to do so within an initramfs (early in the boot process):

This private key can be generated first using :

To generate an unencrypted private key (non-protected):

Or an encrypted private key (password-protected):

To generate the public key:

Immutable file support is mainly used to digitally sign the Linux kernel and the kernel modules and is supported through the EVM technology (which we will discuss in different documentation) but works well on ELF and other binaries as well.

Reading the integrity log
To read the integrity log as registered by the IMA subsystem, look at the file:

What you see here are:


 * The PCR (Process Control Register) in which the values are registered. This only makes sense if you have a TPM chip in use.
 * The hash of the hash of the file + filename (padded with null characters to the length of 255 bytes)
 * The subsystem that registered the integrity value (ima in our case)
 * The hash of the file content

Unless you have asked for a different hashing algorithm, the default one will be used which is the SHA-1 hashing algorithm.

How do I know IMA with appraisal is working?
This is as simple as finding a file that does not have its hash value stored as an extended attribute while ima_appraise is in enforcing mode.

In the above example, the IMA subsystem reports that the file misses its hash value (which should be stored as security.ima ) and as such is denying the   application access to it.

If you can miss the file (such as with ) you can remove it and regenerate it if you wish:

If you are using SELinux:

Next:

I was able to edit an 'immutable' file and still run it. How come?
If you digitally signed a script using  and then edited the file with , then this behavior is to be expected. removes the original file and replaces it with a new one. The newly created file is given an appropriate hash (but no digital signature of course) and thus you can still execute it.

The use of digital signatures is more for kernel modules and ELF binaries. But below an example of how it does work - if you edit the file rather than replace it.

How do I load in my own, custom IMA policy?
You can load in an IMA policy by  'ing it into. If the policy is accepted, then the command will succeed and the (pseudo)file will disappear (this is by design, so that malicious users cannot alter the policy once loaded).

Below is an example custom policy, taken from the default one with one addition: ask it not to measure and appraise log files (through the use of the SELinux  attribute).

Make sure no empty lines are in the policy; if not, it will be refused. You can check the output of  for hints why the policy was refused (it shows what was accepted, so the next line would be a not-accepted line), or the audit logs (but you will need to have   running) if you get lines such as audit_printk_skb: XX callbacks suppressed as you then might not have all the information you need.

Have the policy be loaded in as soon as possible, either in an initramfs or early in the boot process through an init script in the sysinit runlevel. I keep my policy in and use the following small init script to load it early on:

Online resources tell me I can use head -n 1 to regenerate the hashes
I have bad experiences with this method. Some files are left without a hash, or when I later enable EVM the EVM hash itself remains missing. The use of the  command does the trick pretty well.