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>
This commit is contained in:
Roberto Sassu
2026-06-05 19:22:30 +02:00
committed by Mimi Zohar
parent cb431ff6a9
commit 51bedcd803

View File

@@ -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)