From: Eric Paris <epa...@redhat.com>

This patch implements the ability to filter on the executable.  It is
clearly incomplete!  This patch adds the inode/dev of the executable at
the moment the rule is loaded.  It does not update if the executable is
updated/moved/whatever.  That should be added.  But at this moment, this
patch works.

Based-on-user-interface-by: Richard Guy Briggs <r...@redhat.com>
Cc: r...@redhat.com
Based-on-idea-by: Peter Moody <pmo...@google.com>
Cc: pmo...@google.com
Signed-off-by: Eric Paris <epa...@redhat.com>
Signed-off-by: Richard Guy Briggs <r...@redhat.com>
---
 include/linux/audit.h      |    1 +
 include/uapi/linux/audit.h |    2 +
 kernel/Makefile            |    2 +-
 kernel/audit.h             |   27 ++++++++++
 kernel/audit_exe.c         |  113 ++++++++++++++++++++++++++++++++++++++++++++
 kernel/auditfilter.c       |   43 +++++++++++++++++
 kernel/auditsc.c           |   16 ++++++
 7 files changed, 203 insertions(+), 1 deletions(-)
 create mode 100644 kernel/audit_exe.c

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 22cfddb..227171c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -59,6 +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 list_head        rlist;  /* entry in audit_{watch,tree}.rules 
list */
        struct list_head        list;   /* for AUDIT_LIST* purposes only */
        u64                     prio;
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 573dc36..f4a72b9 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -266,6 +266,8 @@
 #define AUDIT_OBJ_UID  109
 #define AUDIT_OBJ_GID  110
 #define AUDIT_FIELD_COMPARE    111
+#define AUDIT_EXE      112
+#define AUDIT_EXE_CHILDREN     113
 
 #define AUDIT_ARG0      200
 #define AUDIT_ARG1      (AUDIT_ARG0+1)
diff --git a/kernel/Makefile b/kernel/Makefile
index bc010ee..a1d5715 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -61,7 +61,7 @@ obj-$(CONFIG_SMP) += stop_machine.o
 obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
 obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
-obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o
+obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o audit_exe.o
 obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
 obj-$(CONFIG_GCOV_KERNEL) += gcov/
 obj-$(CONFIG_KPROBES) += kprobes.o
diff --git a/kernel/audit.h b/kernel/audit.h
index 7bb6573..58ed955 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -56,6 +56,7 @@ enum audit_state {
 
 /* Rule lists */
 struct audit_watch;
+struct audit_exe;
 struct audit_tree;
 struct audit_chunk;
 
@@ -280,6 +281,13 @@ extern int audit_add_watch(struct audit_krule *krule, 
struct list_head **list);
 extern void audit_remove_watch_rule(struct audit_krule *krule);
 extern char *audit_watch_path(struct audit_watch *watch);
 extern int audit_watch_compare(struct audit_watch *watch, unsigned long ino, 
dev_t dev);
+
+int audit_make_exe_rule(struct audit_krule *krule, char *pathname, int len, 
u32 op);
+void audit_remove_exe_rule(struct audit_krule *krule);
+char *audit_exe_path(struct audit_exe *exe);
+int audit_dup_exe(struct audit_krule *new, struct audit_krule *old);
+int audit_exe_compare(struct task_struct *tsk, struct audit_exe *exe);
+
 #else
 #define audit_put_watch(w) {}
 #define audit_get_watch(w) {}
@@ -289,6 +297,25 @@ extern int audit_watch_compare(struct audit_watch *watch, 
unsigned long ino, dev
 #define audit_watch_path(w) ""
 #define audit_watch_compare(w, i, d) 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) {
+       BUG();
+       return "";
+}
+static inline int audit_dup_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;
+}
 #endif /* CONFIG_AUDIT_WATCH */
 
 #ifdef CONFIG_AUDIT_TREE
diff --git a/kernel/audit_exe.c b/kernel/audit_exe.c
new file mode 100644
index 0000000..09c436c
--- /dev/null
+++ b/kernel/audit_exe.c
@@ -0,0 +1,113 @@
+/* audit_exe.c -- filtering of audit events
+ *
+ * Copyright 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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_dup_exe(struct audit_krule *new, struct audit_krule *old)
+{
+       struct audit_exe *exe;
+
+       exe = kmalloc(sizeof(*exe), GFP_KERNEL);
+       if (!exe)
+               return -ENOMEM;
+
+       exe->pathname = kstrdup(old->exe->pathname, GFP_KERNEL);
+       if (!exe->pathname) {
+               kfree(exe);
+               return -ENOMEM;
+       }
+
+       exe->ino = old->exe->ino;
+       exe->dev = old->exe->dev;
+       new->exe = exe;
+
+       return 0;
+}
+
+int audit_exe_compare(struct task_struct *tsk, struct audit_exe *exe)
+{
+       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;
+}
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 8e9bc9c..9caeaf5 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -390,6 +390,13 @@ static int audit_field_valid(struct audit_entry *entry, 
struct audit_field *f)
                if (f->val > AUDIT_MAX_FIELD_COMPARE)
                        return -EINVAL;
                break;
+       case AUDIT_EXE:
+       case AUDIT_EXE_CHILDREN:
+               if (f->op != Audit_equal)
+                       return -EINVAL;
+               if (entry->rule.listnr != AUDIT_FILTER_EXIT)
+                       return -EINVAL;
+               break;
        };
        return 0;
 }
@@ -541,6 +548,23 @@ 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_EXE:
+               case AUDIT_EXE_CHILDREN:
+                       if (entry->rule.exe || f->val > PATH_MAX)
+                               goto exit_free;
+                       str = audit_unpack_string(&bufp, &remain, f->val);
+                       if (IS_ERR(str)) {
+                               err = PTR_ERR(str);
+                               goto exit_free;
+                       }
+                       entry->rule.buflen += f->val;
+
+                       err = audit_make_exe_rule(&entry->rule, str, f->val, 
f->op);
+                       if (err) {
+                               kfree(str);
+                               goto exit_free;
+                       }
+                       break;
                }
        }
 
@@ -619,6 +643,11 @@ static struct audit_rule_data *audit_krule_to_data(struct 
audit_krule *krule)
                        data->buflen += data->values[i] =
                                audit_pack_string(&bufp, krule->filterkey);
                        break;
+               case AUDIT_EXE:
+               case AUDIT_EXE_CHILDREN:
+                       data->buflen += data->values[i] =
+                               audit_pack_string(&bufp, 
audit_exe_path(krule->exe));
+                       break;
                default:
                        data->values[i] = f->val;
                }
@@ -674,6 +703,13 @@ static int audit_compare_rule(struct audit_krule *a, 
struct audit_krule *b)
                        if (strcmp(a->filterkey, b->filterkey))
                                return 1;
                        break;
+               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)))
+                               return 1;
+                       break;
                case AUDIT_UID:
                case AUDIT_EUID:
                case AUDIT_SUID:
@@ -795,6 +831,11 @@ struct audit_entry *audit_dupe_rule(struct audit_krule 
*old)
                                err = -ENOMEM;
                        else
                                new->filterkey = fk;
+                       break;
+               case AUDIT_EXE:
+               case AUDIT_EXE_CHILDREN:
+                       err = audit_dup_exe(new, old);
+                       break;
                }
                if (err) {
                        audit_free_rule(entry);
@@ -966,6 +1007,8 @@ static inline int audit_del_rule(struct audit_entry *entry)
        if (e->rule.tree)
                audit_remove_tree_rule(&e->rule);
 
+       if (e->rule.exe)
+               audit_remove_exe_rule(&e->rule);
        list_del_rcu(&e->list);
        list_del(&e->rule.list);
        call_rcu(&e->rcu, audit_free_rule_rcu);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index f251a5e..2513726 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -48,6 +48,7 @@
 #include <asm/types.h>
 #include <linux/atomic.h>
 #include <linux/fs.h>
+#include <linux/dcache.h>
 #include <linux/namei.h>
 #include <linux/mm.h>
 #include <linux/export.h>
@@ -70,6 +71,7 @@
 #include <linux/capability.h>
 #include <linux/fs_struct.h>
 #include <linux/compat.h>
+#include <linux/sched.h>
 #include <linux/ctype.h>
 
 #include "audit.h"
@@ -471,6 +473,20 @@ static int audit_filter_rules(struct task_struct *tsk,
                                result = audit_comparator(ctx->ppid, f->op, 
f->val);
                        }
                        break;
+               case AUDIT_EXE:
+                       result = audit_exe_compare(tsk, rule->exe);
+                       break;
+               case AUDIT_EXE_CHILDREN:
+               {
+                       struct task_struct *ptsk;
+                       for (ptsk = tsk; ptsk->parent->pid > 0; ptsk = 
find_task_by_vpid(ptsk->parent->pid)) {
+                               if (audit_exe_compare(ptsk, rule->exe)) {
+                                       ++result;
+                                       break;
+                               }
+                       }
+               }
+                       break;
                case AUDIT_UID:
                        result = audit_uid_comparator(cred->uid, f->op, f->uid);
                        break;
-- 
1.7.1

--
Linux-audit mailing list
Linux-audit@redhat.com
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to