diff options
| -rw-r--r-- | Documentation/sysctl/vm.txt | 15 | ||||
| -rw-r--r-- | include/linux/security.h | 17 | ||||
| -rw-r--r-- | kernel/sysctl.c | 10 | ||||
| -rw-r--r-- | mm/mmap.c | 4 | ||||
| -rw-r--r-- | mm/mremap.c | 13 | ||||
| -rw-r--r-- | mm/nommu.c | 2 | ||||
| -rw-r--r-- | security/dummy.c | 6 | ||||
| -rw-r--r-- | security/security.c | 1 | ||||
| -rw-r--r-- | security/selinux/avc.c | 12 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 42 | ||||
| -rw-r--r-- | security/selinux/include/av_perm_to_string.h | 1 | ||||
| -rw-r--r-- | security/selinux/include/av_permissions.h | 1 | ||||
| -rw-r--r-- | security/selinux/include/avc.h | 6 | ||||
| -rw-r--r-- | security/selinux/include/class_to_string.h | 1 | ||||
| -rw-r--r-- | security/selinux/include/flask.h | 1 | ||||
| -rw-r--r-- | security/selinux/include/security.h | 4 | ||||
| -rw-r--r-- | security/selinux/netlabel.c | 34 | ||||
| -rw-r--r-- | security/selinux/selinuxfs.c | 269 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 7 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 144 |
20 files changed, 504 insertions, 86 deletions
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 1d192565e182..8cfca173d4bc 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt | |||
| @@ -31,6 +31,7 @@ Currently, these files are in /proc/sys/vm: | |||
| 31 | - min_unmapped_ratio | 31 | - min_unmapped_ratio |
| 32 | - min_slab_ratio | 32 | - min_slab_ratio |
| 33 | - panic_on_oom | 33 | - panic_on_oom |
| 34 | - mmap_min_address | ||
| 34 | 35 | ||
| 35 | ============================================================== | 36 | ============================================================== |
| 36 | 37 | ||
| @@ -216,3 +217,17 @@ above-mentioned. | |||
| 216 | The default value is 0. | 217 | The default value is 0. |
| 217 | 1 and 2 are for failover of clustering. Please select either | 218 | 1 and 2 are for failover of clustering. Please select either |
| 218 | according to your policy of failover. | 219 | according to your policy of failover. |
| 220 | |||
| 221 | ============================================================== | ||
| 222 | |||
| 223 | mmap_min_addr | ||
| 224 | |||
| 225 | This file indicates the amount of address space which a user process will | ||
| 226 | be restricted from mmaping. Since kernel null dereference bugs could | ||
| 227 | accidentally operate based on the information in the first couple of pages | ||
| 228 | of memory userspace processes should not be allowed to write to them. By | ||
| 229 | default this value is set to 0 and no protections will be enforced by the | ||
| 230 | security module. Setting this value to something like 64k will allow the | ||
| 231 | vast majority of applications to work correctly and provide defense in depth | ||
| 232 | against future potential kernel bugs. | ||
| 233 | |||
diff --git a/include/linux/security.h b/include/linux/security.h index 9eb9e0fe0331..c11dc8aa0351 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
| @@ -71,6 +71,7 @@ struct xfrm_user_sec_ctx; | |||
| 71 | extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); | 71 | extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); |
| 72 | extern int cap_netlink_recv(struct sk_buff *skb, int cap); | 72 | extern int cap_netlink_recv(struct sk_buff *skb, int cap); |
| 73 | 73 | ||
| 74 | extern unsigned long mmap_min_addr; | ||
| 74 | /* | 75 | /* |
| 75 | * Values used in the task_security_ops calls | 76 | * Values used in the task_security_ops calls |
| 76 | */ | 77 | */ |
| @@ -1241,8 +1242,9 @@ struct security_operations { | |||
| 1241 | int (*file_ioctl) (struct file * file, unsigned int cmd, | 1242 | int (*file_ioctl) (struct file * file, unsigned int cmd, |
| 1242 | unsigned long arg); | 1243 | unsigned long arg); |
| 1243 | int (*file_mmap) (struct file * file, | 1244 | int (*file_mmap) (struct file * file, |
| 1244 | unsigned long reqprot, | 1245 | unsigned long reqprot, unsigned long prot, |
| 1245 | unsigned long prot, unsigned long flags); | 1246 | unsigned long flags, unsigned long addr, |
| 1247 | unsigned long addr_only); | ||
| 1246 | int (*file_mprotect) (struct vm_area_struct * vma, | 1248 | int (*file_mprotect) (struct vm_area_struct * vma, |
| 1247 | unsigned long reqprot, | 1249 | unsigned long reqprot, |
| 1248 | unsigned long prot); | 1250 | unsigned long prot); |
| @@ -1814,9 +1816,12 @@ static inline int security_file_ioctl (struct file *file, unsigned int cmd, | |||
| 1814 | 1816 | ||
| 1815 | static inline int security_file_mmap (struct file *file, unsigned long reqprot, | 1817 | static inline int security_file_mmap (struct file *file, unsigned long reqprot, |
| 1816 | unsigned long prot, | 1818 | unsigned long prot, |
| 1817 | unsigned long flags) | 1819 | unsigned long flags, |
| 1820 | unsigned long addr, | ||
| 1821 | unsigned long addr_only) | ||
| 1818 | { | 1822 | { |
| 1819 | return security_ops->file_mmap (file, reqprot, prot, flags); | 1823 | return security_ops->file_mmap (file, reqprot, prot, flags, addr, |
| 1824 | addr_only); | ||
| 1820 | } | 1825 | } |
| 1821 | 1826 | ||
| 1822 | static inline int security_file_mprotect (struct vm_area_struct *vma, | 1827 | static inline int security_file_mprotect (struct vm_area_struct *vma, |
| @@ -2489,7 +2494,9 @@ static inline int security_file_ioctl (struct file *file, unsigned int cmd, | |||
| 2489 | 2494 | ||
| 2490 | static inline int security_file_mmap (struct file *file, unsigned long reqprot, | 2495 | static inline int security_file_mmap (struct file *file, unsigned long reqprot, |
| 2491 | unsigned long prot, | 2496 | unsigned long prot, |
| 2492 | unsigned long flags) | 2497 | unsigned long flags, |
| 2498 | unsigned long addr, | ||
| 2499 | unsigned long addr_only) | ||
| 2493 | { | 2500 | { |
| 2494 | return 0; | 2501 | return 0; |
| 2495 | } | 2502 | } |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 51f5dac42a00..d93e13d93f24 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -949,6 +949,16 @@ static ctl_table vm_table[] = { | |||
| 949 | .strategy = &sysctl_jiffies, | 949 | .strategy = &sysctl_jiffies, |
| 950 | }, | 950 | }, |
| 951 | #endif | 951 | #endif |
| 952 | #ifdef CONFIG_SECURITY | ||
| 953 | { | ||
| 954 | .ctl_name = CTL_UNNUMBERED, | ||
| 955 | .procname = "mmap_min_addr", | ||
| 956 | .data = &mmap_min_addr, | ||
| 957 | .maxlen = sizeof(unsigned long), | ||
| 958 | .mode = 0644, | ||
| 959 | .proc_handler = &proc_doulongvec_minmax, | ||
| 960 | }, | ||
| 961 | #endif | ||
| 952 | #if defined(CONFIG_X86_32) || \ | 962 | #if defined(CONFIG_X86_32) || \ |
| 953 | (defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL)) | 963 | (defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL)) |
| 954 | { | 964 | { |
| @@ -1023,10 +1023,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, | |||
| 1023 | } | 1023 | } |
| 1024 | } | 1024 | } |
| 1025 | 1025 | ||
| 1026 | error = security_file_mmap(file, reqprot, prot, flags); | 1026 | error = security_file_mmap(file, reqprot, prot, flags, addr, 0); |
| 1027 | if (error) | 1027 | if (error) |
| 1028 | return error; | 1028 | return error; |
| 1029 | 1029 | ||
| 1030 | /* Clear old maps */ | 1030 | /* Clear old maps */ |
| 1031 | error = -ENOMEM; | 1031 | error = -ENOMEM; |
| 1032 | munmap_back: | 1032 | munmap_back: |
diff --git a/mm/mremap.c b/mm/mremap.c index 5d4bd4f95b8e..bc7c52efc71b 100644 --- a/mm/mremap.c +++ b/mm/mremap.c | |||
| @@ -291,6 +291,10 @@ unsigned long do_mremap(unsigned long addr, | |||
| 291 | if ((addr <= new_addr) && (addr+old_len) > new_addr) | 291 | if ((addr <= new_addr) && (addr+old_len) > new_addr) |
| 292 | goto out; | 292 | goto out; |
| 293 | 293 | ||
| 294 | ret = security_file_mmap(0, 0, 0, 0, new_addr, 1); | ||
| 295 | if (ret) | ||
| 296 | goto out; | ||
| 297 | |||
| 294 | ret = do_munmap(mm, new_addr, new_len); | 298 | ret = do_munmap(mm, new_addr, new_len); |
| 295 | if (ret) | 299 | if (ret) |
| 296 | goto out; | 300 | goto out; |
| @@ -390,8 +394,13 @@ unsigned long do_mremap(unsigned long addr, | |||
| 390 | 394 | ||
| 391 | new_addr = get_unmapped_area(vma->vm_file, 0, new_len, | 395 | new_addr = get_unmapped_area(vma->vm_file, 0, new_len, |
| 392 | vma->vm_pgoff, map_flags); | 396 | vma->vm_pgoff, map_flags); |
| 393 | ret = new_addr; | 397 | if (new_addr & ~PAGE_MASK) { |
| 394 | if (new_addr & ~PAGE_MASK) | 398 | ret = new_addr; |
| 399 | goto out; | ||
| 400 | } | ||
| 401 | |||
| 402 | ret = security_file_mmap(0, 0, 0, 0, new_addr, 1); | ||
| 403 | if (ret) | ||
| 395 | goto out; | 404 | goto out; |
| 396 | } | 405 | } |
| 397 | ret = move_vma(vma, addr, old_len, new_len, new_addr); | 406 | ret = move_vma(vma, addr, old_len, new_len, new_addr); |
diff --git a/mm/nommu.c b/mm/nommu.c index 2b16b00a5b11..989e2e9af5c3 100644 --- a/mm/nommu.c +++ b/mm/nommu.c | |||
| @@ -639,7 +639,7 @@ static int validate_mmap_request(struct file *file, | |||
| 639 | } | 639 | } |
| 640 | 640 | ||
| 641 | /* allow the security API to have its say */ | 641 | /* allow the security API to have its say */ |
| 642 | ret = security_file_mmap(file, reqprot, prot, flags); | 642 | ret = security_file_mmap(file, reqprot, prot, flags, addr, 0); |
| 643 | if (ret < 0) | 643 | if (ret < 0) |
| 644 | return ret; | 644 | return ret; |
| 645 | 645 | ||
diff --git a/security/dummy.c b/security/dummy.c index 8ffd76405b5b..d6a112ce2975 100644 --- a/security/dummy.c +++ b/security/dummy.c | |||
| @@ -420,8 +420,12 @@ static int dummy_file_ioctl (struct file *file, unsigned int command, | |||
| 420 | 420 | ||
| 421 | static int dummy_file_mmap (struct file *file, unsigned long reqprot, | 421 | static int dummy_file_mmap (struct file *file, unsigned long reqprot, |
| 422 | unsigned long prot, | 422 | unsigned long prot, |
| 423 | unsigned long flags) | 423 | unsigned long flags, |
| 424 | unsigned long addr, | ||
| 425 | unsigned long addr_only) | ||
| 424 | { | 426 | { |
| 427 | if (addr < mmap_min_addr) | ||
| 428 | return -EACCES; | ||
| 425 | return 0; | 429 | return 0; |
| 426 | } | 430 | } |
| 427 | 431 | ||
diff --git a/security/security.c b/security/security.c index fc8601b2b7ac..27e5863d30f1 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -24,6 +24,7 @@ extern struct security_operations dummy_security_ops; | |||
| 24 | extern void security_fixup_ops(struct security_operations *ops); | 24 | extern void security_fixup_ops(struct security_operations *ops); |
| 25 | 25 | ||
| 26 | struct security_operations *security_ops; /* Initialized to NULL */ | 26 | struct security_operations *security_ops; /* Initialized to NULL */ |
| 27 | unsigned long mmap_min_addr; /* 0 means no protection */ | ||
| 27 | 28 | ||
| 28 | static inline int verify(struct security_operations *ops) | 29 | static inline int verify(struct security_operations *ops) |
| 29 | { | 30 | { |
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index e4396a89edc6..78c408fd2b02 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
| @@ -586,7 +586,7 @@ void avc_audit(u32 ssid, u32 tsid, | |||
| 586 | } | 586 | } |
| 587 | } | 587 | } |
| 588 | if (inode) | 588 | if (inode) |
| 589 | audit_log_format(ab, " dev=%s ino=%ld", | 589 | audit_log_format(ab, " dev=%s ino=%lu", |
| 590 | inode->i_sb->s_id, | 590 | inode->i_sb->s_id, |
| 591 | inode->i_ino); | 591 | inode->i_ino); |
| 592 | break; | 592 | break; |
| @@ -832,6 +832,7 @@ int avc_ss_reset(u32 seqno) | |||
| 832 | * @tsid: target security identifier | 832 | * @tsid: target security identifier |
| 833 | * @tclass: target security class | 833 | * @tclass: target security class |
| 834 | * @requested: requested permissions, interpreted based on @tclass | 834 | * @requested: requested permissions, interpreted based on @tclass |
| 835 | * @flags: AVC_STRICT or 0 | ||
| 835 | * @avd: access vector decisions | 836 | * @avd: access vector decisions |
| 836 | * | 837 | * |
| 837 | * Check the AVC to determine whether the @requested permissions are granted | 838 | * Check the AVC to determine whether the @requested permissions are granted |
| @@ -846,8 +847,9 @@ int avc_ss_reset(u32 seqno) | |||
| 846 | * should be released for the auditing. | 847 | * should be released for the auditing. |
| 847 | */ | 848 | */ |
| 848 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, | 849 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, |
| 849 | u16 tclass, u32 requested, | 850 | u16 tclass, u32 requested, |
| 850 | struct av_decision *avd) | 851 | unsigned flags, |
| 852 | struct av_decision *avd) | ||
| 851 | { | 853 | { |
| 852 | struct avc_node *node; | 854 | struct avc_node *node; |
| 853 | struct avc_entry entry, *p_ae; | 855 | struct avc_entry entry, *p_ae; |
| @@ -874,7 +876,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
| 874 | denied = requested & ~(p_ae->avd.allowed); | 876 | denied = requested & ~(p_ae->avd.allowed); |
| 875 | 877 | ||
| 876 | if (!requested || denied) { | 878 | if (!requested || denied) { |
| 877 | if (selinux_enforcing) | 879 | if (selinux_enforcing || (flags & AVC_STRICT)) |
| 878 | rc = -EACCES; | 880 | rc = -EACCES; |
| 879 | else | 881 | else |
| 880 | if (node) | 882 | if (node) |
| @@ -909,7 +911,7 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, | |||
| 909 | struct av_decision avd; | 911 | struct av_decision avd; |
| 910 | int rc; | 912 | int rc; |
| 911 | 913 | ||
| 912 | rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd); | 914 | rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); |
| 913 | avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); | 915 | avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); |
| 914 | return rc; | 916 | return rc; |
| 915 | } | 917 | } |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ad8dd4e8657e..aff8f46c2aa2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -1592,9 +1592,10 @@ static int selinux_vm_enough_memory(long pages) | |||
| 1592 | rc = secondary_ops->capable(current, CAP_SYS_ADMIN); | 1592 | rc = secondary_ops->capable(current, CAP_SYS_ADMIN); |
| 1593 | if (rc == 0) | 1593 | if (rc == 0) |
| 1594 | rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, | 1594 | rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, |
| 1595 | SECCLASS_CAPABILITY, | 1595 | SECCLASS_CAPABILITY, |
| 1596 | CAP_TO_MASK(CAP_SYS_ADMIN), | 1596 | CAP_TO_MASK(CAP_SYS_ADMIN), |
| 1597 | NULL); | 1597 | 0, |
| 1598 | NULL); | ||
| 1598 | 1599 | ||
| 1599 | if (rc == 0) | 1600 | if (rc == 0) |
| 1600 | cap_sys_admin = 1; | 1601 | cap_sys_admin = 1; |
| @@ -2568,12 +2569,16 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared | |||
| 2568 | } | 2569 | } |
| 2569 | 2570 | ||
| 2570 | static int selinux_file_mmap(struct file *file, unsigned long reqprot, | 2571 | static int selinux_file_mmap(struct file *file, unsigned long reqprot, |
| 2571 | unsigned long prot, unsigned long flags) | 2572 | unsigned long prot, unsigned long flags, |
| 2573 | unsigned long addr, unsigned long addr_only) | ||
| 2572 | { | 2574 | { |
| 2573 | int rc; | 2575 | int rc = 0; |
| 2576 | u32 sid = ((struct task_security_struct*)(current->security))->sid; | ||
| 2574 | 2577 | ||
| 2575 | rc = secondary_ops->file_mmap(file, reqprot, prot, flags); | 2578 | if (addr < mmap_min_addr) |
| 2576 | if (rc) | 2579 | rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, |
| 2580 | MEMPROTECT__MMAP_ZERO, NULL); | ||
| 2581 | if (rc || addr_only) | ||
| 2577 | return rc; | 2582 | return rc; |
| 2578 | 2583 | ||
| 2579 | if (selinux_checkreqprot) | 2584 | if (selinux_checkreqprot) |
| @@ -3124,17 +3129,19 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, | |||
| 3124 | /** | 3129 | /** |
| 3125 | * selinux_skb_extlbl_sid - Determine the external label of a packet | 3130 | * selinux_skb_extlbl_sid - Determine the external label of a packet |
| 3126 | * @skb: the packet | 3131 | * @skb: the packet |
| 3127 | * @base_sid: the SELinux SID to use as a context for MLS only external labels | ||
| 3128 | * @sid: the packet's SID | 3132 | * @sid: the packet's SID |
| 3129 | * | 3133 | * |
| 3130 | * Description: | 3134 | * Description: |
| 3131 | * Check the various different forms of external packet labeling and determine | 3135 | * Check the various different forms of external packet labeling and determine |
| 3132 | * the external SID for the packet. | 3136 | * the external SID for the packet. If only one form of external labeling is |
| 3137 | * present then it is used, if both labeled IPsec and NetLabel labels are | ||
| 3138 | * present then the SELinux type information is taken from the labeled IPsec | ||
| 3139 | * SA and the MLS sensitivity label information is taken from the NetLabel | ||
| 3140 | * security attributes. This bit of "magic" is done in the call to | ||
| 3141 | * selinux_netlbl_skbuff_getsid(). | ||
| 3133 | * | 3142 | * |
| 3134 | */ | 3143 | */ |
| 3135 | static void selinux_skb_extlbl_sid(struct sk_buff *skb, | 3144 | static void selinux_skb_extlbl_sid(struct sk_buff *skb, u32 *sid) |
| 3136 | u32 base_sid, | ||
| 3137 | u32 *sid) | ||
| 3138 | { | 3145 | { |
| 3139 | u32 xfrm_sid; | 3146 | u32 xfrm_sid; |
| 3140 | u32 nlbl_sid; | 3147 | u32 nlbl_sid; |
| @@ -3142,10 +3149,9 @@ static void selinux_skb_extlbl_sid(struct sk_buff *skb, | |||
| 3142 | selinux_skb_xfrm_sid(skb, &xfrm_sid); | 3149 | selinux_skb_xfrm_sid(skb, &xfrm_sid); |
| 3143 | if (selinux_netlbl_skbuff_getsid(skb, | 3150 | if (selinux_netlbl_skbuff_getsid(skb, |
| 3144 | (xfrm_sid == SECSID_NULL ? | 3151 | (xfrm_sid == SECSID_NULL ? |
| 3145 | base_sid : xfrm_sid), | 3152 | SECINITSID_NETMSG : xfrm_sid), |
| 3146 | &nlbl_sid) != 0) | 3153 | &nlbl_sid) != 0) |
| 3147 | nlbl_sid = SECSID_NULL; | 3154 | nlbl_sid = SECSID_NULL; |
| 3148 | |||
| 3149 | *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); | 3155 | *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); |
| 3150 | } | 3156 | } |
| 3151 | 3157 | ||
| @@ -3690,7 +3696,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * | |||
| 3690 | if (sock && sock->sk->sk_family == PF_UNIX) | 3696 | if (sock && sock->sk->sk_family == PF_UNIX) |
| 3691 | selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); | 3697 | selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); |
| 3692 | else if (skb) | 3698 | else if (skb) |
| 3693 | selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peer_secid); | 3699 | selinux_skb_extlbl_sid(skb, &peer_secid); |
| 3694 | 3700 | ||
| 3695 | if (peer_secid == SECSID_NULL) | 3701 | if (peer_secid == SECSID_NULL) |
| 3696 | err = -EINVAL; | 3702 | err = -EINVAL; |
| @@ -3751,7 +3757,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
| 3751 | u32 newsid; | 3757 | u32 newsid; |
| 3752 | u32 peersid; | 3758 | u32 peersid; |
| 3753 | 3759 | ||
| 3754 | selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid); | 3760 | selinux_skb_extlbl_sid(skb, &peersid); |
| 3755 | if (peersid == SECSID_NULL) { | 3761 | if (peersid == SECSID_NULL) { |
| 3756 | req->secid = sksec->sid; | 3762 | req->secid = sksec->sid; |
| 3757 | req->peer_secid = SECSID_NULL; | 3763 | req->peer_secid = SECSID_NULL; |
| @@ -3789,7 +3795,7 @@ static void selinux_inet_conn_established(struct sock *sk, | |||
| 3789 | { | 3795 | { |
| 3790 | struct sk_security_struct *sksec = sk->sk_security; | 3796 | struct sk_security_struct *sksec = sk->sk_security; |
| 3791 | 3797 | ||
| 3792 | selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid); | 3798 | selinux_skb_extlbl_sid(skb, &sksec->peer_sid); |
| 3793 | } | 3799 | } |
| 3794 | 3800 | ||
| 3795 | static void selinux_req_classify_flow(const struct request_sock *req, | 3801 | static void selinux_req_classify_flow(const struct request_sock *req, |
| @@ -4626,7 +4632,7 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 4626 | if (p->ptrace & PT_PTRACED) { | 4632 | if (p->ptrace & PT_PTRACED) { |
| 4627 | error = avc_has_perm_noaudit(tsec->ptrace_sid, sid, | 4633 | error = avc_has_perm_noaudit(tsec->ptrace_sid, sid, |
| 4628 | SECCLASS_PROCESS, | 4634 | SECCLASS_PROCESS, |
| 4629 | PROCESS__PTRACE, &avd); | 4635 | PROCESS__PTRACE, 0, &avd); |
| 4630 | if (!error) | 4636 | if (!error) |
| 4631 | tsec->sid = sid; | 4637 | tsec->sid = sid; |
| 4632 | task_unlock(p); | 4638 | task_unlock(p); |
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index b83e74012a97..049bf69429b6 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h | |||
| @@ -158,3 +158,4 @@ | |||
| 158 | S_(SECCLASS_KEY, KEY__CREATE, "create") | 158 | S_(SECCLASS_KEY, KEY__CREATE, "create") |
| 159 | S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind") | 159 | S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind") |
| 160 | S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect") | 160 | S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect") |
| 161 | S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero") | ||
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 5fee1735bffe..eda89a2ec635 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h | |||
| @@ -823,3 +823,4 @@ | |||
| 823 | #define DCCP_SOCKET__NAME_BIND 0x00200000UL | 823 | #define DCCP_SOCKET__NAME_BIND 0x00200000UL |
| 824 | #define DCCP_SOCKET__NODE_BIND 0x00400000UL | 824 | #define DCCP_SOCKET__NODE_BIND 0x00400000UL |
| 825 | #define DCCP_SOCKET__NAME_CONNECT 0x00800000UL | 825 | #define DCCP_SOCKET__NAME_CONNECT 0x00800000UL |
| 826 | #define MEMPROTECT__MMAP_ZERO 0x00000001UL | ||
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 6ed10c3d3339..e145f6e13b0b 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h | |||
| @@ -102,9 +102,11 @@ void avc_audit(u32 ssid, u32 tsid, | |||
| 102 | u16 tclass, u32 requested, | 102 | u16 tclass, u32 requested, |
| 103 | struct av_decision *avd, int result, struct avc_audit_data *auditdata); | 103 | struct av_decision *avd, int result, struct avc_audit_data *auditdata); |
| 104 | 104 | ||
| 105 | #define AVC_STRICT 1 /* Ignore permissive mode. */ | ||
| 105 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, | 106 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, |
| 106 | u16 tclass, u32 requested, | 107 | u16 tclass, u32 requested, |
| 107 | struct av_decision *avd); | 108 | unsigned flags, |
| 109 | struct av_decision *avd); | ||
| 108 | 110 | ||
| 109 | int avc_has_perm(u32 ssid, u32 tsid, | 111 | int avc_has_perm(u32 ssid, u32 tsid, |
| 110 | u16 tclass, u32 requested, | 112 | u16 tclass, u32 requested, |
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index 378799068441..e77de0e62ea0 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h | |||
| @@ -63,3 +63,4 @@ | |||
| 63 | S_("key") | 63 | S_("key") |
| 64 | S_(NULL) | 64 | S_(NULL) |
| 65 | S_("dccp_socket") | 65 | S_("dccp_socket") |
| 66 | S_("memprotect") | ||
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index 35f309f47873..a9c2b20f14b5 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h | |||
| @@ -49,6 +49,7 @@ | |||
| 49 | #define SECCLASS_PACKET 57 | 49 | #define SECCLASS_PACKET 57 |
| 50 | #define SECCLASS_KEY 58 | 50 | #define SECCLASS_KEY 58 |
| 51 | #define SECCLASS_DCCP_SOCKET 60 | 51 | #define SECCLASS_DCCP_SOCKET 60 |
| 52 | #define SECCLASS_MEMPROTECT 61 | ||
| 52 | 53 | ||
| 53 | /* | 54 | /* |
| 54 | * Security identifier indices for initial entities | 55 | * Security identifier indices for initial entities |
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index b94378afea25..83bdd4d2a29e 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
| @@ -41,6 +41,7 @@ extern int selinux_mls_enabled; | |||
| 41 | 41 | ||
| 42 | int security_load_policy(void * data, size_t len); | 42 | int security_load_policy(void * data, size_t len); |
| 43 | 43 | ||
| 44 | #define SEL_VEC_MAX 32 | ||
| 44 | struct av_decision { | 45 | struct av_decision { |
| 45 | u32 allowed; | 46 | u32 allowed; |
| 46 | u32 decided; | 47 | u32 decided; |
| @@ -87,6 +88,9 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, | |||
| 87 | 88 | ||
| 88 | int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); | 89 | int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); |
| 89 | 90 | ||
| 91 | int security_get_classes(char ***classes, int *nclasses); | ||
| 92 | int security_get_permissions(char *class, char ***perms, int *nperms); | ||
| 93 | |||
| 90 | #define SECURITY_FS_USE_XATTR 1 /* use xattr */ | 94 | #define SECURITY_FS_USE_XATTR 1 /* use xattr */ |
| 91 | #define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */ | 95 | #define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */ |
| 92 | #define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */ | 96 | #define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */ |
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index e64eca246f1a..8192e8bc9f5a 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c | |||
| @@ -158,9 +158,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) | |||
| 158 | netlbl_secattr_init(&secattr); | 158 | netlbl_secattr_init(&secattr); |
| 159 | rc = netlbl_skbuff_getattr(skb, &secattr); | 159 | rc = netlbl_skbuff_getattr(skb, &secattr); |
| 160 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) | 160 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) |
| 161 | rc = security_netlbl_secattr_to_sid(&secattr, | 161 | rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid); |
| 162 | base_sid, | ||
| 163 | sid); | ||
| 164 | else | 162 | else |
| 165 | *sid = SECSID_NULL; | 163 | *sid = SECSID_NULL; |
| 166 | netlbl_secattr_destroy(&secattr); | 164 | netlbl_secattr_destroy(&secattr); |
| @@ -198,7 +196,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |||
| 198 | if (netlbl_sock_getattr(sk, &secattr) == 0 && | 196 | if (netlbl_sock_getattr(sk, &secattr) == 0 && |
| 199 | secattr.flags != NETLBL_SECATTR_NONE && | 197 | secattr.flags != NETLBL_SECATTR_NONE && |
| 200 | security_netlbl_secattr_to_sid(&secattr, | 198 | security_netlbl_secattr_to_sid(&secattr, |
| 201 | SECINITSID_UNLABELED, | 199 | SECINITSID_NETMSG, |
| 202 | &nlbl_peer_sid) == 0) | 200 | &nlbl_peer_sid) == 0) |
| 203 | sksec->peer_sid = nlbl_peer_sid; | 201 | sksec->peer_sid = nlbl_peer_sid; |
| 204 | netlbl_secattr_destroy(&secattr); | 202 | netlbl_secattr_destroy(&secattr); |
| @@ -295,38 +293,32 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, | |||
| 295 | struct avc_audit_data *ad) | 293 | struct avc_audit_data *ad) |
| 296 | { | 294 | { |
| 297 | int rc; | 295 | int rc; |
| 298 | u32 netlbl_sid; | 296 | u32 nlbl_sid; |
| 299 | u32 recv_perm; | 297 | u32 perm; |
| 300 | 298 | ||
| 301 | rc = selinux_netlbl_skbuff_getsid(skb, | 299 | rc = selinux_netlbl_skbuff_getsid(skb, SECINITSID_NETMSG, &nlbl_sid); |
| 302 | SECINITSID_UNLABELED, | ||
| 303 | &netlbl_sid); | ||
| 304 | if (rc != 0) | 300 | if (rc != 0) |
| 305 | return rc; | 301 | return rc; |
| 306 | 302 | if (nlbl_sid == SECSID_NULL) | |
| 307 | if (netlbl_sid == SECSID_NULL) | 303 | nlbl_sid = SECINITSID_UNLABELED; |
| 308 | return 0; | ||
| 309 | 304 | ||
| 310 | switch (sksec->sclass) { | 305 | switch (sksec->sclass) { |
| 311 | case SECCLASS_UDP_SOCKET: | 306 | case SECCLASS_UDP_SOCKET: |
| 312 | recv_perm = UDP_SOCKET__RECVFROM; | 307 | perm = UDP_SOCKET__RECVFROM; |
| 313 | break; | 308 | break; |
| 314 | case SECCLASS_TCP_SOCKET: | 309 | case SECCLASS_TCP_SOCKET: |
| 315 | recv_perm = TCP_SOCKET__RECVFROM; | 310 | perm = TCP_SOCKET__RECVFROM; |
| 316 | break; | 311 | break; |
| 317 | default: | 312 | default: |
| 318 | recv_perm = RAWIP_SOCKET__RECVFROM; | 313 | perm = RAWIP_SOCKET__RECVFROM; |
| 319 | } | 314 | } |
| 320 | 315 | ||
| 321 | rc = avc_has_perm(sksec->sid, | 316 | rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad); |
| 322 | netlbl_sid, | ||
| 323 | sksec->sclass, | ||
| 324 | recv_perm, | ||
| 325 | ad); | ||
| 326 | if (rc == 0) | 317 | if (rc == 0) |
| 327 | return 0; | 318 | return 0; |
| 328 | 319 | ||
| 329 | netlbl_skbuff_err(skb, rc); | 320 | if (nlbl_sid != SECINITSID_UNLABELED) |
| 321 | netlbl_skbuff_err(skb, rc); | ||
| 330 | return rc; | 322 | return rc; |
| 331 | } | 323 | } |
| 332 | 324 | ||
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index aca099aa2ed3..c9e92daedee2 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
| @@ -67,6 +67,10 @@ static struct dentry *bool_dir = NULL; | |||
| 67 | static int bool_num = 0; | 67 | static int bool_num = 0; |
| 68 | static int *bool_pending_values = NULL; | 68 | static int *bool_pending_values = NULL; |
| 69 | 69 | ||
| 70 | /* global data for classes */ | ||
| 71 | static struct dentry *class_dir = NULL; | ||
| 72 | static unsigned long last_class_ino; | ||
| 73 | |||
| 70 | extern void selnl_notify_setenforce(int val); | 74 | extern void selnl_notify_setenforce(int val); |
| 71 | 75 | ||
| 72 | /* Check whether a task is allowed to use a security operation. */ | 76 | /* Check whether a task is allowed to use a security operation. */ |
| @@ -106,6 +110,7 @@ static unsigned long sel_last_ino = SEL_INO_NEXT - 1; | |||
| 106 | 110 | ||
| 107 | #define SEL_INITCON_INO_OFFSET 0x01000000 | 111 | #define SEL_INITCON_INO_OFFSET 0x01000000 |
| 108 | #define SEL_BOOL_INO_OFFSET 0x02000000 | 112 | #define SEL_BOOL_INO_OFFSET 0x02000000 |
| 113 | #define SEL_CLASS_INO_OFFSET 0x04000000 | ||
| 109 | #define SEL_INO_MASK 0x00ffffff | 114 | #define SEL_INO_MASK 0x00ffffff |
| 110 | 115 | ||
| 111 | #define TMPBUFLEN 12 | 116 | #define TMPBUFLEN 12 |
| @@ -237,6 +242,11 @@ static const struct file_operations sel_policyvers_ops = { | |||
| 237 | 242 | ||
| 238 | /* declaration for sel_write_load */ | 243 | /* declaration for sel_write_load */ |
| 239 | static int sel_make_bools(void); | 244 | static int sel_make_bools(void); |
| 245 | static int sel_make_classes(void); | ||
| 246 | |||
| 247 | /* declaration for sel_make_class_dirs */ | ||
| 248 | static int sel_make_dir(struct inode *dir, struct dentry *dentry, | ||
| 249 | unsigned long *ino); | ||
| 240 | 250 | ||
| 241 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, | 251 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, |
| 242 | size_t count, loff_t *ppos) | 252 | size_t count, loff_t *ppos) |
| @@ -287,10 +297,18 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, | |||
| 287 | goto out; | 297 | goto out; |
| 288 | 298 | ||
| 289 | ret = sel_make_bools(); | 299 | ret = sel_make_bools(); |
| 300 | if (ret) { | ||
| 301 | length = ret; | ||
| 302 | goto out1; | ||
| 303 | } | ||
| 304 | |||
| 305 | ret = sel_make_classes(); | ||
| 290 | if (ret) | 306 | if (ret) |
| 291 | length = ret; | 307 | length = ret; |
| 292 | else | 308 | else |
| 293 | length = count; | 309 | length = count; |
| 310 | |||
| 311 | out1: | ||
| 294 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, | 312 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, |
| 295 | "policy loaded auid=%u", | 313 | "policy loaded auid=%u", |
| 296 | audit_get_loginuid(current->audit_context)); | 314 | audit_get_loginuid(current->audit_context)); |
| @@ -940,9 +958,8 @@ static const struct file_operations sel_commit_bools_ops = { | |||
| 940 | .write = sel_commit_bools_write, | 958 | .write = sel_commit_bools_write, |
| 941 | }; | 959 | }; |
| 942 | 960 | ||
| 943 | /* delete booleans - partial revoke() from | 961 | /* partial revoke() from fs/proc/generic.c proc_kill_inodes */ |
| 944 | * fs/proc/generic.c proc_kill_inodes */ | 962 | static void sel_remove_entries(struct dentry *de) |
| 945 | static void sel_remove_bools(struct dentry *de) | ||
| 946 | { | 963 | { |
| 947 | struct list_head *p, *node; | 964 | struct list_head *p, *node; |
| 948 | struct super_block *sb = de->d_sb; | 965 | struct super_block *sb = de->d_sb; |
| @@ -998,7 +1015,7 @@ static int sel_make_bools(void) | |||
| 998 | kfree(bool_pending_values); | 1015 | kfree(bool_pending_values); |
| 999 | bool_pending_values = NULL; | 1016 | bool_pending_values = NULL; |
| 1000 | 1017 | ||
| 1001 | sel_remove_bools(dir); | 1018 | sel_remove_entries(dir); |
| 1002 | 1019 | ||
| 1003 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) | 1020 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) |
| 1004 | return -ENOMEM; | 1021 | return -ENOMEM; |
| @@ -1048,7 +1065,7 @@ out: | |||
| 1048 | return ret; | 1065 | return ret; |
| 1049 | err: | 1066 | err: |
| 1050 | kfree(values); | 1067 | kfree(values); |
| 1051 | sel_remove_bools(dir); | 1068 | sel_remove_entries(dir); |
| 1052 | ret = -ENOMEM; | 1069 | ret = -ENOMEM; |
| 1053 | goto out; | 1070 | goto out; |
| 1054 | } | 1071 | } |
| @@ -1294,7 +1311,227 @@ out: | |||
| 1294 | return ret; | 1311 | return ret; |
| 1295 | } | 1312 | } |
| 1296 | 1313 | ||
| 1297 | static int sel_make_dir(struct inode *dir, struct dentry *dentry) | 1314 | static inline unsigned int sel_div(unsigned long a, unsigned long b) |
| 1315 | { | ||
| 1316 | return a / b - (a % b < 0); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | static inline unsigned long sel_class_to_ino(u16 class) | ||
| 1320 | { | ||
| 1321 | return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | static inline u16 sel_ino_to_class(unsigned long ino) | ||
| 1325 | { | ||
| 1326 | return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | static inline unsigned long sel_perm_to_ino(u16 class, u32 perm) | ||
| 1330 | { | ||
| 1331 | return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET; | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | static inline u32 sel_ino_to_perm(unsigned long ino) | ||
| 1335 | { | ||
| 1336 | return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | static ssize_t sel_read_class(struct file * file, char __user *buf, | ||
| 1340 | size_t count, loff_t *ppos) | ||
| 1341 | { | ||
| 1342 | ssize_t rc, len; | ||
| 1343 | char *page; | ||
| 1344 | unsigned long ino = file->f_path.dentry->d_inode->i_ino; | ||
| 1345 | |||
| 1346 | page = (char *)__get_free_page(GFP_KERNEL); | ||
| 1347 | if (!page) { | ||
| 1348 | rc = -ENOMEM; | ||
| 1349 | goto out; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino)); | ||
| 1353 | rc = simple_read_from_buffer(buf, count, ppos, page, len); | ||
| 1354 | free_page((unsigned long)page); | ||
| 1355 | out: | ||
| 1356 | return rc; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | static const struct file_operations sel_class_ops = { | ||
| 1360 | .read = sel_read_class, | ||
| 1361 | }; | ||
| 1362 | |||
| 1363 | static ssize_t sel_read_perm(struct file * file, char __user *buf, | ||
| 1364 | size_t count, loff_t *ppos) | ||
| 1365 | { | ||
| 1366 | ssize_t rc, len; | ||
| 1367 | char *page; | ||
| 1368 | unsigned long ino = file->f_path.dentry->d_inode->i_ino; | ||
| 1369 | |||
| 1370 | page = (char *)__get_free_page(GFP_KERNEL); | ||
| 1371 | if (!page) { | ||
| 1372 | rc = -ENOMEM; | ||
| 1373 | goto out; | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | len = snprintf(page, PAGE_SIZE,"%d", sel_ino_to_perm(ino)); | ||
| 1377 | rc = simple_read_from_buffer(buf, count, ppos, page, len); | ||
| 1378 | free_page((unsigned long)page); | ||
| 1379 | out: | ||
| 1380 | return rc; | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | static const struct file_operations sel_perm_ops = { | ||
| 1384 | .read = sel_read_perm, | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | static int sel_make_perm_files(char *objclass, int classvalue, | ||
| 1388 | struct dentry *dir) | ||
| 1389 | { | ||
| 1390 | int i, rc = 0, nperms; | ||
| 1391 | char **perms; | ||
| 1392 | |||
| 1393 | rc = security_get_permissions(objclass, &perms, &nperms); | ||
| 1394 | if (rc) | ||
| 1395 | goto out; | ||
| 1396 | |||
| 1397 | for (i = 0; i < nperms; i++) { | ||
| 1398 | struct inode *inode; | ||
| 1399 | struct dentry *dentry; | ||
| 1400 | |||
| 1401 | dentry = d_alloc_name(dir, perms[i]); | ||
| 1402 | if (!dentry) { | ||
| 1403 | rc = -ENOMEM; | ||
| 1404 | goto out1; | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); | ||
| 1408 | if (!inode) { | ||
| 1409 | rc = -ENOMEM; | ||
| 1410 | goto out1; | ||
| 1411 | } | ||
| 1412 | inode->i_fop = &sel_perm_ops; | ||
| 1413 | /* i+1 since perm values are 1-indexed */ | ||
| 1414 | inode->i_ino = sel_perm_to_ino(classvalue, i+1); | ||
| 1415 | d_add(dentry, inode); | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | out1: | ||
| 1419 | for (i = 0; i < nperms; i++) | ||
| 1420 | kfree(perms[i]); | ||
| 1421 | kfree(perms); | ||
| 1422 | out: | ||
| 1423 | return rc; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static int sel_make_class_dir_entries(char *classname, int index, | ||
| 1427 | struct dentry *dir) | ||
| 1428 | { | ||
| 1429 | struct dentry *dentry = NULL; | ||
| 1430 | struct inode *inode = NULL; | ||
| 1431 | int rc; | ||
| 1432 | |||
| 1433 | dentry = d_alloc_name(dir, "index"); | ||
| 1434 | if (!dentry) { | ||
| 1435 | rc = -ENOMEM; | ||
| 1436 | goto out; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); | ||
| 1440 | if (!inode) { | ||
| 1441 | rc = -ENOMEM; | ||
| 1442 | goto out; | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | inode->i_fop = &sel_class_ops; | ||
| 1446 | inode->i_ino = sel_class_to_ino(index); | ||
| 1447 | d_add(dentry, inode); | ||
| 1448 | |||
| 1449 | dentry = d_alloc_name(dir, "perms"); | ||
| 1450 | if (!dentry) { | ||
| 1451 | rc = -ENOMEM; | ||
| 1452 | goto out; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | rc = sel_make_dir(dir->d_inode, dentry, &last_class_ino); | ||
| 1456 | if (rc) | ||
| 1457 | goto out; | ||
| 1458 | |||
| 1459 | rc = sel_make_perm_files(classname, index, dentry); | ||
| 1460 | |||
| 1461 | out: | ||
| 1462 | return rc; | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | static void sel_remove_classes(void) | ||
| 1466 | { | ||
| 1467 | struct list_head *class_node; | ||
| 1468 | |||
| 1469 | list_for_each(class_node, &class_dir->d_subdirs) { | ||
| 1470 | struct dentry *class_subdir = list_entry(class_node, | ||
| 1471 | struct dentry, d_u.d_child); | ||
| 1472 | struct list_head *class_subdir_node; | ||
| 1473 | |||
| 1474 | list_for_each(class_subdir_node, &class_subdir->d_subdirs) { | ||
| 1475 | struct dentry *d = list_entry(class_subdir_node, | ||
| 1476 | struct dentry, d_u.d_child); | ||
| 1477 | |||
| 1478 | if (d->d_inode) | ||
| 1479 | if (d->d_inode->i_mode & S_IFDIR) | ||
| 1480 | sel_remove_entries(d); | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | sel_remove_entries(class_subdir); | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | sel_remove_entries(class_dir); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | static int sel_make_classes(void) | ||
| 1490 | { | ||
| 1491 | int rc = 0, nclasses, i; | ||
| 1492 | char **classes; | ||
| 1493 | |||
| 1494 | /* delete any existing entries */ | ||
| 1495 | sel_remove_classes(); | ||
| 1496 | |||
| 1497 | rc = security_get_classes(&classes, &nclasses); | ||
| 1498 | if (rc < 0) | ||
| 1499 | goto out; | ||
| 1500 | |||
| 1501 | /* +2 since classes are 1-indexed */ | ||
| 1502 | last_class_ino = sel_class_to_ino(nclasses+2); | ||
| 1503 | |||
| 1504 | for (i = 0; i < nclasses; i++) { | ||
| 1505 | struct dentry *class_name_dir; | ||
| 1506 | |||
| 1507 | class_name_dir = d_alloc_name(class_dir, classes[i]); | ||
| 1508 | if (!class_name_dir) { | ||
| 1509 | rc = -ENOMEM; | ||
| 1510 | goto out1; | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | rc = sel_make_dir(class_dir->d_inode, class_name_dir, | ||
| 1514 | &last_class_ino); | ||
| 1515 | if (rc) | ||
| 1516 | goto out1; | ||
| 1517 | |||
| 1518 | /* i+1 since class values are 1-indexed */ | ||
| 1519 | rc = sel_make_class_dir_entries(classes[i], i+1, | ||
| 1520 | class_name_dir); | ||
| 1521 | if (rc) | ||
| 1522 | goto out1; | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | out1: | ||
| 1526 | for (i = 0; i < nclasses; i++) | ||
| 1527 | kfree(classes[i]); | ||
| 1528 | kfree(classes); | ||
| 1529 | out: | ||
| 1530 | return rc; | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | static int sel_make_dir(struct inode *dir, struct dentry *dentry, | ||
| 1534 | unsigned long *ino) | ||
| 1298 | { | 1535 | { |
| 1299 | int ret = 0; | 1536 | int ret = 0; |
| 1300 | struct inode *inode; | 1537 | struct inode *inode; |
| @@ -1306,7 +1543,7 @@ static int sel_make_dir(struct inode *dir, struct dentry *dentry) | |||
| 1306 | } | 1543 | } |
| 1307 | inode->i_op = &simple_dir_inode_operations; | 1544 | inode->i_op = &simple_dir_inode_operations; |
| 1308 | inode->i_fop = &simple_dir_operations; | 1545 | inode->i_fop = &simple_dir_operations; |
| 1309 | inode->i_ino = ++sel_last_ino; | 1546 | inode->i_ino = ++(*ino); |
| 1310 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | 1547 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
| 1311 | inc_nlink(inode); | 1548 | inc_nlink(inode); |
| 1312 | d_add(dentry, inode); | 1549 | d_add(dentry, inode); |
| @@ -1352,7 +1589,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
| 1352 | goto err; | 1589 | goto err; |
| 1353 | } | 1590 | } |
| 1354 | 1591 | ||
| 1355 | ret = sel_make_dir(root_inode, dentry); | 1592 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); |
| 1356 | if (ret) | 1593 | if (ret) |
| 1357 | goto err; | 1594 | goto err; |
| 1358 | 1595 | ||
| @@ -1385,7 +1622,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
| 1385 | goto err; | 1622 | goto err; |
| 1386 | } | 1623 | } |
| 1387 | 1624 | ||
| 1388 | ret = sel_make_dir(root_inode, dentry); | 1625 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); |
| 1389 | if (ret) | 1626 | if (ret) |
| 1390 | goto err; | 1627 | goto err; |
| 1391 | 1628 | ||
| @@ -1399,7 +1636,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
| 1399 | goto err; | 1636 | goto err; |
| 1400 | } | 1637 | } |
| 1401 | 1638 | ||
| 1402 | ret = sel_make_dir(root_inode, dentry); | 1639 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); |
| 1403 | if (ret) | 1640 | if (ret) |
| 1404 | goto err; | 1641 | goto err; |
| 1405 | 1642 | ||
| @@ -1407,6 +1644,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
| 1407 | if (ret) | 1644 | if (ret) |
| 1408 | goto err; | 1645 | goto err; |
| 1409 | 1646 | ||
| 1647 | dentry = d_alloc_name(sb->s_root, "class"); | ||
| 1648 | if (!dentry) { | ||
| 1649 | ret = -ENOMEM; | ||
| 1650 | goto err; | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); | ||
| 1654 | if (ret) | ||
| 1655 | goto err; | ||
| 1656 | |||
| 1657 | class_dir = dentry; | ||
| 1658 | |||
| 1410 | out: | 1659 | out: |
| 1411 | return ret; | 1660 | return ret; |
| 1412 | err: | 1661 | err: |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 0ac1021734c0..f05f97a2bc3a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | */ | 21 | */ |
| 22 | 22 | ||
| 23 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
| 24 | #include <linux/sched.h> | ||
| 24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
| 25 | #include <linux/string.h> | 26 | #include <linux/string.h> |
| 26 | #include <linux/errno.h> | 27 | #include <linux/errno.h> |
| @@ -598,6 +599,7 @@ void policydb_destroy(struct policydb *p) | |||
| 598 | struct range_trans *rt, *lrt = NULL; | 599 | struct range_trans *rt, *lrt = NULL; |
| 599 | 600 | ||
| 600 | for (i = 0; i < SYM_NUM; i++) { | 601 | for (i = 0; i < SYM_NUM; i++) { |
| 602 | cond_resched(); | ||
| 601 | hashtab_map(p->symtab[i].table, destroy_f[i], NULL); | 603 | hashtab_map(p->symtab[i].table, destroy_f[i], NULL); |
| 602 | hashtab_destroy(p->symtab[i].table); | 604 | hashtab_destroy(p->symtab[i].table); |
| 603 | } | 605 | } |
| @@ -612,6 +614,7 @@ void policydb_destroy(struct policydb *p) | |||
| 612 | avtab_destroy(&p->te_avtab); | 614 | avtab_destroy(&p->te_avtab); |
| 613 | 615 | ||
| 614 | for (i = 0; i < OCON_NUM; i++) { | 616 | for (i = 0; i < OCON_NUM; i++) { |
| 617 | cond_resched(); | ||
| 615 | c = p->ocontexts[i]; | 618 | c = p->ocontexts[i]; |
| 616 | while (c) { | 619 | while (c) { |
| 617 | ctmp = c; | 620 | ctmp = c; |
| @@ -623,6 +626,7 @@ void policydb_destroy(struct policydb *p) | |||
| 623 | 626 | ||
| 624 | g = p->genfs; | 627 | g = p->genfs; |
| 625 | while (g) { | 628 | while (g) { |
| 629 | cond_resched(); | ||
| 626 | kfree(g->fstype); | 630 | kfree(g->fstype); |
| 627 | c = g->head; | 631 | c = g->head; |
| 628 | while (c) { | 632 | while (c) { |
| @@ -639,18 +643,21 @@ void policydb_destroy(struct policydb *p) | |||
| 639 | cond_policydb_destroy(p); | 643 | cond_policydb_destroy(p); |
| 640 | 644 | ||
| 641 | for (tr = p->role_tr; tr; tr = tr->next) { | 645 | for (tr = p->role_tr; tr; tr = tr->next) { |
| 646 | cond_resched(); | ||
| 642 | kfree(ltr); | 647 | kfree(ltr); |
| 643 | ltr = tr; | 648 | ltr = tr; |
| 644 | } | 649 | } |
| 645 | kfree(ltr); | 650 | kfree(ltr); |
| 646 | 651 | ||
| 647 | for (ra = p->role_allow; ra; ra = ra -> next) { | 652 | for (ra = p->role_allow; ra; ra = ra -> next) { |
| 653 | cond_resched(); | ||
| 648 | kfree(lra); | 654 | kfree(lra); |
| 649 | lra = ra; | 655 | lra = ra; |
| 650 | } | 656 | } |
| 651 | kfree(lra); | 657 | kfree(lra); |
| 652 | 658 | ||
| 653 | for (rt = p->range_tr; rt; rt = rt -> next) { | 659 | for (rt = p->range_tr; rt; rt = rt -> next) { |
| 660 | cond_resched(); | ||
| 654 | if (lrt) { | 661 | if (lrt) { |
| 655 | ebitmap_destroy(&lrt->target_range.level[0].cat); | 662 | ebitmap_destroy(&lrt->target_range.level[0].cat); |
| 656 | ebitmap_destroy(&lrt->target_range.level[1].cat); | 663 | ebitmap_destroy(&lrt->target_range.level[1].cat); |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 40660ffd49b6..b5f017f07a75 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -1587,19 +1587,18 @@ int security_get_user_sids(u32 fromsid, | |||
| 1587 | u32 *nel) | 1587 | u32 *nel) |
| 1588 | { | 1588 | { |
| 1589 | struct context *fromcon, usercon; | 1589 | struct context *fromcon, usercon; |
| 1590 | u32 *mysids, *mysids2, sid; | 1590 | u32 *mysids = NULL, *mysids2, sid; |
| 1591 | u32 mynel = 0, maxnel = SIDS_NEL; | 1591 | u32 mynel = 0, maxnel = SIDS_NEL; |
| 1592 | struct user_datum *user; | 1592 | struct user_datum *user; |
| 1593 | struct role_datum *role; | 1593 | struct role_datum *role; |
| 1594 | struct av_decision avd; | ||
| 1595 | struct ebitmap_node *rnode, *tnode; | 1594 | struct ebitmap_node *rnode, *tnode; |
| 1596 | int rc = 0, i, j; | 1595 | int rc = 0, i, j; |
| 1597 | 1596 | ||
| 1598 | if (!ss_initialized) { | 1597 | *sids = NULL; |
| 1599 | *sids = NULL; | 1598 | *nel = 0; |
| 1600 | *nel = 0; | 1599 | |
| 1600 | if (!ss_initialized) | ||
| 1601 | goto out; | 1601 | goto out; |
| 1602 | } | ||
| 1603 | 1602 | ||
| 1604 | POLICY_RDLOCK; | 1603 | POLICY_RDLOCK; |
| 1605 | 1604 | ||
| @@ -1635,17 +1634,9 @@ int security_get_user_sids(u32 fromsid, | |||
| 1635 | if (mls_setup_user_range(fromcon, user, &usercon)) | 1634 | if (mls_setup_user_range(fromcon, user, &usercon)) |
| 1636 | continue; | 1635 | continue; |
| 1637 | 1636 | ||
| 1638 | rc = context_struct_compute_av(fromcon, &usercon, | ||
| 1639 | SECCLASS_PROCESS, | ||
| 1640 | PROCESS__TRANSITION, | ||
| 1641 | &avd); | ||
| 1642 | if (rc || !(avd.allowed & PROCESS__TRANSITION)) | ||
| 1643 | continue; | ||
| 1644 | rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); | 1637 | rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); |
| 1645 | if (rc) { | 1638 | if (rc) |
| 1646 | kfree(mysids); | ||
| 1647 | goto out_unlock; | 1639 | goto out_unlock; |
| 1648 | } | ||
| 1649 | if (mynel < maxnel) { | 1640 | if (mynel < maxnel) { |
| 1650 | mysids[mynel++] = sid; | 1641 | mysids[mynel++] = sid; |
| 1651 | } else { | 1642 | } else { |
| @@ -1653,7 +1644,6 @@ int security_get_user_sids(u32 fromsid, | |||
| 1653 | mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); | 1644 | mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); |
| 1654 | if (!mysids2) { | 1645 | if (!mysids2) { |
| 1655 | rc = -ENOMEM; | 1646 | rc = -ENOMEM; |
| 1656 | kfree(mysids); | ||
| 1657 | goto out_unlock; | 1647 | goto out_unlock; |
| 1658 | } | 1648 | } |
| 1659 | memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); | 1649 | memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); |
| @@ -1664,11 +1654,32 @@ int security_get_user_sids(u32 fromsid, | |||
| 1664 | } | 1654 | } |
| 1665 | } | 1655 | } |
| 1666 | 1656 | ||
| 1667 | *sids = mysids; | ||
| 1668 | *nel = mynel; | ||
| 1669 | |||
| 1670 | out_unlock: | 1657 | out_unlock: |
| 1671 | POLICY_RDUNLOCK; | 1658 | POLICY_RDUNLOCK; |
| 1659 | if (rc || !mynel) { | ||
| 1660 | kfree(mysids); | ||
| 1661 | goto out; | ||
| 1662 | } | ||
| 1663 | |||
| 1664 | mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); | ||
| 1665 | if (!mysids2) { | ||
| 1666 | rc = -ENOMEM; | ||
| 1667 | kfree(mysids); | ||
| 1668 | goto out; | ||
| 1669 | } | ||
| 1670 | for (i = 0, j = 0; i < mynel; i++) { | ||
| 1671 | rc = avc_has_perm_noaudit(fromsid, mysids[i], | ||
| 1672 | SECCLASS_PROCESS, | ||
| 1673 | PROCESS__TRANSITION, AVC_STRICT, | ||
| 1674 | NULL); | ||
| 1675 | if (!rc) | ||
| 1676 | mysids2[j++] = mysids[i]; | ||
| 1677 | cond_resched(); | ||
| 1678 | } | ||
| 1679 | rc = 0; | ||
| 1680 | kfree(mysids); | ||
| 1681 | *sids = mysids2; | ||
| 1682 | *nel = j; | ||
| 1672 | out: | 1683 | out: |
| 1673 | return rc; | 1684 | return rc; |
| 1674 | } | 1685 | } |
| @@ -1996,6 +2007,101 @@ out: | |||
| 1996 | return rc; | 2007 | return rc; |
| 1997 | } | 2008 | } |
| 1998 | 2009 | ||
| 2010 | static int get_classes_callback(void *k, void *d, void *args) | ||
| 2011 | { | ||
| 2012 | struct class_datum *datum = d; | ||
| 2013 | char *name = k, **classes = args; | ||
| 2014 | int value = datum->value - 1; | ||
| 2015 | |||
| 2016 | classes[value] = kstrdup(name, GFP_ATOMIC); | ||
| 2017 | if (!classes[value]) | ||
| 2018 | return -ENOMEM; | ||
| 2019 | |||
| 2020 | return 0; | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | int security_get_classes(char ***classes, int *nclasses) | ||
| 2024 | { | ||
| 2025 | int rc = -ENOMEM; | ||
| 2026 | |||
| 2027 | POLICY_RDLOCK; | ||
| 2028 | |||
| 2029 | *nclasses = policydb.p_classes.nprim; | ||
| 2030 | *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC); | ||
| 2031 | if (!*classes) | ||
| 2032 | goto out; | ||
| 2033 | |||
| 2034 | rc = hashtab_map(policydb.p_classes.table, get_classes_callback, | ||
| 2035 | *classes); | ||
| 2036 | if (rc < 0) { | ||
| 2037 | int i; | ||
| 2038 | for (i = 0; i < *nclasses; i++) | ||
| 2039 | kfree((*classes)[i]); | ||
| 2040 | kfree(*classes); | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | out: | ||
| 2044 | POLICY_RDUNLOCK; | ||
| 2045 | return rc; | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | static int get_permissions_callback(void *k, void *d, void *args) | ||
| 2049 | { | ||
| 2050 | struct perm_datum *datum = d; | ||
| 2051 | char *name = k, **perms = args; | ||
| 2052 | int value = datum->value - 1; | ||
| 2053 | |||
| 2054 | perms[value] = kstrdup(name, GFP_ATOMIC); | ||
| 2055 | if (!perms[value]) | ||
| 2056 | return -ENOMEM; | ||
| 2057 | |||
| 2058 | return 0; | ||
| 2059 | } | ||
| 2060 | |||
| 2061 | int security_get_permissions(char *class, char ***perms, int *nperms) | ||
| 2062 | { | ||
| 2063 | int rc = -ENOMEM, i; | ||
| 2064 | struct class_datum *match; | ||
| 2065 | |||
| 2066 | POLICY_RDLOCK; | ||
| 2067 | |||
| 2068 | match = hashtab_search(policydb.p_classes.table, class); | ||
| 2069 | if (!match) { | ||
| 2070 | printk(KERN_ERR "%s: unrecognized class %s\n", | ||
| 2071 | __FUNCTION__, class); | ||
| 2072 | rc = -EINVAL; | ||
| 2073 | goto out; | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | *nperms = match->permissions.nprim; | ||
| 2077 | *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC); | ||
| 2078 | if (!*perms) | ||
| 2079 | goto out; | ||
| 2080 | |||
| 2081 | if (match->comdatum) { | ||
| 2082 | rc = hashtab_map(match->comdatum->permissions.table, | ||
| 2083 | get_permissions_callback, *perms); | ||
| 2084 | if (rc < 0) | ||
| 2085 | goto err; | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | rc = hashtab_map(match->permissions.table, get_permissions_callback, | ||
| 2089 | *perms); | ||
| 2090 | if (rc < 0) | ||
| 2091 | goto err; | ||
| 2092 | |||
| 2093 | out: | ||
| 2094 | POLICY_RDUNLOCK; | ||
| 2095 | return rc; | ||
| 2096 | |||
| 2097 | err: | ||
| 2098 | POLICY_RDUNLOCK; | ||
| 2099 | for (i = 0; i < *nperms; i++) | ||
| 2100 | kfree((*perms)[i]); | ||
| 2101 | kfree(*perms); | ||
| 2102 | return rc; | ||
| 2103 | } | ||
| 2104 | |||
| 1999 | struct selinux_audit_rule { | 2105 | struct selinux_audit_rule { |
| 2000 | u32 au_seqno; | 2106 | u32 au_seqno; |
| 2001 | struct context au_ctxt; | 2107 | struct context au_ctxt; |
