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] 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
Another slab size-32 leak 2.6.16-rc4-mm2
by Valdis.Kletnieks@vt.edu
My system isn't leaking as badly after the first patch, but I still
currently have about 174,000+ leaked size-32 from <ipcperms+0xf/0x91>
after about an hour's uptime.
Looks like if audit_ipc_context() is called from elsewhere in kernel/auditsc.c,
it's cleaned up after. However, the call from ipc/util.c in ipcperms() doesn't
seem to get cleaned up after (and, in fact, it isn't clear why it's even called
there, at least to me...)
diff --git a/ipc/util.c b/ipc/util.c
index 8626219..e37e1e9 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -27,6 +27,7 @@
#include <linux/workqueue.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
+#include <linux/audit.h>
#include <asm/unistd.h>
@@ -468,6 +469,7 @@ int ipcperms (struct kern_ipc_perm *ipcp
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
int requested_mode, granted_mode;
+ audit_ipc_context(ipcp);
requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode;
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
Looks like the source of my problem...
18 years, 9 months
[Fwd: Re: [patch] inotify: lock avoidance with parent watch status in dentry]
by Stephen Smalley
FYI.
-------- Forwarded Message --------
From: Andrew Morton <akpm(a)osdl.org>
To: Nick Piggin <nickpiggin(a)yahoo.com.au>
Cc: holt(a)sgi.com, john(a)johnmccutchan.com, linux-kernel(a)vger.kernel.org,
rml(a)novell.com, hch(a)lst.de, linux-fsdevel(a)vger.kernel.org, Al Viro
<viro(a)ftp.linux.org.uk>
Subject: Re: [patch] inotify: lock avoidance with parent watch status in
dentry
Date: Mon, 27 Feb 2006 16:48:12 -0800
Nick Piggin <nickpiggin(a)yahoo.com.au> wrote:
>
> Previous inotify work avoidance is good when inotify is completely
> unused, but it breaks down if even a single watch is in place anywhere
> in the system. Robin Holt notices that udev is one such culprit - it
> slows down a 512-thread application on a 512 CPU system from 6 seconds
> to 22 minutes.
A problem is that the audit tree (believe it or not) adds a pile of new
inotify functionality. I don't know what those changes do and they might
conflict with the changes you've made (apart from giving us two copies of
inotify_inode_watched()) and the audit changes were apparently only
socialised on the linux-audit mailing list and my twice-sent patch to make
the audit tree compile has been ignored for a couple of weeks.
So I'm going to bitbucket the audit tree until a) it compiles and b) its
inotify changes have been explained and reviewed and c) we've reviewed
those changes against your optimisations. I think fixes will be needed.
18 years, 9 months
Linux Auditing patch and additional questions
by Mauricio Lin
Hi all,
I am looking for the patch done by Rik Faith according to the webpage
http://lwn.net/Articles/73623/. The link
http://people.redhat.com/faith/audit/audit-20040226.1411.patch looks not
available anymore.
I would like to check the patch to see clearly the code included in the
kernel and what exactly were changed.
BTW, what is the main difference between the auditing syscall and the strace
tool?
Does auditing syscall provide more accurate values?
Futhermore does auditing tool provide the time or elapsed time that a system
call was allocated for each processor in a SMP platform? Does it trace the
system call by processor?
BR,
Mauricio Lin.
18 years, 9 months
audit-related slab memory leak in recent -mm kernels?
by Valdis.Kletnieks@vt.edu
For a while, I've been seeing a pretty serious leak in slab-32 entries in -mm
kernels. Doing a quilt bisection on -mm calls out git-audit.patch as the
offender.
In kernel/auditsc.c, we have audit_inode_context(), which does:
ctx = kmalloc(len, GFP_KERNEL);
...
context->names[idx].ctx = ctx;
but the only obvious kfree() I can find is in audit_free_names(), but that
one is (a) inside an if statement along with a printk(KERN_ERR) and (b) has
a '#if AUDIT_DEBUG == 2' around it.
[/usr/src/linux-2.6.16-rc4-mm2/kernel]1 grep -n '\.ctx' *.c
auditsc.c:384: kfree(context->names[i].ctx);
auditsc.c:686: if (context->names[i].ctx) {
auditsc.c:688: context->names[i].ctx);
auditsc.c:961: context->names[idx].ctx = ctx;
Is this my memory leak? If so, who is supposed to be freeing it?
18 years, 9 months
[RFC][PATCH] collect security labels on user processes generating audit messages
by Timothy R. Chavez
Hello,
This patch collects the security label for a user process generating
audit messages. Traditionally, obtaining the security label of a
process in the kernel could be done using security_getprocattr().
However, due to the asynchronous nature of netlink, by the time the
kernel gets the audit message, the 'pid' packaged with it, may have
already been recycled. Thus, if a task is found with 'pid', it may not
be the same task that issued the audit message, and the wrong security
label could be collected. One solution to this problem and the solution
implemented here, is to collect the 'sid' of the process, while we are
running in that process' context. This can be accomplished by attaching
the 'sid' to the audit message on the client-side of the netlink
interaction much like 'loginuid'. When the message arrives to the
kernel and is processed by the audit subsystem, the 'sid' can then be
resolved to the correct security label. One thing to keep in mind, is
that should the policy be changed and reloaded while an audit message is
in transit, the security label the 'sid' associated with that audit
message resolves to, may be invalid or incorrect.
Notable details about this implementation:
1) A new SELinux interface was introduced to give other parts of the
kernel the ability to resolve 'sids' into security labels.
2) SELinux and LSM were augmented to obtain the 'sid' of a task in a
similar manner to ipc and inode.
I've posted this message and patch as an RFC to solicit feedback from
the SELinux and audit communities.
I personally think that the Kconfig option could be a bit more
descriptive and selinux_getsecurity() could probably find a new home
outside of selinux/include/objsec.h, though that seemed to make sense
for me, at the time of coding.
Thank you.
-tim
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 6a2ccf7..ccd5905 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -143,6 +143,7 @@ struct netlink_skb_parms
__u32 dst_group;
kernel_cap_t eff_cap;
__u32 loginuid; /* Login (audit) uid */
+ __u32 secid; /* SELinux security id */
};
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
diff --git a/include/linux/security.h b/include/linux/security.h
index b4fe8aa..4178175 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -625,6 +625,11 @@ struct swap_info_struct;
* @p contains the task_struct for the task.
* @inode contains the inode structure for the inode.
*
+ * @task_getsecurity:
+ * Copy the security label associated with the task object into
+ * @buffer. @buffer may be NULL to request the size of the buffer
+ * required. @size indicates the size of @buffer in bytes. Return
+ * number of bytes used/required on success.
* Security hooks for Netlink messaging.
*
* @netlink_send:
@@ -1169,6 +1174,7 @@ struct security_operations {
unsigned long arg5);
void (*task_reparent_to_init) (struct task_struct * p);
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
+ int (*task_getsecurity)(struct task_struct *tsk, void *buffer, size_t size);
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size);
@@ -1817,6 +1823,11 @@ static inline void security_task_to_inod
security_ops->task_to_inode(p, inode);
}
+static inline int security_task_getsecurity(struct task_struct *tsk,void *buffer, size_t size)
+{
+ return security_ops->task_getsecurity(tsk, buffer, size);
+}
+
static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
short flag)
{
@@ -2457,6 +2468,11 @@ static inline void security_task_reparen
static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
{ }
+static inline int security_task_getsecurity(struct task_struct *tsk, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
short flag)
{
diff --git a/include/linux/selinux_api.h b/include/linux/selinux_api.h
new file mode 100644
index 0000000..54102ab
--- /dev/null
+++ b/include/linux/selinux_api.h
@@ -0,0 +1,38 @@
+/*
+ * External SELinux API
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2006
+ *
+ * Author: Timothy R. Chavez <tinytim(a)us.ibm.com>
+ *
+ */
+
+#ifndef _LINUX_SELINUX_API_H
+#define _LINUX_SELINUX_API_H
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#ifdef CONFIG_SECURITY_SELINUX_API
+int selinux_sid_to_context(u32 sid, void *ctx, size_t size);
+#else
+static inline int selinux_sid_to_context(u32 sid, void *ctx, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_SELINUX_API */
+#endif /* _LINUX_SELINUX_API_H */
diff --git a/kernel/audit.c b/kernel/audit.c
index d95efd6..0f158f0 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -49,6 +49,7 @@
#include <linux/err.h>
#include <linux/kthread.h>
+#include <linux/selinux_api.h>
#include <linux/audit.h>
#include <net/sock.h>
@@ -383,7 +384,7 @@ static int audit_netlink_ok(kernel_cap_t
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
- u32 uid, pid, seq;
+ u32 uid, pid, sid, seq;
void *data;
struct audit_status *status_get, status_set;
int err;
@@ -391,6 +392,8 @@ static int audit_receive_msg(struct sk_b
u16 msg_type = nlh->nlmsg_type;
uid_t loginuid; /* loginuid of sender */
struct audit_sig_info sig_data;
+ int len;
+ char *ctx = NULL;
err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
if (err)
@@ -409,6 +412,7 @@ static int audit_receive_msg(struct sk_b
pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid;
loginuid = NETLINK_CB(skb).loginuid;
+ sid = NETLINK_CB(skb).secid;
seq = nlh->nlmsg_seq;
data = NLMSG_DATA(nlh);
@@ -460,11 +464,26 @@ static int audit_receive_msg(struct sk_b
err = 0;
ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
if (ab) {
+ len = selinux_sid_to_context(sid, NULL, 0);
+ if (len < 0 && len != -EOPNOTSUPP)
+ return len;
+ else if (len > 0) {
+ ctx = (char *)kmalloc(len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ len = selinux_sid_to_context(sid, ctx, len);
+ if (len < 0) {
+ kfree(ctx);
+ return len;
+ }
+ }
audit_log_format(ab,
- "user pid=%d uid=%u auid=%u msg='%.1024s'",
- pid, uid, loginuid, (char *)data);
+ "user pid=%d uid=%u auid=%u subj=%s msg='%.1024s'",
+ pid, uid, loginuid, (!ctx ? "null" : ctx),
+ (char *)data);
audit_set_pid(ab, pid);
audit_log_end(ab);
+ kfree(ctx);
}
}
break;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 96020d7..a516453 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1120,6 +1120,7 @@ static int netlink_sendmsg(struct kiocb
NETLINK_CB(skb).dst_pid = dst_pid;
NETLINK_CB(skb).dst_group = dst_group;
NETLINK_CB(skb).loginuid = audit_get_loginuid(current->audit_context);
+ NETLINK_CB(skb).secid = security_task_getsid(current);
memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
/* What can I do? Netlink is asynchronous, so that
diff --git a/security/dummy.c b/security/dummy.c
index 75e7c4a..768a39a 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -557,6 +557,11 @@ static void dummy_task_reparent_to_init
static void dummy_task_to_inode(struct task_struct *p, struct inode *inode)
{ }
+static int dummy_task_getsecurity(struct task_struct *tsk, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag)
{
return 0;
@@ -934,6 +939,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, task_prctl);
set_to_dummy_if_null(ops, task_reparent_to_init);
set_to_dummy_if_null(ops, task_to_inode);
+ set_to_dummy_if_null(ops, task_getsecurity);
set_to_dummy_if_null(ops, ipc_permission);
set_to_dummy_if_null(ops, ipc_getsecurity);
set_to_dummy_if_null(ops, msg_msg_alloc_security);
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index b59582b..424c24e 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -95,3 +95,11 @@ config SECURITY_SELINUX_CHECKREQPROT_VAL
via /selinux/checkreqprot if authorized by policy.
If you are unsure how to answer this question, answer 1.
+
+config SECURITY_SELINUX_API
+ bool "NSA SELinux API"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option exposes internal SELinux concepts such as a 'security id'
+ to other parts of the kernel.
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index b038cd0..4cf0782 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -8,5 +8,7 @@ selinux-y := avc.o hooks.o selinuxfs.o n
selinux-$(CONFIG_SECURITY_NETWORK) += netif.o
+selinux-$(CONFIG_SECURITY_SELINUX_API) += selinux_api.o
+
EXTRA_CFLAGS += -Isecurity/selinux/include
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 21c8aa6..88e9d87 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -118,7 +118,7 @@ static DEFINE_SPINLOCK(sb_security_lock)
/* Return security context for a given sid or just the context
length if the buffer is null or length is 0 */
-static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
+int selinux_getsecurity(u32 sid, void *buffer, size_t size)
{
char *context;
unsigned len;
@@ -2769,6 +2769,13 @@ static void selinux_task_to_inode(struct
return;
}
+static int selinux_task_getsecurity(struct task_struct *tsk, void *buffer, size_t size)
+{
+ struct task_security_struct *tsec = tsk->security;
+
+ return selinux_getsecurity(tsec->sid, buffer, size);
+}
+
#ifdef CONFIG_SECURITY_NETWORK
/* Returns error only if unable to parse addresses */
@@ -4319,6 +4326,7 @@ static struct security_operations selinu
.task_prctl = selinux_task_prctl,
.task_reparent_to_init = selinux_task_reparent_to_init,
.task_to_inode = selinux_task_to_inode,
+ .task_getsecurity = selinux_task_getsecurity,
.ipc_permission = selinux_ipc_permission,
.ipc_getsecurity = selinux_ipc_getsecurity,
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 887937c..9fc4692 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -109,4 +109,6 @@ struct sk_security_struct {
extern unsigned int selinux_checkreqprot;
+int selinux_getsecurity(u32 sid, void *buffer, size_t size);
+
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/selinux_api.c b/security/selinux/selinux_api.c
new file mode 100644
index 0000000..7604766
--- /dev/null
+++ b/security/selinux/selinux_api.c
@@ -0,0 +1,8 @@
+#include <linux/selinux_api.h>
+
+extern int selinux_getsecurity(u32 sid, void *buffer, size_t size);
+
+int selinux_sid_to_context(u32 sid, void *ctx, size_t size)
+{
+ return selinux_getsecurity(sid, ctx, size);
+}
18 years, 9 months
Linux audit for Debian
by Mauricio Lin
Hi all,
I got the "audit-1.1.4.tar.gz" from http://people.redhat.com/sgrubb/audit/,
but I got some errors during the make step on my Debian system as below:
In file included from /usr/include/linux/sched.h:12,
from /usr/include/linux/audit.h:27,
from libaudit.h:35,
from libaudit.c:42:
/usr/include/linux/jiffies.h:84: error: syntax error before 'jiffies_64'
/usr/include/linux/jiffies.h:88: error: syntax error before 'get_jiffies_64'
/usr/include/linux/jiffies.h: In function 'timespec_to_jiffies':
/usr/include/linux/jiffies.h:320: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:320: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:320: error: 'NSEC_PER_SEC' undeclared (first
use in this function)
/usr/include/linux/jiffies.h:320: error: (Each undeclared identifier is
reported only once
/usr/include/linux/jiffies.h:320: error: for each function it appears in.)
/usr/include/linux/jiffies.h:321: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:321: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:324: error: syntax error before 'sec'
/usr/include/linux/jiffies.h:324: error: syntax error before 'NSEC_PER_SEC'
/usr/include/linux/jiffies.h:324: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:324: error: syntax error before ')' token
/usr/include/linux/jiffies.h: In function 'jiffies_to_timespec':
/usr/include/linux/jiffies.h:337: error: syntax error before 'nsec'
/usr/include/linux/jiffies.h:338: error: 'nsec' undeclared (first use in
this function)
/usr/include/linux/jiffies.h:338: error: 'NSEC_PER_SEC' undeclared (first
use in this function)
/usr/include/linux/jiffies.h: In function 'timeval_to_jiffies':
/usr/include/linux/jiffies.h:359: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:359: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:359: error: 'NSEC_PER_SEC' undeclared (first
use in this function)
/usr/include/linux/jiffies.h:360: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:360: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:363: error: syntax error before 'sec'
/usr/include/linux/jiffies.h:363: error: syntax error before 'NSEC_PER_SEC'
/usr/include/linux/jiffies.h:363: error: called object 'u64' is not a
function
/usr/include/linux/jiffies.h:363: error: syntax error before ')' token
/usr/include/linux/jiffies.h: In function 'jiffies_to_timeval':
/usr/include/linux/jiffies.h:375: error: syntax error before 'nsec'
/usr/include/linux/jiffies.h:376: error: 'nsec' undeclared (first use in
this function)
/usr/include/linux/jiffies.h:376: error: 'NSEC_PER_SEC' undeclared (first
use in this function)
/usr/include/linux/jiffies.h:377: error: 'NSEC_PER_USEC' undeclared (first
use in this function)
Does anyone know how can I solve it?
BR,
Mauricio Lin.
18 years, 9 months