#endif
};
-static DEFINE_MUTEX(audit_filter_mutex);
+DEFINE_MUTEX(audit_filter_mutex);
/* Inotify handle */
extern struct inotify_handle *audit_ih;
kfree(e);
}
-static inline void audit_free_rule_rcu(struct rcu_head *head)
+void audit_free_rule_rcu(struct rcu_head *head)
{
struct audit_entry *e = container_of(head, struct audit_entry, rcu);
audit_free_rule(e);
/* Unpack a filter field's string representation from user-space
* buffer. */
-static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
+char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
{
char *str;
struct audit_field *f)
{
if (krule->listnr != AUDIT_FILTER_EXIT ||
- krule->watch || krule->inode_f)
+ krule->watch || krule->inode_f || krule->tree)
return -EINVAL;
krule->inode_f = f;
if (path[0] != '/' || path[len-1] == '/' ||
krule->listnr != AUDIT_FILTER_EXIT ||
op & ~AUDIT_EQUAL ||
- krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */
+ krule->inode_f || krule->watch || krule->tree)
return -EINVAL;
watch = audit_init_watch(path);
int audit_match_class(int class, unsigned syscall)
{
- if (unlikely(syscall >= AUDIT_BITMASK_SIZE * sizeof(__u32)))
+ if (unlikely(syscall >= AUDIT_BITMASK_SIZE * 32))
return 0;
if (unlikely(class >= AUDIT_SYSCALL_CLASSES || !classes[class]))
return 0;
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:
goto exit_free;
}
break;
+ case AUDIT_DIR:
+ str = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(str))
+ goto exit_free;
+ entry->rule.buflen += f->val;
+
+ err = audit_make_tree(&entry->rule, str, f->op);
+ kfree(str);
+ if (err)
+ goto exit_free;
+ break;
case AUDIT_INODE:
err = audit_to_inode(&entry->rule, f);
if (err)
}
/* Pack a filter field's string representation into data block. */
-static inline size_t audit_pack_string(void **bufp, char *str)
+static inline size_t audit_pack_string(void **bufp, const char *str)
{
size_t len = strlen(str);
data->buflen += data->values[i] =
audit_pack_string(&bufp, krule->watch->path);
break;
+ case AUDIT_DIR:
+ data->buflen += data->values[i] =
+ audit_pack_string(&bufp,
+ audit_tree_path(krule->tree));
+ break;
case AUDIT_FILTERKEY:
data->buflen += data->values[i] =
audit_pack_string(&bufp, krule->filterkey);
if (strcmp(a->watch->path, b->watch->path))
return 1;
break;
+ case AUDIT_DIR:
+ if (strcmp(audit_tree_path(a->tree),
+ audit_tree_path(b->tree)))
+ return 1;
+ break;
case AUDIT_FILTERKEY:
/* both filterkeys exist based on above type compare */
if (strcmp(a->filterkey, b->filterkey))
new->inode_f = old->inode_f;
new->watch = NULL;
new->field_count = old->field_count;
+ /*
+ * note that we are OK with not refcounting here; audit_match_tree()
+ * never dereferences tree and we can't get false positives there
+ * since we'd have to have rule gone from the list *and* removed
+ * before the chunks found by lookup had been allocated, i.e. before
+ * the beginning of list scan.
+ */
+ new->tree = old->tree;
memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
/* deep copy this information, updating the se_rule fields, because
struct audit_entry *e;
struct audit_field *inode_f = entry->rule.inode_f;
struct audit_watch *watch = entry->rule.watch;
+ struct audit_tree *tree = entry->rule.tree;
struct nameidata *ndp = NULL, *ndw = NULL;
int h, err;
#ifdef CONFIG_AUDITSYSCALL
mutex_unlock(&audit_filter_mutex);
if (e) {
err = -EEXIST;
+ /* normally audit_add_tree_rule() will free it on failure */
+ if (tree)
+ audit_put_tree(tree);
goto error;
}
h = audit_hash_ino((u32)watch->ino);
list = &audit_inode_hash[h];
}
+ if (tree) {
+ err = audit_add_tree_rule(&entry->rule);
+ if (err) {
+ mutex_unlock(&audit_filter_mutex);
+ goto error;
+ }
+ }
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
list_add_rcu(&entry->list, list);
struct audit_entry *e;
struct audit_field *inode_f = entry->rule.inode_f;
struct audit_watch *watch, *tmp_watch = entry->rule.watch;
+ struct audit_tree *tree = entry->rule.tree;
LIST_HEAD(inotify_list);
int h, ret = 0;
#ifdef CONFIG_AUDITSYSCALL
}
}
+ if (e->rule.tree)
+ audit_remove_tree_rule(&e->rule);
+
list_del_rcu(&e->list);
call_rcu(&e->rcu, audit_free_rule_rcu);
out:
if (tmp_watch)
audit_put_watch(tmp_watch); /* match initial get */
+ if (tree)
+ audit_put_tree(tree); /* that's the temporary one */
return ret;
}
* auditctl to read from it... which isn't ever going to
* happen if we're actually running in the context of auditctl
* trying to _send_ the stuff */
-
+
dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL);
if (!dest)
return -ENOMEM;
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;
{
struct audit_entry *e;
int result = 0;
-
+
rcu_read_lock();
if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
goto unlock_and_return;
{
struct audit_entry *entry, *n, *nentry;
struct audit_watch *watch;
+ struct audit_tree *tree;
int i, err = 0;
/* audit_filter_mutex synchronizes the writers */
continue;
watch = entry->rule.watch;
+ tree = entry->rule.tree;
nentry = audit_dupe_rule(&entry->rule, watch);
if (unlikely(IS_ERR(nentry))) {
/* save the first error encountered for the
list_add(&nentry->rule.rlist,
&watch->rules);
list_del(&entry->rule.rlist);
- }
+ } else if (tree)
+ list_replace_init(&entry->rule.rlist,
+ &nentry->rule.rlist);
list_replace_rcu(&entry->list, &nentry->list);
}
call_rcu(&entry->rcu, audit_free_rule_rcu);