[PATCH 01/14] audit: implement audit by executable

Richard Guy Briggs rgb at redhat.com
Wed Jun 18 03:09:36 UTC 2014


From: Eric Paris <eparis at 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 <rgb at redhat.com>
Cc: rgb at redhat.com
Based-on-idea-by: Peter Moody <pmoody at google.com>
Cc: pmoody at google.com
Signed-off-by: Eric Paris <eparis at redhat.com>
Signed-off-by: Richard Guy Briggs <rgb at 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




More information about the Linux-audit mailing list