audit test results on lspp.10 kernel
by Linda Knippers
In our LSPP concall on Monday I said I'd give our audit tests a try
on the latest kernel. I ran our CAPP audit test suite on an ia32
box installed with FC5T2, the lspp.10 kernel, the 1.1.4 audit tools
and the MLS policy in permissive mode. This is what I got:
fchmod, fchown, fchown32 tests failed to run because the test cases
got errors trying to insert a watch.
> /sbin/auditctl -w /tmp/audit_testPZbtbq -k _tmp_audit_testPZbtbq
> Error sending watch insert request (Invalid argument)
Not sure if this is a kernel/user-space compatibility problem or
we just don't have all the new code in yet.
The negative test cases for our msgctl-set and semctl-set
because they didn't see the right audit records. These tests
attempt to remove a message queue or semaphore set with
insufficient permissions. Our tests are looking for an IPC record
whether the syscall fails or succeeds and I only got one on the success
case.
Our tests for successful mounts and symlinks failed but I believe its
because I got AVC denied messages and that goofed up the way the tests
look for the right fields in the audit records.
The *xattr tests failed to build so I haven't run those yet.
I'll look at the *xattr tests next and also try to set up an x86_64 box.
All in all though, not too bad.
-- ljk
18 years, 9 months
Re: Audit Parsing Library Requirements
by Debora Velarde
> > Since this is introducing the notion of multiple machines
> potentially sharing
> > the same log...would it be more clear to change the name to prevent
> > confusion?
> >
> > Its currently host, but would could make it: server, node, machine,
etc.
>
> OK, I have a question because I think there are two issues here:
> 1) Steve has a function, called auparse_get_host, which is paralleled
> by other things like get_serial, which implies that every event has an
> associated host
> 2) This particular event has a host field, I do not think that
> auparse_get_host(0 and auparse_get_filed(au,"host") are the same
Thanks Mike. I see my confusion now.
Since some of the records do have a field 'hostname', I favor changing
auparse_get_host to auparse_get_machine or auparse_get_node.
-debbie
18 years, 9 months
Re: Another slab size-32 leak 2.6.16-rc4-mm2
by Stephan Mueller
Hi folks,
I reply to the discussion on the function audit_ipc_context() - as I just
subscribed, I do not have the right thread handle.
Before looking into the question, I looked at the thread. The function
audit_ipc_context(struct kern_ipc_perm *ipcp) you are talking about
implements oopses:
quite obvious I guess:
if (len < 0)
goto error_path;
[...]
ctx = kmalloc(len, GFP_ATOMIC);
if (!ctx)
goto error_path;
[...]
error_path:
kfree(ctx);
audit_panic("error in audit_ipc_context");
You free ctx although it has not been allocated - I do not like that ;-)
Other than that, let me see whether I can help you answering the questions.
Please tell me if I misunderstand anything: The question for you
is whether to keep this function. This function is about to collect labels
for IPC functions. If this function is gone, you cannot audit any labels for
the IPC functions any more?!
If this is the case, then this function needs to stay, because:
- syscalls msg*, sem*, shm* (except shmdt) do DAC checks - now, they also
perform MAC checks by calling appropriate SELinux hooks (as required by the
ST as IPC mechanisms are subject to MAC - I think shmdt now must also be
subject to MAC at least), then these syscalls must perform audit
- the audit requirement for IPC calls is specified in FAU_GEN.1.1 LSPP in the
table: "All decisions on requests for information flow" must be audited
- FAU_SAR.3 LSPP requires that subject and object sensitivity labels are to be
audited.
Ergo, the functionality in question must stay.
Ciao
Stephan
18 years, 9 months
[RFC] [PATCH]
by Darrel Goeddel
Hello,
This patch provides the selinux "backend" for the audit system to perform
filtering based on the process context. Dustin Kirkland has previously
a portion of this functionality here:
http://www.redhat.com/archives/linux-audit/2006-February/msg00004.html
This interfaces included in this patch will allow selinux to perform more
efficient matches based on lower level constructs within the selinux module,
rather than relying on string comparisons. It also allows for dominance checks
on the mls portion of the contexts that are impossible with only string
comparisons. Dustin's previous patch will be modified to take advantage of
the new interface.
This is still a work in progress (I'm guessing that the conversion of Dustin's
earlier work will point out some improvements to these interfaces). I also
need to check the context of memory allocations.
I'm only allow == and != for type, role, and user because they seemed to be
the only ones that make sense, is that OK, or should I take them all even
though they may not do anything useful? Does the general approach seem acceptable?
The patch is against Al Viro's audit tree:
http://www.kernel.org/git/?p=linux/kernel/git/viro/audit-current.git;a=su...
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 4bb4b9f..dd4f759 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -140,6 +140,11 @@
#define AUDIT_PERS 10
#define AUDIT_ARCH 11
#define AUDIT_MSGTYPE 12
+#define AUDIT_SE_USER 13 /* security label user */
+#define AUDIT_SE_ROLE 14 /* security label role */
+#define AUDIT_SE_TYPE 15 /* security label type */
+#define AUDIT_SE_SEN 16 /* security label sensitivity label */
+#define AUDIT_SE_CLR 17 /* security label clearance label */
/* These are ONLY useful when checking
* at syscall exit time (AUDIT_AT_EXIT). */
diff --git a/include/linux/selinux.h b/include/linux/selinux.h
new file mode 100644
index 0000000..5a402a5
--- /dev/null
+++ b/include/linux/selinux.h
@@ -0,0 +1,83 @@
+/*
+ * SELinux services exported to the rest of the kernel.
+ *
+ * Author: James Morris <jmorris(a)redhat.com>
+ *
+ * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris(a)redhat.com>
+ * Copyright (C) 2006 Trusted Computer Solutions <dgoeddel(a)trustedcs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#ifndef _LINUX_SELINUX_H
+#define _LINUX_SELINUX_H
+
+#ifdef CONFIG_SECURITY_SELINUX
+
+/**
+ * selinux_audit_rule_init - alloc/init an selinux audit rule structure.
+ * @field: the field this rule refers to
+ * @op: the operater the rule uses
+ * @rulestr: the text "target" of the rule
+ * @rule: address of the rule structure pointer to be returned
+ *
+ * Returns 0 if successful, -errno if not. On success, the rule structure
+ * will be allocated internally. The caller must free this structure with
+ * selinux_audit_rule_free() after use.
+ */
+int selinux_audit_rule_init(u32 field, u32 op, const char *rulestr,
+ void **rule);
+
+/**
+ * selinux_audit_rule_free - free an selinux audit rule structure.
+ * @rule: address of the seliux_audit_rule structure to be freed
+ *
+ * This will free all memory associated with the given rule.
+ */
+void selinux_audit_rule_free(void *rule);
+
+/**
+ * selinux_audit_rule_match - determine if a context ID matches a rule.
+ * @ctxid: the context ID to check
+ * @rule: the audit rule created by selinux_audit_rule_init()
+ *
+ * Returns 1 if the context id matches the rule, 0 if it does not, and
+ * -errno on failure.
+ */
+int selinux_audit_rule_match(u32 ctxid, void *rule);
+
+/**
+ * selinux_task_getsecid - retrieve the context ID of a process.
+ * @tsk: the task_struct of the process we are interested in
+ *
+ * Returns the context ID of the process.
+ */
+
+int selinux_task_getsecid(struct task_struct *tsk);
+
+#else
+
+int selinux_audit_rule_init(u32 field, u32 op, const char *rulestr, void **rule)
+{
+ return -ENOTSUPP;
+}
+
+void selinux_audit_rule_free(void *rule)
+{
+ return;
+}
+
+int selinux_audit_rule_match(u32 ctxid, void *rule)
+{
+ return 0;
+}
+
+int selinux_task_getsecid(struct task_struct *tsk)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SECURITY_SELINUX */
+
+#endif /* _LINUX_SELINUX_H */
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 688c0a2..faf2e02 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
-selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o
+selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
new file mode 100644
index 0000000..a44e301
--- /dev/null
+++ b/security/selinux/exports.c
@@ -0,0 +1,44 @@
+/*
+ * SELinux services exported to the rest of the kernel.
+ *
+ * Author: James Morris <jmorris(a)redhat.com>
+ *
+ * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris(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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/selinux.h>
+
+#include "security.h"
+#include "objsec.h"
+
+int selinux_audit_rule_init(u32 field, u32 op, const char *rulestr, void **rule)
+{
+ return security_aurule_init(field, op, rulestr, rule);
+}
+
+void selinux_audit_rule_free(void *rule)
+{
+ return security_aurule_free(rule);
+}
+
+int selinux_audit_rule_match(u32 ctxid, void *rule)
+{
+ return security_aurule_match(ctxid, rule);
+}
+
+int selinux_task_getsecid(struct task_struct *tsk)
+{
+ struct task_security_struct *tsec = tsk->security;
+ return tsec->sid;
+}
+
+EXPORT_SYMBOL_GPL(selinux_audit_rule_init);
+EXPORT_SYMBOL_GPL(selinux_audit_rule_free);
+EXPORT_SYMBOL_GPL(selinux_audit_rule_match);
+EXPORT_SYMBOL_GPL(selinux_task_getsecid);
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 5f016c9..bfea536 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -96,5 +96,9 @@ int security_fs_use(const char *fstype,
int security_genfs_sid(const char *fstype, char *name, u16 sclass,
u32 *sid);
+int security_aurule_init(u32 field, u32 op, const char *rulestr, void **rule);
+void security_aurule_free(void *rule);
+int security_aurule_match(u32 ctxid, void *rule);
+
#endif /* _SELINUX_SECURITY_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index d877cd1..480df81 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1810,3 +1810,250 @@ out:
POLICY_RDUNLOCK;
return rc;
}
+
+struct selinux_audit_rule {
+ u32 au_skip;
+ u32 au_op;
+ u32 au_field;
+ u32 au_seqno;
+ struct context au_ctxt;
+ char *au_str;
+};
+
+/* needs policy read lock held */
+static void aurule_init_context(struct selinux_audit_rule *aurule)
+{
+ struct role_datum *roledatum;
+ struct type_datum *typedatum;
+ struct user_datum *userdatum;
+ char *tmpstr;
+ int rc = 0;
+
+ switch (aurule->au_field) {
+ case AUDIT_SE_USER:
+ userdatum = hashtab_search(policydb.p_users.table,
+ aurule->au_str);
+ if (!userdatum)
+ rc = -EINVAL;
+ else
+ aurule->au_ctxt.user = userdatum->value;
+ break;
+ case AUDIT_SE_ROLE:
+ roledatum = hashtab_search(policydb.p_roles.table,
+ aurule->au_str);
+ if (!roledatum)
+ rc = -EINVAL;
+ else
+ aurule->au_ctxt.role = roledatum->value;
+ break;
+ case AUDIT_SE_TYPE:
+ typedatum = hashtab_search(policydb.p_types.table,
+ aurule->au_str);
+ if (!typedatum)
+ rc = -EINVAL;
+ else
+ aurule->au_ctxt.type = typedatum->value;
+ break;
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ /* TODO:figure out proper allocation below */
+ tmpstr = kstrdup(aurule->au_str, GFP_KERNEL);
+ rc = mls_context_to_sid(':', &tmpstr, &aurule->au_ctxt, NULL,
+ SECSID_NULL);
+ kfree(tmpstr);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ aurule->au_skip = 1;
+ context_destroy(&aurule->au_ctxt);
+ } else {
+ /* we merely flags this rule to not be processed - the role,
+ user, type, or level of the rule may not be valid now, but
+ may be after a future policy reload. */
+ aurule->au_skip = 0;
+ }
+
+ return;
+}
+
+int security_aurule_init(u32 field, u32 op, const char *rulestr, void **rule)
+{
+ struct selinux_audit_rule *tmprule;
+
+ *rule = NULL;
+
+ switch (field) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ /* only 'equals' and 'not equals' make sense */
+ if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
+ return -EINVAL;
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ /* we do not allow a range, indicated by '-' */
+ if (strchr(rulestr, '-'))
+ return -EINVAL;
+ }
+
+ /* TODO:figure out proper allocations below */
+ tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
+ if (!tmprule)
+ return -ENOMEM;
+ tmprule->au_str = kstrdup(rulestr, GFP_KERNEL);
+ if (!tmprule->au_str) {
+ kfree(tmprule);
+ return -ENOMEM;
+ }
+ tmprule->au_op = op;
+ tmprule->au_field = field;
+ context_init(&tmprule->au_ctxt);
+
+ if (!ss_initialized) {
+ tmprule->au_seqno = latest_granting;
+ tmprule->au_skip = 1;
+ *rule = tmprule;
+ return 0;
+ }
+
+ POLICY_RDLOCK;
+
+ tmprule->au_seqno = latest_granting;
+ aurule_init_context(tmprule);
+
+ POLICY_RDUNLOCK;
+
+ *rule = tmprule;
+
+ return 0;
+}
+
+void security_aurule_free(void *rule)
+{
+ struct selinux_audit_rule *aurule = rule;
+
+ kfree(aurule->au_str);
+ context_destroy(&aurule->au_ctxt);
+ kfree(aurule);
+}
+
+int security_aurule_match(u32 ctxid, void *rule)
+{
+ struct selinux_audit_rule *aurule = rule;
+ struct context *ctxt;
+ struct mls_level *level;
+ int match = 0;
+
+ if (!rule || !ss_initialized)
+ return 0;
+
+ POLICY_RDLOCK;
+
+ if (aurule->au_seqno < latest_granting) {
+ context_destroy(&aurule->au_ctxt);
+ aurule->au_seqno = latest_granting;
+ aurule_init_context(aurule);
+ }
+
+ if (aurule->au_skip)
+ goto out;
+
+ ctxt = sidtab_search(&sidtab, ctxid);
+ if (!ctxt) {
+ /* TODO: what to do? */
+ printk(KERN_ERR "security_aurule_match: unrecognized SID %d\n",
+ ctxid);
+ match = -EINVAL;
+ goto out;
+ }
+
+ switch (aurule->au_field) {
+ case AUDIT_SE_USER:
+ switch (aurule->au_op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->user == aurule->au_ctxt.user);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->user != aurule->au_ctxt.user);
+ break;
+ default:
+ match = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIT_SE_ROLE:
+ switch (aurule->au_op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->role == aurule->au_ctxt.role);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->role != aurule->au_ctxt.role);
+ break;
+ default:
+ match = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIT_SE_TYPE:
+ switch (aurule->au_op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->type == aurule->au_ctxt.type);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->type != aurule->au_ctxt.type);
+ break;
+ default:
+ match = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ level = (aurule->au_op == AUDIT_SE_SEN ?
+ &ctxt->range.level[0] : &ctxt->range.level[1]);
+ switch (aurule->au_op) {
+ case AUDIT_EQUAL:
+ match = mls_level_eq(&aurule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = !mls_level_eq(&aurule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_LESS_THAN:
+ match = mls_level_dom(&aurule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_LESS_THAN_OR_EQUAL:
+ match = (mls_level_eq(&aurule->au_ctxt.range.level[0],
+ level) ||
+ mls_level_dom(&aurule->au_ctxt.range.level[0],
+ level));
+ break;
+ case AUDIT_GREATER_THAN:
+ match = mls_level_dom(level,
+ &aurule->au_ctxt.range.level[0]);
+ break;
+ case AUDIT_GREATER_THAN_OR_EQUAL:
+ match = (mls_level_eq(&aurule->au_ctxt.range.level[0],
+ level) ||
+ mls_level_dom(level,
+ &aurule->au_ctxt.range.level[0]));
+ break;
+ default:
+ match = -EINVAL;
+ break;
+ }
+ default:
+ match = -EINVAL;
+ break;
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return match;
+}
--
Darrel
18 years, 9 months
[PATCH] leak in audit_inode_context
by Steve Grubb
Hi,
Tracked down the last memory leak that I can see. When inode data
gets updated, new context information is allocated and the ctx pointer
gets overwritten without freeing what it points to. The patch below fixes
this.
Signed-off-by: Steve Grubb <sgrubb(a)redhat.com>
diff -urp linux-2.6.15.x86_64.orig/kernel/auditsc.c linux-2.6.15.x86_64/kernel/auditsc.c
--- linux-2.6.15.x86_64.orig/kernel/auditsc.c 2006-03-09 16:53:46.000000000 -0500
+++ linux-2.6.15.x86_64/kernel/auditsc.c 2006-03-09 16:52:10.000000000 -0500
@@ -985,6 +985,7 @@ void audit_inode_context(int idx, const
if (len < 0)
goto error_path;
+ kfree(context->names[idx].ctx);
context->names[idx].ctx = ctx;
goto ret;
18 years, 9 months
[PATCH] filesystem location based auditing
by Amy Griffis
Hello,
This patch provides the functionality which allows a user to specify
audit rules targeting specific filesystem locations. It is an update
of the previously posted patch which provided functionality solely for
adding/removing rules with AUDIT_WATCH fields:
https://www.redhat.com/archives/linux-audit/2006-February/msg00034.html
The patch is intended to be a replacement for linux-2.6-audit-string-2.patch
in the current LSPP test kernel, and the git tree patch represented by
commit 7f54bafe959bcecda97a4a1e4a083a39fb4d9fc8 in branch amg.b0.
Since it depends on the inotify kernel API patch, the commits should
probably be re-ordered in the next branch.
This patch adds parent data structures for filesystem watches
specified in audit rules. A 'parent' is analogous to the dentry
parent for a watched filesystem location. When a parent is added, a
corresponding inotify watch is registered for it. The inotify event
handler specified during during initialization (audit_init) determines
an action to take for various events received from inotify.
The actions taken fall into two groups. When an event is received
which indicates a change at a watched filesystem location, audit
replaces rules in the AUDIT_FILTER_EXIT list with updated copies
containing the new inode # (or the invalid value on removal). When
inotify indicates the parent is being removed from the filesystem,
audit removes the parent, its associated watches, and their
associated rules.
Part of this functionality was also previously posted here as a WIP:
https://www.redhat.com/archives/linux-audit/2006-February/msg00030.html
This patch improves on the WIP patch in the following ways:
- Adds per-filterlist spinlocks to protect against concurrent
filterlist writes/manipulations.
- Improves code for copying rules for list_replace_rcu().
- Removes the per-watch and per-parent semaphores, which weren't
playing well with rcu.
- Moves calls to add/remove inotify watches out to separate
threads, which enables us to hold the filterlist lock through
the duration of operations to add or remove audit rules. I
attempted a solution involving dropping the filterlist locks
part way through, and found it to be difficult to handle the
races.
The locking model implemented in this patch consists of a two-level
lock hierarchy:
per-filterlist spinlock
parentlist spinlock
The per-filterlist spinlock protects the filterlist, the rules it
contains, and the watch.rules and parent.watches lists that are
associated with the filterlist. The contents of the watch structure
are write-once, so don't need an additional lock.
The parentlist spinlock protects the master_parents list and the data
in the parent structure, excluding the parent.watches list.
In this patch, only the AUDIT_FILTER_EXIT list lock is used, but I
added the others as I anticipate they will be used for rule updates
from SELinux policy reload.
This locking model seems to be sufficient to cover the potential race
conditions, but more granularity could be added for performance or
code clarity. I decided to keep it as simple as possible for the
initial implementation.
Limitations:
This implementation has the following known limitations:
- The dentry parent for a watched filesystem location must exist.
- Only one watch may be specified per rule.
Todo:
I anticipate some changes may need to be made to audit_update_rule()
and audit_dupe_rule() to properly accommodate the SELinux filtering
that Darrel and Dustin are implementing.
Additionally, the update of rules resulting from the removal of a file
at a watched filesystem location happens too early in the syscall
path. This causes the inode number for the rule to be invalidated
prior to syscall exit filtering, and the record that should have been
emitted is not. I'm still working on a good solution for this.
TIA for reviewing this patch.
Regards,
Amy
include/linux/audit.h | 1
kernel/audit.c | 22 +
kernel/audit.h | 34 ++
kernel/auditfilter.c | 700 +++++++++++++++++++++++++++++++++++++++++++++-----
kernel/auditsc.c | 53 +--
5 files changed, 709 insertions(+), 101 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index c208554..d76fa58 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -148,6 +148,7 @@
#define AUDIT_INODE 102
#define AUDIT_EXIT 103
#define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */
+#define AUDIT_WATCH 105
#define AUDIT_ARG0 200
#define AUDIT_ARG1 (AUDIT_ARG0+1)
diff --git a/kernel/audit.c b/kernel/audit.c
index 4eb97b6..82d926e 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -55,6 +55,9 @@
#include <net/netlink.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
+#include <linux/inotify.h>
+
+#include "audit.h"
/* No auditing will take place until audit_initialized != 0.
* (Initialization happens after skb_init is called.) */
@@ -99,6 +102,12 @@ static atomic_t audit_lost = ATOMIC_I
/* The netlink socket. */
static struct sock *audit_sock;
+/* Inotify device. */
+struct inotify_device *audit_idev;
+
+/* Audit filter lists, initialized in audit_init() */
+extern struct audit_flist audit_filter_list[];
+
/* The audit_freelist is a list of pre-allocated audit buffers (if more
* than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
* being placed on the freelist). */
@@ -552,6 +561,14 @@ static void audit_receive(struct sock *s
/* Initialize audit support at boot time. */
static int __init audit_init(void)
{
+ int i;
+
+ /* must be initialized before any audit_log calls */
+ for (i = 0; i < AUDIT_NR_FILTERS; i++) {
+ INIT_LIST_HEAD(&audit_filter_list[i].head);
+ spin_lock_init(&audit_filter_list[i].lock);
+ }
+
printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
audit_default ? "enabled" : "disabled");
audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
@@ -564,6 +581,11 @@ static int __init audit_init(void)
audit_initialized = 1;
audit_enabled = audit_default;
audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
+
+ audit_idev = inotify_init(audit_handle_ievent);
+ if (IS_ERR(audit_idev))
+ audit_panic("cannot initialize inotify device");
+
return 0;
}
__initcall(audit_init);
diff --git a/kernel/audit.h b/kernel/audit.h
index 4b602cd..ecb69d0 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -22,6 +22,8 @@
#include <linux/fs.h>
#include <linux/audit.h>
+struct inotify_event;
+
/* 0 = no checking
1 = put_count checking
2 = verbose put_count checking
@@ -52,6 +54,23 @@ enum audit_state {
};
/* Rule lists */
+struct audit_parent {
+ atomic_t count; /* reference count */
+ unsigned int flags; /* flag in-process removals */
+ unsigned long ino; /* associated inode number */
+ u32 wd; /* inotify watch descriptor */
+ struct list_head mlist; /* entry in master_parents */
+ struct list_head watches; /* associated watches */
+};
+
+struct audit_watch {
+ atomic_t count; /* reference count */
+ char *path; /* watch insertion path */
+ struct list_head rules; /* associated rules */
+ struct list_head wlist; /* entry in audit_parent.watches list*/
+ struct audit_parent *parent; /* associated parent */
+};
+
struct audit_field {
u32 type;
u32 val;
@@ -67,18 +86,29 @@ struct audit_krule {
u32 buflen; /* for data alloc on list rules */
u32 field_count;
struct audit_field *fields;
+ struct audit_watch *watch; /* associated watch */
+ struct list_head rlist; /* entry in audit_watch.rules list */
};
struct audit_entry {
struct list_head list;
struct rcu_head rcu;
- struct audit_krule rule;
+ struct audit_entry *replacement; /* used with list_replace_rcu() */
+ unsigned int flags; /* flag in-process adds and removals */
+ struct audit_krule rule; /* audit rule data */
};
+struct audit_flist {
+ struct list_head head;
+ spinlock_t lock; /* syncs filter data manipulation */
+};
extern int audit_pid;
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
-
+extern int audit_compare_dname_path(const char *dname, const char *path);
+extern void audit_handle_ievent(struct inotify_event *event,
+ const char *dname, struct inode * inode,
+ void *ptr);
extern void audit_send_reply(int pid, int seq, int type,
int done, int multi,
void *payload, int size);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 5735acd..697a688 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -22,26 +22,65 @@
#include <linux/kernel.h>
#include <linux/audit.h>
#include <linux/kthread.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
#include <linux/netlink.h>
+#include <linux/inotify.h>
#include "audit.h"
-/* There are three lists of rules -- one to search at task creation
- * time, one to search at syscall entry time, and another to search at
- * syscall exit time. */
-struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
- LIST_HEAD_INIT(audit_filter_list[0]),
- LIST_HEAD_INIT(audit_filter_list[1]),
- LIST_HEAD_INIT(audit_filter_list[2]),
- LIST_HEAD_INIT(audit_filter_list[3]),
- LIST_HEAD_INIT(audit_filter_list[4]),
- LIST_HEAD_INIT(audit_filter_list[5]),
-#if AUDIT_NR_FILTERS != 6
-#error Fix audit_filter_list initialiser
-#endif
-};
+/* Audit filter lists */
+struct audit_flist audit_filter_list[AUDIT_NR_FILTERS];
+
+static LIST_HEAD(master_parents);
+static DEFINE_SPINLOCK(master_parents_lock);
+
+/* Inotify device. */
+extern struct inotify_device *audit_idev;
+
+/* 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
+
+/* Flags for stale filterlist data */
+#define AUDIT_ENTRY_ADD 0x01 /* Rule entry addition in progress. */
+#define AUDIT_ENTRY_DEL 0x02 /* Rule entry deletion in progress. */
+#define AUDIT_PARENT_DEL 0x01 /* Parent deletion in progress. */
+
+static inline void audit_get_parent(struct audit_parent *parent)
+{
+ atomic_inc(&parent->count);
+}
+
+static inline void audit_put_parent(struct audit_parent *parent)
+{
+ if (atomic_dec_and_test(&parent->count)) {
+ BUG_ON(!list_empty(&parent->watches));
+ kfree(parent);
+ }
+}
+
+static inline void audit_get_watch(struct audit_watch *watch)
+{
+ atomic_inc(&watch->count);
+}
+
+static inline void audit_put_watch(struct audit_watch *watch)
+{
+ if (atomic_dec_and_test(&watch->count)) {
+ BUG_ON(!list_empty(&watch->rules));
+ /* watches that were never added don't have a parent */
+ if (watch->parent)
+ audit_put_parent(watch->parent);
+ kfree(watch->path);
+ kfree(watch);
+ }
+}
static inline void audit_free_rule(struct audit_entry *e)
{
+ /* handle rules that don't have associated watches */
+ if (e->rule.watch)
+ audit_put_watch(e->rule.watch);
kfree(e->rule.fields);
kfree(e);
}
@@ -52,6 +91,190 @@ static inline void audit_free_rule_rcu(s
audit_free_rule(e);
}
+/* Remove all watches & rules associated with a parent that is going away. */
+static inline void audit_remove_parent_watches(struct audit_parent *parent)
+{
+ struct audit_watch *w, *nextw;
+ struct audit_krule *r, *nextr;
+ struct audit_entry *e;
+ struct audit_flist *flist = &audit_filter_list[AUDIT_FILTER_EXIT];
+
+ spin_lock(&flist->lock);
+ list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
+ list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
+ e = container_of(r, struct audit_entry, rule);
+
+ /* make sure we have a valid copy */
+ while (e->replacement != NULL)
+ e = e->replacement;
+ if (e->flags & AUDIT_ENTRY_DEL)
+ continue;
+
+ list_del(&r->rlist);
+ list_del_rcu(&e->list);
+ e->flags |= AUDIT_ENTRY_DEL;
+ call_rcu(&e->rcu, audit_free_rule_rcu);
+ audit_log(NULL, GFP_ATOMIC, AUDIT_CONFIG_CHANGE,
+ "audit implicitly removed rule from list=%d\n",
+ AUDIT_FILTER_EXIT);
+ }
+ list_del(&w->wlist);
+ audit_put_watch(w);
+ }
+ spin_unlock(&flist->lock);
+}
+
+/* Actually remove the parent; inotify has acknowleged the removal. */
+static inline void audit_remove_parent(struct audit_parent *parent)
+{
+ BUG_ON(!list_empty(&parent->watches));
+ spin_lock(&master_parents_lock);
+ list_del(&parent->mlist);
+ audit_put_parent(parent);
+ spin_unlock(&master_parents_lock);
+}
+
+
+/* Register this parent's inotify watch. */
+static int audit_inotify_register(void *_data)
+{
+ void **data = _data;
+ struct audit_parent *parent;
+ char *path;
+ struct nameidata nd;
+ int err;
+ u32 wd;
+
+ parent = data[0];
+ path = data[1];
+ kfree(data);
+
+ err = path_lookup(path, LOOKUP_PARENT, &nd);
+ if (err)
+ goto handle_error;
+
+ wd = inotify_add_watch(audit_idev, nd.dentry->d_inode, AUDIT_IN_WATCH,
+ parent);
+ if (wd < 0)
+ goto handle_error;
+
+ spin_lock(&master_parents_lock);
+ parent->wd = wd;
+ parent->ino = nd.dentry->d_inode->i_ino;
+ spin_unlock(&master_parents_lock);
+
+ path_release(&nd);
+ audit_put_parent(parent);
+ return 0;
+
+handle_error:
+ path_release(&nd);
+ audit_remove_parent_watches(parent);
+ audit_remove_parent(parent);
+
+ audit_put_parent(parent);
+ return 0;
+}
+
+/* Unregister this parent's inotify watch. Generates an IN_IGNORED event. */
+static int audit_inotify_unregister(void *data)
+{
+ struct audit_parent *parent = data;
+ s32 wd;
+
+ spin_lock(&master_parents_lock);
+ wd = parent->wd;
+ spin_unlock(&master_parents_lock);
+
+ if (inotify_ignore(audit_idev, wd))
+ printk(KERN_ERR
+ "%s:%d: unable to remove inotify watch for inode %lu\n",
+ __FILE__, __LINE__, parent->ino);
+ audit_put_parent(parent);
+ return 0;
+}
+
+/* Initialize a parent watch entry. */
+static inline struct audit_parent *audit_init_parent(char *path,
+ unsigned long ino)
+{
+ struct audit_parent *parent;
+ void **data;
+ struct task_struct *task;
+
+ parent = kmalloc(sizeof(*parent), GFP_ATOMIC);
+ if (unlikely(!parent))
+ return ERR_PTR(-ENOMEM);
+
+ memset(parent, 0, sizeof(*parent));
+ INIT_LIST_HEAD(&parent->watches);
+ atomic_set(&parent->count, 0);
+ parent->ino = ino;
+ audit_get_parent(parent);
+
+ /* Spawn a thread to register the parent's inotify watch without
+ * the filterlist spinlock. */
+ data = kmalloc(2 * sizeof(void *), GFP_ATOMIC);
+ if (!data) {
+ audit_put_parent(parent);
+ return ERR_PTR(-ENOMEM);
+ }
+ data[0] = parent;
+ data[1] = path;
+ audit_get_parent(parent);
+ task = kthread_run(audit_inotify_register, data,
+ "audit_inotify_register");
+ if (IS_ERR(task)) {
+ audit_put_parent(parent);
+ return ERR_PTR(PTR_ERR(task));
+ }
+
+ return parent;
+}
+
+/* Initialize a watch entry. */
+static inline struct audit_watch *audit_init_watch(char *path)
+{
+ struct audit_watch *watch;
+
+ watch = kmalloc(sizeof(*watch), GFP_KERNEL);
+ if (unlikely(!watch))
+ return ERR_PTR(-ENOMEM);
+
+ memset(watch, 0, sizeof(*watch));
+ INIT_LIST_HEAD(&watch->rules);
+ atomic_set(&watch->count, 0);
+ watch->path = path;
+ audit_get_watch(watch);
+
+ return watch;
+}
+
+/* Initialize an audit filterlist entry. */
+static inline struct audit_entry *audit_init_entry(u32 field_count,
+ gfp_t gfp_mask)
+{
+ struct audit_entry *entry;
+ struct audit_field *fields;
+
+ entry = kmalloc(sizeof(*entry), gfp_mask);
+ if (unlikely(!entry))
+ return NULL;
+
+ fields = kmalloc(sizeof(*fields) * field_count, gfp_mask);
+ if (unlikely(!fields)) {
+ kfree(entry);
+ return NULL;
+ }
+
+ memset(entry, 0, sizeof(struct audit_entry));
+ memset(fields, 0, sizeof(struct audit_field) * field_count);
+
+ entry->rule.fields = fields;
+
+ return entry;
+}
+
/* Unpack a filter field's string representation from user-space
* buffer. */
static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
@@ -79,12 +302,33 @@ static char *audit_unpack_string(void **
return str;
}
+/* Translate a watch string to kernel respresentation. */
+static int audit_to_watch(char *path, struct audit_krule *krule, int fidx)
+{
+ struct audit_field *f = &krule->fields[fidx];
+ struct audit_watch *watch;
+
+ if (path[0] != '/' || path[f->val-1] == '/' ||
+ krule->listnr != AUDIT_FILTER_EXIT ||
+ f->op & ~AUDIT_EQUAL)
+ return -EINVAL;
+
+ watch = audit_init_watch(path);
+ if (unlikely(IS_ERR(watch)))
+ return PTR_ERR(watch);
+
+ audit_get_watch(watch);
+ krule->watch = watch;
+ f->val = (unsigned int)-1;
+
+ return 0;
+}
+
/* Common user-space to kernel rule translation. */
static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
{
unsigned listnr;
struct audit_entry *entry;
- struct audit_field *fields;
int i, err;
err = -EINVAL;
@@ -108,23 +352,14 @@ static inline struct audit_entry *audit_
goto exit_err;
err = -ENOMEM;
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
- if (unlikely(!entry))
+ entry = audit_init_entry(rule->field_count, GFP_KERNEL);
+ if (!entry)
goto exit_err;
- fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL);
- if (unlikely(!fields)) {
- kfree(entry);
- goto exit_err;
- }
-
- memset(&entry->rule, 0, sizeof(struct audit_krule));
- memset(fields, 0, sizeof(struct audit_field));
entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
entry->rule.listnr = listnr;
entry->rule.action = rule->action;
entry->rule.field_count = rule->field_count;
- entry->rule.fields = fields;
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
entry->rule.mask[i] = rule->mask[i];
@@ -150,15 +385,16 @@ static struct audit_entry *audit_rule_to
for (i = 0; i < rule->field_count; i++) {
struct audit_field *f = &entry->rule.fields[i];
- if (rule->fields[i] & AUDIT_UNUSED_BITS) {
- err = -EINVAL;
- goto exit_free;
- }
-
f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
f->val = rule->values[i];
+ if (f->type & AUDIT_UNUSED_BITS ||
+ f->type == AUDIT_WATCH) {
+ err = -EINVAL;
+ goto exit_free;
+ }
+
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
if (f->op & AUDIT_NEGATE)
f->op |= AUDIT_NOT_EQUAL;
@@ -182,8 +418,9 @@ static struct audit_entry *audit_data_to
int err = 0;
struct audit_entry *entry;
void *bufp;
- /* size_t remain = datasz - sizeof(struct audit_rule_data); */
+ size_t remain = datasz - sizeof(struct audit_rule_data);
int i;
+ char *path;
entry = audit_to_entry_common((struct audit_rule *)data);
if (IS_ERR(entry))
@@ -201,10 +438,20 @@ static struct audit_entry *audit_data_to
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
f->type = data->fields[i];
+ f->val = data->values[i];
switch(f->type) {
- /* call type-specific conversion routines here */
- default:
- f->val = data->values[i];
+ case AUDIT_WATCH:
+ path = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(path))
+ goto exit_free;
+ entry->rule.buflen += f->val;
+
+ err = audit_to_watch(path, &entry->rule, i);
+ if (err) {
+ kfree(path);
+ goto exit_free;
+ }
+ break;
}
}
@@ -234,7 +481,8 @@ static struct audit_rule *audit_krule_to
struct audit_rule *rule;
int i;
- rule = kmalloc(sizeof(*rule), GFP_KERNEL);
+ /* use GFP_ATOMIC because we're under rcu_read_lock() */
+ rule = kmalloc(sizeof(*rule), GFP_ATOMIC);
if (unlikely(!rule))
return ERR_PTR(-ENOMEM);
memset(rule, 0, sizeof(*rule));
@@ -265,7 +513,8 @@ static struct audit_rule_data *audit_kru
void *bufp;
int i;
- data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
+ /* use GFP_ATOMIC because we're under rcu_read_lock() */
+ data = kmalloc(sizeof(*data) + krule->buflen, GFP_ATOMIC);
if (unlikely(!data))
return ERR_PTR(-ENOMEM);
memset(data, 0, sizeof(*data));
@@ -280,7 +529,10 @@ static struct audit_rule_data *audit_kru
data->fields[i] = f->type;
data->fieldflags[i] = f->op;
switch(f->type) {
- /* call type-specific conversion routines here */
+ case AUDIT_WATCH:
+ data->buflen += data->values[i] =
+ audit_pack_string(&bufp, krule->watch->path);
+ break;
default:
data->values[i] = f->val;
}
@@ -290,6 +542,12 @@ static struct audit_rule_data *audit_kru
return data;
}
+/* Compare two watches. Considered success if rules don't match. */
+static inline int audit_compare_watch(struct audit_watch *a, struct audit_watch *b)
+{
+ return strcmp(a->path, b->path);
+}
+
/* Compare two rules in kernel format. Considered success if rules
* don't match. */
static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
@@ -308,7 +566,10 @@ static int audit_compare_rule(struct aud
return 1;
switch(a->fields[i].type) {
- /* call type-specific comparison routines here */
+ case AUDIT_WATCH:
+ if (audit_compare_watch(a->watch, b->watch))
+ return 1;
+ break;
default:
if (a->fields[i].val != b->fields[i].val)
return 1;
@@ -322,45 +583,264 @@ static int audit_compare_rule(struct aud
return 0;
}
+/* Copy an audit rule entry to be replaced.
+ * Caller must hold filterlist lock. */
+static inline struct audit_entry *audit_dupe_rule(struct audit_entry *old)
+{
+ u32 fcount = old->rule.field_count;
+ struct audit_entry *new;
+ struct audit_field *fields;
+ struct audit_watch *watch;
+
+ new = audit_init_entry(fcount, GFP_ATOMIC);
+ if (unlikely(!new))
+ return ERR_PTR(-ENOMEM);
+
+ fields = new->rule.fields;
+ memcpy(&new->rule, &old->rule, sizeof(struct audit_krule));
+ memcpy(fields, old->rule.fields, sizeof(struct audit_field) * fcount);
+ new->rule.fields = fields;
+
+ watch = old->rule.watch;
+ audit_get_watch(watch);
+ list_add(&new->rule.rlist, &watch->rules);
+ list_del(&old->rule.rlist);
+
+ return new;
+}
+
+/* Update an audit rule field. If the rule is part of a filterlist, caller
+ * must hold that filterlist's lock. */
+static void audit_update_rule(struct audit_krule *krule, u32 type, u32 val)
+{
+ int i;
+ struct audit_entry *old, *new;
+
+ for (i = 0; i < AUDIT_MAX_FIELDS; i++) {
+ if (krule->fields[i].type != type)
+ continue;
+
+ old = container_of(krule, struct audit_entry, rule);
+
+ /* rule is not in filterlist yet */
+ if (old->flags & AUDIT_ENTRY_ADD) {
+ krule->fields[i].val = val;
+ return;
+ }
+
+ /* make sure we have a valid copy */
+ while (old->replacement != NULL)
+ old = old->replacement;
+ if (old->flags & AUDIT_ENTRY_DEL)
+ return;
+
+ new = audit_dupe_rule(old);
+ if (unlikely(IS_ERR(new))) {
+ audit_panic("cannot allocate memory for rule update");
+ return;
+ }
+ new->rule.fields[i].val = val;
+
+ old->flags |= AUDIT_ENTRY_DEL;
+ old->replacement = new;
+ list_replace_rcu(&old->list, &new->list);
+ call_rcu(&old->rcu, audit_free_rule_rcu);
+ }
+}
+
+/* Find an existing parent entry for this watch, or create a new one.
+ * Caller must hold exit filterlist lock. */
+static inline struct audit_parent *audit_find_parent(char *path)
+{
+ int err;
+ struct nameidata nd;
+ struct audit_parent *p, *parent;
+ unsigned long ino;
+
+ err = path_lookup(path, LOOKUP_PARENT, &nd);
+ if (err) {
+ path_release(&nd);
+ parent = ERR_PTR(err);
+ goto out;
+ }
+
+ /* walk list locked for safe compare of ino field */
+ spin_lock(&master_parents_lock);
+ list_for_each_entry(p, &master_parents, mlist) {
+ if (p->ino != nd.dentry->d_inode->i_ino ||
+ p->flags & AUDIT_PARENT_DEL)
+ continue;
+
+ spin_unlock(&master_parents_lock);
+ path_release(&nd);
+ parent = p;
+ goto out;
+ }
+ ino = nd.dentry->d_inode->i_ino;
+ spin_unlock(&master_parents_lock);
+ path_release(&nd);
+
+ /* Initialize parent with this inode #; the registration thread will
+ * catch any changes. */
+ parent = audit_init_parent(path, ino);
+ if (unlikely(IS_ERR(parent)))
+ goto out;
+
+ spin_lock(&master_parents_lock);
+ list_add(&parent->mlist, &master_parents);
+ spin_unlock(&master_parents_lock);
+
+out:
+ return parent;
+}
+
+/* Find a matching watch entry, or add this one.
+ * Caller must hold exit filterlist lock. */
+static inline int audit_add_watch(struct audit_krule *krule)
+{
+ struct audit_parent *parent;
+ struct audit_watch *w, *watch = krule->watch;
+ struct nameidata nd;
+
+ parent = audit_find_parent(watch->path);
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ list_for_each_entry(w, &parent->watches, wlist) {
+ if (audit_compare_watch(watch, w))
+ continue;
+
+ audit_put_watch(watch); /* krule's ref */
+ audit_put_watch(watch); /* destroy */
+
+ audit_get_watch(w);
+ krule->watch = watch = w;
+ goto add_rule;
+ }
+
+ audit_get_parent(parent);
+ watch->parent = parent;
+ list_add(&watch->wlist, &parent->watches);
+
+add_rule:
+ list_add(&krule->rlist, &watch->rules);
+
+ if (path_lookup(watch->path, 0, &nd) == 0)
+ audit_update_rule(krule, AUDIT_WATCH,
+ nd.dentry->d_inode->i_ino);
+ path_release(&nd);
+ return 0;
+}
+
/* Add rule to given filterlist if not a duplicate. Protected by
* audit_netlink_sem. */
static inline int audit_add_rule(struct audit_entry *entry,
- struct list_head *list)
+ struct audit_flist *flist)
{
struct audit_entry *e;
+ int err;
+
+ /* The *_rcu iterator is needed to protect from filterlist
+ * updates or removals. */
+ rcu_read_lock();
+ list_for_each_entry_rcu(e, &flist->head, list) {
+ if (e->flags & AUDIT_ENTRY_DEL)
+ continue;
+ if (!audit_compare_rule(&entry->rule, &e->rule)) {
+ err = -EEXIST;
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+ rcu_read_unlock();
+
+ spin_lock(&flist->lock);
+ entry->flags |= AUDIT_ENTRY_ADD;
- /* Do not use the _rcu iterator here, since this is the only
- * addition routine. */
- list_for_each_entry(e, list, list) {
- if (!audit_compare_rule(&entry->rule, &e->rule))
- return -EEXIST;
+ if (entry->rule.watch) {
+ err = audit_add_watch(&entry->rule);
+ if (err)
+ goto error;
}
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
- list_add_rcu(&entry->list, list);
+ list_add_rcu(&entry->list, &flist->head);
} else {
- list_add_tail_rcu(&entry->list, list);
+ list_add_tail_rcu(&entry->list, &flist->head);
}
+ entry->flags &= ~AUDIT_ENTRY_ADD;
+ spin_unlock(&flist->lock);
+
return 0;
+
+error:
+ if (entry->rule.watch)
+ audit_put_watch(entry->rule.watch);
+ 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 exit filterlist lock */
+static inline void audit_remove_watch(struct audit_krule *krule)
+{
+ struct audit_watch *watch = krule->watch;
+ struct audit_parent *parent = watch->parent;
+ struct task_struct *task;
+
+ list_del(&krule->rlist);
+ if (list_empty(&watch->rules)) {
+ list_del(&watch->wlist);
+ audit_put_watch(watch);
+
+ if (list_empty(&parent->watches)) {
+ /* This flag only read when user adds a watch,
+ * which is prevented by audit_netlink_sem. */
+ parent->flags |= AUDIT_PARENT_DEL;
+
+ /* Spawn a thread to unregister the parent's inotify
+ * watch without the filterlist spinlock. */
+ audit_get_parent(parent);
+ task = kthread_run(audit_inotify_unregister, parent,
+ "audit_inotify_unregister");
+ if (IS_ERR(task))
+ printk(KERN_ERR
+ "%s:%d: unable to remove inotify watch for inode %lu\n",
+ __FILE__, __LINE__, parent->ino);
+ }
+ }
}
/* Remove an existing rule from filterlist. Protected by
* audit_netlink_sem. */
static inline int audit_del_rule(struct audit_entry *entry,
- struct list_head *list)
+ struct audit_flist *flist)
{
struct audit_entry *e;
+ int ret = 0;
- /* Do not use the _rcu iterator here, since this is the only
- * deletion routine. */
- list_for_each_entry(e, list, list) {
- if (!audit_compare_rule(&entry->rule, &e->rule)) {
- list_del_rcu(&e->list);
- call_rcu(&e->rcu, audit_free_rule_rcu);
- return 0;
+ spin_lock(&flist->lock);
+ list_for_each_entry(e, &flist->head, list) {
+ if (e->flags & AUDIT_ENTRY_DEL ||
+ audit_compare_rule(&entry->rule, &e->rule))
+ continue;
+
+ if (e->rule.watch) {
+ audit_remove_watch(&e->rule);
+ audit_put_watch(entry->rule.watch);
}
+
+ list_del_rcu(&e->list);
+ e->flags |= AUDIT_ENTRY_DEL;
+ call_rcu(&e->rcu, audit_free_rule_rcu);
+ spin_unlock(&flist->lock);
+
+ return ret;
}
+ spin_unlock(&flist->lock);
+ if (entry->rule.watch)
+ audit_put_watch(entry->rule.watch);
return -ENOENT; /* No matching rule */
}
@@ -379,10 +859,12 @@ static int audit_list(void *_dest)
down(&audit_netlink_sem);
- /* The *_rcu iterators not needed here because we are
- always called with audit_netlink_sem held. */
+ /* The *_rcu iterator is needed to protect from filesystem
+ * updates or removals. */
for (i=0; i<AUDIT_NR_FILTERS; i++) {
- list_for_each_entry(entry, &audit_filter_list[i], list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, &audit_filter_list[i].head,
+ list) {
struct audit_rule *rule;
rule = audit_krule_to_rule(&entry->rule);
@@ -392,6 +874,7 @@ static int audit_list(void *_dest)
rule, sizeof(*rule));
kfree(rule);
}
+ rcu_read_unlock();
}
audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
@@ -413,19 +896,21 @@ static int audit_list_rules(void *_dest)
down(&audit_netlink_sem);
- /* The *_rcu iterators not needed here because we are
- always called with audit_netlink_sem held. */
+ /* The *_rcu iterator is needed to protect from filesystem
+ * updates or removals. */
for (i=0; i<AUDIT_NR_FILTERS; i++) {
- list_for_each_entry(e, &audit_filter_list[i], list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(e, &audit_filter_list[i].head, list) {
struct audit_rule_data *data;
data = audit_krule_to_data(&e->rule);
if (unlikely(!data))
break;
audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
- data, sizeof(*data));
+ data, sizeof(*data) + data->buflen);
kfree(data);
}
+ rcu_read_unlock();
}
audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
@@ -516,6 +1001,58 @@ int audit_receive_filter(int type, int p
return err;
}
+/* Update inode numbers in audit rules based on filesystem event. */
+static inline void audit_update_ino(struct audit_parent *parent,
+ const char *dname, u32 ino)
+{
+ struct audit_watch *w;
+ struct audit_krule *r, *next;
+ struct audit_flist *flist = &audit_filter_list[AUDIT_FILTER_EXIT];
+ struct audit_buffer *ab;
+
+ spin_lock(&flist->lock);
+ list_for_each_entry(w, &parent->watches, wlist) {
+ if (audit_compare_dname_path(dname, w->path))
+ continue;
+
+ list_for_each_entry_safe(r, next, &w->rules, rlist)
+ audit_update_rule(r, AUDIT_WATCH, ino);
+
+ ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_CONFIG_CHANGE);
+ audit_log_format(ab, "audit updated rules specifying watch=");
+ audit_log_untrustedstring(ab, w->path);
+ audit_log_format(ab, " with ino=%u\n", ino);
+ audit_log_end(ab);
+ break;
+ }
+ spin_unlock(&flist->lock);
+}
+
+/**
+ * audit_handle_ievent - handler for Inotify events
+ * @event: information about the event
+ * @dname: dentry name associated with event
+ * @inode: inode associated with event
+ * @ptr: kernel's version of a watch descriptor
+ */
+void audit_handle_ievent(struct inotify_event *event, const char *dname,
+ struct inode *inode, void *ptr)
+{
+ struct audit_parent *parent = (struct audit_parent *)ptr;
+
+ if (event->mask & (IN_CREATE|IN_MOVED_TO) && inode)
+ audit_update_ino(parent, dname, (unsigned int)inode->i_ino);
+ else if (event->mask & (IN_DELETE|IN_MOVED_FROM))
+ audit_update_ino(parent, dname, (unsigned int)-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 (event->mask & (AUDIT_IN_SELF))
+ audit_remove_parent_watches(parent);
+ else if (event->mask & IN_IGNORED)
+ audit_remove_parent(parent);
+}
+
int audit_comparator(const u32 left, const u32 op, const u32 right)
{
switch (op) {
@@ -536,7 +1073,39 @@ int audit_comparator(const u32 left, con
}
}
+/* Compare given dentry name with last component in given path,
+ * return of 0 indicates a match. */
+int audit_compare_dname_path(const char *dname, const char *path)
+{
+ int dlen, plen;
+ const char *p;
+ if (!dname || !path)
+ return 1;
+
+ dlen = strlen(dname);
+ plen = strlen(path);
+ if (plen < dlen)
+ return 1;
+
+ /* disregard trailing slashes */
+ p = path + plen - 1;
+ while ((*p == '/') && (p > path))
+ p--;
+
+ /* find last path component */
+ p = p - dlen + 1;
+ if (p < path)
+ return 1;
+ else if (p > path) {
+ if (*--p != '/')
+ return 1;
+ else
+ p++;
+ }
+
+ return strncmp(p, dname, dlen);
+}
static int audit_filter_user_rules(struct netlink_skb_parms *cb,
struct audit_krule *rule,
@@ -581,7 +1150,8 @@ int audit_filter_user(struct netlink_skb
int ret = 1;
rcu_read_lock();
- list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
+ list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER].head,
+ list) {
if (audit_filter_user_rules(cb, &e->rule, &state)) {
if (state == AUDIT_DISABLED)
ret = 0;
@@ -599,10 +1169,10 @@ int audit_filter_type(int type)
int result = 0;
rcu_read_lock();
- if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
+ if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE].head))
goto unlock_and_return;
- list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
+ list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE].head,
list) {
int i;
for (i = 0; i < e->rule.field_count; i++) {
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 8ff51b3..4626c35 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -60,7 +60,7 @@
#include "audit.h"
-extern struct list_head audit_filter_list[];
+extern struct audit_flist audit_filter_list[];
/* No syscall auditing will take place unless audit_enabled != 0. */
extern int audit_enabled;
@@ -241,7 +241,8 @@ static int audit_filter_rules(struct tas
}
break;
case AUDIT_INODE:
- if (ctx) {
+ case AUDIT_WATCH:
+ if (ctx && f->val != (unsigned int)-1) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
audit_comparator(ctx->names[j].pino, f->op, f->val)) {
@@ -286,7 +287,8 @@ static enum audit_state audit_filter_tas
enum audit_state state;
rcu_read_lock();
- list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
+ list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK].head,
+ list) {
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
rcu_read_unlock();
return state;
@@ -342,7 +344,7 @@ static inline struct audit_context *audi
if (context->in_syscall && !context->auditable) {
enum audit_state state;
- state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
+ state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT].head);
if (state == AUDIT_RECORD_CONTEXT)
context->auditable = 1;
}
@@ -789,7 +791,7 @@ void audit_syscall_entry(struct task_str
state = context->state;
if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)
- state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
+ state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY].head);
if (likely(state == AUDIT_DISABLED))
return;
@@ -1033,37 +1035,20 @@ void __audit_inode_child(const char *dna
return;
/* determine matching parent */
- if (dname)
- for (idx = 0; idx < context->name_count; idx++)
- if (context->names[idx].pino == pino) {
- const char *n;
- const char *name = context->names[idx].name;
- int dlen = strlen(dname);
- int nlen = name ? strlen(name) : 0;
-
- if (nlen < dlen)
- continue;
-
- /* disregard trailing slashes */
- n = name + nlen - 1;
- while ((*n == '/') && (n > name))
- n--;
-
- /* find last path component */
- n = n - dlen + 1;
- if (n < name)
- continue;
- else if (n > name) {
- if (*--n != '/')
- continue;
- else
- n++;
- }
+ if (!dname)
+ goto no_match;
+ for (idx = 0; idx < context->name_count; idx++)
+ if (context->names[idx].pino == pino) {
+ const char *name = context->names[idx].name;
- if (strncmp(n, dname, dlen) == 0)
- goto update_context;
- }
+ if (!name)
+ continue;
+
+ if (audit_compare_dname_path(dname, name) == 0)
+ goto update_context;
+ }
+no_match:
/* catch-all in case match not found */
idx = context->name_count++;
context->names[idx].name = NULL;
18 years, 9 months
audit 1.1.5 released
by Steve Grubb
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:
- Changed audit_log_semanage_message to take new params
- In aureport, add class between syscall and permission in avc report
- Fix bug where fsync is called in debug mode
- Add optional support for tty in SYSCALL records for ausearch/aureport
- Reinstate legacy rule operator support
- Add man pages
- Auditd ignore most signals
This is mostly a bugfix release.
Please let me know if there are any problems.
-Steve
18 years, 9 months
- git-audit-fixes.patch removed from -mm tree
by akpm@osdl.org
The patch titled
git-audit-fixes
has been removed from the -mm tree. Its filename is
git-audit-fixes.patch
This patch was probably dropped from -mm because
it has now been merged into a subsystem tree or
into Linus's tree, or because it was folded into
its parent patch in the -mm tree.
From: Andrew Morton <akpm(a)osdl.org>
kernel/auditfilter.c: In function `audit_comparator':
kernel/auditfilter.c:554: warning: control reaches end of non-void function
kernel/auditfilter.c: At top level:
kernel/auditfilter.c:58: warning: `audit_unpack_string' defined but not used
Cc: <linux-audit(a)redhat.com>
Cc: Al Viro <viro(a)ftp.linux.org.uk>
Signed-off-by: Andrew Morton <akpm(a)osdl.org>
---
kernel/auditfilter.c | 31 ++-----------------------------
1 files changed, 2 insertions(+), 29 deletions(-)
diff -puN kernel/auditfilter.c~git-audit-fixes kernel/auditfilter.c
--- devel/kernel/auditfilter.c~git-audit-fixes 2006-03-03 01:09:08.000000000 -0800
+++ devel-akpm/kernel/auditfilter.c 2006-03-03 01:09:08.000000000 -0800
@@ -52,33 +52,6 @@ static inline void audit_free_rule_rcu(s
audit_free_rule(e);
}
-/* Unpack a filter field's string representation from user-space
- * buffer. */
-static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
-{
- char *str;
-
- if (!*bufp || (len == 0) || (len > *remain))
- return ERR_PTR(-EINVAL);
-
- /* Of the currently implemented string fields, PATH_MAX
- * defines the longest valid length.
- */
- if (len > PATH_MAX)
- return ERR_PTR(-ENAMETOOLONG);
-
- str = kmalloc(len + 1, GFP_KERNEL);
- if (unlikely(!str))
- return ERR_PTR(-ENOMEM);
-
- memcpy(str, *bufp, len);
- str[len] = 0;
- *bufp += len;
- *remain -= len;
-
- return str;
-}
-
/* Common user-space to kernel rule translation. */
static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
{
@@ -551,10 +524,10 @@ int audit_comparator(const u32 left, con
case AUDIT_GREATER_THAN_OR_EQUAL:
return (left >= right);
}
+ BUG();
+ return 0;
}
-
-
static int audit_filter_user_rules(struct netlink_skb_parms *cb,
struct audit_krule *rule,
enum audit_state *state)
_
Patches currently in -mm which might be from akpm(a)osdl.org are
linus.patch
fix-ide-locking-error-tidy.patch
multiple-exports-of-strpbrk.patch
git-acpi-up-fix.patch
git-alsa.patch
git-audit-master.patch
blk_execute_rq_nowait-speedup.patch
revert-gregkh-driver-put_device-might_sleep.patch
revert-gregkh-driver-fix-up-the-sysfs-pollable-patch.patch
revert-gregkh-driver-allow-sysfs-attribute-files-to-be-pollable.patch
sysfs_h-cleanup.patch
get_cpu_sysdev-signedness-fix.patch
git-dvb.patch
git-input.patch
git-kbuild.patch
git-libata-all.patch
m25p80-printk-warning-fix.patch
git-netdev-all.patch
revert-ipw2200-Fix-WPA-network-selection-problem.patch
ipw2200-warning-fix.patch
3c509-bus-registration-fix.patch
git-net-vs-remove-module_parm.patch
optimise-d_find_alias-fix.patch
revert-gregkh-pci-x86-pci-domain-support-the-meat.patch
git-pcmcia.patch
git-scsi-misc.patch
megaraid-unused-variable.patch
git-sas-jg.patch
git-sparc64.patch
gregkh-usb-usb-optimise-devio.c-usbdev_read-fix.patch
revert-gregkh-usb-usb-reduce-syslog-clutter.patch
git-watchdog.patch
revert-x86_64-mm-i386-early-alignment.patch
x86_64-mm-dmi-year-fix.patch
revert-x86_64-mm-dmi-early.patch
git-xfs.patch
drivers-block-floppyc-dont-free_irq-from-irq-context-fix.patch
slab-cleanup.patch
slab-remove-slab_no_reap-option-fix.patch
on_each_cpu-disable-local-interupts.patch
slab-use-on_each_cpu.patch
vmscan-scan_control-cleanup.patch
vmscan-use-unsigned-longs.patch
vmscan-return-nr_reclaimed.patch
vmscan-rename-functions.patch
mm-prep_zero_page-in-irq-is-a-bug.patch
hugepage-small-fixes-to-hugepage-clear-copy-path-tidy.patch
hugepage-serialize-hugepage-allocation-and-instantiation-tidy.patch
slab-cache_reap-further-reduction-in-interrupt-holdoff-fix.patch
mm-implement-swap-prefetching-fix.patch
acx1xx-wireless-driver.patch
via-pmu-warning-fix.patch
x86-early-printk-remove-max_ypos-and-max_xpos-macros.patch
register-the-boot-cpu-in-the-cpu-maps-earlier-fix.patch
i386-actively-synchronize-vmalloc-area-when-registering-certain-callbacks-tidy.patch
fix-the-imlicit-declaration-of-mtrr_centaur_report_mcr-in-arch-i386-kernel-cpu-centaurc-fix.patch
enable-sci_emulate-to-manually-simulate-physical-hotplug-testing-fix.patch
revert-swsusp-fix-breakage-with-swap-on-lvm.patch
pm-print-name-of-failed-suspend-function.patch
swsusp-resume-parsing-fix.patch
ext3_readdir-use-generic-readahead.patch
sem2mutex-blockdev-2-git-blktrace-fix.patch
notifier-profileh-forward-decl.patch
pause_on_oops-command-line-option.patch
more-for_each_cpu-conversions.patch
free_uid-locking-improvement.patch
filemap_fdata_write-api-fix-end-parameter.patch
fadvise-async-write-commands.patch
balance_dirty_pages_ratelimited-take-nr_pages-arg.patch
set_page_dirty-return-value-fixes.patch
msync-perform-dirty-page-levelling.patch
msync-ms_sync-dont-hold-mmap_sem-while-syncing.patch
msync-fix-return-value.patch
fsync-extract-internal-code.patch
msync-use-do_fsync.patch
mmc-sdhci-build-fix.patch
sys_setrlimit-cleanup.patch
rlimit_cpu-fix-handling-of-a-zero-limit.patch
rlimit_cpu-document-wrong-return-value.patch
jbd-embed-j_commit_timer-in-journal-struct.patch
jbd-convert-kjournald-to-kthread-api.patch
hysdn-remove-custom-types.patch
remove-module_parm-fix.patch
sysrq-cleanup.patch
initcall-failure-reporting.patch
kconfig-clarify-memory-debug-options.patch
tpm-sparc32-build-fix.patch
ads7846-build-fix.patch
irq-uninline-migration-functions.patch
3c59x-use-mii_check_media-tidy.patch
permit-dual-mit-gpl-licenses.patch
2tb-files-add-blkcnt_t-fixes.patch
ext3-get-blocks-maping-multiple-blocks-at-a-once-vs-ext3_readdir-use-generic-readahead.patch
pass-b_size-to-get_block-speedup.patch
pass-b_size-to-get_block-remove-unneeded-assignments.patch
map-multiple-blocks-for-mpage_readpages-tidy.patch
ext3-cleanups-and-warn_on.patch
time-clocksource-infrastructure-remove-nsec_t.patch
time-generic-timekeeping-infrastructure-remove-nsec_t.patch
time-fix-cpu-frequency-detection.patch
kretprobe-instance-recycled-by-parent-process-tidy.patch
kretprobe-instance-recycled-by-parent-process-fix.patch
edac-switch-to-kthread_-api-tidy.patch
edac-kconfig-dependency-changes-fix.patch
cmpci-dont-use-generig_hweight32.patch
uninline-zone-helpers-fix.patch
uninline-zone-helpers-prefetch-fix.patch
lightweight-robust-futexes-arch-defaults-fix.patch
tref-fix-task_ref-reference-counting-fix.patch
proc-dont-lock-task_structs-indefinitely-git-nfs-fix.patch
proc-use-sane-permission-checks-on-the-proc-pid-fd-fix.patch
proc-use-sane-permission-checks-on-the-proc-pid-fd-fix-2.patch
reiser4-only.patch
reiser4-only-stop-using-__put_page.patch
reiser4-swsusp-build-fix.patch
reiser4-printk-warning-fix.patch
reiser4-mm-remove-pg_highmem-fix.patch
reiser4-big-update-bug-fix-for-readpage-fix.patch
reiser4-big-update-rename-print_address.patch
reiser4-page-private-fixes.patch
reiser4-big-update-div64-fix.patch
reiser4-remove-c99isms.patch
reiser4_releasepage-gfp_t-fixes.patch
reiser4-big-update-update_atime-fixes.patch
reiser4-vs-nfs-apply-mount-root-dentry-override-to-filesystems.patch
ide_generic_all_on-warning-fix.patch
fbdev-framebuffer-driver-for-geode-gx-warning-fix.patch
dm-remove-sector_format.patch
ia64-const-f_ops-fix.patch
kgdb-ga-remove-stuff.patch
kgdb-remove-NO_CPUS.patch
kgdb-remove-KGDB_TS.patch
kgdb-remove-STACK_OVERFLOW_TEST.patch
kgdb-remove-TRAP_BAD_SYSCALL_EXITS.patch
kgdb-always-KGDB_CONSOLE.patch
kgdb-remove-CONFIG_KGDB_USER_CONSOLE.patch
kgdb-serial-cleanup.patch
kgdb-serial-cleanup-2.patch
kgdb-serial-cleanup-3.patch
kgdb-nmi-cleanup.patch
kgdb-cleanup-version.patch
kgdb-cleanup-includes.patch
kgdb-remove-KGDB_SYSRQ.patch
kgdb-rename-breakpoint.patch
kgdb-convert-for-cpu-helpers.patch
kgdb-select-debug_info.patch
nr_blockdev_pages-in_interrupt-warning.patch
device-suspend-debug.patch
revert-tty-buffering-comment-out-debug-code.patch
18 years, 9 months
EXPORT_SYMBOL patch for audit_log, audit_log_start, audit_log_end and audit_format
by Lorenzo Hernández García-Hierro
Hi,
This is a trivial patch that enables the possibility of using some auditing
functions within loadable kernel modules (ie. inside a Linux Security Module).
_
Make the audit_log_start, audit_log_end, audit_format and audit_log
interfaces available to Loadable Kernel Modules, thus making possible
the usage of the audit framework inside LSMs, etc.
Signed-off-by: <Lorenzo Hernández García-Hierro <lorenzo(a)gnu.org>>
---
kernel/audit.c | 5 +++++
1 file changed, 5 insertions(+)
diff -puN kernel/audit.c~audit-export-interfaces kernel/audit.c
--- linux-2.6.15.6/kernel/audit.c~audit-export-interfaces 2006-03-07 02:12:26.000000000 +0100
+++ linux-2.6.15.6-lorenzo/kernel/audit.c 2006-03-07 02:18:32.000000000 +0100
@@ -895,3 +895,8 @@ void audit_log(struct audit_context *ctx
audit_log_end(ab);
}
}
+
+EXPORT_SYMBOL(audit_log_start);
+EXPORT_SYMBOL(audit_log_end);
+EXPORT_SYMBOL(audit_log_format);
+EXPORT_SYMBOL(audit_log);
_
18 years, 9 months
[patch 1/3] git-audit-fixes
by akpm@osdl.org
From: Andrew Morton <akpm(a)osdl.org>
kernel/auditfilter.c: In function `audit_comparator':
kernel/auditfilter.c:554: warning: control reaches end of non-void function
kernel/auditfilter.c: At top level:
kernel/auditfilter.c:58: warning: `audit_unpack_string' defined but not used
Cc: <linux-audit(a)redhat.com>
Cc: Al Viro <viro(a)ftp.linux.org.uk>
Signed-off-by: Andrew Morton <akpm(a)osdl.org>
---
kernel/auditfilter.c | 31 ++-----------------------------
1 files changed, 2 insertions(+), 29 deletions(-)
diff -puN kernel/auditfilter.c~git-audit-fixes kernel/auditfilter.c
--- devel/kernel/auditfilter.c~git-audit-fixes 2006-03-03 01:09:08.000000000 -0800
+++ devel-akpm/kernel/auditfilter.c 2006-03-03 01:09:08.000000000 -0800
@@ -52,33 +52,6 @@ static inline void audit_free_rule_rcu(s
audit_free_rule(e);
}
-/* Unpack a filter field's string representation from user-space
- * buffer. */
-static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
-{
- char *str;
-
- if (!*bufp || (len == 0) || (len > *remain))
- return ERR_PTR(-EINVAL);
-
- /* Of the currently implemented string fields, PATH_MAX
- * defines the longest valid length.
- */
- if (len > PATH_MAX)
- return ERR_PTR(-ENAMETOOLONG);
-
- str = kmalloc(len + 1, GFP_KERNEL);
- if (unlikely(!str))
- return ERR_PTR(-ENOMEM);
-
- memcpy(str, *bufp, len);
- str[len] = 0;
- *bufp += len;
- *remain -= len;
-
- return str;
-}
-
/* Common user-space to kernel rule translation. */
static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
{
@@ -551,10 +524,10 @@ int audit_comparator(const u32 left, con
case AUDIT_GREATER_THAN_OR_EQUAL:
return (left >= right);
}
+ BUG();
+ return 0;
}
-
-
static int audit_filter_user_rules(struct netlink_skb_parms *cb,
struct audit_krule *rule,
enum audit_state *state)
_
18 years, 9 months