]>
Commit | Line | Data |
---|---|---|
d291f1a6 DJ |
1 | /* |
2 | * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. | |
3 | * | |
4 | * This software is available to you under a choice of one of two | |
5 | * licenses. You may choose to be licensed under the terms of the GNU | |
6 | * General Public License (GPL) Version 2, available from the file | |
7 | * COPYING in the main directory of this source tree, or the | |
8 | * OpenIB.org BSD license below: | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or | |
11 | * without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistributions of source code must retain the above | |
15 | * copyright notice, this list of conditions and the following | |
16 | * disclaimer. | |
17 | * | |
18 | * - Redistributions in binary form must reproduce the above | |
19 | * copyright notice, this list of conditions and the following | |
20 | * disclaimer in the documentation and/or other materials | |
21 | * provided with the distribution. | |
22 | * | |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
30 | * SOFTWARE. | |
31 | */ | |
32 | ||
33 | #ifdef CONFIG_SECURITY_INFINIBAND | |
34 | ||
35 | #include <linux/security.h> | |
36 | #include <linux/completion.h> | |
37 | #include <linux/list.h> | |
38 | ||
39 | #include <rdma/ib_verbs.h> | |
40 | #include <rdma/ib_cache.h> | |
41 | #include "core_priv.h" | |
47a2b338 | 42 | #include "mad_priv.h" |
d291f1a6 DJ |
43 | |
44 | static struct pkey_index_qp_list *get_pkey_idx_qp_list(struct ib_port_pkey *pp) | |
45 | { | |
46 | struct pkey_index_qp_list *pkey = NULL; | |
47 | struct pkey_index_qp_list *tmp_pkey; | |
48 | struct ib_device *dev = pp->sec->dev; | |
49 | ||
50 | spin_lock(&dev->port_pkey_list[pp->port_num].list_lock); | |
51 | list_for_each_entry(tmp_pkey, | |
52 | &dev->port_pkey_list[pp->port_num].pkey_list, | |
53 | pkey_index_list) { | |
54 | if (tmp_pkey->pkey_index == pp->pkey_index) { | |
55 | pkey = tmp_pkey; | |
56 | break; | |
57 | } | |
58 | } | |
59 | spin_unlock(&dev->port_pkey_list[pp->port_num].list_lock); | |
60 | return pkey; | |
61 | } | |
62 | ||
63 | static int get_pkey_and_subnet_prefix(struct ib_port_pkey *pp, | |
64 | u16 *pkey, | |
65 | u64 *subnet_prefix) | |
66 | { | |
67 | struct ib_device *dev = pp->sec->dev; | |
68 | int ret; | |
69 | ||
70 | ret = ib_get_cached_pkey(dev, pp->port_num, pp->pkey_index, pkey); | |
71 | if (ret) | |
72 | return ret; | |
73 | ||
74 | ret = ib_get_cached_subnet_prefix(dev, pp->port_num, subnet_prefix); | |
75 | ||
76 | return ret; | |
77 | } | |
78 | ||
79 | static int enforce_qp_pkey_security(u16 pkey, | |
80 | u64 subnet_prefix, | |
81 | struct ib_qp_security *qp_sec) | |
82 | { | |
83 | struct ib_qp_security *shared_qp_sec; | |
84 | int ret; | |
85 | ||
86 | ret = security_ib_pkey_access(qp_sec->security, subnet_prefix, pkey); | |
87 | if (ret) | |
88 | return ret; | |
89 | ||
90 | if (qp_sec->qp == qp_sec->qp->real_qp) { | |
91 | list_for_each_entry(shared_qp_sec, | |
92 | &qp_sec->shared_qp_list, | |
93 | shared_qp_list) { | |
94 | ret = security_ib_pkey_access(shared_qp_sec->security, | |
95 | subnet_prefix, | |
96 | pkey); | |
97 | if (ret) | |
98 | return ret; | |
99 | } | |
100 | } | |
101 | return 0; | |
102 | } | |
103 | ||
104 | /* The caller of this function must hold the QP security | |
105 | * mutex of the QP of the security structure in *pps. | |
106 | * | |
107 | * It takes separate ports_pkeys and security structure | |
108 | * because in some cases the pps will be for a new settings | |
109 | * or the pps will be for the real QP and security structure | |
110 | * will be for a shared QP. | |
111 | */ | |
112 | static int check_qp_port_pkey_settings(struct ib_ports_pkeys *pps, | |
113 | struct ib_qp_security *sec) | |
114 | { | |
115 | u64 subnet_prefix; | |
116 | u16 pkey; | |
117 | int ret = 0; | |
118 | ||
119 | if (!pps) | |
120 | return 0; | |
121 | ||
122 | if (pps->main.state != IB_PORT_PKEY_NOT_VALID) { | |
79d0636a DJ |
123 | ret = get_pkey_and_subnet_prefix(&pps->main, |
124 | &pkey, | |
125 | &subnet_prefix); | |
126 | if (ret) | |
127 | return ret; | |
d291f1a6 DJ |
128 | |
129 | ret = enforce_qp_pkey_security(pkey, | |
130 | subnet_prefix, | |
131 | sec); | |
79d0636a DJ |
132 | if (ret) |
133 | return ret; | |
d291f1a6 | 134 | } |
d291f1a6 DJ |
135 | |
136 | if (pps->alt.state != IB_PORT_PKEY_NOT_VALID) { | |
79d0636a DJ |
137 | ret = get_pkey_and_subnet_prefix(&pps->alt, |
138 | &pkey, | |
139 | &subnet_prefix); | |
140 | if (ret) | |
141 | return ret; | |
d291f1a6 DJ |
142 | |
143 | ret = enforce_qp_pkey_security(pkey, | |
144 | subnet_prefix, | |
145 | sec); | |
146 | } | |
147 | ||
148 | return ret; | |
149 | } | |
150 | ||
151 | /* The caller of this function must hold the QP security | |
152 | * mutex. | |
153 | */ | |
154 | static void qp_to_error(struct ib_qp_security *sec) | |
155 | { | |
156 | struct ib_qp_security *shared_qp_sec; | |
157 | struct ib_qp_attr attr = { | |
158 | .qp_state = IB_QPS_ERR | |
159 | }; | |
160 | struct ib_event event = { | |
161 | .event = IB_EVENT_QP_FATAL | |
162 | }; | |
163 | ||
164 | /* If the QP is in the process of being destroyed | |
165 | * the qp pointer in the security structure is | |
166 | * undefined. It cannot be modified now. | |
167 | */ | |
168 | if (sec->destroying) | |
169 | return; | |
170 | ||
171 | ib_modify_qp(sec->qp, | |
172 | &attr, | |
173 | IB_QP_STATE); | |
174 | ||
175 | if (sec->qp->event_handler && sec->qp->qp_context) { | |
176 | event.element.qp = sec->qp; | |
177 | sec->qp->event_handler(&event, | |
178 | sec->qp->qp_context); | |
179 | } | |
180 | ||
181 | list_for_each_entry(shared_qp_sec, | |
182 | &sec->shared_qp_list, | |
183 | shared_qp_list) { | |
184 | struct ib_qp *qp = shared_qp_sec->qp; | |
185 | ||
186 | if (qp->event_handler && qp->qp_context) { | |
187 | event.element.qp = qp; | |
188 | event.device = qp->device; | |
189 | qp->event_handler(&event, | |
190 | qp->qp_context); | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | static inline void check_pkey_qps(struct pkey_index_qp_list *pkey, | |
196 | struct ib_device *device, | |
197 | u8 port_num, | |
198 | u64 subnet_prefix) | |
199 | { | |
200 | struct ib_port_pkey *pp, *tmp_pp; | |
201 | bool comp; | |
202 | LIST_HEAD(to_error_list); | |
203 | u16 pkey_val; | |
204 | ||
205 | if (!ib_get_cached_pkey(device, | |
206 | port_num, | |
207 | pkey->pkey_index, | |
208 | &pkey_val)) { | |
209 | spin_lock(&pkey->qp_list_lock); | |
210 | list_for_each_entry(pp, &pkey->qp_list, qp_list) { | |
211 | if (atomic_read(&pp->sec->error_list_count)) | |
212 | continue; | |
213 | ||
214 | if (enforce_qp_pkey_security(pkey_val, | |
215 | subnet_prefix, | |
216 | pp->sec)) { | |
217 | atomic_inc(&pp->sec->error_list_count); | |
218 | list_add(&pp->to_error_list, | |
219 | &to_error_list); | |
220 | } | |
221 | } | |
222 | spin_unlock(&pkey->qp_list_lock); | |
223 | } | |
224 | ||
225 | list_for_each_entry_safe(pp, | |
226 | tmp_pp, | |
227 | &to_error_list, | |
228 | to_error_list) { | |
229 | mutex_lock(&pp->sec->mutex); | |
230 | qp_to_error(pp->sec); | |
231 | list_del(&pp->to_error_list); | |
232 | atomic_dec(&pp->sec->error_list_count); | |
233 | comp = pp->sec->destroying; | |
234 | mutex_unlock(&pp->sec->mutex); | |
235 | ||
236 | if (comp) | |
237 | complete(&pp->sec->error_complete); | |
238 | } | |
239 | } | |
240 | ||
241 | /* The caller of this function must hold the QP security | |
242 | * mutex. | |
243 | */ | |
244 | static int port_pkey_list_insert(struct ib_port_pkey *pp) | |
245 | { | |
246 | struct pkey_index_qp_list *tmp_pkey; | |
247 | struct pkey_index_qp_list *pkey; | |
248 | struct ib_device *dev; | |
249 | u8 port_num = pp->port_num; | |
250 | int ret = 0; | |
251 | ||
252 | if (pp->state != IB_PORT_PKEY_VALID) | |
253 | return 0; | |
254 | ||
255 | dev = pp->sec->dev; | |
256 | ||
257 | pkey = get_pkey_idx_qp_list(pp); | |
258 | ||
259 | if (!pkey) { | |
260 | bool found = false; | |
261 | ||
262 | pkey = kzalloc(sizeof(*pkey), GFP_KERNEL); | |
263 | if (!pkey) | |
264 | return -ENOMEM; | |
265 | ||
266 | spin_lock(&dev->port_pkey_list[port_num].list_lock); | |
267 | /* Check for the PKey again. A racing process may | |
268 | * have created it. | |
269 | */ | |
270 | list_for_each_entry(tmp_pkey, | |
271 | &dev->port_pkey_list[port_num].pkey_list, | |
272 | pkey_index_list) { | |
273 | if (tmp_pkey->pkey_index == pp->pkey_index) { | |
274 | kfree(pkey); | |
275 | pkey = tmp_pkey; | |
276 | found = true; | |
277 | break; | |
278 | } | |
279 | } | |
280 | ||
281 | if (!found) { | |
282 | pkey->pkey_index = pp->pkey_index; | |
283 | spin_lock_init(&pkey->qp_list_lock); | |
284 | INIT_LIST_HEAD(&pkey->qp_list); | |
285 | list_add(&pkey->pkey_index_list, | |
286 | &dev->port_pkey_list[port_num].pkey_list); | |
287 | } | |
288 | spin_unlock(&dev->port_pkey_list[port_num].list_lock); | |
289 | } | |
290 | ||
291 | spin_lock(&pkey->qp_list_lock); | |
292 | list_add(&pp->qp_list, &pkey->qp_list); | |
293 | spin_unlock(&pkey->qp_list_lock); | |
294 | ||
295 | pp->state = IB_PORT_PKEY_LISTED; | |
296 | ||
297 | return ret; | |
298 | } | |
299 | ||
300 | /* The caller of this function must hold the QP security | |
301 | * mutex. | |
302 | */ | |
303 | static void port_pkey_list_remove(struct ib_port_pkey *pp) | |
304 | { | |
305 | struct pkey_index_qp_list *pkey; | |
306 | ||
307 | if (pp->state != IB_PORT_PKEY_LISTED) | |
308 | return; | |
309 | ||
310 | pkey = get_pkey_idx_qp_list(pp); | |
311 | ||
312 | spin_lock(&pkey->qp_list_lock); | |
313 | list_del(&pp->qp_list); | |
314 | spin_unlock(&pkey->qp_list_lock); | |
315 | ||
316 | /* The setting may still be valid, i.e. after | |
317 | * a destroy has failed for example. | |
318 | */ | |
319 | pp->state = IB_PORT_PKEY_VALID; | |
320 | } | |
321 | ||
322 | static void destroy_qp_security(struct ib_qp_security *sec) | |
323 | { | |
324 | security_ib_free_security(sec->security); | |
325 | kfree(sec->ports_pkeys); | |
326 | kfree(sec); | |
327 | } | |
328 | ||
329 | /* The caller of this function must hold the QP security | |
330 | * mutex. | |
331 | */ | |
332 | static struct ib_ports_pkeys *get_new_pps(const struct ib_qp *qp, | |
333 | const struct ib_qp_attr *qp_attr, | |
334 | int qp_attr_mask) | |
335 | { | |
336 | struct ib_ports_pkeys *new_pps; | |
337 | struct ib_ports_pkeys *qp_pps = qp->qp_sec->ports_pkeys; | |
338 | ||
339 | new_pps = kzalloc(sizeof(*new_pps), GFP_KERNEL); | |
340 | if (!new_pps) | |
341 | return NULL; | |
342 | ||
343 | if (qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) { | |
344 | if (!qp_pps) { | |
345 | new_pps->main.port_num = qp_attr->port_num; | |
346 | new_pps->main.pkey_index = qp_attr->pkey_index; | |
347 | } else { | |
348 | new_pps->main.port_num = (qp_attr_mask & IB_QP_PORT) ? | |
349 | qp_attr->port_num : | |
350 | qp_pps->main.port_num; | |
351 | ||
352 | new_pps->main.pkey_index = | |
353 | (qp_attr_mask & IB_QP_PKEY_INDEX) ? | |
354 | qp_attr->pkey_index : | |
355 | qp_pps->main.pkey_index; | |
356 | } | |
357 | new_pps->main.state = IB_PORT_PKEY_VALID; | |
358 | } else if (qp_pps) { | |
359 | new_pps->main.port_num = qp_pps->main.port_num; | |
360 | new_pps->main.pkey_index = qp_pps->main.pkey_index; | |
361 | if (qp_pps->main.state != IB_PORT_PKEY_NOT_VALID) | |
362 | new_pps->main.state = IB_PORT_PKEY_VALID; | |
363 | } | |
364 | ||
365 | if (qp_attr_mask & IB_QP_ALT_PATH) { | |
366 | new_pps->alt.port_num = qp_attr->alt_port_num; | |
367 | new_pps->alt.pkey_index = qp_attr->alt_pkey_index; | |
368 | new_pps->alt.state = IB_PORT_PKEY_VALID; | |
369 | } else if (qp_pps) { | |
370 | new_pps->alt.port_num = qp_pps->alt.port_num; | |
371 | new_pps->alt.pkey_index = qp_pps->alt.pkey_index; | |
372 | if (qp_pps->alt.state != IB_PORT_PKEY_NOT_VALID) | |
373 | new_pps->alt.state = IB_PORT_PKEY_VALID; | |
374 | } | |
375 | ||
376 | new_pps->main.sec = qp->qp_sec; | |
377 | new_pps->alt.sec = qp->qp_sec; | |
378 | return new_pps; | |
379 | } | |
380 | ||
381 | int ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev) | |
382 | { | |
383 | struct ib_qp *real_qp = qp->real_qp; | |
384 | int ret; | |
385 | ||
386 | ret = ib_create_qp_security(qp, dev); | |
387 | ||
388 | if (ret) | |
389 | return ret; | |
390 | ||
391 | mutex_lock(&real_qp->qp_sec->mutex); | |
392 | ret = check_qp_port_pkey_settings(real_qp->qp_sec->ports_pkeys, | |
393 | qp->qp_sec); | |
394 | ||
395 | if (ret) | |
396 | goto ret; | |
397 | ||
398 | if (qp != real_qp) | |
399 | list_add(&qp->qp_sec->shared_qp_list, | |
400 | &real_qp->qp_sec->shared_qp_list); | |
401 | ret: | |
402 | mutex_unlock(&real_qp->qp_sec->mutex); | |
403 | if (ret) | |
404 | destroy_qp_security(qp->qp_sec); | |
405 | ||
406 | return ret; | |
407 | } | |
408 | ||
409 | void ib_close_shared_qp_security(struct ib_qp_security *sec) | |
410 | { | |
411 | struct ib_qp *real_qp = sec->qp->real_qp; | |
412 | ||
413 | mutex_lock(&real_qp->qp_sec->mutex); | |
414 | list_del(&sec->shared_qp_list); | |
415 | mutex_unlock(&real_qp->qp_sec->mutex); | |
416 | ||
417 | destroy_qp_security(sec); | |
418 | } | |
419 | ||
420 | int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev) | |
421 | { | |
422 | int ret; | |
423 | ||
424 | qp->qp_sec = kzalloc(sizeof(*qp->qp_sec), GFP_KERNEL); | |
425 | if (!qp->qp_sec) | |
426 | return -ENOMEM; | |
427 | ||
428 | qp->qp_sec->qp = qp; | |
429 | qp->qp_sec->dev = dev; | |
430 | mutex_init(&qp->qp_sec->mutex); | |
431 | INIT_LIST_HEAD(&qp->qp_sec->shared_qp_list); | |
432 | atomic_set(&qp->qp_sec->error_list_count, 0); | |
433 | init_completion(&qp->qp_sec->error_complete); | |
434 | ret = security_ib_alloc_security(&qp->qp_sec->security); | |
73827a60 | 435 | if (ret) { |
d291f1a6 | 436 | kfree(qp->qp_sec); |
73827a60 PP |
437 | qp->qp_sec = NULL; |
438 | } | |
d291f1a6 DJ |
439 | |
440 | return ret; | |
441 | } | |
442 | EXPORT_SYMBOL(ib_create_qp_security); | |
443 | ||
444 | void ib_destroy_qp_security_begin(struct ib_qp_security *sec) | |
445 | { | |
446 | mutex_lock(&sec->mutex); | |
447 | ||
448 | /* Remove the QP from the lists so it won't get added to | |
449 | * a to_error_list during the destroy process. | |
450 | */ | |
451 | if (sec->ports_pkeys) { | |
452 | port_pkey_list_remove(&sec->ports_pkeys->main); | |
453 | port_pkey_list_remove(&sec->ports_pkeys->alt); | |
454 | } | |
455 | ||
456 | /* If the QP is already in one or more of those lists | |
457 | * the destroying flag will ensure the to error flow | |
458 | * doesn't operate on an undefined QP. | |
459 | */ | |
460 | sec->destroying = true; | |
461 | ||
462 | /* Record the error list count to know how many completions | |
463 | * to wait for. | |
464 | */ | |
465 | sec->error_comps_pending = atomic_read(&sec->error_list_count); | |
466 | ||
467 | mutex_unlock(&sec->mutex); | |
468 | } | |
469 | ||
470 | void ib_destroy_qp_security_abort(struct ib_qp_security *sec) | |
471 | { | |
472 | int ret; | |
473 | int i; | |
474 | ||
475 | /* If a concurrent cache update is in progress this | |
476 | * QP security could be marked for an error state | |
477 | * transition. Wait for this to complete. | |
478 | */ | |
479 | for (i = 0; i < sec->error_comps_pending; i++) | |
480 | wait_for_completion(&sec->error_complete); | |
481 | ||
482 | mutex_lock(&sec->mutex); | |
483 | sec->destroying = false; | |
484 | ||
485 | /* Restore the position in the lists and verify | |
486 | * access is still allowed in case a cache update | |
487 | * occurred while attempting to destroy. | |
488 | * | |
489 | * Because these setting were listed already | |
490 | * and removed during ib_destroy_qp_security_begin | |
491 | * we know the pkey_index_qp_list for the PKey | |
492 | * already exists so port_pkey_list_insert won't fail. | |
493 | */ | |
494 | if (sec->ports_pkeys) { | |
495 | port_pkey_list_insert(&sec->ports_pkeys->main); | |
496 | port_pkey_list_insert(&sec->ports_pkeys->alt); | |
497 | } | |
498 | ||
499 | ret = check_qp_port_pkey_settings(sec->ports_pkeys, sec); | |
500 | if (ret) | |
501 | qp_to_error(sec); | |
502 | ||
503 | mutex_unlock(&sec->mutex); | |
504 | } | |
505 | ||
506 | void ib_destroy_qp_security_end(struct ib_qp_security *sec) | |
507 | { | |
508 | int i; | |
509 | ||
510 | /* If a concurrent cache update is occurring we must | |
511 | * wait until this QP security structure is processed | |
512 | * in the QP to error flow before destroying it because | |
513 | * the to_error_list is in use. | |
514 | */ | |
515 | for (i = 0; i < sec->error_comps_pending; i++) | |
516 | wait_for_completion(&sec->error_complete); | |
517 | ||
518 | destroy_qp_security(sec); | |
519 | } | |
520 | ||
521 | void ib_security_cache_change(struct ib_device *device, | |
522 | u8 port_num, | |
523 | u64 subnet_prefix) | |
524 | { | |
525 | struct pkey_index_qp_list *pkey; | |
526 | ||
527 | list_for_each_entry(pkey, | |
528 | &device->port_pkey_list[port_num].pkey_list, | |
529 | pkey_index_list) { | |
530 | check_pkey_qps(pkey, | |
531 | device, | |
532 | port_num, | |
533 | subnet_prefix); | |
534 | } | |
535 | } | |
536 | ||
537 | void ib_security_destroy_port_pkey_list(struct ib_device *device) | |
538 | { | |
539 | struct pkey_index_qp_list *pkey, *tmp_pkey; | |
540 | int i; | |
541 | ||
542 | for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) { | |
543 | spin_lock(&device->port_pkey_list[i].list_lock); | |
544 | list_for_each_entry_safe(pkey, | |
545 | tmp_pkey, | |
546 | &device->port_pkey_list[i].pkey_list, | |
547 | pkey_index_list) { | |
548 | list_del(&pkey->pkey_index_list); | |
549 | kfree(pkey); | |
550 | } | |
551 | spin_unlock(&device->port_pkey_list[i].list_lock); | |
552 | } | |
553 | } | |
554 | ||
555 | int ib_security_modify_qp(struct ib_qp *qp, | |
556 | struct ib_qp_attr *qp_attr, | |
557 | int qp_attr_mask, | |
558 | struct ib_udata *udata) | |
559 | { | |
560 | int ret = 0; | |
561 | struct ib_ports_pkeys *tmp_pps; | |
562 | struct ib_ports_pkeys *new_pps; | |
563 | bool special_qp = (qp->qp_type == IB_QPT_SMI || | |
564 | qp->qp_type == IB_QPT_GSI || | |
565 | qp->qp_type >= IB_QPT_RESERVED1); | |
566 | bool pps_change = ((qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) || | |
567 | (qp_attr_mask & IB_QP_ALT_PATH)); | |
568 | ||
569 | if (pps_change && !special_qp) { | |
570 | mutex_lock(&qp->qp_sec->mutex); | |
571 | new_pps = get_new_pps(qp, | |
572 | qp_attr, | |
573 | qp_attr_mask); | |
574 | ||
575 | /* Add this QP to the lists for the new port | |
576 | * and pkey settings before checking for permission | |
577 | * in case there is a concurrent cache update | |
578 | * occurring. Walking the list for a cache change | |
579 | * doesn't acquire the security mutex unless it's | |
580 | * sending the QP to error. | |
581 | */ | |
582 | ret = port_pkey_list_insert(&new_pps->main); | |
583 | ||
584 | if (!ret) | |
585 | ret = port_pkey_list_insert(&new_pps->alt); | |
586 | ||
587 | if (!ret) | |
588 | ret = check_qp_port_pkey_settings(new_pps, | |
589 | qp->qp_sec); | |
590 | } | |
591 | ||
592 | if (!ret) | |
593 | ret = qp->device->modify_qp(qp->real_qp, | |
594 | qp_attr, | |
595 | qp_attr_mask, | |
596 | udata); | |
597 | ||
598 | if (pps_change && !special_qp) { | |
599 | /* Clean up the lists and free the appropriate | |
600 | * ports_pkeys structure. | |
601 | */ | |
602 | if (ret) { | |
603 | tmp_pps = new_pps; | |
604 | } else { | |
605 | tmp_pps = qp->qp_sec->ports_pkeys; | |
606 | qp->qp_sec->ports_pkeys = new_pps; | |
607 | } | |
608 | ||
609 | if (tmp_pps) { | |
610 | port_pkey_list_remove(&tmp_pps->main); | |
611 | port_pkey_list_remove(&tmp_pps->alt); | |
612 | } | |
613 | kfree(tmp_pps); | |
614 | mutex_unlock(&qp->qp_sec->mutex); | |
615 | } | |
616 | return ret; | |
617 | } | |
618 | EXPORT_SYMBOL(ib_security_modify_qp); | |
619 | ||
47a2b338 DJ |
620 | int ib_security_pkey_access(struct ib_device *dev, |
621 | u8 port_num, | |
622 | u16 pkey_index, | |
623 | void *sec) | |
624 | { | |
625 | u64 subnet_prefix; | |
626 | u16 pkey; | |
627 | int ret; | |
628 | ||
629 | ret = ib_get_cached_pkey(dev, port_num, pkey_index, &pkey); | |
630 | if (ret) | |
631 | return ret; | |
632 | ||
633 | ret = ib_get_cached_subnet_prefix(dev, port_num, &subnet_prefix); | |
634 | ||
635 | if (ret) | |
636 | return ret; | |
637 | ||
638 | return security_ib_pkey_access(sec, subnet_prefix, pkey); | |
639 | } | |
640 | EXPORT_SYMBOL(ib_security_pkey_access); | |
641 | ||
642 | static int ib_mad_agent_security_change(struct notifier_block *nb, | |
643 | unsigned long event, | |
644 | void *data) | |
645 | { | |
646 | struct ib_mad_agent *ag = container_of(nb, struct ib_mad_agent, lsm_nb); | |
647 | ||
648 | if (event != LSM_POLICY_CHANGE) | |
649 | return NOTIFY_DONE; | |
650 | ||
651 | ag->smp_allowed = !security_ib_endport_manage_subnet(ag->security, | |
652 | ag->device->name, | |
653 | ag->port_num); | |
654 | ||
655 | return NOTIFY_OK; | |
656 | } | |
657 | ||
658 | int ib_mad_agent_security_setup(struct ib_mad_agent *agent, | |
659 | enum ib_qp_type qp_type) | |
660 | { | |
661 | int ret; | |
662 | ||
663 | ret = security_ib_alloc_security(&agent->security); | |
664 | if (ret) | |
665 | return ret; | |
666 | ||
667 | if (qp_type != IB_QPT_SMI) | |
668 | return 0; | |
669 | ||
670 | ret = security_ib_endport_manage_subnet(agent->security, | |
671 | agent->device->name, | |
672 | agent->port_num); | |
673 | if (ret) | |
674 | return ret; | |
675 | ||
676 | agent->lsm_nb.notifier_call = ib_mad_agent_security_change; | |
677 | ret = register_lsm_notifier(&agent->lsm_nb); | |
678 | if (ret) | |
679 | return ret; | |
680 | ||
681 | agent->smp_allowed = true; | |
682 | agent->lsm_nb_reg = true; | |
683 | return 0; | |
684 | } | |
685 | ||
686 | void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) | |
687 | { | |
688 | security_ib_free_security(agent->security); | |
689 | if (agent->lsm_nb_reg) | |
690 | unregister_lsm_notifier(&agent->lsm_nb); | |
691 | } | |
692 | ||
693 | int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index) | |
694 | { | |
695 | int ret; | |
696 | ||
697 | if (map->agent.qp->qp_type == IB_QPT_SMI && !map->agent.smp_allowed) | |
698 | return -EACCES; | |
699 | ||
700 | ret = ib_security_pkey_access(map->agent.device, | |
701 | map->agent.port_num, | |
702 | pkey_index, | |
703 | map->agent.security); | |
704 | ||
705 | if (ret) | |
706 | return ret; | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
d291f1a6 | 711 | #endif /* CONFIG_SECURITY_INFINIBAND */ |