mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
Merge branch 'upstream' of git://git.infradead.org/users/pcmoore/audit
Pull audit update from Paul Moore: "This is one of the larger audit patchsets in recent history, consisting of eight patches and almost 400 lines of changes. The bulk of the patchset is the new "audit by executable" functionality which allows admins to set an audit watch based on the executable on disk. Prior to this, admins could only track an application by PID, which has some obvious limitations. Beyond the new functionality we also have some refcnt fixes and a few minor cleanups" * 'upstream' of git://git.infradead.org/users/pcmoore/audit: fixup: audit: implement audit by executable audit: implement audit by executable audit: clean simple fsnotify implementation audit: use macros for unset inode and device values audit: make audit_del_rule() more robust audit: fix uninitialized variable in audit_add_rule() audit: eliminate unnecessary extra layer of watch parent references audit: eliminate unnecessary extra layer of watch references
This commit is contained in:
commit
425afcff13
@ -27,6 +27,9 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <uapi/linux/audit.h>
|
||||
|
||||
#define AUDIT_INO_UNSET ((unsigned long)-1)
|
||||
#define AUDIT_DEV_UNSET ((dev_t)-1)
|
||||
|
||||
struct audit_sig_info {
|
||||
uid_t uid;
|
||||
pid_t pid;
|
||||
@ -59,6 +62,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_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;
|
||||
|
@ -266,6 +266,7 @@
|
||||
#define AUDIT_OBJ_UID 109
|
||||
#define AUDIT_OBJ_GID 110
|
||||
#define AUDIT_FIELD_COMPARE 111
|
||||
#define AUDIT_EXE 112
|
||||
|
||||
#define AUDIT_ARG0 200
|
||||
#define AUDIT_ARG1 (AUDIT_ARG0+1)
|
||||
@ -324,8 +325,10 @@ enum {
|
||||
|
||||
#define AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT 0x00000001
|
||||
#define AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME 0x00000002
|
||||
#define AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH 0x00000004
|
||||
#define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \
|
||||
AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME)
|
||||
AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \
|
||||
AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH)
|
||||
|
||||
/* deprecated: AUDIT_VERSION_* */
|
||||
#define AUDIT_VERSION_LATEST AUDIT_FEATURE_BITMAP_ALL
|
||||
|
@ -64,7 +64,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_fsnotify.o
|
||||
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
|
||||
obj-$(CONFIG_GCOV_KERNEL) += gcov/
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
@ -1761,7 +1761,7 @@ void audit_log_name(struct audit_context *context, struct audit_names *n,
|
||||
} else
|
||||
audit_log_format(ab, " name=(null)");
|
||||
|
||||
if (n->ino != (unsigned long)-1)
|
||||
if (n->ino != AUDIT_INO_UNSET)
|
||||
audit_log_format(ab, " inode=%lu"
|
||||
" dev=%02x:%02x mode=%#ho"
|
||||
" ouid=%u ogid=%u rdev=%02x:%02x",
|
||||
|
@ -50,6 +50,7 @@ enum audit_state {
|
||||
|
||||
/* Rule lists */
|
||||
struct audit_watch;
|
||||
struct audit_fsnotify_mark;
|
||||
struct audit_tree;
|
||||
struct audit_chunk;
|
||||
|
||||
@ -252,6 +253,7 @@ struct audit_net {
|
||||
extern int selinux_audit_rule_update(void);
|
||||
|
||||
extern struct mutex audit_filter_mutex;
|
||||
extern int audit_del_rule(struct audit_entry *);
|
||||
extern void audit_free_rule_rcu(struct rcu_head *);
|
||||
extern struct list_head audit_filter_list[];
|
||||
|
||||
@ -269,6 +271,15 @@ 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);
|
||||
|
||||
extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len);
|
||||
extern char *audit_mark_path(struct audit_fsnotify_mark *mark);
|
||||
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_dupe_exe(struct audit_krule *new, struct audit_krule *old);
|
||||
extern int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark);
|
||||
|
||||
#else
|
||||
#define audit_put_watch(w) {}
|
||||
#define audit_get_watch(w) {}
|
||||
@ -278,6 +289,13 @@ 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
|
||||
|
||||
#define audit_alloc_mark(k, p, l) (ERR_PTR(-EINVAL))
|
||||
#define audit_mark_path(m) ""
|
||||
#define audit_remove_mark(m)
|
||||
#define audit_remove_mark_rule(k)
|
||||
#define audit_mark_compare(m, i, d) 0
|
||||
#define audit_exe_compare(t, m) (-EINVAL)
|
||||
#define audit_dupe_exe(n, o) (-EINVAL)
|
||||
#endif /* CONFIG_AUDIT_WATCH */
|
||||
|
||||
#ifdef CONFIG_AUDIT_TREE
|
||||
|
216
kernel/audit_fsnotify.c
Normal file
216
kernel/audit_fsnotify.c
Normal file
@ -0,0 +1,216 @@
|
||||
/* audit_fsnotify.c -- tracking inodes
|
||||
*
|
||||
* Copyright 2003-2009,2014-2015 Red Hat, Inc.
|
||||
* Copyright 2005 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2005 IBM Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fsnotify_backend.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/security.h>
|
||||
#include "audit.h"
|
||||
|
||||
/*
|
||||
* this mark lives on the parent directory of the inode in question.
|
||||
* but dev, ino, and path are about the child
|
||||
*/
|
||||
struct audit_fsnotify_mark {
|
||||
dev_t dev; /* associated superblock device */
|
||||
unsigned long ino; /* associated inode number */
|
||||
char *path; /* insertion path */
|
||||
struct fsnotify_mark mark; /* fsnotify mark on the inode */
|
||||
struct audit_krule *rule;
|
||||
};
|
||||
|
||||
/* fsnotify handle. */
|
||||
static struct fsnotify_group *audit_fsnotify_group;
|
||||
|
||||
/* fsnotify events we care about. */
|
||||
#define AUDIT_FS_EVENTS (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
|
||||
FS_MOVE_SELF | FS_EVENT_ON_CHILD)
|
||||
|
||||
static void audit_fsnotify_mark_free(struct audit_fsnotify_mark *audit_mark)
|
||||
{
|
||||
kfree(audit_mark->path);
|
||||
kfree(audit_mark);
|
||||
}
|
||||
|
||||
static void audit_fsnotify_free_mark(struct fsnotify_mark *mark)
|
||||
{
|
||||
struct audit_fsnotify_mark *audit_mark;
|
||||
|
||||
audit_mark = container_of(mark, struct audit_fsnotify_mark, mark);
|
||||
audit_fsnotify_mark_free(audit_mark);
|
||||
}
|
||||
|
||||
char *audit_mark_path(struct audit_fsnotify_mark *mark)
|
||||
{
|
||||
return mark->path;
|
||||
}
|
||||
|
||||
int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_t dev)
|
||||
{
|
||||
if (mark->ino == AUDIT_INO_UNSET)
|
||||
return 0;
|
||||
return (mark->ino == ino) && (mark->dev == dev);
|
||||
}
|
||||
|
||||
static void audit_update_mark(struct audit_fsnotify_mark *audit_mark,
|
||||
struct inode *inode)
|
||||
{
|
||||
audit_mark->dev = inode ? inode->i_sb->s_dev : AUDIT_DEV_UNSET;
|
||||
audit_mark->ino = inode ? inode->i_ino : AUDIT_INO_UNSET;
|
||||
}
|
||||
|
||||
struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len)
|
||||
{
|
||||
struct audit_fsnotify_mark *audit_mark;
|
||||
struct path path;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
if (pathname[0] != '/' || pathname[len-1] == '/')
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
dentry = kern_path_locked(pathname, &path);
|
||||
if (IS_ERR(dentry))
|
||||
return (void *)dentry; /* returning an error */
|
||||
inode = path.dentry->d_inode;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL);
|
||||
if (unlikely(!audit_mark)) {
|
||||
audit_mark = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fsnotify_init_mark(&audit_mark->mark, audit_fsnotify_free_mark);
|
||||
audit_mark->mark.mask = AUDIT_FS_EVENTS;
|
||||
audit_mark->path = pathname;
|
||||
audit_update_mark(audit_mark, dentry->d_inode);
|
||||
audit_mark->rule = krule;
|
||||
|
||||
ret = fsnotify_add_mark(&audit_mark->mark, audit_fsnotify_group, inode, NULL, true);
|
||||
if (ret < 0) {
|
||||
audit_fsnotify_mark_free(audit_mark);
|
||||
audit_mark = ERR_PTR(ret);
|
||||
}
|
||||
out:
|
||||
dput(dentry);
|
||||
path_put(&path);
|
||||
return audit_mark;
|
||||
}
|
||||
|
||||
static void audit_mark_log_rule_change(struct audit_fsnotify_mark *audit_mark, char *op)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
struct audit_krule *rule = audit_mark->rule;
|
||||
|
||||
if (!audit_enabled)
|
||||
return;
|
||||
ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_format(ab, "auid=%u ses=%u op=",
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
audit_log_string(ab, op);
|
||||
audit_log_format(ab, " path=");
|
||||
audit_log_untrustedstring(ab, audit_mark->path);
|
||||
audit_log_key(ab, rule->filterkey);
|
||||
audit_log_format(ab, " list=%d res=1", rule->listnr);
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
||||
void audit_remove_mark(struct audit_fsnotify_mark *audit_mark)
|
||||
{
|
||||
fsnotify_destroy_mark(&audit_mark->mark, audit_fsnotify_group);
|
||||
fsnotify_put_mark(&audit_mark->mark);
|
||||
}
|
||||
|
||||
void audit_remove_mark_rule(struct audit_krule *krule)
|
||||
{
|
||||
struct audit_fsnotify_mark *mark = krule->exe;
|
||||
|
||||
audit_remove_mark(mark);
|
||||
}
|
||||
|
||||
static void audit_autoremove_mark_rule(struct audit_fsnotify_mark *audit_mark)
|
||||
{
|
||||
struct audit_krule *rule = audit_mark->rule;
|
||||
struct audit_entry *entry = container_of(rule, struct audit_entry, rule);
|
||||
|
||||
audit_mark_log_rule_change(audit_mark, "autoremove_rule");
|
||||
audit_del_rule(entry);
|
||||
}
|
||||
|
||||
/* Update mark data in audit rules based on fsnotify events. */
|
||||
static int audit_mark_handle_event(struct fsnotify_group *group,
|
||||
struct inode *to_tell,
|
||||
struct fsnotify_mark *inode_mark,
|
||||
struct fsnotify_mark *vfsmount_mark,
|
||||
u32 mask, void *data, int data_type,
|
||||
const unsigned char *dname, u32 cookie)
|
||||
{
|
||||
struct audit_fsnotify_mark *audit_mark;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
audit_mark = container_of(inode_mark, struct audit_fsnotify_mark, mark);
|
||||
|
||||
BUG_ON(group != audit_fsnotify_group);
|
||||
|
||||
switch (data_type) {
|
||||
case (FSNOTIFY_EVENT_PATH):
|
||||
inode = ((struct path *)data)->dentry->d_inode;
|
||||
break;
|
||||
case (FSNOTIFY_EVENT_INODE):
|
||||
inode = (struct inode *)data;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (mask & (FS_CREATE|FS_MOVED_TO|FS_DELETE|FS_MOVED_FROM)) {
|
||||
if (audit_compare_dname_path(dname, audit_mark->path, AUDIT_NAME_FULL))
|
||||
return 0;
|
||||
audit_update_mark(audit_mark, inode);
|
||||
} else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
|
||||
audit_autoremove_mark_rule(audit_mark);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fsnotify_ops audit_mark_fsnotify_ops = {
|
||||
.handle_event = audit_mark_handle_event,
|
||||
};
|
||||
|
||||
static int __init audit_fsnotify_init(void)
|
||||
{
|
||||
audit_fsnotify_group = fsnotify_alloc_group(&audit_mark_fsnotify_ops);
|
||||
if (IS_ERR(audit_fsnotify_group)) {
|
||||
audit_fsnotify_group = NULL;
|
||||
audit_panic("cannot create audit fsnotify group");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
device_initcall(audit_fsnotify_init);
|
@ -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);
|
||||
|
@ -138,7 +138,7 @@ char *audit_watch_path(struct audit_watch *watch)
|
||||
|
||||
int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
|
||||
{
|
||||
return (watch->ino != (unsigned long)-1) &&
|
||||
return (watch->ino != AUDIT_INO_UNSET) &&
|
||||
(watch->ino == ino) &&
|
||||
(watch->dev == dev);
|
||||
}
|
||||
@ -179,8 +179,8 @@ static struct audit_watch *audit_init_watch(char *path)
|
||||
INIT_LIST_HEAD(&watch->rules);
|
||||
atomic_set(&watch->count, 1);
|
||||
watch->path = path;
|
||||
watch->dev = (dev_t)-1;
|
||||
watch->ino = (unsigned long)-1;
|
||||
watch->dev = AUDIT_DEV_UNSET;
|
||||
watch->ino = AUDIT_INO_UNSET;
|
||||
|
||||
return watch;
|
||||
}
|
||||
@ -203,7 +203,6 @@ int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op)
|
||||
if (IS_ERR(watch))
|
||||
return PTR_ERR(watch);
|
||||
|
||||
audit_get_watch(watch);
|
||||
krule->watch = watch;
|
||||
|
||||
return 0;
|
||||
@ -313,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");
|
||||
|
||||
@ -343,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);
|
||||
@ -387,19 +390,20 @@ static void audit_add_to_parent(struct audit_krule *krule,
|
||||
|
||||
watch_found = 1;
|
||||
|
||||
/* put krule's and initial refs to temporary watch */
|
||||
audit_put_watch(watch);
|
||||
/* put krule's ref to temporary watch */
|
||||
audit_put_watch(watch);
|
||||
|
||||
audit_get_watch(w);
|
||||
krule->watch = watch = w;
|
||||
|
||||
audit_put_parent(parent);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!watch_found) {
|
||||
audit_get_parent(parent);
|
||||
watch->parent = parent;
|
||||
|
||||
audit_get_watch(watch);
|
||||
list_add(&watch->wlist, &parent->watches);
|
||||
}
|
||||
list_add(&krule->rlist, &watch->rules);
|
||||
@ -437,9 +441,6 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
|
||||
|
||||
audit_add_to_parent(krule, parent);
|
||||
|
||||
/* match get in audit_find_parent or audit_init_parent */
|
||||
audit_put_parent(parent);
|
||||
|
||||
h = audit_hash_ino((u32)watch->ino);
|
||||
*list = &audit_inode_hash[h];
|
||||
error:
|
||||
@ -496,7 +497,7 @@ static int audit_watch_handle_event(struct fsnotify_group *group,
|
||||
if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
|
||||
audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
|
||||
else if (mask & (FS_DELETE|FS_MOVED_FROM))
|
||||
audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
|
||||
audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1);
|
||||
else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
|
||||
audit_remove_parent_watches(parent);
|
||||
|
||||
@ -517,3 +518,36 @@ static int __init audit_watch_init(void)
|
||||
return 0;
|
||||
}
|
||||
device_initcall(audit_watch_init);
|
||||
|
||||
int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
|
||||
{
|
||||
struct audit_fsnotify_mark *audit_mark;
|
||||
char *pathname;
|
||||
|
||||
pathname = kstrdup(audit_mark_path(old->exe), GFP_KERNEL);
|
||||
if (!pathname)
|
||||
return -ENOMEM;
|
||||
|
||||
audit_mark = audit_alloc_mark(new, pathname, strlen(pathname));
|
||||
if (IS_ERR(audit_mark)) {
|
||||
kfree(pathname);
|
||||
return PTR_ERR(audit_mark);
|
||||
}
|
||||
new->exe = audit_mark;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark)
|
||||
{
|
||||
struct file *exe_file;
|
||||
unsigned long ino;
|
||||
dev_t dev;
|
||||
|
||||
rcu_read_lock();
|
||||
exe_file = rcu_dereference(tsk->mm->exe_file);
|
||||
ino = exe_file->f_inode->i_ino;
|
||||
dev = exe_file->f_inode->i_sb->s_dev;
|
||||
rcu_read_unlock();
|
||||
return audit_mark_compare(mark, ino, dev);
|
||||
}
|
||||
|
@ -405,6 +405,12 @@ 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:
|
||||
if (f->op != Audit_equal)
|
||||
return -EINVAL;
|
||||
if (entry->rule.listnr != AUDIT_FILTER_EXIT)
|
||||
return -EINVAL;
|
||||
break;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
@ -419,6 +425,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))
|
||||
@ -539,6 +546,24 @@ 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:
|
||||
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;
|
||||
|
||||
audit_mark = audit_alloc_mark(&entry->rule, str, f->val);
|
||||
if (IS_ERR(audit_mark)) {
|
||||
kfree(str);
|
||||
err = PTR_ERR(audit_mark);
|
||||
goto exit_free;
|
||||
}
|
||||
entry->rule.exe = audit_mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,10 +574,10 @@ exit_nofree:
|
||||
return entry;
|
||||
|
||||
exit_free:
|
||||
if (entry->rule.watch)
|
||||
audit_put_watch(entry->rule.watch); /* matches initial get */
|
||||
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);
|
||||
}
|
||||
@ -617,6 +642,10 @@ 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:
|
||||
data->buflen += data->values[i] =
|
||||
audit_pack_string(&bufp, audit_mark_path(krule->exe));
|
||||
break;
|
||||
case AUDIT_LOGINUID_SET:
|
||||
if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) {
|
||||
data->fields[i] = AUDIT_LOGINUID;
|
||||
@ -680,6 +709,12 @@ 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:
|
||||
/* both paths exist based on above type compare */
|
||||
if (strcmp(audit_mark_path(a->exe),
|
||||
audit_mark_path(b->exe)))
|
||||
return 1;
|
||||
break;
|
||||
case AUDIT_UID:
|
||||
case AUDIT_EUID:
|
||||
case AUDIT_SUID:
|
||||
@ -801,8 +836,14 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old)
|
||||
err = -ENOMEM;
|
||||
else
|
||||
new->filterkey = fk;
|
||||
break;
|
||||
case AUDIT_EXE:
|
||||
err = audit_dupe_exe(new, old);
|
||||
break;
|
||||
}
|
||||
if (err) {
|
||||
if (new->exe)
|
||||
audit_remove_mark(new->exe);
|
||||
audit_free_rule(entry);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@ -863,7 +904,7 @@ static inline int audit_add_rule(struct audit_entry *entry)
|
||||
struct audit_watch *watch = entry->rule.watch;
|
||||
struct audit_tree *tree = entry->rule.tree;
|
||||
struct list_head *list;
|
||||
int err;
|
||||
int err = 0;
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
int dont_count = 0;
|
||||
|
||||
@ -881,7 +922,7 @@ static inline int audit_add_rule(struct audit_entry *entry)
|
||||
/* normally audit_add_tree_rule() will free it on failure */
|
||||
if (tree)
|
||||
audit_put_tree(tree);
|
||||
goto error;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (watch) {
|
||||
@ -895,14 +936,14 @@ static inline int audit_add_rule(struct audit_entry *entry)
|
||||
*/
|
||||
if (tree)
|
||||
audit_put_tree(tree);
|
||||
goto error;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (tree) {
|
||||
err = audit_add_tree_rule(&entry->rule);
|
||||
if (err) {
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
goto error;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -933,19 +974,13 @@ static inline int audit_add_rule(struct audit_entry *entry)
|
||||
#endif
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
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)
|
||||
int audit_del_rule(struct audit_entry *entry)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
struct audit_watch *watch = entry->rule.watch;
|
||||
struct audit_tree *tree = entry->rule.tree;
|
||||
struct list_head *list;
|
||||
int ret = 0;
|
||||
@ -961,7 +996,6 @@ static inline int audit_del_rule(struct audit_entry *entry)
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
e = audit_find_rule(entry, &list);
|
||||
if (!e) {
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
@ -972,9 +1006,8 @@ static inline int audit_del_rule(struct audit_entry *entry)
|
||||
if (e->rule.tree)
|
||||
audit_remove_tree_rule(&e->rule);
|
||||
|
||||
list_del_rcu(&e->list);
|
||||
list_del(&e->rule.list);
|
||||
call_rcu(&e->rcu, audit_free_rule_rcu);
|
||||
if (e->rule.exe)
|
||||
audit_remove_mark_rule(&e->rule);
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
if (!dont_count)
|
||||
@ -983,11 +1016,14 @@ static inline int audit_del_rule(struct audit_entry *entry)
|
||||
if (!audit_match_signal(entry))
|
||||
audit_signals--;
|
||||
#endif
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
list_del_rcu(&e->list);
|
||||
list_del(&e->rule.list);
|
||||
call_rcu(&e->rcu, audit_free_rule_rcu);
|
||||
|
||||
out:
|
||||
if (watch)
|
||||
audit_put_watch(watch); /* match initial get */
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
if (tree)
|
||||
audit_put_tree(tree); /* that's the temporary one */
|
||||
|
||||
@ -1077,8 +1113,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;
|
||||
}
|
||||
@ -1370,6 +1409,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 */
|
||||
|
@ -180,7 +180,7 @@ static int audit_match_filetype(struct audit_context *ctx, int val)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(n, &ctx->names_list, list) {
|
||||
if ((n->ino != -1) &&
|
||||
if ((n->ino != AUDIT_INO_UNSET) &&
|
||||
((n->mode & S_IFMT) == mode))
|
||||
return 1;
|
||||
}
|
||||
@ -466,6 +466,9 @@ 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_UID:
|
||||
result = audit_uid_comparator(cred->uid, f->op, f->uid);
|
||||
break;
|
||||
@ -1680,7 +1683,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
|
||||
aname->should_free = true;
|
||||
}
|
||||
|
||||
aname->ino = (unsigned long)-1;
|
||||
aname->ino = AUDIT_INO_UNSET;
|
||||
aname->type = type;
|
||||
list_add_tail(&aname->list, &context->names_list);
|
||||
|
||||
@ -1922,7 +1925,7 @@ void __audit_inode_child(const struct inode *parent,
|
||||
if (inode)
|
||||
audit_copy_inode(found_child, dentry, inode);
|
||||
else
|
||||
found_child->ino = (unsigned long)-1;
|
||||
found_child->ino = AUDIT_INO_UNSET;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__audit_inode_child);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user