[PATCH 3.16 023/129] selinux: avoid silent denials in permissive mode under RCU walk

From: Ben Hutchings
Date: Sun Jul 07 2019 - 15:46:39 EST


3.16.70-rc1 review patch. If anyone has any objections, please let me know.

------------------

From: Stephen Smalley <sds@xxxxxxxxxxxxx>

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@xxxxxxxxx>
Signed-off-by: Stephen Smalley <sds@xxxxxxxxxxxxx>
Signed-off-by: Paul Moore <paul@xxxxxxxxxxxxxx>
[bwh: Backported to 3.16:
- Add flags parameter to avc_update_node(), done upstream in commit
fa1aa143ac4a "selinux: extended permissions for ioctls"
- Adjust context]
Signed-off-by: Ben Hutchings <ben@xxxxxxxxxxxxxxx>
---
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -528,6 +528,7 @@ static inline int avc_sidcmp(u32 x, u32
* @perms : Permission mask bits
* @ssid,@tsid,@tclass : identifier of an AVC entry
* @seqno : sequence number when decision was made
+ * @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.
@@ -535,7 +536,7 @@ static inline int avc_sidcmp(u32 x, u32
* will release later by RCU.
*/
static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
- u32 seqno)
+ u32 seqno, unsigned int flags)
{
int hvalue, rc = 0;
unsigned long flag;
@@ -543,6 +544,23 @@ static int avc_update_node(u32 event, u3
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();
if (!node) {
rc = -ENOMEM;
@@ -690,7 +708,7 @@ static noinline int avc_denied(u32 ssid,
return -EACCES;

avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
- tsid, tclass, avd->seqno);
+ tsid, tclass, avd->seqno, flags);
return 0;
}

@@ -701,7 +719,7 @@ static noinline int avc_denied(u32 ssid,
* @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
@@ -781,7 +799,9 @@ int avc_has_perm_flags(u32 ssid, u32 tsi
struct av_decision avd;
int rc, rc2;

- rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
+ rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested,
+ (flags & MAY_NOT_BLOCK) ? AVC_NONBLOCKING : 0,
+ &avd);

rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc,
auditdata, flags);
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2818,7 +2818,9 @@ static int selinux_inode_permission(stru
sid = cred_sid(cred);
isec = inode->i_security;

- rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
+ rc = avc_has_perm_noaudit(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
@@ -143,6 +143,7 @@ static inline int avc_audit(u32 ssid, u3
}

#define AVC_STRICT 1 /* Ignore permissive mode. */
+#define AVC_NONBLOCKING 4 /* non blocking */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,