fs: implement per-VE "xattr-policy" sysctl entry Per-VE "xattr-policy" sysctl entry allows to control how to react on xattr change from inside of a container. There are three options allowed: 1) "reject" - reject any attempt to modify xattr signaling via EPERM error 2) "ignore" - ignore any attempt to modify xattr signaling via EROFS error 3) "accept" - accept any xattr modifications By default "accept" is assigned. The sysctl is placed at /proc/sys/fs/xattr-policy Only node administer is allowed to change it. Closes http://bugzilla.openvz.org/show_bug.cgi?id=1050 Signed-off-by: Cyrill Gorcunov --- fs/Makefile | 4 + fs/vexattr.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xattr.c | 8 +- include/linux/ve.h | 7 + include/linux/vexattr.h | 25 ++++++ kernel/ve/vecalls.c | 4 + 6 files changed, 220 insertions(+), 1 deletion(-) Index: linux-2.6.18-openvz/fs/Makefile ===================================================================== --- linux-2.6.18-openvz.orig/fs/Makefile +++ linux-2.6.18-openvz/fs/Makefile @@ -47,6 +47,10 @@ vzdquota-y += vzdquot.o vzdq_mgmt.o vz vzdquota-$(CONFIG_VZ_QUOTA_UGID) += vzdq_ugid.o vzdquota-$(CONFIG_VZ_QUOTA_UGID) += vzdq_file.o +ifeq ($(CONFIG_SYSFS),y) +obj-y += vexattr.o +endif + obj-$(CONFIG_DNOTIFY) += dnotify.o obj-$(CONFIG_SIM_FS) += simfs.o Index: linux-2.6.18-openvz/fs/vexattr.c ===================================================================== --- /dev/null +++ linux-2.6.18-openvz/fs/vexattr.c @@ -0,0 +1,173 @@ +/* + * linux/kernel/ve/xattr.c + * + * Copyright (C) 2000-2009 Parallels Holdings, Ltd. + * All rights reserved. + * + * Licensing governed by "linux/COPYING.Parallels" file. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Something that isn't CTL_ANY, CTL_NONE or a value that may clash. */ +#define CTL_UNNUMBERED -2 + +#define POLICY_INVALID (1 << 0) +#define POLICY_ACCEPT (1 << 1) +#define POLICY_IGNORE (1 << 2) +#define POLICY_REJECT (1 << 3) + +/* accept/ignore/reject */ +#define POLICY_LEN 8 +#define POLICY_DEFAULT "accept" +#define POLICY_DEFAULT_VAL POLICY_ACCEPT + +DEFINE_SPINLOCK(xattr_policy_spinlock); + +int ve_xattr_check_policy(struct ve_struct *ve) +{ + if (ve_is_super(ve)) + return 0; + + if (ve->xattr_policy & POLICY_REJECT) + return -EPERM; + else if (ve->xattr_policy & POLICY_IGNORE) + return -EROFS; /* strage, I know */ + else if (ve->xattr_policy & POLICY_ACCEPT) + return 0; + + /* should never happen */ + if (!printk_ratelimit()) + printk(KERN_ERR "%s: unexpected value: %d", + __func__, ve->xattr_policy); + + /* we're quite conservative */ + return -EPERM; +} +EXPORT_SYMBOL(ve_xattr_check_policy); + +static int xattr_policy_handler(ctl_table *table, int write, + struct file *file, void __user *buffer, + size_t *length, loff_t *ppos) +{ + struct ve_struct *ve = file->owner_env; + char saved_string[POLICY_LEN]; + int ret; + + if (!ve_is_super(ve)) + return -EPERM; + + if (write) + strncpy(saved_string, (char *)table->data, POLICY_LEN); + + ret = proc_dostring(table, write, file, buffer, length, ppos); + if (ret) + return ret; + + if (write) { + if (!strcmp(table->data, "accept")) { + ve->xattr_policy = POLICY_ACCEPT; + } else if (!strcmp(table->data, "ignore")) { + ve->xattr_policy = POLICY_IGNORE; + } else if (!strcmp(table->data, "reject")) { + ve->xattr_policy = POLICY_REJECT; + } else { + strncpy(table->data, saved_string, POLICY_LEN); + return -EINVAL; + } + } + + return 0; +} + +/* per VE proc/sys/fs/xattr-policy */ +static struct ctl_table xattr_policy_dir[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "xattr-policy", + .maxlen = POLICY_LEN, + .mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), + .proc_handler = xattr_policy_handler, + .strategy = sysctl_string, + }, + { .ctl_name = 0 } +}; + +/* per VE proc/sys/fs */ +static struct ctl_table xattr_policy_root[] = { + { + .ctl_name = CTL_FS, + .procname = "fs", + .mode = 0555, + .child = xattr_policy_dir, + }, + { .ctl_name = 0 } +}; + +int ve_register_xattr_policy(struct ve_struct *ve) +{ + struct ctl_table_header *header; + struct ctl_table *tbl_root, *tbl_dir; + void *p; + + tbl_root = kmemdup(xattr_policy_root, sizeof(xattr_policy_root), GFP_KERNEL); + tbl_dir = kmemdup(xattr_policy_dir, sizeof(xattr_policy_dir), GFP_KERNEL); + p = kzalloc(POLICY_LEN, GFP_KERNEL); + if (!tbl_root || !tbl_dir || !p) + goto err_mem; + + strncpy(p, POLICY_DEFAULT, POLICY_LEN); + tbl_root[0].child = tbl_dir; + tbl_dir[0].data = p; + + header = register_sysctl_table(tbl_root, 0); + if (!header) + goto err_mem; + + spin_lock(&xattr_policy_spinlock); + ve->xattr_policy_header = header; + ve->xattr_policy_table = tbl_root; + ve->xattr_policy = POLICY_DEFAULT_VAL; + spin_unlock(&xattr_policy_spinlock); + + return 0; + +err_mem: + kfree(p); + kfree(tbl_root); + kfree(tbl_dir); + return -ENOMEM; +} +EXPORT_SYMBOL(ve_register_xattr_policy); + +void ve_unregister_xattr_policy(struct ve_struct *ve) +{ + struct ctl_table_header *header; + struct ctl_table *tbl; + + spin_lock(&xattr_policy_spinlock); + header = ve->xattr_policy_header; + tbl = ve->xattr_policy_table; + ve->xattr_policy_header = NULL; + ve->xattr_policy_table = NULL; + ve->xattr_policy = POLICY_INVALID; + spin_unlock(&xattr_policy_spinlock); + + if (!header) + return; + + unregister_sysctl_table(header); + + kfree(tbl[0].child); + kfree(tbl[0].data); + kfree(tbl); +} +EXPORT_SYMBOL(ve_unregister_xattr_policy); Index: linux-2.6.18-openvz/fs/xattr.c ===================================================================== --- linux-2.6.18-openvz.orig/fs/xattr.c +++ linux-2.6.18-openvz/fs/xattr.c @@ -19,7 +19,7 @@ #include #include #include - +#include /* * Check permissions for extended attribute access. This is a bit complicated @@ -69,6 +69,12 @@ vfs_setxattr(struct dentry *dentry, char struct inode *inode = dentry->d_inode; int error; +#ifdef CONFIG_VE + error = ve_xattr_check_policy(get_exec_env()); + if (error) + return error; +#endif + error = xattr_permission(inode, name, MAY_WRITE); if (error) return error; Index: linux-2.6.18-openvz/include/linux/ve.h ===================================================================== --- linux-2.6.18-openvz.orig/include/linux/ve.h +++ linux-2.6.18-openvz/include/linux/ve.h @@ -162,6 +162,13 @@ struct ve_struct { struct proc_dir_entry *_proc_net_devsnmp6; #endif +#ifdef CONFIG_SYSFS + /* per VE xattr policy */ + struct ctl_table_header *xattr_policy_header; + struct ctl_table *xattr_policy_table; + int xattr_policy; +#endif + /* BSD pty's */ #ifdef CONFIG_LEGACY_PTYS struct tty_driver *pty_driver; Index: linux-2.6.18-openvz/include/linux/vexattr.h ===================================================================== --- /dev/null +++ linux-2.6.18-openvz/include/linux/vexattr.h @@ -0,0 +1,25 @@ +/* + * linux/kernel/ve/xattr.c + * + * Copyright (C) 2000-2009 Parallels Holdings, Ltd. + * All rights reserved. + * + * Licensing governed by "linux/COPYING.Parallels" file. + * + */ + +#include + +#ifdef __KERNEL__ + +#ifdef CONFIG_SYSFS +extern int ve_xattr_check_policy(struct ve_struct *ve); +extern int ve_register_xattr_policy(struct ve_struct *ve); +extern void ve_unregister_xattr_policy(struct ve_struct *ve); +#else +static inline int ve_xattr_check_policy(struct ve_struct *ve) { return 0; } +static inline int ve_register_xattr_policy(struct ve_struct *ve) { return 0; } +static inline void ve_unregister_xattr_policy(struct ve_struct *ve) { } +#endif + +#endif Index: linux-2.6.18-openvz/kernel/ve/vecalls.c ===================================================================== --- linux-2.6.18-openvz.orig/kernel/ve/vecalls.c +++ linux-2.6.18-openvz/kernel/ve/vecalls.c @@ -62,6 +62,7 @@ #include #include #include +#include #ifdef CONFIG_FAIRSCHED #include #endif @@ -1619,6 +1620,9 @@ static int do_env_create(envid_t veid, u if ((err = init_ve_sysfs(ve))) goto err_sysfs; + if ((err = ve_register_xattr_policy(ve))) + goto err_sysfs; + if ((err = ve_arp_init(ve)) < 0) goto err_route; if ((err = ve_ndisc_init(ve)) < 0)