diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /security/selinux/selinuxfs.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'security/selinux/selinuxfs.c')
-rw-r--r-- | security/selinux/selinuxfs.c | 1340 |
1 files changed, 1340 insertions, 0 deletions
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c new file mode 100644 index 000000000000..07221568b505 --- /dev/null +++ b/security/selinux/selinuxfs.c | |||
@@ -0,0 +1,1340 @@ | |||
1 | /* Updated: Karl MacMillan <kmacmillan@tresys.com> | ||
2 | * | ||
3 | * Added conditional policy language extensions | ||
4 | * | ||
5 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
6 | * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, version 2. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/pagemap.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/security.h> | ||
21 | #include <linux/major.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/percpu.h> | ||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/semaphore.h> | ||
26 | |||
27 | /* selinuxfs pseudo filesystem for exporting the security policy API. | ||
28 | Based on the proc code and the fs/nfsd/nfsctl.c code. */ | ||
29 | |||
30 | #include "flask.h" | ||
31 | #include "avc.h" | ||
32 | #include "avc_ss.h" | ||
33 | #include "security.h" | ||
34 | #include "objsec.h" | ||
35 | #include "conditional.h" | ||
36 | |||
37 | unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; | ||
38 | |||
39 | static int __init checkreqprot_setup(char *str) | ||
40 | { | ||
41 | selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; | ||
42 | return 1; | ||
43 | } | ||
44 | __setup("checkreqprot=", checkreqprot_setup); | ||
45 | |||
46 | |||
47 | static DECLARE_MUTEX(sel_sem); | ||
48 | |||
49 | /* global data for booleans */ | ||
50 | static struct dentry *bool_dir = NULL; | ||
51 | static int bool_num = 0; | ||
52 | static int *bool_pending_values = NULL; | ||
53 | |||
54 | extern void selnl_notify_setenforce(int val); | ||
55 | |||
56 | /* Check whether a task is allowed to use a security operation. */ | ||
57 | static int task_has_security(struct task_struct *tsk, | ||
58 | u32 perms) | ||
59 | { | ||
60 | struct task_security_struct *tsec; | ||
61 | |||
62 | tsec = tsk->security; | ||
63 | if (!tsec) | ||
64 | return -EACCES; | ||
65 | |||
66 | return avc_has_perm(tsec->sid, SECINITSID_SECURITY, | ||
67 | SECCLASS_SECURITY, perms, NULL); | ||
68 | } | ||
69 | |||
70 | enum sel_inos { | ||
71 | SEL_ROOT_INO = 2, | ||
72 | SEL_LOAD, /* load policy */ | ||
73 | SEL_ENFORCE, /* get or set enforcing status */ | ||
74 | SEL_CONTEXT, /* validate context */ | ||
75 | SEL_ACCESS, /* compute access decision */ | ||
76 | SEL_CREATE, /* compute create labeling decision */ | ||
77 | SEL_RELABEL, /* compute relabeling decision */ | ||
78 | SEL_USER, /* compute reachable user contexts */ | ||
79 | SEL_POLICYVERS, /* return policy version for this kernel */ | ||
80 | SEL_COMMIT_BOOLS, /* commit new boolean values */ | ||
81 | SEL_MLS, /* return if MLS policy is enabled */ | ||
82 | SEL_DISABLE, /* disable SELinux until next reboot */ | ||
83 | SEL_AVC, /* AVC management directory */ | ||
84 | SEL_MEMBER, /* compute polyinstantiation membership decision */ | ||
85 | SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ | ||
86 | }; | ||
87 | |||
88 | #define TMPBUFLEN 12 | ||
89 | static ssize_t sel_read_enforce(struct file *filp, char __user *buf, | ||
90 | size_t count, loff_t *ppos) | ||
91 | { | ||
92 | char tmpbuf[TMPBUFLEN]; | ||
93 | ssize_t length; | ||
94 | |||
95 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing); | ||
96 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
97 | } | ||
98 | |||
99 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP | ||
100 | static ssize_t sel_write_enforce(struct file * file, const char __user * buf, | ||
101 | size_t count, loff_t *ppos) | ||
102 | |||
103 | { | ||
104 | char *page; | ||
105 | ssize_t length; | ||
106 | int new_value; | ||
107 | |||
108 | if (count < 0 || count >= PAGE_SIZE) | ||
109 | return -ENOMEM; | ||
110 | if (*ppos != 0) { | ||
111 | /* No partial writes. */ | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
115 | if (!page) | ||
116 | return -ENOMEM; | ||
117 | length = -EFAULT; | ||
118 | if (copy_from_user(page, buf, count)) | ||
119 | goto out; | ||
120 | |||
121 | length = -EINVAL; | ||
122 | if (sscanf(page, "%d", &new_value) != 1) | ||
123 | goto out; | ||
124 | |||
125 | if (new_value != selinux_enforcing) { | ||
126 | length = task_has_security(current, SECURITY__SETENFORCE); | ||
127 | if (length) | ||
128 | goto out; | ||
129 | selinux_enforcing = new_value; | ||
130 | if (selinux_enforcing) | ||
131 | avc_ss_reset(0); | ||
132 | selnl_notify_setenforce(selinux_enforcing); | ||
133 | } | ||
134 | length = count; | ||
135 | out: | ||
136 | free_page((unsigned long) page); | ||
137 | return length; | ||
138 | } | ||
139 | #else | ||
140 | #define sel_write_enforce NULL | ||
141 | #endif | ||
142 | |||
143 | static struct file_operations sel_enforce_ops = { | ||
144 | .read = sel_read_enforce, | ||
145 | .write = sel_write_enforce, | ||
146 | }; | ||
147 | |||
148 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
149 | static ssize_t sel_write_disable(struct file * file, const char __user * buf, | ||
150 | size_t count, loff_t *ppos) | ||
151 | |||
152 | { | ||
153 | char *page; | ||
154 | ssize_t length; | ||
155 | int new_value; | ||
156 | extern int selinux_disable(void); | ||
157 | |||
158 | if (count < 0 || count >= PAGE_SIZE) | ||
159 | return -ENOMEM; | ||
160 | if (*ppos != 0) { | ||
161 | /* No partial writes. */ | ||
162 | return -EINVAL; | ||
163 | } | ||
164 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
165 | if (!page) | ||
166 | return -ENOMEM; | ||
167 | length = -EFAULT; | ||
168 | if (copy_from_user(page, buf, count)) | ||
169 | goto out; | ||
170 | |||
171 | length = -EINVAL; | ||
172 | if (sscanf(page, "%d", &new_value) != 1) | ||
173 | goto out; | ||
174 | |||
175 | if (new_value) { | ||
176 | length = selinux_disable(); | ||
177 | if (length < 0) | ||
178 | goto out; | ||
179 | } | ||
180 | |||
181 | length = count; | ||
182 | out: | ||
183 | free_page((unsigned long) page); | ||
184 | return length; | ||
185 | } | ||
186 | #else | ||
187 | #define sel_write_disable NULL | ||
188 | #endif | ||
189 | |||
190 | static struct file_operations sel_disable_ops = { | ||
191 | .write = sel_write_disable, | ||
192 | }; | ||
193 | |||
194 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | ||
195 | size_t count, loff_t *ppos) | ||
196 | { | ||
197 | char tmpbuf[TMPBUFLEN]; | ||
198 | ssize_t length; | ||
199 | |||
200 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX); | ||
201 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
202 | } | ||
203 | |||
204 | static struct file_operations sel_policyvers_ops = { | ||
205 | .read = sel_read_policyvers, | ||
206 | }; | ||
207 | |||
208 | /* declaration for sel_write_load */ | ||
209 | static int sel_make_bools(void); | ||
210 | |||
211 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, | ||
212 | size_t count, loff_t *ppos) | ||
213 | { | ||
214 | char tmpbuf[TMPBUFLEN]; | ||
215 | ssize_t length; | ||
216 | |||
217 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled); | ||
218 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
219 | } | ||
220 | |||
221 | static struct file_operations sel_mls_ops = { | ||
222 | .read = sel_read_mls, | ||
223 | }; | ||
224 | |||
225 | static ssize_t sel_write_load(struct file * file, const char __user * buf, | ||
226 | size_t count, loff_t *ppos) | ||
227 | |||
228 | { | ||
229 | int ret; | ||
230 | ssize_t length; | ||
231 | void *data = NULL; | ||
232 | |||
233 | down(&sel_sem); | ||
234 | |||
235 | length = task_has_security(current, SECURITY__LOAD_POLICY); | ||
236 | if (length) | ||
237 | goto out; | ||
238 | |||
239 | if (*ppos != 0) { | ||
240 | /* No partial writes. */ | ||
241 | length = -EINVAL; | ||
242 | goto out; | ||
243 | } | ||
244 | |||
245 | if ((count < 0) || (count > 64 * 1024 * 1024) | ||
246 | || (data = vmalloc(count)) == NULL) { | ||
247 | length = -ENOMEM; | ||
248 | goto out; | ||
249 | } | ||
250 | |||
251 | length = -EFAULT; | ||
252 | if (copy_from_user(data, buf, count) != 0) | ||
253 | goto out; | ||
254 | |||
255 | length = security_load_policy(data, count); | ||
256 | if (length) | ||
257 | goto out; | ||
258 | |||
259 | ret = sel_make_bools(); | ||
260 | if (ret) | ||
261 | length = ret; | ||
262 | else | ||
263 | length = count; | ||
264 | out: | ||
265 | up(&sel_sem); | ||
266 | vfree(data); | ||
267 | return length; | ||
268 | } | ||
269 | |||
270 | static struct file_operations sel_load_ops = { | ||
271 | .write = sel_write_load, | ||
272 | }; | ||
273 | |||
274 | |||
275 | static ssize_t sel_write_context(struct file * file, const char __user * buf, | ||
276 | size_t count, loff_t *ppos) | ||
277 | |||
278 | { | ||
279 | char *page; | ||
280 | u32 sid; | ||
281 | ssize_t length; | ||
282 | |||
283 | length = task_has_security(current, SECURITY__CHECK_CONTEXT); | ||
284 | if (length) | ||
285 | return length; | ||
286 | |||
287 | if (count < 0 || count >= PAGE_SIZE) | ||
288 | return -ENOMEM; | ||
289 | if (*ppos != 0) { | ||
290 | /* No partial writes. */ | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
294 | if (!page) | ||
295 | return -ENOMEM; | ||
296 | length = -EFAULT; | ||
297 | if (copy_from_user(page, buf, count)) | ||
298 | goto out; | ||
299 | |||
300 | length = security_context_to_sid(page, count, &sid); | ||
301 | if (length < 0) | ||
302 | goto out; | ||
303 | |||
304 | length = count; | ||
305 | out: | ||
306 | free_page((unsigned long) page); | ||
307 | return length; | ||
308 | } | ||
309 | |||
310 | static struct file_operations sel_context_ops = { | ||
311 | .write = sel_write_context, | ||
312 | }; | ||
313 | |||
314 | static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, | ||
315 | size_t count, loff_t *ppos) | ||
316 | { | ||
317 | char tmpbuf[TMPBUFLEN]; | ||
318 | ssize_t length; | ||
319 | |||
320 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot); | ||
321 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
322 | } | ||
323 | |||
324 | static ssize_t sel_write_checkreqprot(struct file * file, const char __user * buf, | ||
325 | size_t count, loff_t *ppos) | ||
326 | { | ||
327 | char *page; | ||
328 | ssize_t length; | ||
329 | unsigned int new_value; | ||
330 | |||
331 | length = task_has_security(current, SECURITY__SETCHECKREQPROT); | ||
332 | if (length) | ||
333 | return length; | ||
334 | |||
335 | if (count < 0 || count >= PAGE_SIZE) | ||
336 | return -ENOMEM; | ||
337 | if (*ppos != 0) { | ||
338 | /* No partial writes. */ | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
342 | if (!page) | ||
343 | return -ENOMEM; | ||
344 | length = -EFAULT; | ||
345 | if (copy_from_user(page, buf, count)) | ||
346 | goto out; | ||
347 | |||
348 | length = -EINVAL; | ||
349 | if (sscanf(page, "%u", &new_value) != 1) | ||
350 | goto out; | ||
351 | |||
352 | selinux_checkreqprot = new_value ? 1 : 0; | ||
353 | length = count; | ||
354 | out: | ||
355 | free_page((unsigned long) page); | ||
356 | return length; | ||
357 | } | ||
358 | static struct file_operations sel_checkreqprot_ops = { | ||
359 | .read = sel_read_checkreqprot, | ||
360 | .write = sel_write_checkreqprot, | ||
361 | }; | ||
362 | |||
363 | /* | ||
364 | * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c | ||
365 | */ | ||
366 | static ssize_t sel_write_access(struct file * file, char *buf, size_t size); | ||
367 | static ssize_t sel_write_create(struct file * file, char *buf, size_t size); | ||
368 | static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size); | ||
369 | static ssize_t sel_write_user(struct file * file, char *buf, size_t size); | ||
370 | static ssize_t sel_write_member(struct file * file, char *buf, size_t size); | ||
371 | |||
372 | static ssize_t (*write_op[])(struct file *, char *, size_t) = { | ||
373 | [SEL_ACCESS] = sel_write_access, | ||
374 | [SEL_CREATE] = sel_write_create, | ||
375 | [SEL_RELABEL] = sel_write_relabel, | ||
376 | [SEL_USER] = sel_write_user, | ||
377 | [SEL_MEMBER] = sel_write_member, | ||
378 | }; | ||
379 | |||
380 | static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) | ||
381 | { | ||
382 | ino_t ino = file->f_dentry->d_inode->i_ino; | ||
383 | char *data; | ||
384 | ssize_t rv; | ||
385 | |||
386 | if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino]) | ||
387 | return -EINVAL; | ||
388 | |||
389 | data = simple_transaction_get(file, buf, size); | ||
390 | if (IS_ERR(data)) | ||
391 | return PTR_ERR(data); | ||
392 | |||
393 | rv = write_op[ino](file, data, size); | ||
394 | if (rv>0) { | ||
395 | simple_transaction_set(file, rv); | ||
396 | rv = size; | ||
397 | } | ||
398 | return rv; | ||
399 | } | ||
400 | |||
401 | static struct file_operations transaction_ops = { | ||
402 | .write = selinux_transaction_write, | ||
403 | .read = simple_transaction_read, | ||
404 | .release = simple_transaction_release, | ||
405 | }; | ||
406 | |||
407 | /* | ||
408 | * payload - write methods | ||
409 | * If the method has a response, the response should be put in buf, | ||
410 | * and the length returned. Otherwise return 0 or and -error. | ||
411 | */ | ||
412 | |||
413 | static ssize_t sel_write_access(struct file * file, char *buf, size_t size) | ||
414 | { | ||
415 | char *scon, *tcon; | ||
416 | u32 ssid, tsid; | ||
417 | u16 tclass; | ||
418 | u32 req; | ||
419 | struct av_decision avd; | ||
420 | ssize_t length; | ||
421 | |||
422 | length = task_has_security(current, SECURITY__COMPUTE_AV); | ||
423 | if (length) | ||
424 | return length; | ||
425 | |||
426 | length = -ENOMEM; | ||
427 | scon = kmalloc(size+1, GFP_KERNEL); | ||
428 | if (!scon) | ||
429 | return length; | ||
430 | memset(scon, 0, size+1); | ||
431 | |||
432 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
433 | if (!tcon) | ||
434 | goto out; | ||
435 | memset(tcon, 0, size+1); | ||
436 | |||
437 | length = -EINVAL; | ||
438 | if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) | ||
439 | goto out2; | ||
440 | |||
441 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
442 | if (length < 0) | ||
443 | goto out2; | ||
444 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
445 | if (length < 0) | ||
446 | goto out2; | ||
447 | |||
448 | length = security_compute_av(ssid, tsid, tclass, req, &avd); | ||
449 | if (length < 0) | ||
450 | goto out2; | ||
451 | |||
452 | length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, | ||
453 | "%x %x %x %x %u", | ||
454 | avd.allowed, avd.decided, | ||
455 | avd.auditallow, avd.auditdeny, | ||
456 | avd.seqno); | ||
457 | out2: | ||
458 | kfree(tcon); | ||
459 | out: | ||
460 | kfree(scon); | ||
461 | return length; | ||
462 | } | ||
463 | |||
464 | static ssize_t sel_write_create(struct file * file, char *buf, size_t size) | ||
465 | { | ||
466 | char *scon, *tcon; | ||
467 | u32 ssid, tsid, newsid; | ||
468 | u16 tclass; | ||
469 | ssize_t length; | ||
470 | char *newcon; | ||
471 | u32 len; | ||
472 | |||
473 | length = task_has_security(current, SECURITY__COMPUTE_CREATE); | ||
474 | if (length) | ||
475 | return length; | ||
476 | |||
477 | length = -ENOMEM; | ||
478 | scon = kmalloc(size+1, GFP_KERNEL); | ||
479 | if (!scon) | ||
480 | return length; | ||
481 | memset(scon, 0, size+1); | ||
482 | |||
483 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
484 | if (!tcon) | ||
485 | goto out; | ||
486 | memset(tcon, 0, size+1); | ||
487 | |||
488 | length = -EINVAL; | ||
489 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | ||
490 | goto out2; | ||
491 | |||
492 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
493 | if (length < 0) | ||
494 | goto out2; | ||
495 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
496 | if (length < 0) | ||
497 | goto out2; | ||
498 | |||
499 | length = security_transition_sid(ssid, tsid, tclass, &newsid); | ||
500 | if (length < 0) | ||
501 | goto out2; | ||
502 | |||
503 | length = security_sid_to_context(newsid, &newcon, &len); | ||
504 | if (length < 0) | ||
505 | goto out2; | ||
506 | |||
507 | if (len > SIMPLE_TRANSACTION_LIMIT) { | ||
508 | printk(KERN_ERR "%s: context size (%u) exceeds payload " | ||
509 | "max\n", __FUNCTION__, len); | ||
510 | length = -ERANGE; | ||
511 | goto out3; | ||
512 | } | ||
513 | |||
514 | memcpy(buf, newcon, len); | ||
515 | length = len; | ||
516 | out3: | ||
517 | kfree(newcon); | ||
518 | out2: | ||
519 | kfree(tcon); | ||
520 | out: | ||
521 | kfree(scon); | ||
522 | return length; | ||
523 | } | ||
524 | |||
525 | static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size) | ||
526 | { | ||
527 | char *scon, *tcon; | ||
528 | u32 ssid, tsid, newsid; | ||
529 | u16 tclass; | ||
530 | ssize_t length; | ||
531 | char *newcon; | ||
532 | u32 len; | ||
533 | |||
534 | length = task_has_security(current, SECURITY__COMPUTE_RELABEL); | ||
535 | if (length) | ||
536 | return length; | ||
537 | |||
538 | length = -ENOMEM; | ||
539 | scon = kmalloc(size+1, GFP_KERNEL); | ||
540 | if (!scon) | ||
541 | return length; | ||
542 | memset(scon, 0, size+1); | ||
543 | |||
544 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
545 | if (!tcon) | ||
546 | goto out; | ||
547 | memset(tcon, 0, size+1); | ||
548 | |||
549 | length = -EINVAL; | ||
550 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | ||
551 | goto out2; | ||
552 | |||
553 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
554 | if (length < 0) | ||
555 | goto out2; | ||
556 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
557 | if (length < 0) | ||
558 | goto out2; | ||
559 | |||
560 | length = security_change_sid(ssid, tsid, tclass, &newsid); | ||
561 | if (length < 0) | ||
562 | goto out2; | ||
563 | |||
564 | length = security_sid_to_context(newsid, &newcon, &len); | ||
565 | if (length < 0) | ||
566 | goto out2; | ||
567 | |||
568 | if (len > SIMPLE_TRANSACTION_LIMIT) { | ||
569 | length = -ERANGE; | ||
570 | goto out3; | ||
571 | } | ||
572 | |||
573 | memcpy(buf, newcon, len); | ||
574 | length = len; | ||
575 | out3: | ||
576 | kfree(newcon); | ||
577 | out2: | ||
578 | kfree(tcon); | ||
579 | out: | ||
580 | kfree(scon); | ||
581 | return length; | ||
582 | } | ||
583 | |||
584 | static ssize_t sel_write_user(struct file * file, char *buf, size_t size) | ||
585 | { | ||
586 | char *con, *user, *ptr; | ||
587 | u32 sid, *sids; | ||
588 | ssize_t length; | ||
589 | char *newcon; | ||
590 | int i, rc; | ||
591 | u32 len, nsids; | ||
592 | |||
593 | length = task_has_security(current, SECURITY__COMPUTE_USER); | ||
594 | if (length) | ||
595 | return length; | ||
596 | |||
597 | length = -ENOMEM; | ||
598 | con = kmalloc(size+1, GFP_KERNEL); | ||
599 | if (!con) | ||
600 | return length; | ||
601 | memset(con, 0, size+1); | ||
602 | |||
603 | user = kmalloc(size+1, GFP_KERNEL); | ||
604 | if (!user) | ||
605 | goto out; | ||
606 | memset(user, 0, size+1); | ||
607 | |||
608 | length = -EINVAL; | ||
609 | if (sscanf(buf, "%s %s", con, user) != 2) | ||
610 | goto out2; | ||
611 | |||
612 | length = security_context_to_sid(con, strlen(con)+1, &sid); | ||
613 | if (length < 0) | ||
614 | goto out2; | ||
615 | |||
616 | length = security_get_user_sids(sid, user, &sids, &nsids); | ||
617 | if (length < 0) | ||
618 | goto out2; | ||
619 | |||
620 | length = sprintf(buf, "%u", nsids) + 1; | ||
621 | ptr = buf + length; | ||
622 | for (i = 0; i < nsids; i++) { | ||
623 | rc = security_sid_to_context(sids[i], &newcon, &len); | ||
624 | if (rc) { | ||
625 | length = rc; | ||
626 | goto out3; | ||
627 | } | ||
628 | if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { | ||
629 | kfree(newcon); | ||
630 | length = -ERANGE; | ||
631 | goto out3; | ||
632 | } | ||
633 | memcpy(ptr, newcon, len); | ||
634 | kfree(newcon); | ||
635 | ptr += len; | ||
636 | length += len; | ||
637 | } | ||
638 | out3: | ||
639 | kfree(sids); | ||
640 | out2: | ||
641 | kfree(user); | ||
642 | out: | ||
643 | kfree(con); | ||
644 | return length; | ||
645 | } | ||
646 | |||
647 | static ssize_t sel_write_member(struct file * file, char *buf, size_t size) | ||
648 | { | ||
649 | char *scon, *tcon; | ||
650 | u32 ssid, tsid, newsid; | ||
651 | u16 tclass; | ||
652 | ssize_t length; | ||
653 | char *newcon; | ||
654 | u32 len; | ||
655 | |||
656 | length = task_has_security(current, SECURITY__COMPUTE_MEMBER); | ||
657 | if (length) | ||
658 | return length; | ||
659 | |||
660 | length = -ENOMEM; | ||
661 | scon = kmalloc(size+1, GFP_KERNEL); | ||
662 | if (!scon) | ||
663 | return length; | ||
664 | memset(scon, 0, size+1); | ||
665 | |||
666 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
667 | if (!tcon) | ||
668 | goto out; | ||
669 | memset(tcon, 0, size+1); | ||
670 | |||
671 | length = -EINVAL; | ||
672 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | ||
673 | goto out2; | ||
674 | |||
675 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
676 | if (length < 0) | ||
677 | goto out2; | ||
678 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
679 | if (length < 0) | ||
680 | goto out2; | ||
681 | |||
682 | length = security_member_sid(ssid, tsid, tclass, &newsid); | ||
683 | if (length < 0) | ||
684 | goto out2; | ||
685 | |||
686 | length = security_sid_to_context(newsid, &newcon, &len); | ||
687 | if (length < 0) | ||
688 | goto out2; | ||
689 | |||
690 | if (len > SIMPLE_TRANSACTION_LIMIT) { | ||
691 | printk(KERN_ERR "%s: context size (%u) exceeds payload " | ||
692 | "max\n", __FUNCTION__, len); | ||
693 | length = -ERANGE; | ||
694 | goto out3; | ||
695 | } | ||
696 | |||
697 | memcpy(buf, newcon, len); | ||
698 | length = len; | ||
699 | out3: | ||
700 | kfree(newcon); | ||
701 | out2: | ||
702 | kfree(tcon); | ||
703 | out: | ||
704 | kfree(scon); | ||
705 | return length; | ||
706 | } | ||
707 | |||
708 | static struct inode *sel_make_inode(struct super_block *sb, int mode) | ||
709 | { | ||
710 | struct inode *ret = new_inode(sb); | ||
711 | |||
712 | if (ret) { | ||
713 | ret->i_mode = mode; | ||
714 | ret->i_uid = ret->i_gid = 0; | ||
715 | ret->i_blksize = PAGE_CACHE_SIZE; | ||
716 | ret->i_blocks = 0; | ||
717 | ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; | ||
718 | } | ||
719 | return ret; | ||
720 | } | ||
721 | |||
722 | #define BOOL_INO_OFFSET 30 | ||
723 | |||
724 | static ssize_t sel_read_bool(struct file *filep, char __user *buf, | ||
725 | size_t count, loff_t *ppos) | ||
726 | { | ||
727 | char *page = NULL; | ||
728 | ssize_t length; | ||
729 | ssize_t end; | ||
730 | ssize_t ret; | ||
731 | int cur_enforcing; | ||
732 | struct inode *inode; | ||
733 | |||
734 | down(&sel_sem); | ||
735 | |||
736 | ret = -EFAULT; | ||
737 | |||
738 | /* check to see if this file has been deleted */ | ||
739 | if (!filep->f_op) | ||
740 | goto out; | ||
741 | |||
742 | if (count < 0 || count > PAGE_SIZE) { | ||
743 | ret = -EINVAL; | ||
744 | goto out; | ||
745 | } | ||
746 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) { | ||
747 | ret = -ENOMEM; | ||
748 | goto out; | ||
749 | } | ||
750 | |||
751 | inode = filep->f_dentry->d_inode; | ||
752 | cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET); | ||
753 | if (cur_enforcing < 0) { | ||
754 | ret = cur_enforcing; | ||
755 | goto out; | ||
756 | } | ||
757 | |||
758 | length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, | ||
759 | bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]); | ||
760 | if (length < 0) { | ||
761 | ret = length; | ||
762 | goto out; | ||
763 | } | ||
764 | |||
765 | if (*ppos >= length) { | ||
766 | ret = 0; | ||
767 | goto out; | ||
768 | } | ||
769 | if (count + *ppos > length) | ||
770 | count = length - *ppos; | ||
771 | end = count + *ppos; | ||
772 | if (copy_to_user(buf, (char *) page + *ppos, count)) { | ||
773 | ret = -EFAULT; | ||
774 | goto out; | ||
775 | } | ||
776 | *ppos = end; | ||
777 | ret = count; | ||
778 | out: | ||
779 | up(&sel_sem); | ||
780 | if (page) | ||
781 | free_page((unsigned long)page); | ||
782 | return ret; | ||
783 | } | ||
784 | |||
785 | static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | ||
786 | size_t count, loff_t *ppos) | ||
787 | { | ||
788 | char *page = NULL; | ||
789 | ssize_t length = -EFAULT; | ||
790 | int new_value; | ||
791 | struct inode *inode; | ||
792 | |||
793 | down(&sel_sem); | ||
794 | |||
795 | length = task_has_security(current, SECURITY__SETBOOL); | ||
796 | if (length) | ||
797 | goto out; | ||
798 | |||
799 | /* check to see if this file has been deleted */ | ||
800 | if (!filep->f_op) | ||
801 | goto out; | ||
802 | |||
803 | if (count < 0 || count >= PAGE_SIZE) { | ||
804 | length = -ENOMEM; | ||
805 | goto out; | ||
806 | } | ||
807 | if (*ppos != 0) { | ||
808 | /* No partial writes. */ | ||
809 | goto out; | ||
810 | } | ||
811 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
812 | if (!page) { | ||
813 | length = -ENOMEM; | ||
814 | goto out; | ||
815 | } | ||
816 | |||
817 | if (copy_from_user(page, buf, count)) | ||
818 | goto out; | ||
819 | |||
820 | length = -EINVAL; | ||
821 | if (sscanf(page, "%d", &new_value) != 1) | ||
822 | goto out; | ||
823 | |||
824 | if (new_value) | ||
825 | new_value = 1; | ||
826 | |||
827 | inode = filep->f_dentry->d_inode; | ||
828 | bool_pending_values[inode->i_ino - BOOL_INO_OFFSET] = new_value; | ||
829 | length = count; | ||
830 | |||
831 | out: | ||
832 | up(&sel_sem); | ||
833 | if (page) | ||
834 | free_page((unsigned long) page); | ||
835 | return length; | ||
836 | } | ||
837 | |||
838 | static struct file_operations sel_bool_ops = { | ||
839 | .read = sel_read_bool, | ||
840 | .write = sel_write_bool, | ||
841 | }; | ||
842 | |||
843 | static ssize_t sel_commit_bools_write(struct file *filep, | ||
844 | const char __user *buf, | ||
845 | size_t count, loff_t *ppos) | ||
846 | { | ||
847 | char *page = NULL; | ||
848 | ssize_t length = -EFAULT; | ||
849 | int new_value; | ||
850 | |||
851 | down(&sel_sem); | ||
852 | |||
853 | length = task_has_security(current, SECURITY__SETBOOL); | ||
854 | if (length) | ||
855 | goto out; | ||
856 | |||
857 | /* check to see if this file has been deleted */ | ||
858 | if (!filep->f_op) | ||
859 | goto out; | ||
860 | |||
861 | if (count < 0 || count >= PAGE_SIZE) { | ||
862 | length = -ENOMEM; | ||
863 | goto out; | ||
864 | } | ||
865 | if (*ppos != 0) { | ||
866 | /* No partial writes. */ | ||
867 | goto out; | ||
868 | } | ||
869 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
870 | if (!page) { | ||
871 | length = -ENOMEM; | ||
872 | goto out; | ||
873 | } | ||
874 | |||
875 | if (copy_from_user(page, buf, count)) | ||
876 | goto out; | ||
877 | |||
878 | length = -EINVAL; | ||
879 | if (sscanf(page, "%d", &new_value) != 1) | ||
880 | goto out; | ||
881 | |||
882 | if (new_value) { | ||
883 | security_set_bools(bool_num, bool_pending_values); | ||
884 | } | ||
885 | |||
886 | length = count; | ||
887 | |||
888 | out: | ||
889 | up(&sel_sem); | ||
890 | if (page) | ||
891 | free_page((unsigned long) page); | ||
892 | return length; | ||
893 | } | ||
894 | |||
895 | static struct file_operations sel_commit_bools_ops = { | ||
896 | .write = sel_commit_bools_write, | ||
897 | }; | ||
898 | |||
899 | /* delete booleans - partial revoke() from | ||
900 | * fs/proc/generic.c proc_kill_inodes */ | ||
901 | static void sel_remove_bools(struct dentry *de) | ||
902 | { | ||
903 | struct list_head *p, *node; | ||
904 | struct super_block *sb = de->d_sb; | ||
905 | |||
906 | spin_lock(&dcache_lock); | ||
907 | node = de->d_subdirs.next; | ||
908 | while (node != &de->d_subdirs) { | ||
909 | struct dentry *d = list_entry(node, struct dentry, d_child); | ||
910 | list_del_init(node); | ||
911 | |||
912 | if (d->d_inode) { | ||
913 | d = dget_locked(d); | ||
914 | spin_unlock(&dcache_lock); | ||
915 | d_delete(d); | ||
916 | simple_unlink(de->d_inode, d); | ||
917 | dput(d); | ||
918 | spin_lock(&dcache_lock); | ||
919 | } | ||
920 | node = de->d_subdirs.next; | ||
921 | } | ||
922 | |||
923 | spin_unlock(&dcache_lock); | ||
924 | |||
925 | file_list_lock(); | ||
926 | list_for_each(p, &sb->s_files) { | ||
927 | struct file * filp = list_entry(p, struct file, f_list); | ||
928 | struct dentry * dentry = filp->f_dentry; | ||
929 | |||
930 | if (dentry->d_parent != de) { | ||
931 | continue; | ||
932 | } | ||
933 | filp->f_op = NULL; | ||
934 | } | ||
935 | file_list_unlock(); | ||
936 | } | ||
937 | |||
938 | #define BOOL_DIR_NAME "booleans" | ||
939 | |||
940 | static int sel_make_bools(void) | ||
941 | { | ||
942 | int i, ret = 0; | ||
943 | ssize_t len; | ||
944 | struct dentry *dentry = NULL; | ||
945 | struct dentry *dir = bool_dir; | ||
946 | struct inode *inode = NULL; | ||
947 | struct inode_security_struct *isec; | ||
948 | char **names = NULL, *page; | ||
949 | int num; | ||
950 | int *values = NULL; | ||
951 | u32 sid; | ||
952 | |||
953 | /* remove any existing files */ | ||
954 | if (bool_pending_values) | ||
955 | kfree(bool_pending_values); | ||
956 | |||
957 | sel_remove_bools(dir); | ||
958 | |||
959 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) | ||
960 | return -ENOMEM; | ||
961 | |||
962 | ret = security_get_bools(&num, &names, &values); | ||
963 | if (ret != 0) | ||
964 | goto out; | ||
965 | |||
966 | for (i = 0; i < num; i++) { | ||
967 | dentry = d_alloc_name(dir, names[i]); | ||
968 | if (!dentry) { | ||
969 | ret = -ENOMEM; | ||
970 | goto err; | ||
971 | } | ||
972 | inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); | ||
973 | if (!inode) { | ||
974 | ret = -ENOMEM; | ||
975 | goto err; | ||
976 | } | ||
977 | |||
978 | len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); | ||
979 | if (len < 0) { | ||
980 | ret = -EINVAL; | ||
981 | goto err; | ||
982 | } else if (len >= PAGE_SIZE) { | ||
983 | ret = -ENAMETOOLONG; | ||
984 | goto err; | ||
985 | } | ||
986 | isec = (struct inode_security_struct*)inode->i_security; | ||
987 | if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid))) | ||
988 | goto err; | ||
989 | isec->sid = sid; | ||
990 | isec->initialized = 1; | ||
991 | inode->i_fop = &sel_bool_ops; | ||
992 | inode->i_ino = i + BOOL_INO_OFFSET; | ||
993 | d_add(dentry, inode); | ||
994 | } | ||
995 | bool_num = num; | ||
996 | bool_pending_values = values; | ||
997 | out: | ||
998 | free_page((unsigned long)page); | ||
999 | if (names) { | ||
1000 | for (i = 0; i < num; i++) { | ||
1001 | if (names[i]) | ||
1002 | kfree(names[i]); | ||
1003 | } | ||
1004 | kfree(names); | ||
1005 | } | ||
1006 | return ret; | ||
1007 | err: | ||
1008 | d_genocide(dir); | ||
1009 | ret = -ENOMEM; | ||
1010 | goto out; | ||
1011 | } | ||
1012 | |||
1013 | #define NULL_FILE_NAME "null" | ||
1014 | |||
1015 | struct dentry *selinux_null = NULL; | ||
1016 | |||
1017 | static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, | ||
1018 | size_t count, loff_t *ppos) | ||
1019 | { | ||
1020 | char tmpbuf[TMPBUFLEN]; | ||
1021 | ssize_t length; | ||
1022 | |||
1023 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold); | ||
1024 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
1025 | } | ||
1026 | |||
1027 | static ssize_t sel_write_avc_cache_threshold(struct file * file, | ||
1028 | const char __user * buf, | ||
1029 | size_t count, loff_t *ppos) | ||
1030 | |||
1031 | { | ||
1032 | char *page; | ||
1033 | ssize_t ret; | ||
1034 | int new_value; | ||
1035 | |||
1036 | if (count < 0 || count >= PAGE_SIZE) { | ||
1037 | ret = -ENOMEM; | ||
1038 | goto out; | ||
1039 | } | ||
1040 | |||
1041 | if (*ppos != 0) { | ||
1042 | /* No partial writes. */ | ||
1043 | ret = -EINVAL; | ||
1044 | goto out; | ||
1045 | } | ||
1046 | |||
1047 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
1048 | if (!page) { | ||
1049 | ret = -ENOMEM; | ||
1050 | goto out; | ||
1051 | } | ||
1052 | |||
1053 | if (copy_from_user(page, buf, count)) { | ||
1054 | ret = -EFAULT; | ||
1055 | goto out_free; | ||
1056 | } | ||
1057 | |||
1058 | if (sscanf(page, "%u", &new_value) != 1) { | ||
1059 | ret = -EINVAL; | ||
1060 | goto out; | ||
1061 | } | ||
1062 | |||
1063 | if (new_value != avc_cache_threshold) { | ||
1064 | ret = task_has_security(current, SECURITY__SETSECPARAM); | ||
1065 | if (ret) | ||
1066 | goto out_free; | ||
1067 | avc_cache_threshold = new_value; | ||
1068 | } | ||
1069 | ret = count; | ||
1070 | out_free: | ||
1071 | free_page((unsigned long)page); | ||
1072 | out: | ||
1073 | return ret; | ||
1074 | } | ||
1075 | |||
1076 | static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, | ||
1077 | size_t count, loff_t *ppos) | ||
1078 | { | ||
1079 | char *page; | ||
1080 | ssize_t ret = 0; | ||
1081 | |||
1082 | page = (char *)__get_free_page(GFP_KERNEL); | ||
1083 | if (!page) { | ||
1084 | ret = -ENOMEM; | ||
1085 | goto out; | ||
1086 | } | ||
1087 | ret = avc_get_hash_stats(page); | ||
1088 | if (ret >= 0) | ||
1089 | ret = simple_read_from_buffer(buf, count, ppos, page, ret); | ||
1090 | free_page((unsigned long)page); | ||
1091 | out: | ||
1092 | return ret; | ||
1093 | } | ||
1094 | |||
1095 | static struct file_operations sel_avc_cache_threshold_ops = { | ||
1096 | .read = sel_read_avc_cache_threshold, | ||
1097 | .write = sel_write_avc_cache_threshold, | ||
1098 | }; | ||
1099 | |||
1100 | static struct file_operations sel_avc_hash_stats_ops = { | ||
1101 | .read = sel_read_avc_hash_stats, | ||
1102 | }; | ||
1103 | |||
1104 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
1105 | static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) | ||
1106 | { | ||
1107 | int cpu; | ||
1108 | |||
1109 | for (cpu = *idx; cpu < NR_CPUS; ++cpu) { | ||
1110 | if (!cpu_possible(cpu)) | ||
1111 | continue; | ||
1112 | *idx = cpu + 1; | ||
1113 | return &per_cpu(avc_cache_stats, cpu); | ||
1114 | } | ||
1115 | return NULL; | ||
1116 | } | ||
1117 | |||
1118 | static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos) | ||
1119 | { | ||
1120 | loff_t n = *pos - 1; | ||
1121 | |||
1122 | if (*pos == 0) | ||
1123 | return SEQ_START_TOKEN; | ||
1124 | |||
1125 | return sel_avc_get_stat_idx(&n); | ||
1126 | } | ||
1127 | |||
1128 | static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
1129 | { | ||
1130 | return sel_avc_get_stat_idx(pos); | ||
1131 | } | ||
1132 | |||
1133 | static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) | ||
1134 | { | ||
1135 | struct avc_cache_stats *st = v; | ||
1136 | |||
1137 | if (v == SEQ_START_TOKEN) | ||
1138 | seq_printf(seq, "lookups hits misses allocations reclaims " | ||
1139 | "frees\n"); | ||
1140 | else | ||
1141 | seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups, | ||
1142 | st->hits, st->misses, st->allocations, | ||
1143 | st->reclaims, st->frees); | ||
1144 | return 0; | ||
1145 | } | ||
1146 | |||
1147 | static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) | ||
1148 | { } | ||
1149 | |||
1150 | static struct seq_operations sel_avc_cache_stats_seq_ops = { | ||
1151 | .start = sel_avc_stats_seq_start, | ||
1152 | .next = sel_avc_stats_seq_next, | ||
1153 | .show = sel_avc_stats_seq_show, | ||
1154 | .stop = sel_avc_stats_seq_stop, | ||
1155 | }; | ||
1156 | |||
1157 | static int sel_open_avc_cache_stats(struct inode *inode, struct file *file) | ||
1158 | { | ||
1159 | return seq_open(file, &sel_avc_cache_stats_seq_ops); | ||
1160 | } | ||
1161 | |||
1162 | static struct file_operations sel_avc_cache_stats_ops = { | ||
1163 | .open = sel_open_avc_cache_stats, | ||
1164 | .read = seq_read, | ||
1165 | .llseek = seq_lseek, | ||
1166 | .release = seq_release, | ||
1167 | }; | ||
1168 | #endif | ||
1169 | |||
1170 | static int sel_make_avc_files(struct dentry *dir) | ||
1171 | { | ||
1172 | int i, ret = 0; | ||
1173 | static struct tree_descr files[] = { | ||
1174 | { "cache_threshold", | ||
1175 | &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, | ||
1176 | { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO }, | ||
1177 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
1178 | { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO }, | ||
1179 | #endif | ||
1180 | }; | ||
1181 | |||
1182 | for (i = 0; i < sizeof (files) / sizeof (files[0]); i++) { | ||
1183 | struct inode *inode; | ||
1184 | struct dentry *dentry; | ||
1185 | |||
1186 | dentry = d_alloc_name(dir, files[i].name); | ||
1187 | if (!dentry) { | ||
1188 | ret = -ENOMEM; | ||
1189 | goto err; | ||
1190 | } | ||
1191 | |||
1192 | inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); | ||
1193 | if (!inode) { | ||
1194 | ret = -ENOMEM; | ||
1195 | goto err; | ||
1196 | } | ||
1197 | inode->i_fop = files[i].ops; | ||
1198 | d_add(dentry, inode); | ||
1199 | } | ||
1200 | out: | ||
1201 | return ret; | ||
1202 | err: | ||
1203 | d_genocide(dir); | ||
1204 | goto out; | ||
1205 | } | ||
1206 | |||
1207 | static int sel_make_dir(struct super_block *sb, struct dentry *dentry) | ||
1208 | { | ||
1209 | int ret = 0; | ||
1210 | struct inode *inode; | ||
1211 | |||
1212 | inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); | ||
1213 | if (!inode) { | ||
1214 | ret = -ENOMEM; | ||
1215 | goto out; | ||
1216 | } | ||
1217 | inode->i_op = &simple_dir_inode_operations; | ||
1218 | inode->i_fop = &simple_dir_operations; | ||
1219 | d_add(dentry, inode); | ||
1220 | out: | ||
1221 | return ret; | ||
1222 | } | ||
1223 | |||
1224 | static int sel_fill_super(struct super_block * sb, void * data, int silent) | ||
1225 | { | ||
1226 | int ret; | ||
1227 | struct dentry *dentry; | ||
1228 | struct inode *inode; | ||
1229 | struct inode_security_struct *isec; | ||
1230 | |||
1231 | static struct tree_descr selinux_files[] = { | ||
1232 | [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR}, | ||
1233 | [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR}, | ||
1234 | [SEL_CONTEXT] = {"context", &sel_context_ops, S_IRUGO|S_IWUGO}, | ||
1235 | [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1236 | [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1237 | [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1238 | [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1239 | [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO}, | ||
1240 | [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR}, | ||
1241 | [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO}, | ||
1242 | [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, | ||
1243 | [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1244 | [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, | ||
1245 | /* last one */ {""} | ||
1246 | }; | ||
1247 | ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); | ||
1248 | if (ret) | ||
1249 | return ret; | ||
1250 | |||
1251 | dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME); | ||
1252 | if (!dentry) | ||
1253 | return -ENOMEM; | ||
1254 | |||
1255 | inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); | ||
1256 | if (!inode) | ||
1257 | goto out; | ||
1258 | inode->i_op = &simple_dir_inode_operations; | ||
1259 | inode->i_fop = &simple_dir_operations; | ||
1260 | d_add(dentry, inode); | ||
1261 | bool_dir = dentry; | ||
1262 | ret = sel_make_bools(); | ||
1263 | if (ret) | ||
1264 | goto out; | ||
1265 | |||
1266 | dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); | ||
1267 | if (!dentry) | ||
1268 | return -ENOMEM; | ||
1269 | |||
1270 | inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); | ||
1271 | if (!inode) | ||
1272 | goto out; | ||
1273 | isec = (struct inode_security_struct*)inode->i_security; | ||
1274 | isec->sid = SECINITSID_DEVNULL; | ||
1275 | isec->sclass = SECCLASS_CHR_FILE; | ||
1276 | isec->initialized = 1; | ||
1277 | |||
1278 | init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); | ||
1279 | d_add(dentry, inode); | ||
1280 | selinux_null = dentry; | ||
1281 | |||
1282 | dentry = d_alloc_name(sb->s_root, "avc"); | ||
1283 | if (!dentry) | ||
1284 | return -ENOMEM; | ||
1285 | |||
1286 | ret = sel_make_dir(sb, dentry); | ||
1287 | if (ret) | ||
1288 | goto out; | ||
1289 | |||
1290 | ret = sel_make_avc_files(dentry); | ||
1291 | if (ret) | ||
1292 | goto out; | ||
1293 | |||
1294 | return 0; | ||
1295 | out: | ||
1296 | dput(dentry); | ||
1297 | printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__); | ||
1298 | return -ENOMEM; | ||
1299 | } | ||
1300 | |||
1301 | static struct super_block *sel_get_sb(struct file_system_type *fs_type, | ||
1302 | int flags, const char *dev_name, void *data) | ||
1303 | { | ||
1304 | return get_sb_single(fs_type, flags, data, sel_fill_super); | ||
1305 | } | ||
1306 | |||
1307 | static struct file_system_type sel_fs_type = { | ||
1308 | .name = "selinuxfs", | ||
1309 | .get_sb = sel_get_sb, | ||
1310 | .kill_sb = kill_litter_super, | ||
1311 | }; | ||
1312 | |||
1313 | struct vfsmount *selinuxfs_mount; | ||
1314 | |||
1315 | static int __init init_sel_fs(void) | ||
1316 | { | ||
1317 | int err; | ||
1318 | |||
1319 | if (!selinux_enabled) | ||
1320 | return 0; | ||
1321 | err = register_filesystem(&sel_fs_type); | ||
1322 | if (!err) { | ||
1323 | selinuxfs_mount = kern_mount(&sel_fs_type); | ||
1324 | if (IS_ERR(selinuxfs_mount)) { | ||
1325 | printk(KERN_ERR "selinuxfs: could not mount!\n"); | ||
1326 | err = PTR_ERR(selinuxfs_mount); | ||
1327 | selinuxfs_mount = NULL; | ||
1328 | } | ||
1329 | } | ||
1330 | return err; | ||
1331 | } | ||
1332 | |||
1333 | __initcall(init_sel_fs); | ||
1334 | |||
1335 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
1336 | void exit_sel_fs(void) | ||
1337 | { | ||
1338 | unregister_filesystem(&sel_fs_type); | ||
1339 | } | ||
1340 | #endif | ||