From: Roberto Sassu <[email protected]>

Introduce the ima_measure_users counter, to implement a semaphore-like
locking scheme where the binary and ASCII measurements list interfaces can
be concurrently open by multiple readers, or alternatively by a single
writer.

A semaphore cannot be used because the kernel cannot return to user space
with a lock held.

Introduce the ima_measure_lock() and ima_measure_unlock() primitives, to
respectively lock/unlock the interfaces (safely with the ima_measure_users
counter, without holding a lock).

Finally, introduce _ima_measurements_open() to lock the interface before
seq_open(), and call it from ima_measurements_open() and
ima_ascii_measurements_open(). And, introduce ima_measurements_release(),
to unlock the interface.

Require CAP_SYS_ADMIN if the interface is opened for write (not possible
for the current measurements interfaces, since they only have read
permission).

No functional changes: multiple readers are allowed as before.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <[email protected]>
---
 security/integrity/ima/ima_fs.c | 71 +++++++++++++++++++++++++++++++--
 1 file changed, 67 insertions(+), 4 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 9a8dba14d82a..68edea7139d5 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,6 +25,8 @@
 #include "ima.h"
 
 static DEFINE_MUTEX(ima_write_mutex);
+static DEFINE_MUTEX(ima_measure_mutex);
+static long ima_measure_users;
 
 bool ima_canonical_fmt;
 static int __init default_canonical_fmt_setup(char *str)
@@ -209,16 +211,76 @@ static const struct seq_operations ima_measurments_seqops 
= {
        .show = ima_measurements_show
 };
 
+static int ima_measure_lock(bool write)
+{
+       mutex_lock(&ima_measure_mutex);
+       if ((write && ima_measure_users != 0) ||
+           (!write && ima_measure_users < 0)) {
+               mutex_unlock(&ima_measure_mutex);
+               return -EBUSY;
+       }
+
+       if (write)
+               ima_measure_users--;
+       else
+               ima_measure_users++;
+       mutex_unlock(&ima_measure_mutex);
+       return 0;
+}
+
+static void ima_measure_unlock(bool write)
+{
+       mutex_lock(&ima_measure_mutex);
+       if (write)
+               ima_measure_users++;
+       else
+               ima_measure_users--;
+       mutex_unlock(&ima_measure_mutex);
+}
+
+static int _ima_measurements_open(struct inode *inode, struct file *file,
+                                 const struct seq_operations *seq_ops)
+{
+       bool write = (file->f_mode & FMODE_WRITE);
+       int ret;
+
+       if (write && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       ret = ima_measure_lock(write);
+       if (ret < 0)
+               return ret;
+
+       ret = seq_open(file, seq_ops);
+       if (ret < 0)
+               ima_measure_unlock(write);
+
+       return ret;
+}
+
 static int ima_measurements_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &ima_measurments_seqops);
+       return _ima_measurements_open(inode, file, &ima_measurments_seqops);
+}
+
+static int ima_measurements_release(struct inode *inode, struct file *file)
+{
+       bool write = (file->f_mode & FMODE_WRITE);
+       int ret;
+
+       /* seq_release() always returns zero. */
+       ret = seq_release(inode, file);
+
+       ima_measure_unlock(write);
+
+       return ret;
 }
 
 static const struct file_operations ima_measurements_ops = {
        .open = ima_measurements_open,
        .read = seq_read,
        .llseek = seq_lseek,
-       .release = seq_release,
+       .release = ima_measurements_release,
 };
 
 void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
@@ -283,14 +345,15 @@ static const struct seq_operations 
ima_ascii_measurements_seqops = {
 
 static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &ima_ascii_measurements_seqops);
+       return _ima_measurements_open(inode, file,
+                                     &ima_ascii_measurements_seqops);
 }
 
 static const struct file_operations ima_ascii_measurements_ops = {
        .open = ima_ascii_measurements_open,
        .read = seq_read,
        .llseek = seq_lseek,
-       .release = seq_release,
+       .release = ima_measurements_release,
 };
 
 static ssize_t ima_read_policy(char *path)
-- 
2.43.0


Reply via email to