summaryrefslogtreecommitdiffstats
path: root/security/selinux
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/avc.c63
-rw-r--r--security/selinux/hooks.c30
-rw-r--r--security/selinux/include/avc.h82
3 files changed, 105 insertions, 70 deletions
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)
@@ -497,67 +497,6 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
}
/**
- * 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
* @events: security events
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,