diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 8ee42b2a5f19..1a04247e3a17 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -458,7 +458,7 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) } /* This is the slow part of avc audit with big stack footprint */ -static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, +noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, u32 audited, u32 denied, struct common_audit_data *a, unsigned flags) @@ -496,67 +496,6 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, return 0; } -/** - * avc_audit - Audit the granting or denial of permissions. - * @ssid: source security identifier - * @tsid: target security identifier - * @tclass: target security class - * @requested: requested permissions - * @avd: access vector decisions - * @result: result from avc_has_perm_noaudit - * @a: auxiliary audit data - * @flags: VFS walk flags - * - * Audit the granting or denial of permissions in accordance - * with the policy. This function is typically called by - * avc_has_perm() after a permission check, but can also be - * called directly by callers who use avc_has_perm_noaudit() - * in order to separate the permission check from the auditing. - * For example, this separation is useful when the permission check must - * be performed under a lock, to allow the lock to be released - * before calling the auditing code. - */ -inline int avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, int result, struct common_audit_data *a, - unsigned flags) -{ - u32 denied, audited; - denied = requested & ~avd->allowed; - if (unlikely(denied)) { - audited = denied & avd->auditdeny; - /* - * a->selinux_audit_data->auditdeny is TRICKY! Setting a bit in - * this field means that ANY denials should NOT be audited if - * the policy contains an explicit dontaudit rule for that - * permission. Take notice that this is unrelated to the - * actual permissions that were denied. As an example lets - * assume: - * - * denied == READ - * avd.auditdeny & ACCESS == 0 (not set means explicit rule) - * selinux_audit_data->auditdeny & ACCESS == 1 - * - * We will NOT audit the denial even though the denied - * permission was READ and the auditdeny checks were for - * ACCESS - */ - if (a && - a->selinux_audit_data->auditdeny && - !(a->selinux_audit_data->auditdeny & avd->auditdeny)) - audited = 0; - } else if (result) - audited = denied = requested; - else - audited = requested & avd->auditallow; - if (likely(!audited)) - return 0; - - return slow_avc_audit(ssid, tsid, tclass, - requested, audited, denied, - a, flags); -} - /** * avc_add_callback - Register a callback for security events. * @callback: callback function diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c3ee902306d8..c99027dc0b36 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2684,6 +2684,11 @@ static int selinux_inode_permission(struct inode *inode, int mask) u32 perms; bool from_access; unsigned flags = mask & MAY_NOT_BLOCK; + struct inode_security_struct *isec; + u32 sid; + struct av_decision avd; + int rc, rc2; + u32 audited, denied; from_access = mask & MAY_ACCESS; mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); @@ -2692,6 +2697,23 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (!mask) return 0; + validate_creds(cred); + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + perms = file_mask_to_av(inode->i_mode, mask); + + sid = cred_sid(cred); + isec = inode->i_security; + + rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); + audited = avc_audit_required(perms, &avd, rc, + from_access ? FILE__AUDIT_ACCESS : 0, + &denied); + if (likely(!audited)) + return rc; + COMMON_AUDIT_DATA_INIT(&ad, INODE); ad.selinux_audit_data = &sad; ad.u.inode = inode; @@ -2699,9 +2721,11 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (from_access) ad.selinux_audit_data->auditdeny |= FILE__AUDIT_ACCESS; - perms = file_mask_to_av(inode->i_mode, mask); - - return inode_has_perm(cred, inode, perms, &ad, flags); + rc2 = slow_avc_audit(sid, isec->sid, isec->sclass, perms, + audited, denied, &ad, flags); + if (rc2) + return rc2; + return rc; } static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 1931370233d7..e4e50bb218ee 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -77,11 +77,83 @@ struct selinux_audit_data { void __init avc_init(void); -int avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, - int result, - struct common_audit_data *a, unsigned flags); +static inline u32 avc_audit_required(u32 requested, + struct av_decision *avd, + int result, + u32 auditdeny, + u32 *deniedp) +{ + u32 denied, audited; + denied = requested & ~avd->allowed; + if (unlikely(denied)) { + audited = denied & avd->auditdeny; + /* + * auditdeny is TRICKY! Setting a bit in + * this field means that ANY denials should NOT be audited if + * the policy contains an explicit dontaudit rule for that + * permission. Take notice that this is unrelated to the + * actual permissions that were denied. As an example lets + * assume: + * + * denied == READ + * avd.auditdeny & ACCESS == 0 (not set means explicit rule) + * auditdeny & ACCESS == 1 + * + * We will NOT audit the denial even though the denied + * permission was READ and the auditdeny checks were for + * ACCESS + */ + if (auditdeny && !(auditdeny & avd->auditdeny)) + audited = 0; + } else if (result) + audited = denied = requested; + else + audited = requested & avd->auditallow; + *deniedp = denied; + return audited; +} + +int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, + u32 requested, u32 audited, u32 denied, + struct common_audit_data *a, + unsigned flags); + +/** + * avc_audit - Audit the granting or denial of permissions. + * @ssid: source security identifier + * @tsid: target security identifier + * @tclass: target security class + * @requested: requested permissions + * @avd: access vector decisions + * @result: result from avc_has_perm_noaudit + * @a: auxiliary audit data + * @flags: VFS walk flags + * + * Audit the granting or denial of permissions in accordance + * with the policy. This function is typically called by + * avc_has_perm() after a permission check, but can also be + * called directly by callers who use avc_has_perm_noaudit() + * in order to separate the permission check from the auditing. + * For example, this separation is useful when the permission check must + * be performed under a lock, to allow the lock to be released + * before calling the auditing code. + */ +static inline int avc_audit(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct av_decision *avd, + int result, + struct common_audit_data *a, unsigned flags) +{ + u32 audited, denied; + audited = avc_audit_required(requested, avd, result, + a ? a->selinux_audit_data->auditdeny : 0, + &denied); + if (likely(!audited)) + return 0; + return slow_avc_audit(ssid, tsid, tclass, + requested, audited, denied, + a, flags); +} #define AVC_STRICT 1 /* Ignore permissive mode. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid,