aboutsummaryrefslogtreecommitdiffstats
path: root/security/device_cgroup.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-07-26 11:48:49 -0400
committerIngo Molnar <mingo@elte.hu>2008-07-26 11:48:49 -0400
commitc3cc99ff5d24e2eeaf7ec2032e720681916990e3 (patch)
treec3e74171bbbd2adde9d60b9db1c440415c8d2831 /security/device_cgroup.c
parent38ffbe66d59051fd9cfcfc8545f164700e2fa3bc (diff)
parent024e8ac04453b3525448c31ef39848cf675ba6db (diff)
Merge branch 'linus' into x86/xen
Diffstat (limited to 'security/device_cgroup.c')
-rw-r--r--security/device_cgroup.c158
1 files changed, 68 insertions, 90 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index ddd92cec78ed..7bd296cca041 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -41,6 +41,7 @@ struct dev_whitelist_item {
41 short type; 41 short type;
42 short access; 42 short access;
43 struct list_head list; 43 struct list_head list;
44 struct rcu_head rcu;
44}; 45};
45 46
46struct dev_cgroup { 47struct dev_cgroup {
@@ -59,6 +60,11 @@ static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
59 return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); 60 return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id));
60} 61}
61 62
63static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
64{
65 return css_to_devcgroup(task_subsys_state(task, devices_subsys_id));
66}
67
62struct cgroup_subsys devices_subsys; 68struct cgroup_subsys devices_subsys;
63 69
64static int devcgroup_can_attach(struct cgroup_subsys *ss, 70static int devcgroup_can_attach(struct cgroup_subsys *ss,
@@ -128,11 +134,19 @@ static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
128 } 134 }
129 135
130 if (whcopy != NULL) 136 if (whcopy != NULL)
131 list_add_tail(&whcopy->list, &dev_cgroup->whitelist); 137 list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist);
132 spin_unlock(&dev_cgroup->lock); 138 spin_unlock(&dev_cgroup->lock);
133 return 0; 139 return 0;
134} 140}
135 141
142static void whitelist_item_free(struct rcu_head *rcu)
143{
144 struct dev_whitelist_item *item;
145
146 item = container_of(rcu, struct dev_whitelist_item, rcu);
147 kfree(item);
148}
149
136/* 150/*
137 * called under cgroup_lock() 151 * called under cgroup_lock()
138 * since the list is visible to other tasks, we need the spinlock also 152 * since the list is visible to other tasks, we need the spinlock also
@@ -156,8 +170,8 @@ static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
156remove: 170remove:
157 walk->access &= ~wh->access; 171 walk->access &= ~wh->access;
158 if (!walk->access) { 172 if (!walk->access) {
159 list_del(&walk->list); 173 list_del_rcu(&walk->list);
160 kfree(walk); 174 call_rcu(&walk->rcu, whitelist_item_free);
161 } 175 }
162 } 176 }
163 spin_unlock(&dev_cgroup->lock); 177 spin_unlock(&dev_cgroup->lock);
@@ -188,7 +202,7 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
188 } 202 }
189 wh->minor = wh->major = ~0; 203 wh->minor = wh->major = ~0;
190 wh->type = DEV_ALL; 204 wh->type = DEV_ALL;
191 wh->access = ACC_MKNOD | ACC_READ | ACC_WRITE; 205 wh->access = ACC_MASK;
192 list_add(&wh->list, &dev_cgroup->whitelist); 206 list_add(&wh->list, &dev_cgroup->whitelist);
193 } else { 207 } else {
194 parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup); 208 parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
@@ -250,11 +264,10 @@ static char type_to_char(short type)
250 264
251static void set_majmin(char *str, unsigned m) 265static void set_majmin(char *str, unsigned m)
252{ 266{
253 memset(str, 0, MAJMINLEN);
254 if (m == ~0) 267 if (m == ~0)
255 sprintf(str, "*"); 268 strcpy(str, "*");
256 else 269 else
257 snprintf(str, MAJMINLEN, "%u", m); 270 sprintf(str, "%u", m);
258} 271}
259 272
260static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, 273static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
@@ -264,15 +277,15 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
264 struct dev_whitelist_item *wh; 277 struct dev_whitelist_item *wh;
265 char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; 278 char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
266 279
267 spin_lock(&devcgroup->lock); 280 rcu_read_lock();
268 list_for_each_entry(wh, &devcgroup->whitelist, list) { 281 list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) {
269 set_access(acc, wh->access); 282 set_access(acc, wh->access);
270 set_majmin(maj, wh->major); 283 set_majmin(maj, wh->major);
271 set_majmin(min, wh->minor); 284 set_majmin(min, wh->minor);
272 seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type), 285 seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
273 maj, min, acc); 286 maj, min, acc);
274 } 287 }
275 spin_unlock(&devcgroup->lock); 288 rcu_read_unlock();
276 289
277 return 0; 290 return 0;
278} 291}
@@ -312,10 +325,10 @@ static int may_access_whitelist(struct dev_cgroup *c,
312 * when adding a new allow rule to a device whitelist, the rule 325 * when adding a new allow rule to a device whitelist, the rule
313 * must be allowed in the parent device 326 * must be allowed in the parent device
314 */ 327 */
315static int parent_has_perm(struct cgroup *childcg, 328static int parent_has_perm(struct dev_cgroup *childcg,
316 struct dev_whitelist_item *wh) 329 struct dev_whitelist_item *wh)
317{ 330{
318 struct cgroup *pcg = childcg->parent; 331 struct cgroup *pcg = childcg->css.cgroup->parent;
319 struct dev_cgroup *parent; 332 struct dev_cgroup *parent;
320 int ret; 333 int ret;
321 334
@@ -341,39 +354,19 @@ static int parent_has_perm(struct cgroup *childcg,
341 * new access is only allowed if you're in the top-level cgroup, or your 354 * 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. 355 * parent cgroup has the access you're asking for.
343 */ 356 */
344static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, 357static int devcgroup_update_access(struct dev_cgroup *devcgroup,
345 struct file *file, const char __user *userbuf, 358 int filetype, const char *buffer)
346 size_t nbytes, loff_t *ppos)
347{ 359{
348 struct cgroup *cur_cgroup; 360 struct dev_cgroup *cur_devcgroup;
349 struct dev_cgroup *devcgroup, *cur_devcgroup; 361 const char *b;
350 int filetype = cft->private; 362 char *endp;
351 char *buffer, *b;
352 int retval = 0, count; 363 int retval = 0, count;
353 struct dev_whitelist_item wh; 364 struct dev_whitelist_item wh;
354 365
355 if (!capable(CAP_SYS_ADMIN)) 366 if (!capable(CAP_SYS_ADMIN))
356 return -EPERM; 367 return -EPERM;
357 368
358 devcgroup = cgroup_to_devcgroup(cgroup); 369 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 370
378 memset(&wh, 0, sizeof(wh)); 371 memset(&wh, 0, sizeof(wh));
379 b = buffer; 372 b = buffer;
@@ -392,32 +385,23 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
392 wh.type = DEV_CHAR; 385 wh.type = DEV_CHAR;
393 break; 386 break;
394 default: 387 default:
395 retval = -EINVAL; 388 return -EINVAL;
396 goto out2;
397 } 389 }
398 b++; 390 b++;
399 if (!isspace(*b)) { 391 if (!isspace(*b))
400 retval = -EINVAL; 392 return -EINVAL;
401 goto out2;
402 }
403 b++; 393 b++;
404 if (*b == '*') { 394 if (*b == '*') {
405 wh.major = ~0; 395 wh.major = ~0;
406 b++; 396 b++;
407 } else if (isdigit(*b)) { 397 } else if (isdigit(*b)) {
408 wh.major = 0; 398 wh.major = simple_strtoul(b, &endp, 10);
409 while (isdigit(*b)) { 399 b = endp;
410 wh.major = wh.major*10+(*b-'0');
411 b++;
412 }
413 } else { 400 } else {
414 retval = -EINVAL; 401 return -EINVAL;
415 goto out2;
416 }
417 if (*b != ':') {
418 retval = -EINVAL;
419 goto out2;
420 } 402 }
403 if (*b != ':')
404 return -EINVAL;
421 b++; 405 b++;
422 406
423 /* read minor */ 407 /* read minor */
@@ -425,19 +409,13 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
425 wh.minor = ~0; 409 wh.minor = ~0;
426 b++; 410 b++;
427 } else if (isdigit(*b)) { 411 } else if (isdigit(*b)) {
428 wh.minor = 0; 412 wh.minor = simple_strtoul(b, &endp, 10);
429 while (isdigit(*b)) { 413 b = endp;
430 wh.minor = wh.minor*10+(*b-'0');
431 b++;
432 }
433 } else { 414 } else {
434 retval = -EINVAL; 415 return -EINVAL;
435 goto out2;
436 }
437 if (!isspace(*b)) {
438 retval = -EINVAL;
439 goto out2;
440 } 416 }
417 if (!isspace(*b))
418 return -EINVAL;
441 for (b++, count = 0; count < 3; count++, b++) { 419 for (b++, count = 0; count < 3; count++, b++) {
442 switch (*b) { 420 switch (*b) {
443 case 'r': 421 case 'r':
@@ -454,8 +432,7 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
454 count = 3; 432 count = 3;
455 break; 433 break;
456 default: 434 default:
457 retval = -EINVAL; 435 return -EINVAL;
458 goto out2;
459 } 436 }
460 } 437 }
461 438
@@ -463,38 +440,39 @@ handle:
463 retval = 0; 440 retval = 0;
464 switch (filetype) { 441 switch (filetype) {
465 case DEVCG_ALLOW: 442 case DEVCG_ALLOW:
466 if (!parent_has_perm(cgroup, &wh)) 443 if (!parent_has_perm(devcgroup, &wh))
467 retval = -EPERM; 444 return -EPERM;
468 else 445 return dev_whitelist_add(devcgroup, &wh);
469 retval = dev_whitelist_add(devcgroup, &wh);
470 break;
471 case DEVCG_DENY: 446 case DEVCG_DENY:
472 dev_whitelist_rm(devcgroup, &wh); 447 dev_whitelist_rm(devcgroup, &wh);
473 break; 448 break;
474 default: 449 default:
475 retval = -EINVAL; 450 return -EINVAL;
476 goto out2;
477 } 451 }
452 return 0;
453}
478 454
479 if (retval == 0) 455static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
480 retval = nbytes; 456 const char *buffer)
481 457{
482out2: 458 int retval;
459 if (!cgroup_lock_live_group(cgrp))
460 return -ENODEV;
461 retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
462 cft->private, buffer);
483 cgroup_unlock(); 463 cgroup_unlock();
484out1:
485 kfree(buffer);
486 return retval; 464 return retval;
487} 465}
488 466
489static struct cftype dev_cgroup_files[] = { 467static struct cftype dev_cgroup_files[] = {
490 { 468 {
491 .name = "allow", 469 .name = "allow",
492 .write = devcgroup_access_write, 470 .write_string = devcgroup_access_write,
493 .private = DEVCG_ALLOW, 471 .private = DEVCG_ALLOW,
494 }, 472 },
495 { 473 {
496 .name = "deny", 474 .name = "deny",
497 .write = devcgroup_access_write, 475 .write_string = devcgroup_access_write,
498 .private = DEVCG_DENY, 476 .private = DEVCG_DENY,
499 }, 477 },
500 { 478 {
@@ -535,8 +513,8 @@ int devcgroup_inode_permission(struct inode *inode, int mask)
535 if (!dev_cgroup) 513 if (!dev_cgroup)
536 return 0; 514 return 0;
537 515
538 spin_lock(&dev_cgroup->lock); 516 rcu_read_lock();
539 list_for_each_entry(wh, &dev_cgroup->whitelist, list) { 517 list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
540 if (wh->type & DEV_ALL) 518 if (wh->type & DEV_ALL)
541 goto acc_check; 519 goto acc_check;
542 if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode)) 520 if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
@@ -552,10 +530,10 @@ acc_check:
552 continue; 530 continue;
553 if ((mask & MAY_READ) && !(wh->access & ACC_READ)) 531 if ((mask & MAY_READ) && !(wh->access & ACC_READ))
554 continue; 532 continue;
555 spin_unlock(&dev_cgroup->lock); 533 rcu_read_unlock();
556 return 0; 534 return 0;
557 } 535 }
558 spin_unlock(&dev_cgroup->lock); 536 rcu_read_unlock();
559 537
560 return -EPERM; 538 return -EPERM;
561} 539}
@@ -570,7 +548,7 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
570 if (!dev_cgroup) 548 if (!dev_cgroup)
571 return 0; 549 return 0;
572 550
573 spin_lock(&dev_cgroup->lock); 551 rcu_read_lock();
574 list_for_each_entry(wh, &dev_cgroup->whitelist, list) { 552 list_for_each_entry(wh, &dev_cgroup->whitelist, list) {
575 if (wh->type & DEV_ALL) 553 if (wh->type & DEV_ALL)
576 goto acc_check; 554 goto acc_check;
@@ -585,9 +563,9 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
585acc_check: 563acc_check:
586 if (!(wh->access & ACC_MKNOD)) 564 if (!(wh->access & ACC_MKNOD))
587 continue; 565 continue;
588 spin_unlock(&dev_cgroup->lock); 566 rcu_read_unlock();
589 return 0; 567 return 0;
590 } 568 }
591 spin_unlock(&dev_cgroup->lock); 569 rcu_read_unlock();
592 return -EPERM; 570 return -EPERM;
593} 571}