| From 3a28cff3bd4bf43f02be0c4e7933aebf3dc8197e Mon Sep 17 00:00:00 2001 |
| From: Stephen Smalley <sds@tycho.nsa.gov> |
| Date: Wed, 12 Dec 2018 10:10:55 -0500 |
| Subject: selinux: avoid silent denials in permissive mode under RCU walk |
| |
| From: Stephen Smalley <sds@tycho.nsa.gov> |
| |
| commit 3a28cff3bd4bf43f02be0c4e7933aebf3dc8197e upstream. |
| |
| commit 0dc1ba24f7fff6 ("SELINUX: Make selinux cache VFS RCU walks safe") |
| results in no audit messages at all if in permissive mode because the |
| cache is updated during the rcu walk and thus no denial occurs on |
| the subsequent ref walk. Fix this by not updating the cache when |
| performing a non-blocking permission check. This only affects search |
| and symlink read checks during rcu walk. |
| |
| Fixes: 0dc1ba24f7fff6 ("SELINUX: Make selinux cache VFS RCU walks safe") |
| Reported-by: BMK <bmktuwien@gmail.com> |
| Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> |
| Signed-off-by: Paul Moore <paul@paul-moore.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| security/selinux/avc.c | 23 +++++++++++++++++++++-- |
| security/selinux/hooks.c | 4 +++- |
| security/selinux/include/avc.h | 1 + |
| 3 files changed, 25 insertions(+), 3 deletions(-) |
| |
| --- a/security/selinux/avc.c |
| +++ b/security/selinux/avc.c |
| @@ -838,6 +838,7 @@ out: |
| * @ssid,@tsid,@tclass : identifier of an AVC entry |
| * @seqno : sequence number when decision was made |
| * @xpd: extended_perms_decision to be added to the node |
| + * @flags: the AVC_* flags, e.g. AVC_NONBLOCKING, AVC_EXTENDED_PERMS, or 0. |
| * |
| * if a valid AVC entry doesn't exist,this function returns -ENOENT. |
| * if kmalloc() called internal returns NULL, this function returns -ENOMEM. |
| @@ -856,6 +857,23 @@ static int avc_update_node(struct selinu |
| struct hlist_head *head; |
| spinlock_t *lock; |
| |
| + /* |
| + * If we are in a non-blocking code path, e.g. VFS RCU walk, |
| + * then we must not add permissions to a cache entry |
| + * because we cannot safely audit the denial. Otherwise, |
| + * during the subsequent blocking retry (e.g. VFS ref walk), we |
| + * will find the permissions already granted in the cache entry |
| + * and won't audit anything at all, leading to silent denials in |
| + * permissive mode that only appear when in enforcing mode. |
| + * |
| + * See the corresponding handling in slow_avc_audit(), and the |
| + * logic in selinux_inode_follow_link and selinux_inode_permission |
| + * for the VFS MAY_NOT_BLOCK flag, which is transliterated into |
| + * AVC_NONBLOCKING for avc_has_perm_noaudit(). |
| + */ |
| + if (flags & AVC_NONBLOCKING) |
| + return 0; |
| + |
| node = avc_alloc_node(avc); |
| if (!node) { |
| rc = -ENOMEM; |
| @@ -1115,7 +1133,7 @@ decision: |
| * @tsid: target security identifier |
| * @tclass: target security class |
| * @requested: requested permissions, interpreted based on @tclass |
| - * @flags: AVC_STRICT or 0 |
| + * @flags: AVC_STRICT, AVC_NONBLOCKING, or 0 |
| * @avd: access vector decisions |
| * |
| * Check the AVC to determine whether the @requested permissions are granted |
| @@ -1199,7 +1217,8 @@ int avc_has_perm_flags(struct selinux_st |
| struct av_decision avd; |
| int rc, rc2; |
| |
| - rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0, |
| + rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, |
| + (flags & MAY_NOT_BLOCK) ? AVC_NONBLOCKING : 0, |
| &avd); |
| |
| rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc, |
| --- a/security/selinux/hooks.c |
| +++ b/security/selinux/hooks.c |
| @@ -2985,7 +2985,9 @@ static int selinux_inode_permission(stru |
| return PTR_ERR(isec); |
| |
| rc = avc_has_perm_noaudit(&selinux_state, |
| - sid, isec->sid, isec->sclass, perms, 0, &avd); |
| + sid, isec->sid, isec->sclass, perms, |
| + (flags & MAY_NOT_BLOCK) ? AVC_NONBLOCKING : 0, |
| + &avd); |
| audited = avc_audit_required(perms, &avd, rc, |
| from_access ? FILE__AUDIT_ACCESS : 0, |
| &denied); |
| --- a/security/selinux/include/avc.h |
| +++ b/security/selinux/include/avc.h |
| @@ -142,6 +142,7 @@ static inline int avc_audit(struct selin |
| |
| #define AVC_STRICT 1 /* Ignore permissive mode. */ |
| #define AVC_EXTENDED_PERMS 2 /* update extended permissions */ |
| +#define AVC_NONBLOCKING 4 /* non blocking */ |
| int avc_has_perm_noaudit(struct selinux_state *state, |
| u32 ssid, u32 tsid, |
| u16 tclass, u32 requested, |