diff options
author | Eric Paris <eparis@redhat.com> | 2009-07-31 12:54:11 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-08-17 01:09:11 -0400 |
commit | 788084aba2ab7348257597496befcbccabdc98a3 (patch) | |
tree | 2da42d746d67b16ef705229a1b5a3528ec19c725 | |
parent | 8cf948e744e0218af604c32edecde10006dc8e9e (diff) |
Security/SELinux: seperate lsm specific mmap_min_addr
Currently SELinux enforcement of controls on the ability to map low memory
is determined by the mmap_min_addr tunable. This patch causes SELinux to
ignore the tunable and instead use a seperate Kconfig option specific to how
much space the LSM should protect.
The tunable will now only control the need for CAP_SYS_RAWIO and SELinux
permissions will always protect the amount of low memory designated by
CONFIG_LSM_MMAP_MIN_ADDR.
This allows users who need to disable the mmap_min_addr controls (usual reason
being they run WINE as a non-root user) to do so and still have SELinux
controls preventing confined domains (like a web server) from being able to
map some area of low memory.
Signed-off-by: Eric Paris <eparis@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
-rw-r--r-- | include/linux/mm.h | 15 | ||||
-rw-r--r-- | include/linux/security.h | 17 | ||||
-rw-r--r-- | kernel/sysctl.c | 7 | ||||
-rw-r--r-- | mm/Kconfig | 6 | ||||
-rw-r--r-- | mm/mmap.c | 3 | ||||
-rw-r--r-- | mm/nommu.c | 3 | ||||
-rw-r--r-- | security/Kconfig | 16 | ||||
-rw-r--r-- | security/Makefile | 2 | ||||
-rw-r--r-- | security/commoncap.c | 2 | ||||
-rw-r--r-- | security/min_addr.c | 49 | ||||
-rw-r--r-- | security/selinux/hooks.c | 2 |
11 files changed, 92 insertions, 30 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index ba3a7cb1eaa0..9a72cc78e6b8 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -34,8 +34,6 @@ extern int sysctl_legacy_va_layout; | |||
34 | #define sysctl_legacy_va_layout 0 | 34 | #define sysctl_legacy_va_layout 0 |
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | extern unsigned long mmap_min_addr; | ||
38 | |||
39 | #include <asm/page.h> | 37 | #include <asm/page.h> |
40 | #include <asm/pgtable.h> | 38 | #include <asm/pgtable.h> |
41 | #include <asm/processor.h> | 39 | #include <asm/processor.h> |
@@ -575,19 +573,6 @@ static inline void set_page_links(struct page *page, enum zone_type zone, | |||
575 | } | 573 | } |
576 | 574 | ||
577 | /* | 575 | /* |
578 | * If a hint addr is less than mmap_min_addr change hint to be as | ||
579 | * low as possible but still greater than mmap_min_addr | ||
580 | */ | ||
581 | static inline unsigned long round_hint_to_min(unsigned long hint) | ||
582 | { | ||
583 | hint &= PAGE_MASK; | ||
584 | if (((void *)hint != NULL) && | ||
585 | (hint < mmap_min_addr)) | ||
586 | return PAGE_ALIGN(mmap_min_addr); | ||
587 | return hint; | ||
588 | } | ||
589 | |||
590 | /* | ||
591 | * Some inline functions in vmstat.h depend on page_zone() | 576 | * Some inline functions in vmstat.h depend on page_zone() |
592 | */ | 577 | */ |
593 | #include <linux/vmstat.h> | 578 | #include <linux/vmstat.h> |
diff --git a/include/linux/security.h b/include/linux/security.h index ac4bc3760b46..dc3472c1f781 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/resource.h> | 28 | #include <linux/resource.h> |
29 | #include <linux/sem.h> | 29 | #include <linux/sem.h> |
30 | #include <linux/shm.h> | 30 | #include <linux/shm.h> |
31 | #include <linux/mm.h> /* PAGE_ALIGN */ | ||
31 | #include <linux/msg.h> | 32 | #include <linux/msg.h> |
32 | #include <linux/sched.h> | 33 | #include <linux/sched.h> |
33 | #include <linux/key.h> | 34 | #include <linux/key.h> |
@@ -95,6 +96,7 @@ extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); | |||
95 | extern int cap_netlink_recv(struct sk_buff *skb, int cap); | 96 | extern int cap_netlink_recv(struct sk_buff *skb, int cap); |
96 | 97 | ||
97 | extern unsigned long mmap_min_addr; | 98 | extern unsigned long mmap_min_addr; |
99 | extern unsigned long dac_mmap_min_addr; | ||
98 | /* | 100 | /* |
99 | * Values used in the task_security_ops calls | 101 | * Values used in the task_security_ops calls |
100 | */ | 102 | */ |
@@ -147,6 +149,21 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) | |||
147 | opts->num_mnt_opts = 0; | 149 | opts->num_mnt_opts = 0; |
148 | } | 150 | } |
149 | 151 | ||
152 | /* | ||
153 | * If a hint addr is less than mmap_min_addr change hint to be as | ||
154 | * low as possible but still greater than mmap_min_addr | ||
155 | */ | ||
156 | static inline unsigned long round_hint_to_min(unsigned long hint) | ||
157 | { | ||
158 | hint &= PAGE_MASK; | ||
159 | if (((void *)hint != NULL) && | ||
160 | (hint < mmap_min_addr)) | ||
161 | return PAGE_ALIGN(mmap_min_addr); | ||
162 | return hint; | ||
163 | } | ||
164 | |||
165 | extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp, | ||
166 | void __user *buffer, size_t *lenp, loff_t *ppos); | ||
150 | /** | 167 | /** |
151 | * struct security_operations - main security structure | 168 | * struct security_operations - main security structure |
152 | * | 169 | * |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 98e02328c67d..58be76017fd0 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <linux/acpi.h> | 49 | #include <linux/acpi.h> |
50 | #include <linux/reboot.h> | 50 | #include <linux/reboot.h> |
51 | #include <linux/ftrace.h> | 51 | #include <linux/ftrace.h> |
52 | #include <linux/security.h> | ||
52 | #include <linux/slow-work.h> | 53 | #include <linux/slow-work.h> |
53 | #include <linux/perf_counter.h> | 54 | #include <linux/perf_counter.h> |
54 | 55 | ||
@@ -1306,10 +1307,10 @@ static struct ctl_table vm_table[] = { | |||
1306 | { | 1307 | { |
1307 | .ctl_name = CTL_UNNUMBERED, | 1308 | .ctl_name = CTL_UNNUMBERED, |
1308 | .procname = "mmap_min_addr", | 1309 | .procname = "mmap_min_addr", |
1309 | .data = &mmap_min_addr, | 1310 | .data = &dac_mmap_min_addr, |
1310 | .maxlen = sizeof(unsigned long), | 1311 | .maxlen = sizeof(unsigned long), |
1311 | .mode = 0644, | 1312 | .mode = 0644, |
1312 | .proc_handler = &proc_doulongvec_minmax, | 1313 | .proc_handler = &mmap_min_addr_handler, |
1313 | }, | 1314 | }, |
1314 | #ifdef CONFIG_NUMA | 1315 | #ifdef CONFIG_NUMA |
1315 | { | 1316 | { |
diff --git a/mm/Kconfig b/mm/Kconfig index c948d4ca8bde..fe5f674d7a7d 100644 --- a/mm/Kconfig +++ b/mm/Kconfig | |||
@@ -225,9 +225,9 @@ config DEFAULT_MMAP_MIN_ADDR | |||
225 | For most ia64, ppc64 and x86 users with lots of address space | 225 | For most ia64, ppc64 and x86 users with lots of address space |
226 | a value of 65536 is reasonable and should cause no problems. | 226 | a value of 65536 is reasonable and should cause no problems. |
227 | On arm and other archs it should not be higher than 32768. | 227 | On arm and other archs it should not be higher than 32768. |
228 | Programs which use vm86 functionality would either need additional | 228 | Programs which use vm86 functionality or have some need to map |
229 | permissions from either the LSM or the capabilities module or have | 229 | this low address space will need CAP_SYS_RAWIO or disable this |
230 | this protection disabled. | 230 | protection by setting the value to 0. |
231 | 231 | ||
232 | This value can be changed after boot using the | 232 | This value can be changed after boot using the |
233 | /proc/sys/vm/mmap_min_addr tunable. | 233 | /proc/sys/vm/mmap_min_addr tunable. |
@@ -88,9 +88,6 @@ int sysctl_overcommit_ratio = 50; /* default is 50% */ | |||
88 | int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT; | 88 | int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT; |
89 | struct percpu_counter vm_committed_as; | 89 | struct percpu_counter vm_committed_as; |
90 | 90 | ||
91 | /* amount of vm to protect from userspace access */ | ||
92 | unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR; | ||
93 | |||
94 | /* | 91 | /* |
95 | * Check that a process has enough memory to allocate a new virtual | 92 | * Check that a process has enough memory to allocate a new virtual |
96 | * mapping. 0 means there is enough memory for the allocation to | 93 | * mapping. 0 means there is enough memory for the allocation to |
diff --git a/mm/nommu.c b/mm/nommu.c index 53cab10fece4..28754c40be98 100644 --- a/mm/nommu.c +++ b/mm/nommu.c | |||
@@ -69,9 +69,6 @@ int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT; | |||
69 | int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS; | 69 | int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS; |
70 | int heap_stack_gap = 0; | 70 | int heap_stack_gap = 0; |
71 | 71 | ||
72 | /* amount of vm to protect from userspace access */ | ||
73 | unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR; | ||
74 | |||
75 | atomic_long_t mmap_pages_allocated; | 72 | atomic_long_t mmap_pages_allocated; |
76 | 73 | ||
77 | EXPORT_SYMBOL(mem_map); | 74 | EXPORT_SYMBOL(mem_map); |
diff --git a/security/Kconfig b/security/Kconfig index d23c839038f0..9c60c346a91d 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
@@ -113,6 +113,22 @@ config SECURITY_ROOTPLUG | |||
113 | 113 | ||
114 | If you are unsure how to answer this question, answer N. | 114 | If you are unsure how to answer this question, answer N. |
115 | 115 | ||
116 | config LSM_MMAP_MIN_ADDR | ||
117 | int "Low address space for LSM to from user allocation" | ||
118 | depends on SECURITY && SECURITY_SELINUX | ||
119 | default 65535 | ||
120 | help | ||
121 | This is the portion of low virtual memory which should be protected | ||
122 | from userspace allocation. Keeping a user from writing to low pages | ||
123 | can help reduce the impact of kernel NULL pointer bugs. | ||
124 | |||
125 | For most ia64, ppc64 and x86 users with lots of address space | ||
126 | a value of 65536 is reasonable and should cause no problems. | ||
127 | On arm and other archs it should not be higher than 32768. | ||
128 | Programs which use vm86 functionality or have some need to map | ||
129 | this low address space will need the permission specific to the | ||
130 | systems running LSM. | ||
131 | |||
116 | source security/selinux/Kconfig | 132 | source security/selinux/Kconfig |
117 | source security/smack/Kconfig | 133 | source security/smack/Kconfig |
118 | source security/tomoyo/Kconfig | 134 | source security/tomoyo/Kconfig |
diff --git a/security/Makefile b/security/Makefile index c67557cdaa85..b56e7f9ecbc2 100644 --- a/security/Makefile +++ b/security/Makefile | |||
@@ -8,7 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack | |||
8 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo | 8 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo |
9 | 9 | ||
10 | # always enable default capabilities | 10 | # always enable default capabilities |
11 | obj-y += commoncap.o | 11 | obj-y += commoncap.o min_addr.o |
12 | 12 | ||
13 | # Object file lists | 13 | # Object file lists |
14 | obj-$(CONFIG_SECURITY) += security.o capability.o | 14 | obj-$(CONFIG_SECURITY) += security.o capability.o |
diff --git a/security/commoncap.c b/security/commoncap.c index 6bcf6e81e547..e3097c0a1311 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -1005,7 +1005,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot, | |||
1005 | { | 1005 | { |
1006 | int ret = 0; | 1006 | int ret = 0; |
1007 | 1007 | ||
1008 | if (addr < mmap_min_addr) { | 1008 | if (addr < dac_mmap_min_addr) { |
1009 | ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO, | 1009 | ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO, |
1010 | SECURITY_CAP_AUDIT); | 1010 | SECURITY_CAP_AUDIT); |
1011 | /* set PF_SUPERPRIV if it turns out we allow the low mmap */ | 1011 | /* set PF_SUPERPRIV if it turns out we allow the low mmap */ |
diff --git a/security/min_addr.c b/security/min_addr.c new file mode 100644 index 000000000000..14cc7b3b8d03 --- /dev/null +++ b/security/min_addr.c | |||
@@ -0,0 +1,49 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/mm.h> | ||
3 | #include <linux/security.h> | ||
4 | #include <linux/sysctl.h> | ||
5 | |||
6 | /* amount of vm to protect from userspace access by both DAC and the LSM*/ | ||
7 | unsigned long mmap_min_addr; | ||
8 | /* amount of vm to protect from userspace using CAP_SYS_RAWIO (DAC) */ | ||
9 | unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR; | ||
10 | /* amount of vm to protect from userspace using the LSM = CONFIG_LSM_MMAP_MIN_ADDR */ | ||
11 | |||
12 | /* | ||
13 | * Update mmap_min_addr = max(dac_mmap_min_addr, CONFIG_LSM_MMAP_MIN_ADDR) | ||
14 | */ | ||
15 | static void update_mmap_min_addr(void) | ||
16 | { | ||
17 | #ifdef CONFIG_LSM_MMAP_MIN_ADDR | ||
18 | if (dac_mmap_min_addr > CONFIG_LSM_MMAP_MIN_ADDR) | ||
19 | mmap_min_addr = dac_mmap_min_addr; | ||
20 | else | ||
21 | mmap_min_addr = CONFIG_LSM_MMAP_MIN_ADDR; | ||
22 | #else | ||
23 | mmap_min_addr = dac_mmap_min_addr; | ||
24 | #endif | ||
25 | } | ||
26 | |||
27 | /* | ||
28 | * sysctl handler which just sets dac_mmap_min_addr = the new value and then | ||
29 | * calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly | ||
30 | */ | ||
31 | int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp, | ||
32 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
33 | { | ||
34 | int ret; | ||
35 | |||
36 | ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); | ||
37 | |||
38 | update_mmap_min_addr(); | ||
39 | |||
40 | return ret; | ||
41 | } | ||
42 | |||
43 | int __init init_mmap_min_addr(void) | ||
44 | { | ||
45 | update_mmap_min_addr(); | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | pure_initcall(init_mmap_min_addr); | ||
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e6d1432b0800..8d8b69c5664e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3036,7 +3036,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot, | |||
3036 | * at bad behaviour/exploit that we always want to get the AVC, even | 3036 | * at bad behaviour/exploit that we always want to get the AVC, even |
3037 | * if DAC would have also denied the operation. | 3037 | * if DAC would have also denied the operation. |
3038 | */ | 3038 | */ |
3039 | if (addr < mmap_min_addr) { | 3039 | if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { |
3040 | rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, | 3040 | rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, |
3041 | MEMPROTECT__MMAP_ZERO, NULL); | 3041 | MEMPROTECT__MMAP_ZERO, NULL); |
3042 | if (rc) | 3042 | if (rc) |