[PATCH] support for context based audit filtering
[linux-2.6.git] / security / selinux / ss / services.c
index 6149248..7177e98 100644 (file)
@@ -7,12 +7,13 @@
  * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
  *
  *     Support for enhanced MLS infrastructure.
+ *     Support for context based audit filters.
  *
  * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
  *
  *     Added conditional policy language extensions
  *
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
  * Copyright (C) 2003 - 2004 Tresys Technology, LLC
  * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  *     This program is free software; you can redistribute it and/or modify
@@ -1811,3 +1812,235 @@ out:
        POLICY_RDUNLOCK;
        return rc;
 }
+
+struct selinux_audit_rule {
+       u32 au_seqno;
+       struct context au_ctxt;
+};
+
+void selinux_audit_rule_free(struct selinux_audit_rule *rule)
+{
+       if (rule) {
+               context_destroy(&rule->au_ctxt);
+               kfree(rule);
+       }
+}
+
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
+                            struct selinux_audit_rule **rule)
+{
+       struct selinux_audit_rule *tmprule;
+       struct role_datum *roledatum;
+       struct type_datum *typedatum;
+       struct user_datum *userdatum;
+       int rc = 0;
+
+       *rule = NULL;
+
+       if (!ss_initialized)
+               return -ENOTSUPP;
+
+       switch (field) {
+       case AUDIT_SE_USER:
+       case AUDIT_SE_ROLE:
+       case AUDIT_SE_TYPE:
+               /* only 'equals' and 'not equals' fit user, role, and type */
+               if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
+                       return -EINVAL;
+               break;
+       case AUDIT_SE_SEN:
+       case AUDIT_SE_CLR:
+               /* we do not allow a range, indicated by the presense of '-' */
+               if (strchr(rulestr, '-'))
+                       return -EINVAL;
+               break;
+       default:
+               /* only the above fields are valid */
+               return -EINVAL;
+       }
+
+       tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
+       if (!tmprule)
+               return -ENOMEM;
+
+       context_init(&tmprule->au_ctxt);
+
+       POLICY_RDLOCK;
+
+       tmprule->au_seqno = latest_granting;
+
+       switch (field) {
+       case AUDIT_SE_USER:
+               userdatum = hashtab_search(policydb.p_users.table, rulestr);
+               if (!userdatum)
+                       rc = -EINVAL;
+               else
+                       tmprule->au_ctxt.user = userdatum->value;
+               break;
+       case AUDIT_SE_ROLE:
+               roledatum = hashtab_search(policydb.p_roles.table, rulestr);
+               if (!roledatum)
+                       rc = -EINVAL;
+               else
+                       tmprule->au_ctxt.role = roledatum->value;
+               break;
+       case AUDIT_SE_TYPE:
+               typedatum = hashtab_search(policydb.p_types.table, rulestr);
+               if (!typedatum)
+                       rc = -EINVAL;
+               else
+                       tmprule->au_ctxt.type = typedatum->value;
+               break;
+       case AUDIT_SE_SEN:
+       case AUDIT_SE_CLR:
+               rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
+               break;
+       }
+
+       POLICY_RDUNLOCK;
+
+       if (rc) {
+               selinux_audit_rule_free(tmprule);
+               tmprule = NULL;
+       }
+
+       *rule = tmprule;
+
+       return rc;
+}
+
+int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op,
+                             struct selinux_audit_rule *rule,
+                             struct audit_context *actx)
+{
+       struct context *ctxt;
+       struct mls_level *level;
+       int match = 0;
+
+       if (!rule) {
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "selinux_audit_rule_match: missing rule\n");
+               return -ENOENT;
+       }
+
+       POLICY_RDLOCK;
+
+       if (rule->au_seqno < latest_granting) {
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "selinux_audit_rule_match: stale rule\n");
+               match = -ESTALE;
+               goto out;
+       }
+
+       ctxt = sidtab_search(&sidtab, ctxid);
+       if (!ctxt) {
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "selinux_audit_rule_match: unrecognized SID %d\n",
+                         ctxid);
+               match = -ENOENT;
+               goto out;
+       }
+
+       /* a field/op pair that is not caught here will simply fall through
+          without a match */
+       switch (field) {
+       case AUDIT_SE_USER:
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = (ctxt->user == rule->au_ctxt.user);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = (ctxt->user != rule->au_ctxt.user);
+                       break;
+               }
+               break;
+       case AUDIT_SE_ROLE:
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = (ctxt->role == rule->au_ctxt.role);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = (ctxt->role != rule->au_ctxt.role);
+                       break;
+               }
+               break;
+       case AUDIT_SE_TYPE:
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = (ctxt->type == rule->au_ctxt.type);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = (ctxt->type != rule->au_ctxt.type);
+                       break;
+               }
+               break;
+       case AUDIT_SE_SEN:
+       case AUDIT_SE_CLR:
+               level = (op == AUDIT_SE_SEN ?
+                        &ctxt->range.level[0] : &ctxt->range.level[1]);
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = mls_level_eq(&rule->au_ctxt.range.level[0],
+                                            level);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = !mls_level_eq(&rule->au_ctxt.range.level[0],
+                                             level);
+                       break;
+               case AUDIT_LESS_THAN:
+                       match = (mls_level_dom(&rule->au_ctxt.range.level[0],
+                                              level) &&
+                                !mls_level_eq(&rule->au_ctxt.range.level[0],
+                                              level));
+                       break;
+               case AUDIT_LESS_THAN_OR_EQUAL:
+                       match = mls_level_dom(&rule->au_ctxt.range.level[0],
+                                             level);
+                       break;
+               case AUDIT_GREATER_THAN:
+                       match = (mls_level_dom(level,
+                                             &rule->au_ctxt.range.level[0]) &&
+                                !mls_level_eq(level,
+                                              &rule->au_ctxt.range.level[0]));
+                       break;
+               case AUDIT_GREATER_THAN_OR_EQUAL:
+                       match = mls_level_dom(level,
+                                             &rule->au_ctxt.range.level[0]);
+                       break;
+               }
+       }
+
+out:
+       POLICY_RDUNLOCK;
+       return match;
+}
+
+static int (*aurule_callback)(void) = NULL;
+
+static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
+                               u16 class, u32 perms, u32 *retained)
+{
+       int err = 0;
+
+       if (event == AVC_CALLBACK_RESET && aurule_callback)
+               err = aurule_callback();
+       return err;
+}
+
+static int __init aurule_init(void)
+{
+       int err;
+
+       err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET,
+                              SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+       if (err)
+               panic("avc_add_callback() failed, error %d\n", err);
+
+       return err;
+}
+__initcall(aurule_init);
+
+void selinux_audit_set_callback(int (*callback)(void))
+{
+       aurule_callback = callback;
+}