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>
This commit is contained in:
Eric Paris 2009-07-31 12:54:11 -04:00 committed by James Morris
parent 84336d1a77
commit a2551df7ec
11 changed files with 92 additions and 30 deletions

View File

@ -34,8 +34,6 @@ extern int sysctl_legacy_va_layout;
#define sysctl_legacy_va_layout 0 #define sysctl_legacy_va_layout 0
#endif #endif
extern unsigned long mmap_min_addr;
#include <asm/page.h> #include <asm/page.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/processor.h> #include <asm/processor.h>
@ -574,19 +572,6 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
set_page_section(page, pfn_to_section_nr(pfn)); set_page_section(page, pfn_to_section_nr(pfn));
} }
/*
* If a hint addr is less than mmap_min_addr change hint to be as
* low as possible but still greater than mmap_min_addr
*/
static inline unsigned long round_hint_to_min(unsigned long hint)
{
hint &= PAGE_MASK;
if (((void *)hint != NULL) &&
(hint < mmap_min_addr))
return PAGE_ALIGN(mmap_min_addr);
return hint;
}
/* /*
* Some inline functions in vmstat.h depend on page_zone() * Some inline functions in vmstat.h depend on page_zone()
*/ */

View File

@ -28,6 +28,7 @@
#include <linux/resource.h> #include <linux/resource.h>
#include <linux/sem.h> #include <linux/sem.h>
#include <linux/shm.h> #include <linux/shm.h>
#include <linux/mm.h> /* PAGE_ALIGN */
#include <linux/msg.h> #include <linux/msg.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/key.h> #include <linux/key.h>
@ -95,6 +96,7 @@ extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
extern int cap_netlink_recv(struct sk_buff *skb, int cap); extern int cap_netlink_recv(struct sk_buff *skb, int cap);
extern unsigned long mmap_min_addr; extern unsigned long mmap_min_addr;
extern unsigned long dac_mmap_min_addr;
/* /*
* Values used in the task_security_ops calls * Values used in the task_security_ops calls
*/ */
@ -147,6 +149,21 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
opts->num_mnt_opts = 0; opts->num_mnt_opts = 0;
} }
/*
* If a hint addr is less than mmap_min_addr change hint to be as
* low as possible but still greater than mmap_min_addr
*/
static inline unsigned long round_hint_to_min(unsigned long hint)
{
hint &= PAGE_MASK;
if (((void *)hint != NULL) &&
(hint < mmap_min_addr))
return PAGE_ALIGN(mmap_min_addr);
return hint;
}
extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos);
/** /**
* struct security_operations - main security structure * struct security_operations - main security structure
* *

View File

@ -49,6 +49,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/security.h>
#include <linux/slow-work.h> #include <linux/slow-work.h>
#include <linux/perf_counter.h> #include <linux/perf_counter.h>
@ -1306,10 +1307,10 @@ static struct ctl_table vm_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "mmap_min_addr", .procname = "mmap_min_addr",
.data = &mmap_min_addr, .data = &dac_mmap_min_addr,
.maxlen = sizeof(unsigned long), .maxlen = sizeof(unsigned long),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_doulongvec_minmax, .proc_handler = &mmap_min_addr_handler,
}, },
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
{ {

View File

@ -225,9 +225,9 @@ config DEFAULT_MMAP_MIN_ADDR
For most ia64, ppc64 and x86 users with lots of address space For most ia64, ppc64 and x86 users with lots of address space
a value of 65536 is reasonable and should cause no problems. a value of 65536 is reasonable and should cause no problems.
On arm and other archs it should not be higher than 32768. On arm and other archs it should not be higher than 32768.
Programs which use vm86 functionality would either need additional Programs which use vm86 functionality or have some need to map
permissions from either the LSM or the capabilities module or have this low address space will need CAP_SYS_RAWIO or disable this
this protection disabled. protection by setting the value to 0.
This value can be changed after boot using the This value can be changed after boot using the
/proc/sys/vm/mmap_min_addr tunable. /proc/sys/vm/mmap_min_addr tunable.

View File

@ -88,9 +88,6 @@ int sysctl_overcommit_ratio = 50; /* default is 50% */
int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT; int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
struct percpu_counter vm_committed_as; struct percpu_counter vm_committed_as;
/* amount of vm to protect from userspace access */
unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
/* /*
* Check that a process has enough memory to allocate a new virtual * Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to * mapping. 0 means there is enough memory for the allocation to

View File

@ -69,9 +69,6 @@ int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT;
int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS; int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS;
int heap_stack_gap = 0; int heap_stack_gap = 0;
/* amount of vm to protect from userspace access */
unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
atomic_long_t mmap_pages_allocated; atomic_long_t mmap_pages_allocated;
EXPORT_SYMBOL(mem_map); EXPORT_SYMBOL(mem_map);

View File

@ -113,6 +113,22 @@ config SECURITY_ROOTPLUG
If you are unsure how to answer this question, answer N. If you are unsure how to answer this question, answer N.
config LSM_MMAP_MIN_ADDR
int "Low address space for LSM to from user allocation"
depends on SECURITY && SECURITY_SELINUX
default 65535
help
This is the portion of low virtual memory which should be protected
from userspace allocation. Keeping a user from writing to low pages
can help reduce the impact of kernel NULL pointer bugs.
For most ia64, ppc64 and x86 users with lots of address space
a value of 65536 is reasonable and should cause no problems.
On arm and other archs it should not be higher than 32768.
Programs which use vm86 functionality or have some need to map
this low address space will need the permission specific to the
systems running LSM.
source security/selinux/Kconfig source security/selinux/Kconfig
source security/smack/Kconfig source security/smack/Kconfig
source security/tomoyo/Kconfig source security/tomoyo/Kconfig

View File

@ -8,7 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
# always enable default capabilities # always enable default capabilities
obj-y += commoncap.o obj-y += commoncap.o min_addr.o
# Object file lists # Object file lists
obj-$(CONFIG_SECURITY) += security.o capability.o obj-$(CONFIG_SECURITY) += security.o capability.o

View File

@ -1005,7 +1005,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,
{ {
int ret = 0; int ret = 0;
if (addr < mmap_min_addr) { if (addr < dac_mmap_min_addr) {
ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO, ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO,
SECURITY_CAP_AUDIT); SECURITY_CAP_AUDIT);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */ /* set PF_SUPERPRIV if it turns out we allow the low mmap */

49
security/min_addr.c Normal file
View File

@ -0,0 +1,49 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/security.h>
#include <linux/sysctl.h>
/* amount of vm to protect from userspace access by both DAC and the LSM*/
unsigned long mmap_min_addr;
/* amount of vm to protect from userspace using CAP_SYS_RAWIO (DAC) */
unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
/* amount of vm to protect from userspace using the LSM = CONFIG_LSM_MMAP_MIN_ADDR */
/*
* Update mmap_min_addr = max(dac_mmap_min_addr, CONFIG_LSM_MMAP_MIN_ADDR)
*/
static void update_mmap_min_addr(void)
{
#ifdef CONFIG_LSM_MMAP_MIN_ADDR
if (dac_mmap_min_addr > CONFIG_LSM_MMAP_MIN_ADDR)
mmap_min_addr = dac_mmap_min_addr;
else
mmap_min_addr = CONFIG_LSM_MMAP_MIN_ADDR;
#else
mmap_min_addr = dac_mmap_min_addr;
#endif
}
/*
* sysctl handler which just sets dac_mmap_min_addr = the new value and then
* calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
*/
int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
update_mmap_min_addr();
return ret;
}
int __init init_mmap_min_addr(void)
{
update_mmap_min_addr();
return 0;
}
pure_initcall(init_mmap_min_addr);

View File

@ -3040,7 +3040,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
* at bad behaviour/exploit that we always want to get the AVC, even * at bad behaviour/exploit that we always want to get the AVC, even
* if DAC would have also denied the operation. * if DAC would have also denied the operation.
*/ */
if (addr < mmap_min_addr) { if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
MEMPROTECT__MMAP_ZERO, NULL); MEMPROTECT__MMAP_ZERO, NULL);
if (rc) if (rc)