]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - security/selinux/hooks.c
selinux: use pernet operations for hook registration
[mirror_ubuntu-artful-kernel.git] / security / selinux / hooks.c
index e67a526d1f301e4b9f27cc1685875aef85e6e9c3..9926adbd50a9624e392ad280ac80198e71ad40d8 100644 (file)
@@ -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,10 +524,6 @@ 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;
        if (selinux_is_sblabel_mnt(sb))
                sbsec->flags |= SBLABEL_MNT;
@@ -2063,8 +2059,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 +3056,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 +3072,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 +3107,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 +3150,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 +3276,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 +3557,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 +3729,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 +5938,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 +6148,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 +6296,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 +6398,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 +6468,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 +6540,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 +6566,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 +6579,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