diff options
Diffstat (limited to 'security/device_cgroup.c')
-rw-r--r-- | security/device_cgroup.c | 101 |
1 files changed, 38 insertions, 63 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index ddd92cec78ed..236fffa9d05e 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c | |||
@@ -59,6 +59,11 @@ static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup) | |||
59 | return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); | 59 | return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); |
60 | } | 60 | } |
61 | 61 | ||
62 | static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) | ||
63 | { | ||
64 | return css_to_devcgroup(task_subsys_state(task, devices_subsys_id)); | ||
65 | } | ||
66 | |||
62 | struct cgroup_subsys devices_subsys; | 67 | struct cgroup_subsys devices_subsys; |
63 | 68 | ||
64 | static int devcgroup_can_attach(struct cgroup_subsys *ss, | 69 | static int devcgroup_can_attach(struct cgroup_subsys *ss, |
@@ -312,10 +317,10 @@ static int may_access_whitelist(struct dev_cgroup *c, | |||
312 | * when adding a new allow rule to a device whitelist, the rule | 317 | * when adding a new allow rule to a device whitelist, the rule |
313 | * must be allowed in the parent device | 318 | * must be allowed in the parent device |
314 | */ | 319 | */ |
315 | static int parent_has_perm(struct cgroup *childcg, | 320 | static int parent_has_perm(struct dev_cgroup *childcg, |
316 | struct dev_whitelist_item *wh) | 321 | struct dev_whitelist_item *wh) |
317 | { | 322 | { |
318 | struct cgroup *pcg = childcg->parent; | 323 | struct cgroup *pcg = childcg->css.cgroup->parent; |
319 | struct dev_cgroup *parent; | 324 | struct dev_cgroup *parent; |
320 | int ret; | 325 | int ret; |
321 | 326 | ||
@@ -341,39 +346,18 @@ static int parent_has_perm(struct cgroup *childcg, | |||
341 | * new access is only allowed if you're in the top-level cgroup, or your | 346 | * new access is only allowed if you're in the top-level cgroup, or your |
342 | * parent cgroup has the access you're asking for. | 347 | * parent cgroup has the access you're asking for. |
343 | */ | 348 | */ |
344 | static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | 349 | static int devcgroup_update_access(struct dev_cgroup *devcgroup, |
345 | struct file *file, const char __user *userbuf, | 350 | int filetype, const char *buffer) |
346 | size_t nbytes, loff_t *ppos) | ||
347 | { | 351 | { |
348 | struct cgroup *cur_cgroup; | 352 | struct dev_cgroup *cur_devcgroup; |
349 | struct dev_cgroup *devcgroup, *cur_devcgroup; | 353 | const char *b; |
350 | int filetype = cft->private; | ||
351 | char *buffer, *b; | ||
352 | int retval = 0, count; | 354 | int retval = 0, count; |
353 | struct dev_whitelist_item wh; | 355 | struct dev_whitelist_item wh; |
354 | 356 | ||
355 | if (!capable(CAP_SYS_ADMIN)) | 357 | if (!capable(CAP_SYS_ADMIN)) |
356 | return -EPERM; | 358 | return -EPERM; |
357 | 359 | ||
358 | devcgroup = cgroup_to_devcgroup(cgroup); | 360 | cur_devcgroup = task_devcgroup(current); |
359 | cur_cgroup = task_cgroup(current, devices_subsys.subsys_id); | ||
360 | cur_devcgroup = cgroup_to_devcgroup(cur_cgroup); | ||
361 | |||
362 | buffer = kmalloc(nbytes+1, GFP_KERNEL); | ||
363 | if (!buffer) | ||
364 | return -ENOMEM; | ||
365 | |||
366 | if (copy_from_user(buffer, userbuf, nbytes)) { | ||
367 | retval = -EFAULT; | ||
368 | goto out1; | ||
369 | } | ||
370 | buffer[nbytes] = 0; /* nul-terminate */ | ||
371 | |||
372 | cgroup_lock(); | ||
373 | if (cgroup_is_removed(cgroup)) { | ||
374 | retval = -ENODEV; | ||
375 | goto out2; | ||
376 | } | ||
377 | 361 | ||
378 | memset(&wh, 0, sizeof(wh)); | 362 | memset(&wh, 0, sizeof(wh)); |
379 | b = buffer; | 363 | b = buffer; |
@@ -392,14 +376,11 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
392 | wh.type = DEV_CHAR; | 376 | wh.type = DEV_CHAR; |
393 | break; | 377 | break; |
394 | default: | 378 | default: |
395 | retval = -EINVAL; | 379 | return -EINVAL; |
396 | goto out2; | ||
397 | } | 380 | } |
398 | b++; | 381 | b++; |
399 | if (!isspace(*b)) { | 382 | if (!isspace(*b)) |
400 | retval = -EINVAL; | 383 | return -EINVAL; |
401 | goto out2; | ||
402 | } | ||
403 | b++; | 384 | b++; |
404 | if (*b == '*') { | 385 | if (*b == '*') { |
405 | wh.major = ~0; | 386 | wh.major = ~0; |
@@ -411,13 +392,10 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
411 | b++; | 392 | b++; |
412 | } | 393 | } |
413 | } else { | 394 | } else { |
414 | retval = -EINVAL; | 395 | return -EINVAL; |
415 | goto out2; | ||
416 | } | ||
417 | if (*b != ':') { | ||
418 | retval = -EINVAL; | ||
419 | goto out2; | ||
420 | } | 396 | } |
397 | if (*b != ':') | ||
398 | return -EINVAL; | ||
421 | b++; | 399 | b++; |
422 | 400 | ||
423 | /* read minor */ | 401 | /* read minor */ |
@@ -431,13 +409,10 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
431 | b++; | 409 | b++; |
432 | } | 410 | } |
433 | } else { | 411 | } else { |
434 | retval = -EINVAL; | 412 | return -EINVAL; |
435 | goto out2; | ||
436 | } | ||
437 | if (!isspace(*b)) { | ||
438 | retval = -EINVAL; | ||
439 | goto out2; | ||
440 | } | 413 | } |
414 | if (!isspace(*b)) | ||
415 | return -EINVAL; | ||
441 | for (b++, count = 0; count < 3; count++, b++) { | 416 | for (b++, count = 0; count < 3; count++, b++) { |
442 | switch (*b) { | 417 | switch (*b) { |
443 | case 'r': | 418 | case 'r': |
@@ -454,8 +429,7 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
454 | count = 3; | 429 | count = 3; |
455 | break; | 430 | break; |
456 | default: | 431 | default: |
457 | retval = -EINVAL; | 432 | return -EINVAL; |
458 | goto out2; | ||
459 | } | 433 | } |
460 | } | 434 | } |
461 | 435 | ||
@@ -463,38 +437,39 @@ handle: | |||
463 | retval = 0; | 437 | retval = 0; |
464 | switch (filetype) { | 438 | switch (filetype) { |
465 | case DEVCG_ALLOW: | 439 | case DEVCG_ALLOW: |
466 | if (!parent_has_perm(cgroup, &wh)) | 440 | if (!parent_has_perm(devcgroup, &wh)) |
467 | retval = -EPERM; | 441 | return -EPERM; |
468 | else | 442 | return dev_whitelist_add(devcgroup, &wh); |
469 | retval = dev_whitelist_add(devcgroup, &wh); | ||
470 | break; | ||
471 | case DEVCG_DENY: | 443 | case DEVCG_DENY: |
472 | dev_whitelist_rm(devcgroup, &wh); | 444 | dev_whitelist_rm(devcgroup, &wh); |
473 | break; | 445 | break; |
474 | default: | 446 | default: |
475 | retval = -EINVAL; | 447 | return -EINVAL; |
476 | goto out2; | ||
477 | } | 448 | } |
449 | return 0; | ||
450 | } | ||
478 | 451 | ||
479 | if (retval == 0) | 452 | static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, |
480 | retval = nbytes; | 453 | const char *buffer) |
481 | 454 | { | |
482 | out2: | 455 | int retval; |
456 | if (!cgroup_lock_live_group(cgrp)) | ||
457 | return -ENODEV; | ||
458 | retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp), | ||
459 | cft->private, buffer); | ||
483 | cgroup_unlock(); | 460 | cgroup_unlock(); |
484 | out1: | ||
485 | kfree(buffer); | ||
486 | return retval; | 461 | return retval; |
487 | } | 462 | } |
488 | 463 | ||
489 | static struct cftype dev_cgroup_files[] = { | 464 | static struct cftype dev_cgroup_files[] = { |
490 | { | 465 | { |
491 | .name = "allow", | 466 | .name = "allow", |
492 | .write = devcgroup_access_write, | 467 | .write_string = devcgroup_access_write, |
493 | .private = DEVCG_ALLOW, | 468 | .private = DEVCG_ALLOW, |
494 | }, | 469 | }, |
495 | { | 470 | { |
496 | .name = "deny", | 471 | .name = "deny", |
497 | .write = devcgroup_access_write, | 472 | .write_string = devcgroup_access_write, |
498 | .private = DEVCG_DENY, | 473 | .private = DEVCG_DENY, |
499 | }, | 474 | }, |
500 | { | 475 | { |