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 \
ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \
device.o fmr_pool.o cache.o netlink.o \
roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \
multicast.o mad.o smi.o agent.o mad_rmpp.o
multicast.o mad.o smi.o agent.o mad_rmpp.o \
security.o
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o
......
......@@ -53,6 +53,7 @@ struct ib_update_work {
struct work_struct work;
struct ib_device *device;
u8 port_num;
bool enforce_security;
};
union ib_gid zgid;
......@@ -911,6 +912,26 @@ int ib_get_cached_pkey(struct ib_device *device,
}
EXPORT_SYMBOL(ib_get_cached_pkey);
int ib_get_cached_subnet_prefix(struct ib_device *device,
u8 port_num,
u64 *sn_pfx)
{
unsigned long flags;
int p;
if (port_num < rdma_start_port(device) ||
port_num > rdma_end_port(device))
return -EINVAL;
p = port_num - rdma_start_port(device);
read_lock_irqsave(&device->cache.lock, flags);
*sn_pfx = device->cache.ports[p].subnet_prefix;
read_unlock_irqrestore(&device->cache.lock, flags);
return 0;
}
EXPORT_SYMBOL(ib_get_cached_subnet_prefix);
int ib_find_cached_pkey(struct ib_device *device,
u8 port_num,
u16 pkey,
......@@ -1022,7 +1043,8 @@ int ib_get_cached_port_state(struct ib_device *device,
EXPORT_SYMBOL(ib_get_cached_port_state);
static void ib_cache_update(struct ib_device *device,
u8 port)
u8 port,
bool enforce_security)
{
struct ib_port_attr *tprops = NULL;
struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache;
......@@ -1108,8 +1130,15 @@ static void ib_cache_update(struct ib_device *device,
device->cache.ports[port - rdma_start_port(device)].port_state =
tprops->state;
device->cache.ports[port - rdma_start_port(device)].subnet_prefix =
tprops->subnet_prefix;
write_unlock_irq(&device->cache.lock);
if (enforce_security)
ib_security_cache_change(device,
port,
tprops->subnet_prefix);
kfree(gid_cache);
kfree(old_pkey_cache);
kfree(tprops);
......@@ -1126,7 +1155,9 @@ static void ib_cache_task(struct work_struct *_work)
struct ib_update_work *work =
container_of(_work, struct ib_update_work, work);
ib_cache_update(work->device, work->port_num);
ib_cache_update(work->device,
work->port_num,
work->enforce_security);
kfree(work);
}
......@@ -1147,6 +1178,12 @@ static void ib_cache_event(struct ib_event_handler *handler,
INIT_WORK(&work->work, ib_cache_task);
work->device = event->device;
work->port_num = event->element.port_num;
if (event->event == IB_EVENT_PKEY_CHANGE ||
event->event == IB_EVENT_GID_CHANGE)
work->enforce_security = true;
else
work->enforce_security = false;
queue_work(ib_wq, &work->work);
}
}
......@@ -1172,7 +1209,7 @@ int ib_cache_setup_one(struct ib_device *device)
goto out;
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
ib_cache_update(device, p + rdma_start_port(device));
ib_cache_update(device, p + rdma_start_port(device), true);
INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
device, ib_cache_event);
......
......@@ -38,6 +38,16 @@
#include <linux/cgroup_rdma.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_mad.h>
#include "mad_priv.h"
struct pkey_index_qp_list {
struct list_head pkey_index_list;
u16 pkey_index;
/* Lock to hold while iterating the qp_list. */
spinlock_t qp_list_lock;
struct list_head qp_list;
};
#if IS_ENABLED(CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS)
int cma_configfs_init(void);
......@@ -176,4 +186,109 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb,
int ib_nl_handle_ip_res_resp(struct sk_buff *skb,
struct netlink_callback *cb);
int ib_get_cached_subnet_prefix(struct ib_device *device,
u8 port_num,
u64 *sn_pfx);
#ifdef CONFIG_SECURITY_INFINIBAND
int ib_security_pkey_access(struct ib_device *dev,
u8 port_num,
u16 pkey_index,
void *sec);
void ib_security_destroy_port_pkey_list(struct ib_device *device);
void ib_security_cache_change(struct ib_device *device,
u8 port_num,
u64 subnet_prefix);
int ib_security_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask,
struct ib_udata *udata);
int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev);
void ib_destroy_qp_security_begin(struct ib_qp_security *sec);
void ib_destroy_qp_security_abort(struct ib_qp_security *sec);
void ib_destroy_qp_security_end(struct ib_qp_security *sec);
int ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev);
void ib_close_shared_qp_security(struct ib_qp_security *sec);
int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
enum ib_qp_type qp_type);
void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent);
int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index);
#else
static inline int ib_security_pkey_access(struct ib_device *dev,
u8 port_num,
u16 pkey_index,
void *sec)
{
return 0;
}
static inline void ib_security_destroy_port_pkey_list(struct ib_device *device)
{
}
static inline void ib_security_cache_change(struct ib_device *device,
u8 port_num,
u64 subnet_prefix)
{
}
static inline int ib_security_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask,
struct ib_udata *udata)
{
return qp->device->modify_qp(qp->real_qp,
qp_attr,
qp_attr_mask,
udata);
}
static inline int ib_create_qp_security(struct ib_qp *qp,
struct ib_device *dev)
{
return 0;
}
static inline void ib_destroy_qp_security_begin(struct ib_qp_security *sec)
{
}
static inline void ib_destroy_qp_security_abort(struct ib_qp_security *sec)
{
}
static inline void ib_destroy_qp_security_end(struct ib_qp_security *sec)
{
}
static inline int ib_open_shared_qp_security(struct ib_qp *qp,
struct ib_device *dev)
{
return 0;
}
static inline void ib_close_shared_qp_security(struct ib_qp_security *sec)
{
}
static inline int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
enum ib_qp_type qp_type)
{
return 0;
}
static inline void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent)
{
}
static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map,
u16 pkey_index)
{
return 0;
}
#endif
#endif /* _CORE_PRIV_H */
......@@ -39,6 +39,8 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/security.h>
#include <linux/notifier.h>
#include <rdma/rdma_netlink.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>
......@@ -82,6 +84,14 @@ static LIST_HEAD(client_list);
static DEFINE_MUTEX(device_mutex);
static DECLARE_RWSEM(lists_rwsem);
static int ib_security_change(struct notifier_block *nb, unsigned long event,
void *lsm_data);
static void ib_policy_change_task(struct work_struct *work);
static DECLARE_WORK(ib_policy_change_work, ib_policy_change_task);
static struct notifier_block ibdev_lsm_nb = {
.notifier_call = ib_security_change,
};
static int ib_device_check_mandatory(struct ib_device *device)
{
......@@ -325,6 +335,64 @@ void ib_get_device_fw_str(struct ib_device *dev, char *str, size_t str_len)
}
EXPORT_SYMBOL(ib_get_device_fw_str);
static int setup_port_pkey_list(struct ib_device *device)
{
int i;
/**
* device->port_pkey_list is indexed directly by the port number,
* Therefore it is declared as a 1 based array with potential empty
* slots at the beginning.
*/
device->port_pkey_list = kcalloc(rdma_end_port(device) + 1,
sizeof(*device->port_pkey_list),
GFP_KERNEL);
if (!device->port_pkey_list)
return -ENOMEM;
for (i = 0; i < (rdma_end_port(device) + 1); i++) {
spin_lock_init(&device->port_pkey_list[i].list_lock);
INIT_LIST_HEAD(&device->port_pkey_list[i].pkey_list);
}
return 0;
}
static void ib_policy_change_task(struct work_struct *work)
{
struct ib_device *dev;
down_read(&lists_rwsem);
list_for_each_entry(dev, &device_list, core_list) {
int i;
for (i = rdma_start_port(dev); i <= rdma_end_port(dev); i++) {
u64 sp;
int ret = ib_get_cached_subnet_prefix(dev,
i,
&sp);
WARN_ONCE(ret,
"ib_get_cached_subnet_prefix err: %d, this should never happen here\n",
ret);
ib_security_cache_change(dev, i, sp);
}
}
up_read(&lists_rwsem);
}
static int ib_security_change(struct notifier_block *nb, unsigned long event,
void *lsm_data)
{
if (event != LSM_POLICY_CHANGE)
return NOTIFY_DONE;
schedule_work(&ib_policy_change_work);
return NOTIFY_OK;
}
/**
* ib_register_device - Register an IB device with IB core
* @device:Device to register
......@@ -385,6 +453,12 @@ int ib_register_device(struct ib_device *device,
goto out;
}
ret = setup_port_pkey_list(device);
if (ret) {
pr_warn("Couldn't create per port_pkey_list\n");
goto out;
}
ret = ib_cache_setup_one(device);
if (ret) {
pr_warn("Couldn't set up InfiniBand P_Key/GID cache\n");
......@@ -468,6 +542,9 @@ void ib_unregister_device(struct ib_device *device)
ib_device_unregister_sysfs(device);
ib_cache_cleanup_one(device);
ib_security_destroy_port_pkey_list(device);
kfree(device->port_pkey_list);
down_write(&lists_rwsem);
spin_lock_irqsave(&device->client_data_lock, flags);
list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
......@@ -1082,10 +1159,18 @@ static int __init ib_core_init(void)
goto err_sa;
}
ret = register_lsm_notifier(&ibdev_lsm_nb);
if (ret) {
pr_warn("Couldn't register LSM notifier. ret %d\n", ret);
goto err_ibnl_clients;
}
ib_cache_setup();
return 0;
err_ibnl_clients:
ib_remove_ibnl_clients();
err_sa:
ib_sa_cleanup();
err_mad:
......@@ -1105,6 +1190,7 @@ static int __init ib_core_init(void)
static void __exit ib_core_cleanup(void)
{
unregister_lsm_notifier(&ibdev_lsm_nb);
ib_cache_cleanup();
ib_remove_ibnl_clients();
ib_sa_cleanup();
......
......@@ -40,9 +40,11 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/security.h>
#include <rdma/ib_cache.h>
#include "mad_priv.h"
#include "core_priv.h"
#include "mad_rmpp.h"
#include "smi.h"
#include "opa_smi.h"
......@@ -369,6 +371,12 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
atomic_set(&mad_agent_priv->refcount, 1);
init_completion(&mad_agent_priv->comp);
ret2 = ib_mad_agent_security_setup(&mad_agent_priv->agent, qp_type);
if (ret2) {
ret = ERR_PTR(ret2);
goto error4;
}
spin_lock_irqsave(&port_priv->reg_lock, flags);
mad_agent_priv->agent.hi_tid = ++ib_mad_client_id;
......@@ -386,7 +394,7 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
if (method) {
if (method_in_use(&method,
mad_reg_req))
goto error4;
goto error5;
}
}
ret2 = add_nonoui_reg_req(mad_reg_req, mad_agent_priv,
......@@ -402,14 +410,14 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
if (is_vendor_method_in_use(
vendor_class,
mad_reg_req))
goto error4;
goto error5;
}
}
ret2 = add_oui_reg_req(mad_reg_req, mad_agent_priv);
}
if (ret2) {
ret = ERR_PTR(ret2);
goto error4;
goto error5;
}
}
......@@ -418,9 +426,10 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
spin_unlock_irqrestore(&port_priv->reg_lock, flags);
return &mad_agent_priv->agent;
error4:
error5:
spin_unlock_irqrestore(&port_priv->reg_lock, flags);
ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
error4:
kfree(reg_req);
error3:
kfree(mad_agent_priv);
......@@ -491,6 +500,7 @@ struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
struct ib_mad_agent *ret;
struct ib_mad_snoop_private *mad_snoop_priv;
int qpn;
int err;
/* Validate parameters */
if ((is_snooping_sends(mad_snoop_flags) && !snoop_handler) ||
......@@ -525,17 +535,25 @@ struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
mad_snoop_priv->agent.port_num = port_num;
mad_snoop_priv->mad_snoop_flags = mad_snoop_flags;
init_completion(&mad_snoop_priv->comp);
err = ib_mad_agent_security_setup(&mad_snoop_priv->agent, qp_type);
if (err) {
ret = ERR_PTR(err);
goto error2;
}
mad_snoop_priv->snoop_index = register_snoop_agent(
&port_priv->qp_info[qpn],
mad_snoop_priv);
if (mad_snoop_priv->snoop_index < 0) {
ret = ERR_PTR(mad_snoop_priv->snoop_index);
goto error2;
goto error3;
}
atomic_set(&mad_snoop_priv->refcount, 1);
return &mad_snoop_priv->agent;
error3:
ib_mad_agent_security_cleanup(&mad_snoop_priv->agent);
error2:
kfree(mad_snoop_priv);
error1:
......@@ -581,6 +599,8 @@ static void unregister_mad_agent(struct ib_mad_agent_private *mad_agent_priv)
deref_mad_agent(mad_agent_priv);
wait_for_completion(&mad_agent_priv->comp);
ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
kfree(mad_agent_priv->reg_req);
kfree(mad_agent_priv);
}
......@@ -599,6 +619,8 @@ static void unregister_mad_snoop(struct ib_mad_snoop_private *mad_snoop_priv)
deref_snoop_agent(mad_snoop_priv);
wait_for_completion(&mad_snoop_priv->comp);
ib_mad_agent_security_cleanup(&mad_snoop_priv->agent);
kfree(mad_snoop_priv);
}
......@@ -1215,12 +1237,16 @@ int ib_post_send_mad(struct ib_mad_send_buf *send_buf,
/* Walk list of send WRs and post each on send list */
for (; send_buf; send_buf = next_send_buf) {
mad_send_wr = container_of(send_buf,
struct ib_mad_send_wr_private,
send_buf);
mad_agent_priv = mad_send_wr->mad_agent_priv;
ret = ib_mad_enforce_security(mad_agent_priv,
mad_send_wr->send_wr.pkey_index);
if (ret)
goto error;
if (!send_buf->mad_agent->send_handler ||
(send_buf->timeout_ms &&
!send_buf->mad_agent->recv_handler)) {
......@@ -1946,6 +1972,14 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
struct ib_mad_send_wr_private *mad_send_wr;
struct ib_mad_send_wc mad_send_wc;
unsigned long flags;
int ret;
ret = ib_mad_enforce_security(mad_agent_priv,
mad_recv_wc->wc->pkey_index);
if (ret) {
ib_free_recv_mad(mad_recv_wc);
deref_mad_agent(mad_agent_priv);
}
INIT_LIST_HEAD(&mad_recv_wc->rmpp_list);
list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list);
......@@ -2003,6 +2037,8 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
mad_recv_wc);
deref_mad_agent(mad_agent_priv);
}
return;
}
static enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv,
......
This diff is collapsed.
......@@ -1508,6 +1508,10 @@ static int create_qp(struct ib_uverbs_file *file,
}
if (cmd->qp_type != IB_QPT_XRC_TGT) {
ret = ib_create_qp_security(qp, device);
if (ret)
goto err_cb;
qp->real_qp = qp;
qp->device = device;
qp->pd = pd;
......@@ -2002,14 +2006,17 @@ static int modify_qp(struct ib_uverbs_file *file,
if (ret)
goto release_qp;
}
ret = qp->device->modify_qp(qp, attr,
ret = ib_security_modify_qp(qp,
attr,
modify_qp_mask(qp->qp_type,
cmd->base.attr_mask),
udata);
} else {
ret = ib_modify_qp(qp, attr,
modify_qp_mask(qp->qp_type,
cmd->base.attr_mask));
ret = ib_security_modify_qp(qp,
attr,
modify_qp_mask(qp->qp_type,
cmd->base.attr_mask),
NULL);
}
release_qp:
......
......@@ -44,6 +44,7 @@
#include <linux/in.h>
#include <linux/in6.h>
#include <net/addrconf.h>
#include <linux/security.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
......@@ -713,11 +714,19 @@ static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp,
{
struct ib_qp *qp;
unsigned long flags;
int err;
qp = kzalloc(sizeof *qp, GFP_KERNEL);
if (!qp)
return ERR_PTR(-ENOMEM);
qp->real_qp = real_qp;
err = ib_open_shared_qp_security(qp, real_qp->device);
if (err) {
kfree(qp);
return ERR_PTR(err);
}
qp->real_qp = real_qp;
atomic_inc(&real_qp->usecnt);
qp->device = real_qp->device;
......@@ -804,6 +813,12 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
if (IS_ERR(qp))
return qp;
ret = ib_create_qp_security(qp, device);
if (ret) {
ib_destroy_qp(qp);
return ERR_PTR(ret);
}
qp->device = device;
qp->real_qp = qp;
qp->uobject = NULL;
......@@ -1266,7 +1281,7 @@ int ib_modify_qp(struct ib_qp *qp,
return ret;
}
return qp->device->modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL);
return ib_security_modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL);
}
EXPORT_SYMBOL(ib_modify_qp);
......@@ -1295,6 +1310,7 @@ int ib_close_qp(struct ib_qp *qp)
spin_unlock_irqrestore(&real_qp->device->event_handler_lock, flags);
atomic_dec(&real_qp->usecnt);
ib_close_shared_qp_security(qp->qp_sec);
kfree(qp);
return 0;
......@@ -1335,6 +1351,7 @@ int ib_destroy_qp(struct ib_qp *qp)
struct ib_cq *scq, *rcq;
struct ib_srq *srq;
struct ib_rwq_ind_table *ind_tbl;
struct ib_qp_security *sec;
int ret;
WARN_ON_ONCE(qp->mrs_used > 0);
......@@ -1350,6 +1367,9 @@ int ib_destroy_qp(struct ib_qp *qp)
rcq = qp->recv_cq;
srq = qp->srq;
ind_tbl = qp->rwq_ind_tbl;
sec = qp->qp_sec;
if (sec)
ib_destroy_qp_security_begin(sec);
if (!qp->uobject)
rdma_rw_cleanup_mrs(qp);
......@@ -1366,6 +1386,11 @@ int ib_destroy_qp(struct ib_qp *qp)
atomic_dec(&srq->usecnt);
if (ind_tbl)
atomic_dec(&ind_tbl->usecnt);
if (sec)
ib_destroy_qp_security_end(sec);
} else {
if (sec)
ib_destroy_qp_security_abort(sec);
}
return ret;
......
......@@ -2544,10 +2544,25 @@ EXPORT_SYMBOL_GPL(nfs_set_sb_security);
int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
struct nfs_mount_info *mount_info)
{
int error;
unsigned long kflags = 0, kflags_out = 0;
/* clone any lsm security options from the parent to the new sb */
if (d_inode(mntroot)->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops)
return -ESTALE;
return security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
kflags |= SECURITY_LSM_NATIVE_LABELS;
error = security_sb_clone_mnt_opts(mount_info->cloned->sb, s, kflags,
&kflags_out);
if (error)
return error;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
return 0;
}
EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
......
......@@ -21,6 +21,7 @@
#include <linux/path.h>
#include <linux/key.h>
#include <linux/skbuff.h>
#include <rdma/ib_verbs.h>
struct lsm_network_audit {
int netif;
......@@ -45,6 +46,16 @@ struct lsm_ioctlop_audit {
u16 cmd;
};
struct lsm_ibpkey_audit {
u64 subnet_prefix;
u16 pkey;
};
struct lsm_ibendport_audit {
char dev_name[IB_DEVICE_NAME_MAX];
u8 port;
};
/* Auxiliary data to use in generating the audit record. */
struct common_audit_data {
char type;
......@@ -60,6 +71,8 @@ struct common_audit_data {
#define LSM_AUDIT_DATA_DENTRY 10
#define LSM_AUDIT_DATA_IOCTL_OP 11
#define LSM_AUDIT_DATA_FILE 12
#define LSM_AUDIT_DATA_IBPKEY 13
#define LSM_AUDIT_DATA_IBENDPORT 14
union {
struct path path;
struct dentry *dentry;
......@@ -77,6 +90,8 @@ struct common_audit_data {
char *kmod_name;
struct lsm_ioctlop_audit *op;
struct file *file;
struct lsm_ibpkey_audit *ibpkey;
struct lsm_ibendport_audit *ibendport;
} u;
/* this union contains LSM specific data */
union {
......
......@@ -8,6 +8,7 @@
* Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
* Copyright (C) 2015 Intel Corporation.
* Copyright (C) 2015 Casey Schaufler <casey@schaufler-ca.com>
* Copyright (C) 2016 Mellanox Techonologies
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -911,6 +912,26 @@
* associated with the TUN device's security structure.
* @security pointer to the TUN devices's security structure.
*
* Security hooks for Infiniband
*
* @ib_pkey_access:
* Check permission to access a pkey when modifing a QP.
* @subnet_prefix the subnet prefix of the port being used.
* @pkey the pkey to be accessed.
* @sec pointer to a security structure.
* @ib_endport_manage_subnet:
* Check permissions to send and receive SMPs on a end port.
* @dev_name the IB device name (i.e. mlx4_0).
* @port_num the port number.
* @sec pointer to a security structure.
* @ib_alloc_security:
* Allocate a security structure for Infiniband objects.
* @sec pointer to a security structure pointer.
* Returns 0 on success, non-zero on failure
* @ib_free_security:
* Deallocate an Infiniband security structure.
* @sec contains the security structure to be freed.
*
* Security hooks for XFRM operations.
*
* @xfrm_policy_alloc_security:
......@@ -1388,7 +1409,9 @@ union security_list_options {
unsigned long kern_flags,
unsigned long *set_kern_flags);
int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
struct super_block *newsb);
struct super_block *newsb,
unsigned long kern_flags,
unsigned long *set_kern_flags);
int (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
int (*dentry_init_security)(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
......@@ -1620,6 +1643,14 @@ union security_list_options {
int (*tun_dev_open)(void *security);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
int (*ib_pkey_access)(void *sec, u64 subnet_prefix, u16 pkey);
int (*ib_endport_manage_subnet)(void *sec, const char *dev_name,
u8 port_num);
int (*ib_alloc_security)(void **sec);
void (*ib_free_security)(void *sec);
#endif /* CONFIG_SECURITY_INFINIBAND */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp,
struct xfrm_user_sec_ctx *sec_ctx,
......@@ -1851,6 +1882,12 @@ struct security_hook_heads {
struct list_head tun_dev_attach;
struct list_head tun_dev_open;
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
struct list_head ib_pkey_access;
struct list_head ib_endport_manage_subnet;
struct list_head ib_alloc_security;
struct list_head ib_free_security;
#endif /* CONFIG_SECURITY_INFINIBAND */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
struct list_head xfrm_policy_alloc_security;
struct list_head xfrm_policy_clone_security;
......
......@@ -6,6 +6,7 @@
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
* Copyright (C) 2001 James Morris <jmorris@intercode.com.au>
* Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
* Copyright (C) 2016 Mellanox Techonologies
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -68,6 +69,10 @@ struct audit_krule;
struct user_namespace;
struct timezone;
enum lsm_event {
LSM_POLICY_CHANGE,
};
/* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit);
......@@ -163,6 +168,10 @@ struct security_mnt_opts {
int num_mnt_opts;
};
int call_lsm_notifier(enum lsm_event event, void *data);
int register_lsm_notifier(struct notifier_block *nb);
int unregister_lsm_notifier(struct notifier_block *nb);
static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
{
opts->mnt_opts = NULL;
......@@ -240,7 +249,9 @@ int security_sb_set_mnt_opts(struct super_block *sb,
unsigned long kern_flags,
unsigned long *set_kern_flags);
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
struct super_block *newsb);
struct super_block *newsb,
unsigned long kern_flags,
unsigned long *set_kern_flags);
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
......@@ -381,6 +392,21 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
struct security_mnt_opts {
};
static inline int call_lsm_notifier(enum lsm_event event, void *data)
{
return 0;
}
static inline int register_lsm_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int unregister_lsm_notifier(struct notifier_block *nb)
{
return 0;
}
static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
{
}
......@@ -581,7 +607,9 @@ static inline int security_sb_set_mnt_opts(struct super_block *sb,
}
static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb,
struct super_block *newsb)
struct super_block *newsb,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
return 0;
}
......@@ -1406,6 +1434,32 @@ static inline int security_tun_dev_open(void *security)
}
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey);
int security_ib_endport_manage_subnet(void *sec, const char *name, u8 port_num);
int security_ib_alloc_security(void **sec);
void security_ib_free_security(void *sec);
#else /* CONFIG_SECURITY_INFINIBAND */
static inline int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey)
{
return 0;
}
static inline int security_ib_endport_manage_subnet(void *sec, const char *dev_name, u8 port_num)
{
return 0;
}
static inline int security_ib_alloc_security(void **sec)
{
return 0;
}
static inline void security_ib_free_security(void *sec)
{
}
#endif /* CONFIG_SECURITY_INFINIBAND */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
......
......@@ -575,6 +575,10 @@ struct ib_mad_agent {
u32 flags;
u8 port_num;
u8 rmpp_version;
void *security;
bool smp_allowed;
bool lsm_nb_reg;
struct notifier_block lsm_nb;
};
/**
......
......@@ -1614,6 +1614,45 @@ struct ib_rwq_ind_table_init_attr {
struct ib_wq **ind_tbl;
};
enum port_pkey_state {
IB_PORT_PKEY_NOT_VALID = 0,
IB_PORT_PKEY_VALID = 1,
IB_PORT_PKEY_LISTED = 2,
};
struct ib_qp_security;
struct ib_port_pkey {
enum port_pkey_state state;
u16 pkey_index;
u8 port_num;
struct list_head qp_list;
struct list_head to_error_list;
struct ib_qp_security *sec;
};
struct ib_ports_pkeys {
struct ib_port_pkey main;
struct ib_port_pkey alt;
};
struct ib_qp_security {
struct ib_qp *qp;
struct ib_device *dev;
/* Hold this mutex when changing port and pkey settings. */
struct mutex mutex;
struct ib_ports_pkeys *ports_pkeys;
/* A list of all open shared QP handles. Required to enforce security
* properly for all users of a shared QP.
*/
struct list_head shared_qp_list;
void *security;
bool destroying;
atomic_t error_list_count;
struct completion error_complete;
int error_comps_pending;
};
/*
* @max_write_sge: Maximum SGE elements per RDMA WRITE request.
* @max_read_sge: Maximum SGE elements per RDMA READ request.
......@@ -1643,6 +1682,7 @@ struct ib_qp {
u32 max_read_sge;
enum ib_qp_type qp_type;
struct ib_rwq_ind_table *rwq_ind_tbl;
struct ib_qp_security *qp_sec;
};
struct ib_mr {
......@@ -1891,6 +1931,7 @@ enum ib_mad_result {
};
struct ib_port_cache {
u64 subnet_prefix;
struct ib_pkey_cache *pkey;
struct ib_gid_table *gid;
u8 lmc;
......@@ -1940,6 +1981,12 @@ struct rdma_netdev {
union ib_gid *gid, u16 mlid);
};
struct ib_port_pkey_list {
/* Lock to hold while modifying the list. */
spinlock_t list_lock;
struct list_head pkey_list;
};
struct ib_device {
/* Do not access @dma_device directly from ULP nor from HW drivers. */
struct device *dma_device;
......@@ -1963,6 +2010,8 @@ struct ib_device {
int num_comp_vectors;
struct ib_port_pkey_list *port_pkey_list;
struct iw_cm_verbs *iwcm;
/**
......
......@@ -54,6 +54,15 @@ config SECURITY_NETWORK
implement socket and networking access controls.
If you are unsure how to answer this question, answer N.
config SECURITY_INFINIBAND
bool "Infiniband Security Hooks"
depends on SECURITY && INFINIBAND
help
This enables the Infiniband security hooks.
If enabled, a security module can use these hooks to
implement Infiniband access controls.
If you are unsure how to answer this question, answer N.
config SECURITY_NETWORK_XFRM
bool "XFRM (IPSec) Networking Security Hooks"
depends on XFRM && SECURITY_NETWORK
......
......@@ -410,6 +410,22 @@ static void dump_common_audit_data(struct audit_buffer *ab,
audit_log_format(ab, " kmod=");
audit_log_untrustedstring(ab, a->u.kmod_name);
break;
case LSM_AUDIT_DATA_IBPKEY: {
struct in6_addr sbn_pfx;
memset(&sbn_pfx.s6_addr, 0,
sizeof(sbn_pfx.s6_addr));
memcpy(&sbn_pfx.s6_addr, &a->u.ibpkey->subnet_prefix,
sizeof(a->u.ibpkey->subnet_prefix));
audit_log_format(ab, " pkey=0x%x subnet_prefix=%pI6c",
a->u.ibpkey->pkey, &sbn_pfx);
break;
}
case LSM_AUDIT_DATA_IBENDPORT:
audit_log_format(ab, " device=%s port_num=%u",
a->u.ibendport->dev_name,
a->u.ibendport->port);
break;
} /* switch (a->type) */
}
......
......@@ -4,6 +4,7 @@
* Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
* Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
* Copyright (C) 2016 Mellanox Technologies
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -34,6 +35,8 @@
#define SECURITY_NAME_MAX 10
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
char *lsm_names;
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
......@@ -165,6 +168,24 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
panic("%s - Cannot get early memory.\n", __func__);
}
int call_lsm_notifier(enum lsm_event event, void *data)
{
return atomic_notifier_call_chain(&lsm_notifier_chain, event, data);
}
EXPORT_SYMBOL(call_lsm_notifier);
int register_lsm_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&lsm_notifier_chain, nb);
}
EXPORT_SYMBOL(register_lsm_notifier);
int unregister_lsm_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&lsm_notifier_chain, nb);
}
EXPORT_SYMBOL(unregister_lsm_notifier);
/*
* Hook list operation macros.
*
......@@ -399,9 +420,12 @@ int security_sb_set_mnt_opts(struct super_block *sb,
EXPORT_SYMBOL(security_sb_set_mnt_opts);
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
struct super_block *newsb)
struct super_block *newsb,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb);
return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb,
kern_flags, set_kern_flags);
}
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
......@@ -1515,6 +1539,33 @@ EXPORT_SYMBOL(security_tun_dev_open);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey)
{
return call_int_hook(ib_pkey_access, 0, sec, subnet_prefix, pkey);
}
EXPORT_SYMBOL(security_ib_pkey_access);
int security_ib_endport_manage_subnet(void *sec, const char *dev_name, u8 port_num)
{
return call_int_hook(ib_endport_manage_subnet, 0, sec, dev_name, port_num);
}
EXPORT_SYMBOL(security_ib_endport_manage_subnet);
int security_ib_alloc_security(void **sec)
{
return call_int_hook(ib_alloc_security, 0, sec);
}
EXPORT_SYMBOL(security_ib_alloc_security);
void security_ib_free_security(void *sec)
{
call_void_hook(ib_free_security, sec);
}
EXPORT_SYMBOL(security_ib_free_security);
#endif /* CONFIG_SECURITY_INFINIBAND */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
......
......@@ -5,7 +5,7 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
netnode.o netport.o exports.o \
netnode.o netport.o ibpkey.o exports.o \
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
......
This diff is collapsed.
/*
* 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>
#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", \
"rename", "execute", "quotaon", "mounton", "audit_access", \
......@@ -231,6 +231,10 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } },
{ "smc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "infiniband_pkey",
{ "access", NULL } },
{ "infiniband_endport",
{ "manage_subnet", 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 @@
*
* Copyright (C) 2001,2002 Networks Associates Technology, Inc.
* 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
* it under the terms of the GNU General Public License version 2,
......@@ -139,6 +140,16 @@ struct key_security_struct {
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;
#endif /* _SELINUX_OBJSEC_H_ */
......@@ -36,10 +36,11 @@
#define POLICYDB_VERSION_DEFAULT_TYPE 28
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
#define POLICYDB_VERSION_XPERMS_IOCTL 30
#define POLICYDB_VERSION_INFINIBAND 31
/* Range of policy versions we understand*/
#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 */
#define SE_MNTMASK 0x0f
......@@ -76,6 +77,8 @@ enum {
};
#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_openperm;
extern int selinux_policycap_extsockclass;
......@@ -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_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_node_sid(u16 domain, void *addr, u32 addrlen,
......
......@@ -41,15 +41,6 @@
#include "objsec.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;
static int __init checkreqprot_setup(char *str)
......@@ -163,6 +154,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
avc_ss_reset(0);
selnl_notify_setenforce(selinux_enforcing);
selinux_status_update_setenforce(selinux_enforcing);
if (!selinux_enforcing)
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
}
length = count;
out:
......@@ -1750,9 +1743,9 @@ static int sel_make_policycap(void)
sel_remove_entries(policycap_dir);
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,
policycap_names[iter]);
selinux_policycap_names[iter]);
else
dentry = d_alloc_name(policycap_dir, "unknown");
......
......@@ -24,6 +24,8 @@
#define BITS_PER_U64 (sizeof(u64) * 8)
static struct kmem_cache *ebitmap_node_cachep;
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
{
struct ebitmap_node *n1, *n2;
......@@ -54,7 +56,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
n = src->node;
prev = NULL;
while (n) {
new = kzalloc(sizeof(*new), GFP_ATOMIC);
new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
if (!new) {
ebitmap_destroy(dst);
return -ENOMEM;
......@@ -162,7 +164,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
if (e_iter == NULL ||
offset >= e_iter->startbit + EBITMAP_SIZE) {
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)
goto netlbl_import_failure;
e_iter->startbit = offset - (offset % EBITMAP_SIZE);
......@@ -288,7 +290,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
prev->next = n->next;
else
e->node = n->next;
kfree(n);
kmem_cache_free(ebitmap_node_cachep, n);
}
return 0;
}
......@@ -299,7 +301,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
if (!value)
return 0;
new = kzalloc(sizeof(*new), GFP_ATOMIC);
new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
if (!new)
return -ENOMEM;
......@@ -332,7 +334,7 @@ void ebitmap_destroy(struct ebitmap *e)
while (n) {
temp = n;
n = n->next;
kfree(temp);
kmem_cache_free(ebitmap_node_cachep, temp);
}
e->highbit = 0;
......@@ -400,7 +402,7 @@ int ebitmap_read(struct ebitmap *e, void *fp)
if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
struct ebitmap_node *tmp;
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
tmp = kmem_cache_zalloc(ebitmap_node_cachep, GFP_KERNEL);
if (!tmp) {
printk(KERN_ERR
"SELinux: ebitmap: out of memory\n");
......@@ -519,3 +521,15 @@ int ebitmap_write(struct ebitmap *e, void *fp)
}
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);
int ebitmap_read(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
int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_catmap **catmap);
......
......@@ -17,6 +17,11 @@
*
* 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) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
......@@ -76,81 +81,86 @@ static struct policydb_compat_info policydb_compat[] = {
{
.version = POLICYDB_VERSION_BASE,
.sym_num = SYM_NUM - 3,
.ocon_num = OCON_NUM - 1,
.ocon_num = OCON_NUM - 3,
},
{
.version = POLICYDB_VERSION_BOOL,
.sym_num = SYM_NUM - 2,
.ocon_num = OCON_NUM - 1,
.ocon_num = OCON_NUM - 3,
},
{
.version = POLICYDB_VERSION_IPV6,
.sym_num = SYM_NUM - 2,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_NLCLASS,
.sym_num = SYM_NUM - 2,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_MLS,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_AVTAB,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_RANGETRANS,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_POLCAP,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_PERMISSIVE,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_BOUNDARY,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_FILENAME_TRANS,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_ROLETRANS,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_DEFAULT_TYPE,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_CONSTRAINT_NAMES,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_XPERMS_IOCTL,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM - 2,
},
{
.version = POLICYDB_VERSION_INFINIBAND,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
};
......@@ -538,34 +548,30 @@ static int policydb_index(struct policydb *p)
symtab_hash_eval(p->symtab);
#endif
rc = -ENOMEM;
p->class_val_to_struct = kcalloc(p->p_classes.nprim,
sizeof(*p->class_val_to_struct),
GFP_KERNEL);
if (!p->class_val_to_struct)
goto out;
return -ENOMEM;
rc = -ENOMEM;
p->role_val_to_struct = kcalloc(p->p_roles.nprim,
sizeof(*p->role_val_to_struct),
GFP_KERNEL);
if (!p->role_val_to_struct)
goto out;
return -ENOMEM;
rc = -ENOMEM;
p->user_val_to_struct = kcalloc(p->p_users.nprim,
sizeof(*p->user_val_to_struct),
GFP_KERNEL);
if (!p->user_val_to_struct)
goto out;
return -ENOMEM;
/* 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->p_types.nprim,
GFP_KERNEL | __GFP_ZERO);
if (!p->type_val_to_struct_array)
goto out;
return -ENOMEM;
rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
p->p_types.nprim, GFP_KERNEL | __GFP_ZERO);
......@@ -577,12 +583,11 @@ static int policydb_index(struct policydb *p)
goto out;
for (i = 0; i < SYM_NUM; i++) {
rc = -ENOMEM;
p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
p->symtab[i].nprim,
GFP_KERNEL | __GFP_ZERO);
if (!p->sym_val_to_name[i])
goto out;
return -ENOMEM;
rc = flex_array_prealloc(p->sym_val_to_name[i],
0, p->symtab[i].nprim,
......@@ -2211,6 +2216,51 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
goto out;
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,
if (rc)
return rc;
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 {
u32 addr[4];
u32 mask[4];
} node6; /* IPv6 node information */
struct {
u64 subnet_prefix;
u16 low_pkey;
u16 high_pkey;
} ibpkey;
struct {
char *dev_name;
u8 port;
} ibendport;
} u;
union {
u32 sclass; /* security class for genfs */
......@@ -215,14 +224,16 @@ struct genfs {
#define SYM_NUM 8
/* object context array indices */
#define OCON_ISID 0 /* initial SIDs */
#define OCON_FS 1 /* unlabeled file systems */
#define OCON_PORT 2 /* TCP and UDP port numbers */
#define OCON_NETIF 3 /* network interfaces */
#define OCON_NODE 4 /* nodes */
#define OCON_FSUSE 5 /* fs_use */
#define OCON_NODE6 6 /* IPv6 nodes */
#define OCON_NUM 7
#define OCON_ISID 0 /* initial SIDs */
#define OCON_FS 1 /* unlabeled file systems */
#define OCON_PORT 2 /* TCP and UDP port numbers */
#define OCON_NETIF 3 /* network interfaces */
#define OCON_NODE 4 /* nodes */
#define OCON_FSUSE 5 /* fs_use */
#define OCON_NODE6 6 /* IPv6 nodes */
#define OCON_IBPKEY 7 /* Infiniband PKeys */
#define OCON_IBENDPORT 8 /* Infiniband end ports */
#define OCON_NUM 9
/* The policy database */
struct policydb {
......
......@@ -70,6 +70,15 @@
#include "ebitmap.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_openperm;
int selinux_policycap_extsockclass;
......@@ -1986,6 +1995,9 @@ static int convert_context(u32 key,
static void security_load_policycaps(void)
{
unsigned int i;
struct ebitmap_node *node;
selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_NETPEER);
selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
......@@ -1997,6 +2009,17 @@ static void security_load_policycaps(void)
selinux_policycap_cgroupseclabel =
ebitmap_get_bit(&policydb.policycaps,
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);
......@@ -2031,9 +2054,11 @@ int security_load_policy(void *data, size_t len)
if (!ss_initialized) {
avtab_cache_init();
ebitmap_cache_init();
rc = policydb_read(&policydb, fp);
if (rc) {
avtab_cache_destroy();
ebitmap_cache_destroy();
goto out;
}
......@@ -2044,6 +2069,7 @@ int security_load_policy(void *data, size_t len)
if (rc) {
policydb_destroy(&policydb);
avtab_cache_destroy();
ebitmap_cache_destroy();
goto out;
}
......@@ -2051,6 +2077,7 @@ int security_load_policy(void *data, size_t len)
if (rc) {
policydb_destroy(&policydb);
avtab_cache_destroy();
ebitmap_cache_destroy();
goto out;
}
......@@ -2209,6 +2236,87 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
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.
* @name: interface name
......
......@@ -32,13 +32,11 @@ int sidtab_init(struct sidtab *s)
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
{
int hvalue, rc = 0;
int hvalue;
struct sidtab_node *prev, *cur, *newnode;
if (!s) {
rc = -ENOMEM;
goto out;
}
if (!s)
return -ENOMEM;
hvalue = SIDTAB_HASH(sid);
prev = NULL;
......@@ -48,21 +46,17 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
cur = cur->next;
}
if (cur && sid == cur->sid) {
rc = -EEXIST;
goto out;
}
if (cur && sid == cur->sid)
return -EEXIST;
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
if (!newnode) {
rc = -ENOMEM;
goto out;
}
if (!newnode)
return -ENOMEM;
newnode->sid = sid;
if (context_cpy(&newnode->context, context)) {
kfree(newnode);
rc = -ENOMEM;
goto out;
return -ENOMEM;
}
if (prev) {
......@@ -78,8 +72,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
s->nel++;
if (sid >= s->next_sid)
s->next_sid = sid + 1;
out:
return rc;
return 0;
}
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