[PATCH] filesystem location based auditing
Amy Griffis
amy.griffis at hp.com
Thu Mar 9 23:05:18 UTC 2006
On Fri, Feb 24, 2006 at 03:07:30PM -0600, Timothy R. Chavez wrote:
> > +/* Inotify events we care about. */
> > +#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF
> > +#define AUDIT_IN_SELF IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT
> > +
> > +/* Flags for stale filterlist data */
> > +#define AUDIT_ENTRY_ADD 0x01 /* Rule entry addition in progress. */
> > +#define AUDIT_ENTRY_DEL 0x02 /* Rule entry deletion in progress. */
> > +#define AUDIT_PARENT_DEL 0x01 /* Parent deletion in progress. */
>
> AUDIT_ENTRY_ADD and AUDIT_PARENT_DEL are the same? I'm assuming since
> this is a mask, that it probably makes sense to make 0x04, no?
AUDIT_ENTRY_* and AUDIT_PARENT_* were two different masks in the
audit_entry and audit_parent structs. I've re-written the code
though, so neither of them is needed now.
> > +/* Remove all watches & rules associated with a parent that is going away. */
> > +static inline 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_flist *flist = &audit_filter_list[AUDIT_FILTER_EXIT];
> > +
> > + spin_lock(&flist->lock);
> > + 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);
> > +
> > + /* make sure we have a valid copy */
> > + while (e->replacement != NULL)
> > + e = e->replacement;
> > + if (e->flags & AUDIT_ENTRY_DEL)
> > + continue;
> > +
> > + list_del(&r->rlist);
> > + list_del_rcu(&e->list);
> > + e->flags |= AUDIT_ENTRY_DEL;
> > + call_rcu(&e->rcu, audit_free_rule_rcu);
> > + audit_log(NULL, GFP_ATOMIC, AUDIT_CONFIG_CHANGE,
> > + "audit implicitly removed rule from list=%d\n",
> > + AUDIT_FILTER_EXIT);
> > + }
> > + list_del(&w->wlist);
> > + audit_put_watch(w);
> > + }
> > + spin_unlock(&flist->lock);
> > +}
>
> Why not rcu locking here? You can iterate over the list w/
> rcu_read_lock/unlock and then removals from the list via admin
> action can be defferred using list_del_rcu? Doesn't the same apply
> for additions to the flist as well..
I am using rcu locking, but you need an additional lock to protect
against concurrent list writes.
> > +/* Register this parent's inotify watch. */
> > +static int audit_inotify_register(void *_data)
> > +{
> > + void **data = _data;
> > + struct audit_parent *parent;
> > + char *path;
> > + struct nameidata nd;
> > + int err;
> > + u32 wd;
> > +
> > + parent = data[0];
> > + path = data[1];
> > + kfree(data);
> > +
> > + err = path_lookup(path, LOOKUP_PARENT, &nd);
> > + if (err)
> > + goto handle_error;
> > +
> > + wd = inotify_add_watch(audit_idev, nd.dentry->d_inode, AUDIT_IN_WATCH,
> > + parent);
> > + if (wd < 0)
> > + goto handle_error;
> > +
> > + spin_lock(&master_parents_lock);
> > + parent->wd = wd;
> > + parent->ino = nd.dentry->d_inode->i_ino;
> > + spin_unlock(&master_parents_lock);
> > +
> > + path_release(&nd);
> > + audit_put_parent(parent);
> > + return 0;
> > +
> > +handle_error:
> > + path_release(&nd);
> > + audit_remove_parent_watches(parent);
> > + audit_remove_parent(parent);
> > +
> > + audit_put_parent(parent);
> > + return 0;
>
> Hm... on error you return 0? That's what you return on success too.
Yeah, the function was supposed to contain the error by cleaning up
after itself. It's a little different in the latest iteration...
> > +/* Unregister this parent's inotify watch. Generates an IN_IGNORED event. */
> > +static int audit_inotify_unregister(void *data)
> > +{
> > + struct audit_parent *parent = data;
> > + s32 wd;
> > +
> > + spin_lock(&master_parents_lock);
> > + wd = parent->wd;
> > + spin_unlock(&master_parents_lock);
> > +
> > + if (inotify_ignore(audit_idev, wd))
> > + printk(KERN_ERR
> > + "%s:%d: unable to remove inotify watch for inode %lu\n",
> > + __FILE__, __LINE__, parent->ino);
>
> This is benign?
Not really benign, but a way to signal an un-recoverable error. This
situation has been removed by not using kthread_run.
> > +/* Initialize a parent watch entry. */
> > +static inline struct audit_parent *audit_init_parent(char *path,
> > + unsigned long ino)
> > +{
> > + struct audit_parent *parent;
> > + void **data;
> > + struct task_struct *task;
> > +
> > + parent = kmalloc(sizeof(*parent), GFP_ATOMIC);
> > + if (unlikely(!parent))
> > + return ERR_PTR(-ENOMEM);
> > +
> > + memset(parent, 0, sizeof(*parent));
> > + INIT_LIST_HEAD(&parent->watches);
> > + atomic_set(&parent->count, 0);
> > + parent->ino = ino;
> > + audit_get_parent(parent);
> > +
> > + /* Spawn a thread to register the parent's inotify watch without
> > + * the filterlist spinlock. */
> > + data = kmalloc(2 * sizeof(void *), GFP_ATOMIC);
> > + if (!data) {
> > + audit_put_parent(parent);
> > + return ERR_PTR(-ENOMEM);
> > + }
> > + data[0] = parent;
> > + data[1] = path;
> > + audit_get_parent(parent);
> > + task = kthread_run(audit_inotify_register, data,
> > + "audit_inotify_register");
> > + if (IS_ERR(task)) {
> > + audit_put_parent(parent);
> > + return ERR_PTR(PTR_ERR(task));
>
> Redundant? return ERR_PTR(task), right?
The function returns an audit_parent struct, and this is a task
struct. Anyway, it's been removed as a result of other changes. :-)
> > +/* Initialize a watch entry. */
> > +static inline struct audit_watch *audit_init_watch(char *path)
> > +{
> > + struct audit_watch *watch;
> > +
> > + watch = kmalloc(sizeof(*watch), GFP_KERNEL);
> > + if (unlikely(!watch))
> > + return ERR_PTR(-ENOMEM);
> > +
> > + memset(watch, 0, sizeof(*watch));
> > + INIT_LIST_HEAD(&watch->rules);
> > + atomic_set(&watch->count, 0);
> > + watch->path = path;
> > + audit_get_watch(watch);
> > +
>
> Why not just atomic_set(&watch->count, 1), rather than making two
> atomic calls?
Done.
> > +/* Compare given dentry name with last component in given path,
> > + * return of 0 indicates a match. */
> > +int audit_compare_dname_path(const char *dname, const char *path)
> > +{
> > + int dlen, plen;
> > + const char *p;
> >
> > + if (!dname || !path)
> > + return 1;
> > +
> > + dlen = strlen(dname);
> > + plen = strlen(path);
> > + if (plen < dlen)
> > + return 1;
> > +
> > + /* disregard trailing slashes */
> > + p = path + plen - 1;
> > + while ((*p == '/') && (p > path))
> > + p--;
> > +
> > + /* find last path component */
>
> Why not path_lookup with LOOKUP_PARENT, then d_lookup?
I was hoping to avoid doing path_lookup here.
> > + p = p - dlen + 1;
> > + if (p < path)
> > + return 1;
> > + else if (p > path) {
> > + if (*--p != '/')
> > + return 1;
> > + else
> > + p++;
> > + }
> > +
> > + return strncmp(p, dname, dlen);
> > +}
More information about the Linux-audit
mailing list