Add audit messages on type boundary violations
authorKaiGai Kohei <kaigai@ak.jp.nec.com>
Thu, 18 Jun 2009 08:26:13 +0000 (17:26 +0900)
committerJames Morris <jmorris@namei.org>
Thu, 18 Jun 2009 14:12:28 +0000 (00:12 +1000)
The attached patch adds support to generate audit messages on two cases.

The first one is a case when a multi-thread process tries to switch its
performing security context using setcon(3), but new security context is
not bounded by the old one.

  type=SELINUX_ERR msg=audit(1245311998.599:17):        \
      op=security_bounded_transition result=denied      \
      oldcontext=system_u:system_r:httpd_t:s0           \
      newcontext=system_u:system_r:guest_webapp_t:s0

The other one is a case when security_compute_av() masked any permissions
due to the type boundary violation.

  type=SELINUX_ERR msg=audit(1245312836.035:32): \
      op=security_compute_av reason=bounds              \
      scontext=system_u:object_r:user_webapp_t:s0       \
      tcontext=system_u:object_r:shadow_t:s0:c0         \
      tclass=file perms=getattr,open

Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
security/selinux/avc.c
security/selinux/include/avc.h
security/selinux/ss/services.c

index 7f9b5fac87793a19faf3d310a4e04f8a158bb476..4bf5d08a1f5c721efb46e9035429f7bf67e91f02 100644 (file)
@@ -137,7 +137,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
  * @tclass: target security class
  * @av: access vector
  */
-void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
 {
        const char **common_pts = NULL;
        u32 common_base = 0;
index d12ff1a9c0aa5e383347af29f9f03fe96e626ed2..46a940d9af6738802b0923ddb9299ea0adc6ea15 100644 (file)
@@ -127,9 +127,6 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
                     u32 events, u32 ssid, u32 tsid,
                     u16 tclass, u32 perms);
 
-/* Shows permission in human readable form */
-void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
-
 /* Exported to selinuxfs */
 int avc_get_hash_stats(char *page);
 extern unsigned int avc_cache_threshold;
index cad5765ca1cbb10a48523f9a51f48d75e1aee039..a90cab207d9aea5bca459832f61cbbd2e54dde57 100644 (file)
  *
  *  Added validation of kernel classes and permissions
  *
+ * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ *  Added support for bounds domain and audit messaged on masked permissions
+ *
+ * Copyright (C) 2008, 2009 NEC Corporation
  * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
  * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
@@ -278,6 +283,95 @@ mls_ops:
        return s[0];
 }
 
+/*
+ * security_dump_masked_av - dumps masked permissions during
+ * security_compute_av due to RBAC, MLS/Constraint and Type bounds.
+ */
+static int dump_masked_av_helper(void *k, void *d, void *args)
+{
+       struct perm_datum *pdatum = d;
+       char **permission_names = args;
+
+       BUG_ON(pdatum->value < 1 || pdatum->value > 32);
+
+       permission_names[pdatum->value - 1] = (char *)k;
+
+       return 0;
+}
+
+static void security_dump_masked_av(struct context *scontext,
+                                   struct context *tcontext,
+                                   u16 tclass,
+                                   u32 permissions,
+                                   const char *reason)
+{
+       struct common_datum *common_dat;
+       struct class_datum *tclass_dat;
+       struct audit_buffer *ab;
+       char *tclass_name;
+       char *scontext_name = NULL;
+       char *tcontext_name = NULL;
+       char *permission_names[32];
+       int index, length;
+       bool need_comma = false;
+
+       if (!permissions)
+               return;
+
+       tclass_name = policydb.p_class_val_to_name[tclass - 1];
+       tclass_dat = policydb.class_val_to_struct[tclass - 1];
+       common_dat = tclass_dat->comdatum;
+
+       /* init permission_names */
+       if (common_dat &&
+           hashtab_map(common_dat->permissions.table,
+                       dump_masked_av_helper, permission_names) < 0)
+               goto out;
+
+       if (hashtab_map(tclass_dat->permissions.table,
+                       dump_masked_av_helper, permission_names) < 0)
+               goto out;
+
+       /* get scontext/tcontext in text form */
+       if (context_struct_to_string(scontext,
+                                    &scontext_name, &length) < 0)
+               goto out;
+
+       if (context_struct_to_string(tcontext,
+                                    &tcontext_name, &length) < 0)
+               goto out;
+
+       /* audit a message */
+       ab = audit_log_start(current->audit_context,
+                            GFP_ATOMIC, AUDIT_SELINUX_ERR);
+       if (!ab)
+               goto out;
+
+       audit_log_format(ab, "op=security_compute_av reason=%s "
+                        "scontext=%s tcontext=%s tclass=%s perms=",
+                        reason, scontext_name, tcontext_name, tclass_name);
+
+       for (index = 0; index < 32; index++) {
+               u32 mask = (1 << index);
+
+               if ((mask & permissions) == 0)
+                       continue;
+
+               audit_log_format(ab, "%s%s",
+                                need_comma ? "," : "",
+                                permission_names[index]
+                                ? permission_names[index] : "????");
+               need_comma = true;
+       }
+       audit_log_end(ab);
+out:
+       /* release scontext/tcontext */
+       kfree(tcontext_name);
+       kfree(scontext_name);
+
+       return;
+}
+
 /*
  * security_boundary_permission - drops violated permissions
  * on boundary constraint.
@@ -347,28 +441,12 @@ static void type_attribute_bounds_av(struct context *scontext,
        }
 
        if (masked) {
-               struct audit_buffer *ab;
-               char *stype_name
-                       = policydb.p_type_val_to_name[source->value - 1];
-               char *ttype_name
-                       = policydb.p_type_val_to_name[target->value - 1];
-               char *tclass_name
-                       = policydb.p_class_val_to_name[tclass - 1];
-
                /* mask violated permissions */
                avd->allowed &= ~masked;
 
-               /* notice to userspace via audit message */
-               ab = audit_log_start(current->audit_context,
-                                    GFP_ATOMIC, AUDIT_SELINUX_ERR);
-               if (!ab)
-                       return;
-
-               audit_log_format(ab, "av boundary violation: "
-                                "source=%s target=%s tclass=%s",
-                                stype_name, ttype_name, tclass_name);
-               avc_dump_av(ab, tclass, masked);
-               audit_log_end(ab);
+               /* audit masked permissions */
+               security_dump_masked_av(scontext, tcontext,
+                                       tclass, masked, "bounds");
        }
 }
 
@@ -711,6 +789,26 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
                }
                index = type->bounds;
        }
+
+       if (rc) {
+               char *old_name = NULL;
+               char *new_name = NULL;
+               int length;
+
+               if (!context_struct_to_string(old_context,
+                                             &old_name, &length) &&
+                   !context_struct_to_string(new_context,
+                                             &new_name, &length)) {
+                       audit_log(current->audit_context,
+                                 GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                                 "op=security_bounded_transition "
+                                 "result=denied "
+                                 "oldcontext=%s newcontext=%s",
+                                 old_name, new_name);
+               }
+               kfree(new_name);
+               kfree(old_name);
+       }
 out:
        read_unlock(&policy_rwlock);