Re: [PATCH V6 3/4] audit: convert audit_exe to audit_fsnotify

Richard Guy Briggs
2015-07-17T02:03:03Z

On 15/07/16, Paul Moore wrote:
> On Tuesday, July 14, 2015 11:50:25 AM Richard Guy Briggs wrote:
> > Instead of just hard coding the ino and dev of the executable we care
> > about at the moment the rule is inserted into the kernel, use the new
> > audit_fsnotify infrastructure. This means that if the inode in question
> > is unlinked and creat'd (aka updated) the rule will just continue to
> > work.
> >
> > Signed-off-by: Eric Paris <eparis@redhat.com>
> >
> > RGB: Clean up exe with similar interface as watch and tree.
> > RGB: Clean up audit exe mark just before audit_free_rule() rather than in it
> > to avoid mutex in software interrupt context.
> >
> > Based-on-code-by: Eric Paris <eparis@redhat.com>
> > Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
>
> Ah, good, the fix that patch 1/4 was hoping for is finally happening :)
>
> Since we're not getting paid by the number of patches in a patch set, I think
> I would prefer to see the fsnotify implementation as the first patch and the
> original crappy exe filter patch merged with this fixup patch.

Ah, fair enough. Yeah, I'll do that.

> No in depth comments on this particular patch, I skimmed it but frankly it
> makes much more sense to review this patch once you've merged the two.
>
> > diff --git a/include/linux/audit.h b/include/linux/audit.h
> > index 95463a2..aee456f 100644
> > --- a/include/linux/audit.h
> > +++ b/include/linux/audit.h
> > @@ -59,7 +59,7 @@ struct audit_krule {
> > struct audit_field *inode_f; /* quick access to an inode field */
> > struct audit_watch *watch; /* associated watch */
> > struct audit_tree *tree; /* associated watched tree */
> > - struct audit_exe *exe;
> > + struct audit_fsnotify_mark *exe;
> > struct list_head rlist; /* entry in audit_{watch,tree}.rules list */
> > struct list_head list; /* for AUDIT_LIST* purposes only */
> > u64 prio;
> > diff --git a/kernel/audit.h b/kernel/audit.h
> > index 491bd4b..eeaf054 100644
> > --- a/kernel/audit.h
> > +++ b/kernel/audit.h
> > @@ -51,7 +51,6 @@ enum audit_state {
> > /* Rule lists */
> > struct audit_watch;
> > struct audit_fsnotify_mark;
> > -struct audit_exe;
> > struct audit_tree;
> > struct audit_chunk;
> >
> > @@ -279,11 +278,8 @@ extern void audit_remove_mark(struct
> > audit_fsnotify_mark *audit_mark); extern void audit_remove_mark_rule(struct
> > audit_krule *krule);
> > extern int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned
> > long ino, dev_t dev);
> >
> > -extern int audit_make_exe_rule(struct audit_krule *krule, char *pathname,
> > int len, u32 op); -extern void audit_remove_exe_rule(struct audit_krule
> > *krule);
> > -extern char *audit_exe_path(struct audit_exe *exe);
> > extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule
> > *old); -extern int audit_exe_compare(struct task_struct *tsk, struct
> > audit_exe *exe); +extern int audit_exe_compare(struct task_struct *tsk,
> > struct audit_fsnotify_mark *mark);
> >
> > #else
> > #define audit_put_watch(w) {}
> > @@ -302,36 +298,19 @@ static inline char *audit_mark_path(struct
> > audit_fsnotify_mark *mark) }
> > #define audit_remove_mark(m) BUG()
> > #define audit_remove_mark_rule(k) BUG()
> > -static inline int audit_mark_compare(struct audit_fsnotify_mark *mark,
> > unsigned long ino, dev_t dev) -{
> > - BUG();
> > - return 0;
> > -}
> >
> > -static inline int audit_make_exe_rule(struct audit_krule *krule, char
> > *pathname, int len, u32 op) -{
> > - return -EINVAL;
> > -}
> > -static inline void audit_remove_exe_rule(struct audit_krule *krule)
> > -{
> > - BUG();
> > - return 0;
> > -}
> > -static inline char *audit_exe_path(struct audit_exe *exe)
> > +static inline int audit_exe_compare(struct task_struct *tsk, struct
> > audit_fsnotify_mark *mark) {
> > BUG();
> > - return "";
> > + return -EINVAL;
> > }
> > +
> > static inline int audit_dupe_exe(struct audit_krule *new, struct
> > audit_krule *old) {
> > BUG();
> > - return -EINVAL
> > -}
> > -static inline int audit_exe_compare(struct task_struct *tsk, struct
> > audit_exe *exe) -{
> > - BUG();
> > - return 0;
> > + return -EINVAL;
> > }
> > +
> > #endif /* CONFIG_AUDIT_WATCH */
> >
> > #ifdef CONFIG_AUDIT_TREE
> > diff --git a/kernel/audit_exe.c b/kernel/audit_exe.c
> > index d4cc8b5..75ad4f2 100644
> > --- a/kernel/audit_exe.c
> > +++ b/kernel/audit_exe.c
> > @@ -17,93 +17,30 @@
> >
> > #include <linux/kernel.h>
> > #include <linux/audit.h>
> > -#include <linux/mutex.h>
> > #include <linux/fs.h>
> > #include <linux/namei.h>
> > #include <linux/slab.h>
> > #include "audit.h"
> >
> > -struct audit_exe {
> > - char *pathname;
> > - unsigned long ino;
> > - dev_t dev;
> > -};
> > -
> > -/* Translate a watch string to kernel respresentation. */
> > -int audit_make_exe_rule(struct audit_krule *krule, char *pathname, int len,
> > u32 op) -{
> > - struct audit_exe *exe;
> > - struct path path;
> > - struct dentry *dentry;
> > - unsigned long ino;
> > - dev_t dev;
> > -
> > - if (pathname[0] != '/' || pathname[len-1] == '/')
> > - return -EINVAL;
> > -
> > - dentry = kern_path_locked(pathname, &path);
> > - if (IS_ERR(dentry))
> > - return PTR_ERR(dentry);
> > - mutex_unlock(&path.dentry->d_inode->i_mutex);
> > -
> > - if (!dentry->d_inode)
> > - return -ENOENT;
> > - dev = dentry->d_inode->i_sb->s_dev;
> > - ino = dentry->d_inode->i_ino;
> > - dput(dentry);
> > -
> > - exe = kmalloc(sizeof(*exe), GFP_KERNEL);
> > - if (!exe)
> > - return -ENOMEM;
> > - exe->ino = ino;
> > - exe->dev = dev;
> > - exe->pathname = pathname;
> > - krule->exe = exe;
> > -
> > - return 0;
> > -}
> > -
> > -void audit_remove_exe_rule(struct audit_krule *krule)
> > -{
> > - struct audit_exe *exe;
> > -
> > - exe = krule->exe;
> > - krule->exe = NULL;
> > - kfree(exe->pathname);
> > - kfree(exe);
> > -}
> > -
> > -char *audit_exe_path(struct audit_exe *exe)
> > -{
> > - return exe->pathname;
> > -}
> > -
> > int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
> > {
> > - struct audit_exe *exe;
> > -
> > - exe = kmalloc(sizeof(*exe), GFP_KERNEL);
> > - if (!exe)
> > - return -ENOMEM;
> > + struct audit_fsnotify_mark *audit_mark;
> > + char *pathname;
> >
> > - exe->pathname = kstrdup(old->exe->pathname, GFP_KERNEL);
> > - if (!exe->pathname) {
> > - kfree(exe);
> > - return -ENOMEM;
> > - }
> > + pathname = audit_mark_path(old->exe);
> >
> > - exe->ino = old->exe->ino;
> > - exe->dev = old->exe->dev;
> > - new->exe = exe;
> > + audit_mark = audit_alloc_mark(new, pathname, strlen(pathname));
> > + if (IS_ERR(audit_mark))
> > + return PTR_ERR(audit_mark);
> > + new->exe = audit_mark;
> >
> > return 0;
> > }
> >
> > -int audit_exe_compare(struct task_struct *tsk, struct audit_exe *exe)
> > +int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark
> > *mark) {
> > - if (tsk->mm->exe_file->f_inode->i_ino != exe->ino)
> > - return 0;
> > - if (tsk->mm->exe_file->f_inode->i_sb->s_dev != exe->dev)
> > - return 0;
> > - return 1;
> > + unsigned long ino = tsk->mm->exe_file->f_inode->i_ino;
> > + dev_t dev = tsk->mm->exe_file->f_inode->i_sb->s_dev;
> > +
> > + return audit_mark_compare(mark, ino, dev);
> > }
> > diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
> > index b0f9877..94ecdab 100644
> > --- a/kernel/audit_tree.c
> > +++ b/kernel/audit_tree.c
> > @@ -479,6 +479,8 @@ static void kill_rules(struct audit_tree *tree)
> > if (rule->tree) {
> > /* not a half-baked one */
> > audit_tree_log_remove_rule(rule);
> > + if (entry->rule.exe)
> > + audit_remove_mark(entry->rule.exe);
> > rule->tree = NULL;
> > list_del_rcu(&entry->list);
> > list_del(&entry->rule.list);
> > diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
> > index 8f123d7..4aaa393 100644
> > --- a/kernel/audit_watch.c
> > +++ b/kernel/audit_watch.c
> > @@ -312,6 +312,8 @@ static void audit_update_watch(struct audit_parent
> > *parent, list_replace(&oentry->rule.list,
> > &nentry->rule.list);
> > }
> > + if (oentry->rule.exe)
> > + audit_remove_mark(oentry->rule.exe);
> >
> > audit_watch_log_rule_change(r, owatch, "updated_rules");
> >
> > @@ -342,6 +344,8 @@ static void audit_remove_parent_watches(struct
> > audit_parent *parent) list_for_each_entry_safe(r, nextr, &w->rules, rlist)
> > {
> > e = container_of(r, struct audit_entry, rule);
> > audit_watch_log_rule_change(r, w, "remove_rule");
> > + if (e->rule.exe)
> > + audit_remove_mark(e->rule.exe);
> > list_del(&r->rlist);
> > list_del(&r->list);
> > list_del_rcu(&e->list);
> > diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
> > index bbb39ec..f65c97f 100644
> > --- a/kernel/auditfilter.c
> > +++ b/kernel/auditfilter.c
> > @@ -426,6 +426,7 @@ static struct audit_entry *audit_data_to_entry(struct
> > audit_rule_data *data, size_t remain = datasz - sizeof(struct
> > audit_rule_data);
> > int i;
> > char *str;
> > + struct audit_fsnotify_mark *audit_mark;
> >
> > entry = audit_to_entry_common(data);
> > if (IS_ERR(entry))
> > @@ -557,11 +558,13 @@ static struct audit_entry *audit_data_to_entry(struct
> > audit_rule_data *data, }
> > entry->rule.buflen += f->val;
> >
> > - err = audit_make_exe_rule(&entry->rule, str, f->val, f->op);
> > - if (err) {
> > - kfree(str);
> > + audit_mark = audit_alloc_mark(&entry->rule, str, f->val);
> > + kfree(str);
> > + if (IS_ERR(audit_mark)) {
> > + err = PTR_ERR(audit_mark);
> > goto exit_free;
> > }
> > + entry->rule.exe = audit_mark;
> > break;
> > }
> > }
> > @@ -575,6 +578,8 @@ exit_nofree:
> > exit_free:
> > if (entry->rule.tree)
> > audit_put_tree(entry->rule.tree); /* that's the temporary one */
> > + if (entry->rule.exe)
> > + audit_remove_mark(entry->rule.exe); /* that's the template one */
> > audit_free_rule(entry);
> > return ERR_PTR(err);
> > }
> > @@ -642,7 +647,7 @@ static struct audit_rule_data
> > *audit_krule_to_data(struct audit_krule *krule) case AUDIT_EXE:
> > case AUDIT_EXE_CHILDREN:
> > data->buflen += data->values[i] =
> > - audit_pack_string(&bufp, audit_exe_path(krule->exe));
> > + audit_pack_string(&bufp, audit_mark_path(krule->exe));
> > break;
> > case AUDIT_LOGINUID_SET:
> > if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) {
> > @@ -710,8 +715,8 @@ static int audit_compare_rule(struct audit_krule *a,
> > struct audit_krule *b) case AUDIT_EXE:
> > case AUDIT_EXE_CHILDREN:
> > /* both paths exist based on above type compare */
> > - if (strcmp(audit_exe_path(a->exe),
> > - audit_exe_path(b->exe)))
> > + if (strcmp(audit_mark_path(a->exe),
> > + audit_mark_path(b->exe)))
> > return 1;
> > break;
> > case AUDIT_UID:
> > @@ -842,6 +847,8 @@ struct audit_entry *audit_dupe_rule(struct audit_krule
> > *old) break;
> > }
> > if (err) {
> > + if (new->exe)
> > + audit_remove_mark(new->exe);
> > audit_free_rule(entry);
> > return ERR_PTR(err);
> > }
> > @@ -1008,7 +1015,7 @@ int audit_del_rule(struct audit_entry *entry)
> > audit_remove_tree_rule(&e->rule);
> >
> > if (e->rule.exe)
> > - audit_remove_exe_rule(&e->rule);
> > + audit_remove_mark_rule(&e->rule);
> >
> > list_del_rcu(&e->list);
> > list_del(&e->rule.list);
> > @@ -1113,8 +1120,11 @@ int audit_rule_change(int type, __u32 portid, int
> > seq, void *data, WARN_ON(1);
> > }
> >
> > - if (err || type == AUDIT_DEL_RULE)
> > + if (err || type == AUDIT_DEL_RULE) {
> > + if (entry->rule.exe)
> > + audit_remove_mark(entry->rule.exe);
> > audit_free_rule(entry);
> > + }
> >
> > return err;
> > }
> > @@ -1406,6 +1416,8 @@ static int update_lsm_rule(struct audit_krule *r)
> > return 0;
> >
> > nentry = audit_dupe_rule(r);
> > + if (entry->rule.exe)
> > + audit_remove_mark(entry->rule.exe);
> > if (IS_ERR(nentry)) {
> > /* save the first error encountered for the
> > * return value */
>
> --
> paul moore
> security @ redhat
>

- RGB

--
Richard Guy Briggs <rbriggs@redhat.com>
Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat
Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545