You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'stable-4.13' of git://git.infradead.org/users/pcmoore/selinux into next
This commit is contained in:
@@ -10,7 +10,8 @@ obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o \
|
||||
ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \
|
||||
device.o fmr_pool.o cache.o netlink.o \
|
||||
roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \
|
||||
multicast.o mad.o smi.o agent.o mad_rmpp.o
|
||||
multicast.o mad.o smi.o agent.o mad_rmpp.o \
|
||||
security.o
|
||||
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
|
||||
ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o
|
||||
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o
|
||||
|
||||
@@ -53,6 +53,7 @@ struct ib_update_work {
|
||||
struct work_struct work;
|
||||
struct ib_device *device;
|
||||
u8 port_num;
|
||||
bool enforce_security;
|
||||
};
|
||||
|
||||
union ib_gid zgid;
|
||||
@@ -911,6 +912,26 @@ int ib_get_cached_pkey(struct ib_device *device,
|
||||
}
|
||||
EXPORT_SYMBOL(ib_get_cached_pkey);
|
||||
|
||||
int ib_get_cached_subnet_prefix(struct ib_device *device,
|
||||
u8 port_num,
|
||||
u64 *sn_pfx)
|
||||
{
|
||||
unsigned long flags;
|
||||
int p;
|
||||
|
||||
if (port_num < rdma_start_port(device) ||
|
||||
port_num > rdma_end_port(device))
|
||||
return -EINVAL;
|
||||
|
||||
p = port_num - rdma_start_port(device);
|
||||
read_lock_irqsave(&device->cache.lock, flags);
|
||||
*sn_pfx = device->cache.ports[p].subnet_prefix;
|
||||
read_unlock_irqrestore(&device->cache.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ib_get_cached_subnet_prefix);
|
||||
|
||||
int ib_find_cached_pkey(struct ib_device *device,
|
||||
u8 port_num,
|
||||
u16 pkey,
|
||||
@@ -1022,7 +1043,8 @@ int ib_get_cached_port_state(struct ib_device *device,
|
||||
EXPORT_SYMBOL(ib_get_cached_port_state);
|
||||
|
||||
static void ib_cache_update(struct ib_device *device,
|
||||
u8 port)
|
||||
u8 port,
|
||||
bool enforce_security)
|
||||
{
|
||||
struct ib_port_attr *tprops = NULL;
|
||||
struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache;
|
||||
@@ -1108,8 +1130,15 @@ static void ib_cache_update(struct ib_device *device,
|
||||
device->cache.ports[port - rdma_start_port(device)].port_state =
|
||||
tprops->state;
|
||||
|
||||
device->cache.ports[port - rdma_start_port(device)].subnet_prefix =
|
||||
tprops->subnet_prefix;
|
||||
write_unlock_irq(&device->cache.lock);
|
||||
|
||||
if (enforce_security)
|
||||
ib_security_cache_change(device,
|
||||
port,
|
||||
tprops->subnet_prefix);
|
||||
|
||||
kfree(gid_cache);
|
||||
kfree(old_pkey_cache);
|
||||
kfree(tprops);
|
||||
@@ -1126,7 +1155,9 @@ static void ib_cache_task(struct work_struct *_work)
|
||||
struct ib_update_work *work =
|
||||
container_of(_work, struct ib_update_work, work);
|
||||
|
||||
ib_cache_update(work->device, work->port_num);
|
||||
ib_cache_update(work->device,
|
||||
work->port_num,
|
||||
work->enforce_security);
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
@@ -1147,6 +1178,12 @@ static void ib_cache_event(struct ib_event_handler *handler,
|
||||
INIT_WORK(&work->work, ib_cache_task);
|
||||
work->device = event->device;
|
||||
work->port_num = event->element.port_num;
|
||||
if (event->event == IB_EVENT_PKEY_CHANGE ||
|
||||
event->event == IB_EVENT_GID_CHANGE)
|
||||
work->enforce_security = true;
|
||||
else
|
||||
work->enforce_security = false;
|
||||
|
||||
queue_work(ib_wq, &work->work);
|
||||
}
|
||||
}
|
||||
@@ -1172,7 +1209,7 @@ int ib_cache_setup_one(struct ib_device *device)
|
||||
goto out;
|
||||
|
||||
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
|
||||
ib_cache_update(device, p + rdma_start_port(device));
|
||||
ib_cache_update(device, p + rdma_start_port(device), true);
|
||||
|
||||
INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
|
||||
device, ib_cache_event);
|
||||
|
||||
@@ -38,6 +38,16 @@
|
||||
#include <linux/cgroup_rdma.h>
|
||||
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/ib_mad.h>
|
||||
#include "mad_priv.h"
|
||||
|
||||
struct pkey_index_qp_list {
|
||||
struct list_head pkey_index_list;
|
||||
u16 pkey_index;
|
||||
/* Lock to hold while iterating the qp_list. */
|
||||
spinlock_t qp_list_lock;
|
||||
struct list_head qp_list;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS)
|
||||
int cma_configfs_init(void);
|
||||
@@ -176,4 +186,109 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb,
|
||||
int ib_nl_handle_ip_res_resp(struct sk_buff *skb,
|
||||
struct netlink_callback *cb);
|
||||
|
||||
int ib_get_cached_subnet_prefix(struct ib_device *device,
|
||||
u8 port_num,
|
||||
u64 *sn_pfx);
|
||||
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
int ib_security_pkey_access(struct ib_device *dev,
|
||||
u8 port_num,
|
||||
u16 pkey_index,
|
||||
void *sec);
|
||||
|
||||
void ib_security_destroy_port_pkey_list(struct ib_device *device);
|
||||
|
||||
void ib_security_cache_change(struct ib_device *device,
|
||||
u8 port_num,
|
||||
u64 subnet_prefix);
|
||||
|
||||
int ib_security_modify_qp(struct ib_qp *qp,
|
||||
struct ib_qp_attr *qp_attr,
|
||||
int qp_attr_mask,
|
||||
struct ib_udata *udata);
|
||||
|
||||
int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev);
|
||||
void ib_destroy_qp_security_begin(struct ib_qp_security *sec);
|
||||
void ib_destroy_qp_security_abort(struct ib_qp_security *sec);
|
||||
void ib_destroy_qp_security_end(struct ib_qp_security *sec);
|
||||
int ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev);
|
||||
void ib_close_shared_qp_security(struct ib_qp_security *sec);
|
||||
int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
|
||||
enum ib_qp_type qp_type);
|
||||
void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent);
|
||||
int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index);
|
||||
#else
|
||||
static inline int ib_security_pkey_access(struct ib_device *dev,
|
||||
u8 port_num,
|
||||
u16 pkey_index,
|
||||
void *sec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ib_security_destroy_port_pkey_list(struct ib_device *device)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ib_security_cache_change(struct ib_device *device,
|
||||
u8 port_num,
|
||||
u64 subnet_prefix)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ib_security_modify_qp(struct ib_qp *qp,
|
||||
struct ib_qp_attr *qp_attr,
|
||||
int qp_attr_mask,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
return qp->device->modify_qp(qp->real_qp,
|
||||
qp_attr,
|
||||
qp_attr_mask,
|
||||
udata);
|
||||
}
|
||||
|
||||
static inline int ib_create_qp_security(struct ib_qp *qp,
|
||||
struct ib_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ib_destroy_qp_security_begin(struct ib_qp_security *sec)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ib_destroy_qp_security_abort(struct ib_qp_security *sec)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ib_destroy_qp_security_end(struct ib_qp_security *sec)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ib_open_shared_qp_security(struct ib_qp *qp,
|
||||
struct ib_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ib_close_shared_qp_security(struct ib_qp_security *sec)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
|
||||
enum ib_qp_type qp_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map,
|
||||
u16 pkey_index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* _CORE_PRIV_H */
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <rdma/rdma_netlink.h>
|
||||
#include <rdma/ib_addr.h>
|
||||
#include <rdma/ib_cache.h>
|
||||
@@ -82,6 +84,14 @@ static LIST_HEAD(client_list);
|
||||
static DEFINE_MUTEX(device_mutex);
|
||||
static DECLARE_RWSEM(lists_rwsem);
|
||||
|
||||
static int ib_security_change(struct notifier_block *nb, unsigned long event,
|
||||
void *lsm_data);
|
||||
static void ib_policy_change_task(struct work_struct *work);
|
||||
static DECLARE_WORK(ib_policy_change_work, ib_policy_change_task);
|
||||
|
||||
static struct notifier_block ibdev_lsm_nb = {
|
||||
.notifier_call = ib_security_change,
|
||||
};
|
||||
|
||||
static int ib_device_check_mandatory(struct ib_device *device)
|
||||
{
|
||||
@@ -325,6 +335,64 @@ void ib_get_device_fw_str(struct ib_device *dev, char *str, size_t str_len)
|
||||
}
|
||||
EXPORT_SYMBOL(ib_get_device_fw_str);
|
||||
|
||||
static int setup_port_pkey_list(struct ib_device *device)
|
||||
{
|
||||
int i;
|
||||
|
||||
/**
|
||||
* device->port_pkey_list is indexed directly by the port number,
|
||||
* Therefore it is declared as a 1 based array with potential empty
|
||||
* slots at the beginning.
|
||||
*/
|
||||
device->port_pkey_list = kcalloc(rdma_end_port(device) + 1,
|
||||
sizeof(*device->port_pkey_list),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!device->port_pkey_list)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < (rdma_end_port(device) + 1); i++) {
|
||||
spin_lock_init(&device->port_pkey_list[i].list_lock);
|
||||
INIT_LIST_HEAD(&device->port_pkey_list[i].pkey_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ib_policy_change_task(struct work_struct *work)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
|
||||
down_read(&lists_rwsem);
|
||||
list_for_each_entry(dev, &device_list, core_list) {
|
||||
int i;
|
||||
|
||||
for (i = rdma_start_port(dev); i <= rdma_end_port(dev); i++) {
|
||||
u64 sp;
|
||||
int ret = ib_get_cached_subnet_prefix(dev,
|
||||
i,
|
||||
&sp);
|
||||
|
||||
WARN_ONCE(ret,
|
||||
"ib_get_cached_subnet_prefix err: %d, this should never happen here\n",
|
||||
ret);
|
||||
ib_security_cache_change(dev, i, sp);
|
||||
}
|
||||
}
|
||||
up_read(&lists_rwsem);
|
||||
}
|
||||
|
||||
static int ib_security_change(struct notifier_block *nb, unsigned long event,
|
||||
void *lsm_data)
|
||||
{
|
||||
if (event != LSM_POLICY_CHANGE)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
schedule_work(&ib_policy_change_work);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* ib_register_device - Register an IB device with IB core
|
||||
* @device:Device to register
|
||||
@@ -385,6 +453,12 @@ int ib_register_device(struct ib_device *device,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = setup_port_pkey_list(device);
|
||||
if (ret) {
|
||||
pr_warn("Couldn't create per port_pkey_list\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ib_cache_setup_one(device);
|
||||
if (ret) {
|
||||
pr_warn("Couldn't set up InfiniBand P_Key/GID cache\n");
|
||||
@@ -468,6 +542,9 @@ void ib_unregister_device(struct ib_device *device)
|
||||
ib_device_unregister_sysfs(device);
|
||||
ib_cache_cleanup_one(device);
|
||||
|
||||
ib_security_destroy_port_pkey_list(device);
|
||||
kfree(device->port_pkey_list);
|
||||
|
||||
down_write(&lists_rwsem);
|
||||
spin_lock_irqsave(&device->client_data_lock, flags);
|
||||
list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
|
||||
@@ -1082,10 +1159,18 @@ static int __init ib_core_init(void)
|
||||
goto err_sa;
|
||||
}
|
||||
|
||||
ret = register_lsm_notifier(&ibdev_lsm_nb);
|
||||
if (ret) {
|
||||
pr_warn("Couldn't register LSM notifier. ret %d\n", ret);
|
||||
goto err_ibnl_clients;
|
||||
}
|
||||
|
||||
ib_cache_setup();
|
||||
|
||||
return 0;
|
||||
|
||||
err_ibnl_clients:
|
||||
ib_remove_ibnl_clients();
|
||||
err_sa:
|
||||
ib_sa_cleanup();
|
||||
err_mad:
|
||||
@@ -1105,6 +1190,7 @@ err:
|
||||
|
||||
static void __exit ib_core_cleanup(void)
|
||||
{
|
||||
unregister_lsm_notifier(&ibdev_lsm_nb);
|
||||
ib_cache_cleanup();
|
||||
ib_remove_ibnl_clients();
|
||||
ib_sa_cleanup();
|
||||
|
||||
@@ -40,9 +40,11 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/security.h>
|
||||
#include <rdma/ib_cache.h>
|
||||
|
||||
#include "mad_priv.h"
|
||||
#include "core_priv.h"
|
||||
#include "mad_rmpp.h"
|
||||
#include "smi.h"
|
||||
#include "opa_smi.h"
|
||||
@@ -369,6 +371,12 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
|
||||
atomic_set(&mad_agent_priv->refcount, 1);
|
||||
init_completion(&mad_agent_priv->comp);
|
||||
|
||||
ret2 = ib_mad_agent_security_setup(&mad_agent_priv->agent, qp_type);
|
||||
if (ret2) {
|
||||
ret = ERR_PTR(ret2);
|
||||
goto error4;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&port_priv->reg_lock, flags);
|
||||
mad_agent_priv->agent.hi_tid = ++ib_mad_client_id;
|
||||
|
||||
@@ -386,7 +394,7 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
|
||||
if (method) {
|
||||
if (method_in_use(&method,
|
||||
mad_reg_req))
|
||||
goto error4;
|
||||
goto error5;
|
||||
}
|
||||
}
|
||||
ret2 = add_nonoui_reg_req(mad_reg_req, mad_agent_priv,
|
||||
@@ -402,14 +410,14 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
|
||||
if (is_vendor_method_in_use(
|
||||
vendor_class,
|
||||
mad_reg_req))
|
||||
goto error4;
|
||||
goto error5;
|
||||
}
|
||||
}
|
||||
ret2 = add_oui_reg_req(mad_reg_req, mad_agent_priv);
|
||||
}
|
||||
if (ret2) {
|
||||
ret = ERR_PTR(ret2);
|
||||
goto error4;
|
||||
goto error5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,9 +426,10 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
|
||||
spin_unlock_irqrestore(&port_priv->reg_lock, flags);
|
||||
|
||||
return &mad_agent_priv->agent;
|
||||
|
||||
error4:
|
||||
error5:
|
||||
spin_unlock_irqrestore(&port_priv->reg_lock, flags);
|
||||
ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
|
||||
error4:
|
||||
kfree(reg_req);
|
||||
error3:
|
||||
kfree(mad_agent_priv);
|
||||
@@ -491,6 +500,7 @@ struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
|
||||
struct ib_mad_agent *ret;
|
||||
struct ib_mad_snoop_private *mad_snoop_priv;
|
||||
int qpn;
|
||||
int err;
|
||||
|
||||
/* Validate parameters */
|
||||
if ((is_snooping_sends(mad_snoop_flags) && !snoop_handler) ||
|
||||
@@ -525,17 +535,25 @@ struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
|
||||
mad_snoop_priv->agent.port_num = port_num;
|
||||
mad_snoop_priv->mad_snoop_flags = mad_snoop_flags;
|
||||
init_completion(&mad_snoop_priv->comp);
|
||||
|
||||
err = ib_mad_agent_security_setup(&mad_snoop_priv->agent, qp_type);
|
||||
if (err) {
|
||||
ret = ERR_PTR(err);
|
||||
goto error2;
|
||||
}
|
||||
|
||||
mad_snoop_priv->snoop_index = register_snoop_agent(
|
||||
&port_priv->qp_info[qpn],
|
||||
mad_snoop_priv);
|
||||
if (mad_snoop_priv->snoop_index < 0) {
|
||||
ret = ERR_PTR(mad_snoop_priv->snoop_index);
|
||||
goto error2;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
atomic_set(&mad_snoop_priv->refcount, 1);
|
||||
return &mad_snoop_priv->agent;
|
||||
|
||||
error3:
|
||||
ib_mad_agent_security_cleanup(&mad_snoop_priv->agent);
|
||||
error2:
|
||||
kfree(mad_snoop_priv);
|
||||
error1:
|
||||
@@ -581,6 +599,8 @@ static void unregister_mad_agent(struct ib_mad_agent_private *mad_agent_priv)
|
||||
deref_mad_agent(mad_agent_priv);
|
||||
wait_for_completion(&mad_agent_priv->comp);
|
||||
|
||||
ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
|
||||
|
||||
kfree(mad_agent_priv->reg_req);
|
||||
kfree(mad_agent_priv);
|
||||
}
|
||||
@@ -599,6 +619,8 @@ static void unregister_mad_snoop(struct ib_mad_snoop_private *mad_snoop_priv)
|
||||
deref_snoop_agent(mad_snoop_priv);
|
||||
wait_for_completion(&mad_snoop_priv->comp);
|
||||
|
||||
ib_mad_agent_security_cleanup(&mad_snoop_priv->agent);
|
||||
|
||||
kfree(mad_snoop_priv);
|
||||
}
|
||||
|
||||
@@ -1215,12 +1237,16 @@ int ib_post_send_mad(struct ib_mad_send_buf *send_buf,
|
||||
|
||||
/* Walk list of send WRs and post each on send list */
|
||||
for (; send_buf; send_buf = next_send_buf) {
|
||||
|
||||
mad_send_wr = container_of(send_buf,
|
||||
struct ib_mad_send_wr_private,
|
||||
send_buf);
|
||||
mad_agent_priv = mad_send_wr->mad_agent_priv;
|
||||
|
||||
ret = ib_mad_enforce_security(mad_agent_priv,
|
||||
mad_send_wr->send_wr.pkey_index);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
if (!send_buf->mad_agent->send_handler ||
|
||||
(send_buf->timeout_ms &&
|
||||
!send_buf->mad_agent->recv_handler)) {
|
||||
@@ -1946,6 +1972,14 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
|
||||
struct ib_mad_send_wr_private *mad_send_wr;
|
||||
struct ib_mad_send_wc mad_send_wc;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = ib_mad_enforce_security(mad_agent_priv,
|
||||
mad_recv_wc->wc->pkey_index);
|
||||
if (ret) {
|
||||
ib_free_recv_mad(mad_recv_wc);
|
||||
deref_mad_agent(mad_agent_priv);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&mad_recv_wc->rmpp_list);
|
||||
list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list);
|
||||
@@ -2003,6 +2037,8 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
|
||||
mad_recv_wc);
|
||||
deref_mad_agent(mad_agent_priv);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1508,6 +1508,10 @@ static int create_qp(struct ib_uverbs_file *file,
|
||||
}
|
||||
|
||||
if (cmd->qp_type != IB_QPT_XRC_TGT) {
|
||||
ret = ib_create_qp_security(qp, device);
|
||||
if (ret)
|
||||
goto err_cb;
|
||||
|
||||
qp->real_qp = qp;
|
||||
qp->device = device;
|
||||
qp->pd = pd;
|
||||
@@ -2002,14 +2006,17 @@ static int modify_qp(struct ib_uverbs_file *file,
|
||||
if (ret)
|
||||
goto release_qp;
|
||||
}
|
||||
ret = qp->device->modify_qp(qp, attr,
|
||||
ret = ib_security_modify_qp(qp,
|
||||
attr,
|
||||
modify_qp_mask(qp->qp_type,
|
||||
cmd->base.attr_mask),
|
||||
udata);
|
||||
} else {
|
||||
ret = ib_modify_qp(qp, attr,
|
||||
modify_qp_mask(qp->qp_type,
|
||||
cmd->base.attr_mask));
|
||||
ret = ib_security_modify_qp(qp,
|
||||
attr,
|
||||
modify_qp_mask(qp->qp_type,
|
||||
cmd->base.attr_mask),
|
||||
NULL);
|
||||
}
|
||||
|
||||
release_qp:
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/ib_cache.h>
|
||||
@@ -713,11 +714,19 @@ static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp,
|
||||
{
|
||||
struct ib_qp *qp;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
qp = kzalloc(sizeof *qp, GFP_KERNEL);
|
||||
if (!qp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
qp->real_qp = real_qp;
|
||||
err = ib_open_shared_qp_security(qp, real_qp->device);
|
||||
if (err) {
|
||||
kfree(qp);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
qp->real_qp = real_qp;
|
||||
atomic_inc(&real_qp->usecnt);
|
||||
qp->device = real_qp->device;
|
||||
@@ -804,6 +813,12 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
|
||||
if (IS_ERR(qp))
|
||||
return qp;
|
||||
|
||||
ret = ib_create_qp_security(qp, device);
|
||||
if (ret) {
|
||||
ib_destroy_qp(qp);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
qp->device = device;
|
||||
qp->real_qp = qp;
|
||||
qp->uobject = NULL;
|
||||
@@ -1266,7 +1281,7 @@ int ib_modify_qp(struct ib_qp *qp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
return qp->device->modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL);
|
||||
return ib_security_modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(ib_modify_qp);
|
||||
|
||||
@@ -1295,6 +1310,7 @@ int ib_close_qp(struct ib_qp *qp)
|
||||
spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
|
||||
|
||||
atomic_dec(&real_qp->usecnt);
|
||||
ib_close_shared_qp_security(qp->qp_sec);
|
||||
kfree(qp);
|
||||
|
||||
return 0;
|
||||
@@ -1335,6 +1351,7 @@ int ib_destroy_qp(struct ib_qp *qp)
|
||||
struct ib_cq *scq, *rcq;
|
||||
struct ib_srq *srq;
|
||||
struct ib_rwq_ind_table *ind_tbl;
|
||||
struct ib_qp_security *sec;
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(qp->mrs_used > 0);
|
||||
@@ -1350,6 +1367,9 @@ int ib_destroy_qp(struct ib_qp *qp)
|
||||
rcq = qp->recv_cq;
|
||||
srq = qp->srq;
|
||||
ind_tbl = qp->rwq_ind_tbl;
|
||||
sec = qp->qp_sec;
|
||||
if (sec)
|
||||
ib_destroy_qp_security_begin(sec);
|
||||
|
||||
if (!qp->uobject)
|
||||
rdma_rw_cleanup_mrs(qp);
|
||||
@@ -1366,6 +1386,11 @@ int ib_destroy_qp(struct ib_qp *qp)
|
||||
atomic_dec(&srq->usecnt);
|
||||
if (ind_tbl)
|
||||
atomic_dec(&ind_tbl->usecnt);
|
||||
if (sec)
|
||||
ib_destroy_qp_security_end(sec);
|
||||
} else {
|
||||
if (sec)
|
||||
ib_destroy_qp_security_abort(sec);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
+16
-1
@@ -2544,10 +2544,25 @@ EXPORT_SYMBOL_GPL(nfs_set_sb_security);
|
||||
int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
|
||||
struct nfs_mount_info *mount_info)
|
||||
{
|
||||
int error;
|
||||
unsigned long kflags = 0, kflags_out = 0;
|
||||
|
||||
/* clone any lsm security options from the parent to the new sb */
|
||||
if (d_inode(mntroot)->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops)
|
||||
return -ESTALE;
|
||||
return security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
|
||||
|
||||
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
|
||||
kflags |= SECURITY_LSM_NATIVE_LABELS;
|
||||
|
||||
error = security_sb_clone_mnt_opts(mount_info->cloned->sb, s, kflags,
|
||||
&kflags_out);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
|
||||
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
|
||||
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <linux/path.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
|
||||
struct lsm_network_audit {
|
||||
int netif;
|
||||
@@ -45,6 +46,16 @@ struct lsm_ioctlop_audit {
|
||||
u16 cmd;
|
||||
};
|
||||
|
||||
struct lsm_ibpkey_audit {
|
||||
u64 subnet_prefix;
|
||||
u16 pkey;
|
||||
};
|
||||
|
||||
struct lsm_ibendport_audit {
|
||||
char dev_name[IB_DEVICE_NAME_MAX];
|
||||
u8 port;
|
||||
};
|
||||
|
||||
/* Auxiliary data to use in generating the audit record. */
|
||||
struct common_audit_data {
|
||||
char type;
|
||||
@@ -60,6 +71,8 @@ struct common_audit_data {
|
||||
#define LSM_AUDIT_DATA_DENTRY 10
|
||||
#define LSM_AUDIT_DATA_IOCTL_OP 11
|
||||
#define LSM_AUDIT_DATA_FILE 12
|
||||
#define LSM_AUDIT_DATA_IBPKEY 13
|
||||
#define LSM_AUDIT_DATA_IBENDPORT 14
|
||||
union {
|
||||
struct path path;
|
||||
struct dentry *dentry;
|
||||
@@ -77,6 +90,8 @@ struct common_audit_data {
|
||||
char *kmod_name;
|
||||
struct lsm_ioctlop_audit *op;
|
||||
struct file *file;
|
||||
struct lsm_ibpkey_audit *ibpkey;
|
||||
struct lsm_ibendport_audit *ibendport;
|
||||
} u;
|
||||
/* this union contains LSM specific data */
|
||||
union {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
|
||||
* Copyright (C) 2015 Intel Corporation.
|
||||
* Copyright (C) 2015 Casey Schaufler <casey@schaufler-ca.com>
|
||||
* Copyright (C) 2016 Mellanox Techonologies
|
||||
*
|
||||
* 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
|
||||
@@ -911,6 +912,26 @@
|
||||
* associated with the TUN device's security structure.
|
||||
* @security pointer to the TUN devices's security structure.
|
||||
*
|
||||
* Security hooks for Infiniband
|
||||
*
|
||||
* @ib_pkey_access:
|
||||
* Check permission to access a pkey when modifing a QP.
|
||||
* @subnet_prefix the subnet prefix of the port being used.
|
||||
* @pkey the pkey to be accessed.
|
||||
* @sec pointer to a security structure.
|
||||
* @ib_endport_manage_subnet:
|
||||
* Check permissions to send and receive SMPs on a end port.
|
||||
* @dev_name the IB device name (i.e. mlx4_0).
|
||||
* @port_num the port number.
|
||||
* @sec pointer to a security structure.
|
||||
* @ib_alloc_security:
|
||||
* Allocate a security structure for Infiniband objects.
|
||||
* @sec pointer to a security structure pointer.
|
||||
* Returns 0 on success, non-zero on failure
|
||||
* @ib_free_security:
|
||||
* Deallocate an Infiniband security structure.
|
||||
* @sec contains the security structure to be freed.
|
||||
*
|
||||
* Security hooks for XFRM operations.
|
||||
*
|
||||
* @xfrm_policy_alloc_security:
|
||||
@@ -1388,7 +1409,9 @@ union security_list_options {
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags);
|
||||
int (*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 (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
|
||||
int (*dentry_init_security)(struct dentry *dentry, int mode,
|
||||
const struct qstr *name, void **ctx,
|
||||
@@ -1620,6 +1643,14 @@ union security_list_options {
|
||||
int (*tun_dev_open)(void *security);
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
int (*ib_pkey_access)(void *sec, u64 subnet_prefix, u16 pkey);
|
||||
int (*ib_endport_manage_subnet)(void *sec, const char *dev_name,
|
||||
u8 port_num);
|
||||
int (*ib_alloc_security)(void **sec);
|
||||
void (*ib_free_security)(void *sec);
|
||||
#endif /* CONFIG_SECURITY_INFINIBAND */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp,
|
||||
struct xfrm_user_sec_ctx *sec_ctx,
|
||||
@@ -1851,6 +1882,12 @@ struct security_hook_heads {
|
||||
struct list_head tun_dev_attach;
|
||||
struct list_head tun_dev_open;
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
struct list_head ib_pkey_access;
|
||||
struct list_head ib_endport_manage_subnet;
|
||||
struct list_head ib_alloc_security;
|
||||
struct list_head ib_free_security;
|
||||
#endif /* CONFIG_SECURITY_INFINIBAND */
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
struct list_head xfrm_policy_alloc_security;
|
||||
struct list_head xfrm_policy_clone_security;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
|
||||
* Copyright (C) 2001 James Morris <jmorris@intercode.com.au>
|
||||
* Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
|
||||
* Copyright (C) 2016 Mellanox Techonologies
|
||||
*
|
||||
* 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
|
||||
@@ -68,6 +69,10 @@ struct audit_krule;
|
||||
struct user_namespace;
|
||||
struct timezone;
|
||||
|
||||
enum lsm_event {
|
||||
LSM_POLICY_CHANGE,
|
||||
};
|
||||
|
||||
/* These functions are in security/commoncap.c */
|
||||
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap, int audit);
|
||||
@@ -163,6 +168,10 @@ struct security_mnt_opts {
|
||||
int num_mnt_opts;
|
||||
};
|
||||
|
||||
int call_lsm_notifier(enum lsm_event event, void *data);
|
||||
int register_lsm_notifier(struct notifier_block *nb);
|
||||
int unregister_lsm_notifier(struct notifier_block *nb);
|
||||
|
||||
static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
|
||||
{
|
||||
opts->mnt_opts = NULL;
|
||||
@@ -240,7 +249,9 @@ int security_sb_set_mnt_opts(struct super_block *sb,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags);
|
||||
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);
|
||||
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
|
||||
int security_dentry_init_security(struct dentry *dentry, int mode,
|
||||
const struct qstr *name, void **ctx,
|
||||
@@ -381,6 +392,21 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
|
||||
struct security_mnt_opts {
|
||||
};
|
||||
|
||||
static inline int call_lsm_notifier(enum lsm_event event, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int register_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int unregister_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
|
||||
{
|
||||
}
|
||||
@@ -581,7 +607,9 @@ static inline int security_sb_set_mnt_opts(struct super_block *sb,
|
||||
}
|
||||
|
||||
static inline 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 0;
|
||||
}
|
||||
@@ -1406,6 +1434,32 @@ static inline int security_tun_dev_open(void *security)
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey);
|
||||
int security_ib_endport_manage_subnet(void *sec, const char *name, u8 port_num);
|
||||
int security_ib_alloc_security(void **sec);
|
||||
void security_ib_free_security(void *sec);
|
||||
#else /* CONFIG_SECURITY_INFINIBAND */
|
||||
static inline int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_ib_endport_manage_subnet(void *sec, const char *dev_name, u8 port_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_ib_alloc_security(void **sec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_ib_free_security(void *sec)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_INFINIBAND */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
|
||||
int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
|
||||
|
||||
@@ -575,6 +575,10 @@ struct ib_mad_agent {
|
||||
u32 flags;
|
||||
u8 port_num;
|
||||
u8 rmpp_version;
|
||||
void *security;
|
||||
bool smp_allowed;
|
||||
bool lsm_nb_reg;
|
||||
struct notifier_block lsm_nb;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1614,6 +1614,45 @@ struct ib_rwq_ind_table_init_attr {
|
||||
struct ib_wq **ind_tbl;
|
||||
};
|
||||
|
||||
enum port_pkey_state {
|
||||
IB_PORT_PKEY_NOT_VALID = 0,
|
||||
IB_PORT_PKEY_VALID = 1,
|
||||
IB_PORT_PKEY_LISTED = 2,
|
||||
};
|
||||
|
||||
struct ib_qp_security;
|
||||
|
||||
struct ib_port_pkey {
|
||||
enum port_pkey_state state;
|
||||
u16 pkey_index;
|
||||
u8 port_num;
|
||||
struct list_head qp_list;
|
||||
struct list_head to_error_list;
|
||||
struct ib_qp_security *sec;
|
||||
};
|
||||
|
||||
struct ib_ports_pkeys {
|
||||
struct ib_port_pkey main;
|
||||
struct ib_port_pkey alt;
|
||||
};
|
||||
|
||||
struct ib_qp_security {
|
||||
struct ib_qp *qp;
|
||||
struct ib_device *dev;
|
||||
/* Hold this mutex when changing port and pkey settings. */
|
||||
struct mutex mutex;
|
||||
struct ib_ports_pkeys *ports_pkeys;
|
||||
/* A list of all open shared QP handles. Required to enforce security
|
||||
* properly for all users of a shared QP.
|
||||
*/
|
||||
struct list_head shared_qp_list;
|
||||
void *security;
|
||||
bool destroying;
|
||||
atomic_t error_list_count;
|
||||
struct completion error_complete;
|
||||
int error_comps_pending;
|
||||
};
|
||||
|
||||
/*
|
||||
* @max_write_sge: Maximum SGE elements per RDMA WRITE request.
|
||||
* @max_read_sge: Maximum SGE elements per RDMA READ request.
|
||||
@@ -1643,6 +1682,7 @@ struct ib_qp {
|
||||
u32 max_read_sge;
|
||||
enum ib_qp_type qp_type;
|
||||
struct ib_rwq_ind_table *rwq_ind_tbl;
|
||||
struct ib_qp_security *qp_sec;
|
||||
};
|
||||
|
||||
struct ib_mr {
|
||||
@@ -1891,6 +1931,7 @@ enum ib_mad_result {
|
||||
};
|
||||
|
||||
struct ib_port_cache {
|
||||
u64 subnet_prefix;
|
||||
struct ib_pkey_cache *pkey;
|
||||
struct ib_gid_table *gid;
|
||||
u8 lmc;
|
||||
@@ -1940,6 +1981,12 @@ struct rdma_netdev {
|
||||
union ib_gid *gid, u16 mlid);
|
||||
};
|
||||
|
||||
struct ib_port_pkey_list {
|
||||
/* Lock to hold while modifying the list. */
|
||||
spinlock_t list_lock;
|
||||
struct list_head pkey_list;
|
||||
};
|
||||
|
||||
struct ib_device {
|
||||
/* Do not access @dma_device directly from ULP nor from HW drivers. */
|
||||
struct device *dma_device;
|
||||
@@ -1963,6 +2010,8 @@ struct ib_device {
|
||||
|
||||
int num_comp_vectors;
|
||||
|
||||
struct ib_port_pkey_list *port_pkey_list;
|
||||
|
||||
struct iw_cm_verbs *iwcm;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) */
|
||||
}
|
||||
|
||||
|
||||
+53
-2
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+176
-36
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user