aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorKaiGai Kohei <kaigai@ak.jp.nec.com>2010-09-14 05:28:39 -0400
committerJames Morris <jmorris@namei.org>2010-10-20 19:12:36 -0400
commit119041672592d1890d89dd8f194bd0919d801dc8 (patch)
treeb994abb42446b8637f072194c57359fd80d52a97 /security
parent4b04a7cfc5ccb573ca3752429c81d37f8dd2f7c6 (diff)
selinux: fast status update interface (/selinux/status)
This patch provides a new /selinux/status entry which allows applications read-only mmap(2). This region reflects selinux_kernel_status structure in kernel space. struct selinux_kernel_status { u32 length; /* length of this structure */ u32 sequence; /* sequence number of seqlock logic */ u32 enforcing; /* current setting of enforcing mode */ u32 policyload; /* times of policy reloaded */ u32 deny_unknown; /* current setting of deny_unknown */ }; When userspace object manager caches access control decisions provided by SELinux, it needs to invalidate the cache on policy reload and setenforce to keep consistency. However, the applications need to check the kernel state for each accesses on userspace avc, or launch a background worker process. In heuristic, frequency of invalidation is much less than frequency of making access control decision, so it is annoying to invoke a system call to check we don't need to invalidate the userspace cache. If we can use a background worker thread, it allows to receive invalidation messages from the kernel. But it requires us an invasive coding toward the base application in some cases; E.g, when we provide a feature performing with SELinux as a plugin module, it is unwelcome manner to launch its own worker thread from the module. If we could map /selinux/status to process memory space, application can know updates of selinux status; policy reload or setenforce. A typical application checks selinux_kernel_status::sequence when it tries to reference userspace avc. If it was changed from the last time when it checked userspace avc, it means something was updated in the kernel space. Then, the application can reset userspace avc or update current enforcing mode, without any system call invocations. This sequence number is updated according to the seqlock logic, so we need to wait for a while if it is odd number. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Acked-by: Eric Paris <eparis@redhat.com> -- security/selinux/include/security.h | 21 ++++++ security/selinux/selinuxfs.c | 56 +++++++++++++++ security/selinux/ss/Makefile | 2 +- security/selinux/ss/services.c | 3 + security/selinux/ss/status.c | 129 +++++++++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+), 1 deletions(-) Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security')
-rw-r--r--security/selinux/include/security.h21
-rw-r--r--security/selinux/selinuxfs.c56
-rw-r--r--security/selinux/ss/Makefile2
-rw-r--r--security/selinux/ss/services.c3
-rw-r--r--security/selinux/ss/status.c129
5 files changed, 210 insertions, 1 deletions
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 1f7c2491d3dc..e390e31bb4bf 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -191,5 +191,26 @@ static inline int security_netlbl_sid_to_secattr(u32 sid,
191 191
192const char *security_get_initial_sid_context(u32 sid); 192const char *security_get_initial_sid_context(u32 sid);
193 193
194/*
195 * status notifier using mmap interface
196 */
197extern struct page *selinux_kernel_status_page(void);
198
199#define SELINUX_KERNEL_STATUS_VERSION 1
200struct selinux_kernel_status
201{
202 u32 version; /* version number of thie structure */
203 u32 sequence; /* sequence number of seqlock logic */
204 u32 enforcing; /* current setting of enforcing mode */
205 u32 policyload; /* times of policy reloaded */
206 u32 deny_unknown; /* current setting of deny_unknown */
207 /*
208 * The version > 0 supports above members.
209 */
210} __attribute__((packed));
211
212extern void selinux_status_update_setenforce(int enforcing);
213extern void selinux_status_update_policyload(int seqno);
214
194#endif /* _SELINUX_SECURITY_H_ */ 215#endif /* _SELINUX_SECURITY_H_ */
195 216
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 79a1bb635662..a2e7a8563b38 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -110,6 +110,7 @@ enum sel_inos {
110 SEL_COMPAT_NET, /* whether to use old compat network packet controls */ 110 SEL_COMPAT_NET, /* whether to use old compat network packet controls */
111 SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ 111 SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */
112 SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ 112 SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */
113 SEL_STATUS, /* export current status using mmap() */
113 SEL_INO_NEXT, /* The next inode number to use */ 114 SEL_INO_NEXT, /* The next inode number to use */
114}; 115};
115 116
@@ -171,6 +172,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
171 if (selinux_enforcing) 172 if (selinux_enforcing)
172 avc_ss_reset(0); 173 avc_ss_reset(0);
173 selnl_notify_setenforce(selinux_enforcing); 174 selnl_notify_setenforce(selinux_enforcing);
175 selinux_status_update_setenforce(selinux_enforcing);
174 } 176 }
175 length = count; 177 length = count;
176out: 178out:
@@ -205,6 +207,59 @@ static const struct file_operations sel_handle_unknown_ops = {
205 .llseek = generic_file_llseek, 207 .llseek = generic_file_llseek,
206}; 208};
207 209
210static int sel_open_handle_status(struct inode *inode, struct file *filp)
211{
212 struct page *status = selinux_kernel_status_page();
213
214 if (!status)
215 return -ENOMEM;
216
217 filp->private_data = status;
218
219 return 0;
220}
221
222static ssize_t sel_read_handle_status(struct file *filp, char __user *buf,
223 size_t count, loff_t *ppos)
224{
225 struct page *status = filp->private_data;
226
227 BUG_ON(!status);
228
229 return simple_read_from_buffer(buf, count, ppos,
230 page_address(status),
231 sizeof(struct selinux_kernel_status));
232}
233
234static int sel_mmap_handle_status(struct file *filp,
235 struct vm_area_struct *vma)
236{
237 struct page *status = filp->private_data;
238 unsigned long size = vma->vm_end - vma->vm_start;
239
240 BUG_ON(!status);
241
242 /* only allows one page from the head */
243 if (vma->vm_pgoff > 0 || size != PAGE_SIZE)
244 return -EIO;
245 /* disallow writable mapping */
246 if (vma->vm_flags & VM_WRITE)
247 return -EPERM;
248 /* disallow mprotect() turns it into writable */
249 vma->vm_flags &= ~VM_MAYWRITE;
250
251 return remap_pfn_range(vma, vma->vm_start,
252 page_to_pfn(status),
253 size, vma->vm_page_prot);
254}
255
256static const struct file_operations sel_handle_status_ops = {
257 .open = sel_open_handle_status,
258 .read = sel_read_handle_status,
259 .mmap = sel_mmap_handle_status,
260 .llseek = generic_file_llseek,
261};
262
208#ifdef CONFIG_SECURITY_SELINUX_DISABLE 263#ifdef CONFIG_SECURITY_SELINUX_DISABLE
209static ssize_t sel_write_disable(struct file *file, const char __user *buf, 264static ssize_t sel_write_disable(struct file *file, const char __user *buf,
210 size_t count, loff_t *ppos) 265 size_t count, loff_t *ppos)
@@ -1612,6 +1667,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
1612 [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, 1667 [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
1613 [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, 1668 [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
1614 [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, 1669 [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
1670 [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
1615 /* last one */ {""} 1671 /* last one */ {""}
1616 }; 1672 };
1617 ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); 1673 ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile
index 15d4e62917de..974e11c7cf54 100644
--- a/security/selinux/ss/Makefile
+++ b/security/selinux/ss/Makefile
@@ -5,5 +5,5 @@
5EXTRA_CFLAGS += -Isecurity/selinux -Isecurity/selinux/include 5EXTRA_CFLAGS += -Isecurity/selinux -Isecurity/selinux/include
6obj-y := ss.o 6obj-y := ss.o
7 7
8ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o 8ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o status.o
9 9
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 9ea2feca3cd4..494ff527c174 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1791,6 +1791,7 @@ int security_load_policy(void *data, size_t len)
1791 selinux_complete_init(); 1791 selinux_complete_init();
1792 avc_ss_reset(seqno); 1792 avc_ss_reset(seqno);
1793 selnl_notify_policyload(seqno); 1793 selnl_notify_policyload(seqno);
1794 selinux_status_update_policyload(seqno);
1794 selinux_netlbl_cache_invalidate(); 1795 selinux_netlbl_cache_invalidate();
1795 selinux_xfrm_notify_policyload(); 1796 selinux_xfrm_notify_policyload();
1796 return 0; 1797 return 0;
@@ -1870,6 +1871,7 @@ int security_load_policy(void *data, size_t len)
1870 1871
1871 avc_ss_reset(seqno); 1872 avc_ss_reset(seqno);
1872 selnl_notify_policyload(seqno); 1873 selnl_notify_policyload(seqno);
1874 selinux_status_update_policyload(seqno);
1873 selinux_netlbl_cache_invalidate(); 1875 selinux_netlbl_cache_invalidate();
1874 selinux_xfrm_notify_policyload(); 1876 selinux_xfrm_notify_policyload();
1875 1877
@@ -2374,6 +2376,7 @@ out:
2374 if (!rc) { 2376 if (!rc) {
2375 avc_ss_reset(seqno); 2377 avc_ss_reset(seqno);
2376 selnl_notify_policyload(seqno); 2378 selnl_notify_policyload(seqno);
2379 selinux_status_update_policyload(seqno);
2377 selinux_xfrm_notify_policyload(); 2380 selinux_xfrm_notify_policyload();
2378 } 2381 }
2379 return rc; 2382 return rc;
diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c
new file mode 100644
index 000000000000..5d9b225f8568
--- /dev/null
+++ b/security/selinux/ss/status.c
@@ -0,0 +1,129 @@
1/*
2 * mmap based event notifications for SELinux
3 *
4 * Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
5 *
6 * Copyright (C) 2010 NEC corporation
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2,
10 * as published by the Free Software Foundation.
11 */
12#include <linux/kernel.h>
13#include <linux/gfp.h>
14#include <linux/mm.h>
15#include <linux/mutex.h>
16#include "avc.h"
17#include "services.h"
18
19/*
20 * The selinux_status_page shall be exposed to userspace applications
21 * using mmap interface on /selinux/status.
22 * It enables to notify applications a few events that will cause reset
23 * of userspace access vector without context switching.
24 *
25 * The selinux_kernel_status structure on the head of status page is
26 * protected from concurrent accesses using seqlock logic, so userspace
27 * application should reference the status page according to the seqlock
28 * logic.
29 *
30 * Typically, application checks status->sequence at the head of access
31 * control routine. If it is odd-number, kernel is updating the status,
32 * so please wait for a moment. If it is changed from the last sequence
33 * number, it means something happen, so application will reset userspace
34 * avc, if needed.
35 * In most cases, application shall confirm the kernel status is not
36 * changed without any system call invocations.
37 */
38static struct page *selinux_status_page = NULL;
39static DEFINE_MUTEX(selinux_status_lock);
40
41/*
42 * selinux_kernel_status_page
43 *
44 * It returns a reference to selinux_status_page. If the status page is
45 * not allocated yet, it also tries to allocate it at the first time.
46 */
47struct page *selinux_kernel_status_page(void)
48{
49 struct selinux_kernel_status *status;
50 struct page *result = NULL;
51
52 mutex_lock(&selinux_status_lock);
53 if (!selinux_status_page)
54 {
55 selinux_status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
56 if (selinux_status_page)
57 {
58 status = page_address(selinux_status_page);
59
60 status->version = SELINUX_KERNEL_STATUS_VERSION;
61 status->sequence = 0;
62 status->enforcing = selinux_enforcing;
63 /*
64 * NOTE: the next policyload event shall set
65 * a positive value on the status->policyload,
66 * although it may not be 1, but never zero.
67 * So, application can know it was updated.
68 */
69 status->policyload = 0;
70 status->deny_unknown = !security_get_allow_unknown();
71 }
72 }
73 result = selinux_status_page;
74 mutex_unlock(&selinux_status_lock);
75
76 return result;
77}
78
79/*
80 * selinux_status_update_setenforce
81 *
82 * It updates status of the current enforcing/permissive mode.
83 */
84void selinux_status_update_setenforce(int enforcing)
85{
86 struct selinux_kernel_status *status;
87
88 mutex_lock(&selinux_status_lock);
89 if (selinux_status_page)
90 {
91 status = page_address(selinux_status_page);
92
93 status->sequence++;
94 smp_wmb();
95
96 status->enforcing = enforcing;
97
98 smp_wmb();
99 status->sequence++;
100 }
101 mutex_unlock(&selinux_status_lock);
102}
103
104/*
105 * selinux_status_update_policyload
106 *
107 * It updates status of the times of policy reloaded, and current
108 * setting of deny_unknown.
109 */
110void selinux_status_update_policyload(int seqno)
111{
112 struct selinux_kernel_status *status;
113
114 mutex_lock(&selinux_status_lock);
115 if (selinux_status_page)
116 {
117 status = page_address(selinux_status_page);
118
119 status->sequence++;
120 smp_wmb();
121
122 status->policyload = seqno;
123 status->deny_unknown = !security_get_allow_unknown();
124
125 smp_wmb();
126 status->sequence++;
127 }
128 mutex_unlock(&selinux_status_lock);
129}