tkernel: mounts: add shield mountpoint in container support

Upstream: no

Add shield mountpoint in container support
$ echo "set /dev/name /mnt/point" >/proc/tkernel/shield_mounts
    to shield the mountpoint
$ echo "clear /dev/name /mnt/point" >/proc/tkernel/shield_mounts
    to delete the mountpoint shielded

Signed-off-by: Weiwei Li <nuonuoli@tencent.com>
Signed-off-by: Xiaoming Gao <newtongao@tencent.com>
Signed-off-by: katrinzhou <katrinzhou@tencent.com>
Signed-off-by: Kairui Song <kasong@tencent.com>
This commit is contained in:
katrinzhou 2023-12-06 00:27:39 +08:00 committed by Kairui Song
parent 1c4e7e7a52
commit 7dbd79cf59
5 changed files with 302 additions and 2 deletions

View File

@ -12,6 +12,7 @@
#include <linux/security.h> #include <linux/security.h>
#include <linux/fs_struct.h> #include <linux/fs_struct.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
#include <linux/shield_mounts.h>
#include "proc/internal.h" /* only for get_proc_task() in ->open() */ #include "proc/internal.h" /* only for get_proc_task() in ->open() */
@ -104,7 +105,10 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
struct mount *r = real_mount(mnt); struct mount *r = real_mount(mnt);
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
struct super_block *sb = mnt_path.dentry->d_sb; struct super_block *sb = mnt_path.dentry->d_sb;
int err; int err = 0;
if (is_mount_shielded(current, r->mnt_devname ? r->mnt_devname : "none", mnt))
goto out;
if (sb->s_op->show_devname) { if (sb->s_op->show_devname) {
err = sb->s_op->show_devname(m, mnt_path.dentry); err = sb->s_op->show_devname(m, mnt_path.dentry);
@ -138,7 +142,10 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
struct mount *r = real_mount(mnt); struct mount *r = real_mount(mnt);
struct super_block *sb = mnt->mnt_sb; struct super_block *sb = mnt->mnt_sb;
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
int err; int err = 0;
if (is_mount_shielded(current, r->mnt_devname ? r->mnt_devname : "none", mnt))
goto out;
seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id, seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
MAJOR(sb->s_dev), MINOR(sb->s_dev)); MAJOR(sb->s_dev), MINOR(sb->s_dev));

View File

@ -0,0 +1,15 @@
#ifndef __SHIELD_MOUNTS_H__
#define __SHIELD_MOUNTS_H__
#ifdef CONFIG_TKERNEL_SHIELD_MOUNTS
bool is_mount_shielded(struct task_struct *task,
const char *dev_name, struct vfsmount *mnt);
#else
static inline bool is_mount_shielded(struct task_struct *task,
const char *dev_name, struct vfsmount *mnt)
{
return false;
}
#endif
#endif /* __SHIELD_MOUNTS_H__ */

View File

@ -23,4 +23,8 @@ config TKERNEL_NETATOP
help help
Netatop module from TKernel Netatop module from TKernel
config TKERNEL_SHIELD_MOUNTS
bool 'Shield mount'
default n
endif endif

View File

@ -2,3 +2,4 @@ obj-$(CONFIG_TKERNEL) += base.o
obj-$(CONFIG_TKERNEL_NONPRIV_NETBIND) += netbind.o obj-$(CONFIG_TKERNEL_NONPRIV_NETBIND) += netbind.o
obj-$(CONFIG_TKERNEL_TTOOLS) += ttools/ obj-$(CONFIG_TKERNEL_TTOOLS) += ttools/
obj-$(CONFIG_TKERNEL_NETATOP) += netatop/ obj-$(CONFIG_TKERNEL_NETATOP) += netatop/
obj-$(CONFIG_TKERNEL_SHIELD_MOUNTS) += shield_mounts.o

View File

@ -0,0 +1,273 @@
#include <linux/tkernel.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/shield_mounts.h>
#include <linux/pid_namespace.h>
#define SHIELD_PATH_MAX 1024
struct mount_pair {
struct list_head list;
char dev_name[SHIELD_PATH_MAX];
char mnt_path[SHIELD_PATH_MAX];
};
static DEFINE_RWLOCK(shield_mounts_lock);
static LIST_HEAD(shield_mounts_list);
static unsigned int shield_mounts_count;
static int shield_mounts_max = 512;
static bool __is_mount_shielded(const char *dev_name, const char *mount_path)
{
bool ret = false;
struct mount_pair *p;
read_lock(&shield_mounts_lock);
list_for_each_entry(p, &shield_mounts_list, list) {
if (!strcmp(p->dev_name, dev_name) &&
!strcmp(p->mnt_path, mount_path)) {
ret = true;
break;
}
}
read_unlock(&shield_mounts_lock);
return ret;
}
static bool is_in_container(struct task_struct *task)
{
if (task_active_pid_ns(task)->level)
return true;
else
return false;
}
bool is_mount_shielded(struct task_struct *task, const char *dev_name, struct vfsmount *mnt)
{
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
char *p, *buf;
bool ret;
if (!is_in_container(task))
return false;
buf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!buf)
return false;
p = d_path(&mnt_path, buf, PAGE_SIZE);
if (IS_ERR(p))
ret = PTR_ERR(p);
else
ret = __is_mount_shielded(dev_name, p);
kfree(buf);
return ret;
}
static int shield_mounts_search_and_insert(struct mount_pair *item)
{
int ret = -EEXIST;
struct mount_pair *p;
write_lock(&shield_mounts_lock);
list_for_each_entry(p, &shield_mounts_list, list) {
if (!strcmp(p->dev_name, item->dev_name) &&
!strcmp(p->mnt_path, item->mnt_path))
goto unlock;
}
if (shield_mounts_count >= shield_mounts_max) {
ret = -ENOMEM;
goto unlock;
}
list_add_tail(&item->list, &shield_mounts_list);
shield_mounts_count++;
ret = 0;
unlock:
write_unlock(&shield_mounts_lock);
return ret;
}
static int shield_mounts_search_and_del(struct mount_pair *item)
{
struct mount_pair *p;
write_lock(&shield_mounts_lock);
list_for_each_entry(p, &shield_mounts_list, list) {
if (!strcmp(p->dev_name, item->dev_name) &&
!strcmp(p->mnt_path, item->mnt_path)) {
list_del(&p->list);
kfree(p);
shield_mounts_count--;
goto unlock;
}
}
unlock:
write_unlock(&shield_mounts_lock);
return 0;
}
/* helper function */
static void str_escape(char *s, const char *esc)
{
char *p = s;
while (p && *p != '\0') {
char c = *p++;
while (c != '\0' && strchr(esc, c))
c = *p++;
*s++ = c;
}
*s = '\0';
}
/*
* parse buffer to get shield mount info
* "set devpath mountpoint": means to add a new shield mount info
* "clear devpath mountpoint": means to delete an exist one
*/
static int shield_mounts_parse(char *buf, bool *is_set, struct mount_pair *item)
{
char *token;
str_escape(buf, "\t\n");
buf = skip_spaces(buf);
token = strsep(&buf, " ");
if (!token || !*token || !buf)
goto error;
if (!strcmp(token, "set")) {
/* set */
*is_set = true;
} else if (!strcmp(token, "clear")) {
/* clear */
*is_set = false;
} else {
printk(KERN_ERR"set parse error\n");
goto error;
}
buf = skip_spaces(buf);
/* dev path */
token = strsep(&buf, " ");
if (!buf || !token || !*token || strlen(token) > (PATH_MAX-1)) {
printk(KERN_ERR"dev path faild\n");
goto error;
}
memcpy(item->dev_name, token, strlen(token)+1);
/* mnt */
buf = strim(buf);
memcpy(item->mnt_path, buf, strlen(buf)+1);
return 0;
error:
printk(KERN_ERR"Failed to parse shield mounts pair\n");
return -EFAULT;
}
static int shield_mounts_proc_show(struct seq_file *m, void *v)
{
struct mount_pair *p;
read_lock(&shield_mounts_lock);
list_for_each_entry(p, &shield_mounts_list, list) {
seq_printf(m, "%s on %s\n", p->dev_name, p->mnt_path);
}
read_unlock(&shield_mounts_lock);
return 0;
}
static int shield_mounts_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, shield_mounts_proc_show, NULL);
}
static ssize_t shield_mounts_proc_write(struct file *file, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char *buffer = NULL;
int ret = cnt;
int order = 1;
bool is_set;
struct mount_pair *item;
/*max file lens for 8k*/
if (!ubuf || cnt > PAGE_SIZE * (1 << order))
return -EINVAL;
buffer = (char *)__get_free_pages(GFP_KERNEL, order);
if (!buffer)
return -ENOMEM;
if (copy_from_user(buffer, ubuf, cnt)) {
ret = -EFAULT;
goto out;
}
buffer[cnt] = 0;
item = kmalloc(sizeof(struct mount_pair), GFP_KERNEL);
if (!item) {
printk(KERN_ERR"Failed to malloc mount_pair\n");
ret = -ENOMEM;
goto out;
}
/*parse buffer*/
ret = shield_mounts_parse(buffer, &is_set, item);
if (ret)
goto out1;
if (is_set) {
/*set */
ret = shield_mounts_search_and_insert(item);
if (!ret)
item = NULL;
} else {
/* clear */
ret = shield_mounts_search_and_del(item);
}
if (!ret)
ret = cnt;
out1:
kfree(item);
out:
free_pages((unsigned long) buffer, order);
return ret;
}
int shield_mounts_release(struct inode *inode, struct file *file)
{
return single_release(inode, file);
}
static const struct proc_ops shield_mounts_proc_fops = {
.proc_open = shield_mounts_proc_open,
.proc_read = seq_read,
.proc_write = shield_mounts_proc_write,
.proc_lseek = seq_lseek,
.proc_release = shield_mounts_release,
};
static int __init proc_shield_mounts_init(void)
{
proc_create("shield_mounts", 0, proc_tkernel, &shield_mounts_proc_fops);
return 0;
}
late_initcall(proc_shield_mounts_init);