aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/ss
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/selinux/ss
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/selinux/ss')
-rw-r--r--security/selinux/ss/Makefile2
-rw-r--r--security/selinux/ss/services.c3
-rw-r--r--security/selinux/ss/status.c129
3 files changed, 133 insertions, 1 deletions
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}