From 3a28cff3bd4bf43f02be0c4e7933aebf3dc8197e Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 12 Dec 2018 10:10:55 -0500 Subject: [PATCH 1/9] selinux: avoid silent denials in permissive mode under RCU walk 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 Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 23 +++++++++++++++++++++-- security/selinux/hooks.c | 4 +++- security/selinux/include/avc.h | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 635e5c1e3e48..5de18a6d5c3f 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -838,6 +838,7 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events) * @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 selinux_avc *avc, 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 @@ int avc_has_extended_perms(struct selinux_state *state, * @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_state *state, 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, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f0e36c3492ba..7f6068489a02 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2982,7 +2982,9 @@ static int selinux_inode_permission(struct inode *inode, int mask) 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); diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index ef899bcfd2cb..74ea50977c20 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -142,6 +142,7 @@ static inline int avc_audit(struct selinux_state *state, #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, From e46e01eebbbcf2ff6d28ee7cae9f117e9d1572c8 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 12 Dec 2018 10:10:56 -0500 Subject: [PATCH 2/9] selinux: stop passing MAY_NOT_BLOCK to the AVC upon follow_link commit bda0be7ad9948 ("security: make inode_follow_link RCU-walk aware") switched selinux_inode_follow_link() to use avc_has_perm_flags() and pass down the MAY_NOT_BLOCK flag if called during RCU walk. However, the only test of MAY_NOT_BLOCK occurs during slow_avc_audit() and only if passing an inode as audit data (LSM_AUDIT_DATA_INODE). Since selinux_inode_follow_link() passes a dentry directly, passing MAY_NOT_BLOCK here serves no purpose. Switch selinux_inode_follow_link() to use avc_has_perm() and drop avc_has_perm_flags() since there are no other users. Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 24 ++---------------------- security/selinux/hooks.c | 5 ++--- security/selinux/include/avc.h | 5 ----- 3 files changed, 4 insertions(+), 30 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 5de18a6d5c3f..9b63d8ee1687 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -867,9 +867,8 @@ static int avc_update_node(struct selinux_avc *avc, * 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(). + * logic in selinux_inode_permission for the MAY_NOT_BLOCK flag, + * which is transliterated into AVC_NONBLOCKING. */ if (flags & AVC_NONBLOCKING) return 0; @@ -1209,25 +1208,6 @@ int avc_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass, return rc; } -int avc_has_perm_flags(struct selinux_state *state, - u32 ssid, u32 tsid, u16 tclass, u32 requested, - struct common_audit_data *auditdata, - int flags) -{ - struct av_decision avd; - int rc, rc2; - - 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, - auditdata, flags); - if (rc2) - return rc2; - return rc; -} - u32 avc_policy_seqno(struct selinux_state *state) { return state->avc->avc_cache.latest_notif; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7f6068489a02..f08a0f201967 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2925,9 +2925,8 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, if (IS_ERR(isec)) return PTR_ERR(isec); - return avc_has_perm_flags(&selinux_state, - sid, isec->sid, isec->sclass, FILE__READ, &ad, - rcu ? MAY_NOT_BLOCK : 0); + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, FILE__READ, &ad); } static noinline int audit_inode_permission(struct inode *inode, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 74ea50977c20..7be0e1e90e8b 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -153,11 +153,6 @@ int avc_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass, u32 requested, struct common_audit_data *auditdata); -int avc_has_perm_flags(struct selinux_state *state, - u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct common_audit_data *auditdata, - int flags); int avc_has_extended_perms(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass, u32 requested, From a83d6ddaebe541570291205cb538e35ad4ff94f9 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Fri, 21 Dec 2018 21:18:52 +0100 Subject: [PATCH 3/9] selinux: never allow relabeling on context mounts In the SECURITY_FS_USE_MNTPOINT case we never want to allow relabeling files/directories, so we should never set the SBLABEL_MNT flag. The 'special handling' in selinux_is_sblabel_mnt() is only intended for when the behavior is set to SECURITY_FS_USE_GENFS. While there, make the logic in selinux_is_sblabel_mnt() more explicit and add a BUILD_BUG_ON() to make sure that introducing a new SECURITY_FS_USE_* forces a review of the logic. Fixes: d5f3a5f6e7e7 ("selinux: add security in-core xattr support for pstore and debugfs") Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f08a0f201967..f8931c5d252a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -534,16 +534,10 @@ static int may_context_mount_inode_relabel(u32 sid, return rc; } -static int selinux_is_sblabel_mnt(struct super_block *sb) +static int selinux_is_genfs_special_handling(struct super_block *sb) { - struct superblock_security_struct *sbsec = sb->s_security; - - return sbsec->behavior == SECURITY_FS_USE_XATTR || - sbsec->behavior == SECURITY_FS_USE_TRANS || - sbsec->behavior == SECURITY_FS_USE_TASK || - sbsec->behavior == SECURITY_FS_USE_NATIVE || - /* Special handling. Genfs but also in-core setxattr handler */ - !strcmp(sb->s_type->name, "sysfs") || + /* Special handling. Genfs but also in-core setxattr handler */ + return !strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "pstore") || !strcmp(sb->s_type->name, "debugfs") || !strcmp(sb->s_type->name, "tracefs") || @@ -553,6 +547,34 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) !strcmp(sb->s_type->name, "cgroup2"))); } +static int selinux_is_sblabel_mnt(struct super_block *sb) +{ + struct superblock_security_struct *sbsec = sb->s_security; + + /* + * IMPORTANT: Double-check logic in this function when adding a new + * SECURITY_FS_USE_* definition! + */ + BUILD_BUG_ON(SECURITY_FS_USE_MAX != 7); + + switch (sbsec->behavior) { + case SECURITY_FS_USE_XATTR: + case SECURITY_FS_USE_TRANS: + case SECURITY_FS_USE_TASK: + case SECURITY_FS_USE_NATIVE: + return 1; + + case SECURITY_FS_USE_GENFS: + return selinux_is_genfs_special_handling(sb); + + /* Never allow relabeling on context mounts */ + case SECURITY_FS_USE_MNTPOINT: + case SECURITY_FS_USE_NONE: + default: + return 0; + } +} + static int sb_finish_set_opts(struct super_block *sb) { struct superblock_security_struct *sbsec = sb->s_security; From 53e0c2aa9a59a48e3798ef193d573ade85aa80f5 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Fri, 21 Dec 2018 21:18:53 +0100 Subject: [PATCH 4/9] selinux: do not override context on context mounts Ignore all selinux_inode_notifysecctx() calls on mounts with SBLABEL_MNT flag unset. This is achived by returning -EOPNOTSUPP for this case in selinux_inode_setsecurtity() (because that function should not be called in such case anyway) and translating this error to 0 in selinux_inode_notifysecctx(). This fixes behavior of kernfs-based filesystems when mounted with the 'context=' option. Before this patch, if a node's context had been explicitly set to a non-default value and later the filesystem has been remounted with the 'context=' option, then this node would show up as having the manually-set context and not the mount-specified one. Steps to reproduce: # mount -t cgroup2 cgroup2 /sys/fs/cgroup/unified # chcon unconfined_u:object_r:user_home_t:s0 /sys/fs/cgroup/unified/cgroup.stat # ls -lZ /sys/fs/cgroup/unified total 0 -r--r--r--. 1 root root system_u:object_r:cgroup_t:s0 0 Dec 13 10:41 cgroup.controllers -rw-r--r--. 1 root root system_u:object_r:cgroup_t:s0 0 Dec 13 10:41 cgroup.max.depth -rw-r--r--. 1 root root system_u:object_r:cgroup_t:s0 0 Dec 13 10:41 cgroup.max.descendants -rw-r--r--. 1 root root system_u:object_r:cgroup_t:s0 0 Dec 13 10:41 cgroup.procs -r--r--r--. 1 root root unconfined_u:object_r:user_home_t:s0 0 Dec 13 10:41 cgroup.stat -rw-r--r--. 1 root root system_u:object_r:cgroup_t:s0 0 Dec 13 10:41 cgroup.subtree_control -rw-r--r--. 1 root root system_u:object_r:cgroup_t:s0 0 Dec 13 10:41 cgroup.threads # umount /sys/fs/cgroup/unified # mount -o context=system_u:object_r:tmpfs_t:s0 -t cgroup2 cgroup2 /sys/fs/cgroup/unified Result before: # ls -lZ /sys/fs/cgroup/unified total 0 -r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.controllers -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.max.depth -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.max.descendants -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.procs -r--r--r--. 1 root root unconfined_u:object_r:user_home_t:s0 0 Dec 13 10:41 cgroup.stat -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.subtree_control -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.threads Result after: # ls -lZ /sys/fs/cgroup/unified total 0 -r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.controllers -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.max.depth -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.max.descendants -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.procs -r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.stat -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.subtree_control -rw-r--r--. 1 root root system_u:object_r:tmpfs_t:s0 0 Dec 13 10:41 cgroup.threads Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f8931c5d252a..81e012c66d95 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3264,12 +3264,16 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct inode_security_struct *isec = inode_security_novalidate(inode); + struct superblock_security_struct *sbsec = inode->i_sb->s_security; u32 newsid; int rc; if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; + if (!(sbsec->flags & SBLABEL_MNT)) + return -EOPNOTSUPP; + if (!value || !size) return -EACCES; @@ -6415,7 +6419,10 @@ static void selinux_inode_invalidate_secctx(struct inode *inode) */ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) { - return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0); + int rc = selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, + ctx, ctxlen, 0); + /* Do not return error when suppressing label (SBLABEL_MNT not set). */ + return rc == -EOPNOTSUPP ? 0 : rc; } /* From a2c513835bb6c6ca660ae4ecda8ff9f676e47e55 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Fri, 25 Jan 2019 11:06:48 +0100 Subject: [PATCH 5/9] selinux: inline some AVC functions used only once avc_dump_av() and avc_dump_query() are each used only in one place. Get rid of them and open code their contents in the call sites. Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 140 +++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 82 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 9b63d8ee1687..502162eeb3a0 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -129,75 +129,6 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1); } -/** - * avc_dump_av - Display an access vector in human-readable form. - * @tclass: target security class - * @av: access vector - */ -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) -{ - const char **perms; - int i, perm; - - if (av == 0) { - audit_log_format(ab, " null"); - return; - } - - BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); - perms = secclass_map[tclass-1].perms; - - audit_log_format(ab, " {"); - i = 0; - perm = 1; - while (i < (sizeof(av) * 8)) { - if ((perm & av) && perms[i]) { - audit_log_format(ab, " %s", perms[i]); - av &= ~perm; - } - i++; - perm <<= 1; - } - - if (av) - audit_log_format(ab, " 0x%x", av); - - audit_log_format(ab, " }"); -} - -/** - * avc_dump_query - Display a SID pair and a class in human-readable form. - * @ssid: source security identifier - * @tsid: target security identifier - * @tclass: target security class - */ -static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state, - u32 ssid, u32 tsid, u16 tclass) -{ - int rc; - char *scontext; - u32 scontext_len; - - rc = security_sid_to_context(state, ssid, &scontext, &scontext_len); - if (rc) - audit_log_format(ab, "ssid=%d", ssid); - else { - audit_log_format(ab, "scontext=%s", scontext); - kfree(scontext); - } - - rc = security_sid_to_context(state, tsid, &scontext, &scontext_len); - if (rc) - audit_log_format(ab, " tsid=%d", tsid); - else { - audit_log_format(ab, " tcontext=%s", scontext); - kfree(scontext); - } - - BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); - audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); -} - /** * avc_init - Initialize the AVC. * @@ -735,11 +666,37 @@ static struct avc_node *avc_insert(struct selinux_avc *avc, static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; - audit_log_format(ab, "avc: %s ", - ad->selinux_audit_data->denied ? "denied" : "granted"); - avc_dump_av(ab, ad->selinux_audit_data->tclass, - ad->selinux_audit_data->audited); - audit_log_format(ab, " for "); + struct selinux_audit_data *sad = ad->selinux_audit_data; + u32 av = sad->audited; + const char **perms; + int i, perm; + + audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted"); + + if (av == 0) { + audit_log_string(ab, " null"); + return; + } + + BUG_ON(!sad->tclass || sad->tclass >= ARRAY_SIZE(secclass_map)); + perms = secclass_map[sad->tclass-1].perms; + + audit_log_string(ab, " {"); + i = 0; + perm = 1; + while (i < (sizeof(av) * 8)) { + if ((perm & av) && perms[i]) { + audit_log_format(ab, " %s", perms[i]); + av &= ~perm; + } + i++; + perm <<= 1; + } + + if (av) + audit_log_format(ab, " 0x%x", av); + + audit_log_string(ab, " } for "); } /** @@ -751,15 +708,34 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) static void avc_audit_post_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; - audit_log_format(ab, " "); - avc_dump_query(ab, ad->selinux_audit_data->state, - 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); + struct selinux_audit_data *sad = ad->selinux_audit_data; + char *scontext; + u32 scontext_len; + int rc; + + rc = security_sid_to_context(sad->state, sad->ssid, &scontext, + &scontext_len); + if (rc) + audit_log_format(ab, " ssid=%d", sad->ssid); + else { + audit_log_format(ab, " scontext=%s", scontext); + kfree(scontext); } + + rc = security_sid_to_context(sad->state, sad->tsid, &scontext, + &scontext_len); + if (rc) + audit_log_format(ab, " tsid=%d", sad->tsid); + else { + audit_log_format(ab, " tcontext=%s", scontext); + kfree(scontext); + } + + BUG_ON(!sad->tclass || sad->tclass >= ARRAY_SIZE(secclass_map)); + audit_log_format(ab, " tclass=%s", secclass_map[sad->tclass-1].name); + + if (sad->denied) + audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1); } /* This is the slow part of avc audit with big stack footprint */ From 994fb0651d02e49567e7550eb574981b387fc06f Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Fri, 25 Jan 2019 11:06:49 +0100 Subject: [PATCH 6/9] selinux: replace some BUG_ON()s with a WARN_ON() We don't need to crash the machine in these cases. Let's just detect the buggy state early and error out with a warning. Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 502162eeb3a0..5ebad47391c9 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -678,7 +678,6 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) return; } - BUG_ON(!sad->tclass || sad->tclass >= ARRAY_SIZE(secclass_map)); perms = secclass_map[sad->tclass-1].perms; audit_log_string(ab, " {"); @@ -731,7 +730,6 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) kfree(scontext); } - BUG_ON(!sad->tclass || sad->tclass >= ARRAY_SIZE(secclass_map)); audit_log_format(ab, " tclass=%s", secclass_map[sad->tclass-1].name); if (sad->denied) @@ -748,6 +746,9 @@ noinline int slow_avc_audit(struct selinux_state *state, struct common_audit_data stack_data; struct selinux_audit_data sad; + if (WARN_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map))) + return -EINVAL; + if (!a) { a = &stack_data; a->type = LSM_AUDIT_DATA_NONE; From fede148324c34360ce8c30a9a5bdfac5574b2a59 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Fri, 25 Jan 2019 11:06:51 +0100 Subject: [PATCH 7/9] selinux: log invalid contexts in AVCs In case a file has an invalid context set, in an AVC record generated upon access to such file, the target context is always reported as unlabeled. This patch adds new optional fields to the AVC record (srawcon and trawcon) that report the actual context string if it differs from the one reported in scontext/tcontext. This is useful for diagnosing SELinux denials involving invalid contexts. To trigger an AVC that illustrates this situation: # setenforce 0 # touch /tmp/testfile # setfattr -n security.selinux -v system_u:object_r:banana_t:s0 /tmp/testfile # runcon system_u:system_r:sshd_t:s0 cat /tmp/testfile AVC before: type=AVC msg=audit(1547801083.248:11): avc: denied { open } for pid=1149 comm="cat" path="/tmp/testfile" dev="tmpfs" ino=6608 scontext=system_u:system_r:sshd_t:s0 tcontext=system_u:object_r:unlabeled_t:s15:c0.c1023 tclass=file permissive=1 AVC after: type=AVC msg=audit(1547801083.248:11): avc: denied { open } for pid=1149 comm="cat" path="/tmp/testfile" dev="tmpfs" ino=6608 scontext=system_u:system_r:sshd_t:s0 tcontext=system_u:object_r:unlabeled_t:s15:c0.c1023 tclass=file permissive=1 trawcon=system_u:object_r:banana_t:s0 Note that it is also possible to encounter this situation with the 'scontext' field - e.g. when a new policy is loaded while a process is running, whose context is not valid in the new policy. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1135683 Cc: Daniel Walsh Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 15 ++++++++++++ security/selinux/include/security.h | 3 +++ security/selinux/ss/services.c | 37 +++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 5ebad47391c9..3a27418b20d7 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -734,6 +734,21 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) if (sad->denied) audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1); + + /* in case of invalid context report also the actual context string */ + rc = security_sid_to_context_inval(sad->state, sad->ssid, &scontext, + &scontext_len); + if (!rc && scontext) { + audit_log_format(ab, " srawcon=%s", scontext); + kfree(scontext); + } + + rc = security_sid_to_context_inval(sad->state, sad->tsid, &scontext, + &scontext_len); + if (!rc && scontext) { + audit_log_format(ab, " trawcon=%s", scontext); + kfree(scontext); + } } /* This is the slow part of avc audit with big stack footprint */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ba8eedf42b90..f68fb25b5702 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -255,6 +255,9 @@ int security_sid_to_context(struct selinux_state *state, u32 sid, int security_sid_to_context_force(struct selinux_state *state, u32 sid, char **scontext, u32 *scontext_len); +int security_sid_to_context_inval(struct selinux_state *state, + u32 sid, char **scontext, u32 *scontext_len); + int security_context_to_sid(struct selinux_state *state, const char *scontext, u32 scontext_len, u32 *out_sid, gfp_t gfp); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dd44126c8d14..9be05c3e99dc 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1281,7 +1281,8 @@ const char *security_get_initial_sid_context(u32 sid) static int security_sid_to_context_core(struct selinux_state *state, u32 sid, char **scontext, - u32 *scontext_len, int force) + u32 *scontext_len, int force, + int only_invalid) { struct policydb *policydb; struct sidtab *sidtab; @@ -1326,8 +1327,14 @@ static int security_sid_to_context_core(struct selinux_state *state, rc = -EINVAL; goto out_unlock; } - rc = context_struct_to_string(policydb, context, scontext, - scontext_len); + if (only_invalid && !context->len) { + scontext = NULL; + scontext_len = 0; + rc = 0; + } else { + rc = context_struct_to_string(policydb, context, scontext, + scontext_len); + } out_unlock: read_unlock(&state->ss->policy_rwlock); out: @@ -1349,14 +1356,34 @@ int security_sid_to_context(struct selinux_state *state, u32 sid, char **scontext, u32 *scontext_len) { return security_sid_to_context_core(state, sid, scontext, - scontext_len, 0); + scontext_len, 0, 0); } int security_sid_to_context_force(struct selinux_state *state, u32 sid, char **scontext, u32 *scontext_len) { return security_sid_to_context_core(state, sid, scontext, - scontext_len, 1); + scontext_len, 1, 0); +} + +/** + * security_sid_to_context_inval - Obtain a context for a given SID if it + * is invalid. + * @sid: security identifier, SID + * @scontext: security context + * @scontext_len: length in bytes + * + * Write the string representation of the context associated with @sid + * into a dynamically allocated string of the correct size, but only if the + * context is invalid in the current policy. Set @scontext to point to + * this string (or NULL if the context is valid) and set @scontext_len to + * the length of the string (or 0 if the context is valid). + */ +int security_sid_to_context_inval(struct selinux_state *state, u32 sid, + char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(state, sid, scontext, + scontext_len, 1, 1); } /* From e6f2f381e4015386a656a369835f949c26000e6b Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Mon, 28 Jan 2019 16:43:33 +0100 Subject: [PATCH 8/9] selinux: replace BUG_ONs with WARN_ONs in avc.c These checks are only guarding against programming errors that could silently grant too many permissions. These cases are better handled with WARN_ON(), since it doesn't really help much to crash the machine in this case. Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 3a27418b20d7..33863298a9b5 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -1059,7 +1059,8 @@ int avc_has_extended_perms(struct selinux_state *state, int rc = 0, rc2; xp_node = &local_xp_node; - BUG_ON(!requested); + if (WARN_ON(!requested)) + return -EACCES; rcu_read_lock(); @@ -1149,7 +1150,8 @@ inline int avc_has_perm_noaudit(struct selinux_state *state, int rc = 0; u32 denied; - BUG_ON(!requested); + if (WARN_ON(!requested)) + return -EACCES; rcu_read_lock(); From 45189a1998e00f6375ebd49d1e18161acddd73de Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 5 Feb 2019 11:49:32 -0500 Subject: [PATCH 9/9] selinux: fix avc audit messages commit a2c513835bb6c6 ("selinux: inline some AVC functions used only once") introduced usage of audit_log_string() in place of audit_log_format() for fixed strings. However, audit_log_string() quotes the string. This breaks the avc audit message format and userspace audit parsers. Switch back to using audit_log_format(). Fixes: a2c513835bb6c6 ("selinux: inline some AVC functions used only once") Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/avc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 33863298a9b5..8346a4f7c5d7 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -674,13 +674,13 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted"); if (av == 0) { - audit_log_string(ab, " null"); + audit_log_format(ab, " null"); return; } perms = secclass_map[sad->tclass-1].perms; - audit_log_string(ab, " {"); + audit_log_format(ab, " {"); i = 0; perm = 1; while (i < (sizeof(av) * 8)) { @@ -695,7 +695,7 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) if (av) audit_log_format(ab, " 0x%x", av); - audit_log_string(ab, " } for "); + audit_log_format(ab, " } for "); } /**