From 0909c0ae999c325b9d34c6f4710f40730ae3bc24 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 28 Feb 2014 07:23:24 -0500 Subject: selinux: put the mmap() DAC controls before the MAC controls It turns out that doing the SELinux MAC checks for mmap() before the DAC checks was causing users and the SELinux policy folks headaches as users were seeing a lot of SELinux AVC denials for the memprotect:mmap_zero permission that would have also been denied by the normal DAC capability checks (CAP_SYS_RAWIO). Example: # cat mmap_test.c #include #include #include #include int main(int argc, char *argv[]) { int rc; void *mem; mem = mmap(0x0, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (mem == MAP_FAILED) return errno; printf("mem = %p\n", mem); munmap(mem, 4096); return 0; } # gcc -g -O0 -o mmap_test mmap_test.c # ./mmap_test mem = (nil) # ausearch -m AVC | grep mmap_zero type=AVC msg=audit(...): avc: denied { mmap_zero } for pid=1025 comm="mmap_test" scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=memprotect This patch corrects things so that when the above example is run by a user without CAP_SYS_RAWIO the SELinux AVC is no longer generated as the DAC capability check fails before the SELinux permission check. Signed-off-by: Paul Moore Acked-by: Stephen Smalley --- security/selinux/hooks.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4b34847208cc..a3230de656e4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3204,24 +3204,20 @@ error: static int selinux_mmap_addr(unsigned long addr) { - int rc = 0; - u32 sid = current_sid(); + int rc; + + /* do DAC check on address space usage */ + rc = cap_mmap_addr(addr); + if (rc) + return rc; - /* - * notice that we are intentionally putting the SELinux check before - * the secondary cap_file_mmap check. This is such a likely attempt - * at bad behaviour/exploit that we always want to get the AVC, even - * if DAC would have also denied the operation. - */ if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { + u32 sid = current_sid(); rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, NULL); - if (rc) - return rc; } - /* do DAC check on address space usage */ - return cap_mmap_addr(addr); + return rc; } static int selinux_mmap_file(struct file *file, unsigned long reqprot, -- cgit v1.2.3 From eee3094683fbc7fe6bcdaef58c1ef31f8460cdca Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 5 Mar 2014 15:54:57 -0500 Subject: selinux: correctly label /proc inodes in use before the policy is loaded This patch is based on an earlier patch by Eric Paris, he describes the problem below: "If an inode is accessed before policy load it will get placed on a list of inodes to be initialized after policy load. After policy load we call inode_doinit() which calls inode_doinit_with_dentry() on all inodes accessed before policy load. In the case of inodes in procfs that means we'll end up at the bottom where it does: /* Default to the fs superblock SID. */ isec->sid = sbsec->sid; if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { if (opt_dentry) { isec->sclass = inode_mode_to_security_class(...) rc = selinux_proc_get_sid(opt_dentry, isec->sclass, &sid); if (rc) goto out_unlock; isec->sid = sid; } } Since opt_dentry is null, we'll never call selinux_proc_get_sid() and will leave the inode labeled with the label on the superblock. I believe a fix would be to mimic the behavior of xattrs. Look for an alias of the inode. If it can't be found, just leave the inode uninitialized (and pick it up later) if it can be found, we should be able to call selinux_proc_get_sid() ..." On a system exhibiting this problem, you will notice a lot of files in /proc with the generic "proc_t" type (at least the ones that were accessed early in the boot), for example: # ls -Z /proc/sys/kernel/shmmax | awk '{ print $4 " " $5 }' system_u:object_r:proc_t:s0 /proc/sys/kernel/shmmax However, with this patch in place we see the expected result: # ls -Z /proc/sys/kernel/shmmax | awk '{ print $4 " " $5 }' system_u:object_r:sysctl_kernel_t:s0 /proc/sys/kernel/shmmax Cc: Eric Paris Signed-off-by: Paul Moore Acked-by: Eric Paris --- security/selinux/hooks.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a3230de656e4..8b1656f053f8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1418,15 +1418,33 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent isec->sid = sbsec->sid; if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { - if (opt_dentry) { - isec->sclass = inode_mode_to_security_class(inode->i_mode); - rc = selinux_proc_get_sid(opt_dentry, - isec->sclass, - &sid); - if (rc) - goto out_unlock; - isec->sid = sid; - } + /* We must have a dentry to determine the label on + * procfs inodes */ + if (opt_dentry) + /* Called from d_instantiate or + * d_splice_alias. */ + dentry = dget(opt_dentry); + else + /* Called from selinux_complete_init, try to + * find a dentry. */ + dentry = d_find_alias(inode); + /* + * This can be hit on boot when a file is accessed + * before the policy is loaded. When we load policy we + * may find inodes that have no dentry on the + * sbsec->isec_head list. No reason to complain as + * these will get fixed up the next time we go through + * inode_doinit() with a dentry, before these inodes + * could be used again by userspace. + */ + if (!dentry) + goto out_unlock; + isec->sclass = inode_mode_to_security_class(inode->i_mode); + rc = selinux_proc_get_sid(dentry, isec->sclass, &sid); + dput(dentry); + if (rc) + goto out_unlock; + isec->sid = sid; } break; } -- cgit v1.2.3 From 626b9740fa73cad043e136bfb3b6fca68a4f8a7c Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 29 Apr 2014 11:29:04 -0700 Subject: selinux: Report permissive mode in avc: denied messages. We cannot presently tell from an avc: denied message whether access was in fact denied or was allowed due to global or per-domain permissive mode. Add a permissive= field to the avc message to reflect this information. Signed-off-by: Stephen Smalley Acked-by: Eric Paris Signed-off-by: Paul Moore --- security/selinux/avc.c | 7 ++++++- security/selinux/hooks.c | 5 +++-- security/selinux/include/avc.h | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index fc3e6628a864..a18f1fa6440b 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -444,11 +444,15 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) avc_dump_query(ab, ad->selinux_audit_data->ssid, ad->selinux_audit_data->tsid, ad->selinux_audit_data->tclass); + if (ad->selinux_audit_data->denied) { + audit_log_format(ab, " permissive=%u", + ad->selinux_audit_data->result ? 0 : 1); + } } /* This is the slow part of avc audit with big stack footprint */ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, - u32 requested, u32 audited, u32 denied, + u32 requested, u32 audited, u32 denied, int result, struct common_audit_data *a, unsigned flags) { @@ -477,6 +481,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, sad.tsid = tsid; sad.audited = audited; sad.denied = denied; + sad.result = result; a->selinux_audit_data = &sad; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d58946dca8c9..889cf4c3c3fa 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2770,6 +2770,7 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na static noinline int audit_inode_permission(struct inode *inode, u32 perms, u32 audited, u32 denied, + int result, unsigned flags) { struct common_audit_data ad; @@ -2780,7 +2781,7 @@ static noinline int audit_inode_permission(struct inode *inode, ad.u.inode = inode; rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, - audited, denied, &ad, flags); + audited, denied, result, &ad, flags); if (rc) return rc; return 0; @@ -2822,7 +2823,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (likely(!audited)) return rc; - rc2 = audit_inode_permission(inode, perms, audited, denied, flags); + rc2 = audit_inode_permission(inode, perms, audited, denied, rc, flags); if (rc2) return rc2; return rc; diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index f53ee3c58d0f..ddf8eec03f21 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -102,7 +102,7 @@ static inline u32 avc_audit_required(u32 requested, } int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, - u32 requested, u32 audited, u32 denied, + u32 requested, u32 audited, u32 denied, int result, struct common_audit_data *a, unsigned flags); @@ -137,7 +137,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, if (likely(!audited)) return 0; return slow_avc_audit(ssid, tsid, tclass, - requested, audited, denied, + requested, audited, denied, result, a, 0); } -- cgit v1.2.3 From 4f189988a0a5890db597ec48fc0e8b09922f290a Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 15 May 2014 11:16:06 -0400 Subject: selinux: reject setexeccon() on MNT_NOSUID applications with -EACCES We presently prevent processes from using setexecon() to set the security label of exec()'d processes when NO_NEW_PRIVS is enabled by returning an error; however, we silently ignore setexeccon() when exec()'ing from a nosuid mounted filesystem. This patch makes things a bit more consistent by returning an error in the setexeccon()/nosuid case. Signed-off-by: Paul Moore Acked-by: Andy Lutomirski Acked-by: Stephen Smalley --- security/selinux/hooks.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 889cf4c3c3fa..b03b0776955a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2123,11 +2123,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) new_tsec->exec_sid = 0; /* - * Minimize confusion: if no_new_privs and a transition is - * explicitly requested, then fail the exec. + * Minimize confusion: if no_new_privs or nosuid and a + * transition is explicitly requested, then fail the exec. */ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) return -EPERM; + if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + return -EACCES; } else { /* Check for a default transition on this program. */ rc = security_transition_sid(old_tsec->sid, isec->sid, -- cgit v1.2.3 From 615e51fdda6f274e94b1e905fcaf6111e0d9aa20 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 26 Jun 2014 14:33:56 -0400 Subject: selinux: reduce the number of calls to synchronize_net() when flushing caches When flushing the AVC, such as during a policy load, the various network caches are also flushed, with each making a call to synchronize_net() which has shown to be expensive in some cases. This patch consolidates the network cache flushes into a single AVC callback which only calls synchronize_net() once for each AVC cache flush. Reported-by: Jaejyn Shin Signed-off-by: Paul Moore --- security/selinux/hooks.c | 14 ++++++++++++++ security/selinux/include/netif.h | 2 ++ security/selinux/include/netnode.h | 2 ++ security/selinux/include/netport.h | 2 ++ security/selinux/netif.c | 15 +-------------- security/selinux/netnode.c | 15 +-------------- security/selinux/netport.c | 15 +-------------- 7 files changed, 23 insertions(+), 42 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 336f0a04450e..39bc8c94b969 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -161,6 +161,17 @@ static int selinux_peerlbl_enabled(void) return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); } +static int selinux_netcache_avc_callback(u32 event) +{ + if (event == AVC_CALLBACK_RESET) { + sel_netif_flush(); + sel_netnode_flush(); + sel_netport_flush(); + synchronize_net(); + } + return 0; +} + /* * initialise the security for the init task */ @@ -5993,6 +6004,9 @@ static __init int selinux_init(void) if (register_security(&selinux_ops)) panic("SELinux: Unable to register with kernel.\n"); + if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) + panic("SELinux: Unable to register AVC netcache callback\n"); + if (selinux_enforcing) printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); else diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index 43d507242b42..57c6eae81eac 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -17,6 +17,8 @@ #ifndef _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_ +void sel_netif_flush(void); + int sel_netif_sid(int ifindex, u32 *sid); #endif /* _SELINUX_NETIF_H_ */ diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h index df7a5ed6c694..937668dd3024 100644 --- a/security/selinux/include/netnode.h +++ b/security/selinux/include/netnode.h @@ -27,6 +27,8 @@ #ifndef _SELINUX_NETNODE_H #define _SELINUX_NETNODE_H +void sel_netnode_flush(void); + int sel_netnode_sid(void *addr, u16 family, u32 *sid); #endif diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h index 4d965b83d735..d1ce896b2cb0 100644 --- a/security/selinux/include/netport.h +++ b/security/selinux/include/netport.h @@ -26,6 +26,8 @@ #ifndef _SELINUX_NETPORT_H #define _SELINUX_NETPORT_H +void sel_netport_flush(void); + int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid); #endif diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 694e9e43855f..3c3de4ca0ebc 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -240,7 +240,7 @@ static void sel_netif_kill(int ifindex) * Remove all entries from the network interface table. * */ -static void sel_netif_flush(void) +void sel_netif_flush(void) { int idx; struct sel_netif *netif; @@ -252,15 +252,6 @@ static void sel_netif_flush(void) spin_unlock_bh(&sel_netif_lock); } -static int sel_netif_avc_callback(u32 event) -{ - if (event == AVC_CALLBACK_RESET) { - sel_netif_flush(); - synchronize_net(); - } - return 0; -} - static int sel_netif_netdev_notifier_handler(struct notifier_block *this, unsigned long event, void *ptr) { @@ -291,10 +282,6 @@ static __init int sel_netif_init(void) register_netdevice_notifier(&sel_netif_netdev_notifier); - err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET); - if (err) - panic("avc_add_callback() failed, error %d\n", err); - return err; } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index 03a72c32afd7..ddf315260839 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -283,7 +283,7 @@ int sel_netnode_sid(void *addr, u16 family, u32 *sid) * Remove all entries from the network address table. * */ -static void sel_netnode_flush(void) +void sel_netnode_flush(void) { unsigned int idx; struct sel_netnode *node, *node_tmp; @@ -300,15 +300,6 @@ static void sel_netnode_flush(void) spin_unlock_bh(&sel_netnode_lock); } -static int sel_netnode_avc_callback(u32 event) -{ - if (event == AVC_CALLBACK_RESET) { - sel_netnode_flush(); - synchronize_net(); - } - return 0; -} - static __init int sel_netnode_init(void) { int iter; @@ -322,10 +313,6 @@ static __init int sel_netnode_init(void) sel_netnode_hash[iter].size = 0; } - ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET); - if (ret != 0) - panic("avc_add_callback() failed, error %d\n", ret); - return ret; } diff --git a/security/selinux/netport.c b/security/selinux/netport.c index d35379781c2c..73ac6784d091 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -217,7 +217,7 @@ int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) * Remove all entries from the network address table. * */ -static void sel_netport_flush(void) +void sel_netport_flush(void) { unsigned int idx; struct sel_netport *port, *port_tmp; @@ -234,15 +234,6 @@ static void sel_netport_flush(void) spin_unlock_bh(&sel_netport_lock); } -static int sel_netport_avc_callback(u32 event) -{ - if (event == AVC_CALLBACK_RESET) { - sel_netport_flush(); - synchronize_net(); - } - return 0; -} - static __init int sel_netport_init(void) { int iter; @@ -256,10 +247,6 @@ static __init int sel_netport_init(void) sel_netport_hash[iter].size = 0; } - ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET); - if (ret != 0) - panic("avc_add_callback() failed, error %d\n", ret); - return ret; } -- cgit v1.2.3 From 4da6daf4d3df5a977e4623963f141a627fd2efce Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 10 Jul 2014 10:17:48 -0400 Subject: selinux: fix the default socket labeling in sock_graft() The sock_graft() hook has special handling for AF_INET, AF_INET, and AF_UNIX sockets as those address families have special hooks which label the sock before it is attached its associated socket. Unfortunately, the sock_graft() hook was missing a default approach to labeling sockets which meant that any other address family which made use of connections or the accept() syscall would find the returned socket to be in an "unlabeled" state. This was recently demonstrated by the kcrypto/AF_ALG subsystem and the newly released cryptsetup package (cryptsetup v1.6.5 and later). This patch preserves the special handling in selinux_sock_graft(), but adds a default behavior - setting the sock's label equal to the associated socket - which resolves the problem with AF_ALG and presumably any other address family which makes use of accept(). Cc: stable@vger.kernel.org Signed-off-by: Paul Moore Tested-by: Milan Broz --- include/linux/security.h | 5 ++++- security/selinux/hooks.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/include/linux/security.h b/include/linux/security.h index 6478ce3252c7..794be735ff4b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -987,7 +987,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Retrieve the LSM-specific secid for the sock to enable caching of network * authorizations. * @sock_graft: - * Sets the socket's isec sid to the sock's sid. + * This hook is called in response to a newly created sock struct being + * grafted onto an existing socket and allows the security module to + * perform whatever security attribute management is necessary for both + * the sock and socket. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 336f0a04450e..b3a6754e932b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4499,9 +4499,18 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || - sk->sk_family == PF_UNIX) + switch (sk->sk_family) { + case PF_INET: + case PF_INET6: + case PF_UNIX: isec->sid = sksec->sid; + break; + default: + /* by default there is no special labeling mechanism for the + * sksec label so inherit the label from the parent socket */ + BUG_ON(sksec->sid != SECINITSID_UNLABELED); + sksec->sid = isec->sid; + } sksec->sclass = isec->sclass; } -- cgit v1.2.3 From 2873ead7e46694910ac49c3a8ee0f54956f96e0c Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 28 Jul 2014 10:42:48 -0400 Subject: Revert "selinux: fix the default socket labeling in sock_graft()" This reverts commit 4da6daf4d3df5a977e4623963f141a627fd2efce. Unfortunately, the commit in question caused problems with Bluetooth devices, specifically it caused them to get caught in the newly created BUG_ON() check. The AF_ALG problem still exists, but will be addressed in a future patch. Cc: stable@vger.kernel.org Signed-off-by: Paul Moore --- include/linux/security.h | 5 +---- security/selinux/hooks.c | 13 ++----------- 2 files changed, 3 insertions(+), 15 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/include/linux/security.h b/include/linux/security.h index 794be735ff4b..6478ce3252c7 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -987,10 +987,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Retrieve the LSM-specific secid for the sock to enable caching of network * authorizations. * @sock_graft: - * This hook is called in response to a newly created sock struct being - * grafted onto an existing socket and allows the security module to - * perform whatever security attribute management is necessary for both - * the sock and socket. + * Sets the socket's isec sid to the sock's sid. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b3a6754e932b..336f0a04450e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4499,18 +4499,9 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - switch (sk->sk_family) { - case PF_INET: - case PF_INET6: - case PF_UNIX: + if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || + sk->sk_family == PF_UNIX) isec->sid = sksec->sid; - break; - default: - /* by default there is no special labeling mechanism for the - * sksec label so inherit the label from the parent socket */ - BUG_ON(sksec->sid != SECINITSID_UNLABELED); - sksec->sid = isec->sid; - } sksec->sclass = isec->sclass; } -- cgit v1.2.3