mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-01 10:43:43 +00:00
8b985bbfab
One of concerns for enabling TOMOYO in prebuilt kernels is that distributor wants to avoid bloating kernel packages. Although boot-time kernel command line options allows selecting built-in LSMs to enable, file size increase of vmlinux and memory footprint increase of vmlinux caused by builtin-but- not-enabled LSMs remains. If it becomes possible to make LSMs dynamically appendable after boot using loadable kernel modules, these problems will go away. Another of concerns for enabling TOMOYO in prebuilt kernels is that who can provide support when distributor cannot provide support. Due to "those who compiled kernel code is expected to provide support for that kernel code" spell, TOMOYO is failing to get enabled in Fedora distribution [1]. The point of loadable kernel module is to share the workload. If it becomes possible to make LSMs dynamically appendable after boot using loadable kernel modules, as with people can use device drivers not supported by distributors but provided by third party device vendors, we can break this spell and can lower the barrier for using TOMOYO. This patch is intended for demonstrating that there is nothing difficult for supporting TOMOYO-like loadable LSM modules. For now we need to live with a mixture of built-in part and loadable part because fully loadable LSM modules are not supported since Linux 2.6.24 [2] and number of LSMs which can reserve static call slots is determined at compile time in Linux 6.12. Major changes in this patch are described below. There are no behavior changes as long as TOMOYO is built into vmlinux. Add CONFIG_SECURITY_TOMOYO_LKM as "bool" instead of changing CONFIG_SECURITY_TOMOYO from "bool" to "tristate", for something went wrong with how Makefile is evaluated if I choose "tristate". Add proxy.c for serving as a bridge between vmlinux and tomoyo.ko . Move callback functions from init.c to proxy.c when building as a loadable LSM module. init.c is built-in part and remains for reserving static call slots. proxy.c contains module's init function and tells init.c location of callback functions, making it possible to use static call for tomoyo.ko . By deferring initialization of "struct tomoyo_task" until tomoyo.ko is loaded, threads created between init.c reserved LSM hooks and proxy.c updates LSM hooks will have NULL "struct tomoyo_task" instances. Assuming that tomoyo.ko is loaded by the moment when the global init process starts, initialize "struct tomoyo_task" instance for current thread as a kernel thread when tomoyo_task(current) is called for the first time. There is a hack for exporting currently not-exported functions. This hack will be removed after all relevant functions are exported. Link: https://bugzilla.redhat.com/show_bug.cgi?id=542986 [1] Link: https://lkml.kernel.org/r/caafb609-8bef-4840-a080-81537356fc60@I-love.SAKURA.ne.jp [2] Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
278 lines
7.4 KiB
C
278 lines
7.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* security/tomoyo/securityfs_if.c
|
|
*
|
|
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
|
*/
|
|
|
|
#include <linux/security.h>
|
|
#include "common.h"
|
|
|
|
/**
|
|
* tomoyo_check_task_acl - Check permission for task operation.
|
|
*
|
|
* @r: Pointer to "struct tomoyo_request_info".
|
|
* @ptr: Pointer to "struct tomoyo_acl_info".
|
|
*
|
|
* Returns true if granted, false otherwise.
|
|
*/
|
|
static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
|
|
const struct tomoyo_acl_info *ptr)
|
|
{
|
|
const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
|
|
head);
|
|
|
|
return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
|
|
}
|
|
|
|
/**
|
|
* tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface.
|
|
*
|
|
* @file: Pointer to "struct file".
|
|
* @buf: Domainname to transit to.
|
|
* @count: Size of @buf.
|
|
* @ppos: Unused.
|
|
*
|
|
* Returns @count on success, negative value otherwise.
|
|
*
|
|
* If domain transition was permitted but the domain transition failed, this
|
|
* function returns error rather than terminating current thread with SIGKILL.
|
|
*/
|
|
static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
char *data;
|
|
int error;
|
|
|
|
if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
|
|
return -ENOMEM;
|
|
data = memdup_user_nul(buf, count);
|
|
if (IS_ERR(data))
|
|
return PTR_ERR(data);
|
|
tomoyo_normalize_line(data);
|
|
if (tomoyo_correct_domain(data)) {
|
|
const int idx = tomoyo_read_lock();
|
|
struct tomoyo_path_info name;
|
|
struct tomoyo_request_info r;
|
|
|
|
name.name = data;
|
|
tomoyo_fill_path_info(&name);
|
|
/* Check "task manual_domain_transition" permission. */
|
|
tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
|
|
r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL;
|
|
r.param.task.domainname = &name;
|
|
tomoyo_check_acl(&r, tomoyo_check_task_acl);
|
|
if (!r.granted)
|
|
error = -EPERM;
|
|
else {
|
|
struct tomoyo_domain_info *new_domain =
|
|
tomoyo_assign_domain(data, true);
|
|
if (!new_domain) {
|
|
error = -ENOENT;
|
|
} else {
|
|
struct tomoyo_task *s = tomoyo_task(current);
|
|
struct tomoyo_domain_info *old_domain =
|
|
s->domain_info;
|
|
|
|
s->domain_info = new_domain;
|
|
atomic_inc(&new_domain->users);
|
|
atomic_dec(&old_domain->users);
|
|
error = 0;
|
|
}
|
|
}
|
|
tomoyo_read_unlock(idx);
|
|
} else
|
|
error = -EINVAL;
|
|
kfree(data);
|
|
return error ? error : count;
|
|
}
|
|
|
|
/**
|
|
* tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface.
|
|
*
|
|
* @file: Pointer to "struct file".
|
|
* @buf: Domainname which current thread belongs to.
|
|
* @count: Size of @buf.
|
|
* @ppos: Bytes read by now.
|
|
*
|
|
* Returns read size on success, negative value otherwise.
|
|
*/
|
|
static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
const char *domain = tomoyo_domain()->domainname->name;
|
|
loff_t len = strlen(domain);
|
|
loff_t pos = *ppos;
|
|
|
|
if (pos >= len || !count)
|
|
return 0;
|
|
len -= pos;
|
|
if (count < len)
|
|
len = count;
|
|
if (copy_to_user(buf, domain + pos, len))
|
|
return -EFAULT;
|
|
*ppos += len;
|
|
return len;
|
|
}
|
|
|
|
/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */
|
|
static const struct file_operations tomoyo_self_operations = {
|
|
.write = tomoyo_write_self,
|
|
.read = tomoyo_read_self,
|
|
};
|
|
|
|
/**
|
|
* tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* @inode: Pointer to "struct inode".
|
|
* @file: Pointer to "struct file".
|
|
*
|
|
* Returns 0 on success, negative value otherwise.
|
|
*/
|
|
static int tomoyo_open(struct inode *inode, struct file *file)
|
|
{
|
|
const u8 key = (uintptr_t) file_inode(file)->i_private;
|
|
|
|
return tomoyo_open_control(key, file);
|
|
}
|
|
|
|
/**
|
|
* tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* @inode: Pointer to "struct inode".
|
|
* @file: Pointer to "struct file".
|
|
*
|
|
*/
|
|
static int tomoyo_release(struct inode *inode, struct file *file)
|
|
{
|
|
tomoyo_close_control(file->private_data);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* @file: Pointer to "struct file".
|
|
* @wait: Pointer to "poll_table". Maybe NULL.
|
|
*
|
|
* Returns EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM if ready to read/write,
|
|
* EPOLLOUT | EPOLLWRNORM otherwise.
|
|
*/
|
|
static __poll_t tomoyo_poll(struct file *file, poll_table *wait)
|
|
{
|
|
return tomoyo_poll_control(file, wait);
|
|
}
|
|
|
|
/**
|
|
* tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* @file: Pointer to "struct file".
|
|
* @buf: Pointer to buffer.
|
|
* @count: Size of @buf.
|
|
* @ppos: Unused.
|
|
*
|
|
* Returns bytes read on success, negative value otherwise.
|
|
*/
|
|
static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
return tomoyo_read_control(file->private_data, buf, count);
|
|
}
|
|
|
|
/**
|
|
* tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* @file: Pointer to "struct file".
|
|
* @buf: Pointer to buffer.
|
|
* @count: Size of @buf.
|
|
* @ppos: Unused.
|
|
*
|
|
* Returns @count on success, negative value otherwise.
|
|
*/
|
|
static ssize_t tomoyo_write(struct file *file, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
return tomoyo_write_control(file->private_data, buf, count);
|
|
}
|
|
|
|
/*
|
|
* tomoyo_operations is a "struct file_operations" which is used for handling
|
|
* /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR).
|
|
* See tomoyo_io_buffer for internals.
|
|
*/
|
|
static const struct file_operations tomoyo_operations = {
|
|
.open = tomoyo_open,
|
|
.release = tomoyo_release,
|
|
.poll = tomoyo_poll,
|
|
.read = tomoyo_read,
|
|
.write = tomoyo_write,
|
|
.llseek = noop_llseek,
|
|
};
|
|
|
|
/**
|
|
* tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
|
|
*
|
|
* @name: The name of the interface file.
|
|
* @mode: The permission of the interface file.
|
|
* @parent: The parent directory.
|
|
* @key: Type of interface.
|
|
*
|
|
* Returns nothing.
|
|
*/
|
|
static void __init tomoyo_create_entry(const char *name, const umode_t mode,
|
|
struct dentry *parent, const u8 key)
|
|
{
|
|
securityfs_create_file(name, mode, parent, (void *) (uintptr_t) key,
|
|
&tomoyo_operations);
|
|
}
|
|
|
|
/**
|
|
* tomoyo_interface_init - Initialize /sys/kernel/security/tomoyo/ interface.
|
|
*
|
|
* Returns 0.
|
|
*/
|
|
int __init tomoyo_interface_init(void)
|
|
{
|
|
struct tomoyo_domain_info *domain;
|
|
struct dentry *tomoyo_dir;
|
|
|
|
#ifndef CONFIG_SECURITY_TOMOYO_LKM
|
|
if (!tomoyo_enabled)
|
|
return 0;
|
|
#endif
|
|
domain = tomoyo_domain();
|
|
/* Don't create securityfs entries unless registered. */
|
|
if (domain != &tomoyo_kernel_domain)
|
|
return 0;
|
|
|
|
tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
|
|
tomoyo_create_entry("query", 0600, tomoyo_dir,
|
|
TOMOYO_QUERY);
|
|
tomoyo_create_entry("domain_policy", 0600, tomoyo_dir,
|
|
TOMOYO_DOMAINPOLICY);
|
|
tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
|
|
TOMOYO_EXCEPTIONPOLICY);
|
|
tomoyo_create_entry("audit", 0400, tomoyo_dir,
|
|
TOMOYO_AUDIT);
|
|
tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
|
|
TOMOYO_PROCESS_STATUS);
|
|
tomoyo_create_entry("stat", 0644, tomoyo_dir,
|
|
TOMOYO_STAT);
|
|
tomoyo_create_entry("profile", 0600, tomoyo_dir,
|
|
TOMOYO_PROFILE);
|
|
tomoyo_create_entry("manager", 0600, tomoyo_dir,
|
|
TOMOYO_MANAGER);
|
|
tomoyo_create_entry("version", 0400, tomoyo_dir,
|
|
TOMOYO_VERSION);
|
|
securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL,
|
|
&tomoyo_self_operations);
|
|
tomoyo_load_builtin_policy();
|
|
return 0;
|
|
}
|
|
|
|
#ifndef CONFIG_SECURITY_TOMOYO_LKM
|
|
fs_initcall(tomoyo_interface_init);
|
|
#endif
|