Commit 3a28cff3 authored by Stephen Smalley's avatar Stephen Smalley Committed by Paul Moore

selinux: avoid silent denials in permissive mode under RCU walk

commit 0dc1ba24 ("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: 0dc1ba24 ("SELINUX: Make selinux cache VFS RCU walks safe")
Reported-by: default avatarBMK <bmktuwien@gmail.com>
Signed-off-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent bfeffd15
...@@ -838,6 +838,7 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events) ...@@ -838,6 +838,7 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events)
* @ssid,@tsid,@tclass : identifier of an AVC entry * @ssid,@tsid,@tclass : identifier of an AVC entry
* @seqno : sequence number when decision was made * @seqno : sequence number when decision was made
* @xpd: extended_perms_decision to be added to the node * @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 a valid AVC entry doesn't exist,this function returns -ENOENT.
* if kmalloc() called internal returns NULL, this function returns -ENOMEM. * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
...@@ -856,6 +857,23 @@ static int avc_update_node(struct selinux_avc *avc, ...@@ -856,6 +857,23 @@ static int avc_update_node(struct selinux_avc *avc,
struct hlist_head *head; struct hlist_head *head;
spinlock_t *lock; 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); node = avc_alloc_node(avc);
if (!node) { if (!node) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -1115,7 +1133,7 @@ int avc_has_extended_perms(struct selinux_state *state, ...@@ -1115,7 +1133,7 @@ int avc_has_extended_perms(struct selinux_state *state,
* @tsid: target security identifier * @tsid: target security identifier
* @tclass: target security class * @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass * @requested: requested permissions, interpreted based on @tclass
* @flags: AVC_STRICT or 0 * @flags: AVC_STRICT, AVC_NONBLOCKING, or 0
* @avd: access vector decisions * @avd: access vector decisions
* *
* Check the AVC to determine whether the @requested permissions are granted * Check the AVC to determine whether the @requested permissions are granted
...@@ -1199,7 +1217,8 @@ int avc_has_perm_flags(struct selinux_state *state, ...@@ -1199,7 +1217,8 @@ int avc_has_perm_flags(struct selinux_state *state,
struct av_decision avd; struct av_decision avd;
int rc, rc2; 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); &avd);
rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc, rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
......
...@@ -2982,7 +2982,9 @@ static int selinux_inode_permission(struct inode *inode, int mask) ...@@ -2982,7 +2982,9 @@ static int selinux_inode_permission(struct inode *inode, int mask)
return PTR_ERR(isec); return PTR_ERR(isec);
rc = avc_has_perm_noaudit(&selinux_state, 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, audited = avc_audit_required(perms, &avd, rc,
from_access ? FILE__AUDIT_ACCESS : 0, from_access ? FILE__AUDIT_ACCESS : 0,
&denied); &denied);
......
...@@ -142,6 +142,7 @@ static inline int avc_audit(struct selinux_state *state, ...@@ -142,6 +142,7 @@ static inline int avc_audit(struct selinux_state *state,
#define AVC_STRICT 1 /* Ignore permissive mode. */ #define AVC_STRICT 1 /* Ignore permissive mode. */
#define AVC_EXTENDED_PERMS 2 /* update extended permissions */ #define AVC_EXTENDED_PERMS 2 /* update extended permissions */
#define AVC_NONBLOCKING 4 /* non blocking */
int avc_has_perm_noaudit(struct selinux_state *state, int avc_has_perm_noaudit(struct selinux_state *state,
u32 ssid, u32 tsid, u32 ssid, u32 tsid,
u16 tclass, u32 requested, u16 tclass, u32 requested,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment