selinux_audit_rule_free(f->se_rule);
                }
        kfree(e->rule.fields);
+       kfree(e->rule.filterkey);
        kfree(e);
 }
 
                        if (err)
                                goto exit_free;
                        break;
+               case AUDIT_FILTERKEY:
+                       err = -EINVAL;
+                       if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN)
+                               goto exit_free;
+                       str = audit_unpack_string(&bufp, &remain, f->val);
+                       if (IS_ERR(str))
+                               goto exit_free;
+                       entry->rule.buflen += f->val;
+                       entry->rule.filterkey = str;
+                       break;
                default:
                        goto exit_free;
                }
                        data->buflen += data->values[i] =
                                audit_pack_string(&bufp, krule->watch->path);
                        break;
+               case AUDIT_FILTERKEY:
+                       data->buflen += data->values[i] =
+                               audit_pack_string(&bufp, krule->filterkey);
+                       break;
                default:
                        data->values[i] = f->val;
                }
                        if (strcmp(a->watch->path, b->watch->path))
                                return 1;
                        break;
+               case AUDIT_FILTERKEY:
+                       /* both filterkeys exist based on above type compare */
+                       if (strcmp(a->filterkey, b->filterkey))
+                               return 1;
+                       break;
                default:
                        if (a->fields[i].val != b->fields[i].val)
                                return 1;
        u32 fcount = old->field_count;
        struct audit_entry *entry;
        struct audit_krule *new;
+       char *fk;
        int i, err = 0;
 
        entry = audit_init_entry(fcount);
                case AUDIT_SE_CLR:
                        err = audit_dupe_selinux_field(&new->fields[i],
                                                       &old->fields[i]);
+                       break;
+               case AUDIT_FILTERKEY:
+                       fk = kstrdup(old->filterkey, GFP_KERNEL);
+                       if (unlikely(!fk))
+                               err = -ENOMEM;
+                       else
+                               new->filterkey = fk;
                }
                if (err) {
                        audit_free_rule(entry);
                skb_queue_tail(q, skb);
 }
 
+/* Log rule additions and removals */
+static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action,
+                                 struct audit_krule *rule, int res)
+{
+       struct audit_buffer *ab;
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+       if (!ab)
+               return;
+       audit_log_format(ab, "auid=%u", loginuid);
+       if (sid) {
+               char *ctx = NULL;
+               u32 len;
+               if (selinux_ctxid_to_string(sid, &ctx, &len))
+                       audit_log_format(ab, " ssid=%u", sid);
+               else
+                       audit_log_format(ab, " subj=%s", ctx);
+               kfree(ctx);
+       }
+       audit_log_format(ab, " %s rule key=", action);
+       if (rule->filterkey)
+               audit_log_untrustedstring(ab, rule->filterkey);
+       else
+               audit_log_format(ab, "(null)");
+       audit_log_format(ab, " list=%d res=%d", rule->listnr, res);
+       audit_log_end(ab);
+}
+
 /**
  * audit_receive_filter - apply all rules to the specified message type
  * @type: audit message type
 
                err = audit_add_rule(entry,
                                     &audit_filter_list[entry->rule.listnr]);
-
-               if (sid) {
-                       char *ctx = NULL;
-                       u32 len;
-                       if (selinux_ctxid_to_string(sid, &ctx, &len)) {
-                               /* Maybe call audit_panic? */
-                               audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                "auid=%u ssid=%u add rule to list=%d res=%d",
-                                loginuid, sid, entry->rule.listnr, !err);
-                       } else
-                               audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                "auid=%u subj=%s add rule to list=%d res=%d",
-                                loginuid, ctx, entry->rule.listnr, !err);
-                       kfree(ctx);
-               } else
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                               "auid=%u add rule to list=%d res=%d",
-                               loginuid, entry->rule.listnr, !err);
+               audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err);
 
                if (err)
                        audit_free_rule(entry);
 
                err = audit_del_rule(entry,
                                     &audit_filter_list[entry->rule.listnr]);
-
-               if (sid) {
-                       char *ctx = NULL;
-                       u32 len;
-                       if (selinux_ctxid_to_string(sid, &ctx, &len)) {
-                               /* Maybe call audit_panic? */
-                               audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                       "auid=%u ssid=%u remove rule from list=%d res=%d",
-                                        loginuid, sid, entry->rule.listnr, !err);
-                       } else
-                               audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                       "auid=%u subj=%s remove rule from list=%d res=%d",
-                                        loginuid, ctx, entry->rule.listnr, !err);
-                       kfree(ctx);
-               } else
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                               "auid=%u remove rule from list=%d res=%d",
-                               loginuid, entry->rule.listnr, !err);
+               audit_log_rule_change(loginuid, sid, "remove", &entry->rule,
+                                     !err);
 
                audit_free_rule(entry);
                break;
 
        int                 auditable;  /* 1 if record should be written */
        int                 name_count;
        struct audit_names  names[AUDIT_NAMES];
+       char *              filterkey;  /* key for rule that triggered record */
        struct dentry *     pwd;
        struct vfsmount *   pwdmnt;
        struct audit_context *previous; /* For nested syscalls */
                        if (ctx)
                                result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
                        break;
+               case AUDIT_FILTERKEY:
+                       /* ignore this field for filtering */
+                       result = 1;
+                       break;
                }
 
                if (!result)
                        return 0;
        }
+       if (rule->filterkey)
+               ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
        switch (rule->action) {
        case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
        case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
                }
                audit_free_names(context);
                audit_free_aux(context);
+               kfree(context->filterkey);
                kfree(context);
                context  = previous;
        } while (context);
                  context->euid, context->suid, context->fsuid,
                  context->egid, context->sgid, context->fsgid, tty);
        audit_log_task_info(ab, tsk);
+       if (context->filterkey) {
+               audit_log_format(ab, " key=");
+               audit_log_untrustedstring(ab, context->filterkey);
+       } else
+               audit_log_format(ab, " key=(null)");
        audit_log_end(ab);
 
        for (aux = context->aux; aux; aux = aux->next) {
        } else {
                audit_free_names(context);
                audit_free_aux(context);
+               kfree(context->filterkey);
+               context->filterkey = NULL;
                tsk->audit_context = context;
        }
 }