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.c87
1 files changed, 61 insertions, 26 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 44dfc415a379..842c254396db 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -42,7 +42,10 @@ struct dev_exception_item {
42struct dev_cgroup { 42struct dev_cgroup {
43 struct cgroup_subsys_state css; 43 struct cgroup_subsys_state css;
44 struct list_head exceptions; 44 struct list_head exceptions;
45 bool deny_all; 45 enum {
46 DEVCG_DEFAULT_ALLOW,
47 DEVCG_DEFAULT_DENY,
48 } behavior;
46}; 49};
47 50
48static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) 51static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
@@ -182,13 +185,13 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup)
182 parent_cgroup = cgroup->parent; 185 parent_cgroup = cgroup->parent;
183 186
184 if (parent_cgroup == NULL) 187 if (parent_cgroup == NULL)
185 dev_cgroup->deny_all = false; 188 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
186 else { 189 else {
187 parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup); 190 parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
188 mutex_lock(&devcgroup_mutex); 191 mutex_lock(&devcgroup_mutex);
189 ret = dev_exceptions_copy(&dev_cgroup->exceptions, 192 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
190 &parent_dev_cgroup->exceptions); 193 &parent_dev_cgroup->exceptions);
191 dev_cgroup->deny_all = parent_dev_cgroup->deny_all; 194 dev_cgroup->behavior = parent_dev_cgroup->behavior;
192 mutex_unlock(&devcgroup_mutex); 195 mutex_unlock(&devcgroup_mutex);
193 if (ret) { 196 if (ret) {
194 kfree(dev_cgroup); 197 kfree(dev_cgroup);
@@ -260,7 +263,7 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
260 * - List the exceptions in case the default policy is to deny 263 * - List the exceptions in case the default policy is to deny
261 * This way, the file remains as a "whitelist of devices" 264 * This way, the file remains as a "whitelist of devices"
262 */ 265 */
263 if (devcgroup->deny_all == false) { 266 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
264 set_access(acc, ACC_MASK); 267 set_access(acc, ACC_MASK);
265 set_majmin(maj, ~0); 268 set_majmin(maj, ~0);
266 set_majmin(min, ~0); 269 set_majmin(min, ~0);
@@ -314,12 +317,12 @@ static int may_access(struct dev_cgroup *dev_cgroup,
314 * In two cases we'll consider this new exception valid: 317 * In two cases we'll consider this new exception valid:
315 * - the dev cgroup has its default policy to allow + exception list: 318 * - the dev cgroup has its default policy to allow + exception list:
316 * the new exception should *not* match any of the exceptions 319 * the new exception should *not* match any of the exceptions
317 * (!deny_all, !match) 320 * (behavior == DEVCG_DEFAULT_ALLOW, !match)
318 * - the dev cgroup has its default policy to deny + exception list: 321 * - the dev cgroup has its default policy to deny + exception list:
319 * the new exception *should* match the exceptions 322 * the new exception *should* match the exceptions
320 * (deny_all, match) 323 * (behavior == DEVCG_DEFAULT_DENY, match)
321 */ 324 */
322 if (dev_cgroup->deny_all == match) 325 if ((dev_cgroup->behavior == DEVCG_DEFAULT_DENY) == match)
323 return 1; 326 return 1;
324 return 0; 327 return 0;
325} 328}
@@ -341,6 +344,17 @@ static int parent_has_perm(struct dev_cgroup *childcg,
341 return may_access(parent, ex); 344 return may_access(parent, ex);
342} 345}
343 346
347/**
348 * may_allow_all - checks if it's possible to change the behavior to
349 * allow based on parent's rules.
350 * @parent: device cgroup's parent
351 * returns: != 0 in case it's allowed, 0 otherwise
352 */
353static inline int may_allow_all(struct dev_cgroup *parent)
354{
355 return parent->behavior == DEVCG_DEFAULT_ALLOW;
356}
357
344/* 358/*
345 * Modify the exception list using allow/deny rules. 359 * Modify the exception list using allow/deny rules.
346 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD 360 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
@@ -358,9 +372,11 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
358 int filetype, const char *buffer) 372 int filetype, const char *buffer)
359{ 373{
360 const char *b; 374 const char *b;
361 char *endp; 375 char temp[12]; /* 11 + 1 characters needed for a u32 */
362 int count; 376 int count, rc;
363 struct dev_exception_item ex; 377 struct dev_exception_item ex;
378 struct cgroup *p = devcgroup->css.cgroup;
379 struct dev_cgroup *parent = cgroup_to_devcgroup(p->parent);
364 380
365 if (!capable(CAP_SYS_ADMIN)) 381 if (!capable(CAP_SYS_ADMIN))
366 return -EPERM; 382 return -EPERM;
@@ -372,14 +388,18 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
372 case 'a': 388 case 'a':
373 switch (filetype) { 389 switch (filetype) {
374 case DEVCG_ALLOW: 390 case DEVCG_ALLOW:
375 if (!parent_has_perm(devcgroup, &ex)) 391 if (!may_allow_all(parent))
376 return -EPERM; 392 return -EPERM;
377 dev_exception_clean(devcgroup); 393 dev_exception_clean(devcgroup);
378 devcgroup->deny_all = false; 394 rc = dev_exceptions_copy(&devcgroup->exceptions,
395 &parent->exceptions);
396 if (rc)
397 return rc;
398 devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
379 break; 399 break;
380 case DEVCG_DENY: 400 case DEVCG_DENY:
381 dev_exception_clean(devcgroup); 401 dev_exception_clean(devcgroup);
382 devcgroup->deny_all = true; 402 devcgroup->behavior = DEVCG_DEFAULT_DENY;
383 break; 403 break;
384 default: 404 default:
385 return -EINVAL; 405 return -EINVAL;
@@ -402,8 +422,16 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
402 ex.major = ~0; 422 ex.major = ~0;
403 b++; 423 b++;
404 } else if (isdigit(*b)) { 424 } else if (isdigit(*b)) {
405 ex.major = simple_strtoul(b, &endp, 10); 425 memset(temp, 0, sizeof(temp));
406 b = endp; 426 for (count = 0; count < sizeof(temp) - 1; count++) {
427 temp[count] = *b;
428 b++;
429 if (!isdigit(*b))
430 break;
431 }
432 rc = kstrtou32(temp, 10, &ex.major);
433 if (rc)
434 return -EINVAL;
407 } else { 435 } else {
408 return -EINVAL; 436 return -EINVAL;
409 } 437 }
@@ -416,8 +444,16 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
416 ex.minor = ~0; 444 ex.minor = ~0;
417 b++; 445 b++;
418 } else if (isdigit(*b)) { 446 } else if (isdigit(*b)) {
419 ex.minor = simple_strtoul(b, &endp, 10); 447 memset(temp, 0, sizeof(temp));
420 b = endp; 448 for (count = 0; count < sizeof(temp) - 1; count++) {
449 temp[count] = *b;
450 b++;
451 if (!isdigit(*b))
452 break;
453 }
454 rc = kstrtou32(temp, 10, &ex.minor);
455 if (rc)
456 return -EINVAL;
421 } else { 457 } else {
422 return -EINVAL; 458 return -EINVAL;
423 } 459 }
@@ -452,7 +488,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
452 * an matching exception instead. And be silent about it: we 488 * an matching exception instead. And be silent about it: we
453 * don't want to break compatibility 489 * don't want to break compatibility
454 */ 490 */
455 if (devcgroup->deny_all == false) { 491 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
456 dev_exception_rm(devcgroup, &ex); 492 dev_exception_rm(devcgroup, &ex);
457 return 0; 493 return 0;
458 } 494 }
@@ -463,7 +499,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
463 * an matching exception instead. And be silent about it: we 499 * an matching exception instead. And be silent about it: we
464 * don't want to break compatibility 500 * don't want to break compatibility
465 */ 501 */
466 if (devcgroup->deny_all == true) { 502 if (devcgroup->behavior == DEVCG_DEFAULT_DENY) {
467 dev_exception_rm(devcgroup, &ex); 503 dev_exception_rm(devcgroup, &ex);
468 return 0; 504 return 0;
469 } 505 }
@@ -533,10 +569,10 @@ struct cgroup_subsys devices_subsys = {
533 * 569 *
534 * returns 0 on success, -EPERM case the operation is not permitted 570 * returns 0 on success, -EPERM case the operation is not permitted
535 */ 571 */
536static int __devcgroup_check_permission(struct dev_cgroup *dev_cgroup, 572static int __devcgroup_check_permission(short type, u32 major, u32 minor,
537 short type, u32 major, u32 minor,
538 short access) 573 short access)
539{ 574{
575 struct dev_cgroup *dev_cgroup;
540 struct dev_exception_item ex; 576 struct dev_exception_item ex;
541 int rc; 577 int rc;
542 578
@@ -547,6 +583,7 @@ static int __devcgroup_check_permission(struct dev_cgroup *dev_cgroup,
547 ex.access = access; 583 ex.access = access;
548 584
549 rcu_read_lock(); 585 rcu_read_lock();
586 dev_cgroup = task_devcgroup(current);
550 rc = may_access(dev_cgroup, &ex); 587 rc = may_access(dev_cgroup, &ex);
551 rcu_read_unlock(); 588 rcu_read_unlock();
552 589
@@ -558,7 +595,6 @@ static int __devcgroup_check_permission(struct dev_cgroup *dev_cgroup,
558 595
559int __devcgroup_inode_permission(struct inode *inode, int mask) 596int __devcgroup_inode_permission(struct inode *inode, int mask)
560{ 597{
561 struct dev_cgroup *dev_cgroup = task_devcgroup(current);
562 short type, access = 0; 598 short type, access = 0;
563 599
564 if (S_ISBLK(inode->i_mode)) 600 if (S_ISBLK(inode->i_mode))
@@ -570,13 +606,12 @@ int __devcgroup_inode_permission(struct inode *inode, int mask)
570 if (mask & MAY_READ) 606 if (mask & MAY_READ)
571 access |= ACC_READ; 607 access |= ACC_READ;
572 608
573 return __devcgroup_check_permission(dev_cgroup, type, imajor(inode), 609 return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
574 iminor(inode), access); 610 access);
575} 611}
576 612
577int devcgroup_inode_mknod(int mode, dev_t dev) 613int devcgroup_inode_mknod(int mode, dev_t dev)
578{ 614{
579 struct dev_cgroup *dev_cgroup = task_devcgroup(current);
580 short type; 615 short type;
581 616
582 if (!S_ISBLK(mode) && !S_ISCHR(mode)) 617 if (!S_ISBLK(mode) && !S_ISCHR(mode))
@@ -587,7 +622,7 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
587 else 622 else
588 type = DEV_CHAR; 623 type = DEV_CHAR;
589 624
590 return __devcgroup_check_permission(dev_cgroup, type, MAJOR(dev), 625 return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
591 MINOR(dev), ACC_MKNOD); 626 ACC_MKNOD);
592 627
593} 628}