diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-12-05 12:26:52 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-12-05 12:26:52 -0500 |
commit | ad658cec232771b11e95bb5f0d639d48f898a1f2 (patch) | |
tree | 7ef4ed87cbba8d8395f67c21af5c167d5de0293a | |
parent | 2a1292b36ba106b9b7f030d3fa130f5f634fd8f0 (diff) | |
parent | 5a211a5deabcafdc764817d5b4510c767d317ddc (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6:
VM/Security: add security hook to do_brk
Security: round mmap hint address above mmap_min_addr
security: protect from stack expantion into low vm addresses
Security: allow capable check to permit mmap or low vm space
SELinux: detect dead booleans
SELinux: do not clear f_op when removing entries
-rw-r--r-- | include/linux/mm.h | 16 | ||||
-rw-r--r-- | mm/mmap.c | 11 | ||||
-rw-r--r-- | mm/nommu.c | 3 | ||||
-rw-r--r-- | security/dummy.c | 2 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 65 |
5 files changed, 57 insertions, 40 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index 520238cbae5..1b7b95c67ac 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/prio_tree.h> | 12 | #include <linux/prio_tree.h> |
13 | #include <linux/debug_locks.h> | 13 | #include <linux/debug_locks.h> |
14 | #include <linux/mm_types.h> | 14 | #include <linux/mm_types.h> |
15 | #include <linux/security.h> | ||
15 | 16 | ||
16 | struct mempolicy; | 17 | struct mempolicy; |
17 | struct anon_vma; | 18 | struct anon_vma; |
@@ -513,6 +514,21 @@ static inline void set_page_links(struct page *page, enum zone_type zone, | |||
513 | } | 514 | } |
514 | 515 | ||
515 | /* | 516 | /* |
517 | * If a hint addr is less than mmap_min_addr change hint to be as | ||
518 | * low as possible but still greater than mmap_min_addr | ||
519 | */ | ||
520 | static inline unsigned long round_hint_to_min(unsigned long hint) | ||
521 | { | ||
522 | #ifdef CONFIG_SECURITY | ||
523 | hint &= PAGE_MASK; | ||
524 | if (((void *)hint != NULL) && | ||
525 | (hint < mmap_min_addr)) | ||
526 | return PAGE_ALIGN(mmap_min_addr); | ||
527 | #endif | ||
528 | return hint; | ||
529 | } | ||
530 | |||
531 | /* | ||
516 | * Some inline functions in vmstat.h depend on page_zone() | 532 | * Some inline functions in vmstat.h depend on page_zone() |
517 | */ | 533 | */ |
518 | #include <linux/vmstat.h> | 534 | #include <linux/vmstat.h> |
@@ -912,6 +912,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, | |||
912 | if (!len) | 912 | if (!len) |
913 | return -EINVAL; | 913 | return -EINVAL; |
914 | 914 | ||
915 | if (!(flags & MAP_FIXED)) | ||
916 | addr = round_hint_to_min(addr); | ||
917 | |||
915 | error = arch_mmap_check(addr, len, flags); | 918 | error = arch_mmap_check(addr, len, flags); |
916 | if (error) | 919 | if (error) |
917 | return error; | 920 | return error; |
@@ -1615,6 +1618,12 @@ static inline int expand_downwards(struct vm_area_struct *vma, | |||
1615 | */ | 1618 | */ |
1616 | if (unlikely(anon_vma_prepare(vma))) | 1619 | if (unlikely(anon_vma_prepare(vma))) |
1617 | return -ENOMEM; | 1620 | return -ENOMEM; |
1621 | |||
1622 | address &= PAGE_MASK; | ||
1623 | error = security_file_mmap(0, 0, 0, 0, address, 1); | ||
1624 | if (error) | ||
1625 | return error; | ||
1626 | |||
1618 | anon_vma_lock(vma); | 1627 | anon_vma_lock(vma); |
1619 | 1628 | ||
1620 | /* | 1629 | /* |
@@ -1622,8 +1631,6 @@ static inline int expand_downwards(struct vm_area_struct *vma, | |||
1622 | * is required to hold the mmap_sem in read mode. We need the | 1631 | * is required to hold the mmap_sem in read mode. We need the |
1623 | * anon_vma lock to serialize against concurrent expand_stacks. | 1632 | * anon_vma lock to serialize against concurrent expand_stacks. |
1624 | */ | 1633 | */ |
1625 | address &= PAGE_MASK; | ||
1626 | error = 0; | ||
1627 | 1634 | ||
1628 | /* Somebody else might have raced and expanded it already */ | 1635 | /* Somebody else might have raced and expanded it already */ |
1629 | if (address < vma->vm_start) { | 1636 | if (address < vma->vm_start) { |
diff --git a/mm/nommu.c b/mm/nommu.c index 35622c59092..b989cb928a7 100644 --- a/mm/nommu.c +++ b/mm/nommu.c | |||
@@ -829,6 +829,9 @@ unsigned long do_mmap_pgoff(struct file *file, | |||
829 | void *result; | 829 | void *result; |
830 | int ret; | 830 | int ret; |
831 | 831 | ||
832 | if (!(flags & MAP_FIXED)) | ||
833 | addr = round_hint_to_min(addr); | ||
834 | |||
832 | /* decide whether we should attempt the mapping, and if so what sort of | 835 | /* decide whether we should attempt the mapping, and if so what sort of |
833 | * mapping */ | 836 | * mapping */ |
834 | ret = validate_mmap_request(file, addr, len, prot, flags, pgoff, | 837 | ret = validate_mmap_request(file, addr, len, prot, flags, pgoff, |
diff --git a/security/dummy.c b/security/dummy.c index 6d895ade73d..3ccfbbe973b 100644 --- a/security/dummy.c +++ b/security/dummy.c | |||
@@ -426,7 +426,7 @@ static int dummy_file_mmap (struct file *file, unsigned long reqprot, | |||
426 | unsigned long addr, | 426 | unsigned long addr, |
427 | unsigned long addr_only) | 427 | unsigned long addr_only) |
428 | { | 428 | { |
429 | if (addr < mmap_min_addr) | 429 | if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) |
430 | return -EACCES; | 430 | return -EACCES; |
431 | return 0; | 431 | return 0; |
432 | } | 432 | } |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index f5f3e6da5da..2fa483f2611 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
@@ -65,6 +65,7 @@ static DEFINE_MUTEX(sel_mutex); | |||
65 | /* global data for booleans */ | 65 | /* global data for booleans */ |
66 | static struct dentry *bool_dir = NULL; | 66 | static struct dentry *bool_dir = NULL; |
67 | static int bool_num = 0; | 67 | static int bool_num = 0; |
68 | static char **bool_pending_names; | ||
68 | static int *bool_pending_values = NULL; | 69 | static int *bool_pending_values = NULL; |
69 | 70 | ||
70 | /* global data for classes */ | 71 | /* global data for classes */ |
@@ -832,15 +833,16 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, | |||
832 | ssize_t length; | 833 | ssize_t length; |
833 | ssize_t ret; | 834 | ssize_t ret; |
834 | int cur_enforcing; | 835 | int cur_enforcing; |
835 | struct inode *inode; | 836 | struct inode *inode = filep->f_path.dentry->d_inode; |
837 | unsigned index = inode->i_ino & SEL_INO_MASK; | ||
838 | const char *name = filep->f_path.dentry->d_name.name; | ||
836 | 839 | ||
837 | mutex_lock(&sel_mutex); | 840 | mutex_lock(&sel_mutex); |
838 | 841 | ||
839 | ret = -EFAULT; | 842 | if (index >= bool_num || strcmp(name, bool_pending_names[index])) { |
840 | 843 | ret = -EINVAL; | |
841 | /* check to see if this file has been deleted */ | ||
842 | if (!filep->f_op) | ||
843 | goto out; | 844 | goto out; |
845 | } | ||
844 | 846 | ||
845 | if (count > PAGE_SIZE) { | 847 | if (count > PAGE_SIZE) { |
846 | ret = -EINVAL; | 848 | ret = -EINVAL; |
@@ -851,15 +853,13 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, | |||
851 | goto out; | 853 | goto out; |
852 | } | 854 | } |
853 | 855 | ||
854 | inode = filep->f_path.dentry->d_inode; | 856 | cur_enforcing = security_get_bool_value(index); |
855 | cur_enforcing = security_get_bool_value(inode->i_ino&SEL_INO_MASK); | ||
856 | if (cur_enforcing < 0) { | 857 | if (cur_enforcing < 0) { |
857 | ret = cur_enforcing; | 858 | ret = cur_enforcing; |
858 | goto out; | 859 | goto out; |
859 | } | 860 | } |
860 | |||
861 | length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, | 861 | length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, |
862 | bool_pending_values[inode->i_ino&SEL_INO_MASK]); | 862 | bool_pending_values[index]); |
863 | ret = simple_read_from_buffer(buf, count, ppos, page, length); | 863 | ret = simple_read_from_buffer(buf, count, ppos, page, length); |
864 | out: | 864 | out: |
865 | mutex_unlock(&sel_mutex); | 865 | mutex_unlock(&sel_mutex); |
@@ -872,9 +872,11 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | |||
872 | size_t count, loff_t *ppos) | 872 | size_t count, loff_t *ppos) |
873 | { | 873 | { |
874 | char *page = NULL; | 874 | char *page = NULL; |
875 | ssize_t length = -EFAULT; | 875 | ssize_t length; |
876 | int new_value; | 876 | int new_value; |
877 | struct inode *inode; | 877 | struct inode *inode = filep->f_path.dentry->d_inode; |
878 | unsigned index = inode->i_ino & SEL_INO_MASK; | ||
879 | const char *name = filep->f_path.dentry->d_name.name; | ||
878 | 880 | ||
879 | mutex_lock(&sel_mutex); | 881 | mutex_lock(&sel_mutex); |
880 | 882 | ||
@@ -882,16 +884,19 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | |||
882 | if (length) | 884 | if (length) |
883 | goto out; | 885 | goto out; |
884 | 886 | ||
885 | /* check to see if this file has been deleted */ | 887 | if (index >= bool_num || strcmp(name, bool_pending_names[index])) { |
886 | if (!filep->f_op) | 888 | length = -EINVAL; |
887 | goto out; | 889 | goto out; |
890 | } | ||
888 | 891 | ||
889 | if (count >= PAGE_SIZE) { | 892 | if (count >= PAGE_SIZE) { |
890 | length = -ENOMEM; | 893 | length = -ENOMEM; |
891 | goto out; | 894 | goto out; |
892 | } | 895 | } |
896 | |||
893 | if (*ppos != 0) { | 897 | if (*ppos != 0) { |
894 | /* No partial writes. */ | 898 | /* No partial writes. */ |
899 | length = -EINVAL; | ||
895 | goto out; | 900 | goto out; |
896 | } | 901 | } |
897 | page = (char*)get_zeroed_page(GFP_KERNEL); | 902 | page = (char*)get_zeroed_page(GFP_KERNEL); |
@@ -900,6 +905,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | |||
900 | goto out; | 905 | goto out; |
901 | } | 906 | } |
902 | 907 | ||
908 | length = -EFAULT; | ||
903 | if (copy_from_user(page, buf, count)) | 909 | if (copy_from_user(page, buf, count)) |
904 | goto out; | 910 | goto out; |
905 | 911 | ||
@@ -910,8 +916,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | |||
910 | if (new_value) | 916 | if (new_value) |
911 | new_value = 1; | 917 | new_value = 1; |
912 | 918 | ||
913 | inode = filep->f_path.dentry->d_inode; | 919 | bool_pending_values[index] = new_value; |
914 | bool_pending_values[inode->i_ino&SEL_INO_MASK] = new_value; | ||
915 | length = count; | 920 | length = count; |
916 | 921 | ||
917 | out: | 922 | out: |
@@ -931,7 +936,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, | |||
931 | size_t count, loff_t *ppos) | 936 | size_t count, loff_t *ppos) |
932 | { | 937 | { |
933 | char *page = NULL; | 938 | char *page = NULL; |
934 | ssize_t length = -EFAULT; | 939 | ssize_t length; |
935 | int new_value; | 940 | int new_value; |
936 | 941 | ||
937 | mutex_lock(&sel_mutex); | 942 | mutex_lock(&sel_mutex); |
@@ -940,10 +945,6 @@ static ssize_t sel_commit_bools_write(struct file *filep, | |||
940 | if (length) | 945 | if (length) |
941 | goto out; | 946 | goto out; |
942 | 947 | ||
943 | /* check to see if this file has been deleted */ | ||
944 | if (!filep->f_op) | ||
945 | goto out; | ||
946 | |||
947 | if (count >= PAGE_SIZE) { | 948 | if (count >= PAGE_SIZE) { |
948 | length = -ENOMEM; | 949 | length = -ENOMEM; |
949 | goto out; | 950 | goto out; |
@@ -958,6 +959,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, | |||
958 | goto out; | 959 | goto out; |
959 | } | 960 | } |
960 | 961 | ||
962 | length = -EFAULT; | ||
961 | if (copy_from_user(page, buf, count)) | 963 | if (copy_from_user(page, buf, count)) |
962 | goto out; | 964 | goto out; |
963 | 965 | ||
@@ -982,11 +984,9 @@ static const struct file_operations sel_commit_bools_ops = { | |||
982 | .write = sel_commit_bools_write, | 984 | .write = sel_commit_bools_write, |
983 | }; | 985 | }; |
984 | 986 | ||
985 | /* partial revoke() from fs/proc/generic.c proc_kill_inodes */ | ||
986 | static void sel_remove_entries(struct dentry *de) | 987 | static void sel_remove_entries(struct dentry *de) |
987 | { | 988 | { |
988 | struct list_head *p, *node; | 989 | struct list_head *node; |
989 | struct super_block *sb = de->d_sb; | ||
990 | 990 | ||
991 | spin_lock(&dcache_lock); | 991 | spin_lock(&dcache_lock); |
992 | node = de->d_subdirs.next; | 992 | node = de->d_subdirs.next; |
@@ -1006,18 +1006,6 @@ static void sel_remove_entries(struct dentry *de) | |||
1006 | } | 1006 | } |
1007 | 1007 | ||
1008 | spin_unlock(&dcache_lock); | 1008 | spin_unlock(&dcache_lock); |
1009 | |||
1010 | file_list_lock(); | ||
1011 | list_for_each(p, &sb->s_files) { | ||
1012 | struct file * filp = list_entry(p, struct file, f_u.fu_list); | ||
1013 | struct dentry * dentry = filp->f_path.dentry; | ||
1014 | |||
1015 | if (dentry->d_parent != de) { | ||
1016 | continue; | ||
1017 | } | ||
1018 | filp->f_op = NULL; | ||
1019 | } | ||
1020 | file_list_unlock(); | ||
1021 | } | 1009 | } |
1022 | 1010 | ||
1023 | #define BOOL_DIR_NAME "booleans" | 1011 | #define BOOL_DIR_NAME "booleans" |
@@ -1036,7 +1024,9 @@ static int sel_make_bools(void) | |||
1036 | u32 sid; | 1024 | u32 sid; |
1037 | 1025 | ||
1038 | /* remove any existing files */ | 1026 | /* remove any existing files */ |
1027 | kfree(bool_pending_names); | ||
1039 | kfree(bool_pending_values); | 1028 | kfree(bool_pending_values); |
1029 | bool_pending_names = NULL; | ||
1040 | bool_pending_values = NULL; | 1030 | bool_pending_values = NULL; |
1041 | 1031 | ||
1042 | sel_remove_entries(dir); | 1032 | sel_remove_entries(dir); |
@@ -1078,16 +1068,17 @@ static int sel_make_bools(void) | |||
1078 | d_add(dentry, inode); | 1068 | d_add(dentry, inode); |
1079 | } | 1069 | } |
1080 | bool_num = num; | 1070 | bool_num = num; |
1071 | bool_pending_names = names; | ||
1081 | bool_pending_values = values; | 1072 | bool_pending_values = values; |
1082 | out: | 1073 | out: |
1083 | free_page((unsigned long)page); | 1074 | free_page((unsigned long)page); |
1075 | return ret; | ||
1076 | err: | ||
1084 | if (names) { | 1077 | if (names) { |
1085 | for (i = 0; i < num; i++) | 1078 | for (i = 0; i < num; i++) |
1086 | kfree(names[i]); | 1079 | kfree(names[i]); |
1087 | kfree(names); | 1080 | kfree(names); |
1088 | } | 1081 | } |
1089 | return ret; | ||
1090 | err: | ||
1091 | kfree(values); | 1082 | kfree(values); |
1092 | sel_remove_entries(dir); | 1083 | sel_remove_entries(dir); |
1093 | ret = -ENOMEM; | 1084 | ret = -ENOMEM; |