user.c: #ifdef ->mq_bytes
[safe/jmp/linux-2.6] / kernel / auditfilter.c
index 7f2ea8b..359645c 100644 (file)
@@ -279,6 +279,77 @@ 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 * 32))
+               return 0;
+       if (unlikely(class >= AUDIT_SYSCALL_CLASSES || !classes[class]))
+               return 0;
+       return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall);
+}
+
+#ifdef CONFIG_AUDITSYSCALL
+static inline int audit_match_class_bits(int class, u32 *mask)
+{
+       int i;
+
+       if (classes[class]) {
+               for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+                       if (mask[i] & classes[class][i])
+                               return 0;
+       }
+       return 1;
+}
+
+static int audit_match_signal(struct audit_entry *entry)
+{
+       struct audit_field *arch = entry->rule.arch_f;
+
+       if (!arch) {
+               /* When arch is unspecified, we must check both masks on biarch
+                * as syscall number alone is ambiguous. */
+               return (audit_match_class_bits(AUDIT_CLASS_SIGNAL,
+                                              entry->rule.mask) &&
+                       audit_match_class_bits(AUDIT_CLASS_SIGNAL_32,
+                                              entry->rule.mask));
+       }
+
+       switch(audit_classify_arch(arch->val)) {
+       case 0: /* native */
+               return (audit_match_class_bits(AUDIT_CLASS_SIGNAL,
+                                              entry->rule.mask));
+       case 1: /* 32bit on biarch */
+               return (audit_match_class_bits(AUDIT_CLASS_SIGNAL_32,
+                                              entry->rule.mask));
+       default:
+               return 1;
+       }
+}
+#endif
+
 /* Common user-space to kernel rule translation. */
 static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
 {
@@ -322,6 +393,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:
@@ -363,17 +450,37 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                case AUDIT_FSGID:
                case AUDIT_LOGINUID:
                case AUDIT_PERS:
-               case AUDIT_ARCH:
                case AUDIT_MSGTYPE:
+               case AUDIT_PPID:
                case AUDIT_DEVMAJOR:
                case AUDIT_DEVMINOR:
                case AUDIT_EXIT:
                case AUDIT_SUCCESS:
+                       /* bit ops are only useful on syscall args */
+                       if (f->op == AUDIT_BIT_MASK ||
+                                               f->op == AUDIT_BIT_TEST) {
+                               err = -EINVAL;
+                               goto exit_free;
+                       }
+                       break;
                case AUDIT_ARG0:
                case AUDIT_ARG1:
                case AUDIT_ARG2:
                case AUDIT_ARG3:
                        break;
+               /* arch is only allowed to be = or != */
+               case AUDIT_ARCH:
+                       if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL)
+                                       && (f->op != AUDIT_NEGATE) && (f->op)) {
+                               err = -EINVAL;
+                               goto exit_free;
+                       }
+                       entry->rule.arch_f = f;
+                       break;
+               case AUDIT_PERM:
+                       if (f->val & ~15)
+                               goto exit_free;
+                       break;
                case AUDIT_INODE:
                        err = audit_to_inode(&entry->rule, f);
                        if (err)
@@ -403,6 +510,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                case AUDIT_EQUAL:
                        break;
                default:
+                       err = -EINVAL;
                        goto exit_free;
                }
        }
@@ -458,7 +566,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                case AUDIT_FSGID:
                case AUDIT_LOGINUID:
                case AUDIT_PERS:
-               case AUDIT_ARCH:
                case AUDIT_MSGTYPE:
                case AUDIT_PPID:
                case AUDIT_DEVMAJOR:
@@ -470,6 +577,9 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                case AUDIT_ARG2:
                case AUDIT_ARG3:
                        break;
+               case AUDIT_ARCH:
+                       entry->rule.arch_f = f;
+                       break;
                case AUDIT_SUBJ_USER:
                case AUDIT_SUBJ_ROLE:
                case AUDIT_SUBJ_TYPE:
@@ -527,6 +637,10 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                        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;
                }
@@ -540,6 +654,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                case AUDIT_EQUAL:
                        break;
                default:
+                       err = -EINVAL;
                        goto exit_free;
                }
        }
@@ -570,10 +685,9 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
        struct audit_rule *rule;
        int i;
 
-       rule = kmalloc(sizeof(*rule), GFP_KERNEL);
+       rule = kzalloc(sizeof(*rule), GFP_KERNEL);
        if (unlikely(!rule))
                return NULL;
-       memset(rule, 0, sizeof(*rule));
 
        rule->flags = krule->flags | krule->listnr;
        rule->action = krule->action;
@@ -735,8 +849,8 @@ static inline int audit_dupe_selinux_field(struct audit_field *df,
 
        /* our own copy of se_str */
        se_str = kstrdup(sf->se_str, GFP_KERNEL);
-       if (unlikely(IS_ERR(se_str)))
-           return -ENOMEM;
+       if (unlikely(!se_str))
+               return -ENOMEM;
        df->se_str = se_str;
 
        /* our own (refreshed) copy of se_rule */
@@ -840,7 +954,7 @@ static void audit_update_watch(struct audit_parent *parent,
 
                /* If the update involves invalidating rules, do the inode-based
                 * filtering now, so we don't omit records. */
-               if (invalidating &&
+               if (invalidating && current->audit_context &&
                    audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
                        audit_set_auditable(current->audit_context);
 
@@ -872,9 +986,10 @@ 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, "op=updated rules specifying path=");
                audit_log_untrustedstring(ab, owatch->path);
                audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
+               audit_log_format(ab, " list=%d res=1", r->listnr);
                audit_log_end(ab);
 
                audit_remove_watch(owatch);
@@ -895,19 +1010,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, "op=remove 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 res=1", 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);
        }
@@ -1093,8 +1217,16 @@ static inline int audit_add_rule(struct audit_entry *entry,
        struct audit_entry *e;
        struct audit_field *inode_f = entry->rule.inode_f;
        struct audit_watch *watch = entry->rule.watch;
-       struct nameidata *ndp, *ndw;
-       int h, err, putnd_needed = 0;
+       struct nameidata *ndp = NULL, *ndw = NULL;
+       int h, err;
+#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);
@@ -1114,7 +1246,6 @@ static inline int audit_add_rule(struct audit_entry *entry,
                err = audit_get_nd(watch->path, &ndp, &ndw);
                if (err)
                        goto error;
-               putnd_needed = 1;
        }
 
        mutex_lock(&audit_filter_mutex);
@@ -1135,16 +1266,20 @@ static inline int audit_add_rule(struct audit_entry *entry,
        } else {
                list_add_tail_rcu(&entry->list, list);
        }
-       mutex_unlock(&audit_filter_mutex);
+#ifdef CONFIG_AUDITSYSCALL
+       if (!dont_count)
+               audit_n_rules++;
 
-       if (putnd_needed)
-               audit_put_nd(ndp, ndw);
+       if (!audit_match_signal(entry))
+               audit_signals++;
+#endif
+       mutex_unlock(&audit_filter_mutex);
 
+       audit_put_nd(ndp, ndw);         /* NULL args OK */
        return 0;
 
 error:
-       if (putnd_needed)
-               audit_put_nd(ndp, ndw);
+       audit_put_nd(ndp, ndw);         /* NULL args OK */
        if (watch)
                audit_put_watch(watch); /* tmp watch, matches initial get */
        return err;
@@ -1159,6 +1294,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);
@@ -1196,6 +1339,13 @@ 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--;
+
+       if (!audit_match_signal(entry))
+               audit_signals--;
+#endif
        mutex_unlock(&audit_filter_mutex);
 
        if (!list_empty(&inotify_list))
@@ -1306,13 +1456,13 @@ static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action,
        if (sid) {
                char *ctx = NULL;
                u32 len;
-               if (selinux_ctxid_to_string(sid, &ctx, &len))
+               if (selinux_sid_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);
+       audit_log_format(ab, " op=%s rule key=", action);
        if (rule->filterkey)
                audit_log_untrustedstring(ab, rule->filterkey);
        else
@@ -1423,6 +1573,10 @@ int audit_comparator(const u32 left, const u32 op, const u32 right)
                return (left > right);
        case AUDIT_GREATER_THAN_OR_EQUAL:
                return (left >= right);
+       case AUDIT_BIT_MASK:
+               return (left & right);
+       case AUDIT_BIT_TEST:
+               return ((left & right) == right);
        }
        BUG();
        return 0;
@@ -1503,8 +1657,8 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb,
 
 int audit_filter_user(struct netlink_skb_parms *cb, int type)
 {
+       enum audit_state state = AUDIT_DISABLED;
        struct audit_entry *e;
-       enum audit_state   state;
        int ret = 1;
 
        rcu_read_lock();