diff options
Diffstat (limited to 'security/selinux/selinuxfs.c')
-rw-r--r-- | security/selinux/selinuxfs.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 79a1bb635662..87e0556bae70 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
@@ -68,6 +68,8 @@ static int *bool_pending_values; | |||
68 | static struct dentry *class_dir; | 68 | static struct dentry *class_dir; |
69 | static unsigned long last_class_ino; | 69 | static unsigned long last_class_ino; |
70 | 70 | ||
71 | static char policy_opened; | ||
72 | |||
71 | /* global data for policy capabilities */ | 73 | /* global data for policy capabilities */ |
72 | static struct dentry *policycap_dir; | 74 | static struct dentry *policycap_dir; |
73 | 75 | ||
@@ -110,6 +112,8 @@ enum sel_inos { | |||
110 | SEL_COMPAT_NET, /* whether to use old compat network packet controls */ | 112 | SEL_COMPAT_NET, /* whether to use old compat network packet controls */ |
111 | SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ | 113 | SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ |
112 | SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ | 114 | SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ |
115 | SEL_STATUS, /* export current status using mmap() */ | ||
116 | SEL_POLICY, /* allow userspace to read the in kernel policy */ | ||
113 | SEL_INO_NEXT, /* The next inode number to use */ | 117 | SEL_INO_NEXT, /* The next inode number to use */ |
114 | }; | 118 | }; |
115 | 119 | ||
@@ -171,6 +175,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, | |||
171 | if (selinux_enforcing) | 175 | if (selinux_enforcing) |
172 | avc_ss_reset(0); | 176 | avc_ss_reset(0); |
173 | selnl_notify_setenforce(selinux_enforcing); | 177 | selnl_notify_setenforce(selinux_enforcing); |
178 | selinux_status_update_setenforce(selinux_enforcing); | ||
174 | } | 179 | } |
175 | length = count; | 180 | length = count; |
176 | out: | 181 | out: |
@@ -205,6 +210,59 @@ static const struct file_operations sel_handle_unknown_ops = { | |||
205 | .llseek = generic_file_llseek, | 210 | .llseek = generic_file_llseek, |
206 | }; | 211 | }; |
207 | 212 | ||
213 | static int sel_open_handle_status(struct inode *inode, struct file *filp) | ||
214 | { | ||
215 | struct page *status = selinux_kernel_status_page(); | ||
216 | |||
217 | if (!status) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | filp->private_data = status; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static ssize_t sel_read_handle_status(struct file *filp, char __user *buf, | ||
226 | size_t count, loff_t *ppos) | ||
227 | { | ||
228 | struct page *status = filp->private_data; | ||
229 | |||
230 | BUG_ON(!status); | ||
231 | |||
232 | return simple_read_from_buffer(buf, count, ppos, | ||
233 | page_address(status), | ||
234 | sizeof(struct selinux_kernel_status)); | ||
235 | } | ||
236 | |||
237 | static int sel_mmap_handle_status(struct file *filp, | ||
238 | struct vm_area_struct *vma) | ||
239 | { | ||
240 | struct page *status = filp->private_data; | ||
241 | unsigned long size = vma->vm_end - vma->vm_start; | ||
242 | |||
243 | BUG_ON(!status); | ||
244 | |||
245 | /* only allows one page from the head */ | ||
246 | if (vma->vm_pgoff > 0 || size != PAGE_SIZE) | ||
247 | return -EIO; | ||
248 | /* disallow writable mapping */ | ||
249 | if (vma->vm_flags & VM_WRITE) | ||
250 | return -EPERM; | ||
251 | /* disallow mprotect() turns it into writable */ | ||
252 | vma->vm_flags &= ~VM_MAYWRITE; | ||
253 | |||
254 | return remap_pfn_range(vma, vma->vm_start, | ||
255 | page_to_pfn(status), | ||
256 | size, vma->vm_page_prot); | ||
257 | } | ||
258 | |||
259 | static const struct file_operations sel_handle_status_ops = { | ||
260 | .open = sel_open_handle_status, | ||
261 | .read = sel_read_handle_status, | ||
262 | .mmap = sel_mmap_handle_status, | ||
263 | .llseek = generic_file_llseek, | ||
264 | }; | ||
265 | |||
208 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | 266 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE |
209 | static ssize_t sel_write_disable(struct file *file, const char __user *buf, | 267 | static ssize_t sel_write_disable(struct file *file, const char __user *buf, |
210 | size_t count, loff_t *ppos) | 268 | size_t count, loff_t *ppos) |
@@ -296,6 +354,141 @@ static const struct file_operations sel_mls_ops = { | |||
296 | .llseek = generic_file_llseek, | 354 | .llseek = generic_file_llseek, |
297 | }; | 355 | }; |
298 | 356 | ||
357 | struct policy_load_memory { | ||
358 | size_t len; | ||
359 | void *data; | ||
360 | }; | ||
361 | |||
362 | static int sel_open_policy(struct inode *inode, struct file *filp) | ||
363 | { | ||
364 | struct policy_load_memory *plm = NULL; | ||
365 | int rc; | ||
366 | |||
367 | BUG_ON(filp->private_data); | ||
368 | |||
369 | mutex_lock(&sel_mutex); | ||
370 | |||
371 | rc = task_has_security(current, SECURITY__READ_POLICY); | ||
372 | if (rc) | ||
373 | goto err; | ||
374 | |||
375 | rc = -EBUSY; | ||
376 | if (policy_opened) | ||
377 | goto err; | ||
378 | |||
379 | rc = -ENOMEM; | ||
380 | plm = kzalloc(sizeof(*plm), GFP_KERNEL); | ||
381 | if (!plm) | ||
382 | goto err; | ||
383 | |||
384 | if (i_size_read(inode) != security_policydb_len()) { | ||
385 | mutex_lock(&inode->i_mutex); | ||
386 | i_size_write(inode, security_policydb_len()); | ||
387 | mutex_unlock(&inode->i_mutex); | ||
388 | } | ||
389 | |||
390 | rc = security_read_policy(&plm->data, &plm->len); | ||
391 | if (rc) | ||
392 | goto err; | ||
393 | |||
394 | policy_opened = 1; | ||
395 | |||
396 | filp->private_data = plm; | ||
397 | |||
398 | mutex_unlock(&sel_mutex); | ||
399 | |||
400 | return 0; | ||
401 | err: | ||
402 | mutex_unlock(&sel_mutex); | ||
403 | |||
404 | if (plm) | ||
405 | vfree(plm->data); | ||
406 | kfree(plm); | ||
407 | return rc; | ||
408 | } | ||
409 | |||
410 | static int sel_release_policy(struct inode *inode, struct file *filp) | ||
411 | { | ||
412 | struct policy_load_memory *plm = filp->private_data; | ||
413 | |||
414 | BUG_ON(!plm); | ||
415 | |||
416 | policy_opened = 0; | ||
417 | |||
418 | vfree(plm->data); | ||
419 | kfree(plm); | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static ssize_t sel_read_policy(struct file *filp, char __user *buf, | ||
425 | size_t count, loff_t *ppos) | ||
426 | { | ||
427 | struct policy_load_memory *plm = filp->private_data; | ||
428 | int ret; | ||
429 | |||
430 | mutex_lock(&sel_mutex); | ||
431 | |||
432 | ret = task_has_security(current, SECURITY__READ_POLICY); | ||
433 | if (ret) | ||
434 | goto out; | ||
435 | |||
436 | ret = simple_read_from_buffer(buf, count, ppos, plm->data, plm->len); | ||
437 | out: | ||
438 | mutex_unlock(&sel_mutex); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | static int sel_mmap_policy_fault(struct vm_area_struct *vma, | ||
443 | struct vm_fault *vmf) | ||
444 | { | ||
445 | struct policy_load_memory *plm = vma->vm_file->private_data; | ||
446 | unsigned long offset; | ||
447 | struct page *page; | ||
448 | |||
449 | if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE)) | ||
450 | return VM_FAULT_SIGBUS; | ||
451 | |||
452 | offset = vmf->pgoff << PAGE_SHIFT; | ||
453 | if (offset >= roundup(plm->len, PAGE_SIZE)) | ||
454 | return VM_FAULT_SIGBUS; | ||
455 | |||
456 | page = vmalloc_to_page(plm->data + offset); | ||
457 | get_page(page); | ||
458 | |||
459 | vmf->page = page; | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | static struct vm_operations_struct sel_mmap_policy_ops = { | ||
465 | .fault = sel_mmap_policy_fault, | ||
466 | .page_mkwrite = sel_mmap_policy_fault, | ||
467 | }; | ||
468 | |||
469 | int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) | ||
470 | { | ||
471 | if (vma->vm_flags & VM_SHARED) { | ||
472 | /* do not allow mprotect to make mapping writable */ | ||
473 | vma->vm_flags &= ~VM_MAYWRITE; | ||
474 | |||
475 | if (vma->vm_flags & VM_WRITE) | ||
476 | return -EACCES; | ||
477 | } | ||
478 | |||
479 | vma->vm_flags |= VM_RESERVED; | ||
480 | vma->vm_ops = &sel_mmap_policy_ops; | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static const struct file_operations sel_policy_ops = { | ||
486 | .open = sel_open_policy, | ||
487 | .read = sel_read_policy, | ||
488 | .mmap = sel_mmap_policy, | ||
489 | .release = sel_release_policy, | ||
490 | }; | ||
491 | |||
299 | static ssize_t sel_write_load(struct file *file, const char __user *buf, | 492 | static ssize_t sel_write_load(struct file *file, const char __user *buf, |
300 | size_t count, loff_t *ppos) | 493 | size_t count, loff_t *ppos) |
301 | 494 | ||
@@ -1612,6 +1805,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) | |||
1612 | [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, | 1805 | [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, |
1613 | [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, | 1806 | [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, |
1614 | [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, | 1807 | [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, |
1808 | [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, | ||
1809 | [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUSR}, | ||
1615 | /* last one */ {""} | 1810 | /* last one */ {""} |
1616 | }; | 1811 | }; |
1617 | ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); | 1812 | ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); |