Merge mulgrave-w:git/scsi-misc-2.6
[pandora-kernel.git] / kernel / auditfilter.c
index 4c99d2c..a44879b 100644 (file)
@@ -141,6 +141,7 @@ static inline void audit_free_rule(struct audit_entry *e)
                        selinux_audit_rule_free(f->se_rule);
                }
        kfree(e->rule.fields);
+       kfree(e->rule.filterkey);
        kfree(e);
 }
 
@@ -278,6 +279,38 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len,
        return 0;
 }
 
+static __u32 *classes[AUDIT_SYSCALL_CLASSES];
+
+int __init audit_register_class(int class, unsigned *list)
+{
+       __u32 *p = kzalloc(AUDIT_BITMASK_SIZE * sizeof(__u32), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+       while (*list != ~0U) {
+               unsigned n = *list++;
+               if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) {
+                       kfree(p);
+                       return -EINVAL;
+               }
+               p[AUDIT_WORD(n)] |= AUDIT_BIT(n);
+       }
+       if (class >= AUDIT_SYSCALL_CLASSES || classes[class]) {
+               kfree(p);
+               return -EINVAL;
+       }
+       classes[class] = p;
+       return 0;
+}
+
+int audit_match_class(int class, unsigned syscall)
+{
+       if (unlikely(syscall >= AUDIT_BITMASK_SIZE * sizeof(__u32)))
+               return 0;
+       if (unlikely(class >= AUDIT_SYSCALL_CLASSES || !classes[class]))
+               return 0;
+       return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall);
+}
+
 /* Common user-space to kernel rule translation. */
 static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
 {
@@ -321,6 +354,22 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
        for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
                entry->rule.mask[i] = rule->mask[i];
 
+       for (i = 0; i < AUDIT_SYSCALL_CLASSES; i++) {
+               int bit = AUDIT_BITMASK_SIZE * 32 - i - 1;
+               __u32 *p = &entry->rule.mask[AUDIT_WORD(bit)];
+               __u32 *class;
+
+               if (!(*p & AUDIT_BIT(bit)))
+                       continue;
+               *p &= ~AUDIT_BIT(bit);
+               class = classes[i];
+               if (class) {
+                       int j;
+                       for (j = 0; j < AUDIT_BITMASK_SIZE; j++)
+                               entry->rule.mask[j] |= class[j];
+               }
+       }
+
        return entry;
 
 exit_err:
@@ -364,6 +413,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                case AUDIT_PERS:
                case AUDIT_ARCH:
                case AUDIT_MSGTYPE:
+               case AUDIT_PPID:
                case AUDIT_DEVMAJOR:
                case AUDIT_DEVMINOR:
                case AUDIT_EXIT:
@@ -373,6 +423,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                case AUDIT_ARG2:
                case AUDIT_ARG3:
                        break;
+               case AUDIT_PERM:
+                       if (f->val & ~15)
+                               goto exit_free;
+                       break;
                case AUDIT_INODE:
                        err = audit_to_inode(&entry->rule, f);
                        if (err)
@@ -402,6 +456,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                case AUDIT_EQUAL:
                        break;
                default:
+                       err = -EINVAL;
                        goto exit_free;
                }
        }
@@ -469,11 +524,16 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                case AUDIT_ARG2:
                case AUDIT_ARG3:
                        break;
-               case AUDIT_SE_USER:
-               case AUDIT_SE_ROLE:
-               case AUDIT_SE_TYPE:
-               case AUDIT_SE_SEN:
-               case AUDIT_SE_CLR:
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
                        str = audit_unpack_string(&bufp, &remain, f->val);
                        if (IS_ERR(str))
                                goto exit_free;
@@ -511,6 +571,20 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                        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;
+               case AUDIT_PERM:
+                       if (f->val & ~15)
+                               goto exit_free;
+                       break;
                default:
                        goto exit_free;
                }
@@ -524,6 +598,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                case AUDIT_EQUAL:
                        break;
                default:
+                       err = -EINVAL;
                        goto exit_free;
                }
        }
@@ -600,11 +675,16 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
                data->fields[i] = f->type;
                data->fieldflags[i] = f->op;
                switch(f->type) {
-               case AUDIT_SE_USER:
-               case AUDIT_SE_ROLE:
-               case AUDIT_SE_TYPE:
-               case AUDIT_SE_SEN:
-               case AUDIT_SE_CLR:
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
                        data->buflen += data->values[i] =
                                audit_pack_string(&bufp, f->se_str);
                        break;
@@ -612,6 +692,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
                        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;
                }
@@ -639,11 +723,16 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
                        return 1;
 
                switch(a->fields[i].type) {
-               case AUDIT_SE_USER:
-               case AUDIT_SE_ROLE:
-               case AUDIT_SE_TYPE:
-               case AUDIT_SE_SEN:
-               case AUDIT_SE_CLR:
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
                        if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
                                return 1;
                        break;
@@ -651,6 +740,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
                        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;
@@ -730,6 +824,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
        u32 fcount = old->field_count;
        struct audit_entry *entry;
        struct audit_krule *new;
+       char *fk;
        int i, err = 0;
 
        entry = audit_init_entry(fcount);
@@ -753,13 +848,25 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
         * the originals will all be freed when the old rule is freed. */
        for (i = 0; i < fcount; i++) {
                switch (new->fields[i].type) {
-               case AUDIT_SE_USER:
-               case AUDIT_SE_ROLE:
-               case AUDIT_SE_TYPE:
-               case AUDIT_SE_SEN:
-               case AUDIT_SE_CLR:
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
                        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);
@@ -824,7 +931,7 @@ static void audit_update_watch(struct audit_parent *parent,
                }
 
                ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
-               audit_log_format(ab, "audit updated rules specifying watch=");
+               audit_log_format(ab, "audit updated rules specifying path=");
                audit_log_untrustedstring(ab, owatch->path);
                audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
                audit_log_end(ab);
@@ -847,19 +954,28 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
        struct audit_watch *w, *nextw;
        struct audit_krule *r, *nextr;
        struct audit_entry *e;
+       struct audit_buffer *ab;
 
        mutex_lock(&audit_filter_mutex);
        parent->flags |= AUDIT_PARENT_INVALID;
        list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
                list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
                        e = container_of(r, struct audit_entry, rule);
+
+                       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+                       audit_log_format(ab, "audit implicitly removed rule path=");
+                       audit_log_untrustedstring(ab, w->path);
+                       if (r->filterkey) {
+                               audit_log_format(ab, " key=");
+                               audit_log_untrustedstring(ab, r->filterkey);
+                       } else
+                               audit_log_format(ab, " key=(null)");
+                       audit_log_format(ab, " list=%d", r->listnr);
+                       audit_log_end(ab);
+
                        list_del(&r->rlist);
                        list_del_rcu(&e->list);
                        call_rcu(&e->rcu, audit_free_rule_rcu);
-
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                "audit implicitly removed rule from list=%d\n",
-                                 AUDIT_FILTER_EXIT);
                }
                audit_remove_watch(w);
        }
@@ -1047,6 +1163,14 @@ static inline int audit_add_rule(struct audit_entry *entry,
        struct audit_watch *watch = entry->rule.watch;
        struct nameidata *ndp, *ndw;
        int h, err, putnd_needed = 0;
+#ifdef CONFIG_AUDITSYSCALL
+       int dont_count = 0;
+
+       /* If either of these, don't count towards total */
+       if (entry->rule.listnr == AUDIT_FILTER_USER ||
+               entry->rule.listnr == AUDIT_FILTER_TYPE)
+               dont_count = 1;
+#endif
 
        if (inode_f) {
                h = audit_hash_ino(inode_f->val);
@@ -1087,6 +1211,10 @@ static inline int audit_add_rule(struct audit_entry *entry,
        } else {
                list_add_tail_rcu(&entry->list, list);
        }
+#ifdef CONFIG_AUDITSYSCALL
+       if (!dont_count)
+               audit_n_rules++;
+#endif
        mutex_unlock(&audit_filter_mutex);
 
        if (putnd_needed)
@@ -1111,6 +1239,14 @@ static inline int audit_del_rule(struct audit_entry *entry,
        struct audit_watch *watch, *tmp_watch = entry->rule.watch;
        LIST_HEAD(inotify_list);
        int h, ret = 0;
+#ifdef CONFIG_AUDITSYSCALL
+       int dont_count = 0;
+
+       /* If either of these, don't count towards total */
+       if (entry->rule.listnr == AUDIT_FILTER_USER ||
+               entry->rule.listnr == AUDIT_FILTER_TYPE)
+               dont_count = 1;
+#endif
 
        if (inode_f) {
                h = audit_hash_ino(inode_f->val);
@@ -1148,6 +1284,10 @@ static inline int audit_del_rule(struct audit_entry *entry,
        list_del_rcu(&e->list);
        call_rcu(&e->rcu, audit_free_rule_rcu);
 
+#ifdef CONFIG_AUDITSYSCALL
+       if (!dont_count)
+               audit_n_rules--;
+#endif
        mutex_unlock(&audit_filter_mutex);
 
        if (!list_empty(&inotify_list))
@@ -1245,6 +1385,34 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
                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
@@ -1304,24 +1472,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
 
                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);
@@ -1337,24 +1488,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
 
                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;
@@ -1514,11 +1649,16 @@ static inline int audit_rule_has_selinux(struct audit_krule *rule)
        for (i = 0; i < rule->field_count; i++) {
                struct audit_field *f = &rule->fields[i];
                switch (f->type) {
-               case AUDIT_SE_USER:
-               case AUDIT_SE_ROLE:
-               case AUDIT_SE_TYPE:
-               case AUDIT_SE_SEN:
-               case AUDIT_SE_CLR:
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
                        return 1;
                }
        }