[RFC PATCH v4 2/2] ima: force re-appraisal on filesystems with FS_IMA_NO_CACHE
From: Dongsu Park
Date: Tue Jan 30 2018 - 13:06:10 EST
From: Alban Crequy <alban@xxxxxxxxxx>
This patch forces files to be re-measured, re-appraised and re-audited
on file systems with the feature flag FS_IMA_NO_CACHE. In that way,
cached integrity results won't be used.
How to test this:
The test I did was using a patched version of the memfs FUSE driver
[1][2] and two very simple "hello-world" programs [4] (prog1 prints
"hello world: 1" and prog2 prints "hello world: 2").
I copy prog1 and prog2 in the fuse-memfs mount point, execute them and
check the sha1 hash in
"/sys/kernel/security/ima/ascii_runtime_measurements".
My patch on the memfs FUSE driver added a backdoor command to serve
prog1 when the kernel asks for prog2 or vice-versa. In this way, I can
exec prog1 and get it to print "hello world: 2" without ever replacing
the file via the VFS, so the kernel is not aware of the change.
The test was done using the branch "dongsu/fuse-flag-ima-nocache-v4" [3].
Step by step test procedure:
1. Mount the memfs FUSE using [2]:
rm -f /tmp/memfs-switch* ; memfs -L DEBUG /mnt/memfs
2. Copy prog1 and prog2 using [4]
cp prog1 /mnt/memfs/prog1
cp prog2 /mnt/memfs/prog2
3. Lookup the files and let the FUSE driver to keep the handles open:
dd if=/mnt/memfs/prog1 bs=1 | (read -n 1 x ; sleep 3600 ) &
dd if=/mnt/memfs/prog2 bs=1 | (read -n 1 x ; sleep 3600 ) &
4. Check the 2 programs work correctly:
$ /mnt/memfs/prog1
hello world: 1
$ /mnt/memfs/prog2
hello world: 2
5. Check the measurements for prog1 and prog2:
$ sudo cat /sys/kernel/security/ima/ascii_runtime_measurements \
| grep /mnt/memfs/prog
10 [...] ima-ng sha1:ac14c9268cd2[...] /mnt/memfs/prog1
10 [...] ima-ng sha1:799cb5d1e06d[...] /mnt/memfs/prog2
6. Use the backdoor command in my patched memfs to redirect file
operations on file handle 3 to file handle 2:
rm -f /tmp/memfs-switch* ; touch /tmp/memfs-switch-3-2
7. Check how the FUSE driver serves different content for the files:
$ /mnt/memfs/prog1
hello world: 2
$ /mnt/memfs/prog2
hello world: 2
8. Check the measurements:
sudo cat /sys/kernel/security/ima/ascii_runtime_measurements \
| grep /mnt/memfs/prog
Without the patch, there are no new measurements, despite the FUSE
driver having served different executables.
With the patch, I can see additional measurements for prog1 and prog2
with the hashes reversed when the FUSE driver served the alternative
content.
[1] https://github.com/bbengfort/memfs
[2] https://github.com/kinvolk/memfs/commits/alban/switch-files
[3] https://github.com/kinvolk/linux/commits/dongsu/fuse-flag-ima-nocache-v4
[4] https://github.com/kinvolk/fuse-userns-patches/commit/cf1f5750cab0
Cc: linux-kernel@xxxxxxxxxxxxxxx
Cc: linux-integrity@xxxxxxxxxxxxxxx
Cc: linux-security-module@xxxxxxxxxxxxxxx
Cc: linux-fsdevel@xxxxxxxxxxxxxxx
Cc: Miklos Szeredi <miklos@xxxxxxxxxx>
Cc: Alexander Viro <viro@xxxxxxxxxxxxxxxxxx>
Cc: Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx>
Cc: Dmitry Kasatkin <dmitry.kasatkin@xxxxxxxxx>
Cc: James Morris <jmorris@xxxxxxxxx>
Cc: Christoph Hellwig <hch@xxxxxxxxxxxxx>
Acked-by: "Serge E. Hallyn" <serge@xxxxxxxxxx>
Acked-by: Seth Forshee <seth.forshee@xxxxxxxxxxxxx>
Tested-by: Dongsu Park <dongsu@xxxxxxxxxx>
Signed-off-by: Alban Crequy <alban@xxxxxxxxxx>
[dongsu: optimized code to address review comments by Mimi]
Signed-off-by: Dongsu Park <dongsu@xxxxxxxxxx>
---
security/integrity/ima/ima_main.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 6d78cb26..83edbad8 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/xattr.h>
#include <linux/ima.h>
+#include <linux/fs.h>
#include "ima.h"
@@ -228,9 +229,19 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
IMA_ACTION_FLAGS);
- if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags))
- /* reset all flags if ima_inode_setxattr was called */
+ /*
+ * Reset the measure, appraise and audit cached flags either if:
+ * - ima_inode_setxattr was called, or
+ * - based on filesystem feature flag
+ * forcing the file to be re-evaluated.
+ */
+ if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags)) {
iint->flags &= ~IMA_DONE_MASK;
+ } else if (inode->i_sb->s_type->fs_flags & FS_IMA_NO_CACHE) {
+ iint->flags &= ~IMA_DONE_MASK;
+ if (action & IMA_MEASURE)
+ iint->measured_pcrs = 0;
+ }
/* Determine if already appraised/measured based on bitmask
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
--
2.13.6