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; |