Commit 5965453d authored by James Morris's avatar James Morris

Merge branch 'stable-4.13' of git://git.infradead.org/users/pcmoore/selinux into next

parents fc26bd50 6a391183
...@@ -10,7 +10,8 @@ obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o \ ...@@ -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 \ 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 \ device.o fmr_pool.o cache.o netlink.o \
roce_gid_mgmt.o mr_pool.o addr.o sa_query.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_USER_MEM) += umem.o
ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o
......
...@@ -53,6 +53,7 @@ struct ib_update_work { ...@@ -53,6 +53,7 @@ struct ib_update_work {
struct work_struct work; struct work_struct work;
struct ib_device *device; struct ib_device *device;
u8 port_num; u8 port_num;
bool enforce_security;
}; };
union ib_gid zgid; union ib_gid zgid;
...@@ -911,6 +912,26 @@ int ib_get_cached_pkey(struct ib_device *device, ...@@ -911,6 +912,26 @@ int ib_get_cached_pkey(struct ib_device *device,
} }
EXPORT_SYMBOL(ib_get_cached_pkey); 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, int ib_find_cached_pkey(struct ib_device *device,
u8 port_num, u8 port_num,
u16 pkey, u16 pkey,
...@@ -1022,7 +1043,8 @@ int ib_get_cached_port_state(struct ib_device *device, ...@@ -1022,7 +1043,8 @@ int ib_get_cached_port_state(struct ib_device *device,
EXPORT_SYMBOL(ib_get_cached_port_state); EXPORT_SYMBOL(ib_get_cached_port_state);
static void ib_cache_update(struct ib_device *device, static void ib_cache_update(struct ib_device *device,
u8 port) u8 port,
bool enforce_security)
{ {
struct ib_port_attr *tprops = NULL; struct ib_port_attr *tprops = NULL;
struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache; struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache;
...@@ -1108,8 +1130,15 @@ static void ib_cache_update(struct ib_device *device, ...@@ -1108,8 +1130,15 @@ static void ib_cache_update(struct ib_device *device,
device->cache.ports[port - rdma_start_port(device)].port_state = device->cache.ports[port - rdma_start_port(device)].port_state =
tprops->state; tprops->state;
device->cache.ports[port - rdma_start_port(device)].subnet_prefix =
tprops->subnet_prefix;
write_unlock_irq(&device->cache.lock); write_unlock_irq(&device->cache.lock);
if (enforce_security)
ib_security_cache_change(device,
port,
tprops->subnet_prefix);
kfree(gid_cache); kfree(gid_cache);
kfree(old_pkey_cache); kfree(old_pkey_cache);
kfree(tprops); kfree(tprops);
...@@ -1126,7 +1155,9 @@ static void ib_cache_task(struct work_struct *_work) ...@@ -1126,7 +1155,9 @@ static void ib_cache_task(struct work_struct *_work)
struct ib_update_work *work = struct ib_update_work *work =
container_of(_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); kfree(work);
} }
...@@ -1147,6 +1178,12 @@ static void ib_cache_event(struct ib_event_handler *handler, ...@@ -1147,6 +1178,12 @@ static void ib_cache_event(struct ib_event_handler *handler,
INIT_WORK(&work->work, ib_cache_task); INIT_WORK(&work->work, ib_cache_task);
work->device = event->device; work->device = event->device;
work->port_num = event->element.port_num; 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); queue_work(ib_wq, &work->work);
} }
} }
...@@ -1172,7 +1209,7 @@ int ib_cache_setup_one(struct ib_device *device) ...@@ -1172,7 +1209,7 @@ int ib_cache_setup_one(struct ib_device *device)
goto out; goto out;
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) 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, INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
device, ib_cache_event); device, ib_cache_event);
......
...@@ -38,6 +38,16 @@ ...@@ -38,6 +38,16 @@
#include <linux/cgroup_rdma.h> #include <linux/cgroup_rdma.h>
#include <rdma/ib_verbs.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) #if IS_ENABLED(CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS)
int cma_configfs_init(void); int cma_configfs_init(void);
...@@ -176,4 +186,109 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb, ...@@ -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, int ib_nl_handle_ip_res_resp(struct sk_buff *skb,
struct netlink_callback *cb); 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 */ #endif /* _CORE_PRIV_H */
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/security.h>
#include <linux/notifier.h>
#include <rdma/rdma_netlink.h> #include <rdma/rdma_netlink.h>
#include <rdma/ib_addr.h> #include <rdma/ib_addr.h>
#include <rdma/ib_cache.h> #include <rdma/ib_cache.h>
...@@ -82,6 +84,14 @@ static LIST_HEAD(client_list); ...@@ -82,6 +84,14 @@ static LIST_HEAD(client_list);
static DEFINE_MUTEX(device_mutex); static DEFINE_MUTEX(device_mutex);
static DECLARE_RWSEM(lists_rwsem); 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) 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) ...@@ -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); 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 * ib_register_device - Register an IB device with IB core
* @device:Device to register * @device:Device to register
...@@ -385,6 +453,12 @@ int ib_register_device(struct ib_device *device, ...@@ -385,6 +453,12 @@ int ib_register_device(struct ib_device *device,
goto out; 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); ret = ib_cache_setup_one(device);
if (ret) { if (ret) {
pr_warn("Couldn't set up InfiniBand P_Key/GID cache\n"); pr_warn("Couldn't set up InfiniBand P_Key/GID cache\n");
...@@ -468,6 +542,9 @@ void ib_unregister_device(struct ib_device *device) ...@@ -468,6 +542,9 @@ void ib_unregister_device(struct ib_device *device)
ib_device_unregister_sysfs(device); ib_device_unregister_sysfs(device);
ib_cache_cleanup_one(device); ib_cache_cleanup_one(device);
ib_security_destroy_port_pkey_list(device);
kfree(device->port_pkey_list);
down_write(&lists_rwsem); down_write(&lists_rwsem);
spin_lock_irqsave(&device->client_data_lock, flags); spin_lock_irqsave(&device->client_data_lock, flags);
list_for_each_entry_safe(context, tmp, &device->client_data_list, list) list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
...@@ -1082,10 +1159,18 @@ static int __init ib_core_init(void) ...@@ -1082,10 +1159,18 @@ static int __init ib_core_init(void)
goto err_sa; 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(); ib_cache_setup();
return 0; return 0;
err_ibnl_clients:
ib_remove_ibnl_clients();
err_sa: err_sa:
ib_sa_cleanup(); ib_sa_cleanup();
err_mad: err_mad:
...@@ -1105,6 +1190,7 @@ static int __init ib_core_init(void) ...@@ -1105,6 +1190,7 @@ static int __init ib_core_init(void)
static void __exit ib_core_cleanup(void) static void __exit ib_core_cleanup(void)
{ {
unregister_lsm_notifier(&ibdev_lsm_nb);
ib_cache_cleanup(); ib_cache_cleanup();
ib_remove_ibnl_clients(); ib_remove_ibnl_clients();
ib_sa_cleanup(); ib_sa_cleanup();
......
...@@ -40,9 +40,11 @@ ...@@ -40,9 +40,11 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/security.h>
#include <rdma/ib_cache.h> #include <rdma/ib_cache.h>
#include "mad_priv.h" #include "mad_priv.h"
#include "core_priv.h"
#include "mad_rmpp.h" #include "mad_rmpp.h"
#include "smi.h" #include "smi.h"
#include "opa_smi.h" #include "opa_smi.h"
...@@ -369,6 +371,12 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device, ...@@ -369,6 +371,12 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
atomic_set(&mad_agent_priv->refcount, 1); atomic_set(&mad_agent_priv->refcount, 1);
init_completion(&mad_agent_priv->comp); 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); spin_lock_irqsave(&port_priv->reg_lock, flags);
mad_agent_priv->agent.hi_tid = ++ib_mad_client_id; 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, ...@@ -386,7 +394,7 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
if (method) { if (method) {
if (method_in_use(&method, if (method_in_use(&method,
mad_reg_req)) mad_reg_req))
goto error4; goto error5;
} }
} }
ret2 = add_nonoui_reg_req(mad_reg_req, mad_agent_priv, 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, ...@@ -402,14 +410,14 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
if (is_vendor_method_in_use( if (is_vendor_method_in_use(
vendor_class, vendor_class,
mad_reg_req)) mad_reg_req))
goto error4; goto error5;
} }
} }
ret2 = add_oui_reg_req(mad_reg_req, mad_agent_priv); ret2 = add_oui_reg_req(mad_reg_req, mad_agent_priv);
} }
if (ret2) { if (ret2) {
ret = ERR_PTR(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, ...@@ -418,9 +426,10 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
spin_unlock_irqrestore(&port_priv->reg_lock, flags); spin_unlock_irqrestore(&port_priv->reg_lock, flags);
return &mad_agent_priv->agent; return &mad_agent_priv->agent;
error5:
error4:
spin_unlock_irqrestore(&port_priv->reg_lock, flags); spin_unlock_irqrestore(&port_priv->reg_lock, flags);
ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
error4:
kfree(reg_req); kfree(reg_req);
error3: error3:
kfree(mad_agent_priv); kfree(mad_agent_priv);
...@@ -491,6 +500,7 @@ struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device, ...@@ -491,6 +500,7 @@ struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
struct ib_mad_agent *ret; struct ib_mad_agent *ret;
struct ib_mad_snoop_private *mad_snoop_priv; struct ib_mad_snoop_private *mad_snoop_priv;
int qpn; int qpn;
int err;
/* Validate parameters */ /* Validate parameters */
if ((is_snooping_sends(mad_snoop_flags) && !snoop_handler) || 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, ...@@ -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->agent.port_num = port_num;
mad_snoop_priv->mad_snoop_flags = mad_snoop_flags; mad_snoop_priv->mad_snoop_flags = mad_snoop_flags;
init_completion(&mad_snoop_priv->comp); 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( mad_snoop_priv->snoop_index = register_snoop_agent(
&port_priv->qp_info[qpn], &port_priv->qp_info[qpn],
mad_snoop_priv); mad_snoop_priv);
if (mad_snoop_priv->snoop_index < 0) { if (mad_snoop_priv->snoop_index < 0) {
ret = ERR_PTR(mad_snoop_priv->snoop_index); ret = ERR_PTR(mad_snoop_priv->snoop_index);
goto error2; goto error3;
} }
atomic_set(&mad_snoop_priv->refcount, 1); atomic_set(&mad_snoop_priv->refcount, 1);
return &mad_snoop_priv->agent; return &mad_snoop_priv->agent;
error3:
ib_mad_agent_security_cleanup(&mad_snoop_priv->agent);
error2: error2:
kfree(mad_snoop_priv); kfree(mad_snoop_priv);
error1: error1:
...@@ -581,6 +599,8 @@ static void unregister_mad_agent(struct ib_mad_agent_private *mad_agent_priv) ...@@ -581,6 +599,8 @@ static void unregister_mad_agent(struct ib_mad_agent_private *mad_agent_priv)
deref_mad_agent(mad_agent_priv); deref_mad_agent(mad_agent_priv);
wait_for_completion(&mad_agent_priv->comp); 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->reg_req);
kfree(mad_agent_priv); kfree(mad_agent_priv);
} }
...@@ -599,6 +619,8 @@ static void unregister_mad_snoop(struct ib_mad_snoop_private *mad_snoop_priv) ...@@ -599,6 +619,8 @@ static void unregister_mad_snoop(struct ib_mad_snoop_private *mad_snoop_priv)
deref_snoop_agent(mad_snoop_priv); deref_snoop_agent(mad_snoop_priv);
wait_for_completion(&mad_snoop_priv->comp); wait_for_completion(&mad_snoop_priv->comp);
ib_mad_agent_security_cleanup(&mad_snoop_priv->agent);
kfree(mad_snoop_priv); kfree(mad_snoop_priv);
} }
...@@ -1215,12 +1237,16 @@ int ib_post_send_mad(struct ib_mad_send_buf *send_buf, ...@@ -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 */ /* Walk list of send WRs and post each on send list */
for (; send_buf; send_buf = next_send_buf) { for (; send_buf; send_buf = next_send_buf) {
mad_send_wr = container_of(send_buf, mad_send_wr = container_of(send_buf,
struct ib_mad_send_wr_private, struct ib_mad_send_wr_private,
send_buf); send_buf);
mad_agent_priv = mad_send_wr->mad_agent_priv; 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 || if (!send_buf->mad_agent->send_handler ||
(send_buf->timeout_ms && (send_buf->timeout_ms &&
!send_buf->mad_agent->recv_handler)) { !send_buf->mad_agent->recv_handler)) {
...@@ -1946,6 +1972,14 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, ...@@ -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_wr_private *mad_send_wr;
struct ib_mad_send_wc mad_send_wc; struct ib_mad_send_wc mad_send_wc;
unsigned long flags; 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); INIT_LIST_HEAD(&mad_recv_wc->rmpp_list);
list_add(&mad_recv_wc->recv_buf.list, &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, ...@@ -2003,6 +2037,8 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
mad_recv_wc); mad_recv_wc);
deref_mad_agent(mad_agent_priv); deref_mad_agent(mad_agent_priv);
} }
return;
} }
static enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv, static enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv,
......
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef CONFIG_SECURITY_INFINIBAND
#include <linux/security.h>
#include <linux/completion.h>
#include <linux/list.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
#include "core_priv.h"
#include "mad_priv.h"
static struct pkey_index_qp_list *get_pkey_idx_qp_list(struct ib_port_pkey *pp)
{
struct pkey_index_qp_list *pkey = NULL;
struct pkey_index_qp_list *tmp_pkey;
struct ib_device *dev = pp->sec->dev;
spin_lock(&dev->port_pkey_list[pp->port_num].list_lock);
list_for_each_entry(tmp_pkey,
&dev->port_pkey_list[pp->port_num].pkey_list,
pkey_index_list) {
if (tmp_pkey->pkey_index == pp->pkey_index) {
pkey = tmp_pkey;
break;
}
}
spin_unlock(&dev->port_pkey_list[pp->port_num].list_lock);
return pkey;
}
static int get_pkey_and_subnet_prefix(struct ib_port_pkey *pp,
u16 *pkey,
u64 *subnet_prefix)
{
struct ib_device *dev = pp->sec->dev;
int ret;
ret = ib_get_cached_pkey(dev, pp->port_num, pp->pkey_index, pkey);
if (ret)
return ret;
ret = ib_get_cached_subnet_prefix(dev, pp->port_num, subnet_prefix);
return ret;
}
static int enforce_qp_pkey_security(u16 pkey,
u64 subnet_prefix,
struct ib_qp_security *qp_sec)
{
struct ib_qp_security *shared_qp_sec;
int ret;
ret = security_ib_pkey_access(qp_sec->security, subnet_prefix, pkey);
if (ret)
return ret;
if (qp_sec->qp == qp_sec->qp->real_qp) {
list_for_each_entry(shared_qp_sec,
&qp_sec->shared_qp_list,
shared_qp_list) {
ret = security_ib_pkey_access(shared_qp_sec->security,
subnet_prefix,
pkey);
if (ret)
return ret;
}
}
return 0;
}
/* The caller of this function must hold the QP security
* mutex of the QP of the security structure in *pps.
*
* It takes separate ports_pkeys and security structure
* because in some cases the pps will be for a new settings
* or the pps will be for the real QP and security structure
* will be for a shared QP.
*/
static int check_qp_port_pkey_settings(struct ib_ports_pkeys *pps,
struct ib_qp_security *sec)
{
u64 subnet_prefix;
u16 pkey;
int ret = 0;
if (!pps)
return 0;
if (pps->main.state != IB_PORT_PKEY_NOT_VALID) {
get_pkey_and_subnet_prefix(&pps->main,
&pkey,
&subnet_prefix);
ret = enforce_qp_pkey_security(pkey,
subnet_prefix,
sec);
}
if (ret)
return ret;
if (pps->alt.state != IB_PORT_PKEY_NOT_VALID) {
get_pkey_and_subnet_prefix(&pps->alt,
&pkey,
&subnet_prefix);
ret = enforce_qp_pkey_security(pkey,
subnet_prefix,
sec);
}
return ret;
}
/* The caller of this function must hold the QP security
* mutex.
*/
static void qp_to_error(struct ib_qp_security *sec)
{
struct ib_qp_security *shared_qp_sec;
struct ib_qp_attr attr = {
.qp_state = IB_QPS_ERR
};
struct ib_event event = {
.event = IB_EVENT_QP_FATAL
};
/* If the QP is in the process of being destroyed
* the qp pointer in the security structure is
* undefined. It cannot be modified now.
*/
if (sec->destroying)
return;
ib_modify_qp(sec->qp,
&attr,
IB_QP_STATE);
if (sec->qp->event_handler && sec->qp->qp_context) {
event.element.qp = sec->qp;
sec->qp->event_handler(&event,
sec->qp->qp_context);
}
list_for_each_entry(shared_qp_sec,
&sec->shared_qp_list,
shared_qp_list) {
struct ib_qp *qp = shared_qp_sec->qp;
if (qp->event_handler && qp->qp_context) {
event.element.qp = qp;
event.device = qp->device;
qp->event_handler(&event,
qp->qp_context);
}
}
}
static inline void check_pkey_qps(struct pkey_index_qp_list *pkey,
struct ib_device *device,
u8 port_num,
u64 subnet_prefix)
{
struct ib_port_pkey *pp, *tmp_pp;
bool comp;
LIST_HEAD(to_error_list);
u16 pkey_val;
if (!ib_get_cached_pkey(device,
port_num,
pkey->pkey_index,
&pkey_val)) {
spin_lock(&pkey->qp_list_lock);
list_for_each_entry(pp, &pkey->qp_list, qp_list) {
if (atomic_read(&pp->sec->error_list_count))
continue;
if (enforce_qp_pkey_security(pkey_val,
subnet_prefix,
pp->sec)) {
atomic_inc(&pp->sec->error_list_count);
list_add(&pp->to_error_list,
&to_error_list);
}
}
spin_unlock(&pkey->qp_list_lock);
}
list_for_each_entry_safe(pp,
tmp_pp,
&to_error_list,
to_error_list) {
mutex_lock(&pp->sec->mutex);
qp_to_error(pp->sec);
list_del(&pp->to_error_list);
atomic_dec(&pp->sec->error_list_count);
comp = pp->sec->destroying;
mutex_unlock(&pp->sec->mutex);
if (comp)
complete(&pp->sec->error_complete);
}
}
/* The caller of this function must hold the QP security
* mutex.
*/
static int port_pkey_list_insert(struct ib_port_pkey *pp)
{
struct pkey_index_qp_list *tmp_pkey;
struct pkey_index_qp_list *pkey;
struct ib_device *dev;
u8 port_num = pp->port_num;
int ret = 0;
if (pp->state != IB_PORT_PKEY_VALID)
return 0;
dev = pp->sec->dev;
pkey = get_pkey_idx_qp_list(pp);
if (!pkey) {
bool found = false;
pkey = kzalloc(sizeof(*pkey), GFP_KERNEL);
if (!pkey)
return -ENOMEM;
spin_lock(&dev->port_pkey_list[port_num].list_lock);
/* Check for the PKey again. A racing process may
* have created it.
*/
list_for_each_entry(tmp_pkey,
&dev->port_pkey_list[port_num].pkey_list,
pkey_index_list) {
if (tmp_pkey->pkey_index == pp->pkey_index) {
kfree(pkey);
pkey = tmp_pkey;
found = true;
break;
}
}
if (!found) {
pkey->pkey_index = pp->pkey_index;
spin_lock_init(&pkey->qp_list_lock);
INIT_LIST_HEAD(&pkey->qp_list);
list_add(&pkey->pkey_index_list,
&dev->port_pkey_list[port_num].pkey_list);
}
spin_unlock(&dev->port_pkey_list[port_num].list_lock);
}
spin_lock(&pkey->qp_list_lock);
list_add(&pp->qp_list, &pkey->qp_list);
spin_unlock(&pkey->qp_list_lock);
pp->state = IB_PORT_PKEY_LISTED;
return ret;
}
/* The caller of this function must hold the QP security
* mutex.
*/
static void port_pkey_list_remove(struct ib_port_pkey *pp)
{
struct pkey_index_qp_list *pkey;
if (pp->state != IB_PORT_PKEY_LISTED)
return;
pkey = get_pkey_idx_qp_list(pp);
spin_lock(&pkey->qp_list_lock);
list_del(&pp->qp_list);
spin_unlock(&pkey->qp_list_lock);
/* The setting may still be valid, i.e. after
* a destroy has failed for example.
*/
pp->state = IB_PORT_PKEY_VALID;
}
static void destroy_qp_security(struct ib_qp_security *sec)
{
security_ib_free_security(sec->security);
kfree(sec->ports_pkeys);
kfree(sec);
}
/* The caller of this function must hold the QP security
* mutex.
*/
static struct ib_ports_pkeys *get_new_pps(const struct ib_qp *qp,
const struct ib_qp_attr *qp_attr,
int qp_attr_mask)
{
struct ib_ports_pkeys *new_pps;
struct ib_ports_pkeys *qp_pps = qp->qp_sec->ports_pkeys;
new_pps = kzalloc(sizeof(*new_pps), GFP_KERNEL);
if (!new_pps)
return NULL;
if (qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) {
if (!qp_pps) {
new_pps->main.port_num = qp_attr->port_num;
new_pps->main.pkey_index = qp_attr->pkey_index;
} else {
new_pps->main.port_num = (qp_attr_mask & IB_QP_PORT) ?
qp_attr->port_num :
qp_pps->main.port_num;
new_pps->main.pkey_index =
(qp_attr_mask & IB_QP_PKEY_INDEX) ?
qp_attr->pkey_index :
qp_pps->main.pkey_index;
}
new_pps->main.state = IB_PORT_PKEY_VALID;
} else if (qp_pps) {
new_pps->main.port_num = qp_pps->main.port_num;
new_pps->main.pkey_index = qp_pps->main.pkey_index;
if (qp_pps->main.state != IB_PORT_PKEY_NOT_VALID)
new_pps->main.state = IB_PORT_PKEY_VALID;
}
if (qp_attr_mask & IB_QP_ALT_PATH) {
new_pps->alt.port_num = qp_attr->alt_port_num;
new_pps->alt.pkey_index = qp_attr->alt_pkey_index;
new_pps->alt.state = IB_PORT_PKEY_VALID;
} else if (qp_pps) {
new_pps->alt.port_num = qp_pps->alt.port_num;
new_pps->alt.pkey_index = qp_pps->alt.pkey_index;
if (qp_pps->alt.state != IB_PORT_PKEY_NOT_VALID)
new_pps->alt.state = IB_PORT_PKEY_VALID;
}
new_pps->main.sec = qp->qp_sec;
new_pps->alt.sec = qp->qp_sec;
return new_pps;
}
int ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev)
{
struct ib_qp *real_qp = qp->real_qp;
int ret;
ret = ib_create_qp_security(qp, dev);
if (ret)
return ret;
mutex_lock(&real_qp->qp_sec->mutex);
ret = check_qp_port_pkey_settings(real_qp->qp_sec->ports_pkeys,
qp->qp_sec);
if (ret)
goto ret;
if (qp != real_qp)
list_add(&qp->qp_sec->shared_qp_list,
&real_qp->qp_sec->shared_qp_list);
ret:
mutex_unlock(&real_qp->qp_sec->mutex);
if (ret)
destroy_qp_security(qp->qp_sec);
return ret;
}
void ib_close_shared_qp_security(struct ib_qp_security *sec)
{
struct ib_qp *real_qp = sec->qp->real_qp;
mutex_lock(&real_qp->qp_sec->mutex);
list_del(&sec->shared_qp_list);
mutex_unlock(&real_qp->qp_sec->mutex);
destroy_qp_security(sec);
}
int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev)
{
int ret;
qp->qp_sec = kzalloc(sizeof(*qp->qp_sec), GFP_KERNEL);
if (!qp->qp_sec)
return -ENOMEM;
qp->qp_sec->qp = qp;
qp->qp_sec->dev = dev;
mutex_init(&qp->qp_sec->mutex);
INIT_LIST_HEAD(&qp->qp_sec->shared_qp_list);
atomic_set(&qp->qp_sec->error_list_count, 0);
init_completion(&qp->qp_sec->error_complete);
ret = security_ib_alloc_security(&qp->qp_sec->security);
if (ret)
kfree(qp->qp_sec);
return ret;
}
EXPORT_SYMBOL(ib_create_qp_security);
void ib_destroy_qp_security_begin(struct ib_qp_security *sec)
{
mutex_lock(&sec->mutex);
/* Remove the QP from the lists so it won't get added to
* a to_error_list during the destroy process.
*/
if (sec->ports_pkeys) {
port_pkey_list_remove(&sec->ports_pkeys->main);
port_pkey_list_remove(&sec->ports_pkeys->alt);
}
/* If the QP is already in one or more of those lists
* the destroying flag will ensure the to error flow
* doesn't operate on an undefined QP.
*/
sec->destroying = true;
/* Record the error list count to know how many completions
* to wait for.
*/
sec->error_comps_pending = atomic_read(&sec->error_list_count);
mutex_unlock(&sec->mutex);
}
void ib_destroy_qp_security_abort(struct ib_qp_security *sec)
{
int ret;
int i;
/* If a concurrent cache update is in progress this
* QP security could be marked for an error state
* transition. Wait for this to complete.
*/
for (i = 0; i < sec->error_comps_pending; i++)
wait_for_completion(&sec->error_complete);
mutex_lock(&sec->mutex);
sec->destroying = false;
/* Restore the position in the lists and verify
* access is still allowed in case a cache update
* occurred while attempting to destroy.
*
* Because these setting were listed already
* and removed during ib_destroy_qp_security_begin
* we know the pkey_index_qp_list for the PKey
* already exists so port_pkey_list_insert won't fail.
*/
if (sec->ports_pkeys) {
port_pkey_list_insert(&sec->ports_pkeys->main);
port_pkey_list_insert(&sec->ports_pkeys->alt);
}
ret = check_qp_port_pkey_settings(sec->ports_pkeys, sec);
if (ret)
qp_to_error(sec);
mutex_unlock(&sec->mutex);
}
void ib_destroy_qp_security_end(struct ib_qp_security *sec)
{
int i;
/* If a concurrent cache update is occurring we must
* wait until this QP security structure is processed
* in the QP to error flow before destroying it because
* the to_error_list is in use.
*/
for (i = 0; i < sec->error_comps_pending; i++)
wait_for_completion(&sec->error_complete);
destroy_qp_security(sec);
}
void ib_security_cache_change(struct ib_device *device,
u8 port_num,
u64 subnet_prefix)
{
struct pkey_index_qp_list *pkey;
list_for_each_entry(pkey,
&device->port_pkey_list[port_num].pkey_list,
pkey_index_list) {
check_pkey_qps(pkey,
device,
port_num,
subnet_prefix);
}
}
void ib_security_destroy_port_pkey_list(struct ib_device *device)
{
struct pkey_index_qp_list *pkey, *tmp_pkey;
int i;
for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) {
spin_lock(&device->port_pkey_list[i].list_lock);
list_for_each_entry_safe(pkey,
tmp_pkey,
&device->port_pkey_list[i].pkey_list,
pkey_index_list) {
list_del(&pkey->pkey_index_list);
kfree(pkey);
}
spin_unlock(&device->port_pkey_list[i].list_lock);
}
}
int ib_security_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask,
struct ib_udata *udata)
{
int ret = 0;
struct ib_ports_pkeys *tmp_pps;
struct ib_ports_pkeys *new_pps;
bool special_qp = (qp->qp_type == IB_QPT_SMI ||
qp->qp_type == IB_QPT_GSI ||
qp->qp_type >= IB_QPT_RESERVED1);
bool pps_change = ((qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) ||
(qp_attr_mask & IB_QP_ALT_PATH));
if (pps_change && !special_qp) {
mutex_lock(&qp->qp_sec->mutex);
new_pps = get_new_pps(qp,
qp_attr,
qp_attr_mask);
/* Add this QP to the lists for the new port
* and pkey settings before checking for permission
* in case there is a concurrent cache update
* occurring. Walking the list for a cache change
* doesn't acquire the security mutex unless it's
* sending the QP to error.
*/
ret = port_pkey_list_insert(&new_pps->main);
if (!ret)
ret = port_pkey_list_insert(&new_pps->alt);
if (!ret)
ret = check_qp_port_pkey_settings(new_pps,
qp->qp_sec);
}
if (!ret)
ret = qp->device->modify_qp(qp->real_qp,
qp_attr,
qp_attr_mask,
udata);
if (pps_change && !special_qp) {
/* Clean up the lists and free the appropriate
* ports_pkeys structure.
*/
if (ret) {
tmp_pps = new_pps;
} else {
tmp_pps = qp->qp_sec->ports_pkeys;
qp->qp_sec->ports_pkeys = new_pps;
}
if (tmp_pps) {
port_pkey_list_remove(&tmp_pps->main);
port_pkey_list_remove(&tmp_pps->alt);
}
kfree(tmp_pps);
mutex_unlock(&qp->qp_sec->mutex);
}
return ret;
}
EXPORT_SYMBOL(ib_security_modify_qp);
int ib_security_pkey_access(struct ib_device *dev,
u8 port_num,
u16 pkey_index,
void *sec)
{
u64 subnet_prefix;
u16 pkey;
int ret;
ret = ib_get_cached_pkey(dev, port_num, pkey_index, &pkey);
if (ret)
return ret;
ret = ib_get_cached_subnet_prefix(dev, port_num, &subnet_prefix);
if (ret)
return ret;
return security_ib_pkey_access(sec, subnet_prefix, pkey);
}
EXPORT_SYMBOL(ib_security_pkey_access);
static int ib_mad_agent_security_change(struct notifier_block *nb,
unsigned long event,
void *data)
{
struct ib_mad_agent *ag = container_of(nb, struct ib_mad_agent, lsm_nb);
if (event != LSM_POLICY_CHANGE)
return NOTIFY_DONE;
ag->smp_allowed = !security_ib_endport_manage_subnet(ag->security,
ag->device->name,
ag->port_num);
return NOTIFY_OK;
}
int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
enum ib_qp_type qp_type)
{
int ret;
ret = security_ib_alloc_security(&agent->security);
if (ret)
return ret;
if (qp_type != IB_QPT_SMI)
return 0;
ret = security_ib_endport_manage_subnet(agent->security,
agent->device->name,
agent->port_num);
if (ret)
return ret;
agent->lsm_nb.notifier_call = ib_mad_agent_security_change;
ret = register_lsm_notifier(&agent->lsm_nb);
if (ret)
return ret;
agent->smp_allowed = true;
agent->lsm_nb_reg = true;
return 0;
}
void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent)
{
security_ib_free_security(agent->security);
if (agent->lsm_nb_reg)
unregister_lsm_notifier(&agent->lsm_nb);
}
int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index)
{
int ret;
if (map->agent.qp->qp_type == IB_QPT_SMI && !map->agent.smp_allowed)
return -EACCES;
ret = ib_security_pkey_access(map->agent.device,
map->agent.port_num,
pkey_index,
map->agent.security);
if (ret)
return ret;
return 0;
}
#endif /* CONFIG_SECURITY_INFINIBAND */
...@@ -1508,6 +1508,10 @@ static int create_qp(struct ib_uverbs_file *file, ...@@ -1508,6 +1508,10 @@ static int create_qp(struct ib_uverbs_file *file,
} }
if (cmd->qp_type != IB_QPT_XRC_TGT) { 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->real_qp = qp;
qp->device = device; qp->device = device;
qp->pd = pd; qp->pd = pd;
...@@ -2002,14 +2006,17 @@ static int modify_qp(struct ib_uverbs_file *file, ...@@ -2002,14 +2006,17 @@ static int modify_qp(struct ib_uverbs_file *file,
if (ret) if (ret)
goto release_qp; goto release_qp;
} }
ret = qp->device->modify_qp(qp, attr, ret = ib_security_modify_qp(qp,
attr,
modify_qp_mask(qp->qp_type, modify_qp_mask(qp->qp_type,
cmd->base.attr_mask), cmd->base.attr_mask),
udata); udata);
} else { } else {
ret = ib_modify_qp(qp, attr, ret = ib_security_modify_qp(qp,
modify_qp_mask(qp->qp_type, attr,
cmd->base.attr_mask)); modify_qp_mask(qp->qp_type,
cmd->base.attr_mask),
NULL);
} }
release_qp: release_qp:
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <net/addrconf.h> #include <net/addrconf.h>
#include <linux/security.h>
#include <rdma/ib_verbs.h> #include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h> #include <rdma/ib_cache.h>
...@@ -713,11 +714,19 @@ static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp, ...@@ -713,11 +714,19 @@ static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp,
{ {
struct ib_qp *qp; struct ib_qp *qp;
unsigned long flags; unsigned long flags;
int err;
qp = kzalloc(sizeof *qp, GFP_KERNEL); qp = kzalloc(sizeof *qp, GFP_KERNEL);
if (!qp) if (!qp)
return ERR_PTR(-ENOMEM); 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; qp->real_qp = real_qp;
atomic_inc(&real_qp->usecnt); atomic_inc(&real_qp->usecnt);
qp->device = real_qp->device; qp->device = real_qp->device;
...@@ -804,6 +813,12 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, ...@@ -804,6 +813,12 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
if (IS_ERR(qp)) if (IS_ERR(qp))
return qp; return qp;
ret = ib_create_qp_security(qp, device);
if (ret) {
ib_destroy_qp(qp);
return ERR_PTR(ret);
}
qp->device = device; qp->device = device;
qp->real_qp = qp; qp->real_qp = qp;
qp->uobject = NULL; qp->uobject = NULL;
...@@ -1266,7 +1281,7 @@ int ib_modify_qp(struct ib_qp *qp, ...@@ -1266,7 +1281,7 @@ int ib_modify_qp(struct ib_qp *qp,
return ret; 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); EXPORT_SYMBOL(ib_modify_qp);
...@@ -1295,6 +1310,7 @@ int ib_close_qp(struct ib_qp *qp) ...@@ -1295,6 +1310,7 @@ int ib_close_qp(struct ib_qp *qp)
spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags); spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
atomic_dec(&real_qp->usecnt); atomic_dec(&real_qp->usecnt);
ib_close_shared_qp_security(qp->qp_sec);
kfree(qp); kfree(qp);
return 0; return 0;
...@@ -1335,6 +1351,7 @@ int ib_destroy_qp(struct ib_qp *qp) ...@@ -1335,6 +1351,7 @@ int ib_destroy_qp(struct ib_qp *qp)
struct ib_cq *scq, *rcq; struct ib_cq *scq, *rcq;
struct ib_srq *srq; struct ib_srq *srq;
struct ib_rwq_ind_table *ind_tbl; struct ib_rwq_ind_table *ind_tbl;
struct ib_qp_security *sec;
int ret; int ret;
WARN_ON_ONCE(qp->mrs_used > 0); WARN_ON_ONCE(qp->mrs_used > 0);
...@@ -1350,6 +1367,9 @@ int ib_destroy_qp(struct ib_qp *qp) ...@@ -1350,6 +1367,9 @@ int ib_destroy_qp(struct ib_qp *qp)
rcq = qp->recv_cq; rcq = qp->recv_cq;
srq = qp->srq; srq = qp->srq;
ind_tbl = qp->rwq_ind_tbl; ind_tbl = qp->rwq_ind_tbl;
sec = qp->qp_sec;
if (sec)
ib_destroy_qp_security_begin(sec);
if (!qp->uobject) if (!qp->uobject)
rdma_rw_cleanup_mrs(qp); rdma_rw_cleanup_mrs(qp);
...@@ -1366,6 +1386,11 @@ int ib_destroy_qp(struct ib_qp *qp) ...@@ -1366,6 +1386,11 @@ int ib_destroy_qp(struct ib_qp *qp)
atomic_dec(&srq->usecnt); atomic_dec(&srq->usecnt);
if (ind_tbl) if (ind_tbl)
atomic_dec(&ind_tbl->usecnt); atomic_dec(&ind_tbl->usecnt);
if (sec)
ib_destroy_qp_security_end(sec);
} else {
if (sec)
ib_destroy_qp_security_abort(sec);
} }
return ret; return ret;
......
...@@ -2544,10 +2544,25 @@ EXPORT_SYMBOL_GPL(nfs_set_sb_security); ...@@ -2544,10 +2544,25 @@ EXPORT_SYMBOL_GPL(nfs_set_sb_security);
int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
struct nfs_mount_info *mount_info) 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 */ /* 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) if (d_inode(mntroot)->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops)
return -ESTALE; 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); EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/path.h> #include <linux/path.h>
#include <linux/key.h> #include <linux/key.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <rdma/ib_verbs.h>
struct lsm_network_audit { struct lsm_network_audit {
int netif; int netif;
...@@ -45,6 +46,16 @@ struct lsm_ioctlop_audit { ...@@ -45,6 +46,16 @@ struct lsm_ioctlop_audit {
u16 cmd; 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. */ /* Auxiliary data to use in generating the audit record. */
struct common_audit_data { struct common_audit_data {
char type; char type;
...@@ -60,6 +71,8 @@ struct common_audit_data { ...@@ -60,6 +71,8 @@ struct common_audit_data {
#define LSM_AUDIT_DATA_DENTRY 10 #define LSM_AUDIT_DATA_DENTRY 10
#define LSM_AUDIT_DATA_IOCTL_OP 11 #define LSM_AUDIT_DATA_IOCTL_OP 11
#define LSM_AUDIT_DATA_FILE 12 #define LSM_AUDIT_DATA_FILE 12
#define LSM_AUDIT_DATA_IBPKEY 13
#define LSM_AUDIT_DATA_IBENDPORT 14
union { union {
struct path path; struct path path;
struct dentry *dentry; struct dentry *dentry;
...@@ -77,6 +90,8 @@ struct common_audit_data { ...@@ -77,6 +90,8 @@ struct common_audit_data {
char *kmod_name; char *kmod_name;
struct lsm_ioctlop_audit *op; struct lsm_ioctlop_audit *op;
struct file *file; struct file *file;
struct lsm_ibpkey_audit *ibpkey;
struct lsm_ibendport_audit *ibendport;
} u; } u;
/* this union contains LSM specific data */ /* this union contains LSM specific data */
union { union {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group) * Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
* Copyright (C) 2015 Intel Corporation. * Copyright (C) 2015 Intel Corporation.
* Copyright (C) 2015 Casey Schaufler <casey@schaufler-ca.com> * 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -911,6 +912,26 @@ ...@@ -911,6 +912,26 @@
* associated with the TUN device's security structure. * associated with the TUN device's security structure.
* @security pointer to the TUN devices'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. * Security hooks for XFRM operations.
* *
* @xfrm_policy_alloc_security: * @xfrm_policy_alloc_security:
...@@ -1388,7 +1409,9 @@ union security_list_options { ...@@ -1388,7 +1409,9 @@ union security_list_options {
unsigned long kern_flags, unsigned long kern_flags,
unsigned long *set_kern_flags); unsigned long *set_kern_flags);
int (*sb_clone_mnt_opts)(const struct super_block *oldsb, 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 (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
int (*dentry_init_security)(struct dentry *dentry, int mode, int (*dentry_init_security)(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx, const struct qstr *name, void **ctx,
...@@ -1620,6 +1643,14 @@ union security_list_options { ...@@ -1620,6 +1643,14 @@ union security_list_options {
int (*tun_dev_open)(void *security); int (*tun_dev_open)(void *security);
#endif /* CONFIG_SECURITY_NETWORK */ #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 #ifdef CONFIG_SECURITY_NETWORK_XFRM
int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp, int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp,
struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_user_sec_ctx *sec_ctx,
...@@ -1851,6 +1882,12 @@ struct security_hook_heads { ...@@ -1851,6 +1882,12 @@ struct security_hook_heads {
struct list_head tun_dev_attach; struct list_head tun_dev_attach;
struct list_head tun_dev_open; struct list_head tun_dev_open;
#endif /* CONFIG_SECURITY_NETWORK */ #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 #ifdef CONFIG_SECURITY_NETWORK_XFRM
struct list_head xfrm_policy_alloc_security; struct list_head xfrm_policy_alloc_security;
struct list_head xfrm_policy_clone_security; struct list_head xfrm_policy_clone_security;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com> * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
* Copyright (C) 2001 James Morris <jmorris@intercode.com.au> * Copyright (C) 2001 James Morris <jmorris@intercode.com.au>
* Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group) * 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -68,6 +69,10 @@ struct audit_krule; ...@@ -68,6 +69,10 @@ struct audit_krule;
struct user_namespace; struct user_namespace;
struct timezone; struct timezone;
enum lsm_event {
LSM_POLICY_CHANGE,
};
/* These functions are in security/commoncap.c */ /* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns, extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit); int cap, int audit);
...@@ -163,6 +168,10 @@ struct security_mnt_opts { ...@@ -163,6 +168,10 @@ struct security_mnt_opts {
int num_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) static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
{ {
opts->mnt_opts = NULL; opts->mnt_opts = NULL;
...@@ -240,7 +249,9 @@ int security_sb_set_mnt_opts(struct super_block *sb, ...@@ -240,7 +249,9 @@ int security_sb_set_mnt_opts(struct super_block *sb,
unsigned long kern_flags, unsigned long kern_flags,
unsigned long *set_kern_flags); unsigned long *set_kern_flags);
int security_sb_clone_mnt_opts(const struct super_block *oldsb, 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_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
int security_dentry_init_security(struct dentry *dentry, int mode, int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx, const struct qstr *name, void **ctx,
...@@ -381,6 +392,21 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen); ...@@ -381,6 +392,21 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
struct security_mnt_opts { 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) 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, ...@@ -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, 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; return 0;
} }
...@@ -1406,6 +1434,32 @@ static inline int security_tun_dev_open(void *security) ...@@ -1406,6 +1434,32 @@ static inline int security_tun_dev_open(void *security)
} }
#endif /* CONFIG_SECURITY_NETWORK */ #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 #ifdef CONFIG_SECURITY_NETWORK_XFRM
int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
......
...@@ -575,6 +575,10 @@ struct ib_mad_agent { ...@@ -575,6 +575,10 @@ struct ib_mad_agent {
u32 flags; u32 flags;
u8 port_num; u8 port_num;
u8 rmpp_version; 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 { ...@@ -1614,6 +1614,45 @@ struct ib_rwq_ind_table_init_attr {
struct ib_wq **ind_tbl; 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_write_sge: Maximum SGE elements per RDMA WRITE request.
* @max_read_sge: Maximum SGE elements per RDMA READ request. * @max_read_sge: Maximum SGE elements per RDMA READ request.
...@@ -1643,6 +1682,7 @@ struct ib_qp { ...@@ -1643,6 +1682,7 @@ struct ib_qp {
u32 max_read_sge; u32 max_read_sge;
enum ib_qp_type qp_type; enum ib_qp_type qp_type;
struct ib_rwq_ind_table *rwq_ind_tbl; struct ib_rwq_ind_table *rwq_ind_tbl;
struct ib_qp_security *qp_sec;
}; };
struct ib_mr { struct ib_mr {
...@@ -1891,6 +1931,7 @@ enum ib_mad_result { ...@@ -1891,6 +1931,7 @@ enum ib_mad_result {
}; };
struct ib_port_cache { struct ib_port_cache {
u64 subnet_prefix;
struct ib_pkey_cache *pkey; struct ib_pkey_cache *pkey;
struct ib_gid_table *gid; struct ib_gid_table *gid;
u8 lmc; u8 lmc;
...@@ -1940,6 +1981,12 @@ struct rdma_netdev { ...@@ -1940,6 +1981,12 @@ struct rdma_netdev {
union ib_gid *gid, u16 mlid); 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 { struct ib_device {
/* Do not access @dma_device directly from ULP nor from HW drivers. */ /* Do not access @dma_device directly from ULP nor from HW drivers. */
struct device *dma_device; struct device *dma_device;
...@@ -1963,6 +2010,8 @@ struct ib_device { ...@@ -1963,6 +2010,8 @@ struct ib_device {
int num_comp_vectors; int num_comp_vectors;
struct ib_port_pkey_list *port_pkey_list;
struct iw_cm_verbs *iwcm; struct iw_cm_verbs *iwcm;
/** /**
......
...@@ -54,6 +54,15 @@ config SECURITY_NETWORK ...@@ -54,6 +54,15 @@ config SECURITY_NETWORK
implement socket and networking access controls. implement socket and networking access controls.
If you are unsure how to answer this question, answer N. 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 config SECURITY_NETWORK_XFRM
bool "XFRM (IPSec) Networking Security Hooks" bool "XFRM (IPSec) Networking Security Hooks"
depends on XFRM && SECURITY_NETWORK depends on XFRM && SECURITY_NETWORK
......
...@@ -410,6 +410,22 @@ static void dump_common_audit_data(struct audit_buffer *ab, ...@@ -410,6 +410,22 @@ static void dump_common_audit_data(struct audit_buffer *ab,
audit_log_format(ab, " kmod="); audit_log_format(ab, " kmod=");
audit_log_untrustedstring(ab, a->u.kmod_name); audit_log_untrustedstring(ab, a->u.kmod_name);
break; 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) */ } /* switch (a->type) */
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com> * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
* Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -34,6 +35,8 @@ ...@@ -34,6 +35,8 @@
#define SECURITY_NAME_MAX 10 #define SECURITY_NAME_MAX 10
struct security_hook_heads security_hook_heads __lsm_ro_after_init; struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
char *lsm_names; char *lsm_names;
/* Boot-time LSM user choice */ /* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = 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, ...@@ -165,6 +168,24 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
panic("%s - Cannot get early memory.\n", __func__); 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. * Hook list operation macros.
* *
...@@ -399,9 +420,12 @@ int security_sb_set_mnt_opts(struct super_block *sb, ...@@ -399,9 +420,12 @@ int security_sb_set_mnt_opts(struct super_block *sb,
EXPORT_SYMBOL(security_sb_set_mnt_opts); EXPORT_SYMBOL(security_sb_set_mnt_opts);
int security_sb_clone_mnt_opts(const struct super_block *oldsb, 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); EXPORT_SYMBOL(security_sb_clone_mnt_opts);
...@@ -1515,6 +1539,33 @@ EXPORT_SYMBOL(security_tun_dev_open); ...@@ -1515,6 +1539,33 @@ EXPORT_SYMBOL(security_tun_dev_open);
#endif /* CONFIG_SECURITY_NETWORK */ #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 #ifdef CONFIG_SECURITY_NETWORK_XFRM
int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.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/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 ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* Paul Moore <paul@paul-moore.com> * Paul Moore <paul@paul-moore.com>
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
* Yuichi Nakamura <ynakam@hitachisoft.jp> * Yuichi Nakamura <ynakam@hitachisoft.jp>
* Copyright (C) 2016 Mellanox Technologies
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, * it under the terms of the GNU General Public License version 2,
...@@ -90,6 +91,7 @@ ...@@ -90,6 +91,7 @@
#include "netif.h" #include "netif.h"
#include "netnode.h" #include "netnode.h"
#include "netport.h" #include "netport.h"
#include "ibpkey.h"
#include "xfrm.h" #include "xfrm.h"
#include "netlabel.h" #include "netlabel.h"
#include "audit.h" #include "audit.h"
...@@ -171,6 +173,16 @@ static int selinux_netcache_avc_callback(u32 event) ...@@ -171,6 +173,16 @@ static int selinux_netcache_avc_callback(u32 event)
return 0; 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 * initialise the security for the init task
*/ */
...@@ -398,18 +410,6 @@ static void superblock_free_security(struct super_block *sb) ...@@ -398,18 +410,6 @@ static void superblock_free_security(struct super_block *sb)
kfree(sbsec); 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) static inline int inode_doinit(struct inode *inode)
{ {
return inode_doinit_with_dentry(inode, NULL); return inode_doinit_with_dentry(inode, NULL);
...@@ -524,13 +524,17 @@ static int sb_finish_set_opts(struct super_block *sb) ...@@ -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; 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)) if (selinux_is_sblabel_mnt(sb))
sbsec->flags |= SBLABEL_MNT; sbsec->flags |= SBLABEL_MNT;
else
sbsec->flags &= ~SBLABEL_MNT;
/* Initialize the root inode. */ /* Initialize the root inode. */
rc = inode_doinit_with_dentry(root_inode, root); rc = inode_doinit_with_dentry(root_inode, root);
...@@ -809,6 +813,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, ...@@ -809,6 +813,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
sbsec->flags |= SE_SBPROC | SE_SBGENFS; sbsec->flags |= SE_SBPROC | SE_SBGENFS;
if (!strcmp(sb->s_type->name, "debugfs") || if (!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "pstore")) !strcmp(sb->s_type->name, "pstore"))
sbsec->flags |= SE_SBGENFS; sbsec->flags |= SE_SBGENFS;
...@@ -963,8 +968,11 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb, ...@@ -963,8 +968,11 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb,
} }
static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, 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; const struct superblock_security_struct *oldsbsec = oldsb->s_security;
struct superblock_security_struct *newsbsec = newsb->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, ...@@ -979,6 +987,13 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
if (!ss_initialized) if (!ss_initialized)
return 0; 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?? */ /* how can we clone if the old one wasn't set up?? */
BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
...@@ -994,6 +1009,18 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, ...@@ -994,6 +1009,18 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
newsbsec->def_sid = oldsbsec->def_sid; newsbsec->def_sid = oldsbsec->def_sid;
newsbsec->behavior = oldsbsec->behavior; 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) { if (set_context) {
u32 sid = oldsbsec->mntpoint_sid; u32 sid = oldsbsec->mntpoint_sid;
...@@ -1013,8 +1040,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, ...@@ -1013,8 +1040,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
} }
sb_finish_set_opts(newsb); sb_finish_set_opts(newsb);
out:
mutex_unlock(&newsbsec->lock); mutex_unlock(&newsbsec->lock);
return 0; return rc;
} }
static int selinux_parse_opts_str(char *options, static int selinux_parse_opts_str(char *options,
...@@ -2063,8 +2091,9 @@ static inline u32 file_to_av(struct file *file) ...@@ -2063,8 +2091,9 @@ static inline u32 file_to_av(struct file *file)
static inline u32 open_file_to_av(struct file *file) static inline u32 open_file_to_av(struct file *file)
{ {
u32 av = file_to_av(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; av |= FILE__OPEN;
return av; return av;
...@@ -3059,6 +3088,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) ...@@ -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) static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{ {
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = iattr->ia_valid; unsigned int ia_valid = iattr->ia_valid;
__u32 av = FILE__WRITE; __u32 av = FILE__WRITE;
...@@ -3074,8 +3104,10 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ...@@ -3074,8 +3104,10 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, dentry, FILE__SETATTR); return dentry_has_perm(cred, dentry, FILE__SETATTR);
if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE) if (selinux_policycap_openperm &&
&& !(ia_valid & ATTR_FILE)) inode->i_sb->s_magic != SOCKFS_MAGIC &&
(ia_valid & ATTR_SIZE) &&
!(ia_valid & ATTR_FILE))
av |= FILE__OPEN; av |= FILE__OPEN;
return dentry_has_perm(cred, dentry, av); return dentry_has_perm(cred, dentry, av);
...@@ -3107,6 +3139,18 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name) ...@@ -3107,6 +3139,18 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
return dentry_has_perm(cred, dentry, FILE__SETATTR); 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, static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
{ {
...@@ -3138,7 +3182,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, ...@@ -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); rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);
if (rc == -EINVAL) { if (rc == -EINVAL) {
if (!capable(CAP_MAC_ADMIN)) { if (!has_cap_mac_admin(true)) {
struct audit_buffer *ab; struct audit_buffer *ab;
size_t audit_size; size_t audit_size;
const char *str; const char *str;
...@@ -3264,13 +3308,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void ...@@ -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 * and lack of permission just means that we fall back to the
* in-core context value, not a denial. * 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); isec = inode_security(inode);
if (!error) if (has_cap_mac_admin(false))
error = security_sid_to_context_force(isec->sid, &context, error = security_sid_to_context_force(isec->sid, &context,
&size); &size);
else else
...@@ -3550,6 +3589,18 @@ static int selinux_mmap_addr(unsigned long addr) ...@@ -3550,6 +3589,18 @@ static int selinux_mmap_addr(unsigned long addr)
static int selinux_mmap_file(struct file *file, unsigned long reqprot, static int selinux_mmap_file(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags) 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) if (selinux_checkreqprot)
prot = reqprot; prot = reqprot;
...@@ -3710,7 +3761,8 @@ static int selinux_file_open(struct file *file, const struct cred *cred) ...@@ -3710,7 +3761,8 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
/* task security operations */ /* 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(); u32 sid = current_sid();
...@@ -5918,7 +5970,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) ...@@ -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); error = security_context_to_sid(value, size, &sid, GFP_KERNEL);
if (error == -EINVAL && !strcmp(name, "fscreate")) { if (error == -EINVAL && !strcmp(name, "fscreate")) {
if (!capable(CAP_MAC_ADMIN)) { if (!has_cap_mac_admin(true)) {
struct audit_buffer *ab; struct audit_buffer *ab;
size_t audit_size; size_t audit_size;
...@@ -6128,7 +6180,70 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) ...@@ -6128,7 +6180,70 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
*_buffer = context; *_buffer = context;
return rc; 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 #endif
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { 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 = { ...@@ -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(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_alloc_blank, selinux_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, selinux_cred_free), LSM_HOOK_INIT(cred_free, selinux_cred_free),
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare), LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
...@@ -6315,7 +6430,13 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { ...@@ -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_queue, selinux_tun_dev_attach_queue),
LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach), LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach),
LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open), 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 #ifdef CONFIG_SECURITY_NETWORK_XFRM
LSM_HOOK_INIT(xfrm_policy_alloc_security, selinux_xfrm_policy_alloc), LSM_HOOK_INIT(xfrm_policy_alloc_security, selinux_xfrm_policy_alloc),
LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone), LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone),
...@@ -6379,6 +6500,9 @@ static __init int selinux_init(void) ...@@ -6379,6 +6500,9 @@ static __init int selinux_init(void)
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC netcache callback\n"); 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) if (selinux_enforcing)
printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n");
else else
...@@ -6448,6 +6572,23 @@ static struct nf_hook_ops selinux_nf_ops[] = { ...@@ -6448,6 +6572,23 @@ static struct nf_hook_ops selinux_nf_ops[] = {
#endif /* IPV6 */ #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) static int __init selinux_nf_ip_init(void)
{ {
int err; int err;
...@@ -6457,13 +6598,12 @@ static int __init selinux_nf_ip_init(void) ...@@ -6457,13 +6598,12 @@ static int __init selinux_nf_ip_init(void)
printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); 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) if (err)
panic("SELinux: nf_register_hooks: error %d\n", err); panic("SELinux: register_pernet_subsys: error %d\n", err);
return 0; return 0;
} }
__initcall(selinux_nf_ip_init); __initcall(selinux_nf_ip_init);
#ifdef CONFIG_SECURITY_SELINUX_DISABLE #ifdef CONFIG_SECURITY_SELINUX_DISABLE
...@@ -6471,7 +6611,7 @@ static void selinux_nf_ip_exit(void) ...@@ -6471,7 +6611,7 @@ static void selinux_nf_ip_exit(void)
{ {
printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); 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 #endif
......
/*
* 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);
#include <linux/capability.h> #include <linux/capability.h>
#define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \ #define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \
"getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append", "map"
#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \
"rename", "execute", "quotaon", "mounton", "audit_access", \ "rename", "execute", "quotaon", "mounton", "audit_access", \
...@@ -231,6 +231,10 @@ struct security_class_mapping secclass_map[] = { ...@@ -231,6 +231,10 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } }, { COMMON_SOCK_PERMS, NULL } },
{ "smc_socket", { "smc_socket",
{ COMMON_SOCK_PERMS, NULL } }, { COMMON_SOCK_PERMS, NULL } },
{ "infiniband_pkey",
{ "access", NULL } },
{ "infiniband_endport",
{ "manage_subnet", NULL } },
{ NULL } { NULL }
}; };
......
/*
* 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
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* *
* Copyright (C) 2001,2002 Networks Associates Technology, Inc. * Copyright (C) 2001,2002 Networks Associates Technology, Inc.
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2016 Mellanox Technologies
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, * it under the terms of the GNU General Public License version 2,
...@@ -139,6 +140,16 @@ struct key_security_struct { ...@@ -139,6 +140,16 @@ struct key_security_struct {
u32 sid; /* SID of key */ u32 sid; /* SID of key */
}; };
struct ib_security_struct {
u32 sid; /* SID of the queue pair or MAD agent */
};
struct pkey_security_struct {
u64 subnet_prefix; /* Port subnet prefix */
u16 pkey; /* PKey number */
u32 sid; /* SID of pkey */
};
extern unsigned int selinux_checkreqprot; extern unsigned int selinux_checkreqprot;
#endif /* _SELINUX_OBJSEC_H_ */ #endif /* _SELINUX_OBJSEC_H_ */
...@@ -36,10 +36,11 @@ ...@@ -36,10 +36,11 @@
#define POLICYDB_VERSION_DEFAULT_TYPE 28 #define POLICYDB_VERSION_DEFAULT_TYPE 28
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29 #define POLICYDB_VERSION_CONSTRAINT_NAMES 29
#define POLICYDB_VERSION_XPERMS_IOCTL 30 #define POLICYDB_VERSION_XPERMS_IOCTL 30
#define POLICYDB_VERSION_INFINIBAND 31
/* Range of policy versions we understand*/ /* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL #define POLICYDB_VERSION_MAX POLICYDB_VERSION_INFINIBAND
/* Mask for just the mount related flags */ /* Mask for just the mount related flags */
#define SE_MNTMASK 0x0f #define SE_MNTMASK 0x0f
...@@ -76,6 +77,8 @@ enum { ...@@ -76,6 +77,8 @@ enum {
}; };
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
extern int selinux_policycap_netpeer; extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm; extern int selinux_policycap_openperm;
extern int selinux_policycap_extsockclass; extern int selinux_policycap_extsockclass;
...@@ -178,6 +181,10 @@ int security_get_user_sids(u32 callsid, char *username, ...@@ -178,6 +181,10 @@ int security_get_user_sids(u32 callsid, char *username,
int security_port_sid(u8 protocol, u16 port, u32 *out_sid); int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid);
int security_netif_sid(char *name, u32 *if_sid); int security_netif_sid(char *name, u32 *if_sid);
int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_node_sid(u16 domain, void *addr, u32 addrlen,
......
...@@ -41,15 +41,6 @@ ...@@ -41,15 +41,6 @@
#include "objsec.h" #include "objsec.h"
#include "conditional.h" #include "conditional.h"
/* Policy capability filenames */
static char *policycap_names[] = {
"network_peer_controls",
"open_perms",
"extended_socket_class",
"always_check_network",
"cgroup_seclabel"
};
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
static int __init checkreqprot_setup(char *str) static int __init checkreqprot_setup(char *str)
...@@ -163,6 +154,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, ...@@ -163,6 +154,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
avc_ss_reset(0); avc_ss_reset(0);
selnl_notify_setenforce(selinux_enforcing); selnl_notify_setenforce(selinux_enforcing);
selinux_status_update_setenforce(selinux_enforcing); selinux_status_update_setenforce(selinux_enforcing);
if (!selinux_enforcing)
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
} }
length = count; length = count;
out: out:
...@@ -1750,9 +1743,9 @@ static int sel_make_policycap(void) ...@@ -1750,9 +1743,9 @@ static int sel_make_policycap(void)
sel_remove_entries(policycap_dir); sel_remove_entries(policycap_dir);
for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
if (iter < ARRAY_SIZE(policycap_names)) if (iter < ARRAY_SIZE(selinux_policycap_names))
dentry = d_alloc_name(policycap_dir, dentry = d_alloc_name(policycap_dir,
policycap_names[iter]); selinux_policycap_names[iter]);
else else
dentry = d_alloc_name(policycap_dir, "unknown"); dentry = d_alloc_name(policycap_dir, "unknown");
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#define BITS_PER_U64 (sizeof(u64) * 8) #define BITS_PER_U64 (sizeof(u64) * 8)
static struct kmem_cache *ebitmap_node_cachep;
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
{ {
struct ebitmap_node *n1, *n2; struct ebitmap_node *n1, *n2;
...@@ -54,7 +56,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) ...@@ -54,7 +56,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
n = src->node; n = src->node;
prev = NULL; prev = NULL;
while (n) { while (n) {
new = kzalloc(sizeof(*new), GFP_ATOMIC); new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
if (!new) { if (!new) {
ebitmap_destroy(dst); ebitmap_destroy(dst);
return -ENOMEM; return -ENOMEM;
...@@ -162,7 +164,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap, ...@@ -162,7 +164,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
if (e_iter == NULL || if (e_iter == NULL ||
offset >= e_iter->startbit + EBITMAP_SIZE) { offset >= e_iter->startbit + EBITMAP_SIZE) {
e_prev = e_iter; e_prev = e_iter;
e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); e_iter = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
if (e_iter == NULL) if (e_iter == NULL)
goto netlbl_import_failure; goto netlbl_import_failure;
e_iter->startbit = offset - (offset % EBITMAP_SIZE); e_iter->startbit = offset - (offset % EBITMAP_SIZE);
...@@ -288,7 +290,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) ...@@ -288,7 +290,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
prev->next = n->next; prev->next = n->next;
else else
e->node = n->next; e->node = n->next;
kfree(n); kmem_cache_free(ebitmap_node_cachep, n);
} }
return 0; return 0;
} }
...@@ -299,7 +301,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) ...@@ -299,7 +301,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
if (!value) if (!value)
return 0; return 0;
new = kzalloc(sizeof(*new), GFP_ATOMIC); new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
...@@ -332,7 +334,7 @@ void ebitmap_destroy(struct ebitmap *e) ...@@ -332,7 +334,7 @@ void ebitmap_destroy(struct ebitmap *e)
while (n) { while (n) {
temp = n; temp = n;
n = n->next; n = n->next;
kfree(temp); kmem_cache_free(ebitmap_node_cachep, temp);
} }
e->highbit = 0; e->highbit = 0;
...@@ -400,7 +402,7 @@ int ebitmap_read(struct ebitmap *e, void *fp) ...@@ -400,7 +402,7 @@ int ebitmap_read(struct ebitmap *e, void *fp)
if (!n || startbit >= n->startbit + EBITMAP_SIZE) { if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
struct ebitmap_node *tmp; struct ebitmap_node *tmp;
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); tmp = kmem_cache_zalloc(ebitmap_node_cachep, GFP_KERNEL);
if (!tmp) { if (!tmp) {
printk(KERN_ERR printk(KERN_ERR
"SELinux: ebitmap: out of memory\n"); "SELinux: ebitmap: out of memory\n");
...@@ -519,3 +521,15 @@ int ebitmap_write(struct ebitmap *e, void *fp) ...@@ -519,3 +521,15 @@ int ebitmap_write(struct ebitmap *e, void *fp)
} }
return 0; return 0;
} }
void ebitmap_cache_init(void)
{
ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
sizeof(struct ebitmap_node),
0, SLAB_PANIC, NULL);
}
void ebitmap_cache_destroy(void)
{
kmem_cache_destroy(ebitmap_node_cachep);
}
...@@ -130,6 +130,9 @@ void ebitmap_destroy(struct ebitmap *e); ...@@ -130,6 +130,9 @@ void ebitmap_destroy(struct ebitmap *e);
int ebitmap_read(struct ebitmap *e, void *fp); int ebitmap_read(struct ebitmap *e, void *fp);
int ebitmap_write(struct ebitmap *e, void *fp); int ebitmap_write(struct ebitmap *e, void *fp);
void ebitmap_cache_init(void);
void ebitmap_cache_destroy(void);
#ifdef CONFIG_NETLABEL #ifdef CONFIG_NETLABEL
int ebitmap_netlbl_export(struct ebitmap *ebmap, int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_catmap **catmap); struct netlbl_lsm_catmap **catmap);
......
...@@ -17,6 +17,11 @@ ...@@ -17,6 +17,11 @@
* *
* Added support for the policy capability bitmap * Added support for the policy capability bitmap
* *
* Update: Mellanox Techonologies
*
* Added Infiniband support
*
* Copyright (C) 2016 Mellanox Techonologies
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC * Copyright (C) 2003 - 2004 Tresys Technology, LLC
...@@ -76,81 +81,86 @@ static struct policydb_compat_info policydb_compat[] = { ...@@ -76,81 +81,86 @@ static struct policydb_compat_info policydb_compat[] = {
{ {
.version = POLICYDB_VERSION_BASE, .version = POLICYDB_VERSION_BASE,
.sym_num = SYM_NUM - 3, .sym_num = SYM_NUM - 3,
.ocon_num = OCON_NUM - 1, .ocon_num = OCON_NUM - 3,
}, },
{ {
.version = POLICYDB_VERSION_BOOL, .version = POLICYDB_VERSION_BOOL,
.sym_num = SYM_NUM - 2, .sym_num = SYM_NUM - 2,
.ocon_num = OCON_NUM - 1, .ocon_num = OCON_NUM - 3,
}, },
{ {
.version = POLICYDB_VERSION_IPV6, .version = POLICYDB_VERSION_IPV6,
.sym_num = SYM_NUM - 2, .sym_num = SYM_NUM - 2,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_NLCLASS, .version = POLICYDB_VERSION_NLCLASS,
.sym_num = SYM_NUM - 2, .sym_num = SYM_NUM - 2,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_MLS, .version = POLICYDB_VERSION_MLS,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_AVTAB, .version = POLICYDB_VERSION_AVTAB,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_RANGETRANS, .version = POLICYDB_VERSION_RANGETRANS,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_POLCAP, .version = POLICYDB_VERSION_POLCAP,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_PERMISSIVE, .version = POLICYDB_VERSION_PERMISSIVE,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_BOUNDARY, .version = POLICYDB_VERSION_BOUNDARY,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_FILENAME_TRANS, .version = POLICYDB_VERSION_FILENAME_TRANS,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_ROLETRANS, .version = POLICYDB_VERSION_ROLETRANS,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS, .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_DEFAULT_TYPE, .version = POLICYDB_VERSION_DEFAULT_TYPE,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_CONSTRAINT_NAMES, .version = POLICYDB_VERSION_CONSTRAINT_NAMES,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM - 2,
}, },
{ {
.version = POLICYDB_VERSION_XPERMS_IOCTL, .version = POLICYDB_VERSION_XPERMS_IOCTL,
.sym_num = SYM_NUM, .sym_num = SYM_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_INFINIBAND,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM, .ocon_num = OCON_NUM,
}, },
}; };
...@@ -538,34 +548,30 @@ static int policydb_index(struct policydb *p) ...@@ -538,34 +548,30 @@ static int policydb_index(struct policydb *p)
symtab_hash_eval(p->symtab); symtab_hash_eval(p->symtab);
#endif #endif
rc = -ENOMEM;
p->class_val_to_struct = kcalloc(p->p_classes.nprim, p->class_val_to_struct = kcalloc(p->p_classes.nprim,
sizeof(*p->class_val_to_struct), sizeof(*p->class_val_to_struct),
GFP_KERNEL); GFP_KERNEL);
if (!p->class_val_to_struct) if (!p->class_val_to_struct)
goto out; return -ENOMEM;
rc = -ENOMEM;
p->role_val_to_struct = kcalloc(p->p_roles.nprim, p->role_val_to_struct = kcalloc(p->p_roles.nprim,
sizeof(*p->role_val_to_struct), sizeof(*p->role_val_to_struct),
GFP_KERNEL); GFP_KERNEL);
if (!p->role_val_to_struct) if (!p->role_val_to_struct)
goto out; return -ENOMEM;
rc = -ENOMEM;
p->user_val_to_struct = kcalloc(p->p_users.nprim, p->user_val_to_struct = kcalloc(p->p_users.nprim,
sizeof(*p->user_val_to_struct), sizeof(*p->user_val_to_struct),
GFP_KERNEL); GFP_KERNEL);
if (!p->user_val_to_struct) if (!p->user_val_to_struct)
goto out; return -ENOMEM;
/* Yes, I want the sizeof the pointer, not the structure */ /* Yes, I want the sizeof the pointer, not the structure */
rc = -ENOMEM;
p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *), p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *),
p->p_types.nprim, p->p_types.nprim,
GFP_KERNEL | __GFP_ZERO); GFP_KERNEL | __GFP_ZERO);
if (!p->type_val_to_struct_array) if (!p->type_val_to_struct_array)
goto out; return -ENOMEM;
rc = flex_array_prealloc(p->type_val_to_struct_array, 0, rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); p->p_types.nprim, GFP_KERNEL | __GFP_ZERO);
...@@ -577,12 +583,11 @@ static int policydb_index(struct policydb *p) ...@@ -577,12 +583,11 @@ static int policydb_index(struct policydb *p)
goto out; goto out;
for (i = 0; i < SYM_NUM; i++) { for (i = 0; i < SYM_NUM; i++) {
rc = -ENOMEM;
p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *), p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
p->symtab[i].nprim, p->symtab[i].nprim,
GFP_KERNEL | __GFP_ZERO); GFP_KERNEL | __GFP_ZERO);
if (!p->sym_val_to_name[i]) if (!p->sym_val_to_name[i])
goto out; return -ENOMEM;
rc = flex_array_prealloc(p->sym_val_to_name[i], rc = flex_array_prealloc(p->sym_val_to_name[i],
0, p->symtab[i].nprim, 0, p->symtab[i].nprim,
...@@ -2211,6 +2216,51 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, ...@@ -2211,6 +2216,51 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
goto out; goto out;
break; break;
} }
case OCON_IBPKEY:
rc = next_entry(nodebuf, fp, sizeof(u32) * 4);
if (rc)
goto out;
c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf));
if (nodebuf[2] > 0xffff ||
nodebuf[3] > 0xffff) {
rc = -EINVAL;
goto out;
}
c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]);
c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]);
rc = context_read_and_validate(&c->context[0],
p,
fp);
if (rc)
goto out;
break;
case OCON_IBENDPORT:
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto out;
len = le32_to_cpu(buf[0]);
rc = str_read(&c->u.ibendport.dev_name, GFP_KERNEL, fp, len);
if (rc)
goto out;
if (buf[1] > 0xff || buf[1] == 0) {
rc = -EINVAL;
goto out;
}
c->u.ibendport.port = le32_to_cpu(buf[1]);
rc = context_read_and_validate(&c->context[0],
p,
fp);
if (rc)
goto out;
break;
} }
} }
} }
...@@ -3140,6 +3190,33 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, ...@@ -3140,6 +3190,33 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
if (rc) if (rc)
return rc; return rc;
break; break;
case OCON_IBPKEY:
*((__be64 *)nodebuf) = cpu_to_be64(c->u.ibpkey.subnet_prefix);
nodebuf[2] = cpu_to_le32(c->u.ibpkey.low_pkey);
nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);
rc = put_entry(nodebuf, sizeof(u32), 4, fp);
if (rc)
return rc;
rc = context_write(p, &c->context[0], fp);
if (rc)
return rc;
break;
case OCON_IBENDPORT:
len = strlen(c->u.ibendport.dev_name);
buf[0] = cpu_to_le32(len);
buf[1] = cpu_to_le32(c->u.ibendport.port);
rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
rc = put_entry(c->u.ibendport.dev_name, 1, len, fp);
if (rc)
return rc;
rc = context_write(p, &c->context[0], fp);
if (rc)
return rc;
break;
} }
} }
} }
......
...@@ -187,6 +187,15 @@ struct ocontext { ...@@ -187,6 +187,15 @@ struct ocontext {
u32 addr[4]; u32 addr[4];
u32 mask[4]; u32 mask[4];
} node6; /* IPv6 node information */ } node6; /* IPv6 node information */
struct {
u64 subnet_prefix;
u16 low_pkey;
u16 high_pkey;
} ibpkey;
struct {
char *dev_name;
u8 port;
} ibendport;
} u; } u;
union { union {
u32 sclass; /* security class for genfs */ u32 sclass; /* security class for genfs */
...@@ -215,14 +224,16 @@ struct genfs { ...@@ -215,14 +224,16 @@ struct genfs {
#define SYM_NUM 8 #define SYM_NUM 8
/* object context array indices */ /* object context array indices */
#define OCON_ISID 0 /* initial SIDs */ #define OCON_ISID 0 /* initial SIDs */
#define OCON_FS 1 /* unlabeled file systems */ #define OCON_FS 1 /* unlabeled file systems */
#define OCON_PORT 2 /* TCP and UDP port numbers */ #define OCON_PORT 2 /* TCP and UDP port numbers */
#define OCON_NETIF 3 /* network interfaces */ #define OCON_NETIF 3 /* network interfaces */
#define OCON_NODE 4 /* nodes */ #define OCON_NODE 4 /* nodes */
#define OCON_FSUSE 5 /* fs_use */ #define OCON_FSUSE 5 /* fs_use */
#define OCON_NODE6 6 /* IPv6 nodes */ #define OCON_NODE6 6 /* IPv6 nodes */
#define OCON_NUM 7 #define OCON_IBPKEY 7 /* Infiniband PKeys */
#define OCON_IBENDPORT 8 /* Infiniband end ports */
#define OCON_NUM 9
/* The policy database */ /* The policy database */
struct policydb { struct policydb {
......
...@@ -70,6 +70,15 @@ ...@@ -70,6 +70,15 @@
#include "ebitmap.h" #include "ebitmap.h"
#include "audit.h" #include "audit.h"
/* Policy capability names */
char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
"network_peer_controls",
"open_perms",
"extended_socket_class",
"always_check_network",
"cgroup_seclabel"
};
int selinux_policycap_netpeer; int selinux_policycap_netpeer;
int selinux_policycap_openperm; int selinux_policycap_openperm;
int selinux_policycap_extsockclass; int selinux_policycap_extsockclass;
...@@ -1986,6 +1995,9 @@ static int convert_context(u32 key, ...@@ -1986,6 +1995,9 @@ static int convert_context(u32 key,
static void security_load_policycaps(void) static void security_load_policycaps(void)
{ {
unsigned int i;
struct ebitmap_node *node;
selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_NETPEER); POLICYDB_CAPABILITY_NETPEER);
selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
...@@ -1997,6 +2009,17 @@ static void security_load_policycaps(void) ...@@ -1997,6 +2009,17 @@ static void security_load_policycaps(void)
selinux_policycap_cgroupseclabel = selinux_policycap_cgroupseclabel =
ebitmap_get_bit(&policydb.policycaps, ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_CGROUPSECLABEL); POLICYDB_CAPABILITY_CGROUPSECLABEL);
for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++)
pr_info("SELinux: policy capability %s=%d\n",
selinux_policycap_names[i],
ebitmap_get_bit(&policydb.policycaps, i));
ebitmap_for_each_positive_bit(&policydb.policycaps, node, i) {
if (i >= ARRAY_SIZE(selinux_policycap_names))
pr_info("SELinux: unknown policy capability %u\n",
i);
}
} }
static int security_preserve_bools(struct policydb *p); static int security_preserve_bools(struct policydb *p);
...@@ -2031,9 +2054,11 @@ int security_load_policy(void *data, size_t len) ...@@ -2031,9 +2054,11 @@ int security_load_policy(void *data, size_t len)
if (!ss_initialized) { if (!ss_initialized) {
avtab_cache_init(); avtab_cache_init();
ebitmap_cache_init();
rc = policydb_read(&policydb, fp); rc = policydb_read(&policydb, fp);
if (rc) { if (rc) {
avtab_cache_destroy(); avtab_cache_destroy();
ebitmap_cache_destroy();
goto out; goto out;
} }
...@@ -2044,6 +2069,7 @@ int security_load_policy(void *data, size_t len) ...@@ -2044,6 +2069,7 @@ int security_load_policy(void *data, size_t len)
if (rc) { if (rc) {
policydb_destroy(&policydb); policydb_destroy(&policydb);
avtab_cache_destroy(); avtab_cache_destroy();
ebitmap_cache_destroy();
goto out; goto out;
} }
...@@ -2051,6 +2077,7 @@ int security_load_policy(void *data, size_t len) ...@@ -2051,6 +2077,7 @@ int security_load_policy(void *data, size_t len)
if (rc) { if (rc) {
policydb_destroy(&policydb); policydb_destroy(&policydb);
avtab_cache_destroy(); avtab_cache_destroy();
ebitmap_cache_destroy();
goto out; goto out;
} }
...@@ -2209,6 +2236,87 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid) ...@@ -2209,6 +2236,87 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
return rc; return rc;
} }
/**
* security_pkey_sid - Obtain the SID for a pkey.
* @subnet_prefix: Subnet Prefix
* @pkey_num: pkey number
* @out_sid: security identifier
*/
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
{
struct ocontext *c;
int rc = 0;
read_lock(&policy_rwlock);
c = policydb.ocontexts[OCON_IBPKEY];
while (c) {
if (c->u.ibpkey.low_pkey <= pkey_num &&
c->u.ibpkey.high_pkey >= pkey_num &&
c->u.ibpkey.subnet_prefix == subnet_prefix)
break;
c = c->next;
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
*out_sid = c->sid[0];
} else
*out_sid = SECINITSID_UNLABELED;
out:
read_unlock(&policy_rwlock);
return rc;
}
/**
* security_ib_endport_sid - Obtain the SID for a subnet management interface.
* @dev_name: device name
* @port: port number
* @out_sid: security identifier
*/
int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid)
{
struct ocontext *c;
int rc = 0;
read_lock(&policy_rwlock);
c = policydb.ocontexts[OCON_IBENDPORT];
while (c) {
if (c->u.ibendport.port == port_num &&
!strncmp(c->u.ibendport.dev_name,
dev_name,
IB_DEVICE_NAME_MAX))
break;
c = c->next;
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
*out_sid = c->sid[0];
} else
*out_sid = SECINITSID_UNLABELED;
out:
read_unlock(&policy_rwlock);
return rc;
}
/** /**
* security_netif_sid - Obtain the SID for a network interface. * security_netif_sid - Obtain the SID for a network interface.
* @name: interface name * @name: interface name
......
...@@ -32,13 +32,11 @@ int sidtab_init(struct sidtab *s) ...@@ -32,13 +32,11 @@ int sidtab_init(struct sidtab *s)
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
{ {
int hvalue, rc = 0; int hvalue;
struct sidtab_node *prev, *cur, *newnode; struct sidtab_node *prev, *cur, *newnode;
if (!s) { if (!s)
rc = -ENOMEM; return -ENOMEM;
goto out;
}
hvalue = SIDTAB_HASH(sid); hvalue = SIDTAB_HASH(sid);
prev = NULL; prev = NULL;
...@@ -48,21 +46,17 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) ...@@ -48,21 +46,17 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
cur = cur->next; cur = cur->next;
} }
if (cur && sid == cur->sid) { if (cur && sid == cur->sid)
rc = -EEXIST; return -EEXIST;
goto out;
}
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
if (!newnode) { if (!newnode)
rc = -ENOMEM; return -ENOMEM;
goto out;
}
newnode->sid = sid; newnode->sid = sid;
if (context_cpy(&newnode->context, context)) { if (context_cpy(&newnode->context, context)) {
kfree(newnode); kfree(newnode);
rc = -ENOMEM; return -ENOMEM;
goto out;
} }
if (prev) { if (prev) {
...@@ -78,8 +72,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) ...@@ -78,8 +72,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
s->nel++; s->nel++;
if (sid >= s->next_sid) if (sid >= s->next_sid)
s->next_sid = sid + 1; s->next_sid = sid + 1;
out: return 0;
return rc;
} }
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment