[PATCH] POSIX MQ audit memleak fixes and helper inlines
by George C. Wilson
This is an incremental patch that addresses two issues with the previous patch
to audit POSIX message queue syscalls. It should apply cleanly to Al Viro's
current lspp.b15 git branch.
There were memory leaks introduced in several audit_posix_mq* functions. They
are triggered by bad data from userspace. Fixed.
Inlined aux record helper functions.
Tested with audit enabled, disabled, and audit compiled out of kernel.
Please apply.
include/linux/audit.h | 41 +++++++++++++++++++++++++++++++++++------
kernel/auditsc.c | 36 ++++++++++++++++++++++--------------
2 files changed, 57 insertions(+), 20 deletions(-)
Signed-off-by: George Wilson <ltcgcw(a)us.ibm.com>
--
diff --git a/include/linux/audit.h b/include/linux/audit.h
index aa93091..c783275 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -343,6 +343,11 @@ extern int audit_socketcall(int nargs, u
extern int audit_sockaddr(int len, void *addr);
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
extern int audit_set_macxattr(const char *name);
+extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr);
+extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout);
+extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
+extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
+extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
@@ -356,12 +361,36 @@ static inline int audit_ipc_set_perm(uns
return __audit_ipc_set_perm(qbytes, uid, gid, mode);
return 0;
}
-
-extern int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr);
-extern int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout);
-extern int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
-extern int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
-extern int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
+static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
+{
+ if (unlikely(current->audit_context))
+ return __audit_mq_open(oflag, mode, u_attr);
+ return 0;
+}
+static inline int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout)
+{
+ if (unlikely(current->audit_context))
+ return __audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
+ return 0;
+}
+static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout)
+{
+ if (unlikely(current->audit_context))
+ return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
+ return 0;
+}
+static inline int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
+{
+ if (unlikely(current->audit_context))
+ return __audit_mq_notify(mqdes, u_notification);
+ return 0;
+}
+static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
+{
+ if (unlikely(current->audit_context))
+ return __audit_mq_getsetattr(mqdes, mqstat);
+ return 0;
+}
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 52da1eb..f4b09a3 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1298,14 +1298,14 @@ uid_t audit_get_loginuid(struct audit_co
}
/**
- * audit_mq_open - record audit data for a POSIX MQ open
+ * __audit_mq_open - record audit data for a POSIX MQ open
* @oflag: open flag
* @mode: mode bits
* @u_attr: queue attributes
*
* Returns 0 for success or NULL context or < 0 on error.
*/
-int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
+int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
{
struct audit_aux_data_mq_open *ax;
struct audit_context *context = current->audit_context;
@@ -1321,8 +1321,10 @@ int audit_mq_open(int oflag, mode_t mode
return -ENOMEM;
if (u_attr != NULL) {
- if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr)))
+ if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr))) {
+ kfree(ax);
return -EFAULT;
+ }
} else
memset(&ax->attr, 0, sizeof(ax->attr));
@@ -1336,7 +1338,7 @@ int audit_mq_open(int oflag, mode_t mode
}
/**
- * audit_mq_timedsend - record audit data for a POSIX MQ timed send
+ * __audit_mq_timedsend - record audit data for a POSIX MQ timed send
* @mqdes: MQ descriptor
* @msg_len: Message length
* @msg_prio: Message priority
@@ -1344,7 +1346,7 @@ int audit_mq_open(int oflag, mode_t mode
*
* Returns 0 for success or NULL context or < 0 on error.
*/
-int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
+int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
const struct timespec __user *u_abs_timeout)
{
struct audit_aux_data_mq_sendrecv *ax;
@@ -1361,8 +1363,10 @@ int audit_mq_timedsend(mqd_t mqdes, size
return -ENOMEM;
if (u_abs_timeout != NULL) {
- if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout)))
+ if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
+ kfree(ax);
return -EFAULT;
+ }
} else
memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
@@ -1377,7 +1381,7 @@ int audit_mq_timedsend(mqd_t mqdes, size
}
/**
- * audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
+ * __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
* @mqdes: MQ descriptor
* @msg_len: Message length
* @msg_prio: Message priority
@@ -1385,7 +1389,7 @@ int audit_mq_timedsend(mqd_t mqdes, size
*
* Returns 0 for success or NULL context or < 0 on error.
*/
-int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
+int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
unsigned int __user *u_msg_prio,
const struct timespec __user *u_abs_timeout)
{
@@ -1409,8 +1413,10 @@ int audit_mq_timedreceive(mqd_t mqdes, s
ax->msg_prio = 0;
if (u_abs_timeout != NULL) {
- if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout)))
+ if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
+ kfree(ax);
return -EFAULT;
+ }
} else
memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
@@ -1424,14 +1430,14 @@ int audit_mq_timedreceive(mqd_t mqdes, s
}
/**
- * audit_mq_notify - record audit data for a POSIX MQ notify
+ * __audit_mq_notify - record audit data for a POSIX MQ notify
* @mqdes: MQ descriptor
* @u_notification: Notification event
*
* Returns 0 for success or NULL context or < 0 on error.
*/
-int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
+int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
{
struct audit_aux_data_mq_notify *ax;
struct audit_context *context = current->audit_context;
@@ -1447,8 +1453,10 @@ int audit_mq_notify(mqd_t mqdes, const s
return -ENOMEM;
if (u_notification != NULL) {
- if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification)))
+ if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification))) {
+ kfree(ax);
return -EFAULT;
+ }
} else
memset(&ax->notification, 0, sizeof(ax->notification));
@@ -1461,13 +1469,13 @@ int audit_mq_notify(mqd_t mqdes, const s
}
/**
- * audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute
+ * __audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute
* @mqdes: MQ descriptor
* @mqstat: MQ flags
*
* Returns 0 for success or NULL context or < 0 on error.
*/
-int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
+int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
{
struct audit_aux_data_mq_getsetattr *ax;
struct audit_context *context = current->audit_context;
--
George Wilson <ltcgcw(a)us.ibm.com>
IBM Linux Technology Center
18 years, 7 months
auditctl filter keyword: "path"
by Michael C Thompson
Hey Steve,
audit-1.2.2-2 seems to be having problems with the path filter word.
# auditctl -a exit,always -S open -F path=bfile
Error sending add rule request (Invalid argument)
# auditctl -a entry,always -S open -F path=bfile
Error sending add rule request (Invalid argument)
I'm on kernel 27... Any idea on why this would be failing?
Thanks,
Mike
18 years, 7 months
audit 1.2.2 released
by Steve Grubb
Hi,
I've just released a new version of the audit daemon. It can be downloaded
from http://people.redhat.com/sgrubb/audit It will also be in rawhide
tomorrow. The Changelog is:
- Updates for new glibc-kernheaders
- Change auditctl to collect list of rules then delete them on -D
- Update capp.rules and lspp.rules to comment out rules for the possible list
- Add new message types
- Support sigusr1 sender identity of newer kernels
- Add support for ppid in auditctl and ausearch
- fix auditctl to trim the '/' from watches
- Move audit daemon config files to /etc/audit for better SE Linux protection
Beware ! This release has 2 changes to notice. It requires newer
glibc-kernheaders and it moves the audit configuration files to
the /etc/audit directory. The specfile should handle the transition
gracefully.
This release also supports new options in our current development kernels. It
adds support for filtering by ppid and searching for ppid in the logs. It
supports getting the signal info for senders of sigusr1. And completes the
fix for listing or deleting large amounts of syscall rules. Watches that have
a trailing '/' will now have it trimmed to make the kernel happier.
2 new message types were added AUDIT_DEV_ALLOC and AUDIT_DEV_DEALLOC for LSPP
work. The capp & lspp rules were updated to not have "possible" as the list
action.
Please let me know if there are any problems with this release.
-Steve
18 years, 7 months
avc_snap a python tool that grabs Audit messages from the auditd.
by Daniel J Walsh
We are building a tool that will try to diagnose an AVC Message and tell
the administrator what it means.
To do this I have written a fairly simple python script that grabs the
dispatcher message from the audit daemon and then formats
python dictionary which will eventually be handed off to the analyzer
code.
Dan
#!/usr/bin/python
# Copyright (C) 2006 Red Hat, Inc
# Author by Dan Walsh, <dwalsh(a)redhat.com>
#
# 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
#
# avc_snap - this script is is launched by the auditd and listense on
# stdin for Audit messages. It then searches for AVC Messages and assembles
# them into a python dictionary for later processing by the AVC Plugins
#
#
import os, string, select, struct, syslog
# audit python provided by audit-python package
# avc python provided by policycoreutils package
import audit, avc
#
# Collect a list of audit messages until a timeout or the audit signature changes
# Check to see if the list contains an AUDIT_AVC message
# If yes
# create the dictionary using serules.
# For now just syslog the dictionary,
# eventually will call analyze and process avc message
# else
# discard
#
class avc_snap:
def __init__(self):
self.audit_list = []
self.cur_sig = ""
def is_avc(self):
for i in self.audit_list:
if i[0] == audit.AUDIT_AVC:
return True
return False
def out(self):
if self.is_avc():
rules=avc.SERules()
l=[]
for ( type, data_list ) in self.audit_list:
l += data_list
syslog.syslog("data %s" % string.join(data_list, " "))
rules.translate(l)
for dict in rules.AVCS:
for d in dict.keys():
syslog.syslog("%s->%s" % (d, dict[d]))
self.audit_list = []
def process(self, type, data):
data_list=data.split()
new_sig=data_list[0]
if len(self.audit_list) > 0 and new_sig != self.cur_sig:
self.out()
self.cur_sig = new_sig
self.audit_list.append((type, data_list[1:]))
def run(self):
while 1:
input,output, err = select.select([0],[], [], 5)
try:
if 0 in input:
baseformat="iiii"
theline=os.read(0,16 + audit.MAX_AUDIT_MESSAGE_LENGTH)
numremain = len(theline)-struct.calcsize(baseformat)
format = "%s%ds" % (baseformat, numremain)
version, size, type, length, data = struct.unpack(format, theline)
# syslog.syslog("append type=%d data %s" % (type,data[:length]))
self.process(type, data[:length])
else:
self.out()
except struct.error, e:
syslog.syslog("struct exception %s " % e.args)
except TypeError, e:
syslog.syslog("Type exception %s " % e.args)
try:
snap=avc_snap()
snap.run()
except IOError,e:
syslog.syslog("IOError exception %s" % e.args)
except Exception, e:
syslog.syslog("Unexpected exception %s " % e.args)
18 years, 7 months
[PATCH git] another audit watch fix
by Amy Griffis
This patch contains another fix for the audit watch implementation in
the git tree. Al, both this patch and Monday's "audit watch fixes"
should fold into commit 95f06755b00c44d17757df4dc150997058c64849.
Don't call put_inotify_watch() via audit_free_rule_rcu. If it's the
last ref it will call iput(), which can sleep. We can't do that in
an rcu callback.
Signed-off-by: Amy Griffis <amy.griffis(a)hp.com>
---
kernel/auditfilter.c | 88 +++++++++++++++++++++++---------------------------
1 files changed, 41 insertions(+), 47 deletions(-)
00dff88f35a3a09e72411b06c847557d6d82da02
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 54bb856..7f2fcd6 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -49,12 +49,10 @@ #include "audit.h"
* audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED
* event. Each audit_watch holds a reference to its associated parent.
*
- * audit_watch: if added to lists, lifetime is from audit_init_watch() to one
- * of: audit_remove_watch() [user removes], audit_update_watch() [kernel
- * replaces], or audit_remove_parent_watches() [kernel removes].
- * Additionally, an audit_watch may exist temporarily to assist in
- * searching existing filter data. Each audit_krule holds a reference to
- * its associated watch.
+ * audit_watch: if added to lists, lifetime is from audit_init_watch() to
+ * audit_remove_watch(). Additionally, an audit_watch may exist
+ * temporarily to assist in searching existing filter data. Each
+ * audit_krule holds a reference to its associated watch.
*/
struct audit_parent {
@@ -68,12 +66,10 @@ struct audit_parent {
* audit_parent status flags:
*
* AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
- * a filesystem event. Technically not needed for IN_DELETE_SELF or IN_UNMOUNT
- * events, as we cannot receive them while we have nameidata (during rule add)
- * and the audit_parent is immediately removed when processing the following
- * IN_IGNORED event. The IN_MOVE_SELF event is different. We can receive it
- * while holding nameidata, and inotify will not send us the IN_IGNORED so we
- * must later remove the inotify watch on audit_parent ourselves.
+ * a filesystem event to ensure we're adding audit watches to a valid parent.
+ * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot
+ * receive them while we have nameidata, but must be used for IN_MOVE_SELF which
+ * we can receive while holding nameidata.
*/
#define AUDIT_PARENT_INVALID 0x001
@@ -115,15 +111,21 @@ static inline void audit_get_watch(struc
static inline void audit_put_watch(struct audit_watch *watch)
{
if (atomic_dec_and_test(&watch->count)) {
+ WARN_ON(watch->parent);
WARN_ON(!list_empty(&watch->rules));
- /* watches that were never added don't have a parent */
- if (watch->parent)
- put_inotify_watch(&watch->parent->wdata);
kfree(watch->path);
kfree(watch);
}
}
+static inline void audit_remove_watch(struct audit_watch *watch)
+{
+ list_del(&watch->wlist);
+ put_inotify_watch(&watch->parent->wdata);
+ watch->parent = NULL;
+ audit_put_watch(watch); /* match initial get */
+}
+
static inline void audit_free_rule(struct audit_entry *e)
{
int i;
@@ -779,8 +781,7 @@ static inline void audit_update_watch(st
audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
audit_log_end(ab);
- list_del(&owatch->wlist);
- audit_put_watch(owatch); /* matches initial get */
+ audit_remove_watch(owatch);
goto add_watch_to_parent; /* event applies to a single watch */
}
mutex_unlock(&audit_filter_mutex);
@@ -812,8 +813,7 @@ static inline void audit_remove_parent_w
"audit implicitly removed rule from list=%d\n",
AUDIT_FILTER_EXIT);
}
- list_del(&w->wlist);
- audit_put_watch(w); /* matches initial get */
+ audit_remove_watch(w);
}
mutex_unlock(&audit_filter_mutex);
}
@@ -826,7 +826,7 @@ static void audit_inotify_unregister(str
list_for_each_entry(p, in_list, ilist) {
inotify_rm_watch(audit_ih, &p->wdata);
- /* the put matching the get in audit_remove_watch() */
+ /* the put matching the get in audit_do_del_rule() */
put_inotify_watch(&p->wdata);
}
}
@@ -953,7 +953,7 @@ static int audit_add_watch(struct audit_
else
audit_add_to_parent(krule, parent);
- /* put ref grabbed by inotify_find_watch or before inotify_add_watch */
+ /* match get in audit_init_parent or inotify_find_watch */
put_inotify_watch(&parent->wdata);
return ret;
}
@@ -1022,30 +1022,6 @@ error:
return err;
}
-/* Remove given krule from its associated watch's rules list and clean up any
- * last instances of associated watch and parent.
- * Caller must hold audit_filter_mutex. */
-static inline void audit_remove_watch(struct audit_krule *krule,
- struct list_head *in_list)
-{
- struct audit_watch *watch = krule->watch;
- struct audit_parent *parent = watch->parent;
-
- list_del(&krule->rlist);
- if (list_empty(&watch->rules)) {
- list_del(&watch->wlist);
- audit_put_watch(watch); /* matches initial get */
-
- if (list_empty(&parent->watches)) {
- /* Put parent on the inotify un-registration list.
- * Grab a reference before releasing audit_filter_mutex,
- * to be released in audit_inotify_unregister(). */
- list_add(&parent->ilist, in_list);
- get_inotify_watch(&parent->wdata);
- }
- }
-}
-
/* Rule removal helper.
* Caller must hold audit_filter_mutex. */
static inline int audit_do_del_rule(struct audit_entry *entry,
@@ -1055,11 +1031,29 @@ static inline int audit_do_del_rule(stru
struct audit_entry *e;
list_for_each_entry(e, list, list) {
+ struct audit_watch *watch = e->rule.watch;
+ struct audit_parent *parent = watch->parent;
+
if (audit_compare_rule(&entry->rule, &e->rule))
continue;
- if (e->rule.watch)
- audit_remove_watch(&e->rule, inotify_list);
+ if (watch) {
+ list_del(&e->rule.rlist);
+
+ if (list_empty(&watch->rules)) {
+ audit_remove_watch(watch);
+
+ if (list_empty(&parent->watches)) {
+ /* Put parent on the inotify
+ * un-registration list. Grab
+ * a reference before releasing
+ * audit_filter_mutex, to be released in
+ * audit_inotify_unregister(). */
+ list_add(&parent->ilist, inotify_list);
+ get_inotify_watch(&parent->wdata);
+ }
+ }
+ }
list_del_rcu(&e->list);
call_rcu(&e->rcu, audit_free_rule_rcu);
--
1.3.0
18 years, 7 months
[PATCH] Audit of POSIX Message Queue Syscalls
by George C. Wilson
This patch adds audit support to POSIX message queues. It applies cleanly to
the lspp.b12 branch of Al Viro's git tree. There are new auxiliary data
structures, and collection and emission routines in kernel/auditsc.c. New hooks
in ipc/mqueue.c collect arguments from the syscalls.
I tested the patch by building the examples from the POSIX MQ library tarball.
Build them -lrt, not against the old MQ library in the tarball. Here's the URL:
http://www.geocities.com/wronski12/posix_ipc/libmqueue-4.41.tar.gz
Do auditctl -a exit,always -S for mq_open, mq_timedsend, mq_timedreceive,
mq_notify, mq_getsetattr. mq_unlink has no new hooks. Please see the
corresponding userspace patch to get correct output from auditd for the new
record types.
Assumptions/Notes:
- Capturing the name arg is not required for sys_mq_open() sys_mq_open and
mq_unlink() as it is already collected in the syscall record.
- I was collecting more data from the notification struct for mq_notify().
However nothing other than the signal number appeared to be useful.
I don't like copying the entire sigevent struct just to get the signal
number.
- Because mq_getsetattr() takes both in and out parameters,
uninitialized data can show up in the audit records.
- I don't like having to copy as many parameters as I did from
userspace. The intent is to ensure the in parameters are captured no
matter the error path. Also, in mq_open() the existing copy_from_user()
is done in another function. Multiple hooks to avoid introducing the new
copy_from_user() would be messy.
- I don't like what I did with the return code in mq_getsetattr().
But the idea is only to return the audit hook's return code if the
code above it did not fail.
include/linux/audit.h | 15 ++
ipc/mqueue.c | 21 +++-
kernel/auditsc.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 288 insertions(+), 2 deletions(-)
Signed-off-by: George Wilson <ltcgcw(a)us.ibm.com>
--
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 3c69de2..60b3119 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/elf.h>
+#include <linux/mqueue.h>
/* The netlink messages for the audit system is divided into blocks:
* 1000 - 1099 are for commanding the audit system
@@ -85,6 +86,10 @@
#define AUDIT_CWD 1307 /* Current working directory */
#define AUDIT_EXECVE 1309 /* execve arguments */
#define AUDIT_IPC_SET_PERM 1311 /* IPC new permissions record type */
+#define AUDIT_MQ_OPEN 1312 /* POSIX MQ open record type */
+#define AUDIT_MQ_SENDRECV 1313 /* POSIX MQ send/receive record type */
+#define AUDIT_MQ_NOTIFY 1314 /* POSIX MQ notify record type */
+#define AUDIT_MQ_GETSETATTR 1315 /* POSIX MQ get/set attribute record type */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -332,6 +337,11 @@ extern int audit_socketcall(int nargs, u
extern int audit_sockaddr(int len, void *addr);
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
extern int audit_set_macxattr(const char *name);
+extern int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr);
+extern int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout);
+extern int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
+extern int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
+extern int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat, struct mq_attr *omqstat);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -352,6 +362,11 @@ extern int audit_set_macxattr(const char
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_avc_path(dentry, mnt) ({ 0; })
#define audit_set_macxattr(n) do { ; } while (0)
+#define audit_mq_open(n) do { ; } while (0)
+#define audit_mq_timedsend(n) do { ; } while (0)
+#define audit_mq_timedreceive(n) do { ; } while (0)
+#define audit_mq_notify(n) do { ; } while (0)
+#define audit_mq_getsetattr(n) do { ; } while (0)
#endif
#ifdef CONFIG_AUDIT
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 41ecbd4..8f6a82d 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -8,6 +8,8 @@
* Lockless receive & send, fd based notify:
* Manfred Spraul (manfred(a)colorfullife.com)
*
+ * Audit: George Wilson (ltcgcw(a)us.ibm.com)
+ *
* This file is released under the GPL.
*/
@@ -24,6 +26,7 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/syscalls.h>
+#include <linux/audit.h>
#include <linux/signal.h>
#include <linux/mutex.h>
@@ -660,6 +663,9 @@ asmlinkage long sys_mq_open(const char _
if (IS_ERR(name = getname(u_name)))
return PTR_ERR(name);
+ if ((error = audit_mq_open(oflag, mode, u_attr)) != 0)
+ return error;
+
fd = get_unused_fd();
if (fd < 0)
goto out_putname;
@@ -814,6 +820,9 @@ asmlinkage long sys_mq_timedsend(mqd_t m
long timeout;
int ret;
+ if ((ret = audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout)) != 0)
+ return ret;
+
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
@@ -896,6 +905,9 @@ asmlinkage ssize_t sys_mq_timedreceive(m
struct mqueue_inode_info *info;
struct ext_wait_queue wait;
+ if ((ret = audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout)) != 0)
+ return ret;
+
timeout = prepare_timeout(u_abs_timeout);
ret = -EBADF;
@@ -975,6 +987,9 @@ asmlinkage long sys_mq_notify(mqd_t mqde
struct mqueue_inode_info *info;
struct sk_buff *nc;
+ if ((ret = audit_mq_notify(mqdes, u_notification)) != 0)
+ return ret;
+
nc = NULL;
sock = NULL;
if (u_notification != NULL) {
@@ -1087,7 +1102,7 @@ asmlinkage long sys_mq_getsetattr(mqd_t
const struct mq_attr __user *u_mqstat,
struct mq_attr __user *u_omqstat)
{
- int ret;
+ int ret, audret;
struct mq_attr mqstat, omqstat;
struct file *filp;
struct inode *inode;
@@ -1130,9 +1145,13 @@ asmlinkage long sys_mq_getsetattr(mqd_t
sizeof(struct mq_attr)))
ret = -EFAULT;
+
out_fput:
fput(filp);
out:
+ audret = audit_mq_getsetattr(mqdes, &mqstat, &omqstat);
+ if (ret == 0)
+ ret = audret;
return ret;
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index c30f146..2fdead5 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -3,7 +3,7 @@
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
* Copyright 2005 Hewlett-Packard Development Company, L.P.
- * Copyright (C) 2005 IBM Corporation
+ * Copyright (C) 2005, 2006 IBM Corporation
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,9 @@
* this file -- see entry.S) is based on a GPL'd patch written by
* okir(a)suse.de and Copyright 2003 SuSE Linux AG.
*
+ * POSIX message queue support added by George Wilson <ltcgcw(a)us.ibm.com>,
+ * 2006.
+ *
* The support of additional filter rules compares (>, <, >=, <=) was
* added by Dustin Kirkland <dustin.kirkland(a)us.ibm.com>, 2005.
*
@@ -102,6 +105,34 @@ struct audit_aux_data {
#define AUDIT_AUX_IPCPERM 0
+struct audit_aux_data_mq_open {
+ struct audit_aux_data d;
+ int oflag;
+ mode_t mode;
+ struct mq_attr attr;
+};
+
+struct audit_aux_data_mq_sendrecv {
+ struct audit_aux_data d;
+ mqd_t mqdes;
+ size_t msg_len;
+ unsigned int msg_prio;
+ struct timespec abs_timeout;
+};
+
+struct audit_aux_data_mq_notify {
+ struct audit_aux_data d;
+ mqd_t mqdes;
+ struct sigevent notification;
+};
+
+struct audit_aux_data_mq_getsetattr {
+ struct audit_aux_data d;
+ mqd_t mqdes;
+ struct mq_attr mqstat;
+ struct mq_attr omqstat;
+};
+
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
struct ipc_perm p;
@@ -709,6 +740,46 @@ static void audit_log_exit(struct audit_
continue; /* audit_panic has been called */
switch (aux->type) {
+ case AUDIT_MQ_OPEN: {
+ struct audit_aux_data_mq_open *axi = (void *)aux;
+ audit_log_format(ab,
+ "oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld "
+ "mq_msgsize=%ld mq_curmsgs=%ld",
+ axi->oflag, axi->mode, axi->attr.mq_flags,
+ axi->attr.mq_maxmsg, axi->attr.mq_msgsize,
+ axi->attr.mq_curmsgs);
+ break; }
+
+ case AUDIT_MQ_SENDRECV: {
+ struct audit_aux_data_mq_sendrecv *axi = (void *)aux;
+ audit_log_format(ab,
+ "mqdes=%d msg_len=%ld msg_prio=%u "
+ "abs_timeout_sec=%ld abs_timeout_nsec=%ld",
+ axi->mqdes, axi->msg_len, axi->msg_prio,
+ axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec);
+ break; }
+
+ case AUDIT_MQ_NOTIFY: {
+ struct audit_aux_data_mq_notify *axi = (void *)aux;
+ audit_log_format(ab,
+ "mqdes=%d sigev_signo=%d",
+ axi->mqdes,
+ axi->notification.sigev_signo);
+ break; }
+
+ case AUDIT_MQ_GETSETATTR: {
+ struct audit_aux_data_mq_getsetattr *axi = (void *)aux;
+ audit_log_format(ab,
+ "mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld "
+ "mq_curmsgs=%ld omq_flags=0x%lx omq_maxmsg=%ld "
+ "omq_msgsize=%ld omq_curmsgs=%ld",
+ axi->mqdes,
+ axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg,
+ axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs,
+ axi->omqstat.mq_flags, axi->omqstat.mq_maxmsg,
+ axi->omqstat.mq_msgsize, axi->omqstat.mq_curmsgs);
+ break; }
+
case AUDIT_IPC: {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
@@ -1242,6 +1313,187 @@ uid_t audit_get_loginuid(struct audit_co
}
/**
+ * audit_mq_open - record audit data for a POSIX MQ open
+ * @oflag: open flag
+ * @mode: mode bits
+ * @u_attr: queue attributes
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
+{
+ struct audit_aux_data_mq_open *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ if (u_attr != NULL) {
+ if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr)))
+ return -EFAULT;
+ } else
+ memset(&ax->attr, 0, sizeof(ax->attr));
+
+ ax->oflag = oflag;
+ ax->mode = mode;
+
+ ax->d.type = AUDIT_MQ_OPEN;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_mq_timedsend - record audit data for a POSIX MQ timed send
+ * @mqdes: MQ descriptor
+ * @msg_len: Message length
+ * @msg_prio: Message priority
+ * @abs_timeout: Message timeout in absolute time
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
+ const struct timespec __user *u_abs_timeout)
+{
+ struct audit_aux_data_mq_sendrecv *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ if (u_abs_timeout != NULL) {
+ if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout)))
+ return -EFAULT;
+ } else
+ memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+
+ ax->mqdes = mqdes;
+ ax->msg_len = msg_len;
+ ax->msg_prio = msg_prio;
+
+ ax->d.type = AUDIT_MQ_SENDRECV;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
+ * @mqdes: MQ descriptor
+ * @msg_len: Message length
+ * @msg_prio: Message priority
+ * @abs_timeout: Message timeout in absolute time
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
+ unsigned int __user *u_msg_prio,
+ const struct timespec __user *u_abs_timeout)
+{
+ struct audit_aux_data_mq_sendrecv *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ if (u_msg_prio != NULL) {
+ if (get_user(ax->msg_prio, u_msg_prio))
+ return -EFAULT;
+ } else
+ ax->msg_prio = 0;
+
+ if (u_abs_timeout != NULL) {
+ if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout)))
+ return -EFAULT;
+ } else
+ memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+
+ ax->mqdes = mqdes;
+ ax->msg_len = msg_len;
+
+ ax->d.type = AUDIT_MQ_SENDRECV;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_mq_notify - record audit data for a POSIX MQ notify
+ * @mqdes: MQ descriptor
+ * @u_notification: Notification event
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+
+int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
+{
+ struct audit_aux_data_mq_notify *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ if (u_notification != NULL) {
+ if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification)))
+ return -EFAULT;
+ } else
+ memset(&ax->notification, 0, sizeof(ax->notification));
+
+ ax->mqdes = mqdes;
+
+ ax->d.type = AUDIT_MQ_NOTIFY;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute
+ * @mqdes: MQ descriptor
+ * @mqstat: MQ flags
+ * @omqstat: Old MQ flags
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat, struct mq_attr *omqstat)
+{
+ struct audit_aux_data_mq_getsetattr *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->mqdes = mqdes;
+ ax->mqstat = *mqstat;
+ ax->omqstat = *omqstat;
+
+ ax->d.type = AUDIT_MQ_GETSETATTR;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
* audit_ipc_obj - record audit data for ipc object
* @ipcp: ipc permissions
*
--
George Wilson <ltcgcw(a)us.ibm.com>
IBM Linux Technology Center
18 years, 7 months
RE: porting test suite ( testing audit system ) from RHEL 3 to RHEL 4
by Upadhyay, Vikas
> -----Original Message-----
> From: Matt Anderson [mailto:mra@hp.com]
> Sent: Tuesday, May 23, 2006 9:27 PM
> To: Upadhyay, Vikas; linux-audit(a)redhat.com
> Subject: Re: porting test suite ( testing audit system ) from RHEL 3
to
> RHEL 4
>
> Upadhyay, Vikas wrote:
> > I want to port the LTP test suite,
linux_security_test_suite_EAL3.tar.gz
> > (available from http://ltp.sourceforge.net/EAL3.RHEL.html) from RHEL
3
> > to RHEL 4. The major issue is porting of *laus_test* directory,
which
> > tests the underlying audit system and the audit system has undergone
a
> > substantial change from RHEL 3 to RHEL 4.
> >
> > I plan to change the directory *laus_test*, which tests *LAuS* on
*RHEL
> > 3* to something like, *audit_test*, which will test the current
audit
> > system (on *RHEL 4*). And for that, I need to change each and every
> > single file in the directory *laus_test*. I am unable to find any
> > detailed document on the new audit system.
> >
> > Any pointers will be really appreciated.
>
> You probably want to check out our audit-test package. We recently
got
> it added to sourceforge: http://sourceforge.net/projects/audit-test
>
> HP hasn't had much time to work on it as we're getting ready for the
> next evaluation, but it will be a better starting point. Maybe if we
> get some more people using it (and sending patches) we'll be able to
> justify spending more time on it.
>
> -matt
Hi Matt,
Looking at the TODO file in the test sub suite (audit-test, available at
the mentioned URL) & your mail, it seems that the audit-test sub suite
is not just a simple replacement of laus_test (available in the older
test suite, meant for RHEL 3). So, for having a complete compliance with
CAPP, do I need to make further changes or they are just enhancements
(to do away with manual testing)?
As such, it compiled cleanly & a few test cases failed, I guess because
of not having the system configured in proper way.
Regards
Vikas
18 years, 7 months
RE: porting test suite ( testing audit system ) from RHEL 3 to RHEL 4
by Upadhyay, Vikas
-----Original Message-----
From: Matt Anderson [mailto:mra@hp.com]
Sent: Tuesday, May 23, 2006 9:27 PM
To: Upadhyay, Vikas; linux-audit(a)redhat.com
Subject: Re: porting test suite ( testing audit system ) from RHEL 3 to
RHEL 4
> You probably want to check out our audit-test package. We recently
got
> it added to sourceforge: http://sourceforge.net/projects/audit-test
> HP hasn't had much time to work on it as we're getting ready for the
> next evaluation, but it will be a better starting point. Maybe if we
> get some more people using it (and sending patches) we'll be able to
> justify spending more time on it.
Matt,
This is what I was looking for.
Thanks a lot for help.
18 years, 7 months
porting test suite ( testing audit system ) from RHEL 3 to RHEL 4
by Upadhyay, Vikas
Hi all,
I want to port the LTP test suite, linux_security_test_suite_EAL3.tar.gz
(available from http://ltp.sourceforge.net/EAL3.RHEL.html) from RHEL 3
to RHEL 4. The major issue is porting of laus_test directory, which
tests the underlying audit system and the audit system has undergone a
substantial change from RHEL 3 to RHEL 4.
I plan to change the directory laus_test, which tests LAuS on RHEL 3 to
something like, audit_test, which will test the current audit system (on
RHEL 4). And for that, I need to change each and every single file in
the directory laus_test. I am unable to find any detailed document on
the new audit system.
Any pointers will be really appreciated.
Thanks in anticipation.
Vikas
18 years, 7 months
[PATCH git] audit watch fixes
by Amy Griffis
This patch contains fixes for the audit watch implementation in the
git tree.
- deadlock fix: release mutex on error from audit_add_watch
- remove "moved" inotify watches from event handler
- use inotify refcounting for audit_parent data
- register inotify watches before adding rule instead of after;
using inotify refcounting, this ensures our audit_watch->parent
is valid
- some code consolidation based on above changes
Please take a look and make sure I haven't bungled anything.
Signed-off-by: Amy Griffis <amy.griffis(a)hp.com>
--
audit.c | 8 ++
audit.h | 1
auditfilter.c | 175 ++++++++++++++++++++++------------------------------------
3 files changed, 75 insertions(+), 109 deletions(-)
diff --git a/kernel/audit.c b/kernel/audit.c
index b834b2e..db8bce4 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -680,6 +680,12 @@ static void audit_receive(struct sock *s
mutex_unlock(&audit_cmd_mutex);
}
+#ifdef CONFIG_AUDITSYSCALL
+static const struct inotify_operations audit_inotify_ops = {
+ .handle_event = audit_handle_ievent,
+ .destroy_watch = audit_free_parent,
+};
+#endif
/* Initialize audit support at boot time. */
static int __init audit_init(void)
@@ -706,7 +712,7 @@ static int __init audit_init(void)
audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
#ifdef CONFIG_AUDITSYSCALL
- audit_ih = inotify_init(audit_handle_ievent);
+ audit_ih = inotify_init(&audit_inotify_ops);
if (IS_ERR(audit_ih))
audit_panic("cannot initialize inotify handle");
diff --git a/kernel/audit.h b/kernel/audit.h
index 8506287..125aebe 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -122,6 +122,7 @@ struct audit_netlink_list {
int audit_send_list(void *);
struct inotify_watch;
+extern void audit_free_parent(struct inotify_watch *);
extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
const char *, struct inode *);
extern int selinux_audit_rule_update(void);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index e116322..6caa46e 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -58,7 +58,6 @@ #include "audit.h"
*/
struct audit_parent {
- atomic_t count; /* reference count */
struct list_head ilist; /* entry in inotify registration list */
struct list_head watches; /* associated watches */
struct inotify_watch wdata; /* inotify watch data */
@@ -98,19 +97,14 @@ extern struct inotify_handle *audit_ih;
/* 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
-static inline void audit_get_parent(struct audit_parent *parent)
+void audit_free_parent(struct inotify_watch *i_watch)
{
- atomic_inc(&parent->count);
-}
+ struct audit_parent *parent;
-static inline void audit_put_parent(struct audit_parent *parent)
-{
- if (atomic_dec_and_test(&parent->count)) {
- WARN_ON(!list_empty(&parent->watches));
- kfree(parent);
- }
+ parent = container_of(i_watch, struct audit_parent, wdata);
+ WARN_ON(!list_empty(&parent->watches));
+ kfree(parent);
}
static inline void audit_get_watch(struct audit_watch *watch)
@@ -124,7 +118,7 @@ static inline void audit_put_watch(struc
WARN_ON(!list_empty(&watch->rules));
/* watches that were never added don't have a parent */
if (watch->parent)
- audit_put_parent(watch->parent);
+ put_inotify_watch(&watch->parent->wdata);
kfree(watch->path);
kfree(watch);
}
@@ -154,18 +148,28 @@ static inline void audit_free_rule_rcu(s
}
/* Initialize a parent watch entry. */
-static inline struct audit_parent *audit_init_parent(void)
+static inline struct audit_parent *audit_init_parent(struct nameidata *ndp)
{
struct audit_parent *parent;
+ s32 wd;
parent = kzalloc(sizeof(*parent), GFP_KERNEL);
if (unlikely(!parent))
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&parent->watches);
- atomic_set(&parent->count, 1);
parent->flags = 0;
+ inotify_init_watch(&parent->wdata);
+ /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */
+ get_inotify_watch(&parent->wdata);
+ wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode,
+ AUDIT_IN_WATCH);
+ if (wd < 0) {
+ audit_free_parent(&parent->wdata);
+ return ERR_PTR(wd);
+ }
+
return parent;
}
@@ -632,7 +636,7 @@ static inline struct audit_watch *audit_
new->dev = old->dev;
new->ino = old->ino;
- audit_get_parent(old->parent);
+ get_inotify_watch(&old->parent->wdata);
new->parent = old->parent;
out:
@@ -811,30 +815,6 @@ static inline void audit_remove_parent_w
mutex_unlock(&audit_filter_mutex);
}
-/* Register inotify watches for parents on in_list. */
-static int audit_inotify_register(struct nameidata *nd,
- struct list_head *in_list)
-{
- struct audit_parent *p;
- s32 wd;
- int ret = 0;
-
- list_for_each_entry(p, in_list, ilist) {
- wd = inotify_add_watch(audit_ih, &p->wdata, nd->dentry->d_inode,
- AUDIT_IN_WATCH);
- if (wd < 0) {
- audit_remove_parent_watches(p);
- /* the put matching the get in audit_init_parent() */
- audit_put_parent(p);
- /* save the first error for return value */
- if (!ret)
- ret = wd;
- }
- }
-
- return ret;
-}
-
/* Unregister inotify watches for parents on in_list.
* Generates an IN_IGNORED event. */
static void audit_inotify_unregister(struct list_head *in_list)
@@ -844,7 +824,7 @@ static void audit_inotify_unregister(str
list_for_each_entry(p, in_list, ilist) {
inotify_rm_watch(audit_ih, &p->wdata);
/* the put matching the get in audit_remove_watch() */
- audit_put_parent(p);
+ put_inotify_watch(&p->wdata);
}
}
@@ -897,46 +877,14 @@ static inline void audit_put_nd(struct n
}
}
-/* Add a parent inotify_watch for the given rule. */
-static int audit_add_parent(struct audit_krule *krule,
- struct list_head *inotify_list)
-{
- struct audit_parent *parent;
- struct audit_watch *watch = krule->watch;
-
- parent = audit_init_parent();
- if (IS_ERR(parent))
- return PTR_ERR(parent);
-
- audit_get_parent(parent);
- watch->parent = parent;
-
- /* krule, watch and parent have not been added to any global
- * lists, so we don't need to take audit_filter_mutex. */
- list_add(&watch->wlist, &parent->watches);
- list_add(&krule->rlist, &watch->rules);
-
- /* add parent to inotify registration list */
- list_add(&parent->ilist, inotify_list);
-
- return 0;
-}
-
/* Associate the given rule with an existing parent inotify_watch.
* Caller must hold audit_filter_mutex. */
-static int audit_add_to_parent(struct audit_krule *krule,
- struct inotify_watch *iwatch)
+static void audit_add_to_parent(struct audit_krule *krule,
+ struct audit_parent *parent)
{
- struct audit_parent *parent;
struct audit_watch *w, *watch = krule->watch;
int watch_found = 0;
- parent = container_of(iwatch, struct audit_parent, wdata);
-
- /* parent was moved before we took audit_filter_mutex */
- if (parent->flags & AUDIT_PARENT_INVALID)
- return -ENOENT;
-
list_for_each_entry(w, &parent->watches, wlist) {
if (strcmp(watch->path, w->path))
continue;
@@ -953,26 +901,23 @@ static int audit_add_to_parent(struct au
}
if (!watch_found) {
- audit_get_parent(parent);
+ get_inotify_watch(&parent->wdata);
watch->parent = parent;
list_add(&watch->wlist, &parent->watches);
}
-
list_add(&krule->rlist, &watch->rules);
-
- return 0;
}
/* Find a matching watch entry, or add this one.
* Caller must hold audit_filter_mutex. */
static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
- struct nameidata *ndw,
- struct list_head *inotify_list)
+ struct nameidata *ndw)
{
struct audit_watch *watch = krule->watch;
- struct inotify_watch *iwatch;
- int ret;
+ struct inotify_watch *i_watch;
+ struct audit_parent *parent;
+ int ret = 0;
/* update watch filter fields */
if (ndw) {
@@ -981,18 +926,32 @@ static int audit_add_watch(struct audit_
}
/* The audit_filter_mutex must not be held during inotify calls because
- * we hold it during inotify event callback processing.
- * We can trust iwatch to stick around because we hold nameidata (ndp). */
+ * we hold it during inotify event callback processing. If an existing
+ * inotify watch is found, inotify_find_watch() grabs a reference before
+ * returning.
+ */
mutex_unlock(&audit_filter_mutex);
- if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &iwatch) < 0) {
- ret = audit_add_parent(krule, inotify_list);
- mutex_lock(&audit_filter_mutex);
- } else {
- mutex_lock(&audit_filter_mutex);
- ret = audit_add_to_parent(krule, iwatch);
- }
+ if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &i_watch) < 0) {
+ parent = audit_init_parent(ndp);
+ if (IS_ERR(parent)) {
+ /* caller expects mutex locked */
+ mutex_lock(&audit_filter_mutex);
+ return PTR_ERR(parent);
+ }
+ } else
+ parent = container_of(i_watch, struct audit_parent, wdata);
+ mutex_lock(&audit_filter_mutex);
+
+ /* parent was moved before we took audit_filter_mutex */
+ if (parent->flags & AUDIT_PARENT_INVALID)
+ ret = -ENOENT;
+ else
+ audit_add_to_parent(krule, parent);
+
+ /* put ref grabbed by inotify_find_watch or before inotify_add_watch */
+ put_inotify_watch(&parent->wdata);
return ret;
}
@@ -1004,7 +963,6 @@ static inline int audit_add_rule(struct
struct audit_field *inode_f = entry->rule.inode_f;
struct audit_watch *watch = entry->rule.watch;
struct nameidata *ndp, *ndw;
- LIST_HEAD(inotify_list);
int h, err, putnd_needed = 0;
/* Taking audit_filter_mutex protects from stale rule data. */
@@ -1029,9 +987,11 @@ static inline int audit_add_rule(struct
mutex_lock(&audit_filter_mutex);
if (watch) {
/* audit_filter_mutex is dropped and re-taken during this call */
- err = audit_add_watch(&entry->rule, ndp, ndw, &inotify_list);
- if (err)
+ err = audit_add_watch(&entry->rule, ndp, ndw);
+ if (err) {
+ mutex_unlock(&audit_filter_mutex);
goto error;
+ }
h = audit_hash_ino((u32)watch->ino);
list = &audit_inode_hash[h];
} else if (inode_f) {
@@ -1046,11 +1006,6 @@ static inline int audit_add_rule(struct
}
mutex_unlock(&audit_filter_mutex);
- if (!list_empty(&inotify_list)) {
- err = audit_inotify_register(ndp, &inotify_list);
- if (err)
- goto error;
- }
if (putnd_needed)
audit_put_nd(ndp, ndw);
@@ -1083,7 +1038,7 @@ static inline void audit_remove_watch(st
* Grab a reference before releasing audit_filter_mutex,
* to be released in audit_inotify_unregister(). */
list_add(&parent->ilist, in_list);
- audit_get_parent(parent);
+ get_inotify_watch(&parent->wdata);
}
}
}
@@ -1561,21 +1516,25 @@ int selinux_audit_rule_update(void)
}
/* Update watch data in audit rules based on inotify events. */
-void audit_handle_ievent(struct inotify_watch *iwatch, u32 wd, u32 mask,
+void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask,
u32 cookie, const char *dname, struct inode *inode)
{
- struct audit_parent *parent = container_of(iwatch, struct audit_parent, wdata);
+ struct audit_parent *parent;
+
+ parent = container_of(i_watch, struct audit_parent, wdata);
if (mask & (IN_CREATE|IN_MOVED_TO) && inode)
audit_update_watch(parent, dname, inode->i_sb->s_dev,
inode->i_ino);
else if (mask & (IN_DELETE|IN_MOVED_FROM))
audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1);
- /* Note: Inotify doesn't remove the watch for the IN_MOVE_SELF event.
- * Work around this by leaving the parent around with an empty
- * watchlist. It will be re-used if new watches are added. */
- else if (mask & (AUDIT_IN_SELF))
+ /* inotify automatically removes the watch and sends IN_IGNORED */
+ else if (mask & (IN_DELETE_SELF|IN_UNMOUNT))
+ audit_remove_parent_watches(parent);
+ /* inotify does not remove the watch, so remove it manually */
+ else if(mask & IN_MOVE_SELF) {
audit_remove_parent_watches(parent);
- else if (mask & IN_IGNORED)
- audit_put_parent(parent); /* match get in audit_init_parent() */
+ inotify_remove_watch_locked(audit_ih, i_watch);
+ } else if (mask & IN_IGNORED)
+ put_inotify_watch(i_watch);
}
18 years, 7 months