mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
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:
committed by
Mimi Zohar
parent
cb431ff6a9
commit
51bedcd803
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user