aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/device_cgroup.c267
1 files changed, 211 insertions, 56 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 1c69e38e3a2c..dd0dc574d78d 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -25,6 +25,12 @@
25 25
26static DEFINE_MUTEX(devcgroup_mutex); 26static DEFINE_MUTEX(devcgroup_mutex);
27 27
28enum devcg_behavior {
29 DEVCG_DEFAULT_NONE,
30 DEVCG_DEFAULT_ALLOW,
31 DEVCG_DEFAULT_DENY,
32};
33
28/* 34/*
29 * exception list locking rules: 35 * exception list locking rules:
30 * hold devcgroup_mutex for update/read. 36 * hold devcgroup_mutex for update/read.
@@ -42,10 +48,9 @@ struct dev_exception_item {
42struct dev_cgroup { 48struct dev_cgroup {
43 struct cgroup_subsys_state css; 49 struct cgroup_subsys_state css;
44 struct list_head exceptions; 50 struct list_head exceptions;
45 enum { 51 enum devcg_behavior behavior;
46 DEVCG_DEFAULT_ALLOW, 52 /* temporary list for pending propagation operations */
47 DEVCG_DEFAULT_DENY, 53 struct list_head propagate_pending;
48 } behavior;
49}; 54};
50 55
51static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) 56static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
@@ -182,35 +187,62 @@ static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
182 __dev_exception_clean(dev_cgroup); 187 __dev_exception_clean(dev_cgroup);
183} 188}
184 189
190static inline bool is_devcg_online(const struct dev_cgroup *devcg)
191{
192 return (devcg->behavior != DEVCG_DEFAULT_NONE);
193}
194
195/**
196 * devcgroup_online - initializes devcgroup's behavior and exceptions based on
197 * parent's
198 * @cgroup: cgroup getting online
199 * returns 0 in case of success, error code otherwise
200 */
201static int devcgroup_online(struct cgroup *cgroup)
202{
203 struct dev_cgroup *dev_cgroup, *parent_dev_cgroup = NULL;
204 int ret = 0;
205
206 mutex_lock(&devcgroup_mutex);
207 dev_cgroup = cgroup_to_devcgroup(cgroup);
208 if (cgroup->parent)
209 parent_dev_cgroup = cgroup_to_devcgroup(cgroup->parent);
210
211 if (parent_dev_cgroup == NULL)
212 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
213 else {
214 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
215 &parent_dev_cgroup->exceptions);
216 if (!ret)
217 dev_cgroup->behavior = parent_dev_cgroup->behavior;
218 }
219 mutex_unlock(&devcgroup_mutex);
220
221 return ret;
222}
223
224static void devcgroup_offline(struct cgroup *cgroup)
225{
226 struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup);
227
228 mutex_lock(&devcgroup_mutex);
229 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
230 mutex_unlock(&devcgroup_mutex);
231}
232
185/* 233/*
186 * called from kernel/cgroup.c with cgroup_lock() held. 234 * called from kernel/cgroup.c with cgroup_lock() held.
187 */ 235 */
188static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) 236static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup)
189{ 237{
190 struct dev_cgroup *dev_cgroup, *parent_dev_cgroup; 238 struct dev_cgroup *dev_cgroup;
191 struct cgroup *parent_cgroup;
192 int ret;
193 239
194 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL); 240 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
195 if (!dev_cgroup) 241 if (!dev_cgroup)
196 return ERR_PTR(-ENOMEM); 242 return ERR_PTR(-ENOMEM);
197 INIT_LIST_HEAD(&dev_cgroup->exceptions); 243 INIT_LIST_HEAD(&dev_cgroup->exceptions);
198 parent_cgroup = cgroup->parent; 244 INIT_LIST_HEAD(&dev_cgroup->propagate_pending);
199 245 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
200 if (parent_cgroup == NULL)
201 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
202 else {
203 parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
204 mutex_lock(&devcgroup_mutex);
205 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
206 &parent_dev_cgroup->exceptions);
207 dev_cgroup->behavior = parent_dev_cgroup->behavior;
208 mutex_unlock(&devcgroup_mutex);
209 if (ret) {
210 kfree(dev_cgroup);
211 return ERR_PTR(ret);
212 }
213 }
214 246
215 return &dev_cgroup->css; 247 return &dev_cgroup->css;
216} 248}
@@ -304,9 +336,11 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
304 * verify if a certain access is allowed. 336 * verify if a certain access is allowed.
305 * @dev_cgroup: dev cgroup to be tested against 337 * @dev_cgroup: dev cgroup to be tested against
306 * @refex: new exception 338 * @refex: new exception
339 * @behavior: behavior of the exception
307 */ 340 */
308static int may_access(struct dev_cgroup *dev_cgroup, 341static bool may_access(struct dev_cgroup *dev_cgroup,
309 struct dev_exception_item *refex) 342 struct dev_exception_item *refex,
343 enum devcg_behavior behavior)
310{ 344{
311 struct dev_exception_item *ex; 345 struct dev_exception_item *ex;
312 bool match = false; 346 bool match = false;
@@ -330,18 +364,29 @@ static int may_access(struct dev_cgroup *dev_cgroup,
330 break; 364 break;
331 } 365 }
332 366
333 /* 367 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
334 * In two cases we'll consider this new exception valid: 368 if (behavior == DEVCG_DEFAULT_ALLOW) {
335 * - the dev cgroup has its default policy to allow + exception list: 369 /* the exception will deny access to certain devices */
336 * the new exception should *not* match any of the exceptions 370 return true;
337 * (behavior == DEVCG_DEFAULT_ALLOW, !match) 371 } else {
338 * - the dev cgroup has its default policy to deny + exception list: 372 /* the exception will allow access to certain devices */
339 * the new exception *should* match the exceptions 373 if (match)
340 * (behavior == DEVCG_DEFAULT_DENY, match) 374 /*
341 */ 375 * a new exception allowing access shouldn't
342 if ((dev_cgroup->behavior == DEVCG_DEFAULT_DENY) == match) 376 * match an parent's exception
343 return 1; 377 */
344 return 0; 378 return false;
379 return true;
380 }
381 } else {
382 /* only behavior == DEVCG_DEFAULT_DENY allowed here */
383 if (match)
384 /* parent has an exception that matches the proposed */
385 return true;
386 else
387 return false;
388 }
389 return false;
345} 390}
346 391
347/* 392/*
@@ -358,7 +403,7 @@ static int parent_has_perm(struct dev_cgroup *childcg,
358 if (!pcg) 403 if (!pcg)
359 return 1; 404 return 1;
360 parent = cgroup_to_devcgroup(pcg); 405 parent = cgroup_to_devcgroup(pcg);
361 return may_access(parent, ex); 406 return may_access(parent, ex, childcg->behavior);
362} 407}
363 408
364/** 409/**
@@ -374,6 +419,111 @@ static inline int may_allow_all(struct dev_cgroup *parent)
374 return parent->behavior == DEVCG_DEFAULT_ALLOW; 419 return parent->behavior == DEVCG_DEFAULT_ALLOW;
375} 420}
376 421
422/**
423 * revalidate_active_exceptions - walks through the active exception list and
424 * revalidates the exceptions based on parent's
425 * behavior and exceptions. The exceptions that
426 * are no longer valid will be removed.
427 * Called with devcgroup_mutex held.
428 * @devcg: cgroup which exceptions will be checked
429 *
430 * This is one of the three key functions for hierarchy implementation.
431 * This function is responsible for re-evaluating all the cgroup's active
432 * exceptions due to a parent's exception change.
433 * Refer to Documentation/cgroups/devices.txt for more details.
434 */
435static void revalidate_active_exceptions(struct dev_cgroup *devcg)
436{
437 struct dev_exception_item *ex;
438 struct list_head *this, *tmp;
439
440 list_for_each_safe(this, tmp, &devcg->exceptions) {
441 ex = container_of(this, struct dev_exception_item, list);
442 if (!parent_has_perm(devcg, ex))
443 dev_exception_rm(devcg, ex);
444 }
445}
446
447/**
448 * get_online_devcg - walks the cgroup tree and fills a list with the online
449 * groups
450 * @root: cgroup used as starting point
451 * @online: list that will be filled with online groups
452 *
453 * Must be called with devcgroup_mutex held. Grabs RCU lock.
454 * Because devcgroup_mutex is held, no devcg will become online or offline
455 * during the tree walk (see devcgroup_online, devcgroup_offline)
456 * A separated list is needed because propagate_behavior() and
457 * propagate_exception() need to allocate memory and can block.
458 */
459static void get_online_devcg(struct cgroup *root, struct list_head *online)
460{
461 struct cgroup *pos;
462 struct dev_cgroup *devcg;
463
464 lockdep_assert_held(&devcgroup_mutex);
465
466 rcu_read_lock();
467 cgroup_for_each_descendant_pre(pos, root) {
468 devcg = cgroup_to_devcgroup(pos);
469 if (is_devcg_online(devcg))
470 list_add_tail(&devcg->propagate_pending, online);
471 }
472 rcu_read_unlock();
473}
474
475/**
476 * propagate_exception - propagates a new exception to the children
477 * @devcg_root: device cgroup that added a new exception
478 * @ex: new exception to be propagated
479 *
480 * returns: 0 in case of success, != 0 in case of error
481 */
482static int propagate_exception(struct dev_cgroup *devcg_root,
483 struct dev_exception_item *ex)
484{
485 struct cgroup *root = devcg_root->css.cgroup;
486 struct dev_cgroup *devcg, *parent, *tmp;
487 int rc = 0;
488 LIST_HEAD(pending);
489
490 get_online_devcg(root, &pending);
491
492 list_for_each_entry_safe(devcg, tmp, &pending, propagate_pending) {
493 parent = cgroup_to_devcgroup(devcg->css.cgroup->parent);
494
495 /*
496 * in case both root's behavior and devcg is allow, a new
497 * restriction means adding to the exception list
498 */
499 if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
500 devcg->behavior == DEVCG_DEFAULT_ALLOW) {
501 rc = dev_exception_add(devcg, ex);
502 if (rc)
503 break;
504 } else {
505 /*
506 * in the other possible cases:
507 * root's behavior: allow, devcg's: deny
508 * root's behavior: deny, devcg's: deny
509 * the exception will be removed
510 */
511 dev_exception_rm(devcg, ex);
512 }
513 revalidate_active_exceptions(devcg);
514
515 list_del_init(&devcg->propagate_pending);
516 }
517 return rc;
518}
519
520static inline bool has_children(struct dev_cgroup *devcgroup)
521{
522 struct cgroup *cgrp = devcgroup->css.cgroup;
523
524 return !list_empty(&cgrp->children);
525}
526
377/* 527/*
378 * Modify the exception list using allow/deny rules. 528 * Modify the exception list using allow/deny rules.
379 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD 529 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
@@ -392,7 +542,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
392{ 542{
393 const char *b; 543 const char *b;
394 char temp[12]; /* 11 + 1 characters needed for a u32 */ 544 char temp[12]; /* 11 + 1 characters needed for a u32 */
395 int count, rc; 545 int count, rc = 0;
396 struct dev_exception_item ex; 546 struct dev_exception_item ex;
397 struct cgroup *p = devcgroup->css.cgroup; 547 struct cgroup *p = devcgroup->css.cgroup;
398 struct dev_cgroup *parent = NULL; 548 struct dev_cgroup *parent = NULL;
@@ -410,6 +560,9 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
410 case 'a': 560 case 'a':
411 switch (filetype) { 561 switch (filetype) {
412 case DEVCG_ALLOW: 562 case DEVCG_ALLOW:
563 if (has_children(devcgroup))
564 return -EINVAL;
565
413 if (!may_allow_all(parent)) 566 if (!may_allow_all(parent))
414 return -EPERM; 567 return -EPERM;
415 dev_exception_clean(devcgroup); 568 dev_exception_clean(devcgroup);
@@ -423,6 +576,9 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
423 return rc; 576 return rc;
424 break; 577 break;
425 case DEVCG_DENY: 578 case DEVCG_DENY:
579 if (has_children(devcgroup))
580 return -EINVAL;
581
426 dev_exception_clean(devcgroup); 582 dev_exception_clean(devcgroup);
427 devcgroup->behavior = DEVCG_DEFAULT_DENY; 583 devcgroup->behavior = DEVCG_DEFAULT_DENY;
428 break; 584 break;
@@ -517,22 +673,28 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
517 dev_exception_rm(devcgroup, &ex); 673 dev_exception_rm(devcgroup, &ex);
518 return 0; 674 return 0;
519 } 675 }
520 return dev_exception_add(devcgroup, &ex); 676 rc = dev_exception_add(devcgroup, &ex);
677 break;
521 case DEVCG_DENY: 678 case DEVCG_DENY:
522 /* 679 /*
523 * If the default policy is to deny by default, try to remove 680 * If the default policy is to deny by default, try to remove
524 * an matching exception instead. And be silent about it: we 681 * an matching exception instead. And be silent about it: we
525 * don't want to break compatibility 682 * don't want to break compatibility
526 */ 683 */
527 if (devcgroup->behavior == DEVCG_DEFAULT_DENY) { 684 if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
528 dev_exception_rm(devcgroup, &ex); 685 dev_exception_rm(devcgroup, &ex);
529 return 0; 686 else
530 } 687 rc = dev_exception_add(devcgroup, &ex);
531 return dev_exception_add(devcgroup, &ex); 688
689 if (rc)
690 break;
691 /* we only propagate new restrictions */
692 rc = propagate_exception(devcgroup, &ex);
693 break;
532 default: 694 default:
533 return -EINVAL; 695 rc = -EINVAL;
534 } 696 }
535 return 0; 697 return rc;
536} 698}
537 699
538static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, 700static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
@@ -571,17 +733,10 @@ struct cgroup_subsys devices_subsys = {
571 .can_attach = devcgroup_can_attach, 733 .can_attach = devcgroup_can_attach,
572 .css_alloc = devcgroup_css_alloc, 734 .css_alloc = devcgroup_css_alloc,
573 .css_free = devcgroup_css_free, 735 .css_free = devcgroup_css_free,
736 .css_online = devcgroup_online,
737 .css_offline = devcgroup_offline,
574 .subsys_id = devices_subsys_id, 738 .subsys_id = devices_subsys_id,
575 .base_cftypes = dev_cgroup_files, 739 .base_cftypes = dev_cgroup_files,
576
577 /*
578 * While devices cgroup has the rudimentary hierarchy support which
579 * checks the parent's restriction, it doesn't properly propagates
580 * config changes in ancestors to their descendents. A child
581 * should only be allowed to add more restrictions to the parent's
582 * configuration. Fix it and remove the following.
583 */
584 .broken_hierarchy = true,
585}; 740};
586 741
587/** 742/**
@@ -609,7 +764,7 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
609 764
610 rcu_read_lock(); 765 rcu_read_lock();
611 dev_cgroup = task_devcgroup(current); 766 dev_cgroup = task_devcgroup(current);
612 rc = may_access(dev_cgroup, &ex); 767 rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
613 rcu_read_unlock(); 768 rcu_read_unlock();
614 769
615 if (!rc) 770 if (!rc)