aboutsummaryrefslogtreecommitdiffstats
path: root/security/device_cgroup.c
diff options
context:
space:
mode:
authorPaul Moore <pmoore@redhat.com>2014-06-17 17:30:23 -0400
committerPaul Moore <pmoore@redhat.com>2014-06-17 17:30:23 -0400
commit170b5910d9fbea79de1bb40df22eda5f98250c0c (patch)
treeca9560e878d2842d45c6f99077d0d8b8f8b0f9ba /security/device_cgroup.c
parent47dd0b76ace953bd2c0479076db0d3e3b9594003 (diff)
parent1860e379875dfe7271c649058aeddffe5afd9d0d (diff)
Merge tag 'v3.15' into next
Linux 3.15
Diffstat (limited to 'security/device_cgroup.c')
-rw-r--r--security/device_cgroup.c214
1 files changed, 163 insertions, 51 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index d3b6d2cd3a06..9134dbf70d3e 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -58,11 +58,9 @@ static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
58 58
59static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) 59static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
60{ 60{
61 return css_to_devcgroup(task_css(task, devices_subsys_id)); 61 return css_to_devcgroup(task_css(task, devices_cgrp_id));
62} 62}
63 63
64struct cgroup_subsys devices_subsys;
65
66/* 64/*
67 * called under devcgroup_mutex 65 * called under devcgroup_mutex
68 */ 66 */
@@ -308,57 +306,138 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
308} 306}
309 307
310/** 308/**
311 * may_access - verifies if a new exception is part of what is allowed 309 * match_exception - iterates the exception list trying to find a complete match
312 * by a dev cgroup based on the default policy + 310 * @exceptions: list of exceptions
313 * exceptions. This is used to make sure a child cgroup 311 * @type: device type (DEV_BLOCK or DEV_CHAR)
314 * won't have more privileges than its parent or to 312 * @major: device file major number, ~0 to match all
315 * verify if a certain access is allowed. 313 * @minor: device file minor number, ~0 to match all
316 * @dev_cgroup: dev cgroup to be tested against 314 * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
317 * @refex: new exception 315 *
318 * @behavior: behavior of the exception 316 * It is considered a complete match if an exception is found that will
317 * contain the entire range of provided parameters.
318 *
319 * Return: true in case it matches an exception completely
319 */ 320 */
320static bool may_access(struct dev_cgroup *dev_cgroup, 321static bool match_exception(struct list_head *exceptions, short type,
321 struct dev_exception_item *refex, 322 u32 major, u32 minor, short access)
322 enum devcg_behavior behavior)
323{ 323{
324 struct dev_exception_item *ex; 324 struct dev_exception_item *ex;
325 bool match = false;
326 325
327 rcu_lockdep_assert(rcu_read_lock_held() || 326 list_for_each_entry_rcu(ex, exceptions, list) {
328 lockdep_is_held(&devcgroup_mutex), 327 if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
329 "device_cgroup::may_access() called without proper synchronization"); 328 continue;
329 if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
330 continue;
331 if (ex->major != ~0 && ex->major != major)
332 continue;
333 if (ex->minor != ~0 && ex->minor != minor)
334 continue;
335 /* provided access cannot have more than the exception rule */
336 if (access & (~ex->access))
337 continue;
338 return true;
339 }
340 return false;
341}
330 342
331 list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) { 343/**
332 if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) 344 * match_exception_partial - iterates the exception list trying to find a partial match
345 * @exceptions: list of exceptions
346 * @type: device type (DEV_BLOCK or DEV_CHAR)
347 * @major: device file major number, ~0 to match all
348 * @minor: device file minor number, ~0 to match all
349 * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
350 *
351 * It is considered a partial match if an exception's range is found to
352 * contain *any* of the devices specified by provided parameters. This is
353 * used to make sure no extra access is being granted that is forbidden by
354 * any of the exception list.
355 *
356 * Return: true in case the provided range mat matches an exception completely
357 */
358static bool match_exception_partial(struct list_head *exceptions, short type,
359 u32 major, u32 minor, short access)
360{
361 struct dev_exception_item *ex;
362
363 list_for_each_entry_rcu(ex, exceptions, list) {
364 if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
333 continue; 365 continue;
334 if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR)) 366 if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
335 continue; 367 continue;
336 if (ex->major != ~0 && ex->major != refex->major) 368 /*
369 * We must be sure that both the exception and the provided
370 * range aren't masking all devices
371 */
372 if (ex->major != ~0 && major != ~0 && ex->major != major)
337 continue; 373 continue;
338 if (ex->minor != ~0 && ex->minor != refex->minor) 374 if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)
339 continue; 375 continue;
340 if (refex->access & (~ex->access)) 376 /*
377 * In order to make sure the provided range isn't matching
378 * an exception, all its access bits shouldn't match the
379 * exception's access bits
380 */
381 if (!(access & ex->access))
341 continue; 382 continue;
342 match = true; 383 return true;
343 break;
344 } 384 }
385 return false;
386}
387
388/**
389 * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions
390 * @dev_cgroup: dev cgroup to be tested against
391 * @refex: new exception
392 * @behavior: behavior of the exception's dev_cgroup
393 *
394 * This is used to make sure a child cgroup won't have more privileges
395 * than its parent
396 */
397static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
398 struct dev_exception_item *refex,
399 enum devcg_behavior behavior)
400{
401 bool match = false;
402
403 rcu_lockdep_assert(rcu_read_lock_held() ||
404 lockdep_is_held(&devcgroup_mutex),
405 "device_cgroup:verify_new_ex called without proper synchronization");
345 406
346 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) { 407 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
347 if (behavior == DEVCG_DEFAULT_ALLOW) { 408 if (behavior == DEVCG_DEFAULT_ALLOW) {
348 /* the exception will deny access to certain devices */ 409 /*
410 * new exception in the child doesn't matter, only
411 * adding extra restrictions
412 */
349 return true; 413 return true;
350 } else { 414 } else {
351 /* the exception will allow access to certain devices */ 415 /*
416 * new exception in the child will add more devices
417 * that can be acessed, so it can't match any of
418 * parent's exceptions, even slightly
419 */
420 match = match_exception_partial(&dev_cgroup->exceptions,
421 refex->type,
422 refex->major,
423 refex->minor,
424 refex->access);
425
352 if (match) 426 if (match)
353 /*
354 * a new exception allowing access shouldn't
355 * match an parent's exception
356 */
357 return false; 427 return false;
358 return true; 428 return true;
359 } 429 }
360 } else { 430 } else {
361 /* only behavior == DEVCG_DEFAULT_DENY allowed here */ 431 /*
432 * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore
433 * the new exception will add access to more devices and must
434 * be contained completely in an parent's exception to be
435 * allowed
436 */
437 match = match_exception(&dev_cgroup->exceptions, refex->type,
438 refex->major, refex->minor,
439 refex->access);
440
362 if (match) 441 if (match)
363 /* parent has an exception that matches the proposed */ 442 /* parent has an exception that matches the proposed */
364 return true; 443 return true;
@@ -380,7 +459,38 @@ static int parent_has_perm(struct dev_cgroup *childcg,
380 459
381 if (!parent) 460 if (!parent)
382 return 1; 461 return 1;
383 return may_access(parent, ex, childcg->behavior); 462 return verify_new_ex(parent, ex, childcg->behavior);
463}
464
465/**
466 * parent_allows_removal - verify if it's ok to remove an exception
467 * @childcg: child cgroup from where the exception will be removed
468 * @ex: exception being removed
469 *
470 * When removing an exception in cgroups with default ALLOW policy, it must
471 * be checked if removing it will give the child cgroup more access than the
472 * parent.
473 *
474 * Return: true if it's ok to remove exception, false otherwise
475 */
476static bool parent_allows_removal(struct dev_cgroup *childcg,
477 struct dev_exception_item *ex)
478{
479 struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css));
480
481 if (!parent)
482 return true;
483
484 /* It's always allowed to remove access to devices */
485 if (childcg->behavior == DEVCG_DEFAULT_DENY)
486 return true;
487
488 /*
489 * Make sure you're not removing part or a whole exception existing in
490 * the parent cgroup
491 */
492 return !match_exception_partial(&parent->exceptions, ex->type,
493 ex->major, ex->minor, ex->access);
384} 494}
385 495
386/** 496/**
@@ -498,7 +608,7 @@ static inline bool has_children(struct dev_cgroup *devcgroup)
498 * parent cgroup has the access you're asking for. 608 * parent cgroup has the access you're asking for.
499 */ 609 */
500static int devcgroup_update_access(struct dev_cgroup *devcgroup, 610static int devcgroup_update_access(struct dev_cgroup *devcgroup,
501 int filetype, const char *buffer) 611 int filetype, char *buffer)
502{ 612{
503 const char *b; 613 const char *b;
504 char temp[12]; /* 11 + 1 characters needed for a u32 */ 614 char temp[12]; /* 11 + 1 characters needed for a u32 */
@@ -618,17 +728,21 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
618 728
619 switch (filetype) { 729 switch (filetype) {
620 case DEVCG_ALLOW: 730 case DEVCG_ALLOW:
621 if (!parent_has_perm(devcgroup, &ex))
622 return -EPERM;
623 /* 731 /*
624 * If the default policy is to allow by default, try to remove 732 * If the default policy is to allow by default, try to remove
625 * an matching exception instead. And be silent about it: we 733 * an matching exception instead. And be silent about it: we
626 * don't want to break compatibility 734 * don't want to break compatibility
627 */ 735 */
628 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { 736 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
737 /* Check if the parent allows removing it first */
738 if (!parent_allows_removal(devcgroup, &ex))
739 return -EPERM;
629 dev_exception_rm(devcgroup, &ex); 740 dev_exception_rm(devcgroup, &ex);
630 return 0; 741 break;
631 } 742 }
743
744 if (!parent_has_perm(devcgroup, &ex))
745 return -EPERM;
632 rc = dev_exception_add(devcgroup, &ex); 746 rc = dev_exception_add(devcgroup, &ex);
633 break; 747 break;
634 case DEVCG_DENY: 748 case DEVCG_DENY:
@@ -654,7 +768,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
654} 768}
655 769
656static int devcgroup_access_write(struct cgroup_subsys_state *css, 770static int devcgroup_access_write(struct cgroup_subsys_state *css,
657 struct cftype *cft, const char *buffer) 771 struct cftype *cft, char *buffer)
658{ 772{
659 int retval; 773 int retval;
660 774
@@ -684,13 +798,11 @@ static struct cftype dev_cgroup_files[] = {
684 { } /* terminate */ 798 { } /* terminate */
685}; 799};
686 800
687struct cgroup_subsys devices_subsys = { 801struct cgroup_subsys devices_cgrp_subsys = {
688 .name = "devices",
689 .css_alloc = devcgroup_css_alloc, 802 .css_alloc = devcgroup_css_alloc,
690 .css_free = devcgroup_css_free, 803 .css_free = devcgroup_css_free,
691 .css_online = devcgroup_online, 804 .css_online = devcgroup_online,
692 .css_offline = devcgroup_offline, 805 .css_offline = devcgroup_offline,
693 .subsys_id = devices_subsys_id,
694 .base_cftypes = dev_cgroup_files, 806 .base_cftypes = dev_cgroup_files,
695}; 807};
696 808
@@ -708,18 +820,18 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
708 short access) 820 short access)
709{ 821{
710 struct dev_cgroup *dev_cgroup; 822 struct dev_cgroup *dev_cgroup;
711 struct dev_exception_item ex; 823 bool rc;
712 int rc;
713
714 memset(&ex, 0, sizeof(ex));
715 ex.type = type;
716 ex.major = major;
717 ex.minor = minor;
718 ex.access = access;
719 824
720 rcu_read_lock(); 825 rcu_read_lock();
721 dev_cgroup = task_devcgroup(current); 826 dev_cgroup = task_devcgroup(current);
722 rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior); 827 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW)
828 /* Can't match any of the exceptions, even partially */
829 rc = !match_exception_partial(&dev_cgroup->exceptions,
830 type, major, minor, access);
831 else
832 /* Need to match completely one exception to be allowed */
833 rc = match_exception(&dev_cgroup->exceptions, type, major,
834 minor, access);
723 rcu_read_unlock(); 835 rcu_read_unlock();
724 836
725 if (!rc) 837 if (!rc)