summaryrefslogtreecommitdiff
path: root/security/selinux/hooks.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 11:26:35 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 11:26:35 -0700
commite24dd9ee5399747b71c1d982a484fc7601795f31 (patch)
tree14fcec8728916092a9f6dbeb0f2b8d5c5a4e5c9a /security/selinux/hooks.c
parent7391786a64dcfe9c609a1f8e2204c1abf42ded23 (diff)
parentc4758fa59285fe4dbfeab4364a6957936d040fbf (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security layer updates from James Morris: - a major update for AppArmor. From JJ: * several bug fixes and cleanups * the patch to add symlink support to securityfs that was floated on the list earlier and the apparmorfs changes that make use of securityfs symlinks * it introduces the domain labeling base code that Ubuntu has been carrying for several years, with several cleanups applied. And it converts the current mediation over to using the domain labeling base, which brings domain stacking support with it. This finally will bring the base upstream code in line with Ubuntu and provide a base to upstream the new feature work that Ubuntu carries. * This does _not_ contain any of the newer apparmor mediation features/controls (mount, signals, network, keys, ...) that Ubuntu is currently carrying, all of which will be RFC'd on top of this. - Notable also is the Infiniband work in SELinux, and the new file:map permission. From Paul: "While we're down to 21 patches for v4.13 (it was 31 for v4.12), the diffstat jumps up tremendously with over 2k of line changes. Almost all of these changes are the SELinux/IB work done by Daniel Jurgens; some other noteworthy changes include a NFS v4.2 labeling fix, a new file:map permission, and reporting of policy capabilities on policy load" There's also now genfscon labeling support for tracefs, which was lost in v4.1 with the separation from debugfs. - Smack incorporates a safer socket check in file_receive, and adds a cap_capable call in privilege check. - TPM as usual has a bunch of fixes and enhancements. - Multiple calls to security_add_hooks() can now be made for the same LSM, to allow LSMs to have hook declarations across multiple files. - IMA now supports different "ima_appraise=" modes (eg. log, fix) from the boot command line. * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (126 commits) apparmor: put back designators in struct initialisers seccomp: Switch from atomic_t to recount_t seccomp: Adjust selftests to avoid double-join seccomp: Clean up core dump logic IMA: update IMA policy documentation to include pcr= option ima: Log the same audit cause whenever a file has no signature ima: Simplify policy_func_show. integrity: Small code improvements ima: fix get_binary_runtime_size() ima: use ima_parse_buf() to parse template data ima: use ima_parse_buf() to parse measurements headers ima: introduce ima_parse_buf() ima: Add cgroups2 to the defaults list ima: use memdup_user_nul ima: fix up #endif comments IMA: Correct Kconfig dependencies for hash selection ima: define is_ima_appraise_enabled() ima: define Kconfig IMA_APPRAISE_BOOTPARAM option ima: define a set of appraisal rules requiring file signatures ima: extend the "ima_policy" boot command line to support multiple policies ...
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r--security/selinux/hooks.c212
1 files changed, 176 insertions, 36 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 819fd6858b49..33fd061305c4 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,
@@ -2062,8 +2090,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;
@@ -3058,6 +3087,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;
@@ -3073,8 +3103,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);
@@ -3106,6 +3138,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)
{
@@ -3137,7 +3181,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;
@@ -3263,13 +3307,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
@@ -3549,6 +3588,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;
@@ -3709,7 +3760,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();
@@ -5917,7 +5969,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;
@@ -6127,7 +6179,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 = {
@@ -6212,7 +6327,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),
@@ -6314,7 +6429,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),
@@ -6378,6 +6499,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
@@ -6447,6 +6571,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;
@@ -6456,13 +6597,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
@@ -6470,7 +6610,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