sevki
c9e1dba412
setup CI/CD for kernel development - added CodeQL for code scanning - every pr is built as an image and is available for 30days on https://oklinux.dev - tagged and released on github for now Signed-off-by: sevki <s@sevki.io>
242 lines
6.2 KiB
C
242 lines
6.2 KiB
C
/*
|
|
* Linux Security Module for Chromium OS
|
|
*
|
|
* Copyright 2016 Google Inc. All Rights Reserved
|
|
*
|
|
* Authors:
|
|
* Mattias Nissler <mnissler@chromium.org>
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/capability.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/dcache.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/security.h>
|
|
#include <linux/string.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include "inode_mark.h"
|
|
|
|
static struct dentry *chromiumos_dir;
|
|
static struct dentry *chromiumos_inode_policy_dir;
|
|
|
|
struct chromiumos_inode_policy_file_entry {
|
|
const char *name;
|
|
int (*handle_write)(struct chromiumos_inode_policy_file_entry *,
|
|
struct dentry *);
|
|
enum chromiumos_inode_security_policy_type type;
|
|
enum chromiumos_inode_security_policy policy;
|
|
struct dentry *dentry;
|
|
};
|
|
|
|
static int chromiumos_inode_policy_file_write(
|
|
struct chromiumos_inode_policy_file_entry *file_entry,
|
|
struct dentry *dentry)
|
|
{
|
|
return chromiumos_update_inode_security_policy(dentry->d_inode,
|
|
file_entry->type, file_entry->policy);
|
|
}
|
|
|
|
/*
|
|
* Causes all marks to be removed from inodes thus removing all inode security
|
|
* policies.
|
|
*/
|
|
static int chromiumos_inode_policy_file_flush_write(
|
|
struct chromiumos_inode_policy_file_entry *file_entry,
|
|
struct dentry *dentry)
|
|
{
|
|
return chromiumos_flush_inode_security_policies(dentry->d_sb);
|
|
}
|
|
|
|
static struct chromiumos_inode_policy_file_entry
|
|
chromiumos_inode_policy_files[] = {
|
|
{.name = "block_symlink",
|
|
.handle_write = chromiumos_inode_policy_file_write,
|
|
.type = CHROMIUMOS_SYMLINK_TRAVERSAL,
|
|
.policy = CHROMIUMOS_INODE_POLICY_BLOCK},
|
|
{.name = "allow_symlink",
|
|
.handle_write = chromiumos_inode_policy_file_write,
|
|
.type = CHROMIUMOS_SYMLINK_TRAVERSAL,
|
|
.policy = CHROMIUMOS_INODE_POLICY_ALLOW},
|
|
{.name = "reset_symlink",
|
|
.handle_write = chromiumos_inode_policy_file_write,
|
|
.type = CHROMIUMOS_SYMLINK_TRAVERSAL,
|
|
.policy = CHROMIUMOS_INODE_POLICY_INHERIT},
|
|
{.name = "block_fifo",
|
|
.handle_write = chromiumos_inode_policy_file_write,
|
|
.type = CHROMIUMOS_FIFO_ACCESS,
|
|
.policy = CHROMIUMOS_INODE_POLICY_BLOCK},
|
|
{.name = "allow_fifo",
|
|
.handle_write = chromiumos_inode_policy_file_write,
|
|
.type = CHROMIUMOS_FIFO_ACCESS,
|
|
.policy = CHROMIUMOS_INODE_POLICY_ALLOW},
|
|
{.name = "reset_fifo",
|
|
.handle_write = chromiumos_inode_policy_file_write,
|
|
.type = CHROMIUMOS_FIFO_ACCESS,
|
|
.policy = CHROMIUMOS_INODE_POLICY_INHERIT},
|
|
{.name = "flush_policies",
|
|
.handle_write = &chromiumos_inode_policy_file_flush_write},
|
|
};
|
|
|
|
static int chromiumos_resolve_path(const char __user *buf, size_t len,
|
|
struct path *path)
|
|
{
|
|
char *filename = NULL;
|
|
char *canonical_buf = NULL;
|
|
char *canonical;
|
|
int ret;
|
|
|
|
if (len + 1 > PATH_MAX)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* Copy the path to a kernel buffer. We can't use user_path_at()
|
|
* since it expects a zero-terminated path, which we generally don't
|
|
* have here.
|
|
*/
|
|
filename = kzalloc(len + 1, GFP_KERNEL);
|
|
if (!filename)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(filename, buf, len)) {
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
ret = kern_path(filename, 0, path);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/*
|
|
* Make sure the path is canonical, i.e. it didn't contain symlinks. To
|
|
* check this we convert |path| back to an absolute path (within the
|
|
* global root) and compare the resulting path name with the passed-in
|
|
* |filename|. This is stricter than needed (i.e. consecutive slashes
|
|
* don't get ignored), but that's fine for our purposes.
|
|
*/
|
|
canonical_buf = kzalloc(len + 1, GFP_KERNEL);
|
|
if (!canonical_buf) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
canonical = d_absolute_path(path, canonical_buf, len + 1);
|
|
if (IS_ERR(canonical)) {
|
|
ret = PTR_ERR(canonical);
|
|
|
|
/* Buffer too short implies |filename| wasn't canonical. */
|
|
if (ret == -ENAMETOOLONG)
|
|
ret = -EMLINK;
|
|
|
|
goto out;
|
|
}
|
|
|
|
ret = strcmp(filename, canonical) ? -EMLINK : 0;
|
|
|
|
out:
|
|
kfree(canonical_buf);
|
|
if (ret < 0)
|
|
path_put(path);
|
|
kfree(filename);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t chromiumos_inode_file_write(
|
|
struct file *file,
|
|
const char __user *buf,
|
|
size_t len,
|
|
loff_t *ppos)
|
|
{
|
|
struct chromiumos_inode_policy_file_entry *file_entry =
|
|
file->f_inode->i_private;
|
|
struct path path = {};
|
|
int ret;
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
return -EPERM;
|
|
|
|
if (*ppos != 0)
|
|
return -EINVAL;
|
|
|
|
ret = chromiumos_resolve_path(buf, len, &path);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = file_entry->handle_write(file_entry, path.dentry);
|
|
path_put(&path);
|
|
return ret < 0 ? ret : len;
|
|
}
|
|
|
|
static const struct file_operations chromiumos_inode_policy_file_fops = {
|
|
.write = chromiumos_inode_file_write,
|
|
};
|
|
|
|
static void chromiumos_shutdown_securityfs(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(chromiumos_inode_policy_files); ++i) {
|
|
struct chromiumos_inode_policy_file_entry *entry =
|
|
&chromiumos_inode_policy_files[i];
|
|
securityfs_remove(entry->dentry);
|
|
entry->dentry = NULL;
|
|
}
|
|
|
|
securityfs_remove(chromiumos_inode_policy_dir);
|
|
chromiumos_inode_policy_dir = NULL;
|
|
|
|
securityfs_remove(chromiumos_dir);
|
|
chromiumos_dir = NULL;
|
|
}
|
|
|
|
static int chromiumos_init_securityfs(void)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
chromiumos_dir = securityfs_create_dir("chromiumos", NULL);
|
|
if (!chromiumos_dir) {
|
|
ret = PTR_ERR(chromiumos_dir);
|
|
goto error;
|
|
}
|
|
|
|
chromiumos_inode_policy_dir =
|
|
securityfs_create_dir(
|
|
"inode_security_policies",
|
|
chromiumos_dir);
|
|
if (!chromiumos_inode_policy_dir) {
|
|
ret = PTR_ERR(chromiumos_inode_policy_dir);
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(chromiumos_inode_policy_files); ++i) {
|
|
struct chromiumos_inode_policy_file_entry *entry =
|
|
&chromiumos_inode_policy_files[i];
|
|
entry->dentry = securityfs_create_file(
|
|
entry->name, 0200, chromiumos_inode_policy_dir,
|
|
entry, &chromiumos_inode_policy_file_fops);
|
|
if (IS_ERR(entry->dentry)) {
|
|
ret = PTR_ERR(entry->dentry);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
chromiumos_shutdown_securityfs();
|
|
return ret;
|
|
}
|
|
fs_initcall(chromiumos_init_securityfs);
|