--- linux-2.6.16-rc1/fs/attr.c 2006-01-26 22:35:10 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/attr.c 2006-01-25 05:34:51 +0100 @@ -56,6 +59,30 @@ int inode_change_ok(struct inode *inode, if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) goto error; } + + /* Check for evil vserver activity */ + if (vx_check(0, VX_ADMIN)) + goto fine; + + if (IS_BARRIER(inode)) { + vxwprintk(1, "xid=%d messing with the barrier.", + vx_current_xid()); + goto error; + } + switch (inode->i_sb->s_magic) { + case PROC_SUPER_MAGIC: + /* maybe allow that in the future? */ + vxwprintk(1, "xid=%d messing with the procfs.", + vx_current_xid()); + goto error; + case DEVPTS_SUPER_MAGIC: + /* devpts is xid tagged */ + if (vx_check((xid_t)inode->i_tag, VX_IDENT)) + goto fine; + vxwprintk(1, "xid=%d messing with the devpts.", + vx_current_xid()); + goto error; + } fine: retval = 0; error: --- linux-2.6.16-rc1/fs/attr.c 2006-01-26 22:35:10 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/attr.c 2006-01-25 05:34:51 +0100 @@ -153,7 +182,8 @@ int notify_change(struct dentry * dentry error = security_inode_setattr(dentry, attr); if (!error) { if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || - (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid) || + (ia_valid & ATTR_TAG && attr->ia_tag != inode->i_tag)) error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; if (!error) error = inode_setattr(inode, attr); --- linux-2.6.16-rc1/fs/devpts/inode.c 2006-01-26 22:35:10 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/devpts/inode.c 2006-01-25 05:34:51 +0100 @@ -19,7 +19,20 @@ #include #include -#define DEVPTS_SUPER_MAGIC 0x1cd1 + +static int devpts_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + int ret = -EACCES; + + /* devpts is xid tagged */ + if (vx_check((xid_t)inode->i_tag, VX_IDENT)) + ret = generic_permission(inode, mask, NULL); + return ret; +} + +static struct inode_operations devpts_file_inode_operations = { + .permission = devpts_permission, +}; static struct vfsmount *devpts_mnt; static struct dentry *devpts_root; --- linux-2.6.16-rc1/fs/devpts/inode.c 2006-01-26 22:35:10 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/devpts/inode.c 2006-01-25 05:34:51 +0100 @@ -69,6 +82,25 @@ static int devpts_remount(struct super_b return 0; } +static int devpts_filter(struct dentry *de) +{ + /* devpts is xid tagged */ + return vx_check((xid_t)de->d_inode->i_tag, VX_IDENT); +} + +static int devpts_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + return dcache_readdir_filter(filp, dirent, filldir, devpts_filter); +} + +static struct file_operations devpts_dir_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .llseek = dcache_dir_lseek, + .read = generic_read_dir, + .readdir = devpts_readdir, +}; + static struct super_operations devpts_sops = { .statfs = simple_statfs, .remount_fs = devpts_remount, --- linux-2.6.16-rc1/fs/ext2/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext2/inode.c 2006-01-25 05:34:51 +0100 @@ -66,6 +67,8 @@ void ext2_put_inode(struct inode *inode) ext2_discard_prealloc(inode); } +static void ext2_truncate_nocheck (struct inode * inode); + /* * Called at the last iput() if i_nlink is zero. */ --- linux-2.6.16-rc1/fs/ext2/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext2/inode.c 2006-01-25 05:34:51 +0100 @@ -81,7 +84,7 @@ void ext2_delete_inode (struct inode * i inode->i_size = 0; if (inode->i_blocks) - ext2_truncate (inode); + ext2_truncate_nocheck(inode); ext2_free_inode (inode); return; --- linux-2.6.16-rc1/fs/ext2/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext2/inode.c 2006-01-25 05:34:51 +0100 @@ -906,7 +909,7 @@ static void ext2_free_branches(struct in ext2_free_data(inode, p, q); } -void ext2_truncate (struct inode * inode) +static void ext2_truncate_nocheck(struct inode * inode) { __le32 *i_data = EXT2_I(inode)->i_data; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); --- linux-2.6.16-rc1/fs/ext2/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext2/inode.c 2006-01-25 05:34:51 +0100 @@ -1048,6 +1049,13 @@ Egdp: return ERR_PTR(-EIO); } +void ext2_truncate (struct inode * inode) +{ + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + ext2_truncate_nocheck(inode); +} + void ext2_set_inode_flags(struct inode *inode) { unsigned int flags = EXT2_I(inode)->i_flags; --- linux-2.6.16-rc1/fs/ext2/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext2/inode.c 2006-01-25 05:34:51 +0100 @@ -1071,6 +1122,8 @@ ***** ino_t ino = inode->i_ino; struct buffer_head * bh; struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); + uid_t uid; + gid_t gid; int n; #ifdef CONFIG_EXT2_FS_POSIX_ACL --- linux-2.6.16-rc1/fs/ext3/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext3/inode.c 2006-01-25 05:34:51 +0100 @@ -180,6 +181,8 @@ static int ext3_journal_test_restart(han return ext3_journal_restart(handle, blocks_for_truncate(inode)); } +static void ext3_truncate_nocheck (struct inode *inode); + /* * Called at the last iput() if i_nlink is zero. */ --- linux-2.6.16-rc1/fs/ext3/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext3/inode.c 2006-01-25 05:34:51 +0100 @@ -205,7 +208,7 @@ void ext3_delete_inode (struct inode * i handle->h_sync = 1; inode->i_size = 0; if (inode->i_blocks) - ext3_truncate(inode); + ext3_truncate_nocheck(inode); /* * Kill off the orphan record which ext3_truncate created. * AKPM: I think this can be inside the above `if'. --- linux-2.6.16-rc1/fs/ext3/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext3/inode.c 2006-01-25 05:34:51 +0100 @@ -2076,7 +2079,7 @@ static void ext3_free_branches(handle_t * ext3_truncate() run will find them and release them. */ -void ext3_truncate(struct inode * inode) +void ext3_truncate_nocheck(struct inode * inode) { handle_t *handle; struct ext3_inode_info *ei = EXT3_I(inode); --- linux-2.6.16-rc1/fs/ext3/inode.c 2006-01-03 17:29:56 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/ext3/inode.c 2006-01-25 05:34:51 +0100 @@ -2417,6 +2418,13 @@ int ext3_get_inode_loc(struct inode *ino !(EXT3_I(inode)->i_state & EXT3_STATE_XATTR)); } +void ext3_truncate(struct inode * inode) +{ + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + ext3_truncate_nocheck(inode); +} + void ext3_set_inode_flags(struct inode *inode) { unsigned int flags = EXT3_I(inode)->i_flags; --- linux-2.6.16-rc1/fs/namei.c 2006-01-26 22:35:12 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/namei.c 2006-01-25 05:34:51 +0100 @@ -223,6 +227,24 @@ int generic_permission(struct inode *ino return -EACCES; } +static inline int dx_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + if (IS_BARRIER(inode) && !vx_check(0, VX_ADMIN)) { + vxwprintk(1, "xid=%d did hit the barrier.", + vx_current_xid()); + return -EACCES; + } + if (inode->i_tag == 0) + return 0; + if (dx_check(inode->i_tag, DX_ADMIN|DX_WATCH|DX_IDENT)) + return 0; + + vxwprintk(1, "xid=%d denied access to %p[#%d,%lu] »%s«.", + vx_current_xid(), inode, inode->i_tag, inode->i_ino, + vxd_path(nd->dentry, nd->mnt)); + return -EACCES; +} + int permission(struct inode *inode, int mask, struct nameidata *nd) { int retval, submask; --- linux-2.6.16-rc1/fs/reiserfs/inode.c 2006-01-26 22:35:13 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/reiserfs/inode.c 2006-01-25 05:34:51 +0100 @@ -2939,9 +2991,11 @@ int reiserfs_setattr(struct dentry *dent } error = inode_change_ok(inode, attr); + if (!error) { if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || - (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid) || + (ia_valid & ATTR_TAG && attr->ia_tag != inode->i_tag)) { error = reiserfs_chown_xattrs(inode, attr); if (!error) { --- linux-2.6.16-rc1/fs/xfs/linux-2.6/xfs_iops.c 2006-01-26 22:35:13 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/fs/xfs/linux-2.6/xfs_iops.c 2006-01-25 05:34:51 +0100 @@ -643,6 +680,10 @@ linvfs_setattr( int flags = 0; int error; + error = inode_change_ok(inode, attr); + if (error) + return error; + memset(&vattr, 0, sizeof(vattr_t)); if (ia_valid & ATTR_UID) { vattr.va_mask |= XFS_AT_UID; --- linux-2.6.16-rc1/include/linux/vserver/inode.h 1970-01-01 01:00:00 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/include/linux/vserver/inode.h 2006-01-27 20:38:52 +0100 @@ -0,0 +1,40 @@ +#ifndef _VX_INODE_H +#define _VX_INODE_H + + +#define IATTR_TAG 0x01000000 + +#define IATTR_ADMIN 0x00000001 +#define IATTR_WATCH 0x00000002 +#define IATTR_HIDE 0x00000004 +#define IATTR_FLAGS 0x00000007 + +#define IATTR_BARRIER 0x00010000 +#define IATTR_IUNLINK 0x00020000 +#define IATTR_IMMUTABLE 0x00040000 + +#ifdef __KERNEL__ + +#include + + +#ifdef CONFIG_VSERVER_PROC_SECURE +#define IATTR_PROC_DEFAULT ( IATTR_ADMIN | IATTR_HIDE ) +#define IATTR_PROC_SYMLINK ( IATTR_ADMIN ) +#else +#define IATTR_PROC_DEFAULT ( IATTR_ADMIN ) +#define IATTR_PROC_SYMLINK ( IATTR_ADMIN ) +#endif + +#define vx_hide_check(c,m) (((m) & IATTR_HIDE) ? vx_check(c,m) : 1) + +#endif /* __KERNEL__ */ + +/* inode ioctls */ + +#define FIOC_GETXFLG _IOR('x', 5, long) +#define FIOC_SETXFLG _IOW('x', 6, long) + +#else /* _VX_INODE_H */ +#warning duplicate inclusion +#endif /* _VX_INODE_H */ --- linux-2.6.16-rc1/kernel/vserver/inode.c 1970-01-01 01:00:00 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/kernel/vserver/inode.c 2006-01-27 22:03:05 +0100 @@ -0,0 +1,372 @@ +/* + * linux/kernel/vserver/inode.c + * + * Virtual Server: File System Support + * + * Copyright (C) 2004-2005 Herbert Pötzl + * + * V0.01 separated from vcontext V0.05 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +static int __vc_get_iattr(struct inode *in, uint32_t *tag, uint32_t *flags, uint32_t *mask) +{ + struct proc_dir_entry *entry; + + if (!in || !in->i_sb) + return -ESRCH; + + *flags = IATTR_TAG + | (IS_BARRIER(in) ? IATTR_BARRIER : 0) + | (IS_IUNLINK(in) ? IATTR_IUNLINK : 0) + | (IS_IMMUTABLE(in) ? IATTR_IMMUTABLE : 0); + *mask = IATTR_IUNLINK | IATTR_IMMUTABLE; + + if (S_ISDIR(in->i_mode)) + *mask |= IATTR_BARRIER; + + if (IS_TAGGED(in)) { + *tag = in->i_tag; + *mask |= IATTR_TAG; + } + + switch (in->i_sb->s_magic) { + case PROC_SUPER_MAGIC: + entry = PROC_I(in)->pde; + + /* check for specific inodes? */ + if (entry) + *mask |= IATTR_FLAGS; + if (entry) + *flags |= (entry->vx_flags & IATTR_FLAGS); + else + *flags |= (PROC_I(in)->vx_flags & IATTR_FLAGS); + break; + + case DEVPTS_SUPER_MAGIC: + *tag = in->i_tag; + *mask |= IATTR_TAG; + break; + + default: + break; + } + return 0; +} + +int vc_get_iattr(uint32_t id, void __user *data) +{ + struct nameidata nd; + struct vcmd_ctx_iattr_v1 vc_data = { .xid = -1 }; + int ret; + + if (!vx_check(0, VX_ADMIN)) + return -ENOSYS; + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + ret = user_path_walk_link(vc_data.name, &nd); + if (!ret) { + ret = __vc_get_iattr(nd.dentry->d_inode, + &vc_data.xid, &vc_data.flags, &vc_data.mask); + path_release(&nd); + } + if (ret) + return ret; + + if (copy_to_user (data, &vc_data, sizeof(vc_data))) + ret = -EFAULT; + return ret; +} + +#ifdef CONFIG_COMPAT + +int vc_get_iattr_x32(uint32_t id, void __user *data) +{ + struct nameidata nd; + struct vcmd_ctx_iattr_v1_x32 vc_data = { .xid = -1 }; + int ret; + + if (!vx_check(0, VX_ADMIN)) + return -ENOSYS; + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + ret = user_path_walk_link(compat_ptr(vc_data.name_ptr), &nd); + if (!ret) { + ret = __vc_get_iattr(nd.dentry->d_inode, + &vc_data.xid, &vc_data.flags, &vc_data.mask); + path_release(&nd); + } + if (ret) + return ret; + + if (copy_to_user (data, &vc_data, sizeof(vc_data))) + ret = -EFAULT; + return ret; +} + +#endif /* CONFIG_COMPAT */ + + +static int __vc_set_iattr(struct dentry *de, uint32_t *tag, uint32_t *flags, uint32_t *mask) +{ + struct inode *in = de->d_inode; + int error = 0, is_proc = 0, has_tag = 0; + struct iattr attr = { 0 }; + + if (!in || !in->i_sb) + return -ESRCH; + + is_proc = (in->i_sb->s_magic == PROC_SUPER_MAGIC); + if ((*mask & IATTR_FLAGS) && !is_proc) + return -EINVAL; + + has_tag = IS_TAGGED(in) || + (in->i_sb->s_magic == DEVPTS_SUPER_MAGIC); + if ((*mask & IATTR_TAG) && !has_tag) + return -EINVAL; + + mutex_lock(&in->i_mutex); + if (*mask & IATTR_TAG) { + attr.ia_tag = *tag; + attr.ia_valid |= ATTR_TAG; + } + + if (*mask & IATTR_FLAGS) { + struct proc_dir_entry *entry = PROC_I(in)->pde; + unsigned int iflags = PROC_I(in)->vx_flags; + + iflags = (iflags & ~(*mask & IATTR_FLAGS)) + | (*flags & IATTR_FLAGS); + PROC_I(in)->vx_flags = iflags; + if (entry) + entry->vx_flags = iflags; + } + + if (*mask & (IATTR_BARRIER | IATTR_IUNLINK | IATTR_IMMUTABLE)) { + if (*mask & IATTR_IMMUTABLE) { + if (*flags & IATTR_IMMUTABLE) + in->i_flags |= S_IMMUTABLE; + else + in->i_flags &= ~S_IMMUTABLE; + } + if (*mask & IATTR_IUNLINK) { + if (*flags & IATTR_IUNLINK) + in->i_flags |= S_IUNLINK; + else + in->i_flags &= ~S_IUNLINK; + } + if (S_ISDIR(in->i_mode) && (*mask & IATTR_BARRIER)) { + if (*flags & IATTR_BARRIER) + in->i_flags |= S_BARRIER; + else + in->i_flags &= ~S_BARRIER; + } + if (in->i_op && in->i_op->sync_flags) { + error = in->i_op->sync_flags(in); + if (error) + goto out; + } + } + + if (attr.ia_valid) { + if (in->i_op && in->i_op->setattr) + error = in->i_op->setattr(de, &attr); + else { + error = inode_change_ok(in, &attr); + if (!error) + error = inode_setattr(in, &attr); + } + } + +out: + mutex_unlock(&in->i_mutex); + return error; +} + +int vc_set_iattr(uint32_t id, void __user *data) +{ + struct nameidata nd; + struct vcmd_ctx_iattr_v1 vc_data; + int ret; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_LINUX_IMMUTABLE)) + return -EPERM; + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + ret = user_path_walk_link(vc_data.name, &nd); + if (!ret) { + ret = __vc_set_iattr(nd.dentry, + &vc_data.xid, &vc_data.flags, &vc_data.mask); + path_release(&nd); + } + + if (copy_to_user (data, &vc_data, sizeof(vc_data))) + ret = -EFAULT; + return ret; +} + +#ifdef CONFIG_COMPAT + +int vc_set_iattr_x32(uint32_t id, void __user *data) +{ + struct nameidata nd; + struct vcmd_ctx_iattr_v1_x32 vc_data; + int ret; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_LINUX_IMMUTABLE)) + return -EPERM; + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + ret = user_path_walk_link(compat_ptr(vc_data.name_ptr), &nd); + if (!ret) { + ret = __vc_set_iattr(nd.dentry, + &vc_data.xid, &vc_data.flags, &vc_data.mask); + path_release(&nd); + } + + if (copy_to_user (data, &vc_data, sizeof(vc_data))) + ret = -EFAULT; + return ret; +} + +#endif /* CONFIG_COMPAT */ + +#ifdef CONFIG_VSERVER_LEGACY + +#define PROC_DYNAMIC_FIRST 0xF0000000UL + +int vx_proc_ioctl(struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct proc_dir_entry *entry; + int error = 0; + int flags; + + if (inode->i_ino < PROC_DYNAMIC_FIRST) + return -ENOTTY; + + entry = PROC_I(inode)->pde; + if (!entry) + return -ENOTTY; + + switch(cmd) { + case FIOC_GETXFLG: { + /* fixme: if stealth, return -ENOTTY */ + error = -EPERM; + flags = entry->vx_flags; + if (capable(CAP_CONTEXT)) + error = put_user(flags, (int *) arg); + break; + } + case FIOC_SETXFLG: { + /* fixme: if stealth, return -ENOTTY */ + error = -EPERM; + if (!capable(CAP_CONTEXT)) + break; + error = -EROFS; + if (IS_RDONLY(inode)) + break; + error = -EFAULT; + if (get_user(flags, (int *) arg)) + break; + error = 0; + entry->vx_flags = flags; + break; + } + default: + return -ENOTTY; + } + return error; +} +#endif /* CONFIG_VSERVER_LEGACY */ + +#ifdef CONFIG_PROPAGATE + +int dx_parse_tag(char *string, tag_t *tag, int remove) +{ + static match_table_t tokens = { + {1, "tagid=%u"}, + {0, NULL} + }; + substring_t args[MAX_OPT_ARGS]; + int token, option = 0; + + if (!string) + return 0; + + token = match_token(string, tokens, args); + if (token && tag && !match_int(args, &option)) + *tag = option; + + vxdprintk(VXD_CBIT(tag, 7), + "dx_parse_tag(»%s«): %d:#%d", + string, token, option); + + if ((token == 1) && remove) { + char *p = strstr(string, "tagid="); + char *q = p; + + if (p) { + while (*q != '\0' && *q != ',') + q++; + while (*q) + *p++ = *q++; + while (*p) + *p++ = '\0'; + } + } + return token; +} + +void __dx_propagate_tag(struct nameidata *nd, struct inode *inode) +{ + tag_t new_tag = 0; + struct vfsmount *mnt; + int propagate; + + if (!nd) + return; + mnt = nd->mnt; + if (!mnt) + return; + + propagate = (mnt->mnt_flags & MNT_TAGID); + if (propagate) + new_tag = mnt->mnt_tag; + + vxdprintk(VXD_CBIT(tag, 7), + "dx_propagate_tag(%p[#%lu.%d]): %d,%d", + inode, inode->i_ino, inode->i_tag, + new_tag, (propagate)?1:0); + + if (propagate) + inode->i_tag = new_tag; +} + +#include + +EXPORT_SYMBOL_GPL(__dx_propagate_tag); + +#endif /* CONFIG_PROPAGATE */ + --- linux-2.6.16-rc1/mm/slab.c 2006-01-26 22:35:33 +0100 +++ linux-2.6.16-rc1-vs2.1.0.9.4/mm/slab.c 2006-01-21 18:28:15 +0100 @@ -2679,6 +2682,7 @@ static void free_block(kmem_cache_t *cac int i; struct kmem_list3 *l3; + // printk("·· free_block(%x) = %dx%x\n", cachep->gfpflags, nr_objects, cachep->objsize); for (i = 0; i < nr_objects; i++) { void *objp = objpp[i]; struct slab *slabp;