Merge branch 'audit.b3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current
* 'audit.b3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current: (22 commits) [PATCH] fix audit_init failure path [PATCH] EXPORT_SYMBOL patch for audit_log, audit_log_start, audit_log_end and audit_format [PATCH] sem2mutex: audit_netlink_sem [PATCH] simplify audit_free() locking [PATCH] Fix audit operators [PATCH] promiscuous mode [PATCH] Add tty to syscall audit records [PATCH] add/remove rule update [PATCH] audit string fields interface + consumer [PATCH] SE Linux audit events [PATCH] Minor cosmetic cleanups to the code moved into auditfilter.c [PATCH] Fix audit record filtering with !CONFIG_AUDITSYSCALL [PATCH] Fix IA64 success/failure indication in syscall auditing. [PATCH] Miscellaneous bug and warning fixes [PATCH] Capture selinux subject/object context information. [PATCH] Exclude messages by message type [PATCH] Collect more inode information during syscall processing. [PATCH] Pass dentry, not just name, in fsnotify creation hooks. [PATCH] Define new range of userspace messages. [PATCH] Filter rule comparators ... Fixed trivial conflict in security/selinux/hooks.c
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@ obj-$(CONFIG_COMPAT) += compat.o
|
||||
obj-$(CONFIG_CPUSETS) += cpuset.o
|
||||
obj-$(CONFIG_IKCONFIG) += configs.o
|
||||
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
|
||||
obj-$(CONFIG_AUDIT) += audit.o
|
||||
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
|
||||
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
obj-$(CONFIG_SYSFS) += ksysfs.o
|
||||
|
||||
+140
-35
@@ -52,6 +52,7 @@
|
||||
#include <linux/audit.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
#include <net/netlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
@@ -72,7 +73,7 @@ static int audit_failure = AUDIT_FAIL_PRINTK;
|
||||
* contains the (non-zero) pid. */
|
||||
int audit_pid;
|
||||
|
||||
/* If audit_limit is non-zero, limit the rate of sending audit records
|
||||
/* If audit_rate_limit is non-zero, limit the rate of sending audit records
|
||||
* to that number per second. This prevents DoS attacks, but results in
|
||||
* audit records being dropped. */
|
||||
static int audit_rate_limit;
|
||||
@@ -102,7 +103,7 @@ static struct sock *audit_sock;
|
||||
* than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
|
||||
* being placed on the freelist). */
|
||||
static DEFINE_SPINLOCK(audit_freelist_lock);
|
||||
static int audit_freelist_count = 0;
|
||||
static int audit_freelist_count;
|
||||
static LIST_HEAD(audit_freelist);
|
||||
|
||||
static struct sk_buff_head audit_skb_queue;
|
||||
@@ -113,7 +114,7 @@ static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
|
||||
/* The netlink socket is only to be read by 1 CPU, which lets us assume
|
||||
* that list additions and deletions never happen simultaneously in
|
||||
* auditsc.c */
|
||||
DECLARE_MUTEX(audit_netlink_sem);
|
||||
DEFINE_MUTEX(audit_netlink_mutex);
|
||||
|
||||
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
|
||||
* audit records. Since printk uses a 1024 byte buffer, this buffer
|
||||
@@ -142,7 +143,7 @@ static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
|
||||
nlh->nlmsg_pid = pid;
|
||||
}
|
||||
|
||||
static void audit_panic(const char *message)
|
||||
void audit_panic(const char *message)
|
||||
{
|
||||
switch (audit_failure)
|
||||
{
|
||||
@@ -186,8 +187,14 @@ static inline int audit_rate_check(void)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Emit at least 1 message per second, even if audit_rate_check is
|
||||
* throttling. */
|
||||
/**
|
||||
* audit_log_lost - conditionally log lost audit message event
|
||||
* @message: the message stating reason for lost audit message
|
||||
*
|
||||
* Emit at least 1 message per second, even if audit_rate_check is
|
||||
* throttling.
|
||||
* Always increment the lost messages counter.
|
||||
*/
|
||||
void audit_log_lost(const char *message)
|
||||
{
|
||||
static unsigned long last_msg = 0;
|
||||
@@ -218,7 +225,6 @@ void audit_log_lost(const char *message)
|
||||
audit_backlog_limit);
|
||||
audit_panic(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int audit_set_rate_limit(int limit, uid_t loginuid)
|
||||
@@ -300,8 +306,22 @@ static int kauditd_thread(void *dummy)
|
||||
remove_wait_queue(&kauditd_wait, &wait);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* audit_send_reply - send an audit reply message via netlink
|
||||
* @pid: process id to send reply to
|
||||
* @seq: sequence number
|
||||
* @type: audit message type
|
||||
* @done: done (last) flag
|
||||
* @multi: multi-part message flag
|
||||
* @payload: payload data
|
||||
* @size: payload size
|
||||
*
|
||||
* Allocates an skb, builds the netlink message, and sends it to the pid.
|
||||
* No failure notifications.
|
||||
*/
|
||||
void audit_send_reply(int pid, int seq, int type, int done, int multi,
|
||||
void *payload, int size)
|
||||
{
|
||||
@@ -342,15 +362,19 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
|
||||
switch (msg_type) {
|
||||
case AUDIT_GET:
|
||||
case AUDIT_LIST:
|
||||
case AUDIT_LIST_RULES:
|
||||
case AUDIT_SET:
|
||||
case AUDIT_ADD:
|
||||
case AUDIT_ADD_RULE:
|
||||
case AUDIT_DEL:
|
||||
case AUDIT_DEL_RULE:
|
||||
case AUDIT_SIGNAL_INFO:
|
||||
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
|
||||
err = -EPERM;
|
||||
break;
|
||||
case AUDIT_USER:
|
||||
case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
|
||||
case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
|
||||
if (!cap_raised(eff_cap, CAP_AUDIT_WRITE))
|
||||
err = -EPERM;
|
||||
break;
|
||||
@@ -376,7 +400,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* As soon as there's any sign of userspace auditd, start kauditd to talk to it */
|
||||
/* As soon as there's any sign of userspace auditd,
|
||||
* start kauditd to talk to it */
|
||||
if (!kauditd_task)
|
||||
kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");
|
||||
if (IS_ERR(kauditd_task)) {
|
||||
@@ -430,6 +455,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
break;
|
||||
case AUDIT_USER:
|
||||
case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
|
||||
case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
|
||||
if (!audit_enabled && msg_type != AUDIT_USER_AVC)
|
||||
return 0;
|
||||
|
||||
@@ -448,12 +474,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
break;
|
||||
case AUDIT_ADD:
|
||||
case AUDIT_DEL:
|
||||
if (nlh->nlmsg_len < sizeof(struct audit_rule))
|
||||
if (nlmsg_len(nlh) < sizeof(struct audit_rule))
|
||||
return -EINVAL;
|
||||
/* fallthrough */
|
||||
case AUDIT_LIST:
|
||||
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
|
||||
uid, seq, data, loginuid);
|
||||
uid, seq, data, nlmsg_len(nlh),
|
||||
loginuid);
|
||||
break;
|
||||
case AUDIT_ADD_RULE:
|
||||
case AUDIT_DEL_RULE:
|
||||
if (nlmsg_len(nlh) < sizeof(struct audit_rule_data))
|
||||
return -EINVAL;
|
||||
/* fallthrough */
|
||||
case AUDIT_LIST_RULES:
|
||||
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
|
||||
uid, seq, data, nlmsg_len(nlh),
|
||||
loginuid);
|
||||
break;
|
||||
case AUDIT_SIGNAL_INFO:
|
||||
sig_data.uid = audit_sig_uid;
|
||||
@@ -469,9 +506,11 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
/* Get message from skb (based on rtnetlink_rcv_skb). Each message is
|
||||
/*
|
||||
* Get message from skb (based on rtnetlink_rcv_skb). Each message is
|
||||
* processed by audit_receive_msg. Malformed skbs with wrong length are
|
||||
* discarded silently. */
|
||||
* discarded silently.
|
||||
*/
|
||||
static void audit_receive_skb(struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
@@ -499,14 +538,14 @@ static void audit_receive(struct sock *sk, int length)
|
||||
struct sk_buff *skb;
|
||||
unsigned int qlen;
|
||||
|
||||
down(&audit_netlink_sem);
|
||||
mutex_lock(&audit_netlink_mutex);
|
||||
|
||||
for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
|
||||
skb = skb_dequeue(&sk->sk_receive_queue);
|
||||
audit_receive_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
up(&audit_netlink_sem);
|
||||
mutex_unlock(&audit_netlink_mutex);
|
||||
}
|
||||
|
||||
|
||||
@@ -519,8 +558,9 @@ static int __init audit_init(void)
|
||||
THIS_MODULE);
|
||||
if (!audit_sock)
|
||||
audit_panic("cannot initialize netlink socket");
|
||||
else
|
||||
audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
|
||||
|
||||
audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
|
||||
skb_queue_head_init(&audit_skb_queue);
|
||||
audit_initialized = 1;
|
||||
audit_enabled = audit_default;
|
||||
@@ -600,7 +640,10 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compute a serial number for the audit record. Audit records are
|
||||
/**
|
||||
* audit_serial - compute a serial number for the audit record
|
||||
*
|
||||
* Compute a serial number for the audit record. Audit records are
|
||||
* written to user-space as soon as they are generated, so a complete
|
||||
* audit record may be written in several pieces. The timestamp of the
|
||||
* record and this serial number are used by the user-space tools to
|
||||
@@ -612,8 +655,8 @@ err:
|
||||
* audit context (for those records that have a context), and emit them
|
||||
* all at syscall exit. However, this could delay the reporting of
|
||||
* significant errors until syscall exit (or never, if the system
|
||||
* halts). */
|
||||
|
||||
* halts).
|
||||
*/
|
||||
unsigned int audit_serial(void)
|
||||
{
|
||||
static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED;
|
||||
@@ -649,6 +692,21 @@ static inline void audit_get_stamp(struct audit_context *ctx,
|
||||
* will be written at syscall exit. If there is no associated task, tsk
|
||||
* should be NULL. */
|
||||
|
||||
/**
|
||||
* audit_log_start - obtain an audit buffer
|
||||
* @ctx: audit_context (may be NULL)
|
||||
* @gfp_mask: type of allocation
|
||||
* @type: audit message type
|
||||
*
|
||||
* Returns audit_buffer pointer on success or NULL on error.
|
||||
*
|
||||
* Obtain an audit buffer. This routine does locking to obtain the
|
||||
* audit buffer, but then no locking is required for calls to
|
||||
* audit_log_*format. If the task (ctx) is a task that is currently in a
|
||||
* syscall, then the syscall is marked as auditable and an audit record
|
||||
* will be written at syscall exit. If there is no associated task, then
|
||||
* task context (ctx) should be NULL.
|
||||
*/
|
||||
struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
|
||||
int type)
|
||||
{
|
||||
@@ -661,6 +719,9 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
|
||||
if (!audit_initialized)
|
||||
return NULL;
|
||||
|
||||
if (unlikely(audit_filter_type(type)))
|
||||
return NULL;
|
||||
|
||||
if (gfp_mask & __GFP_WAIT)
|
||||
reserve = 0;
|
||||
else
|
||||
@@ -713,6 +774,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
|
||||
/**
|
||||
* audit_expand - expand skb in the audit buffer
|
||||
* @ab: audit_buffer
|
||||
* @extra: space to add at tail of the skb
|
||||
*
|
||||
* Returns 0 (no space) on failed expansion, or available space if
|
||||
* successful.
|
||||
@@ -729,10 +791,12 @@ static inline int audit_expand(struct audit_buffer *ab, int extra)
|
||||
return skb_tailroom(skb);
|
||||
}
|
||||
|
||||
/* Format an audit message into the audit buffer. If there isn't enough
|
||||
/*
|
||||
* Format an audit message into the audit buffer. If there isn't enough
|
||||
* room in the audit buffer, more room will be allocated and vsnprint
|
||||
* will be called a second time. Currently, we assume that a printk
|
||||
* can't format message larger than 1024 bytes, so we don't either. */
|
||||
* can't format message larger than 1024 bytes, so we don't either.
|
||||
*/
|
||||
static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
@@ -757,7 +821,8 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
|
||||
/* The printk buffer is 1024 bytes long, so if we get
|
||||
* here and AUDIT_BUFSIZ is at least 1024, then we can
|
||||
* log everything that printk could have logged. */
|
||||
avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
|
||||
avail = audit_expand(ab,
|
||||
max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
|
||||
if (!avail)
|
||||
goto out;
|
||||
len = vsnprintf(skb->tail, avail, fmt, args2);
|
||||
@@ -768,8 +833,14 @@ out:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Format a message into the audit buffer. All the work is done in
|
||||
* audit_log_vformat. */
|
||||
/**
|
||||
* audit_log_format - format a message into the audit buffer.
|
||||
* @ab: audit_buffer
|
||||
* @fmt: format string
|
||||
* @...: optional parameters matching @fmt string
|
||||
*
|
||||
* All the work is done in audit_log_vformat.
|
||||
*/
|
||||
void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
@@ -781,9 +852,18 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/* This function will take the passed buf and convert it into a string of
|
||||
* ascii hex digits. The new string is placed onto the skb. */
|
||||
void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
|
||||
/**
|
||||
* audit_log_hex - convert a buffer to hex and append it to the audit skb
|
||||
* @ab: the audit_buffer
|
||||
* @buf: buffer to convert to hex
|
||||
* @len: length of @buf to be converted
|
||||
*
|
||||
* No return value; failure to expand is silently ignored.
|
||||
*
|
||||
* This function will take the passed buf and convert it into a string of
|
||||
* ascii hex digits. The new string is placed onto the skb.
|
||||
*/
|
||||
void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
|
||||
size_t len)
|
||||
{
|
||||
int i, avail, new_len;
|
||||
@@ -812,10 +892,16 @@ void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
|
||||
skb_put(skb, len << 1); /* new string is twice the old string */
|
||||
}
|
||||
|
||||
/* This code will escape a string that is passed to it if the string
|
||||
* contains a control character, unprintable character, double quote mark,
|
||||
/**
|
||||
* audit_log_unstrustedstring - log a string that may contain random characters
|
||||
* @ab: audit_buffer
|
||||
* @string: string to be logged
|
||||
*
|
||||
* This code will escape a string that is passed to it if the string
|
||||
* contains a control character, unprintable character, double quote mark,
|
||||
* or a space. Unescaped strings will start and end with a double quote mark.
|
||||
* Strings that are escaped are printed in hex (2 digits per char). */
|
||||
* Strings that are escaped are printed in hex (2 digits per char).
|
||||
*/
|
||||
void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
|
||||
{
|
||||
const unsigned char *p = string;
|
||||
@@ -854,10 +940,15 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
|
||||
kfree(path);
|
||||
}
|
||||
|
||||
/* The netlink_* functions cannot be called inside an irq context, so
|
||||
* the audit buffer is places on a queue and a tasklet is scheduled to
|
||||
/**
|
||||
* audit_log_end - end one audit record
|
||||
* @ab: the audit_buffer
|
||||
*
|
||||
* The netlink_* functions cannot be called inside an irq context, so
|
||||
* the audit buffer is placed on a queue and a tasklet is scheduled to
|
||||
* remove them from the queue outside the irq context. May be called in
|
||||
* any context. */
|
||||
* any context.
|
||||
*/
|
||||
void audit_log_end(struct audit_buffer *ab)
|
||||
{
|
||||
if (!ab)
|
||||
@@ -878,9 +969,18 @@ void audit_log_end(struct audit_buffer *ab)
|
||||
audit_buffer_free(ab);
|
||||
}
|
||||
|
||||
/* Log an audit record. This is a convenience function that calls
|
||||
* audit_log_start, audit_log_vformat, and audit_log_end. It may be
|
||||
* called in any context. */
|
||||
/**
|
||||
* audit_log - Log an audit record
|
||||
* @ctx: audit context
|
||||
* @gfp_mask: type of allocation
|
||||
* @type: audit message type
|
||||
* @fmt: format string to use
|
||||
* @...: variable parameters matching the format string
|
||||
*
|
||||
* This is a convenience function that calls audit_log_start,
|
||||
* audit_log_vformat, and audit_log_end. It may be called
|
||||
* in any context.
|
||||
*/
|
||||
void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
@@ -895,3 +995,8 @@ void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
|
||||
audit_log_end(ab);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(audit_log_start);
|
||||
EXPORT_SYMBOL(audit_log_end);
|
||||
EXPORT_SYMBOL(audit_log_format);
|
||||
EXPORT_SYMBOL(audit_log);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/* audit -- definition of audit_context structure and supporting types
|
||||
*
|
||||
* Copyright 2003-2004 Red Hat, Inc.
|
||||
* Copyright 2005 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2005 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/audit.h>
|
||||
|
||||
/* 0 = no checking
|
||||
1 = put_count checking
|
||||
2 = verbose put_count checking
|
||||
*/
|
||||
#define AUDIT_DEBUG 0
|
||||
|
||||
/* At task start time, the audit_state is set in the audit_context using
|
||||
a per-task filter. At syscall entry, the audit_state is augmented by
|
||||
the syscall filter. */
|
||||
enum audit_state {
|
||||
AUDIT_DISABLED, /* Do not create per-task audit_context.
|
||||
* No syscall-specific audit records can
|
||||
* be generated. */
|
||||
AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context,
|
||||
* but don't necessarily fill it in at
|
||||
* syscall entry time (i.e., filter
|
||||
* instead). */
|
||||
AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context,
|
||||
* and always fill it in at syscall
|
||||
* entry time. This makes a full
|
||||
* syscall record available if some
|
||||
* other part of the kernel decides it
|
||||
* should be recorded. */
|
||||
AUDIT_RECORD_CONTEXT /* Create the per-task audit_context,
|
||||
* always fill it in at syscall entry
|
||||
* time, and always write out the audit
|
||||
* record at syscall exit time. */
|
||||
};
|
||||
|
||||
/* Rule lists */
|
||||
struct audit_field {
|
||||
u32 type;
|
||||
u32 val;
|
||||
u32 op;
|
||||
};
|
||||
|
||||
struct audit_krule {
|
||||
int vers_ops;
|
||||
u32 flags;
|
||||
u32 listnr;
|
||||
u32 action;
|
||||
u32 mask[AUDIT_BITMASK_SIZE];
|
||||
u32 buflen; /* for data alloc on list rules */
|
||||
u32 field_count;
|
||||
struct audit_field *fields;
|
||||
};
|
||||
|
||||
struct audit_entry {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
struct audit_krule rule;
|
||||
};
|
||||
|
||||
|
||||
extern int audit_pid;
|
||||
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
|
||||
|
||||
extern void audit_send_reply(int pid, int seq, int type,
|
||||
int done, int multi,
|
||||
void *payload, int size);
|
||||
extern void audit_log_lost(const char *message);
|
||||
extern void audit_panic(const char *message);
|
||||
extern struct mutex audit_netlink_mutex;
|
||||
@@ -0,0 +1,630 @@
|
||||
/* auditfilter.c -- filtering of audit events
|
||||
*
|
||||
* Copyright 2003-2004 Red Hat, Inc.
|
||||
* Copyright 2005 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2005 IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/netlink.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
|
||||
};
|
||||
|
||||
static inline void audit_free_rule(struct audit_entry *e)
|
||||
{
|
||||
kfree(e->rule.fields);
|
||||
kfree(e);
|
||||
}
|
||||
|
||||
static inline void audit_free_rule_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct audit_entry *e = container_of(head, struct audit_entry, rcu);
|
||||
audit_free_rule(e);
|
||||
}
|
||||
|
||||
/* Unpack a filter field's string representation from user-space
|
||||
* buffer. */
|
||||
static __attribute__((unused)) 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)
|
||||
{
|
||||
unsigned listnr;
|
||||
struct audit_entry *entry;
|
||||
struct audit_field *fields;
|
||||
int i, err;
|
||||
|
||||
err = -EINVAL;
|
||||
listnr = rule->flags & ~AUDIT_FILTER_PREPEND;
|
||||
switch(listnr) {
|
||||
default:
|
||||
goto exit_err;
|
||||
case AUDIT_FILTER_USER:
|
||||
case AUDIT_FILTER_TYPE:
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
case AUDIT_FILTER_ENTRY:
|
||||
case AUDIT_FILTER_EXIT:
|
||||
case AUDIT_FILTER_TASK:
|
||||
#endif
|
||||
;
|
||||
}
|
||||
if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE &&
|
||||
rule->action != AUDIT_ALWAYS)
|
||||
goto exit_err;
|
||||
if (rule->field_count > AUDIT_MAX_FIELDS)
|
||||
goto exit_err;
|
||||
|
||||
err = -ENOMEM;
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (unlikely(!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];
|
||||
|
||||
return entry;
|
||||
|
||||
exit_err:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* Translate struct audit_rule to kernel's rule respresentation.
|
||||
* Exists for backward compatibility with userspace. */
|
||||
static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
{
|
||||
struct audit_entry *entry;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
entry = audit_to_entry_common(rule);
|
||||
if (IS_ERR(entry))
|
||||
goto exit_nofree;
|
||||
|
||||
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];
|
||||
|
||||
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
|
||||
|
||||
/* Support for legacy operators where
|
||||
* AUDIT_NEGATE bit signifies != and otherwise assumes == */
|
||||
if (f->op & AUDIT_NEGATE)
|
||||
f->op = AUDIT_NOT_EQUAL;
|
||||
else if (!f->op)
|
||||
f->op = AUDIT_EQUAL;
|
||||
else if (f->op == AUDIT_OPERATORS) {
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
exit_nofree:
|
||||
return entry;
|
||||
|
||||
exit_free:
|
||||
audit_free_rule(entry);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* Translate struct audit_rule_data to kernel's rule respresentation. */
|
||||
static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||
size_t datasz)
|
||||
{
|
||||
int err = 0;
|
||||
struct audit_entry *entry;
|
||||
void *bufp;
|
||||
/* size_t remain = datasz - sizeof(struct audit_rule_data); */
|
||||
int i;
|
||||
|
||||
entry = audit_to_entry_common((struct audit_rule *)data);
|
||||
if (IS_ERR(entry))
|
||||
goto exit_nofree;
|
||||
|
||||
bufp = data->buf;
|
||||
entry->rule.vers_ops = 2;
|
||||
for (i = 0; i < data->field_count; i++) {
|
||||
struct audit_field *f = &entry->rule.fields[i];
|
||||
|
||||
err = -EINVAL;
|
||||
if (!(data->fieldflags[i] & AUDIT_OPERATORS) ||
|
||||
data->fieldflags[i] & ~AUDIT_OPERATORS)
|
||||
goto exit_free;
|
||||
|
||||
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
|
||||
f->type = data->fields[i];
|
||||
switch(f->type) {
|
||||
/* call type-specific conversion routines here */
|
||||
default:
|
||||
f->val = data->values[i];
|
||||
}
|
||||
}
|
||||
|
||||
exit_nofree:
|
||||
return entry;
|
||||
|
||||
exit_free:
|
||||
audit_free_rule(entry);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* Pack a filter field's string representation into data block. */
|
||||
static inline size_t audit_pack_string(void **bufp, char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
|
||||
memcpy(*bufp, str, len);
|
||||
*bufp += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Translate kernel rule respresentation to struct audit_rule.
|
||||
* Exists for backward compatibility with userspace. */
|
||||
static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
|
||||
{
|
||||
struct audit_rule *rule;
|
||||
int i;
|
||||
|
||||
rule = kmalloc(sizeof(*rule), GFP_KERNEL);
|
||||
if (unlikely(!rule))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memset(rule, 0, sizeof(*rule));
|
||||
|
||||
rule->flags = krule->flags | krule->listnr;
|
||||
rule->action = krule->action;
|
||||
rule->field_count = krule->field_count;
|
||||
for (i = 0; i < rule->field_count; i++) {
|
||||
rule->values[i] = krule->fields[i].val;
|
||||
rule->fields[i] = krule->fields[i].type;
|
||||
|
||||
if (krule->vers_ops == 1) {
|
||||
if (krule->fields[i].op & AUDIT_NOT_EQUAL)
|
||||
rule->fields[i] |= AUDIT_NEGATE;
|
||||
} else {
|
||||
rule->fields[i] |= krule->fields[i].op;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
/* Translate kernel rule respresentation to struct audit_rule_data. */
|
||||
static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
|
||||
{
|
||||
struct audit_rule_data *data;
|
||||
void *bufp;
|
||||
int i;
|
||||
|
||||
data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
|
||||
if (unlikely(!data))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memset(data, 0, sizeof(*data));
|
||||
|
||||
data->flags = krule->flags | krule->listnr;
|
||||
data->action = krule->action;
|
||||
data->field_count = krule->field_count;
|
||||
bufp = data->buf;
|
||||
for (i = 0; i < data->field_count; i++) {
|
||||
struct audit_field *f = &krule->fields[i];
|
||||
|
||||
data->fields[i] = f->type;
|
||||
data->fieldflags[i] = f->op;
|
||||
switch(f->type) {
|
||||
/* call type-specific conversion routines here */
|
||||
default:
|
||||
data->values[i] = f->val;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (a->flags != b->flags ||
|
||||
a->listnr != b->listnr ||
|
||||
a->action != b->action ||
|
||||
a->field_count != b->field_count)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < a->field_count; i++) {
|
||||
if (a->fields[i].type != b->fields[i].type ||
|
||||
a->fields[i].op != b->fields[i].op)
|
||||
return 1;
|
||||
|
||||
switch(a->fields[i].type) {
|
||||
/* call type-specific comparison routines here */
|
||||
default:
|
||||
if (a->fields[i].val != b->fields[i].val)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
|
||||
if (a->mask[i] != b->mask[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add rule to given filterlist if not a duplicate. Protected by
|
||||
* audit_netlink_mutex. */
|
||||
static inline int audit_add_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
|
||||
/* 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.flags & AUDIT_FILTER_PREPEND) {
|
||||
list_add_rcu(&entry->list, list);
|
||||
} else {
|
||||
list_add_tail_rcu(&entry->list, list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove an existing rule from filterlist. Protected by
|
||||
* audit_netlink_mutex. */
|
||||
static inline int audit_del_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
return -ENOENT; /* No matching rule */
|
||||
}
|
||||
|
||||
/* List rules using struct audit_rule. Exists for backward
|
||||
* compatibility with userspace. */
|
||||
static int audit_list(void *_dest)
|
||||
{
|
||||
int pid, seq;
|
||||
int *dest = _dest;
|
||||
struct audit_entry *entry;
|
||||
int i;
|
||||
|
||||
pid = dest[0];
|
||||
seq = dest[1];
|
||||
kfree(dest);
|
||||
|
||||
mutex_lock(&audit_netlink_mutex);
|
||||
|
||||
/* The *_rcu iterators not needed here because we are
|
||||
always called with audit_netlink_mutex held. */
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(entry, &audit_filter_list[i], list) {
|
||||
struct audit_rule *rule;
|
||||
|
||||
rule = audit_krule_to_rule(&entry->rule);
|
||||
if (unlikely(!rule))
|
||||
break;
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
rule, sizeof(*rule));
|
||||
kfree(rule);
|
||||
}
|
||||
}
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
|
||||
|
||||
mutex_unlock(&audit_netlink_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* List rules using struct audit_rule_data. */
|
||||
static int audit_list_rules(void *_dest)
|
||||
{
|
||||
int pid, seq;
|
||||
int *dest = _dest;
|
||||
struct audit_entry *e;
|
||||
int i;
|
||||
|
||||
pid = dest[0];
|
||||
seq = dest[1];
|
||||
kfree(dest);
|
||||
|
||||
mutex_lock(&audit_netlink_mutex);
|
||||
|
||||
/* The *_rcu iterators not needed here because we are
|
||||
always called with audit_netlink_mutex held. */
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(e, &audit_filter_list[i], 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));
|
||||
kfree(data);
|
||||
}
|
||||
}
|
||||
audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
|
||||
|
||||
mutex_unlock(&audit_netlink_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* audit_receive_filter - apply all rules to the specified message type
|
||||
* @type: audit message type
|
||||
* @pid: target pid for netlink audit messages
|
||||
* @uid: target uid for netlink audit messages
|
||||
* @seq: netlink audit message sequence (serial) number
|
||||
* @data: payload data
|
||||
* @datasz: size of payload data
|
||||
* @loginuid: loginuid of sender
|
||||
*/
|
||||
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||
size_t datasz, uid_t loginuid)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
int *dest;
|
||||
int err = 0;
|
||||
struct audit_entry *entry;
|
||||
|
||||
switch (type) {
|
||||
case AUDIT_LIST:
|
||||
case AUDIT_LIST_RULES:
|
||||
/* We can't just spew out the rules here because we might fill
|
||||
* the available socket buffer space and deadlock waiting for
|
||||
* auditctl to read from it... which isn't ever going to
|
||||
* happen if we're actually running in the context of auditctl
|
||||
* trying to _send_ the stuff */
|
||||
|
||||
dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
|
||||
if (!dest)
|
||||
return -ENOMEM;
|
||||
dest[0] = pid;
|
||||
dest[1] = seq;
|
||||
|
||||
if (type == AUDIT_LIST)
|
||||
tsk = kthread_run(audit_list, dest, "audit_list");
|
||||
else
|
||||
tsk = kthread_run(audit_list_rules, dest,
|
||||
"audit_list_rules");
|
||||
if (IS_ERR(tsk)) {
|
||||
kfree(dest);
|
||||
err = PTR_ERR(tsk);
|
||||
}
|
||||
break;
|
||||
case AUDIT_ADD:
|
||||
case AUDIT_ADD_RULE:
|
||||
if (type == AUDIT_ADD)
|
||||
entry = audit_rule_to_entry(data);
|
||||
else
|
||||
entry = audit_data_to_entry(data, datasz);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
err = audit_add_rule(entry,
|
||||
&audit_filter_list[entry->rule.listnr]);
|
||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||
"auid=%u add rule to list=%d res=%d\n",
|
||||
loginuid, entry->rule.listnr, !err);
|
||||
|
||||
if (err)
|
||||
audit_free_rule(entry);
|
||||
break;
|
||||
case AUDIT_DEL:
|
||||
case AUDIT_DEL_RULE:
|
||||
if (type == AUDIT_DEL)
|
||||
entry = audit_rule_to_entry(data);
|
||||
else
|
||||
entry = audit_data_to_entry(data, datasz);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
err = audit_del_rule(entry,
|
||||
&audit_filter_list[entry->rule.listnr]);
|
||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||
"auid=%u remove rule from list=%d res=%d\n",
|
||||
loginuid, entry->rule.listnr, !err);
|
||||
|
||||
audit_free_rule(entry);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int audit_comparator(const u32 left, const u32 op, const u32 right)
|
||||
{
|
||||
switch (op) {
|
||||
case AUDIT_EQUAL:
|
||||
return (left == right);
|
||||
case AUDIT_NOT_EQUAL:
|
||||
return (left != right);
|
||||
case AUDIT_LESS_THAN:
|
||||
return (left < right);
|
||||
case AUDIT_LESS_THAN_OR_EQUAL:
|
||||
return (left <= right);
|
||||
case AUDIT_GREATER_THAN:
|
||||
return (left > right);
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < rule->field_count; i++) {
|
||||
struct audit_field *f = &rule->fields[i];
|
||||
int result = 0;
|
||||
|
||||
switch (f->type) {
|
||||
case AUDIT_PID:
|
||||
result = audit_comparator(cb->creds.pid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_UID:
|
||||
result = audit_comparator(cb->creds.uid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_GID:
|
||||
result = audit_comparator(cb->creds.gid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_LOGINUID:
|
||||
result = audit_comparator(cb->loginuid, f->op, f->val);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
return 0;
|
||||
}
|
||||
switch (rule->action) {
|
||||
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
|
||||
case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break;
|
||||
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int audit_filter_user(struct netlink_skb_parms *cb, int type)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
enum audit_state state;
|
||||
int ret = 1;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
|
||||
if (audit_filter_user_rules(cb, &e->rule, &state)) {
|
||||
if (state == AUDIT_DISABLED)
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret; /* Audit by default */
|
||||
}
|
||||
|
||||
int audit_filter_type(int type)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
int result = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
|
||||
goto unlock_and_return;
|
||||
|
||||
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
|
||||
list) {
|
||||
int i;
|
||||
for (i = 0; i < e->rule.field_count; i++) {
|
||||
struct audit_field *f = &e->rule.fields[i];
|
||||
if (f->type == AUDIT_MSGTYPE) {
|
||||
result = audit_comparator(type, f->op, f->val);
|
||||
if (!result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
goto unlock_and_return;
|
||||
}
|
||||
unlock_and_return:
|
||||
rcu_read_unlock();
|
||||
return result;
|
||||
}
|
||||
+433
-399
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user