summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Sassu <roberto.sassu@huawei.com>2026-06-05 20:22:30 +0300
committerMimi Zohar <zohar@linux.ibm.com>2026-06-08 18:43:07 +0300
commit51bedcd803e0f140ee39e70a930d01223e1afb58 (patch)
tree2965bcfe100e6fe2015ca5e5632c56514c89e796
parentcb431ff6a92fc62d91ba64f04c7af3bb54017a1d (diff)
downloadlinux-51bedcd803e0f140ee39e70a930d01223e1afb58.tar.xz
ima: Mediate open/release method of the measurements list
Introduce the ima_measure_users counter, to implement a semaphore-like locking scheme where the binary and ASCII measurements list interfaces can be concurrently opened by multiple readers, or alternatively by a single writer. In addition, allow the same writer to open the other interfaces for write or read/write, so that it can see the same measurement state across all the interfaces. 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 <roberto.sassu@huawei.com> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
-rw-r--r--security/integrity/ima/ima_fs.c102
1 files changed, 98 insertions, 4 deletions
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index dcdc4cb8fa0f..91bd831d070f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,6 +25,10 @@
#include "ima.h"
static DEFINE_MUTEX(ima_write_mutex);
+static DEFINE_MUTEX(ima_measure_mutex);
+static long ima_measure_users;
+static struct task_struct *measure_writer;
+static long measure_writer_extra_writes;
bool ima_canonical_fmt;
static int __init default_canonical_fmt_setup(char *str)
@@ -209,16 +213,105 @@ static const struct seq_operations ima_measurments_seqops = {
.show = ima_measurements_show
};
+static int ima_measure_lock(bool write)
+{
+ mutex_lock(&ima_measure_mutex);
+ /* Overflow check. */
+ if (!write && ima_measure_users == LONG_MAX) {
+ mutex_unlock(&ima_measure_mutex);
+ return -ENFILE;
+ }
+
+ /* Same writer can do additional writes or read/writes. */
+ if (write && current == measure_writer) {
+ measure_writer_extra_writes++;
+ mutex_unlock(&ima_measure_mutex);
+ return 0;
+ }
+
+ /*
+ * ima_measure_users: > 0 open readers
+ * ima_measure_users: == -1 open writer
+ */
+ if ((write && ima_measure_users != 0) ||
+ (!write && ima_measure_users < 0)) {
+ mutex_unlock(&ima_measure_mutex);
+ return -EBUSY;
+ }
+
+ if (write) {
+ ima_measure_users--;
+ /* Pointer valid, no reuse while the file descriptor is open. */
+ measure_writer = current;
+ } else {
+ ima_measure_users++;
+ }
+ mutex_unlock(&ima_measure_mutex);
+ return 0;
+}
+
+static void ima_measure_unlock(bool write)
+{
+ mutex_lock(&ima_measure_mutex);
+ /* Decrement additional writes or read/writes. */
+ if (write && current == measure_writer &&
+ measure_writer_extra_writes != 0) {
+ measure_writer_extra_writes--;
+ mutex_unlock(&ima_measure_mutex);
+ return;
+ }
+ if (write) {
+ ima_measure_users++;
+ measure_writer = NULL;
+ } 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 +376,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)