diff options
author | James Morris | 2017-06-23 11:55:57 +1000 |
---|---|---|
committer | James Morris | 2017-06-23 11:55:57 +1000 |
commit | 5965453d5e3fb425e6f9d6b4fec403bda3f33107 (patch) | |
tree | 8014b3803a185b8b10280122849ecf7cfdacdb73 /security | |
parent | fc26bd50539b6f52aa75ffbaec7b083825ec5451 (diff) | |
parent | 6a3911837da0a90ed599fd0a9836472f5e7ddf1b (diff) |
Merge branch 'stable-4.13' of git://git.infradead.org/users/pcmoore/selinux into next
Diffstat (limited to 'security')
-rw-r--r-- | security/Kconfig | 9 | ||||
-rw-r--r-- | security/lsm_audit.c | 16 | ||||
-rw-r--r-- | security/security.c | 55 | ||||
-rw-r--r-- | security/selinux/Makefile | 2 | ||||
-rw-r--r-- | security/selinux/hooks.c | 212 | ||||
-rw-r--r-- | security/selinux/ibpkey.c | 245 | ||||
-rw-r--r-- | security/selinux/include/classmap.h | 6 | ||||
-rw-r--r-- | security/selinux/include/ibpkey.h | 31 | ||||
-rw-r--r-- | security/selinux/include/objsec.h | 11 | ||||
-rw-r--r-- | security/selinux/include/security.h | 9 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 15 | ||||
-rw-r--r-- | security/selinux/ss/ebitmap.c | 26 | ||||
-rw-r--r-- | security/selinux/ss/ebitmap.h | 3 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 127 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 27 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 108 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.c | 27 |
17 files changed, 821 insertions, 108 deletions
diff --git a/security/Kconfig b/security/Kconfig index bdcbb92927ab..d540bfe73190 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -54,6 +54,15 @@ config SECURITY_NETWORK implement socket and networking access controls. If you are unsure how to answer this question, answer N. +config SECURITY_INFINIBAND + bool "Infiniband Security Hooks" + depends on SECURITY && INFINIBAND + help + This enables the Infiniband security hooks. + If enabled, a security module can use these hooks to + implement Infiniband access controls. + If you are unsure how to answer this question, answer N. + config SECURITY_NETWORK_XFRM bool "XFRM (IPSec) Networking Security Hooks" depends on XFRM && SECURITY_NETWORK diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 37f04dadc8d6..28d4c3a528ab 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -410,6 +410,22 @@ static void dump_common_audit_data(struct audit_buffer *ab, audit_log_format(ab, " kmod="); audit_log_untrustedstring(ab, a->u.kmod_name); break; + case LSM_AUDIT_DATA_IBPKEY: { + struct in6_addr sbn_pfx; + + memset(&sbn_pfx.s6_addr, 0, + sizeof(sbn_pfx.s6_addr)); + memcpy(&sbn_pfx.s6_addr, &a->u.ibpkey->subnet_prefix, + sizeof(a->u.ibpkey->subnet_prefix)); + audit_log_format(ab, " pkey=0x%x subnet_prefix=%pI6c", + a->u.ibpkey->pkey, &sbn_pfx); + break; + } + case LSM_AUDIT_DATA_IBENDPORT: + audit_log_format(ab, " device=%s port_num=%u", + a->u.ibendport->dev_name, + a->u.ibendport->port); + break; } /* switch (a->type) */ } diff --git a/security/security.c b/security/security.c index 38316bb28b16..30132378d103 100644 --- a/security/security.c +++ b/security/security.c @@ -4,6 +4,7 @@ * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com> * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com> + * Copyright (C) 2016 Mellanox Technologies * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +35,8 @@ #define SECURITY_NAME_MAX 10 struct security_hook_heads security_hook_heads __lsm_ro_after_init; +static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); + char *lsm_names; /* Boot-time LSM user choice */ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = @@ -165,6 +168,24 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, panic("%s - Cannot get early memory.\n", __func__); } +int call_lsm_notifier(enum lsm_event event, void *data) +{ + return atomic_notifier_call_chain(&lsm_notifier_chain, event, data); +} +EXPORT_SYMBOL(call_lsm_notifier); + +int register_lsm_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&lsm_notifier_chain, nb); +} +EXPORT_SYMBOL(register_lsm_notifier); + +int unregister_lsm_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&lsm_notifier_chain, nb); +} +EXPORT_SYMBOL(unregister_lsm_notifier); + /* * Hook list operation macros. * @@ -399,9 +420,12 @@ int security_sb_set_mnt_opts(struct super_block *sb, EXPORT_SYMBOL(security_sb_set_mnt_opts); int security_sb_clone_mnt_opts(const struct super_block *oldsb, - struct super_block *newsb) + struct super_block *newsb, + unsigned long kern_flags, + unsigned long *set_kern_flags) { - return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb); + return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb, + kern_flags, set_kern_flags); } EXPORT_SYMBOL(security_sb_clone_mnt_opts); @@ -1515,6 +1539,33 @@ EXPORT_SYMBOL(security_tun_dev_open); #endif /* CONFIG_SECURITY_NETWORK */ +#ifdef CONFIG_SECURITY_INFINIBAND + +int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey) +{ + return call_int_hook(ib_pkey_access, 0, sec, subnet_prefix, pkey); +} +EXPORT_SYMBOL(security_ib_pkey_access); + +int security_ib_endport_manage_subnet(void *sec, const char *dev_name, u8 port_num) +{ + return call_int_hook(ib_endport_manage_subnet, 0, sec, dev_name, port_num); +} +EXPORT_SYMBOL(security_ib_endport_manage_subnet); + +int security_ib_alloc_security(void **sec) +{ + return call_int_hook(ib_alloc_security, 0, sec); +} +EXPORT_SYMBOL(security_ib_alloc_security); + +void security_ib_free_security(void *sec) +{ + call_void_hook(ib_free_security, sec); +} +EXPORT_SYMBOL(security_ib_free_security); +#endif /* CONFIG_SECURITY_INFINIBAND */ + #ifdef CONFIG_SECURITY_NETWORK_XFRM int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, diff --git a/security/selinux/Makefile b/security/selinux/Makefile index 3411c33e2a44..ff5895ede96f 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ - netnode.o netport.o exports.o \ + netnode.o netport.o ibpkey.o exports.o \ ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e67a526d1f30..3a06afbd2f6f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -17,6 +17,7 @@ * Paul Moore <paul@paul-moore.com> * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. * Yuichi Nakamura <ynakam@hitachisoft.jp> + * Copyright (C) 2016 Mellanox Technologies * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -90,6 +91,7 @@ #include "netif.h" #include "netnode.h" #include "netport.h" +#include "ibpkey.h" #include "xfrm.h" #include "netlabel.h" #include "audit.h" @@ -171,6 +173,16 @@ static int selinux_netcache_avc_callback(u32 event) return 0; } +static int selinux_lsm_notifier_avc_callback(u32 event) +{ + if (event == AVC_CALLBACK_RESET) { + sel_ib_pkey_flush(); + call_lsm_notifier(LSM_POLICY_CHANGE, NULL); + } + + return 0; +} + /* * initialise the security for the init task */ @@ -398,18 +410,6 @@ static void superblock_free_security(struct super_block *sb) kfree(sbsec); } -/* The file system's label must be initialized prior to use. */ - -static const char *labeling_behaviors[7] = { - "uses xattr", - "uses transition SIDs", - "uses task SIDs", - "uses genfs_contexts", - "not configured for labeling", - "uses mountpoint labeling", - "uses native labeling", -}; - static inline int inode_doinit(struct inode *inode) { return inode_doinit_with_dentry(inode, NULL); @@ -524,13 +524,17 @@ static int sb_finish_set_opts(struct super_block *sb) } } - if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) - printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n", - sb->s_id, sb->s_type->name); - sbsec->flags |= SE_SBINITIALIZED; + + /* + * Explicitly set or clear SBLABEL_MNT. It's not sufficient to simply + * leave the flag untouched because sb_clone_mnt_opts might be handing + * us a superblock that needs the flag to be cleared. + */ if (selinux_is_sblabel_mnt(sb)) sbsec->flags |= SBLABEL_MNT; + else + sbsec->flags &= ~SBLABEL_MNT; /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); @@ -809,6 +813,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, sbsec->flags |= SE_SBPROC | SE_SBGENFS; if (!strcmp(sb->s_type->name, "debugfs") || + !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "pstore")) sbsec->flags |= SE_SBGENFS; @@ -963,8 +968,11 @@ mismatch: } static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, - struct super_block *newsb) + struct super_block *newsb, + unsigned long kern_flags, + unsigned long *set_kern_flags) { + int rc = 0; const struct superblock_security_struct *oldsbsec = oldsb->s_security; struct superblock_security_struct *newsbsec = newsb->s_security; @@ -979,6 +987,13 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, if (!ss_initialized) return 0; + /* + * Specifying internal flags without providing a place to + * place the results is not allowed. + */ + if (kern_flags && !set_kern_flags) + return -EINVAL; + /* how can we clone if the old one wasn't set up?? */ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); @@ -994,6 +1009,18 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, newsbsec->def_sid = oldsbsec->def_sid; newsbsec->behavior = oldsbsec->behavior; + if (newsbsec->behavior == SECURITY_FS_USE_NATIVE && + !(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) { + rc = security_fs_use(newsb); + if (rc) + goto out; + } + + if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !set_context) { + newsbsec->behavior = SECURITY_FS_USE_NATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } + if (set_context) { u32 sid = oldsbsec->mntpoint_sid; @@ -1013,8 +1040,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, } sb_finish_set_opts(newsb); +out: mutex_unlock(&newsbsec->lock); - return 0; + return rc; } static int selinux_parse_opts_str(char *options, @@ -2063,8 +2091,9 @@ static inline u32 file_to_av(struct file *file) static inline u32 open_file_to_av(struct file *file) { u32 av = file_to_av(file); + struct inode *inode = file_inode(file); - if (selinux_policycap_openperm) + if (selinux_policycap_openperm && inode->i_sb->s_magic != SOCKFS_MAGIC) av |= FILE__OPEN; return av; @@ -3059,6 +3088,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) { const struct cred *cred = current_cred(); + struct inode *inode = d_backing_inode(dentry); unsigned int ia_valid = iattr->ia_valid; __u32 av = FILE__WRITE; @@ -3074,8 +3104,10 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, dentry, FILE__SETATTR); - if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE) - && !(ia_valid & ATTR_FILE)) + if (selinux_policycap_openperm && + inode->i_sb->s_magic != SOCKFS_MAGIC && + (ia_valid & ATTR_SIZE) && + !(ia_valid & ATTR_FILE)) av |= FILE__OPEN; return dentry_has_perm(cred, dentry, av); @@ -3107,6 +3139,18 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name) return dentry_has_perm(cred, dentry, FILE__SETATTR); } +static bool has_cap_mac_admin(bool audit) +{ + const struct cred *cred = current_cred(); + int cap_audit = audit ? SECURITY_CAP_AUDIT : SECURITY_CAP_NOAUDIT; + + if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, cap_audit)) + return false; + if (cred_has_capability(cred, CAP_MAC_ADMIN, cap_audit, true)) + return false; + return true; +} + static int selinux_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { @@ -3138,7 +3182,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); if (rc == -EINVAL) { - if (!capable(CAP_MAC_ADMIN)) { + if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; size_t audit_size; const char *str; @@ -3264,13 +3308,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void * and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error = cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN, - SECURITY_CAP_NOAUDIT); - if (!error) - error = cred_has_capability(current_cred(), CAP_MAC_ADMIN, - SECURITY_CAP_NOAUDIT, true); isec = inode_security(inode); - if (!error) + if (has_cap_mac_admin(false)) error = security_sid_to_context_force(isec->sid, &context, &size); else @@ -3550,6 +3589,18 @@ static int selinux_mmap_addr(unsigned long addr) static int selinux_mmap_file(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) { + struct common_audit_data ad; + int rc; + + if (file) { + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; + rc = inode_has_perm(current_cred(), file_inode(file), + FILE__MAP, &ad); + if (rc) + return rc; + } + if (selinux_checkreqprot) prot = reqprot; @@ -3710,7 +3761,8 @@ static int selinux_file_open(struct file *file, const struct cred *cred) /* task security operations */ -static int selinux_task_create(unsigned long clone_flags) +static int selinux_task_alloc(struct task_struct *task, + unsigned long clone_flags) { u32 sid = current_sid(); @@ -5918,7 +5970,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) } error = security_context_to_sid(value, size, &sid, GFP_KERNEL); if (error == -EINVAL && !strcmp(name, "fscreate")) { - if (!capable(CAP_MAC_ADMIN)) { + if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; size_t audit_size; @@ -6128,7 +6180,70 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) *_buffer = context; return rc; } +#endif + +#ifdef CONFIG_SECURITY_INFINIBAND +static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val) +{ + struct common_audit_data ad; + int err; + u32 sid = 0; + struct ib_security_struct *sec = ib_sec; + struct lsm_ibpkey_audit ibpkey; + + err = sel_ib_pkey_sid(subnet_prefix, pkey_val, &sid); + if (err) + return err; + + ad.type = LSM_AUDIT_DATA_IBPKEY; + ibpkey.subnet_prefix = subnet_prefix; + ibpkey.pkey = pkey_val; + ad.u.ibpkey = &ibpkey; + return avc_has_perm(sec->sid, sid, + SECCLASS_INFINIBAND_PKEY, + INFINIBAND_PKEY__ACCESS, &ad); +} + +static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, + u8 port_num) +{ + struct common_audit_data ad; + int err; + u32 sid = 0; + struct ib_security_struct *sec = ib_sec; + struct lsm_ibendport_audit ibendport; + + err = security_ib_endport_sid(dev_name, port_num, &sid); + + if (err) + return err; + + ad.type = LSM_AUDIT_DATA_IBENDPORT; + strncpy(ibendport.dev_name, dev_name, sizeof(ibendport.dev_name)); + ibendport.port = port_num; + ad.u.ibendport = &ibendport; + return avc_has_perm(sec->sid, sid, + SECCLASS_INFINIBAND_ENDPORT, + INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad); +} + +static int selinux_ib_alloc_security(void **ib_sec) +{ + struct ib_security_struct *sec; + sec = kzalloc(sizeof(*sec), GFP_KERNEL); + if (!sec) + return -ENOMEM; + sec->sid = current_sid(); + + *ib_sec = sec; + return 0; +} + +static void selinux_ib_free_security(void *ib_sec) +{ + kfree(ib_sec); +} #endif static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { @@ -6213,7 +6328,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(file_open, selinux_file_open), - LSM_HOOK_INIT(task_create, selinux_task_create), + LSM_HOOK_INIT(task_alloc, selinux_task_alloc), LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank), LSM_HOOK_INIT(cred_free, selinux_cred_free), LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare), @@ -6315,7 +6430,13 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue), LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach), LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open), - +#ifdef CONFIG_SECURITY_INFINIBAND + LSM_HOOK_INIT(ib_pkey_access, selinux_ib_pkey_access), + LSM_HOOK_INIT(ib_endport_manage_subnet, + selinux_ib_endport_manage_subnet), + LSM_HOOK_INIT(ib_alloc_security, selinux_ib_alloc_security), + LSM_HOOK_INIT(ib_free_security, selinux_ib_free_security), +#endif #ifdef CONFIG_SECURITY_NETWORK_XFRM LSM_HOOK_INIT(xfrm_policy_alloc_security, selinux_xfrm_policy_alloc), LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone), @@ -6379,6 +6500,9 @@ static __init int selinux_init(void) if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC netcache callback\n"); + if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET)) + panic("SELinux: Unable to register AVC LSM notifier callback\n"); + if (selinux_enforcing) printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); else @@ -6448,6 +6572,23 @@ static struct nf_hook_ops selinux_nf_ops[] = { #endif /* IPV6 */ }; +static int __net_init selinux_nf_register(struct net *net) +{ + return nf_register_net_hooks(net, selinux_nf_ops, + ARRAY_SIZE(selinux_nf_ops)); +} + +static void __net_exit selinux_nf_unregister(struct net *net) +{ + nf_unregister_net_hooks(net, selinux_nf_ops, + ARRAY_SIZE(selinux_nf_ops)); +} + +static struct pernet_operations selinux_net_ops = { + .init = selinux_nf_register, + .exit = selinux_nf_unregister, +}; + static int __init selinux_nf_ip_init(void) { int err; @@ -6457,13 +6598,12 @@ static int __init selinux_nf_ip_init(void) printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); - err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); + err = register_pernet_subsys(&selinux_net_ops); if (err) - panic("SELinux: nf_register_hooks: error %d\n", err); + panic("SELinux: register_pernet_subsys: error %d\n", err); return 0; } - __initcall(selinux_nf_ip_init); #ifdef CONFIG_SECURITY_SELINUX_DISABLE @@ -6471,7 +6611,7 @@ static void selinux_nf_ip_exit(void) { printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); - nf_unregister_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); + unregister_pernet_subsys(&selinux_net_ops); } #endif diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c new file mode 100644 index 000000000000..e3614ee5f1c0 --- /dev/null +++ b/security/selinux/ibpkey.c @@ -0,0 +1,245 @@ +/* + * Pkey table + * + * SELinux must keep a mapping of Infinband PKEYs to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead. + * + * This code is heavily based on the "netif" and "netport" concept originally + * developed by + * James Morris <jmorris@redhat.com> and + * Paul Moore <paul@paul-moore.com> + * (see security/selinux/netif.c and security/selinux/netport.c for more + * information) + * + */ + +/* + * (c) Mellanox Technologies, 2016 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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/types.h> +#include <linux/rcupdate.h> +#include <linux/list.h> +#include <linux/spinlock.h> + +#include "ibpkey.h" +#include "objsec.h" + +#define SEL_PKEY_HASH_SIZE 256 +#define SEL_PKEY_HASH_BKT_LIMIT 16 + +struct sel_ib_pkey_bkt { + int size; + struct list_head list; +}; + +struct sel_ib_pkey { + struct pkey_security_struct psec; + struct list_head list; + struct rcu_head rcu; +}; + +static LIST_HEAD(sel_ib_pkey_list); +static DEFINE_SPINLOCK(sel_ib_pkey_lock); +static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE]; + +/** + * sel_ib_pkey_hashfn - Hashing function for the pkey table + * @pkey: pkey number + * + * Description: + * This is the hashing function for the pkey table, it returns the bucket + * number for the given pkey. + * + */ +static unsigned int sel_ib_pkey_hashfn(u16 pkey) +{ + return (pkey & (SEL_PKEY_HASH_SIZE - 1)); +} + +/** + * sel_ib_pkey_find - Search for a pkey record + * @subnet_prefix: subnet_prefix + * @pkey_num: pkey_num + * + * Description: + * Search the pkey table and return the matching record. If an entry + * can not be found in the table return NULL. + * + */ +static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num) +{ + unsigned int idx; + struct sel_ib_pkey *pkey; + + idx = sel_ib_pkey_hashfn(pkey_num); + list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) { + if (pkey->psec.pkey == pkey_num && + pkey->psec.subnet_prefix == subnet_prefix) + return pkey; + } + + return NULL; +} + +/** + * sel_ib_pkey_insert - Insert a new pkey into the table + * @pkey: the new pkey record + * + * Description: + * Add a new pkey record to the hash table. + * + */ +static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey) +{ + unsigned int idx; + + /* we need to impose a limit on the growth of the hash table so check + * this bucket to make sure it is within the specified bounds + */ + idx = sel_ib_pkey_hashfn(pkey->psec.pkey); + list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list); + if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) { + struct sel_ib_pkey *tail; + + tail = list_entry( + rcu_dereference_protected( + sel_ib_pkey_hash[idx].list.prev, + lockdep_is_held(&sel_ib_pkey_lock)), + struct sel_ib_pkey, list); + list_del_rcu(&tail->list); + kfree_rcu(tail, rcu); + } else { + sel_ib_pkey_hash[idx].size++; + } +} + +/** + * sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy + * @subnet_prefix: subnet prefix + * @pkey_num: pkey number + * @sid: pkey SID + * + * Description: + * This function determines the SID of a pkey by querying the security + * policy. The result is added to the pkey table to speedup future + * queries. Returns zero on success, negative values on failure. + * + */ +static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid) +{ + int ret; + struct sel_ib_pkey *pkey; + struct sel_ib_pkey *new = NULL; + unsigned long flags; + + spin_lock_irqsave(&sel_ib_pkey_lock, flags); + pkey = sel_ib_pkey_find(subnet_prefix, pkey_num); + if (pkey) { + *sid = pkey->psec.sid; + spin_unlock_irqrestore(&sel_ib_pkey_lock, flags); + return 0; + } + + ret = security_ib_pkey_sid(subnet_prefix, pkey_num, sid); + if (ret) + goto out; + + /* If this memory allocation fails still return 0. The SID + * is valid, it just won't be added to the cache. + */ + new = kzalloc(sizeof(*new), GFP_ATOMIC); + if (!new) + goto out; + + new->psec.subnet_prefix = subnet_prefix; + new->psec.pkey = pkey_num; + new->psec.sid = *sid; + sel_ib_pkey_insert(new); + +out: + spin_unlock_irqrestore(&sel_ib_pkey_lock, flags); + return ret; +} + +/** + * sel_ib_pkey_sid - Lookup the SID of a PKEY + * @subnet_prefix: subnet_prefix + * @pkey_num: pkey number + * @sid: pkey SID + * + * Description: + * This function determines the SID of a PKEY using the fastest method + * possible. First the pkey table is queried, but if an entry can't be found + * then the policy is queried and the result is added to the table to speedup + * future queries. Returns zero on success, negative values on failure. + * + */ +int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid) +{ + struct sel_ib_pkey *pkey; + + rcu_read_lock(); + pkey = sel_ib_pkey_find(subnet_prefix, pkey_num); + if (pkey) { + *sid = pkey->psec.sid; + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + + return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid); +} + +/** + * sel_ib_pkey_flush - Flush the entire pkey table + * + * Description: + * Remove all entries from the pkey table + * + */ +void sel_ib_pkey_flush(void) +{ + unsigned int idx; + struct sel_ib_pkey *pkey, *pkey_tmp; + unsigned long flags; + + spin_lock_irqsave(&sel_ib_pkey_lock, flags); + for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) { + list_for_each_entry_safe(pkey, pkey_tmp, + &sel_ib_pkey_hash[idx].list, list) { + list_del_rcu(&pkey->list); + kfree_rcu(pkey, rcu); + } + sel_ib_pkey_hash[idx].size = 0; + } + spin_unlock_irqrestore(&sel_ib_pkey_lock, flags); +} + +static __init int sel_ib_pkey_init(void) +{ + int iter; + + if (!selinux_enabled) + return 0; + + for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) { + INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list); + sel_ib_pkey_hash[iter].size = 0; + } + + return 0; +} + +subsys_initcall(sel_ib_pkey_init); diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 1e0cc9b5de20..b9fe3434b036 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -1,7 +1,7 @@ #include <linux/capability.h> #define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \ - "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" + "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append", "map" #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ "rename", "execute", "quotaon", "mounton", "audit_access", \ @@ -231,6 +231,10 @@ struct security_class_mapping secclass_map[] = { { COMMON_SOCK_PERMS, NULL } }, { "smc_socket", { COMMON_SOCK_PERMS, NULL } }, + { "infiniband_pkey", + { "access", NULL } }, + { "infiniband_endport", + { "manage_subnet", NULL } }, { NULL } }; diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h new file mode 100644 index 000000000000..b17a19e348e6 --- /dev/null +++ b/security/selinux/include/ibpkey.h @@ -0,0 +1,31 @@ +/* + * pkey table + * + * SELinux must keep a mapping of pkeys to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead. + * + */ + +/* + * (c) Mellanox Technologies, 2016 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _SELINUX_IB_PKEY_H +#define _SELINUX_IB_PKEY_H + +void sel_ib_pkey_flush(void); + +int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid); + +#endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index c03cdcd12a3b..6ebc61e370ff 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -10,6 +10,7 @@ * * Copyright (C) 2001,2002 Networks Associates Technology, Inc. * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2016 Mellanox Technologies * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -139,6 +140,16 @@ struct key_security_struct { u32 sid; /* SID of key */ }; +struct ib_security_struct { + u32 sid; /* SID of the queue pair or MAD agent */ +}; + +struct pkey_security_struct { + u64 subnet_prefix; /* Port subnet prefix */ + u16 pkey; /* PKey number */ + u32 sid; /* SID of pkey */ +}; + extern unsigned int selinux_checkreqprot; #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index f979c35e037e..e91f08c16c0b 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -36,10 +36,11 @@ #define POLICYDB_VERSION_DEFAULT_TYPE 28 #define POLICYDB_VERSION_CONSTRAINT_NAMES 29 #define POLICYDB_VERSION_XPERMS_IOCTL 30 +#define POLICYDB_VERSION_INFINIBAND 31 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_INFINIBAND /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f @@ -76,6 +77,8 @@ enum { }; #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) +extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX]; + extern int selinux_policycap_netpeer; extern int selinux_policycap_openperm; extern int selinux_policycap_extsockclass; @@ -178,6 +181,10 @@ int security_get_user_sids(u32 callsid, char *username, int security_port_sid(u8 protocol, u16 port, u32 *out_sid); +int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid); + +int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid); + int security_netif_sid(char *name, u32 *if_sid); int security_node_sid(u16 domain, void *addr, u32 addrlen, diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 50062e70140d..9010a3632d6f 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -41,15 +41,6 @@ #include "objsec.h" #include "conditional.h" -/* Policy capability filenames */ -static char *policycap_names[] = { - "network_peer_controls", - "open_perms", - "extended_socket_class", - "always_check_network", - "cgroup_seclabel" -}; - unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; static int __init checkreqprot_setup(char *str) @@ -163,6 +154,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, avc_ss_reset(0); selnl_notify_setenforce(selinux_enforcing); selinux_status_update_setenforce(selinux_enforcing); + if (!selinux_enforcing) + call_lsm_notifier(LSM_POLICY_CHANGE, NULL); } length = count; out: @@ -1750,9 +1743,9 @@ static int sel_make_policycap(void) sel_remove_entries(policycap_dir); for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { - if (iter < ARRAY_SIZE(policycap_names)) + if (iter < ARRAY_SIZE(selinux_policycap_names)) dentry = d_alloc_name(policycap_dir, - policycap_names[iter]); + selinux_policycap_names[iter]); else dentry = d_alloc_name(policycap_dir, "unknown"); diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 9db4709a6877..ad38299164c3 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -24,6 +24,8 @@ #define BITS_PER_U64 (sizeof(u64) * 8) +static struct kmem_cache *ebitmap_node_cachep; + int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) { struct ebitmap_node *n1, *n2; @@ -54,7 +56,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) n = src->node; prev = NULL; while (n) { - new = kzalloc(sizeof(*new), GFP_ATOMIC); + new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC); if (!new) { ebitmap_destroy(dst); return -ENOMEM; @@ -162,7 +164,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap, if (e_iter == NULL || offset >= e_iter->startbit + EBITMAP_SIZE) { e_prev = e_iter; - e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + e_iter = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC); if (e_iter == NULL) goto netlbl_import_failure; e_iter->startbit = offset - (offset % EBITMAP_SIZE); @@ -288,7 +290,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) prev->next = n->next; else e->node = n->next; - kfree(n); + kmem_cache_free(ebitmap_node_cachep, n); } return 0; } @@ -299,7 +301,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) if (!value) return 0; - new = kzalloc(sizeof(*new), GFP_ATOMIC); + new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC); if (!new) return -ENOMEM; @@ -332,7 +334,7 @@ void ebitmap_destroy(struct ebitmap *e) while (n) { temp = n; n = n->next; - kfree(temp); + kmem_cache_free(ebitmap_node_cachep, temp); } e->highbit = 0; @@ -400,7 +402,7 @@ int ebitmap_read(struct ebitmap *e, void *fp) if (!n || startbit >= n->startbit + EBITMAP_SIZE) { struct ebitmap_node *tmp; - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + tmp = kmem_cache_zalloc(ebitmap_node_cachep, GFP_KERNEL); if (!tmp) { printk(KERN_ERR "SELinux: ebitmap: out of memory\n"); @@ -519,3 +521,15 @@ int ebitmap_write(struct ebitmap *e, void *fp) } return 0; } + +void ebitmap_cache_init(void) +{ + ebitmap_node_cachep = kmem_cache_create("ebitmap_node", + sizeof(struct ebitmap_node), + 0, SLAB_PANIC, NULL); +} + +void ebitmap_cache_destroy(void) +{ + kmem_cache_destroy(ebitmap_node_cachep); +} diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 9637b8c71085..6d5a9ac4251f 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -130,6 +130,9 @@ void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); int ebitmap_write(struct ebitmap *e, void *fp); +void ebitmap_cache_init(void); +void ebitmap_cache_destroy(void); + #ifdef CONFIG_NETLABEL int ebitmap_netlbl_export(struct ebitmap *ebmap, struct netlbl_lsm_catmap **catmap); diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 0080122760ad..aa6500abb178 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -17,6 +17,11 @@ * * Added support for the policy capability bitmap * + * Update: Mellanox Techonologies + * + * Added Infiniband support + * + * Copyright (C) 2016 Mellanox Techonologies * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC @@ -76,81 +81,86 @@ static struct policydb_compat_info policydb_compat[] = { { .version = POLICYDB_VERSION_BASE, .sym_num = SYM_NUM - 3, - .ocon_num = OCON_NUM - 1, + .ocon_num = OCON_NUM - 3, }, { .version = POLICYDB_VERSION_BOOL, .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM - 1, + .ocon_num = OCON_NUM - 3, }, { .version = POLICYDB_VERSION_IPV6, .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_NLCLASS, .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_MLS, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_AVTAB, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_RANGETRANS, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_POLCAP, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_BOUNDARY, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_FILENAME_TRANS, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_ROLETRANS, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_DEFAULT_TYPE, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_CONSTRAINT_NAMES, .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .ocon_num = OCON_NUM - 2, }, { .version = POLICYDB_VERSION_XPERMS_IOCTL, .sym_num = SYM_NUM, + .ocon_num = OCON_NUM - 2, + }, + { + .version = POLICYDB_VERSION_INFINIBAND, + .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, }; @@ -538,34 +548,30 @@ static int policydb_index(struct policydb *p) symtab_hash_eval(p->symtab); #endif - rc = -ENOMEM; p->class_val_to_struct = kcalloc(p->p_classes.nprim, sizeof(*p->class_val_to_struct), GFP_KERNEL); if (!p->class_val_to_struct) - goto out; + return -ENOMEM; - rc = -ENOMEM; p->role_val_to_struct = kcalloc(p->p_roles.nprim, sizeof(*p->role_val_to_struct), GFP_KERNEL); if (!p->role_val_to_struct) - goto out; + return -ENOMEM; - rc = -ENOMEM; p->user_val_to_struct = kcalloc(p->p_users.nprim, sizeof(*p->user_val_to_struct), GFP_KERNEL); if (!p->user_val_to_struct) - goto out; + return -ENOMEM; /* Yes, I want the sizeof the pointer, not the structure */ - rc = -ENOMEM; p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *), p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (!p->type_val_to_struct_array) - goto out; + return -ENOMEM; rc = flex_array_prealloc(p->type_val_to_struct_array, 0, p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); @@ -577,12 +583,11 @@ static int policydb_index(struct policydb *p) goto out; for (i = 0; i < SYM_NUM; i++) { - rc = -ENOMEM; p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *), p->symtab[i].nprim, GFP_KERNEL | __GFP_ZERO); if (!p->sym_val_to_name[i]) - goto out; + return -ENOMEM; rc = flex_array_prealloc(p->sym_val_to_name[i], 0, p->symtab[i].nprim, @@ -2211,6 +2216,51 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, goto out; break; } + case OCON_IBPKEY: + rc = next_entry(nodebuf, fp, sizeof(u32) * 4); + if (rc) + goto out; + + c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf)); + + if (nodebuf[2] > 0xffff || + nodebuf[3] > 0xffff) { + rc = -EINVAL; + goto out; + } + + c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]); + c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]); + + rc = context_read_and_validate(&c->context[0], + p, + fp); + if (rc) + goto out; + break; + case OCON_IBENDPORT: + rc = next_entry(buf, fp, sizeof(u32) * 2); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = str_read(&c->u.ibendport.dev_name, GFP_KERNEL, fp, len); + if (rc) + goto out; + + if (buf[1] > 0xff || buf[1] == 0) { + rc = -EINVAL; + goto out; + } + + c->u.ibendport.port = le32_to_cpu(buf[1]); + + rc = context_read_and_validate(&c->context[0], + p, + fp); + if (rc) + goto out; + break; } } } @@ -3140,6 +3190,33 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, if (rc) return rc; break; + case OCON_IBPKEY: + *((__be64 *)nodebuf) = cpu_to_be64(c->u.ibpkey.subnet_prefix); + + nodebuf[2] = cpu_to_le32(c->u.ibpkey.low_pkey); + nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey); + + rc = put_entry(nodebuf, sizeof(u32), 4, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; + break; + case OCON_IBENDPORT: + len = strlen(c->u.ibendport.dev_name); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(c->u.ibendport.port); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = put_entry(c->u.ibendport.dev_name, 1, len, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; + break; } } } diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 725d5945a97e..5d23eed35fa7 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -187,6 +187,15 @@ struct ocontext { u32 addr[4]; u32 mask[4]; } node6; /* IPv6 node information */ + struct { + u64 subnet_prefix; + u16 low_pkey; + u16 high_pkey; + } ibpkey; + struct { + char *dev_name; + u8 port; + } ibendport; } u; union { u32 sclass; /* security class for genfs */ @@ -215,14 +224,16 @@ struct genfs { #define SYM_NUM 8 /* object context array indices */ -#define OCON_ISID 0 /* initial SIDs */ -#define OCON_FS 1 /* unlabeled file systems */ -#define OCON_PORT 2 /* TCP and UDP port numbers */ -#define OCON_NETIF 3 /* network interfaces */ -#define OCON_NODE 4 /* nodes */ -#define OCON_FSUSE 5 /* fs_use */ -#define OCON_NODE6 6 /* IPv6 nodes */ -#define OCON_NUM 7 +#define OCON_ISID 0 /* initial SIDs */ +#define OCON_FS 1 /* unlabeled file systems */ +#define OCON_PORT 2 /* TCP and UDP port numbers */ +#define OCON_NETIF 3 /* network interfaces */ +#define OCON_NODE 4 /* nodes */ +#define OCON_FSUSE 5 /* fs_use */ +#define OCON_NODE6 6 /* IPv6 nodes */ +#define OCON_IBPKEY 7 /* Infiniband PKeys */ +#define OCON_IBENDPORT 8 /* Infiniband end ports */ +#define OCON_NUM 9 /* The policy database */ struct policydb { diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 60d9b0252321..2f02fa67ec2e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -70,6 +70,15 @@ #include "ebitmap.h" #include "audit.h" +/* Policy capability names */ +char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { + "network_peer_controls", + "open_perms", + "extended_socket_class", + "always_check_network", + "cgroup_seclabel" +}; + int selinux_policycap_netpeer; int selinux_policycap_openperm; int selinux_policycap_extsockclass; @@ -1986,6 +1995,9 @@ bad: static void security_load_policycaps(void) { + unsigned int i; + struct ebitmap_node *node; + selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, POLICYDB_CAPABILITY_NETPEER); selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, @@ -1997,6 +2009,17 @@ static void security_load_policycaps(void) selinux_policycap_cgroupseclabel = ebitmap_get_bit(&policydb.policycaps, POLICYDB_CAPABILITY_CGROUPSECLABEL); + + for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++) + pr_info("SELinux: policy capability %s=%d\n", + selinux_policycap_names[i], + ebitmap_get_bit(&policydb.policycaps, i)); + + ebitmap_for_each_positive_bit(&policydb.policycaps, node, i) { + if (i >= ARRAY_SIZE(selinux_policycap_names)) + pr_info("SELinux: unknown policy capability %u\n", + i); + } } static int security_preserve_bools(struct policydb *p); @@ -2031,9 +2054,11 @@ int security_load_policy(void *data, size_t len) if (!ss_initialized) { avtab_cache_init(); + ebitmap_cache_init(); rc = policydb_read(&policydb, fp); if (rc) { avtab_cache_destroy(); + ebitmap_cache_destroy(); goto out; } @@ -2044,6 +2069,7 @@ int security_load_policy(void *data, size_t len) if (rc) { policydb_destroy(&policydb); avtab_cache_destroy(); + ebitmap_cache_destroy(); goto out; } @@ -2051,6 +2077,7 @@ int security_load_policy(void *data, size_t len) if (rc) { policydb_destroy(&policydb); avtab_cache_destroy(); + ebitmap_cache_destroy(); goto out; } @@ -2210,6 +2237,87 @@ out: } /** + * security_pkey_sid - Obtain the SID for a pkey. + * @subnet_prefix: Subnet Prefix + * @pkey_num: pkey number + * @out_sid: security identifier + */ +int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid) +{ + struct ocontext *c; + int rc = 0; + + read_lock(&policy_rwlock); + + c = policydb.ocontexts[OCON_IBPKEY]; + while (c) { + if (c->u.ibpkey.low_pkey <= pkey_num && + c->u.ibpkey.high_pkey >= pkey_num && + c->u.ibpkey.subnet_prefix == subnet_prefix) + break; + + c = c->next; + } + + if (c) { + if (!c->sid[0]) { + rc = sidtab_context_to_sid(&sidtab, + &c->context[0], + &c->sid[0]); + if (rc) + goto out; + } + *out_sid = c->sid[0]; + } else + *out_sid = SECINITSID_UNLABELED; + +out: + read_unlock(&policy_rwlock); + return rc; +} + +/** + * security_ib_endport_sid - Obtain the SID for a subnet management interface. + * @dev_name: device name + * @port: port number + * @out_sid: security identifier + */ +int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid) +{ + struct ocontext *c; + int rc = 0; + + read_lock(&policy_rwlock); + + c = policydb.ocontexts[OCON_IBENDPORT]; + while (c) { + if (c->u.ibendport.port == port_num && + !strncmp(c->u.ibendport.dev_name, + dev_name, + IB_DEVICE_NAME_MAX)) + break; + + c = c->next; + } + + if (c) { + if (!c->sid[0]) { + rc = sidtab_context_to_sid(&sidtab, + &c->context[0], + &c->sid[0]); + if (rc) + goto out; + } + *out_sid = c->sid[0]; + } else + *out_sid = SECINITSID_UNLABELED; + +out: + read_unlock(&policy_rwlock); + return rc; +} + +/** * security_netif_sid - Obtain the SID for a network interface. * @name: interface name * @if_sid: interface SID diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index f6915f257486..c5f436b15d19 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -32,13 +32,11 @@ int sidtab_init(struct sidtab *s) int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) { - int hvalue, rc = 0; + int hvalue; struct sidtab_node *prev, *cur, *newnode; - if (!s) { - rc = -ENOMEM; - goto out; - } + if (!s) + return -ENOMEM; hvalue = SIDTAB_HASH(sid); prev = NULL; @@ -48,21 +46,17 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) cur = cur->next; } - if (cur && sid == cur->sid) { - rc = -EEXIST; - goto out; - } + if (cur && sid == cur->sid) + return -EEXIST; newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); - if (!newnode) { - rc = -ENOMEM; - goto out; - } + if (!newnode) + return -ENOMEM; + newnode->sid = sid; if (context_cpy(&newnode->context, context)) { kfree(newnode); - rc = -ENOMEM; - goto out; + return -ENOMEM; } if (prev) { @@ -78,8 +72,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) s->nel++; if (sid >= s->next_sid) s->next_sid = sid + 1; -out: - return rc; + return 0; } static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) |