]> Pileus Git - ~andy/linux/commitdiff
SELinux: delay initialization of audit data in selinux_inode_permission
authorEric Paris <eparis@redhat.com>
Wed, 4 Apr 2012 19:01:42 +0000 (15:01 -0400)
committerEric Paris <eparis@redhat.com>
Mon, 9 Apr 2012 16:22:59 +0000 (12:22 -0400)
We pay a rather large overhead initializing the common_audit_data.
Since we only need this information if we actually emit an audit
message there is little need to set it up in the hot path.  This patch
splits the functionality of avc_has_perm() into avc_has_perm_noaudit(),
avc_audit_required() and slow_avc_audit().  But we take care of setting
up to audit between required() and the actual audit call.  Thus saving
measurable time in a hot path.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: Eric Paris <eparis@redhat.com>
security/selinux/avc.c
security/selinux/hooks.c
security/selinux/include/avc.h

index 8ee42b2a5f19d0b93c1d6fe4fbe3cdda3670376c..1a04247e3a173864f32b921d5988a41b7820b589 100644 (file)
@@ -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
index c3ee902306d876b72f148fa6984fb1adf6e88728..c99027dc0b36f7c7033e21f493231b237df14a23 100644 (file)
@@ -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)
index 1931370233d7b2123dad2947c930c0ecb9722ac5..e4e50bb218ee42f81b2e3583bae85d293f976067 100644 (file)
@@ -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,