diff options
| author | Roberto Sassu <roberto.sassu@huawei.com> | 2026-06-05 20:22:30 +0300 |
|---|---|---|
| committer | Mimi Zohar <zohar@linux.ibm.com> | 2026-06-08 18:43:07 +0300 |
| commit | 51bedcd803e0f140ee39e70a930d01223e1afb58 (patch) | |
| tree | 2965bcfe100e6fe2015ca5e5632c56514c89e796 | |
| parent | cb431ff6a92fc62d91ba64f04c7af3bb54017a1d (diff) | |
| download | linux-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.c | 102 |
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) |
