📄 auditfilter.c
字号:
* 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 lsm_rule fields, because * 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_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_lsm_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); return ERR_PTR(err); } } if (watch) { audit_get_watch(watch); new->watch = watch; } return entry;}/* Update inode info in audit rules based on filesystem event. */static void audit_update_watch(struct audit_parent *parent, const char *dname, dev_t dev, unsigned long ino, unsigned invalidating){ struct audit_watch *owatch, *nwatch, *nextw; struct audit_krule *r, *nextr; struct audit_entry *oentry, *nentry; mutex_lock(&audit_filter_mutex); list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { if (audit_compare_dname_path(dname, owatch->path, NULL)) continue; /* If the update involves invalidating rules, do the inode-based * filtering now, so we don't omit records. */ if (invalidating && current->audit_context && audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT) audit_set_auditable(current->audit_context); nwatch = audit_dupe_watch(owatch); if (IS_ERR(nwatch)) { mutex_unlock(&audit_filter_mutex); audit_panic("error updating watch, skipping"); return; } nwatch->dev = dev; nwatch->ino = ino; list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) { oentry = container_of(r, struct audit_entry, rule); list_del(&oentry->rule.rlist); list_del_rcu(&oentry->list); nentry = audit_dupe_rule(&oentry->rule, nwatch); if (IS_ERR(nentry)) audit_panic("error updating watch, removing"); else { int h = audit_hash_ino((u32)ino); list_add(&nentry->rule.rlist, &nwatch->rules); list_add_rcu(&nentry->list, &audit_inode_hash[h]); } call_rcu(&oentry->rcu, audit_free_rule_rcu); } if (audit_enabled) { struct audit_buffer *ab; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); audit_log_format(ab, "auid=%u ses=%u", audit_get_loginuid(current), audit_get_sessionid(current)); 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); goto add_watch_to_parent; /* event applies to a single watch */ } mutex_unlock(&audit_filter_mutex); return;add_watch_to_parent: list_add(&nwatch->wlist, &parent->watches); mutex_unlock(&audit_filter_mutex); return;}/* Remove all watches & rules associated with a parent that is going away. */static void audit_remove_parent_watches(struct audit_parent *parent){ struct audit_watch *w, *nextw; struct audit_krule *r, *nextr; struct audit_entry *e; 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); if (audit_enabled) { struct audit_buffer *ab; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); audit_log_format(ab, "auid=%u ses=%u", audit_get_loginuid(current), audit_get_sessionid(current)); 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_remove_watch(w); } mutex_unlock(&audit_filter_mutex);}/* Unregister inotify watches for parents on in_list. * Generates an IN_IGNORED event. */static void audit_inotify_unregister(struct list_head *in_list){ struct audit_parent *p, *n; list_for_each_entry_safe(p, n, in_list, ilist) { list_del(&p->ilist); inotify_rm_watch(audit_ih, &p->wdata); /* the put matching the get in audit_do_del_rule() */ put_inotify_watch(&p->wdata); }}/* Find an existing audit rule. * Caller must hold audit_filter_mutex to prevent stale rule data. */static struct audit_entry *audit_find_rule(struct audit_entry *entry, struct list_head *list){ struct audit_entry *e, *found = NULL; int h; if (entry->rule.watch) { /* we don't know the inode number, so must walk entire hash */ for (h = 0; h < AUDIT_INODE_BUCKETS; h++) { list = &audit_inode_hash[h]; list_for_each_entry(e, list, list) if (!audit_compare_rule(&entry->rule, &e->rule)) { found = e; goto out; } } goto out; } list_for_each_entry(e, list, list) if (!audit_compare_rule(&entry->rule, &e->rule)) { found = e; goto out; }out: return found;}/* Get path information necessary for adding watches. */static int audit_get_nd(char *path, struct nameidata **ndp, struct nameidata **ndw){ struct nameidata *ndparent, *ndwatch; int err; ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL); if (unlikely(!ndparent)) return -ENOMEM; ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL); if (unlikely(!ndwatch)) { kfree(ndparent); return -ENOMEM; } err = path_lookup(path, LOOKUP_PARENT, ndparent); if (err) { kfree(ndparent); kfree(ndwatch); return err; } err = path_lookup(path, 0, ndwatch); if (err) { kfree(ndwatch); ndwatch = NULL; } *ndp = ndparent; *ndw = ndwatch; return 0;}/* Release resources used for watch path information. */static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw){ if (ndp) { path_put(&ndp->path); kfree(ndp); } if (ndw) { path_put(&ndw->path); kfree(ndw); }}/* Associate the given rule with an existing parent inotify_watch. * Caller must hold audit_filter_mutex. */static void audit_add_to_parent(struct audit_krule *krule, struct audit_parent *parent){ struct audit_watch *w, *watch = krule->watch; int watch_found = 0; list_for_each_entry(w, &parent->watches, wlist) { if (strcmp(watch->path, w->path)) continue; watch_found = 1; /* put krule's and initial refs to temporary watch */ audit_put_watch(watch); audit_put_watch(watch); audit_get_watch(w); krule->watch = watch = w; break; } if (!watch_found) { get_inotify_watch(&parent->wdata); watch->parent = parent; list_add(&watch->wlist, &parent->watches); } list_add(&krule->rlist, &watch->rules);}/* Find a matching watch entry, or add this one. * Caller must hold audit_filter_mutex. */static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp, struct nameidata *ndw){ struct audit_watch *watch = krule->watch; struct inotify_watch *i_watch; struct audit_parent *parent; int ret = 0; /* update watch filter fields */ if (ndw) { watch->dev = ndw->path.dentry->d_inode->i_sb->s_dev; watch->ino = ndw->path.dentry->d_inode->i_ino; } /* The audit_filter_mutex must not be held during inotify calls because * we hold it during inotify event callback processing. If an existing * inotify watch is found, inotify_find_watch() grabs a reference before * returning. */ mutex_unlock(&audit_filter_mutex); if (inotify_find_watch(audit_ih, ndp->path.dentry->d_inode, &i_watch) < 0) { parent = audit_init_parent(ndp); if (IS_ERR(parent)) { /* caller expects mutex locked */ mutex_lock(&audit_filter_mutex); return PTR_ERR(parent); } } else parent = container_of(i_watch, struct audit_parent, wdata); mutex_lock(&audit_filter_mutex); /* parent was moved before we took audit_filter_mutex */ if (parent->flags & AUDIT_PARENT_INVALID) ret = -ENOENT; else audit_add_to_parent(krule, parent); /* match get in audit_init_parent or inotify_find_watch */ put_inotify_watch(&parent->wdata); return ret;}/* Add rule to given filterlist if not a duplicate. */static inline int audit_add_rule(struct audit_entry *entry, struct list_head *list){ 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 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); list = &audit_inode_hash[h]; } mutex_lock(&audit_filter_mutex); e = audit_find_rule(entry, list); 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; } /* Avoid calling path_lookup under audit_filter_mutex. */ if (watch) { err = audit_get_nd(watch->path, &ndp, &ndw); if (err) goto error; } mutex_lock(&audit_filter_mutex); if (watch) { /* audit_filter_mutex is dropped and re-taken during this call */ err = audit_add_watch(&entry->rule, ndp, ndw); if (err) { mutex_unlock(&audit_filter_mutex); 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); entry->rule.flags &= ~AUDIT_FILTER_PREPEND; } else { list_add_tail_rcu(&entry->list, list); }#ifdef CONFIG_AUDITSYSCALL if (!dont_count) audit_n_rules++; if (!audit_match_signal(entry)) audit_signals++;#endif mutex_unlock(&audit_filter_mutex); audit_put_nd(ndp, ndw); /* NULL args OK */ return 0;error: audit_put_nd(ndp, ndw); /* NULL args OK */ if (watch) audit_put_watch(watch); /* tmp watch, matches initial get */ return err;}/* Remove an existing rule from filterlist. */static inline int audit_del_rule(struct audit_entry *entry, struct list_head *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 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); list = &audit_inode_hash[h]; } mutex_lock(&audit_filter_mutex); e = audit_find_rule(entry, list); if (!e) { mutex_unlock(&audit_filter_mutex); ret = -ENOENT; goto out; } watch = e->rule.watch; if (watch) { struct audit_parent *parent = watch->parent; list_del(&e->rule.rlist); if (list_empty(&watch->rules)) { audit_remove_watch(watch); if (list_empty(&parent->watches)) { /* Put parent on the inotify un-registration
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -