• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/security/
1/*
2 * device_cgroup.c - device cgroup subsystem
3 *
4 * Copyright 2007 IBM Corp
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  /* this represents all devices */
25
26static DEFINE_MUTEX(devcgroup_mutex);
27
28/*
29 * whitelist locking rules:
30 * hold devcgroup_mutex for update/read.
31 * hold rcu_read_lock() for read.
32 */
33
34struct dev_whitelist_item {
35	u32 major, minor;
36	short type;
37	short access;
38	struct list_head list;
39	struct rcu_head rcu;
40};
41
42struct dev_cgroup {
43	struct cgroup_subsys_state css;
44	struct list_head whitelist;
45};
46
47static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
48{
49	return container_of(s, struct dev_cgroup, css);
50}
51
52static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
53{
54	return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id));
55}
56
57static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
58{
59	return css_to_devcgroup(task_subsys_state(task, devices_subsys_id));
60}
61
62struct cgroup_subsys devices_subsys;
63
64static int devcgroup_can_attach(struct cgroup_subsys *ss,
65		struct cgroup *new_cgroup, struct task_struct *task,
66		bool threadgroup)
67{
68	if (current != task && !capable(CAP_SYS_ADMIN))
69			return -EPERM;
70
71	return 0;
72}
73
74/*
75 * called under devcgroup_mutex
76 */
77static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
78{
79	struct dev_whitelist_item *wh, *tmp, *new;
80
81	list_for_each_entry(wh, orig, list) {
82		new = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
83		if (!new)
84			goto free_and_exit;
85		list_add_tail(&new->list, dest);
86	}
87
88	return 0;
89
90free_and_exit:
91	list_for_each_entry_safe(wh, tmp, dest, list) {
92		list_del(&wh->list);
93		kfree(wh);
94	}
95	return -ENOMEM;
96}
97
98/* Stupid prototype - don't bother combining existing entries */
99/*
100 * called under devcgroup_mutex
101 */
102static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
103			struct dev_whitelist_item *wh)
104{
105	struct dev_whitelist_item *whcopy, *walk;
106
107	whcopy = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
108	if (!whcopy)
109		return -ENOMEM;
110
111	list_for_each_entry(walk, &dev_cgroup->whitelist, list) {
112		if (walk->type != wh->type)
113			continue;
114		if (walk->major != wh->major)
115			continue;
116		if (walk->minor != wh->minor)
117			continue;
118
119		walk->access |= wh->access;
120		kfree(whcopy);
121		whcopy = NULL;
122	}
123
124	if (whcopy != NULL)
125		list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist);
126	return 0;
127}
128
129static void whitelist_item_free(struct rcu_head *rcu)
130{
131	struct dev_whitelist_item *item;
132
133	item = container_of(rcu, struct dev_whitelist_item, rcu);
134	kfree(item);
135}
136
137/*
138 * called under devcgroup_mutex
139 */
140static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
141			struct dev_whitelist_item *wh)
142{
143	struct dev_whitelist_item *walk, *tmp;
144
145	list_for_each_entry_safe(walk, tmp, &dev_cgroup->whitelist, list) {
146		if (walk->type == DEV_ALL)
147			goto remove;
148		if (walk->type != wh->type)
149			continue;
150		if (walk->major != ~0 && walk->major != wh->major)
151			continue;
152		if (walk->minor != ~0 && walk->minor != wh->minor)
153			continue;
154
155remove:
156		walk->access &= ~wh->access;
157		if (!walk->access) {
158			list_del_rcu(&walk->list);
159			call_rcu(&walk->rcu, whitelist_item_free);
160		}
161	}
162}
163
164/*
165 * called from kernel/cgroup.c with cgroup_lock() held.
166 */
167static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
168						struct cgroup *cgroup)
169{
170	struct dev_cgroup *dev_cgroup, *parent_dev_cgroup;
171	struct cgroup *parent_cgroup;
172	int ret;
173
174	dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
175	if (!dev_cgroup)
176		return ERR_PTR(-ENOMEM);
177	INIT_LIST_HEAD(&dev_cgroup->whitelist);
178	parent_cgroup = cgroup->parent;
179
180	if (parent_cgroup == NULL) {
181		struct dev_whitelist_item *wh;
182		wh = kmalloc(sizeof(*wh), GFP_KERNEL);
183		if (!wh) {
184			kfree(dev_cgroup);
185			return ERR_PTR(-ENOMEM);
186		}
187		wh->minor = wh->major = ~0;
188		wh->type = DEV_ALL;
189		wh->access = ACC_MASK;
190		list_add(&wh->list, &dev_cgroup->whitelist);
191	} else {
192		parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
193		mutex_lock(&devcgroup_mutex);
194		ret = dev_whitelist_copy(&dev_cgroup->whitelist,
195				&parent_dev_cgroup->whitelist);
196		mutex_unlock(&devcgroup_mutex);
197		if (ret) {
198			kfree(dev_cgroup);
199			return ERR_PTR(ret);
200		}
201	}
202
203	return &dev_cgroup->css;
204}
205
206static void devcgroup_destroy(struct cgroup_subsys *ss,
207			struct cgroup *cgroup)
208{
209	struct dev_cgroup *dev_cgroup;
210	struct dev_whitelist_item *wh, *tmp;
211
212	dev_cgroup = cgroup_to_devcgroup(cgroup);
213	list_for_each_entry_safe(wh, tmp, &dev_cgroup->whitelist, list) {
214		list_del(&wh->list);
215		kfree(wh);
216	}
217	kfree(dev_cgroup);
218}
219
220#define DEVCG_ALLOW 1
221#define DEVCG_DENY 2
222#define DEVCG_LIST 3
223
224#define MAJMINLEN 13
225#define ACCLEN 4
226
227static void set_access(char *acc, short access)
228{
229	int idx = 0;
230	memset(acc, 0, ACCLEN);
231	if (access & ACC_READ)
232		acc[idx++] = 'r';
233	if (access & ACC_WRITE)
234		acc[idx++] = 'w';
235	if (access & ACC_MKNOD)
236		acc[idx++] = 'm';
237}
238
239static char type_to_char(short type)
240{
241	if (type == DEV_ALL)
242		return 'a';
243	if (type == DEV_CHAR)
244		return 'c';
245	if (type == DEV_BLOCK)
246		return 'b';
247	return 'X';
248}
249
250static void set_majmin(char *str, unsigned m)
251{
252	if (m == ~0)
253		strcpy(str, "*");
254	else
255		sprintf(str, "%u", m);
256}
257
258static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
259				struct seq_file *m)
260{
261	struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup);
262	struct dev_whitelist_item *wh;
263	char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
264
265	rcu_read_lock();
266	list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) {
267		set_access(acc, wh->access);
268		set_majmin(maj, wh->major);
269		set_majmin(min, wh->minor);
270		seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
271			   maj, min, acc);
272	}
273	rcu_read_unlock();
274
275	return 0;
276}
277
278/*
279 * may_access_whitelist:
280 * does the access granted to dev_cgroup c contain the access
281 * requested in whitelist item refwh.
282 * return 1 if yes, 0 if no.
283 * call with devcgroup_mutex held
284 */
285static int may_access_whitelist(struct dev_cgroup *c,
286				       struct dev_whitelist_item *refwh)
287{
288	struct dev_whitelist_item *whitem;
289
290	list_for_each_entry(whitem, &c->whitelist, list) {
291		if (whitem->type & DEV_ALL)
292			return 1;
293		if ((refwh->type & DEV_BLOCK) && !(whitem->type & DEV_BLOCK))
294			continue;
295		if ((refwh->type & DEV_CHAR) && !(whitem->type & DEV_CHAR))
296			continue;
297		if (whitem->major != ~0 && whitem->major != refwh->major)
298			continue;
299		if (whitem->minor != ~0 && whitem->minor != refwh->minor)
300			continue;
301		if (refwh->access & (~whitem->access))
302			continue;
303		return 1;
304	}
305	return 0;
306}
307
308/*
309 * parent_has_perm:
310 * when adding a new allow rule to a device whitelist, the rule
311 * must be allowed in the parent device
312 */
313static int parent_has_perm(struct dev_cgroup *childcg,
314				  struct dev_whitelist_item *wh)
315{
316	struct cgroup *pcg = childcg->css.cgroup->parent;
317	struct dev_cgroup *parent;
318
319	if (!pcg)
320		return 1;
321	parent = cgroup_to_devcgroup(pcg);
322	return may_access_whitelist(parent, wh);
323}
324
325/*
326 * Modify the whitelist using allow/deny rules.
327 * CAP_SYS_ADMIN is needed for this.  It's at least separate from CAP_MKNOD
328 * so we can give a container CAP_MKNOD to let it create devices but not
329 * modify the whitelist.
330 * It seems likely we'll want to add a CAP_CONTAINER capability to allow
331 * us to also grant CAP_SYS_ADMIN to containers without giving away the
332 * device whitelist controls, but for now we'll stick with CAP_SYS_ADMIN
333 *
334 * Taking rules away is always allowed (given CAP_SYS_ADMIN).  Granting
335 * new access is only allowed if you're in the top-level cgroup, or your
336 * parent cgroup has the access you're asking for.
337 */
338static int devcgroup_update_access(struct dev_cgroup *devcgroup,
339				   int filetype, const char *buffer)
340{
341	const char *b;
342	char *endp;
343	int count;
344	struct dev_whitelist_item wh;
345
346	if (!capable(CAP_SYS_ADMIN))
347		return -EPERM;
348
349	memset(&wh, 0, sizeof(wh));
350	b = buffer;
351
352	switch (*b) {
353	case 'a':
354		wh.type = DEV_ALL;
355		wh.access = ACC_MASK;
356		wh.major = ~0;
357		wh.minor = ~0;
358		goto handle;
359	case 'b':
360		wh.type = DEV_BLOCK;
361		break;
362	case 'c':
363		wh.type = DEV_CHAR;
364		break;
365	default:
366		return -EINVAL;
367	}
368	b++;
369	if (!isspace(*b))
370		return -EINVAL;
371	b++;
372	if (*b == '*') {
373		wh.major = ~0;
374		b++;
375	} else if (isdigit(*b)) {
376		wh.major = simple_strtoul(b, &endp, 10);
377		b = endp;
378	} else {
379		return -EINVAL;
380	}
381	if (*b != ':')
382		return -EINVAL;
383	b++;
384
385	/* read minor */
386	if (*b == '*') {
387		wh.minor = ~0;
388		b++;
389	} else if (isdigit(*b)) {
390		wh.minor = simple_strtoul(b, &endp, 10);
391		b = endp;
392	} else {
393		return -EINVAL;
394	}
395	if (!isspace(*b))
396		return -EINVAL;
397	for (b++, count = 0; count < 3; count++, b++) {
398		switch (*b) {
399		case 'r':
400			wh.access |= ACC_READ;
401			break;
402		case 'w':
403			wh.access |= ACC_WRITE;
404			break;
405		case 'm':
406			wh.access |= ACC_MKNOD;
407			break;
408		case '\n':
409		case '\0':
410			count = 3;
411			break;
412		default:
413			return -EINVAL;
414		}
415	}
416
417handle:
418	switch (filetype) {
419	case DEVCG_ALLOW:
420		if (!parent_has_perm(devcgroup, &wh))
421			return -EPERM;
422		return dev_whitelist_add(devcgroup, &wh);
423	case DEVCG_DENY:
424		dev_whitelist_rm(devcgroup, &wh);
425		break;
426	default:
427		return -EINVAL;
428	}
429	return 0;
430}
431
432static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
433				  const char *buffer)
434{
435	int retval;
436
437	mutex_lock(&devcgroup_mutex);
438	retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
439					 cft->private, buffer);
440	mutex_unlock(&devcgroup_mutex);
441	return retval;
442}
443
444static struct cftype dev_cgroup_files[] = {
445	{
446		.name = "allow",
447		.write_string  = devcgroup_access_write,
448		.private = DEVCG_ALLOW,
449	},
450	{
451		.name = "deny",
452		.write_string = devcgroup_access_write,
453		.private = DEVCG_DENY,
454	},
455	{
456		.name = "list",
457		.read_seq_string = devcgroup_seq_read,
458		.private = DEVCG_LIST,
459	},
460};
461
462static int devcgroup_populate(struct cgroup_subsys *ss,
463				struct cgroup *cgroup)
464{
465	return cgroup_add_files(cgroup, ss, dev_cgroup_files,
466					ARRAY_SIZE(dev_cgroup_files));
467}
468
469struct cgroup_subsys devices_subsys = {
470	.name = "devices",
471	.can_attach = devcgroup_can_attach,
472	.create = devcgroup_create,
473	.destroy = devcgroup_destroy,
474	.populate = devcgroup_populate,
475	.subsys_id = devices_subsys_id,
476};
477
478int devcgroup_inode_permission(struct inode *inode, int mask)
479{
480	struct dev_cgroup *dev_cgroup;
481	struct dev_whitelist_item *wh;
482
483	dev_t device = inode->i_rdev;
484	if (!device)
485		return 0;
486	if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
487		return 0;
488
489	rcu_read_lock();
490
491	dev_cgroup = task_devcgroup(current);
492
493	list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
494		if (wh->type & DEV_ALL)
495			goto found;
496		if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
497			continue;
498		if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))
499			continue;
500		if (wh->major != ~0 && wh->major != imajor(inode))
501			continue;
502		if (wh->minor != ~0 && wh->minor != iminor(inode))
503			continue;
504
505		if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))
506			continue;
507		if ((mask & MAY_READ) && !(wh->access & ACC_READ))
508			continue;
509found:
510		rcu_read_unlock();
511		return 0;
512	}
513
514	rcu_read_unlock();
515
516	return -EPERM;
517}
518
519int devcgroup_inode_mknod(int mode, dev_t dev)
520{
521	struct dev_cgroup *dev_cgroup;
522	struct dev_whitelist_item *wh;
523
524	if (!S_ISBLK(mode) && !S_ISCHR(mode))
525		return 0;
526
527	rcu_read_lock();
528
529	dev_cgroup = task_devcgroup(current);
530
531	list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
532		if (wh->type & DEV_ALL)
533			goto found;
534		if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode))
535			continue;
536		if ((wh->type & DEV_CHAR) && !S_ISCHR(mode))
537			continue;
538		if (wh->major != ~0 && wh->major != MAJOR(dev))
539			continue;
540		if (wh->minor != ~0 && wh->minor != MINOR(dev))
541			continue;
542
543		if (!(wh->access & ACC_MKNOD))
544			continue;
545found:
546		rcu_read_unlock();
547		return 0;
548	}
549
550	rcu_read_unlock();
551
552	return -EPERM;
553}
554