Message ID | alpine.LFD.2.20.1711212009330.6297@localhost (mailing list archive) |
---|---|
State | RFC |
Headers | show |
On Tue, 2017-11-21 at 20:49 +1100, James Morris wrote: > This is an updated version of the patch which I first posted here: > > http://kernsec.org/pipermail/linux-security-module-archive/2017-Octob > er/004053.html > > I've incorporated some of the feedback provided, as follows: > > 1. The init namespace is now an empty string, rather than a hidden > "init". > > 2. The ".ns." field for namespaced SELinux xattrs has been removed. > > 3. Namespace names are now hierarchical, and always include parent > namespace names preceding the current namespace. > > As before, this works transparently for normal applications. > > Some usage examples with this patch: > > - Create a namespace "VM1": > > # echo VM1 > /sys/fs/selinux/unshare > # unshare -m -n > # umount /sys/fs/selinux && mount -t selinuxfs none /sys/fs/selinux > && load_policy > # runcon unconfined_u:unconfined_r:unconfined_t:s0:c0.c1023 /bin/bash > # setenforce 1 > # cat /sys/fs/selinux/unshare > VM1 > > > - Note that the current directory appears unlabeled: > > # ls -dZ > drwxr-xr-x. root root system_u:object_r:unlabeled_t:s0 . > > > - If you create a file, it will also thus be unlabeled: > > # touch b > # ls -Z > -rw-r--r--. root root unconfined_u:object_r:unlabeled_t:s0 b > > > - With the current code, you need to manually label the current > directory > if you want labeling to work correctly: > > # restorecon -v . > restorecon reset /root/selinux/test context > system_u:object_r:unlabeled_t:s0->system_u:object_r:admin_home_t:s0 > > # touch c > # ls -lZ > -rw-r--r--. root root unconfined_u:object_r:unlabeled_t:s0 b > -rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 c > > > - The on-disk label looks like this: > > # getfattr -d -m . c > # file: c > security.selinux.VM1="unconfined_u:object_r:admin_home_t:s0" > > > - Now, create a nested namespace, "VM2": > > # echo VM2 > /sys/fs/selinux/unshare > # unshare -m -n > # umount /sys/fs/selinux && mount -t selinuxfs none /sys/fs/selinux > && load_policy > # runcon unconfined_u:unconfined_r:unconfined_t:s0:c0.c1023 /bin/bash > # setenforce 1 > # cat /sys/fs/selinux/unshare > VM1.VM2 > > - Label .: > > # restorecon -v . > restorecon reset /root/selinux/test context > system_u:object_r:unlabeled_t:s0->system_u:object_r:admin_home_t:s0 > > > - Create a new file (note files from parent namespace are unlabled in > this > one): > > # touch d > # ls -Z > -rw-r--r--. root root system_u:object_r:unlabeled_t:s0 b > -rw-r--r--. root root system_u:object_r:unlabeled_t:s0 c > -rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 d > > > - Relabel the file: > > # chcon -t etc_t d > # ls -Z d > -rw-r--r--. root root unconfined_u:object_r:etc_t:s0 d > > > - View the on-disk label: > > # getfattr -d -m . d > # file: d > security.selinux.VM1.VM2="unconfined_u:object_r:etc_t:s0" > > > There's a lot more work to do but I figured it would be useful for > folk to > see the hierarchical namespacing changes. Feedback welcome! Thanks, this looks good to me wrt the new naming scheme for namespaces and xattrs. > > Next TODO items: > > - Perform labeling of files in ancestor namespaces upon creation in > a > child namespace. > - Label inheritence & label sharing (both ro/rw). > > > --- > > > From 91bcb0b9fe058d9b7b0de1d5a1c22e5e7303989a Mon Sep 17 00:00:00 > > 2001 > > From: James Morris <james.l.morris@oracle.com> > Date: Tue, 21 Nov 2017 19:45:22 +1100 > Subject: [PATCH] selinuxns: extend namespace support to > security.selinux > xattrs > > RFC v0.2 > > Signed-off-by: James Morris <james.l.morris@oracle.com> > > --- > > fs/xattr.c | 12 +++-- > include/linux/lsm_hooks.h | 2 + > include/linux/security.h | 6 +++ > include/linux/xattr.h | 2 +- > security/integrity/evm/evm_crypto.c | 2 +- > security/integrity/ima/ima_appraise.c | 2 +- > security/security.c | 6 +++ > security/selinux/hooks.c | 95 > +++++++++++++++++++++++++++++++---- > security/selinux/include/security.h | 7 ++- > security/selinux/selinuxfs.c | 63 ++++++++++++++--------- > security/smack/smack_lsm.c | 2 +- > 11 files changed, 159 insertions(+), 40 deletions(-) > > diff --git a/fs/xattr.c b/fs/xattr.c > index 4424f7f..d8107b7 100644 > --- a/fs/xattr.c > +++ b/fs/xattr.c > @@ -157,6 +157,7 @@ > * > * @dentry - object to perform setxattr on > * @name - xattr name to set > + * @nsname - namespaced xattr name, use instead of @name if set > * @value - value to set @name to > * @size - size of @value > * @flags - flags to pass into filesystem operations > @@ -168,7 +169,7 @@ > * permission checks. > */ > int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, > - const void *value, size_t size, int flags) > + const char *nsname, const void *value, size_t size, > int flags) > { > struct inode *inode = dentry->d_inode; > int error = -EAGAIN; > @@ -178,7 +179,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, > const char *name, > if (issec) > inode->i_flags &= ~S_NOSEC; > if (inode->i_opflags & IOP_XATTR) { > - error = __vfs_setxattr(dentry, inode, name, value, > size, flags); > + error = __vfs_setxattr(dentry, inode, nsname?:name, > value, > + size, flags); > if (!error) { > fsnotify_xattr(dentry); > security_inode_post_setxattr(dentry, name, > value, > @@ -211,6 +213,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, > const char *name, > { > struct inode *inode = dentry->d_inode; > int error; > + char *nsname = NULL; > > error = xattr_permission(inode, name, MAY_WRITE); > if (error) > @@ -221,8 +224,11 @@ int __vfs_setxattr_noperm(struct dentry *dentry, > const char *name, > if (error) > goto out; > > - error = __vfs_setxattr_noperm(dentry, name, value, size, > flags); > + error = security_inode_translate_xattr_to_ns(name, &nsname); > + if (error) > + goto out; > > + error = __vfs_setxattr_noperm(dentry, name, nsname, value, > size, flags); > out: > inode_unlock(inode); > return error; > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h > index c925812..e4eb43e 100644 > --- a/include/linux/lsm_hooks.h > +++ b/include/linux/lsm_hooks.h > @@ -1480,6 +1480,7 @@ > void (*inode_getsecid)(struct inode *inode, u32 *secid); > int (*inode_copy_up)(struct dentry *src, struct cred **new); > int (*inode_copy_up_xattr)(const char *name); > + int (*inode_translate_xattr_to_ns)(const char *name, char > **tr); > > int (*file_permission)(struct file *file, int mask); > int (*file_alloc_security)(struct file *file); > @@ -1760,6 +1761,7 @@ struct security_hook_heads { > struct list_head inode_getsecid; > struct list_head inode_copy_up; > struct list_head inode_copy_up_xattr; > + struct list_head inode_translate_xattr_to_ns; > struct list_head file_permission; > struct list_head file_alloc_security; > struct list_head file_free_security; > diff --git a/include/linux/security.h b/include/linux/security.h > index ce62659..64297e1 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -302,6 +302,7 @@ void security_inode_post_setxattr(struct dentry > *dentry, const char *name, > void security_inode_getsecid(struct inode *inode, u32 *secid); > int security_inode_copy_up(struct dentry *src, struct cred **new); > int security_inode_copy_up_xattr(const char *name); > +int security_inode_translate_xattr_to_ns(const char *name, char > **tr); > int security_file_permission(struct file *file, int mask); > int security_file_alloc(struct file *file); > void security_file_free(struct file *file); > @@ -808,6 +809,11 @@ static inline int > security_inode_copy_up_xattr(const char *name) > return -EOPNOTSUPP; > } > > +static inline int security_inode_translate_xattr_to_ns(const char > *name, char **tr) > +{ > + return 0; > +} > + > static inline int security_file_permission(struct file *file, int > mask) > { > return 0; > diff --git a/include/linux/xattr.h b/include/linux/xattr.h > index e77605a..c25eb8a 100644 > --- a/include/linux/xattr.h > +++ b/include/linux/xattr.h > @@ -50,7 +50,7 @@ struct xattr { > ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t); > ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); > int __vfs_setxattr(struct dentry *, struct inode *, const char *, > const void *, size_t, int); > -int __vfs_setxattr_noperm(struct dentry *, const char *, const void > *, size_t, int); > +int __vfs_setxattr_noperm(struct dentry *, const char *, const char > *, const void *, size_t, int); > int vfs_setxattr(struct dentry *, const char *, const void *, > size_t, int); > int __vfs_removexattr(struct dentry *, const char *); > int vfs_removexattr(struct dentry *, const char *); > diff --git a/security/integrity/evm/evm_crypto.c > b/security/integrity/evm/evm_crypto.c > index 1d32cd2..2249186 100644 > --- a/security/integrity/evm/evm_crypto.c > +++ b/security/integrity/evm/evm_crypto.c > @@ -260,7 +260,7 @@ int evm_update_evmxattr(struct dentry *dentry, > const char *xattr_name, > if (rc == 0) { > xattr_data.type = EVM_XATTR_HMAC; > rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, > - &xattr_data, > + NULL, &xattr_data, > sizeof(xattr_data), 0); > } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) > { > rc = __vfs_removexattr(dentry, XATTR_NAME_EVM); > diff --git a/security/integrity/ima/ima_appraise.c > b/security/integrity/ima/ima_appraise.c > index 809ba70..914cf5f 100644 > --- a/security/integrity/ima/ima_appraise.c > +++ b/security/integrity/ima/ima_appraise.c > @@ -71,7 +71,7 @@ static int ima_fix_xattr(struct dentry *dentry, > iint->ima_hash->xattr.ng.algo = algo; > } > rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, > - &iint->ima_hash- > >xattr.data[offset], > + NULL, &iint->ima_hash- > >xattr.data[offset], > (sizeof(iint->ima_hash->xattr) - > offset) + > iint->ima_hash->length, 0); > return rc; > diff --git a/security/security.c b/security/security.c > index 4bf0f57..7fce259 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -856,6 +856,12 @@ int security_inode_copy_up_xattr(const char > *name) > } > EXPORT_SYMBOL(security_inode_copy_up_xattr); > > +int security_inode_translate_xattr_to_ns(const char *name, char > **tr) > +{ > + return call_int_hook(inode_translate_xattr_to_ns, 0, name, > tr); > +} > +EXPORT_SYMBOL(security_inode_translate_xattr_to_ns); > + > int security_file_permission(struct file *file, int mask) > { > int ret; > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 3daad14..e439ce2 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -646,6 +646,16 @@ static int selinux_is_sblabel_mnt(struct > super_block *sb) > !strcmp(sb->s_type->name, "cgroup2"))); > } > > +static char *current_xattr_suffix(void) > +{ > + return current_selinux_ns->xattr_name + > XATTR_SECURITY_PREFIX_LEN; > +} > + > +static char *current_xattr_name(void) > +{ > + return current_selinux_ns->xattr_name; > +} > + > static int sb_finish_set_opts(struct super_block *sb) > { > struct superblock_security_struct *sbsec = > superblock_security(sb); > @@ -654,6 +664,8 @@ static int sb_finish_set_opts(struct super_block > *sb) > int rc = 0; > > if (sbsec->behavior == SECURITY_FS_USE_XATTR) { > + char *name; > + > /* Make sure that the xattr handler exists and that > no > error other than -ENODATA is returned by getxattr > on > the root directory. -ENODATA is ok, as this may > be > @@ -666,7 +678,9 @@ static int sb_finish_set_opts(struct super_block > *sb) > goto out; > } > > - rc = __vfs_getxattr(root, root_inode, > XATTR_NAME_SELINUX, NULL, 0); > + name = current_xattr_name(); > + > + rc = __vfs_getxattr(root, root_inode, name, NULL, > 0); > if (rc < 0 && rc != -ENODATA) { > if (rc == -EOPNOTSUPP) > printk(KERN_WARNING "SELinux: (dev > %s, type " > @@ -1660,6 +1674,7 @@ static int inode_doinit_with_dentry(struct > inode *inode, > char *context = NULL; > unsigned len = 0; > int rc = 0; > + char *name; > > if (isec->initialized == LABEL_INITIALIZED) > return 0; > @@ -1729,12 +1744,14 @@ static int inode_doinit_with_dentry(struct > inode *inode, > goto out; > } > context[len] = '\0'; > - rc = __vfs_getxattr(dentry, inode, > XATTR_NAME_SELINUX, context, len); > + > + name = current_xattr_name(); > + rc = __vfs_getxattr(dentry, inode, name, context, > len); > if (rc == -ERANGE) { > kfree(context); > > /* Need a larger buffer. Query for the > right size. */ > - rc = __vfs_getxattr(dentry, inode, > XATTR_NAME_SELINUX, NULL, 0); > + rc = __vfs_getxattr(dentry, inode, name, > NULL, 0); > if (rc < 0) { > dput(dentry); > goto out; > @@ -1747,7 +1764,7 @@ static int inode_doinit_with_dentry(struct > inode *inode, > goto out; > } > context[len] = '\0'; > - rc = __vfs_getxattr(dentry, inode, > XATTR_NAME_SELINUX, context, len); > + rc = __vfs_getxattr(dentry, inode, name, > context, len); > } > dput(dentry); > if (rc < 0) { > @@ -3167,7 +3184,7 @@ static int selinux_inode_init_security(struct > inode *inode, struct inode *dir, > return -EOPNOTSUPP; > > if (name) > - *name = XATTR_SELINUX_SUFFIX; > + *name = current_xattr_suffix(); > > if (value && len) { > rc = > security_sid_to_context_force(current_selinux_ns, newsid, > @@ -3382,6 +3399,10 @@ static bool has_cap_mac_admin(bool audit) > return true; > } > > +/* TODO: > + * - audit > + * - handle raw namespaced xattrs > + */ > static int selinux_inode_setxattr(struct dentry *dentry, const char > *name, > const void *value, size_t size, > int flags) > { > @@ -3392,8 +3413,12 @@ static int selinux_inode_setxattr(struct > dentry *dentry, const char *name, > u32 newsid, sid = current_sid(); > int rc = 0; > > - if (strcmp(name, XATTR_NAME_SELINUX)) > + if (strcmp(name, XATTR_NAME_SELINUX)) { > + /* No raw namespaced xattrs, yet */ > + if (!strncmp(name, XATTR_NAME_SELINUX, > strlen(XATTR_NAME_SELINUX))) > + return -EACCES; > return selinux_inode_setotherxattr(dentry, name); > + } > > sbsec = superblock_security(inode->i_sb); > if (!(sbsec->flags & SBLABEL_MNT)) > @@ -3640,6 +3665,13 @@ static int selinux_inode_copy_up_xattr(const > char *name) > return -EOPNOTSUPP; > } > > +static int selinux_inode_translate_xattr_to_ns(const char *name, > char **tr) > +{ > + if(!strcmp(name, XATTR_NAME_SELINUX)) > + *tr = current_xattr_name(); > + return 0; > +} > + > /* file security operations */ > > static int selinux_revalidate_file_permission(struct file *file, int > mask) > @@ -6430,10 +6462,11 @@ static int selinux_inode_notifysecctx(struct > inode *inode, void *ctx, u32 ctxlen > > /* > * called with inode->i_mutex locked > + * TODO: namespace translation > */ > static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, > u32 ctxlen) > { > - return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, > ctx, ctxlen, 0); > + return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, > NULL, ctx, ctxlen, 0); > } > > static int selinux_inode_getsecctx(struct inode *inode, void **ctx, > u32 *ctxlen) > @@ -6647,6 +6680,7 @@ static void selinux_ib_free_security(void > *ib_sec) > LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), > LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), > LSM_HOOK_INIT(inode_copy_up_xattr, > selinux_inode_copy_up_xattr), > + LSM_HOOK_INIT(inode_translate_xattr_to_ns, > selinux_inode_translate_xattr_to_ns), > > LSM_HOOK_INIT(file_permission, selinux_file_permission), > LSM_HOOK_INIT(file_alloc_security, > selinux_file_alloc_security), > @@ -6805,7 +6839,37 @@ static void selinux_ib_free_security(void > *ib_sec) > > static void selinux_ns_free(struct work_struct *work); > > -int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns > **ns) > +static int selinux_ns_create_name(struct selinux_ns *parent, struct > selinux_ns *newns, const char *name) > +{ > + int rc = 0; > + > + if (parent && parent->parent) > + newns->name = kasprintf(GFP_KERNEL, "%s.%s", parent- > >name, name); > + else > + newns->name = kstrdup(name, GFP_KERNEL); > + > + if (!newns->name) > + rc = -ENOMEM; > + > + return rc; > +} > + > +static int selinux_ns_create_xattr_name(struct selinux_ns *parent, > struct selinux_ns *newns) > +{ > + int rc = 0; > + > + if (!parent) > + newns->xattr_name = kstrdup(XATTR_NAME_SELINUX, > GFP_KERNEL); > + else > + newns->xattr_name = kasprintf(GFP_KERNEL, "%s.%s", > XATTR_NAME_SELINUX, newns->name); > + > + if (!newns->xattr_name) > + rc = -ENOMEM; > + > + return rc; > +} > + > +int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns > **ns, const char *name) > { > struct selinux_ns *newns; > int rc; > @@ -6825,14 +6889,25 @@ int selinux_ns_create(struct selinux_ns > *parent, struct selinux_ns **ns) > if (rc) > goto err; > > + rc = selinux_ns_create_name(parent, newns, name); > + if (rc) > + goto err_avc; > + > + rc = selinux_ns_create_xattr_name(parent, newns); > + if (rc) > + goto err_avc; > + > if (parent) > newns->parent = get_selinux_ns(parent); > > *ns = newns; > return 0; > +err_avc: > + selinux_avc_free(newns->avc); > err: > selinux_ss_free(newns->ss); > kfree(newns); > + kfree(newns->name); > return rc; > } > > @@ -6845,6 +6920,8 @@ static void selinux_ns_free(struct work_struct > *work) > parent = ns->parent; > selinux_ss_free(ns->ss); > selinux_avc_free(ns->avc); > + kfree(ns->name); > + kfree(ns->xattr_name); > kfree(ns); > ns = parent; > } while (ns && refcount_dec_and_test(&ns->count)); > @@ -6869,7 +6946,7 @@ static __init int selinux_init(void) > > printk(KERN_INFO "SELinux: Initializing.\n"); > > - if (selinux_ns_create(NULL, &init_selinux_ns)) > + if (selinux_ns_create(NULL, &init_selinux_ns, > SELINUX_NS_INIT_NAME)) > panic("SELinux: Could not create initial > namespace\n"); > > set_ns_enforcing(init_selinux_ns, selinux_enforcing_boot); > diff --git a/security/selinux/include/security.h > b/security/selinux/include/security.h > index b80f9bd..300cc8a 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -92,6 +92,9 @@ enum { > /* limitation of boundary depth */ > #define POLICYDB_BOUNDS_MAXDEPTH 4 > > +/* Name of SELinux initial namespace */ > +#define SELINUX_NS_INIT_NAME "" > + > struct selinux_avc; > struct selinux_ss; > > @@ -108,9 +111,11 @@ struct selinux_ns { > struct selinux_avc *avc; > struct selinux_ss *ss; > struct selinux_ns *parent; > + char *name; > + char *xattr_name; > }; > > -int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns > **ns); > +int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns > **ns, const char *name); > void __put_selinux_ns(struct selinux_ns *ns); > > int selinux_ss_create(struct selinux_ss **ss); > diff --git a/security/selinux/selinuxfs.c > b/security/selinux/selinuxfs.c > index 6c52d24..d190213 100644 > --- a/security/selinux/selinuxfs.c > +++ b/security/selinux/selinuxfs.c > @@ -334,9 +334,10 @@ static ssize_t sel_write_unshare(struct file > *file, const char __user *buf, > { > struct selinux_fs_info *fsi = file_inode(file)->i_sb- > >s_fs_info; > struct selinux_ns *ns = fsi->ns; > + struct cred *cred; > + struct task_security_struct *tsec; > char *page; > ssize_t length; > - bool set; > int rc; > > if (ns != current_selinux_ns) > @@ -359,30 +360,32 @@ static ssize_t sel_write_unshare(struct file > *file, const char __user *buf, > if (IS_ERR(page)) > return PTR_ERR(page); > > - length = -EINVAL; > - if (kstrtobool(page, &set)) > - goto out; > + /* strip any trailing newline */ > + if (page[strlen(page) - 1] == '\n') > + page[strlen(page) - 1] = 0; > > - if (set) { > - struct cred *cred = prepare_creds(); > - struct task_security_struct *tsec; > + /* TODO: check for uniqueness! */ > + if (!strcmp(SELINUX_NS_INIT_NAME, page)) { > + length = -EINVAL; > + goto out; > + } > > - if (!cred) { > - length = -ENOMEM; > - goto out; > - } > - tsec = cred->security; > - if (selinux_ns_create(ns, &tsec->ns)) { > - abort_creds(cred); > - length = -ENOMEM; > - goto out; > - } > - tsec->osid = tsec->sid = SECINITSID_KERNEL; > - tsec->exec_sid = tsec->create_sid = tsec- > >keycreate_sid = > - tsec->sockcreate_sid = SECSID_NULL; > - tsec->parent_cred = get_current_cred(); > - commit_creds(cred); > + cred = prepare_creds(); > + if (!cred) { > + length = -ENOMEM; > + goto out; > + } > + tsec = cred->security; > + if (selinux_ns_create(ns, &tsec->ns, page)) { > + abort_creds(cred); > + length = -ENOMEM; > + goto out; > } > + tsec->osid = tsec->sid = SECINITSID_KERNEL; > + tsec->exec_sid = tsec->create_sid = tsec->keycreate_sid = > + tsec->sockcreate_sid = SECSID_NULL; > + tsec->parent_cred = get_current_cred(); > + commit_creds(cred); > > length = count; > out: > @@ -390,8 +393,22 @@ static ssize_t sel_write_unshare(struct file > *file, const char __user *buf, > return length; > } > > +static ssize_t sel_read_unshare(struct file *file, char __user *buf, > + size_t count, loff_t *ppos) > +{ > + struct selinux_fs_info *fsi = file_inode(file)->i_sb- > >s_fs_info; > + struct selinux_ns *ns = fsi->ns; > + char *name = ns->name; > + > + if (ns != current_selinux_ns) > + return -EPERM; > + > + return simple_read_from_buffer(buf, count, ppos, name, > strlen(name)); > +} > + > static const struct file_operations sel_unshare_ops = { > .write = sel_write_unshare, > + .read = sel_read_unshare, > .llseek = generic_file_llseek, > }; > > @@ -2021,7 +2038,7 @@ static int sel_fill_super(struct super_block > *sb, void *data, int silent) > [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, > [SEL_VALIDATE_TRANS] = {"validatetrans", > &sel_transition_ops, > S_IWUGO}, > - [SEL_UNSHARE] = {"unshare", &sel_unshare_ops, 0222}, > + [SEL_UNSHARE] = {"unshare", &sel_unshare_ops, 0666}, > /* last one */ {""} > }; > > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 319add3..5ea841f 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -4591,7 +4591,7 @@ static int smack_inode_notifysecctx(struct > inode *inode, void *ctx, u32 ctxlen) > > static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, > u32 ctxlen) > { > - return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, ctx, > ctxlen, 0); > + return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, NULL, > ctx, ctxlen, 0); > } > > static int smack_inode_getsecctx(struct inode *inode, void **ctx, > u32 *ctxlen)
diff --git a/fs/xattr.c b/fs/xattr.c index 4424f7f..d8107b7 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -157,6 +157,7 @@ * * @dentry - object to perform setxattr on * @name - xattr name to set + * @nsname - namespaced xattr name, use instead of @name if set * @value - value to set @name to * @size - size of @value * @flags - flags to pass into filesystem operations @@ -168,7 +169,7 @@ * permission checks. */ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) + const char *nsname, const void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; int error = -EAGAIN; @@ -178,7 +179,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, if (issec) inode->i_flags &= ~S_NOSEC; if (inode->i_opflags & IOP_XATTR) { - error = __vfs_setxattr(dentry, inode, name, value, size, flags); + error = __vfs_setxattr(dentry, inode, nsname?:name, value, + size, flags); if (!error) { fsnotify_xattr(dentry); security_inode_post_setxattr(dentry, name, value, @@ -211,6 +213,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, { struct inode *inode = dentry->d_inode; int error; + char *nsname = NULL; error = xattr_permission(inode, name, MAY_WRITE); if (error) @@ -221,8 +224,11 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, if (error) goto out; - error = __vfs_setxattr_noperm(dentry, name, value, size, flags); + error = security_inode_translate_xattr_to_ns(name, &nsname); + if (error) + goto out; + error = __vfs_setxattr_noperm(dentry, name, nsname, value, size, flags); out: inode_unlock(inode); return error; diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index c925812..e4eb43e 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1480,6 +1480,7 @@ void (*inode_getsecid)(struct inode *inode, u32 *secid); int (*inode_copy_up)(struct dentry *src, struct cred **new); int (*inode_copy_up_xattr)(const char *name); + int (*inode_translate_xattr_to_ns)(const char *name, char **tr); int (*file_permission)(struct file *file, int mask); int (*file_alloc_security)(struct file *file); @@ -1760,6 +1761,7 @@ struct security_hook_heads { struct list_head inode_getsecid; struct list_head inode_copy_up; struct list_head inode_copy_up_xattr; + struct list_head inode_translate_xattr_to_ns; struct list_head file_permission; struct list_head file_alloc_security; struct list_head file_free_security; diff --git a/include/linux/security.h b/include/linux/security.h index ce62659..64297e1 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -302,6 +302,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name, void security_inode_getsecid(struct inode *inode, u32 *secid); int security_inode_copy_up(struct dentry *src, struct cred **new); int security_inode_copy_up_xattr(const char *name); +int security_inode_translate_xattr_to_ns(const char *name, char **tr); int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); void security_file_free(struct file *file); @@ -808,6 +809,11 @@ static inline int security_inode_copy_up_xattr(const char *name) return -EOPNOTSUPP; } +static inline int security_inode_translate_xattr_to_ns(const char *name, char **tr) +{ + return 0; +} + static inline int security_file_permission(struct file *file, int mask) { return 0; diff --git a/include/linux/xattr.h b/include/linux/xattr.h index e77605a..c25eb8a 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -50,7 +50,7 @@ struct xattr { ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t); ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); int __vfs_setxattr(struct dentry *, struct inode *, const char *, const void *, size_t, int); -int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int); +int __vfs_setxattr_noperm(struct dentry *, const char *, const char *, const void *, size_t, int); int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int); int __vfs_removexattr(struct dentry *, const char *); int vfs_removexattr(struct dentry *, const char *); diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 1d32cd2..2249186 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -260,7 +260,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, if (rc == 0) { xattr_data.type = EVM_XATTR_HMAC; rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, - &xattr_data, + NULL, &xattr_data, sizeof(xattr_data), 0); } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { rc = __vfs_removexattr(dentry, XATTR_NAME_EVM); diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 809ba70..914cf5f 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -71,7 +71,7 @@ static int ima_fix_xattr(struct dentry *dentry, iint->ima_hash->xattr.ng.algo = algo; } rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, - &iint->ima_hash->xattr.data[offset], + NULL, &iint->ima_hash->xattr.data[offset], (sizeof(iint->ima_hash->xattr) - offset) + iint->ima_hash->length, 0); return rc; diff --git a/security/security.c b/security/security.c index 4bf0f57..7fce259 100644 --- a/security/security.c +++ b/security/security.c @@ -856,6 +856,12 @@ int security_inode_copy_up_xattr(const char *name) } EXPORT_SYMBOL(security_inode_copy_up_xattr); +int security_inode_translate_xattr_to_ns(const char *name, char **tr) +{ + return call_int_hook(inode_translate_xattr_to_ns, 0, name, tr); +} +EXPORT_SYMBOL(security_inode_translate_xattr_to_ns); + int security_file_permission(struct file *file, int mask) { int ret; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3daad14..e439ce2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -646,6 +646,16 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) !strcmp(sb->s_type->name, "cgroup2"))); } +static char *current_xattr_suffix(void) +{ + return current_selinux_ns->xattr_name + XATTR_SECURITY_PREFIX_LEN; +} + +static char *current_xattr_name(void) +{ + return current_selinux_ns->xattr_name; +} + static int sb_finish_set_opts(struct super_block *sb) { struct superblock_security_struct *sbsec = superblock_security(sb); @@ -654,6 +664,8 @@ static int sb_finish_set_opts(struct super_block *sb) int rc = 0; if (sbsec->behavior == SECURITY_FS_USE_XATTR) { + char *name; + /* Make sure that the xattr handler exists and that no error other than -ENODATA is returned by getxattr on the root directory. -ENODATA is ok, as this may be @@ -666,7 +678,9 @@ static int sb_finish_set_opts(struct super_block *sb) goto out; } - rc = __vfs_getxattr(root, root_inode, XATTR_NAME_SELINUX, NULL, 0); + name = current_xattr_name(); + + rc = __vfs_getxattr(root, root_inode, name, NULL, 0); if (rc < 0 && rc != -ENODATA) { if (rc == -EOPNOTSUPP) printk(KERN_WARNING "SELinux: (dev %s, type " @@ -1660,6 +1674,7 @@ static int inode_doinit_with_dentry(struct inode *inode, char *context = NULL; unsigned len = 0; int rc = 0; + char *name; if (isec->initialized == LABEL_INITIALIZED) return 0; @@ -1729,12 +1744,14 @@ static int inode_doinit_with_dentry(struct inode *inode, goto out; } context[len] = '\0'; - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); + + name = current_xattr_name(); + rc = __vfs_getxattr(dentry, inode, name, context, len); if (rc == -ERANGE) { kfree(context); /* Need a larger buffer. Query for the right size. */ - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); + rc = __vfs_getxattr(dentry, inode, name, NULL, 0); if (rc < 0) { dput(dentry); goto out; @@ -1747,7 +1764,7 @@ static int inode_doinit_with_dentry(struct inode *inode, goto out; } context[len] = '\0'; - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); + rc = __vfs_getxattr(dentry, inode, name, context, len); } dput(dentry); if (rc < 0) { @@ -3167,7 +3184,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, return -EOPNOTSUPP; if (name) - *name = XATTR_SELINUX_SUFFIX; + *name = current_xattr_suffix(); if (value && len) { rc = security_sid_to_context_force(current_selinux_ns, newsid, @@ -3382,6 +3399,10 @@ static bool has_cap_mac_admin(bool audit) return true; } +/* TODO: + * - audit + * - handle raw namespaced xattrs + */ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { @@ -3392,8 +3413,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, u32 newsid, sid = current_sid(); int rc = 0; - if (strcmp(name, XATTR_NAME_SELINUX)) + if (strcmp(name, XATTR_NAME_SELINUX)) { + /* No raw namespaced xattrs, yet */ + if (!strncmp(name, XATTR_NAME_SELINUX, strlen(XATTR_NAME_SELINUX))) + return -EACCES; return selinux_inode_setotherxattr(dentry, name); + } sbsec = superblock_security(inode->i_sb); if (!(sbsec->flags & SBLABEL_MNT)) @@ -3640,6 +3665,13 @@ static int selinux_inode_copy_up_xattr(const char *name) return -EOPNOTSUPP; } +static int selinux_inode_translate_xattr_to_ns(const char *name, char **tr) +{ + if(!strcmp(name, XATTR_NAME_SELINUX)) + *tr = current_xattr_name(); + return 0; +} + /* file security operations */ static int selinux_revalidate_file_permission(struct file *file, int mask) @@ -6430,10 +6462,11 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen /* * called with inode->i_mutex locked + * TODO: namespace translation */ static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) { - return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0); + return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, NULL, ctx, ctxlen, 0); } static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) @@ -6647,6 +6680,7 @@ static void selinux_ib_free_security(void *ib_sec) LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr), + LSM_HOOK_INIT(inode_translate_xattr_to_ns, selinux_inode_translate_xattr_to_ns), LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), @@ -6805,7 +6839,37 @@ static void selinux_ib_free_security(void *ib_sec) static void selinux_ns_free(struct work_struct *work); -int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns) +static int selinux_ns_create_name(struct selinux_ns *parent, struct selinux_ns *newns, const char *name) +{ + int rc = 0; + + if (parent && parent->parent) + newns->name = kasprintf(GFP_KERNEL, "%s.%s", parent->name, name); + else + newns->name = kstrdup(name, GFP_KERNEL); + + if (!newns->name) + rc = -ENOMEM; + + return rc; +} + +static int selinux_ns_create_xattr_name(struct selinux_ns *parent, struct selinux_ns *newns) +{ + int rc = 0; + + if (!parent) + newns->xattr_name = kstrdup(XATTR_NAME_SELINUX, GFP_KERNEL); + else + newns->xattr_name = kasprintf(GFP_KERNEL, "%s.%s", XATTR_NAME_SELINUX, newns->name); + + if (!newns->xattr_name) + rc = -ENOMEM; + + return rc; +} + +int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns, const char *name) { struct selinux_ns *newns; int rc; @@ -6825,14 +6889,25 @@ int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns) if (rc) goto err; + rc = selinux_ns_create_name(parent, newns, name); + if (rc) + goto err_avc; + + rc = selinux_ns_create_xattr_name(parent, newns); + if (rc) + goto err_avc; + if (parent) newns->parent = get_selinux_ns(parent); *ns = newns; return 0; +err_avc: + selinux_avc_free(newns->avc); err: selinux_ss_free(newns->ss); kfree(newns); + kfree(newns->name); return rc; } @@ -6845,6 +6920,8 @@ static void selinux_ns_free(struct work_struct *work) parent = ns->parent; selinux_ss_free(ns->ss); selinux_avc_free(ns->avc); + kfree(ns->name); + kfree(ns->xattr_name); kfree(ns); ns = parent; } while (ns && refcount_dec_and_test(&ns->count)); @@ -6869,7 +6946,7 @@ static __init int selinux_init(void) printk(KERN_INFO "SELinux: Initializing.\n"); - if (selinux_ns_create(NULL, &init_selinux_ns)) + if (selinux_ns_create(NULL, &init_selinux_ns, SELINUX_NS_INIT_NAME)) panic("SELinux: Could not create initial namespace\n"); set_ns_enforcing(init_selinux_ns, selinux_enforcing_boot); diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index b80f9bd..300cc8a 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -92,6 +92,9 @@ enum { /* limitation of boundary depth */ #define POLICYDB_BOUNDS_MAXDEPTH 4 +/* Name of SELinux initial namespace */ +#define SELINUX_NS_INIT_NAME "" + struct selinux_avc; struct selinux_ss; @@ -108,9 +111,11 @@ struct selinux_ns { struct selinux_avc *avc; struct selinux_ss *ss; struct selinux_ns *parent; + char *name; + char *xattr_name; }; -int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns); +int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns, const char *name); void __put_selinux_ns(struct selinux_ns *ns); int selinux_ss_create(struct selinux_ss **ss); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 6c52d24..d190213 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -334,9 +334,10 @@ static ssize_t sel_write_unshare(struct file *file, const char __user *buf, { struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; struct selinux_ns *ns = fsi->ns; + struct cred *cred; + struct task_security_struct *tsec; char *page; ssize_t length; - bool set; int rc; if (ns != current_selinux_ns) @@ -359,30 +360,32 @@ static ssize_t sel_write_unshare(struct file *file, const char __user *buf, if (IS_ERR(page)) return PTR_ERR(page); - length = -EINVAL; - if (kstrtobool(page, &set)) - goto out; + /* strip any trailing newline */ + if (page[strlen(page) - 1] == '\n') + page[strlen(page) - 1] = 0; - if (set) { - struct cred *cred = prepare_creds(); - struct task_security_struct *tsec; + /* TODO: check for uniqueness! */ + if (!strcmp(SELINUX_NS_INIT_NAME, page)) { + length = -EINVAL; + goto out; + } - if (!cred) { - length = -ENOMEM; - goto out; - } - tsec = cred->security; - if (selinux_ns_create(ns, &tsec->ns)) { - abort_creds(cred); - length = -ENOMEM; - goto out; - } - tsec->osid = tsec->sid = SECINITSID_KERNEL; - tsec->exec_sid = tsec->create_sid = tsec->keycreate_sid = - tsec->sockcreate_sid = SECSID_NULL; - tsec->parent_cred = get_current_cred(); - commit_creds(cred); + cred = prepare_creds(); + if (!cred) { + length = -ENOMEM; + goto out; + } + tsec = cred->security; + if (selinux_ns_create(ns, &tsec->ns, page)) { + abort_creds(cred); + length = -ENOMEM; + goto out; } + tsec->osid = tsec->sid = SECINITSID_KERNEL; + tsec->exec_sid = tsec->create_sid = tsec->keycreate_sid = + tsec->sockcreate_sid = SECSID_NULL; + tsec->parent_cred = get_current_cred(); + commit_creds(cred); length = count; out: @@ -390,8 +393,22 @@ static ssize_t sel_write_unshare(struct file *file, const char __user *buf, return length; } +static ssize_t sel_read_unshare(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_ns *ns = fsi->ns; + char *name = ns->name; + + if (ns != current_selinux_ns) + return -EPERM; + + return simple_read_from_buffer(buf, count, ppos, name, strlen(name)); +} + static const struct file_operations sel_unshare_ops = { .write = sel_write_unshare, + .read = sel_read_unshare, .llseek = generic_file_llseek, }; @@ -2021,7 +2038,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, S_IWUGO}, - [SEL_UNSHARE] = {"unshare", &sel_unshare_ops, 0222}, + [SEL_UNSHARE] = {"unshare", &sel_unshare_ops, 0666}, /* last one */ {""} }; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 319add3..5ea841f 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4591,7 +4591,7 @@ static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) { - return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, ctx, ctxlen, 0); + return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, NULL, ctx, ctxlen, 0); } static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)