selinux: Add a cache for quicker retreival of PKey SIDs
authorDaniel Jurgens <danielj@mellanox.com>
Fri, 19 May 2017 12:48:59 +0000 (15:48 +0300)
committerPaul Moore <paul@paul-moore.com>
Tue, 23 May 2017 16:28:12 +0000 (12:28 -0400)
It is likely that the SID for the same PKey will be requested many
times. To reduce the time to modify QPs and process MADs use a cache to
store PKey SIDs.

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)

Signed-off-by: Daniel Jurgens <danielj@mellanox.com>
Acked-by: Doug Ledford <dledford@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
security/selinux/Makefile
security/selinux/hooks.c
security/selinux/ibpkey.c [new file with mode: 0644]
security/selinux/include/ibpkey.h [new file with mode: 0644]
security/selinux/include/objsec.h

index 3411c33e2a445f28282aa5315e4b17a42a69ad23..ff5895ede96fec6bb5507b2c6d531e41ca2bed00 100644 (file)
@@ -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
 
index 91ec46dd34d9d60b30d4b294ab626ddf92a22e01..158f6a0052469da8b915a69d405166f7a350c137 100644 (file)
@@ -91,6 +91,7 @@
 #include "netif.h"
 #include "netnode.h"
 #include "netport.h"
+#include "ibpkey.h"
 #include "xfrm.h"
 #include "netlabel.h"
 #include "audit.h"
@@ -174,8 +175,10 @@ static int selinux_netcache_avc_callback(u32 event)
 
 static int selinux_lsm_notifier_avc_callback(u32 event)
 {
-       if (event == AVC_CALLBACK_RESET)
+       if (event == AVC_CALLBACK_RESET) {
+               sel_ib_pkey_flush();
                call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
+       }
 
        return 0;
 }
@@ -6156,7 +6159,7 @@ static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val)
        struct ib_security_struct *sec = ib_sec;
        struct lsm_ibpkey_audit ibpkey;
 
-       err = security_ib_pkey_sid(subnet_prefix, pkey_val, &sid);
+       err = sel_ib_pkey_sid(subnet_prefix, pkey_val, &sid);
        if (err)
                return err;
 
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
new file mode 100644 (file)
index 0000000..e3614ee
--- /dev/null
@@ -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/ibpkey.h b/security/selinux/include/ibpkey.h
new file mode 100644 (file)
index 0000000..b17a19e
--- /dev/null
@@ -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
index b7f15f7dc9af5b21d6cb7fe9381bb6d63d171e4e..6ebc61e370ff32856ef9b164355ffd2e9645699e 100644 (file)
@@ -144,6 +144,12 @@ 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_ */