Skip to content

Commit d291f1a

Browse files
Daniel Jurgenspcmoore
authored andcommitted
IB/core: Enforce PKey security on QPs
Add new LSM hooks to allocate and free security contexts and check for permission to access a PKey. Allocate and free a security context when creating and destroying a QP. This context is used for controlling access to PKeys. When a request is made to modify a QP that changes the port, PKey index, or alternate path, check that the QP has permission for the PKey in the PKey table index on the subnet prefix of the port. If the QP is shared make sure all handles to the QP also have access. Store which port and PKey index a QP is using. After the reset to init transition the user can modify the port, PKey index and alternate path independently. So port and PKey settings changes can be a merge of the previous settings and the new ones. In order to maintain access control if there are PKey table or subnet prefix change keep a list of all QPs are using each PKey index on each port. If a change occurs all QPs using that device and port must have access enforced for the new cache settings. These changes add a transaction to the QP modify process. Association with the old port and PKey index must be maintained if the modify fails, and must be removed if it succeeds. Association with the new port and PKey index must be established prior to the modify and removed if the modify fails. 1. When a QP is modified to a particular Port, PKey index or alternate path insert that QP into the appropriate lists. 2. Check permission to access the new settings. 3. If step 2 grants access attempt to modify the QP. 4a. If steps 2 and 3 succeed remove any prior associations. 4b. If ether fails remove the new setting associations. If a PKey table or subnet prefix changes walk the list of QPs and check that they have permission. If not send the QP to the error state and raise a fatal error event. If it's a shared QP make sure all the QPs that share the real_qp have permission as well. If the QP that owns a security structure is denied access the security structure is marked as such and the QP is added to an error_list. Once the moving the QP to error is complete the security structure mark is cleared. Maintaining the lists correctly turns QP destroy into a transaction. The hardware driver for the device frees the ib_qp structure, so while the destroy is in progress the ib_qp pointer in the ib_qp_security struct is undefined. When the destroy process begins the ib_qp_security structure is marked as destroying. This prevents any action from being taken on the QP pointer. After the QP is destroyed successfully it could still listed on an error_list wait for it to be processed by that flow before cleaning up the structure. If the destroy fails the QPs port and PKey settings are reinserted into the appropriate lists, the destroying flag is cleared, and access control is enforced, in case there were any cache changes during the destroy flow. To keep the security changes isolated a new file is used to hold security related functionality. Signed-off-by: Daniel Jurgens <[email protected]> Acked-by: Doug Ledford <[email protected]> [PM: merge fixup in ib_verbs.h and uverbs_cmd.c] Signed-off-by: Paul Moore <[email protected]>
1 parent 883c71f commit d291f1a

File tree

12 files changed

+907
-9
lines changed

12 files changed

+907
-9
lines changed

drivers/infiniband/core/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o \
1010
ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \
1111
device.o fmr_pool.o cache.o netlink.o \
1212
roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \
13-
multicast.o mad.o smi.o agent.o mad_rmpp.o
13+
multicast.o mad.o smi.o agent.o mad_rmpp.o \
14+
security.o
1415
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
1516
ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o
1617
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o

drivers/infiniband/core/cache.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ struct ib_update_work {
5353
struct work_struct work;
5454
struct ib_device *device;
5555
u8 port_num;
56+
bool enforce_security;
5657
};
5758

5859
union ib_gid zgid;
@@ -1042,7 +1043,8 @@ int ib_get_cached_port_state(struct ib_device *device,
10421043
EXPORT_SYMBOL(ib_get_cached_port_state);
10431044

10441045
static void ib_cache_update(struct ib_device *device,
1045-
u8 port)
1046+
u8 port,
1047+
bool enforce_security)
10461048
{
10471049
struct ib_port_attr *tprops = NULL;
10481050
struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache;
@@ -1132,6 +1134,11 @@ static void ib_cache_update(struct ib_device *device,
11321134
tprops->subnet_prefix;
11331135
write_unlock_irq(&device->cache.lock);
11341136

1137+
if (enforce_security)
1138+
ib_security_cache_change(device,
1139+
port,
1140+
tprops->subnet_prefix);
1141+
11351142
kfree(gid_cache);
11361143
kfree(old_pkey_cache);
11371144
kfree(tprops);
@@ -1148,7 +1155,9 @@ static void ib_cache_task(struct work_struct *_work)
11481155
struct ib_update_work *work =
11491156
container_of(_work, struct ib_update_work, work);
11501157

1151-
ib_cache_update(work->device, work->port_num);
1158+
ib_cache_update(work->device,
1159+
work->port_num,
1160+
work->enforce_security);
11521161
kfree(work);
11531162
}
11541163

@@ -1169,6 +1178,12 @@ static void ib_cache_event(struct ib_event_handler *handler,
11691178
INIT_WORK(&work->work, ib_cache_task);
11701179
work->device = event->device;
11711180
work->port_num = event->element.port_num;
1181+
if (event->event == IB_EVENT_PKEY_CHANGE ||
1182+
event->event == IB_EVENT_GID_CHANGE)
1183+
work->enforce_security = true;
1184+
else
1185+
work->enforce_security = false;
1186+
11721187
queue_work(ib_wq, &work->work);
11731188
}
11741189
}
@@ -1194,7 +1209,7 @@ int ib_cache_setup_one(struct ib_device *device)
11941209
goto out;
11951210

11961211
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
1197-
ib_cache_update(device, p + rdma_start_port(device));
1212+
ib_cache_update(device, p + rdma_start_port(device), true);
11981213

11991214
INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
12001215
device, ib_cache_event);

drivers/infiniband/core/core_priv.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@
3939

4040
#include <rdma/ib_verbs.h>
4141

42+
struct pkey_index_qp_list {
43+
struct list_head pkey_index_list;
44+
u16 pkey_index;
45+
/* Lock to hold while iterating the qp_list. */
46+
spinlock_t qp_list_lock;
47+
struct list_head qp_list;
48+
};
49+
4250
#if IS_ENABLED(CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS)
4351
int cma_configfs_init(void);
4452
void cma_configfs_exit(void);
@@ -179,4 +187,73 @@ int ib_nl_handle_ip_res_resp(struct sk_buff *skb,
179187
int ib_get_cached_subnet_prefix(struct ib_device *device,
180188
u8 port_num,
181189
u64 *sn_pfx);
190+
191+
#ifdef CONFIG_SECURITY_INFINIBAND
192+
void ib_security_destroy_port_pkey_list(struct ib_device *device);
193+
194+
void ib_security_cache_change(struct ib_device *device,
195+
u8 port_num,
196+
u64 subnet_prefix);
197+
198+
int ib_security_modify_qp(struct ib_qp *qp,
199+
struct ib_qp_attr *qp_attr,
200+
int qp_attr_mask,
201+
struct ib_udata *udata);
202+
203+
int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev);
204+
void ib_destroy_qp_security_begin(struct ib_qp_security *sec);
205+
void ib_destroy_qp_security_abort(struct ib_qp_security *sec);
206+
void ib_destroy_qp_security_end(struct ib_qp_security *sec);
207+
int ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev);
208+
void ib_close_shared_qp_security(struct ib_qp_security *sec);
209+
#else
210+
static inline void ib_security_destroy_port_pkey_list(struct ib_device *device)
211+
{
212+
}
213+
214+
static inline void ib_security_cache_change(struct ib_device *device,
215+
u8 port_num,
216+
u64 subnet_prefix)
217+
{
218+
}
219+
220+
static inline int ib_security_modify_qp(struct ib_qp *qp,
221+
struct ib_qp_attr *qp_attr,
222+
int qp_attr_mask,
223+
struct ib_udata *udata)
224+
{
225+
return qp->device->modify_qp(qp->real_qp,
226+
qp_attr,
227+
qp_attr_mask,
228+
udata);
229+
}
230+
231+
static inline int ib_create_qp_security(struct ib_qp *qp,
232+
struct ib_device *dev)
233+
{
234+
return 0;
235+
}
236+
237+
static inline void ib_destroy_qp_security_begin(struct ib_qp_security *sec)
238+
{
239+
}
240+
241+
static inline void ib_destroy_qp_security_abort(struct ib_qp_security *sec)
242+
{
243+
}
244+
245+
static inline void ib_destroy_qp_security_end(struct ib_qp_security *sec)
246+
{
247+
}
248+
249+
static inline int ib_open_shared_qp_security(struct ib_qp *qp,
250+
struct ib_device *dev)
251+
{
252+
return 0;
253+
}
254+
255+
static inline void ib_close_shared_qp_security(struct ib_qp_security *sec)
256+
{
257+
}
258+
#endif
182259
#endif /* _CORE_PRIV_H */

drivers/infiniband/core/device.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,30 @@ void ib_get_device_fw_str(struct ib_device *dev, char *str, size_t str_len)
325325
}
326326
EXPORT_SYMBOL(ib_get_device_fw_str);
327327

328+
static int setup_port_pkey_list(struct ib_device *device)
329+
{
330+
int i;
331+
332+
/**
333+
* device->port_pkey_list is indexed directly by the port number,
334+
* Therefore it is declared as a 1 based array with potential empty
335+
* slots at the beginning.
336+
*/
337+
device->port_pkey_list = kcalloc(rdma_end_port(device) + 1,
338+
sizeof(*device->port_pkey_list),
339+
GFP_KERNEL);
340+
341+
if (!device->port_pkey_list)
342+
return -ENOMEM;
343+
344+
for (i = 0; i < (rdma_end_port(device) + 1); i++) {
345+
spin_lock_init(&device->port_pkey_list[i].list_lock);
346+
INIT_LIST_HEAD(&device->port_pkey_list[i].pkey_list);
347+
}
348+
349+
return 0;
350+
}
351+
328352
/**
329353
* ib_register_device - Register an IB device with IB core
330354
* @device:Device to register
@@ -385,6 +409,12 @@ int ib_register_device(struct ib_device *device,
385409
goto out;
386410
}
387411

412+
ret = setup_port_pkey_list(device);
413+
if (ret) {
414+
pr_warn("Couldn't create per port_pkey_list\n");
415+
goto out;
416+
}
417+
388418
ret = ib_cache_setup_one(device);
389419
if (ret) {
390420
pr_warn("Couldn't set up InfiniBand P_Key/GID cache\n");
@@ -468,6 +498,9 @@ void ib_unregister_device(struct ib_device *device)
468498
ib_device_unregister_sysfs(device);
469499
ib_cache_cleanup_one(device);
470500

501+
ib_security_destroy_port_pkey_list(device);
502+
kfree(device->port_pkey_list);
503+
471504
down_write(&lists_rwsem);
472505
spin_lock_irqsave(&device->client_data_lock, flags);
473506
list_for_each_entry_safe(context, tmp, &device->client_data_list, list)

0 commit comments

Comments
 (0)