aboutsummaryrefslogtreecommitdiffstats
path: root/security/device_cgroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/device_cgroup.c')
-rw-r--r--security/device_cgroup.c162
1 files changed, 122 insertions, 40 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 8365909f5f8c..b9048dc46b1a 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -306,57 +306,139 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
306} 306}
307 307
308/** 308/**
309 * may_access - verifies if a new exception is part of what is allowed 309 * match_exception - iterates the exception list trying to match a rule
310 * by a dev cgroup based on the default policy + 310 * based on type, major, minor and access type. It is
311 * exceptions. This is used to make sure a child cgroup 311 * considered a match if an exception is found that
312 * won't have more privileges than its parent or to 312 * will contain the entire range of provided parameters.
313 * verify if a certain access is allowed. 313 * @exceptions: list of exceptions
314 * @dev_cgroup: dev cgroup to be tested against 314 * @type: device type (DEV_BLOCK or DEV_CHAR)
315 * @refex: new exception 315 * @major: device file major number, ~0 to match all
316 * @behavior: behavior of the exception 316 * @minor: device file minor number, ~0 to match all
317 * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
318 *
319 * returns: true in case it matches an exception completely
317 */ 320 */
318static bool may_access(struct dev_cgroup *dev_cgroup, 321static bool match_exception(struct list_head *exceptions, short type,
319 struct dev_exception_item *refex, 322 u32 major, u32 minor, short access)
320 enum devcg_behavior behavior)
321{ 323{
322 struct dev_exception_item *ex; 324 struct dev_exception_item *ex;
323 bool match = false;
324 325
325 rcu_lockdep_assert(rcu_read_lock_held() || 326 list_for_each_entry_rcu(ex, exceptions, list) {
326 lockdep_is_held(&devcgroup_mutex), 327 if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
327 "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}
342
343/**
344 * match_exception_partial - iterates the exception list trying to match a rule
345 * based on type, major, minor and access type. It is
346 * considered a match if an exception's range is
347 * found to contain *any* of the devices specified by
348 * provided parameters. This is used to make sure no
349 * extra access is being granted that is forbidden by
350 * any of the exception list.
351 * @exceptions: list of exceptions
352 * @type: device type (DEV_BLOCK or DEV_CHAR)
353 * @major: device file major number, ~0 to match all
354 * @minor: device file minor number, ~0 to match all
355 * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
356 *
357 * returns: true in case the provided range mat matches an exception completely
358 */
359static bool match_exception_partial(struct list_head *exceptions, short type,
360 u32 major, u32 minor, short access)
361{
362 struct dev_exception_item *ex;
328 363
329 list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) { 364 list_for_each_entry_rcu(ex, exceptions, list) {
330 if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) 365 if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
331 continue; 366 continue;
332 if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR)) 367 if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
333 continue; 368 continue;
334 if (ex->major != ~0 && ex->major != refex->major) 369 /*
370 * We must be sure that both the exception and the provided
371 * range aren't masking all devices
372 */
373 if (ex->major != ~0 && major != ~0 && ex->major != major)
335 continue; 374 continue;
336 if (ex->minor != ~0 && ex->minor != refex->minor) 375 if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)
337 continue; 376 continue;
338 if (refex->access & (~ex->access)) 377 /*
378 * In order to make sure the provided range isn't matching
379 * an exception, all its access bits shouldn't match the
380 * exception's access bits
381 */
382 if (!(access & ex->access))
339 continue; 383 continue;
340 match = true; 384 return true;
341 break;
342 } 385 }
386 return false;
387}
388
389/**
390 * verify_new_ex - verifies if a new exception is part of what is allowed
391 * by a dev cgroup based on the default policy +
392 * exceptions. This is used to make sure a child cgroup
393 * won't have more privileges than its parent
394 * @dev_cgroup: dev cgroup to be tested against
395 * @refex: new exception
396 * @behavior: behavior of the exception's dev_cgroup
397 */
398static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
399 struct dev_exception_item *refex,
400 enum devcg_behavior behavior)
401{
402 bool match = false;
403
404 rcu_lockdep_assert(rcu_read_lock_held() ||
405 lockdep_is_held(&devcgroup_mutex),
406 "device_cgroup:verify_new_ex called without proper synchronization");
343 407
344 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) { 408 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
345 if (behavior == DEVCG_DEFAULT_ALLOW) { 409 if (behavior == DEVCG_DEFAULT_ALLOW) {
346 /* the exception will deny access to certain devices */ 410 /*
411 * new exception in the child doesn't matter, only
412 * adding extra restrictions
413 */
347 return true; 414 return true;
348 } else { 415 } else {
349 /* the exception will allow access to certain devices */ 416 /*
417 * new exception in the child will add more devices
418 * that can be acessed, so it can't match any of
419 * parent's exceptions, even slightly
420 */
421 match = match_exception_partial(&dev_cgroup->exceptions,
422 refex->type,
423 refex->major,
424 refex->minor,
425 refex->access);
426
350 if (match) 427 if (match)
351 /*
352 * a new exception allowing access shouldn't
353 * match an parent's exception
354 */
355 return false; 428 return false;
356 return true; 429 return true;
357 } 430 }
358 } else { 431 } else {
359 /* only behavior == DEVCG_DEFAULT_DENY allowed here */ 432 /*
433 * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore
434 * the new exception will add access to more devices and must
435 * be contained completely in an parent's exception to be
436 * allowed
437 */
438 match = match_exception(&dev_cgroup->exceptions, refex->type,
439 refex->major, refex->minor,
440 refex->access);
441
360 if (match) 442 if (match)
361 /* parent has an exception that matches the proposed */ 443 /* parent has an exception that matches the proposed */
362 return true; 444 return true;
@@ -378,7 +460,7 @@ static int parent_has_perm(struct dev_cgroup *childcg,
378 460
379 if (!parent) 461 if (!parent)
380 return 1; 462 return 1;
381 return may_access(parent, ex, childcg->behavior); 463 return verify_new_ex(parent, ex, childcg->behavior);
382} 464}
383 465
384/** 466/**
@@ -704,18 +786,18 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
704 short access) 786 short access)
705{ 787{
706 struct dev_cgroup *dev_cgroup; 788 struct dev_cgroup *dev_cgroup;
707 struct dev_exception_item ex; 789 bool rc;
708 int rc;
709
710 memset(&ex, 0, sizeof(ex));
711 ex.type = type;
712 ex.major = major;
713 ex.minor = minor;
714 ex.access = access;
715 790
716 rcu_read_lock(); 791 rcu_read_lock();
717 dev_cgroup = task_devcgroup(current); 792 dev_cgroup = task_devcgroup(current);
718 rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior); 793 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW)
794 /* Can't match any of the exceptions, even partially */
795 rc = !match_exception_partial(&dev_cgroup->exceptions,
796 type, major, minor, access);
797 else
798 /* Need to match completely one exception to be allowed */
799 rc = match_exception(&dev_cgroup->exceptions, type, major,
800 minor, access);
719 rcu_read_unlock(); 801 rcu_read_unlock();
720 802
721 if (!rc) 803 if (!rc)