1
2
3
4
5
6
7#include <linux/device_cgroup.h>
8#include <linux/cgroup.h>
9#include <linux/ctype.h>
10#include <linux/list.h>
11#include <linux/uaccess.h>
12#include <linux/seq_file.h>
13#include <linux/slab.h>
14#include <linux/rcupdate.h>
15#include <linux/mutex.h>
16
17#define ACC_MKNOD 1
18#define ACC_READ 2
19#define ACC_WRITE 4
20#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
21
22#define DEV_BLOCK 1
23#define DEV_CHAR 2
24#define DEV_ALL 4
25
26static DEFINE_MUTEX(devcgroup_mutex);
27
28enum devcg_behavior {
29 DEVCG_DEFAULT_NONE,
30 DEVCG_DEFAULT_ALLOW,
31 DEVCG_DEFAULT_DENY,
32};
33
34
35
36
37
38
39
40struct dev_exception_item {
41 u32 major, minor;
42 short type;
43 short access;
44 struct list_head list;
45 struct rcu_head rcu;
46};
47
48struct dev_cgroup {
49 struct cgroup_subsys_state css;
50 struct list_head exceptions;
51 enum devcg_behavior behavior;
52};
53
54static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
55{
56 return container_of(s, struct dev_cgroup, css);
57}
58
59static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
60{
61 return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id));
62}
63
64static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
65{
66 return css_to_devcgroup(task_subsys_state(task, devices_subsys_id));
67}
68
69struct cgroup_subsys devices_subsys;
70
71static int devcgroup_can_attach(struct cgroup *new_cgrp,
72 struct cgroup_taskset *set)
73{
74 struct task_struct *task = cgroup_taskset_first(set);
75
76 if (current != task && !capable(CAP_SYS_ADMIN))
77 return -EPERM;
78 return 0;
79}
80
81
82
83
84static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
85{
86 struct dev_exception_item *ex, *tmp, *new;
87
88 lockdep_assert_held(&devcgroup_mutex);
89
90 list_for_each_entry(ex, orig, list) {
91 new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
92 if (!new)
93 goto free_and_exit;
94 list_add_tail(&new->list, dest);
95 }
96
97 return 0;
98
99free_and_exit:
100 list_for_each_entry_safe(ex, tmp, dest, list) {
101 list_del(&ex->list);
102 kfree(ex);
103 }
104 return -ENOMEM;
105}
106
107
108
109
110static int dev_exception_add(struct dev_cgroup *dev_cgroup,
111 struct dev_exception_item *ex)
112{
113 struct dev_exception_item *excopy, *walk;
114
115 lockdep_assert_held(&devcgroup_mutex);
116
117 excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
118 if (!excopy)
119 return -ENOMEM;
120
121 list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
122 if (walk->type != ex->type)
123 continue;
124 if (walk->major != ex->major)
125 continue;
126 if (walk->minor != ex->minor)
127 continue;
128
129 walk->access |= ex->access;
130 kfree(excopy);
131 excopy = NULL;
132 }
133
134 if (excopy != NULL)
135 list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
136 return 0;
137}
138
139
140
141
142static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
143 struct dev_exception_item *ex)
144{
145 struct dev_exception_item *walk, *tmp;
146
147 lockdep_assert_held(&devcgroup_mutex);
148
149 list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
150 if (walk->type != ex->type)
151 continue;
152 if (walk->major != ex->major)
153 continue;
154 if (walk->minor != ex->minor)
155 continue;
156
157 walk->access &= ~ex->access;
158 if (!walk->access) {
159 list_del_rcu(&walk->list);
160 kfree_rcu(walk, rcu);
161 }
162 }
163}
164
165static void __dev_exception_clean(struct dev_cgroup *dev_cgroup)
166{
167 struct dev_exception_item *ex, *tmp;
168
169 list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
170 list_del_rcu(&ex->list);
171 kfree_rcu(ex, rcu);
172 }
173}
174
175
176
177
178
179
180
181static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
182{
183 lockdep_assert_held(&devcgroup_mutex);
184
185 __dev_exception_clean(dev_cgroup);
186}
187
188static inline bool is_devcg_online(const struct dev_cgroup *devcg)
189{
190 return (devcg->behavior != DEVCG_DEFAULT_NONE);
191}
192
193
194
195
196
197
198
199static int devcgroup_online(struct cgroup *cgroup)
200{
201 struct dev_cgroup *dev_cgroup, *parent_dev_cgroup = NULL;
202 int ret = 0;
203
204 mutex_lock(&devcgroup_mutex);
205 dev_cgroup = cgroup_to_devcgroup(cgroup);
206 if (cgroup->parent)
207 parent_dev_cgroup = cgroup_to_devcgroup(cgroup->parent);
208
209 if (parent_dev_cgroup == NULL)
210 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
211 else {
212 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
213 &parent_dev_cgroup->exceptions);
214 if (!ret)
215 dev_cgroup->behavior = parent_dev_cgroup->behavior;
216 }
217 mutex_unlock(&devcgroup_mutex);
218
219 return ret;
220}
221
222static void devcgroup_offline(struct cgroup *cgroup)
223{
224 struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup);
225
226 mutex_lock(&devcgroup_mutex);
227 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
228 mutex_unlock(&devcgroup_mutex);
229}
230
231
232
233
234static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup)
235{
236 struct dev_cgroup *dev_cgroup;
237
238 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
239 if (!dev_cgroup)
240 return ERR_PTR(-ENOMEM);
241 INIT_LIST_HEAD(&dev_cgroup->exceptions);
242 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
243
244 return &dev_cgroup->css;
245}
246
247static void devcgroup_css_free(struct cgroup *cgroup)
248{
249 struct dev_cgroup *dev_cgroup;
250
251 dev_cgroup = cgroup_to_devcgroup(cgroup);
252 __dev_exception_clean(dev_cgroup);
253 kfree(dev_cgroup);
254}
255
256#define DEVCG_ALLOW 1
257#define DEVCG_DENY 2
258#define DEVCG_LIST 3
259
260#define MAJMINLEN 13
261#define ACCLEN 4
262
263static void set_access(char *acc, short access)
264{
265 int idx = 0;
266 memset(acc, 0, ACCLEN);
267 if (access & ACC_READ)
268 acc[idx++] = 'r';
269 if (access & ACC_WRITE)
270 acc[idx++] = 'w';
271 if (access & ACC_MKNOD)
272 acc[idx++] = 'm';
273}
274
275static char type_to_char(short type)
276{
277 if (type == DEV_ALL)
278 return 'a';
279 if (type == DEV_CHAR)
280 return 'c';
281 if (type == DEV_BLOCK)
282 return 'b';
283 return 'X';
284}
285
286static void set_majmin(char *str, unsigned m)
287{
288 if (m == ~0)
289 strcpy(str, "*");
290 else
291 sprintf(str, "%u", m);
292}
293
294static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
295 struct seq_file *m)
296{
297 struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup);
298 struct dev_exception_item *ex;
299 char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
300
301 rcu_read_lock();
302
303
304
305
306
307
308 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
309 set_access(acc, ACC_MASK);
310 set_majmin(maj, ~0);
311 set_majmin(min, ~0);
312 seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL),
313 maj, min, acc);
314 } else {
315 list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
316 set_access(acc, ex->access);
317 set_majmin(maj, ex->major);
318 set_majmin(min, ex->minor);
319 seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
320 maj, min, acc);
321 }
322 }
323 rcu_read_unlock();
324
325 return 0;
326}
327
328
329
330
331
332
333
334
335
336
337
338static bool may_access(struct dev_cgroup *dev_cgroup,
339 struct dev_exception_item *refex,
340 enum devcg_behavior behavior)
341{
342 struct dev_exception_item *ex;
343 bool match = false;
344
345 rcu_lockdep_assert(rcu_read_lock_held() ||
346 lockdep_is_held(&devcgroup_mutex),
347 "device_cgroup::may_access() called without proper synchronization");
348
349 list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
350 if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
351 continue;
352 if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
353 continue;
354 if (ex->major != ~0 && ex->major != refex->major)
355 continue;
356 if (ex->minor != ~0 && ex->minor != refex->minor)
357 continue;
358 if (refex->access & (~ex->access))
359 continue;
360 match = true;
361 break;
362 }
363
364 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
365 if (behavior == DEVCG_DEFAULT_ALLOW) {
366
367 return true;
368 } else {
369
370 if (match)
371
372
373
374
375 return false;
376 return true;
377 }
378 } else {
379
380 if (match)
381
382 return true;
383 else
384 return false;
385 }
386 return false;
387}
388
389
390
391
392
393
394static int parent_has_perm(struct dev_cgroup *childcg,
395 struct dev_exception_item *ex)
396{
397 struct cgroup *pcg = childcg->css.cgroup->parent;
398 struct dev_cgroup *parent;
399
400 if (!pcg)
401 return 1;
402 parent = cgroup_to_devcgroup(pcg);
403 return may_access(parent, ex, childcg->behavior);
404}
405
406
407
408
409
410
411
412static inline int may_allow_all(struct dev_cgroup *parent)
413{
414 if (!parent)
415 return 1;
416 return parent->behavior == DEVCG_DEFAULT_ALLOW;
417}
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432static void revalidate_active_exceptions(struct dev_cgroup *devcg)
433{
434 struct dev_exception_item *ex;
435 struct list_head *this, *tmp;
436
437 list_for_each_safe(this, tmp, &devcg->exceptions) {
438 ex = container_of(this, struct dev_exception_item, list);
439 if (!parent_has_perm(devcg, ex))
440 dev_exception_rm(devcg, ex);
441 }
442}
443
444
445
446
447
448
449
450
451static int propagate_exception(struct dev_cgroup *devcg_root,
452 struct dev_exception_item *ex)
453{
454 struct cgroup *root = devcg_root->css.cgroup, *pos;
455 int rc = 0;
456
457 rcu_read_lock();
458
459 cgroup_for_each_descendant_pre(pos, root) {
460 struct dev_cgroup *devcg = cgroup_to_devcgroup(pos);
461
462
463
464
465
466
467
468 if (!is_devcg_online(devcg))
469 continue;
470
471 rcu_read_unlock();
472
473
474
475
476
477 if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
478 devcg->behavior == DEVCG_DEFAULT_ALLOW) {
479 rc = dev_exception_add(devcg, ex);
480 if (rc)
481 break;
482 } else {
483
484
485
486
487
488
489 dev_exception_rm(devcg, ex);
490 }
491 revalidate_active_exceptions(devcg);
492
493 rcu_read_lock();
494 }
495
496 rcu_read_unlock();
497 return rc;
498}
499
500static inline bool has_children(struct dev_cgroup *devcgroup)
501{
502 struct cgroup *cgrp = devcgroup->css.cgroup;
503
504 return !list_empty(&cgrp->children);
505}
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520static int devcgroup_update_access(struct dev_cgroup *devcgroup,
521 int filetype, const char *buffer)
522{
523 const char *b;
524 char temp[12];
525 int count, rc = 0;
526 struct dev_exception_item ex;
527 struct cgroup *p = devcgroup->css.cgroup;
528 struct dev_cgroup *parent = NULL;
529
530 if (!capable(CAP_SYS_ADMIN))
531 return -EPERM;
532
533 if (p->parent)
534 parent = cgroup_to_devcgroup(p->parent);
535
536 memset(&ex, 0, sizeof(ex));
537 b = buffer;
538
539 switch (*b) {
540 case 'a':
541 switch (filetype) {
542 case DEVCG_ALLOW:
543 if (has_children(devcgroup))
544 return -EINVAL;
545
546 if (!may_allow_all(parent))
547 return -EPERM;
548 dev_exception_clean(devcgroup);
549 devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
550 if (!parent)
551 break;
552
553 rc = dev_exceptions_copy(&devcgroup->exceptions,
554 &parent->exceptions);
555 if (rc)
556 return rc;
557 break;
558 case DEVCG_DENY:
559 if (has_children(devcgroup))
560 return -EINVAL;
561
562 dev_exception_clean(devcgroup);
563 devcgroup->behavior = DEVCG_DEFAULT_DENY;
564 break;
565 default:
566 return -EINVAL;
567 }
568 return 0;
569 case 'b':
570 ex.type = DEV_BLOCK;
571 break;
572 case 'c':
573 ex.type = DEV_CHAR;
574 break;
575 default:
576 return -EINVAL;
577 }
578 b++;
579 if (!isspace(*b))
580 return -EINVAL;
581 b++;
582 if (*b == '*') {
583 ex.major = ~0;
584 b++;
585 } else if (isdigit(*b)) {
586 memset(temp, 0, sizeof(temp));
587 for (count = 0; count < sizeof(temp) - 1; count++) {
588 temp[count] = *b;
589 b++;
590 if (!isdigit(*b))
591 break;
592 }
593 rc = kstrtou32(temp, 10, &ex.major);
594 if (rc)
595 return -EINVAL;
596 } else {
597 return -EINVAL;
598 }
599 if (*b != ':')
600 return -EINVAL;
601 b++;
602
603
604 if (*b == '*') {
605 ex.minor = ~0;
606 b++;
607 } else if (isdigit(*b)) {
608 memset(temp, 0, sizeof(temp));
609 for (count = 0; count < sizeof(temp) - 1; count++) {
610 temp[count] = *b;
611 b++;
612 if (!isdigit(*b))
613 break;
614 }
615 rc = kstrtou32(temp, 10, &ex.minor);
616 if (rc)
617 return -EINVAL;
618 } else {
619 return -EINVAL;
620 }
621 if (!isspace(*b))
622 return -EINVAL;
623 for (b++, count = 0; count < 3; count++, b++) {
624 switch (*b) {
625 case 'r':
626 ex.access |= ACC_READ;
627 break;
628 case 'w':
629 ex.access |= ACC_WRITE;
630 break;
631 case 'm':
632 ex.access |= ACC_MKNOD;
633 break;
634 case '\n':
635 case '\0':
636 count = 3;
637 break;
638 default:
639 return -EINVAL;
640 }
641 }
642
643 switch (filetype) {
644 case DEVCG_ALLOW:
645 if (!parent_has_perm(devcgroup, &ex))
646 return -EPERM;
647
648
649
650
651
652 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
653 dev_exception_rm(devcgroup, &ex);
654 return 0;
655 }
656 rc = dev_exception_add(devcgroup, &ex);
657 break;
658 case DEVCG_DENY:
659
660
661
662
663
664 if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
665 dev_exception_rm(devcgroup, &ex);
666 else
667 rc = dev_exception_add(devcgroup, &ex);
668
669 if (rc)
670 break;
671
672 rc = propagate_exception(devcgroup, &ex);
673 break;
674 default:
675 rc = -EINVAL;
676 }
677 return rc;
678}
679
680static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
681 const char *buffer)
682{
683 int retval;
684
685 mutex_lock(&devcgroup_mutex);
686 retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
687 cft->private, buffer);
688 mutex_unlock(&devcgroup_mutex);
689 return retval;
690}
691
692static struct cftype dev_cgroup_files[] = {
693 {
694 .name = "allow",
695 .write_string = devcgroup_access_write,
696 .private = DEVCG_ALLOW,
697 },
698 {
699 .name = "deny",
700 .write_string = devcgroup_access_write,
701 .private = DEVCG_DENY,
702 },
703 {
704 .name = "list",
705 .read_seq_string = devcgroup_seq_read,
706 .private = DEVCG_LIST,
707 },
708 { }
709};
710
711struct cgroup_subsys devices_subsys = {
712 .name = "devices",
713 .can_attach = devcgroup_can_attach,
714 .css_alloc = devcgroup_css_alloc,
715 .css_free = devcgroup_css_free,
716 .css_online = devcgroup_online,
717 .css_offline = devcgroup_offline,
718 .subsys_id = devices_subsys_id,
719 .base_cftypes = dev_cgroup_files,
720};
721
722
723
724
725
726
727
728
729
730
731
732static int __devcgroup_check_permission(short type, u32 major, u32 minor,
733 short access)
734{
735 struct dev_cgroup *dev_cgroup;
736 struct dev_exception_item ex;
737 int rc;
738
739 memset(&ex, 0, sizeof(ex));
740 ex.type = type;
741 ex.major = major;
742 ex.minor = minor;
743 ex.access = access;
744
745 rcu_read_lock();
746 dev_cgroup = task_devcgroup(current);
747 rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
748 rcu_read_unlock();
749
750 if (!rc)
751 return -EPERM;
752
753 return 0;
754}
755
756int __devcgroup_inode_permission(struct inode *inode, int mask)
757{
758 short type, access = 0;
759
760 if (S_ISBLK(inode->i_mode))
761 type = DEV_BLOCK;
762 if (S_ISCHR(inode->i_mode))
763 type = DEV_CHAR;
764 if (mask & MAY_WRITE)
765 access |= ACC_WRITE;
766 if (mask & MAY_READ)
767 access |= ACC_READ;
768
769 return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
770 access);
771}
772
773int devcgroup_inode_mknod(int mode, dev_t dev)
774{
775 short type;
776
777 if (!S_ISBLK(mode) && !S_ISCHR(mode))
778 return 0;
779
780 if (S_ISBLK(mode))
781 type = DEV_BLOCK;
782 else
783 type = DEV_CHAR;
784
785 return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
786 ACC_MKNOD);
787
788}
789