Hi,
This patch makes audit look respectable :)
-tim
diff --git a/kernel/Makefile b/kernel/Makefile
index 58908f9..88ca434 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,6 +10,7 @@ obj-y = sched.o fork.o exec_domain.o
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o
+obj-$(CONFIG_AUDIT) += audit/
obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
obj-$(CONFIG_FUTEX) += futex.o
ifeq ($(CONFIG_COMPAT),y)
@@ -29,8 +30,6 @@ 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 auditfilter.o
-obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_SYSFS) += ksysfs.o
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
diff --git a/kernel/audit.c b/kernel/audit.c
deleted file mode 100644
index 7637410..0000000
--- a/kernel/audit.c
+++ /dev/null
@@ -1,1133 +0,0 @@
-/* audit.c -- Auditing support
- * Gateway between the kernel (e.g., selinux) and the user-space audit daemon.
- * System-call specific features have moved to auditsc.c
- *
- * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
- * All Rights Reserved.
- *
- * 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
- *
- * Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
- *
- * Goals: 1) Integrate fully with SELinux.
- * 2) Minimal run-time overhead:
- * a) Minimal when syscall auditing is disabled (audit_enable=0).
- * b) Small when syscall auditing is enabled and no audit record
- * is generated (defer as much work as possible to record
- * generation time):
- * i) context is allocated,
- * ii) names from getname are stored without a copy, and
- * iii) inode information stored from path_lookup.
- * 3) Ability to disable syscall auditing at boot time (audit=0).
- * 4) Usable by other parts of the kernel (if audit_log* is called,
- * then a syscall record will be generated automatically for the
- * current syscall).
- * 5) Netlink interface to user-space.
- * 6) Support low-overhead kernel-based filtering to minimize the
- * information that must be passed to user-space.
- *
- * Example user-space utilities:
http://people.redhat.com/sgrubb/audit/
- */
-
-#include <linux/init.h>
-#include <asm/types.h>
-#include <asm/atomic.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/kthread.h>
-
-#include <linux/audit.h>
-
-#include <net/sock.h>
-#include <net/netlink.h>
-#include <linux/skbuff.h>
-#include <linux/netlink.h>
-#include <linux/selinux.h>
-#include <linux/inotify.h>
-
-#include "audit.h"
-
-/* No auditing will take place until audit_initialized != 0.
- * (Initialization happens after skb_init is called.) */
-static int audit_initialized;
-
-/* No syscall auditing will take place unless audit_enabled != 0. */
-int audit_enabled;
-
-/* Default state when kernel boots without any parameters. */
-static int audit_default;
-
-/* If auditing cannot proceed, audit_failure selects what happens. */
-static int audit_failure = AUDIT_FAIL_PRINTK;
-
-/* If audit records are to be written to the netlink socket, audit_pid
- * contains the (non-zero) pid. */
-int audit_pid;
-
-/* 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;
-
-/* Number of outstanding audit_buffers allowed. */
-static int audit_backlog_limit = 64;
-static int audit_backlog_wait_time = 60 * HZ;
-static int audit_backlog_wait_overflow = 0;
-
-/* The identity of the user shutting down the audit system. */
-uid_t audit_sig_uid = -1;
-pid_t audit_sig_pid = -1;
-
-/* Records can be lost in several ways:
- 0) [suppressed in audit_alloc]
- 1) out of memory in audit_log_start [kmalloc of struct audit_buffer]
- 2) out of memory in audit_log_move [alloc_skb]
- 3) suppressed due to audit_rate_limit
- 4) suppressed due to audit_backlog_limit
-*/
-static atomic_t audit_lost = ATOMIC_INIT(0);
-
-/* The netlink socket. */
-static struct sock *audit_sock;
-
-/* Inotify handle. */
-struct inotify_handle *audit_ih;
-
-/* 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). */
-static DEFINE_SPINLOCK(audit_freelist_lock);
-static int audit_freelist_count;
-static LIST_HEAD(audit_freelist);
-
-static struct sk_buff_head audit_skb_queue;
-static struct task_struct *kauditd_task;
-static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
-static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
-
-/* Serialize requests from userspace. */
-static DEFINE_MUTEX(audit_cmd_mutex);
-
-/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
- * audit records. Since printk uses a 1024 byte buffer, this buffer
- * should be at least that large. */
-#define AUDIT_BUFSIZ 1024
-
-/* AUDIT_MAXFREE is the number of empty audit_buffers we keep on the
- * audit_freelist. Doing so eliminates many kmalloc/kfree calls. */
-#define AUDIT_MAXFREE (2*NR_CPUS)
-
-/* The audit_buffer is used when formatting an audit record. The caller
- * locks briefly to get the record off the freelist or to allocate the
- * buffer, and locks briefly to send the buffer to the netlink layer or
- * to place it on a transmit queue. Multiple audit_buffers can be in
- * use simultaneously. */
-struct audit_buffer {
- struct list_head list;
- struct sk_buff *skb; /* formatted skb ready to send */
- struct audit_context *ctx; /* NULL or associated context */
- gfp_t gfp_mask;
-};
-
-static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
-{
- struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
- nlh->nlmsg_pid = pid;
-}
-
-void audit_panic(const char *message)
-{
- switch (audit_failure)
- {
- case AUDIT_FAIL_SILENT:
- break;
- case AUDIT_FAIL_PRINTK:
- printk(KERN_ERR "audit: %s\n", message);
- break;
- case AUDIT_FAIL_PANIC:
- panic("audit: %s\n", message);
- break;
- }
-}
-
-static inline int audit_rate_check(void)
-{
- static unsigned long last_check = 0;
- static int messages = 0;
- static DEFINE_SPINLOCK(lock);
- unsigned long flags;
- unsigned long now;
- unsigned long elapsed;
- int retval = 0;
-
- if (!audit_rate_limit) return 1;
-
- spin_lock_irqsave(&lock, flags);
- if (++messages < audit_rate_limit) {
- retval = 1;
- } else {
- now = jiffies;
- elapsed = now - last_check;
- if (elapsed > HZ) {
- last_check = now;
- messages = 0;
- retval = 1;
- }
- }
- spin_unlock_irqrestore(&lock, flags);
-
- return retval;
-}
-
-/**
- * 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;
- static DEFINE_SPINLOCK(lock);
- unsigned long flags;
- unsigned long now;
- int print;
-
- atomic_inc(&audit_lost);
-
- print = (audit_failure == AUDIT_FAIL_PANIC || !audit_rate_limit);
-
- if (!print) {
- spin_lock_irqsave(&lock, flags);
- now = jiffies;
- if (now - last_msg > HZ) {
- print = 1;
- last_msg = now;
- }
- spin_unlock_irqrestore(&lock, flags);
- }
-
- if (print) {
- printk(KERN_WARNING
- "audit: audit_lost=%d audit_rate_limit=%d audit_backlog_limit=%d\n",
- atomic_read(&audit_lost),
- audit_rate_limit,
- audit_backlog_limit);
- audit_panic(message);
- }
-}
-
-static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
-{
- int old = audit_rate_limit;
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- int rc;
- if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
- return rc;
- else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_rate_limit=%d old=%d by auid=%u subj=%s",
- limit, old, loginuid, ctx);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_rate_limit=%d old=%d by auid=%u",
- limit, old, loginuid);
- audit_rate_limit = limit;
- return old;
-}
-
-static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
-{
- int old = audit_backlog_limit;
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- int rc;
- if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
- return rc;
- else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_backlog_limit=%d old=%d by auid=%u subj=%s",
- limit, old, loginuid, ctx);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_backlog_limit=%d old=%d by auid=%u",
- limit, old, loginuid);
- audit_backlog_limit = limit;
- return old;
-}
-
-static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
-{
- int old = audit_enabled;
-
- if (state != 0 && state != 1)
- return -EINVAL;
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- int rc;
- if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
- return rc;
- else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_enabled=%d old=%d by auid=%u subj=%s",
- state, old, loginuid, ctx);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_enabled=%d old=%d by auid=%u",
- state, old, loginuid);
- audit_enabled = state;
- return old;
-}
-
-static int audit_set_failure(int state, uid_t loginuid, u32 sid)
-{
- int old = audit_failure;
-
- if (state != AUDIT_FAIL_SILENT
- && state != AUDIT_FAIL_PRINTK
- && state != AUDIT_FAIL_PANIC)
- return -EINVAL;
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- int rc;
- if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
- return rc;
- else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_failure=%d old=%d by auid=%u subj=%s",
- state, old, loginuid, ctx);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_failure=%d old=%d by auid=%u",
- state, old, loginuid);
- audit_failure = state;
- return old;
-}
-
-static int kauditd_thread(void *dummy)
-{
- struct sk_buff *skb;
-
- while (1) {
- skb = skb_dequeue(&audit_skb_queue);
- wake_up(&audit_backlog_wait);
- if (skb) {
- if (audit_pid) {
- int err = netlink_unicast(audit_sock, skb, audit_pid, 0);
- if (err < 0) {
- BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */
- printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
- audit_pid = 0;
- }
- } else {
- printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0));
- kfree_skb(skb);
- }
- } else {
- DECLARE_WAITQUEUE(wait, current);
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&kauditd_wait, &wait);
-
- if (!skb_queue_len(&audit_skb_queue)) {
- try_to_freeze();
- schedule();
- }
-
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&kauditd_wait, &wait);
- }
- }
- return 0;
-}
-
-int audit_send_list(void *_dest)
-{
- struct audit_netlink_list *dest = _dest;
- int pid = dest->pid;
- struct sk_buff *skb;
-
- while ((skb = __skb_dequeue(&dest->q)) != NULL)
- netlink_unicast(audit_sock, skb, pid, 0);
-
- kfree(dest);
-
- return 0;
-}
-
-struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
- int multi, void *payload, int size)
-{
- struct sk_buff *skb;
- struct nlmsghdr *nlh;
- int len = NLMSG_SPACE(size);
- void *data;
- int flags = multi ? NLM_F_MULTI : 0;
- int t = done ? NLMSG_DONE : type;
-
- skb = alloc_skb(len, GFP_KERNEL);
- if (!skb)
- return NULL;
-
- nlh = NLMSG_PUT(skb, pid, seq, t, size);
- nlh->nlmsg_flags = flags;
- data = NLMSG_DATA(nlh);
- memcpy(data, payload, size);
- return skb;
-
-nlmsg_failure: /* Used by NLMSG_PUT */
- if (skb)
- kfree_skb(skb);
- return NULL;
-}
-
-/**
- * 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)
-{
- struct sk_buff *skb;
- skb = audit_make_reply(pid, seq, type, done, multi, payload, size);
- if (!skb)
- return;
- /* Ignore failure. It'll only happen if the sender goes away,
- because our timeout is set to infinite. */
- netlink_unicast(audit_sock, skb, pid, 0);
- return;
-}
-
-/*
- * Check for appropriate CAP_AUDIT_ capabilities on incoming audit
- * control messages.
- */
-static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
-{
- int err = 0;
-
- 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;
- default: /* bad msg */
- err = -EINVAL;
- }
-
- return err;
-}
-
-static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
-{
- u32 uid, pid, seq, sid;
- void *data;
- struct audit_status *status_get, status_set;
- int err;
- struct audit_buffer *ab;
- u16 msg_type = nlh->nlmsg_type;
- uid_t loginuid; /* loginuid of sender */
- struct audit_sig_info sig_data;
-
- err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
- if (err)
- return err;
-
- /* 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)) {
- err = PTR_ERR(kauditd_task);
- kauditd_task = NULL;
- return err;
- }
-
- pid = NETLINK_CREDS(skb)->pid;
- uid = NETLINK_CREDS(skb)->uid;
- loginuid = NETLINK_CB(skb).loginuid;
- sid = NETLINK_CB(skb).sid;
- seq = nlh->nlmsg_seq;
- data = NLMSG_DATA(nlh);
-
- switch (msg_type) {
- case AUDIT_GET:
- status_set.enabled = audit_enabled;
- status_set.failure = audit_failure;
- status_set.pid = audit_pid;
- status_set.rate_limit = audit_rate_limit;
- status_set.backlog_limit = audit_backlog_limit;
- status_set.lost = atomic_read(&audit_lost);
- status_set.backlog = skb_queue_len(&audit_skb_queue);
- audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0,
- &status_set, sizeof(status_set));
- break;
- case AUDIT_SET:
- if (nlh->nlmsg_len < sizeof(struct audit_status))
- return -EINVAL;
- status_get = (struct audit_status *)data;
- if (status_get->mask & AUDIT_STATUS_ENABLED) {
- err = audit_set_enabled(status_get->enabled,
- loginuid, sid);
- if (err < 0) return err;
- }
- if (status_get->mask & AUDIT_STATUS_FAILURE) {
- err = audit_set_failure(status_get->failure,
- loginuid, sid);
- if (err < 0) return err;
- }
- if (status_get->mask & AUDIT_STATUS_PID) {
- int old = audit_pid;
- if (sid) {
- char *ctx = NULL;
- u32 len;
- int rc;
- if ((rc = selinux_ctxid_to_string(
- sid, &ctx, &len)))
- return rc;
- else
- audit_log(NULL, GFP_KERNEL,
- AUDIT_CONFIG_CHANGE,
- "audit_pid=%d old=%d by auid=%u subj=%s",
- status_get->pid, old,
- loginuid, ctx);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_pid=%d old=%d by auid=%u",
- status_get->pid, old, loginuid);
- audit_pid = status_get->pid;
- }
- if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
- audit_set_rate_limit(status_get->rate_limit,
- loginuid, sid);
- if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
- audit_set_backlog_limit(status_get->backlog_limit,
- loginuid, sid);
- 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;
-
- err = audit_filter_user(&NETLINK_CB(skb), msg_type);
- if (err == 1) {
- err = 0;
- ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
- if (ab) {
- audit_log_format(ab,
- "user pid=%d uid=%u auid=%u",
- pid, uid, loginuid);
- if (sid) {
- char *ctx = NULL;
- u32 len;
- if (selinux_ctxid_to_string(
- sid, &ctx, &len)) {
- audit_log_format(ab,
- " ssid=%u", sid);
- /* Maybe call audit_panic? */
- } else
- audit_log_format(ab,
- " subj=%s", ctx);
- kfree(ctx);
- }
- audit_log_format(ab, " msg='%.1024s'",
- (char *)data);
- audit_set_pid(ab, pid);
- audit_log_end(ab);
- }
- }
- break;
- case AUDIT_ADD:
- case AUDIT_DEL:
- 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, nlmsg_len(nlh),
- loginuid, sid);
- 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, sid);
- break;
- case AUDIT_SIGNAL_INFO:
- sig_data.uid = audit_sig_uid;
- sig_data.pid = audit_sig_pid;
- audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO,
- 0, 0, &sig_data, sizeof(sig_data));
- break;
- default:
- err = -EINVAL;
- break;
- }
-
- return err < 0 ? err : 0;
-}
-
-/*
- * 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.
- */
-static void audit_receive_skb(struct sk_buff *skb)
-{
- int err;
- struct nlmsghdr *nlh;
- u32 rlen;
-
- while (skb->len >= NLMSG_SPACE(0)) {
- nlh = (struct nlmsghdr *)skb->data;
- if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
- return;
- rlen = NLMSG_ALIGN(nlh->nlmsg_len);
- if (rlen > skb->len)
- rlen = skb->len;
- if ((err = audit_receive_msg(skb, nlh))) {
- netlink_ack(skb, nlh, err);
- } else if (nlh->nlmsg_flags & NLM_F_ACK)
- netlink_ack(skb, nlh, 0);
- skb_pull(skb, rlen);
- }
-}
-
-/* Receive messages from netlink socket. */
-static void audit_receive(struct sock *sk, int length)
-{
- struct sk_buff *skb;
- unsigned int qlen;
-
- mutex_lock(&audit_cmd_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);
- }
- mutex_unlock(&audit_cmd_mutex);
-}
-
-
-/* Initialize audit support at boot time. */
-static int __init audit_init(void)
-{
- printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
- audit_default ? "enabled" : "disabled");
- audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
- THIS_MODULE);
- if (!audit_sock)
- audit_panic("cannot initialize netlink socket");
- else
- audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
-
- skb_queue_head_init(&audit_skb_queue);
- audit_initialized = 1;
- audit_enabled = audit_default;
-
- /* Register the callback with selinux. This callback will be invoked
- * when a new policy is loaded. */
- selinux_audit_set_callback(&selinux_audit_rule_update);
-
- audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
-
-#ifdef CONFIG_AUDITSYSCALL
- audit_ih = inotify_init(audit_handle_ievent);
- if (IS_ERR(audit_ih))
- audit_panic("cannot initialize inotify handle");
-#endif
-
- return 0;
-}
-__initcall(audit_init);
-
-/* Process kernel command-line parameter at boot time. audit=0 or audit=1. */
-static int __init audit_enable(char *str)
-{
- audit_default = !!simple_strtol(str, NULL, 0);
- printk(KERN_INFO "audit: %s%s\n",
- audit_default ? "enabled" : "disabled",
- audit_initialized ? "" : " (after initialization)");
- if (audit_initialized)
- audit_enabled = audit_default;
- return 1;
-}
-
-__setup("audit=", audit_enable);
-
-static void audit_buffer_free(struct audit_buffer *ab)
-{
- unsigned long flags;
-
- if (!ab)
- return;
-
- if (ab->skb)
- kfree_skb(ab->skb);
-
- spin_lock_irqsave(&audit_freelist_lock, flags);
- if (++audit_freelist_count > AUDIT_MAXFREE)
- kfree(ab);
- else
- list_add(&ab->list, &audit_freelist);
- spin_unlock_irqrestore(&audit_freelist_lock, flags);
-}
-
-static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
- gfp_t gfp_mask, int type)
-{
- unsigned long flags;
- struct audit_buffer *ab = NULL;
- struct nlmsghdr *nlh;
-
- spin_lock_irqsave(&audit_freelist_lock, flags);
- if (!list_empty(&audit_freelist)) {
- ab = list_entry(audit_freelist.next,
- struct audit_buffer, list);
- list_del(&ab->list);
- --audit_freelist_count;
- }
- spin_unlock_irqrestore(&audit_freelist_lock, flags);
-
- if (!ab) {
- ab = kmalloc(sizeof(*ab), gfp_mask);
- if (!ab)
- goto err;
- }
-
- ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask);
- if (!ab->skb)
- goto err;
-
- ab->ctx = ctx;
- ab->gfp_mask = gfp_mask;
- nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0));
- nlh->nlmsg_type = type;
- nlh->nlmsg_flags = 0;
- nlh->nlmsg_pid = 0;
- nlh->nlmsg_seq = 0;
- return ab;
-err:
- audit_buffer_free(ab);
- return NULL;
-}
-
-/**
- * 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
- * determine which pieces belong to the same audit record. The
- * (timestamp,serial) tuple is unique for each syscall and is live from
- * syscall entry to syscall exit.
- *
- * NOTE: Another possibility is to store the formatted records off the
- * 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).
- */
-unsigned int audit_serial(void)
-{
- static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED;
- static unsigned int serial = 0;
-
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&serial_lock, flags);
- do {
- ret = ++serial;
- } while (unlikely(!ret));
- spin_unlock_irqrestore(&serial_lock, flags);
-
- return ret;
-}
-
-static inline void audit_get_stamp(struct audit_context *ctx,
- struct timespec *t, unsigned int *serial)
-{
- if (ctx)
- auditsc_get_stamp(ctx, t, serial);
- else {
- *t = CURRENT_TIME;
- *serial = audit_serial();
- }
-}
-
-/* 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 tsk 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, 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)
-{
- struct audit_buffer *ab = NULL;
- struct timespec t;
- unsigned int serial;
- int reserve;
- unsigned long timeout_start = jiffies;
-
- if (!audit_initialized)
- return NULL;
-
- if (unlikely(audit_filter_type(type)))
- return NULL;
-
- if (gfp_mask & __GFP_WAIT)
- reserve = 0;
- else
- reserve = 5; /* Allow atomic callers to go up to five
- entries over the normal backlog limit */
-
- while (audit_backlog_limit
- && skb_queue_len(&audit_skb_queue) > audit_backlog_limit +
reserve) {
- if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time
- && time_before(jiffies, timeout_start + audit_backlog_wait_time)) {
-
- /* Wait for auditd to drain the queue a little */
- DECLARE_WAITQUEUE(wait, current);
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&audit_backlog_wait, &wait);
-
- if (audit_backlog_limit &&
- skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
- schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies);
-
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&audit_backlog_wait, &wait);
- continue;
- }
- if (audit_rate_check())
- printk(KERN_WARNING
- "audit: audit_backlog=%d > "
- "audit_backlog_limit=%d\n",
- skb_queue_len(&audit_skb_queue),
- audit_backlog_limit);
- audit_log_lost("backlog limit exceeded");
- audit_backlog_wait_time = audit_backlog_wait_overflow;
- wake_up(&audit_backlog_wait);
- return NULL;
- }
-
- ab = audit_buffer_alloc(ctx, gfp_mask, type);
- if (!ab) {
- audit_log_lost("out of memory in audit_log_start");
- return NULL;
- }
-
- audit_get_stamp(ab->ctx, &t, &serial);
-
- audit_log_format(ab, "audit(%lu.%03lu:%u): ",
- t.tv_sec, t.tv_nsec/1000000, serial);
- return ab;
-}
-
-/**
- * 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.
- */
-static inline int audit_expand(struct audit_buffer *ab, int extra)
-{
- struct sk_buff *skb = ab->skb;
- int ret = pskb_expand_head(skb, skb_headroom(skb), extra,
- ab->gfp_mask);
- if (ret < 0) {
- audit_log_lost("out of memory in audit_expand");
- return 0;
- }
- return skb_tailroom(skb);
-}
-
-/*
- * 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.
- */
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
-{
- int len, avail;
- struct sk_buff *skb;
- va_list args2;
-
- if (!ab)
- return;
-
- BUG_ON(!ab->skb);
- skb = ab->skb;
- avail = skb_tailroom(skb);
- if (avail == 0) {
- avail = audit_expand(ab, AUDIT_BUFSIZ);
- if (!avail)
- goto out;
- }
- va_copy(args2, args);
- len = vsnprintf(skb->tail, avail, fmt, args);
- if (len >= avail) {
- /* 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));
- if (!avail)
- goto out;
- len = vsnprintf(skb->tail, avail, fmt, args2);
- }
- if (len > 0)
- skb_put(skb, len);
-out:
- return;
-}
-
-/**
- * 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;
-
- if (!ab)
- return;
- va_start(args, fmt);
- audit_log_vformat(ab, fmt, args);
- va_end(args);
-}
-
-/**
- * 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;
- unsigned char *ptr;
- struct sk_buff *skb;
- static const unsigned char *hex = "0123456789ABCDEF";
-
- BUG_ON(!ab->skb);
- skb = ab->skb;
- avail = skb_tailroom(skb);
- new_len = len<<1;
- if (new_len >= avail) {
- /* Round the buffer request up to the next multiple */
- new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
- avail = audit_expand(ab, new_len);
- if (!avail)
- return;
- }
-
- ptr = skb->tail;
- for (i=0; i<len; i++) {
- *ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */
- *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */
- }
- *ptr = 0;
- skb_put(skb, len << 1); /* new string is twice the old string */
-}
-
-/**
- * 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).
- */
-const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
-{
- const unsigned char *p = string;
- size_t len = strlen(string);
-
- while (*p) {
- if (*p == '"' || *p < 0x21 || *p > 0x7f) {
- audit_log_hex(ab, string, len);
- return string + len + 1;
- }
- p++;
- }
- audit_log_format(ab, "\"%s\"", string);
- return p + 1;
-}
-
-/* This is a helper-function to print the escaped d_path */
-void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
- struct dentry *dentry, struct vfsmount *vfsmnt)
-{
- char *p, *path;
-
- if (prefix)
- audit_log_format(ab, " %s", prefix);
-
- /* We will allow 11 spaces for ' (deleted)' to be appended */
- path = kmalloc(PATH_MAX+11, ab->gfp_mask);
- if (!path) {
- audit_log_format(ab, "<no memory>");
- return;
- }
- p = d_path(dentry, vfsmnt, path, PATH_MAX+11);
- if (IS_ERR(p)) { /* Should never happen since we send PATH_MAX */
- /* FIXME: can we save some information here? */
- audit_log_format(ab, "<too long>");
- } else
- audit_log_untrustedstring(ab, p);
- kfree(path);
-}
-
-/**
- * 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.
- */
-void audit_log_end(struct audit_buffer *ab)
-{
- if (!ab)
- return;
- if (!audit_rate_check()) {
- audit_log_lost("rate limit exceeded");
- } else {
- if (audit_pid) {
- struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
- nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
- skb_queue_tail(&audit_skb_queue, ab->skb);
- ab->skb = NULL;
- wake_up_interruptible(&kauditd_wait);
- } else {
- printk(KERN_NOTICE "%s\n", ab->skb->data + NLMSG_SPACE(0));
- }
- }
- audit_buffer_free(ab);
-}
-
-/**
- * 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, ...)
-{
- struct audit_buffer *ab;
- va_list args;
-
- ab = audit_log_start(ctx, gfp_mask, type);
- if (ab) {
- va_start(args, fmt);
- audit_log_vformat(ab, fmt, args);
- va_end(args);
- audit_log_end(ab);
- }
-}
-
-EXPORT_SYMBOL(audit_log_start);
-EXPORT_SYMBOL(audit_log_end);
-EXPORT_SYMBOL(audit_log_format);
-EXPORT_SYMBOL(audit_log);
diff --git a/kernel/audit.h b/kernel/audit.h
deleted file mode 100644
index 771833d..0000000
--- a/kernel/audit.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/* 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/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_parent;
-
-struct audit_watch {
- atomic_t count; /* reference count */
- char *path; /* insertion path */
- dev_t dev; /* associated superblock device */
- unsigned long ino; /* associated inode number */
- struct audit_parent *parent; /* associated parent */
- struct list_head wlist; /* entry in parent->watches list */
- struct list_head rules; /* associated rules */
-};
-
-struct audit_field {
- u32 type;
- u32 val;
- u32 op;
- char *se_str;
- struct selinux_audit_rule *se_rule;
-};
-
-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_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;
-};
-
-
-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 struct sk_buff * audit_make_reply(int pid, int seq, int type,
- int done, int multi,
- void *payload, int size);
-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);
-
-struct audit_netlink_list {
- int pid;
- struct sk_buff_head q;
-};
-
-int audit_send_list(void *);
-
-struct inotify_watch;
-extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
- const char *, struct inode *);
-extern int selinux_audit_rule_update(void);
-
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
deleted file mode 100644
index 35dca7e..0000000
--- a/kernel/auditfilter.c
+++ /dev/null
@@ -1,1466 +0,0 @@
-/* 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/mutex.h>
-#include <linux/fs.h>
-#include <linux/namei.h>
-#include <linux/netlink.h>
-#include <linux/inotify.h>
-#include <linux/selinux.h>
-#include "audit.h"
-
-/*
- * Locking model:
- *
- * audit_filter_mutex:
- * Synchronizes writes and blocking reads of audit's filterlist
- * data. Rcu is used to traverse the filterlist and access
- * contents of structs audit_entry, audit_watch and opaque
- * selinux rules during filtering. If modified, these structures
- * must be copied and replace their counterparts in the filterlist.
- * An audit_parent struct is not accessed during filtering, so may
- * be written directly provided audit_filter_mutex is held.
- */
-
-/*
- * Reference counting:
- *
- * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED
- * event. Each audit_watch holds a reference to its associated parent.
- *
- * audit_watch: if added to lists, lifetime is from audit_init_watch() to one
- * of: audit_remove_watch() [user removes], audit_update_watch() [kernel
- * replaces], or audit_remove_parent_watches() [kernel removes].
- * Additionally, an audit_watch may exist temporarily to assist in
- * searching existing filter data. Each audit_krule holds a reference to
- * its associated watch.
- */
-
-struct audit_parent {
- atomic_t count; /* reference count */
- struct list_head ilist; /* entry in inotify registration list */
- struct list_head watches; /* associated watches */
- struct inotify_watch wdata; /* inotify watch data */
- unsigned flags; /* status flags */
-};
-
-/*
- * audit_parent status flags:
- *
- * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
- * a filesystem event. Technically not needed for IN_DELETE_SELF or IN_UNMOUNT
- * events, as we cannot receive them while we have nameidata (during rule add)
- * and the audit_parent is immediately removed when processing the following
- * IN_IGNORED event. The IN_MOVE_SELF event is different. We can receive it
- * while holding nameidata, and inotify will not send us the IN_IGNORED so we
- * must later remove the inotify watch on audit_parent ourselves.
- */
-#define AUDIT_PARENT_INVALID 0x001
-
-/* Audit filter lists, defined in <linux/audit.h> */
-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
-};
-
-DEFINE_MUTEX(audit_filter_mutex);
-
-/* Inotify handle */
-extern struct inotify_handle *audit_ih;
-
-/* Inotify events we care about. */
-#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF
-#define AUDIT_IN_SELF IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT
-
-static inline void audit_get_parent(struct audit_parent *parent)
-{
- atomic_inc(&parent->count);
-}
-
-static inline void audit_put_parent(struct audit_parent *parent)
-{
- if (atomic_dec_and_test(&parent->count)) {
- WARN_ON(!list_empty(&parent->watches));
- kfree(parent);
- }
-}
-
-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)) {
- WARN_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)
-{
- int i;
-
- /* some rules don't have associated watches */
- if (e->rule.watch)
- audit_put_watch(e->rule.watch);
- if (e->rule.fields)
- for (i = 0; i < e->rule.field_count; i++) {
- struct audit_field *f = &e->rule.fields[i];
- kfree(f->se_str);
- selinux_audit_rule_free(f->se_rule);
- }
- 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);
-}
-
-/* Initialize a parent watch entry. */
-static inline struct audit_parent *audit_init_parent(void)
-{
- struct audit_parent *parent;
-
- parent = kzalloc(sizeof(*parent), GFP_KERNEL);
- if (unlikely(!parent))
- return ERR_PTR(-ENOMEM);
-
- INIT_LIST_HEAD(&parent->watches);
- atomic_set(&parent->count, 1);
- parent->flags = 0;
-
- return parent;
-}
-
-/* Initialize a watch entry. */
-static inline struct audit_watch *audit_init_watch(char *path)
-{
- struct audit_watch *watch;
-
- watch = kzalloc(sizeof(*watch), GFP_KERNEL);
- if (unlikely(!watch))
- return ERR_PTR(-ENOMEM);
-
- INIT_LIST_HEAD(&watch->rules);
- atomic_set(&watch->count, 1);
- watch->path = path;
- watch->dev = (dev_t)-1;
- watch->ino = (unsigned long)-1;
-
- return watch;
-}
-
-/* Initialize an audit filterlist entry. */
-static inline struct audit_entry *audit_init_entry(u32 field_count)
-{
- struct audit_entry *entry;
- struct audit_field *fields;
-
- entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (unlikely(!entry))
- return NULL;
-
- fields = kzalloc(sizeof(*fields) * field_count, GFP_KERNEL);
- if (unlikely(!fields)) {
- kfree(entry);
- return NULL;
- }
- 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)
-{
- 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;
-}
-
-/* Translate a watch string to kernel respresentation. */
-static int audit_to_watch(struct audit_krule *krule, char *path, int len,
- u32 op)
-{
- struct audit_watch *watch;
-
- if (path[0] != '/' || path[len-1] == '/' ||
- krule->listnr != AUDIT_FILTER_EXIT ||
- op & ~AUDIT_EQUAL ||
- krule->watch) /* allow only 1 watch per rule */
- return -EINVAL;
-
- /* ensure inotify handle was initialized */
- if (!audit_ih)
- return -EOPNOTSUPP;
-
- watch = audit_init_watch(path);
- if (unlikely(IS_ERR(watch)))
- return PTR_ERR(watch);
-
- audit_get_watch(watch);
- krule->watch = watch;
-
- 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;
- 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 = audit_init_entry(rule->field_count);
- if (!entry)
- goto exit_err;
-
- entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
- entry->rule.listnr = listnr;
- entry->rule.action = rule->action;
- entry->rule.field_count = rule->field_count;
-
- 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];
-
- 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_SE_USER ||
- f->type == AUDIT_SE_ROLE ||
- f->type == AUDIT_SE_TYPE ||
- f->type == AUDIT_SE_SEN ||
- f->type == AUDIT_SE_CLR ||
- f->type == AUDIT_WATCH) {
- err = -EINVAL;
- goto exit_free;
- }
-
- 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;
- char *str;
-
- 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];
- f->val = data->values[i];
- f->se_str = NULL;
- f->se_rule = NULL;
- switch(f->type) {
- case AUDIT_SE_USER:
- case AUDIT_SE_ROLE:
- case AUDIT_SE_TYPE:
- case AUDIT_SE_SEN:
- case AUDIT_SE_CLR:
- str = audit_unpack_string(&bufp, &remain, f->val);
- if (IS_ERR(str))
- goto exit_free;
- entry->rule.buflen += f->val;
-
- err = selinux_audit_rule_init(f->type, f->op, str,
- &f->se_rule);
- /* Keep currently invalid fields around in case they
- * become valid after a policy reload. */
- if (err == -EINVAL) {
- printk(KERN_WARNING "audit rule for selinux "
- "\'%s\' is invalid\n", str);
- err = 0;
- }
- if (err) {
- kfree(str);
- goto exit_free;
- } else
- f->se_str = str;
- break;
- case AUDIT_WATCH:
- str = audit_unpack_string(&bufp, &remain, f->val);
- if (IS_ERR(str))
- goto exit_free;
- entry->rule.buflen += f->val;
-
- err = audit_to_watch(&entry->rule, str, f->val, f->op);
- if (err) {
- kfree(str);
- goto exit_free;
- }
- break;
- }
- }
-
-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) {
- case AUDIT_SE_USER:
- case AUDIT_SE_ROLE:
- case AUDIT_SE_TYPE:
- case AUDIT_SE_SEN:
- case AUDIT_SE_CLR:
- data->buflen += data->values[i] =
- audit_pack_string(&bufp, f->se_str);
- break;
- case AUDIT_WATCH:
- data->buflen += data->values[i] =
- audit_pack_string(&bufp, krule->watch->path);
- break;
- 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) {
- case AUDIT_SE_USER:
- case AUDIT_SE_ROLE:
- case AUDIT_SE_TYPE:
- case AUDIT_SE_SEN:
- case AUDIT_SE_CLR:
- if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
- return 1;
- break;
- case AUDIT_WATCH:
- if (strcmp(a->watch->path, b->watch->path))
- return 1;
- break;
- 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;
-}
-
-/* Duplicate the given audit watch. The new watch's rules list is initialized
- * to an empty list and wlist is undefined. */
-static inline struct audit_watch *audit_dupe_watch(struct audit_watch *old)
-{
- char *path;
- struct audit_watch *new;
-
- path = kstrdup(old->path, GFP_KERNEL);
- if (unlikely(!path))
- return ERR_PTR(-ENOMEM);
-
- new = audit_init_watch(path);
- if (unlikely(IS_ERR(new))) {
- kfree(path);
- goto out;
- }
-
- new->dev = old->dev;
- new->ino = old->ino;
- audit_get_parent(old->parent);
- new->parent = old->parent;
-
-out:
- return new;
-}
-
-/* Duplicate selinux field information. The se_rule is opaque, so must be
- * re-initialized. */
-static inline int audit_dupe_selinux_field(struct audit_field *df,
- struct audit_field *sf)
-{
- int ret = 0;
- char *se_str;
-
- /* our own copy of se_str */
- se_str = kstrdup(sf->se_str, GFP_KERNEL);
- if (unlikely(IS_ERR(se_str)))
- return -ENOMEM;
- df->se_str = se_str;
-
- /* our own (refreshed) copy of se_rule */
- ret = selinux_audit_rule_init(df->type, df->op, df->se_str,
- &df->se_rule);
- /* Keep currently invalid fields around in case they
- * become valid after a policy reload. */
- if (ret == -EINVAL) {
- printk(KERN_WARNING "audit rule for selinux \'%s\' is "
- "invalid\n", df->se_str);
- ret = 0;
- }
-
- return ret;
-}
-
-/* Duplicate an audit rule. This will be a deep copy with the exception
- * of the watch - that pointer is carried over. The selinux specific fields
- * will be updated in the copy. The point is to be able to replace the old
- * rule with the new rule in the filterlist, then free the old rule.
- * The rlist element is undefined; list manipulations are handled apart from
- * the initial copy. */
-static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
- struct audit_watch *watch)
-{
- u32 fcount = old->field_count;
- struct audit_entry *entry;
- struct audit_krule *new;
- int i, err = 0;
-
- entry = audit_init_entry(fcount);
- if (unlikely(!entry))
- return ERR_PTR(-ENOMEM);
-
- new = &entry->rule;
- new->vers_ops = old->vers_ops;
- new->flags = old->flags;
- new->listnr = old->listnr;
- new->action = old->action;
- for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
- new->mask[i] = old->mask[i];
- new->buflen = old->buflen;
- new->watch = NULL;
- new->field_count = old->field_count;
- memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
-
- /* deep copy this information, updating the se_rule fields, because
- * the originals will all be freed when the old rule is freed. */
- for (i = 0; i < fcount; i++) {
- switch (new->fields[i].type) {
- case AUDIT_SE_USER:
- case AUDIT_SE_ROLE:
- case AUDIT_SE_TYPE:
- case AUDIT_SE_SEN:
- case AUDIT_SE_CLR:
- err = audit_dupe_selinux_field(&new->fields[i],
- &old->fields[i]);
- }
- if (err) {
- audit_free_rule(entry);
- return ERR_PTR(err);
- }
- }
-
- if (watch) {
- audit_get_watch(watch);
- new->watch = watch;
- }
-
- return entry;
-}
-
-/* Update inode info in audit rules based on filesystem event. */
-static inline void audit_update_watch(struct audit_parent *parent,
- const char *dname, dev_t dev,
- unsigned long ino)
-{
- struct audit_watch *owatch, *nwatch, *nextw;
- struct audit_krule *r, *nextr;
- struct audit_entry *oentry, *nentry;
- struct audit_buffer *ab;
-
- mutex_lock(&audit_filter_mutex);
- list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
- if (audit_compare_dname_path(dname, owatch->path))
- continue;
-
- nwatch = audit_dupe_watch(owatch);
- if (unlikely(IS_ERR(nwatch))) {
- mutex_unlock(&audit_filter_mutex);
- audit_panic("error updating watch, skipping");
- return;
- }
- nwatch->dev = dev;
- nwatch->ino = ino;
-
- list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
- oentry = container_of(r, struct audit_entry, rule);
-
- nentry = audit_dupe_rule(&oentry->rule, nwatch);
- if (unlikely(IS_ERR(nentry))) {
- audit_panic("error updating watch, removing");
- list_del(&oentry->rule.rlist);
- list_del_rcu(&oentry->list);
- } else {
- list_add(&nentry->rule.rlist, &nwatch->rules);
- list_del(&oentry->rule.rlist);
- list_replace_rcu(&oentry->list, &nentry->list);
- }
- call_rcu(&oentry->rcu, audit_free_rule_rcu);
- }
-
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
- audit_log_format(ab, "audit updated rules specifying watch=");
- audit_log_untrustedstring(ab, owatch->path);
- audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
- audit_log_end(ab);
-
- list_del(&owatch->wlist);
- audit_put_watch(owatch); /* matches initial get */
- goto add_watch_to_parent; /* event applies to a single watch */
- }
- mutex_unlock(&audit_filter_mutex);
- return;
-
-add_watch_to_parent:
- list_add(&nwatch->wlist, &parent->watches);
- mutex_unlock(&audit_filter_mutex);
- return;
-}
-
-/* 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;
-
- mutex_lock(&audit_filter_mutex);
- parent->flags |= AUDIT_PARENT_INVALID;
- 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);
- list_del(&r->rlist);
- list_del_rcu(&e->list);
- call_rcu(&e->rcu, audit_free_rule_rcu);
-
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit implicitly removed rule from list=%d\n",
- AUDIT_FILTER_EXIT);
- }
- list_del(&w->wlist);
- audit_put_watch(w); /* matches initial get */
- }
- mutex_unlock(&audit_filter_mutex);
-}
-
-/* Register inotify watches for parents on in_list. */
-static int audit_inotify_register(struct nameidata *nd,
- struct list_head *in_list)
-{
- struct audit_parent *p;
- s32 wd;
- int ret = 0;
-
- list_for_each_entry(p, in_list, ilist) {
- wd = inotify_add_watch(audit_ih, &p->wdata, nd->dentry->d_inode,
- AUDIT_IN_WATCH);
- if (wd < 0) {
- audit_remove_parent_watches(p);
- /* the put matching the get in audit_init_parent() */
- audit_put_parent(p);
- /* save the first error for return value */
- if (!ret)
- ret = wd;
- }
- }
-
- return ret;
-}
-
-/* Unregister inotify watches for parents on in_list.
- * Generates an IN_IGNORED event. */
-static void audit_inotify_unregister(struct list_head *in_list)
-{
- struct audit_parent *p;
-
- list_for_each_entry(p, in_list, ilist) {
- inotify_rm_watch(audit_ih, &p->wdata);
- /* the put matching the get in audit_remove_watch() */
- audit_put_parent(p);
- }
-}
-
-/* Get path information necessary for adding watches. */
-static int audit_get_nd(char *path, struct nameidata **ndp,
- struct nameidata **ndw)
-{
- struct nameidata *ndparent, *ndwatch;
- int err;
-
- ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL);
- if (unlikely(!ndparent))
- return -ENOMEM;
-
- ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL);
- if (unlikely(!ndwatch)) {
- kfree(ndparent);
- return -ENOMEM;
- }
-
- err = path_lookup(path, LOOKUP_PARENT, ndparent);
- if (err) {
- kfree(ndparent);
- kfree(ndwatch);
- return err;
- }
-
- err = path_lookup(path, 0, ndwatch);
- if (err) {
- kfree(ndwatch);
- ndwatch = NULL;
- }
-
- *ndp = ndparent;
- *ndw = ndwatch;
-
- return 0;
-}
-
-/* Release resources used for watch path information. */
-static inline void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
-{
- if (ndp) {
- path_release(ndp);
- kfree(ndp);
- }
- if (ndw) {
- path_release(ndw);
- kfree(ndw);
- }
-}
-
-/* Add a parent inotify_watch for the given rule. */
-static int audit_add_parent(struct audit_krule *krule,
- struct list_head *inotify_list)
-{
- struct audit_parent *parent;
- struct audit_watch *watch = krule->watch;
-
- parent = audit_init_parent();
- if (IS_ERR(parent))
- return PTR_ERR(parent);
-
- audit_get_parent(parent);
- watch->parent = parent;
-
- /* krule, watch and parent have not been added to any global
- * lists, so we don't need to take audit_filter_mutex. */
- list_add(&watch->wlist, &parent->watches);
- list_add(&krule->rlist, &watch->rules);
-
- /* add parent to inotify registration list */
- list_add(&parent->ilist, inotify_list);
-
- return 0;
-}
-
-/* Associate the given rule with an existing parent inotify_watch.
- * Caller must hold audit_filter_mutex. */
-static int audit_add_to_parent(struct audit_krule *krule,
- struct inotify_watch *iwatch)
-{
- struct audit_parent *parent;
- struct audit_watch *w, *watch = krule->watch;
- int watch_found = 0;
-
- parent = container_of(iwatch, struct audit_parent, wdata);
-
- /* parent was moved before we took audit_filter_mutex */
- if (parent->flags & AUDIT_PARENT_INVALID)
- return -ENOENT;
-
- list_for_each_entry(w, &parent->watches, wlist) {
- if (strcmp(watch->path, w->path))
- continue;
-
- watch_found = 1;
-
- /* put krule's and initial refs to temporary watch */
- audit_put_watch(watch);
- audit_put_watch(watch);
-
- audit_get_watch(w);
- krule->watch = watch = w;
- break;
- }
-
- if (!watch_found) {
- audit_get_parent(parent);
- watch->parent = parent;
-
- list_add(&watch->wlist, &parent->watches);
- }
-
- list_add(&krule->rlist, &watch->rules);
-
- return 0;
-}
-
-/* Find a matching watch entry, or add this one.
- * Caller must hold audit_filter_mutex. */
-static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
- struct nameidata *ndw,
- struct list_head *inotify_list)
-{
- struct audit_watch *watch = krule->watch;
- struct inotify_watch *iwatch;
- int ret;
-
- /* update watch filter fields */
- if (ndw) {
- watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
- watch->ino = ndw->dentry->d_inode->i_ino;
- }
-
- /* The audit_filter_mutex must not be held during inotify calls because
- * we hold it during inotify event callback processing.
- * We can trust iwatch to stick around because we hold nameidata (ndp). */
- mutex_unlock(&audit_filter_mutex);
-
- if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &iwatch) < 0) {
- ret = audit_add_parent(krule, inotify_list);
- mutex_lock(&audit_filter_mutex);
- } else {
- mutex_lock(&audit_filter_mutex);
- ret = audit_add_to_parent(krule, iwatch);
- }
-
- return ret;
-}
-
-/* Add rule to given filterlist if not a duplicate. */
-static inline int audit_add_rule(struct audit_entry *entry,
- struct list_head *list)
-{
- struct audit_entry *e;
- struct audit_watch *watch = entry->rule.watch;
- struct nameidata *ndp, *ndw;
- LIST_HEAD(inotify_list);
- int err;
-
- /* Taking audit_filter_mutex protects from stale rule data. */
- mutex_lock(&audit_filter_mutex);
- list_for_each_entry(e, list, list) {
- if (!audit_compare_rule(&entry->rule, &e->rule)) {
- err = -EEXIST;
- mutex_unlock(&audit_filter_mutex);
- goto error;
- }
- }
- mutex_unlock(&audit_filter_mutex);
-
- /* Avoid calling path_lookup under audit_filter_mutex. */
- if (watch) {
- err = audit_get_nd(watch->path, &ndp, &ndw);
- if (err)
- goto error;
- }
-
- mutex_lock(&audit_filter_mutex);
- if (watch) {
- /* audit_filter_mutex is dropped and re-taken during this call */
- err = audit_add_watch(&entry->rule, ndp, ndw, &inotify_list);
- if (err) {
- audit_put_nd(ndp, ndw);
- goto error;
- }
- }
-
- if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
- list_add_rcu(&entry->list, list);
- } else {
- list_add_tail_rcu(&entry->list, list);
- }
- mutex_unlock(&audit_filter_mutex);
-
- if (!list_empty(&inotify_list)) {
- err = audit_inotify_register(ndp, &inotify_list);
- if (err)
- goto error;
- audit_put_nd(ndp, ndw);
- }
-
- return 0;
-
-error:
- if (watch)
- audit_put_watch(watch); /* tmp watch, matches initial get */
- return err;
-}
-
-/* Remove given krule from its associated watch's rules list and clean up any
- * last instances of associated watch and parent.
- * Caller must hold audit_filter_mutex. */
-static inline void audit_remove_watch(struct audit_krule *krule,
- struct list_head *in_list)
-{
- struct audit_watch *watch = krule->watch;
- struct audit_parent *parent = watch->parent;
-
- list_del(&krule->rlist);
- if (list_empty(&watch->rules)) {
- list_del(&watch->wlist);
- audit_put_watch(watch); /* matches initial get */
-
- if (list_empty(&parent->watches)) {
- /* Put parent on the inotify un-registration list.
- * Grab a reference before releasing audit_filter_mutex,
- * to be released in audit_inotify_unregister(). */
- list_add(&parent->ilist, in_list);
- audit_get_parent(parent);
- }
- }
-}
-
-/* Remove an existing rule from filterlist. */
-static inline int audit_del_rule(struct audit_entry *entry,
- struct list_head *list)
-{
- struct audit_entry *e;
- LIST_HEAD(inotify_list);
-
- mutex_lock(&audit_filter_mutex);
- list_for_each_entry(e, list, list) {
- if (audit_compare_rule(&entry->rule, &e->rule))
- continue;
-
- if (e->rule.watch) {
- audit_remove_watch(&e->rule, &inotify_list);
- /* match initial get for tmp watch */
- audit_put_watch(entry->rule.watch);
- }
-
- list_del_rcu(&e->list);
- call_rcu(&e->rcu, audit_free_rule_rcu);
- mutex_unlock(&audit_filter_mutex);
-
- if (!list_empty(&inotify_list))
- audit_inotify_unregister(&inotify_list);
-
- return 0;
- }
- mutex_unlock(&audit_filter_mutex);
- /* match initial get for tmp watch */
- if (entry->rule.watch)
- audit_put_watch(entry->rule.watch);
- return -ENOENT; /* No matching rule */
-}
-
-/* List rules using struct audit_rule. Exists for backward
- * compatibility with userspace. */
-static void audit_list(int pid, int seq, struct sk_buff_head *q)
-{
- struct sk_buff *skb;
- struct audit_entry *entry;
- int i;
-
- /* This is a blocking read, so use audit_filter_mutex instead of rcu
- * iterator to sync with list writers. */
- 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;
- skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
- rule, sizeof(*rule));
- if (skb)
- skb_queue_tail(q, skb);
- kfree(rule);
- }
- }
- skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
- if (skb)
- skb_queue_tail(q, skb);
-}
-
-/* List rules using struct audit_rule_data. */
-static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
-{
- struct sk_buff *skb;
- struct audit_entry *e;
- int i;
-
- /* This is a blocking read, so use audit_filter_mutex instead of rcu
- * iterator to sync with list writers. */
- 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;
- skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
- data, sizeof(*data) + data->buflen);
- if (skb)
- skb_queue_tail(q, skb);
- kfree(data);
- }
- }
- skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
- if (skb)
- skb_queue_tail(q, skb);
-}
-
-/**
- * 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
- * @sid: SE Linux Security ID of sender
- */
-int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
- size_t datasz, uid_t loginuid, u32 sid)
-{
- struct task_struct *tsk;
- struct audit_netlink_list *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(sizeof(struct audit_netlink_list), GFP_KERNEL);
- if (!dest)
- return -ENOMEM;
- dest->pid = pid;
- skb_queue_head_init(&dest->q);
-
- mutex_lock(&audit_filter_mutex);
- if (type == AUDIT_LIST)
- audit_list(pid, seq, &dest->q);
- else
- audit_list_rules(pid, seq, &dest->q);
- mutex_unlock(&audit_filter_mutex);
-
- tsk = kthread_run(audit_send_list, dest, "audit_send_list");
- if (IS_ERR(tsk)) {
- skb_queue_purge(&dest->q);
- 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]);
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- if (selinux_ctxid_to_string(sid, &ctx, &len)) {
- /* Maybe call audit_panic? */
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "auid=%u ssid=%u add rule to list=%d res=%d",
- loginuid, sid, entry->rule.listnr, !err);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "auid=%u subj=%s add rule to list=%d res=%d",
- loginuid, ctx, entry->rule.listnr, !err);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "auid=%u add rule to list=%d res=%d",
- 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]);
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- if (selinux_ctxid_to_string(sid, &ctx, &len)) {
- /* Maybe call audit_panic? */
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "auid=%u ssid=%u remove rule from list=%d res=%d",
- loginuid, sid, entry->rule.listnr, !err);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "auid=%u subj=%s remove rule from list=%d res=%d",
- loginuid, ctx, entry->rule.listnr, !err);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "auid=%u remove rule from list=%d res=%d",
- 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;
-}
-
-/* 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,
- 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;
-}
-
-/* Check to see if the rule contains any selinux fields. Returns 1 if there
- are selinux fields specified in the rule, 0 otherwise. */
-static inline int audit_rule_has_selinux(struct audit_krule *rule)
-{
- int i;
-
- for (i = 0; i < rule->field_count; i++) {
- struct audit_field *f = &rule->fields[i];
- switch (f->type) {
- case AUDIT_SE_USER:
- case AUDIT_SE_ROLE:
- case AUDIT_SE_TYPE:
- case AUDIT_SE_SEN:
- case AUDIT_SE_CLR:
- return 1;
- }
- }
-
- return 0;
-}
-
-/* This function will re-initialize the se_rule field of all applicable rules.
- * It will traverse the filter lists serarching for rules that contain selinux
- * specific filter fields. When such a rule is found, it is copied, the
- * selinux field is re-initialized, and the old rule is replaced with the
- * updated rule. */
-int selinux_audit_rule_update(void)
-{
- struct audit_entry *entry, *n, *nentry;
- struct audit_watch *watch;
- int i, err = 0;
-
- /* audit_filter_mutex synchronizes the writers */
- mutex_lock(&audit_filter_mutex);
-
- for (i = 0; i < AUDIT_NR_FILTERS; i++) {
- list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
- if (!audit_rule_has_selinux(&entry->rule))
- continue;
-
- watch = entry->rule.watch;
- nentry = audit_dupe_rule(&entry->rule, watch);
- if (unlikely(IS_ERR(nentry))) {
- /* save the first error encountered for the
- * return value */
- if (!err)
- err = PTR_ERR(nentry);
- audit_panic("error updating selinux filters");
- if (watch)
- list_del(&entry->rule.rlist);
- list_del_rcu(&entry->list);
- } else {
- if (watch) {
- list_add(&nentry->rule.rlist,
- &watch->rules);
- list_del(&entry->rule.rlist);
- }
- list_replace_rcu(&entry->list, &nentry->list);
- }
- call_rcu(&entry->rcu, audit_free_rule_rcu);
- }
- }
-
- mutex_unlock(&audit_filter_mutex);
-
- return err;
-}
-
-/* Update watch data in audit rules based on inotify events. */
-void audit_handle_ievent(struct inotify_watch *iwatch, u32 wd, u32 mask,
- u32 cookie, const char *dname, struct inode *inode)
-{
- struct audit_parent *parent = container_of(iwatch, struct audit_parent, wdata);
-
- if (mask & (IN_CREATE|IN_MOVED_TO) && inode)
- audit_update_watch(parent, dname, inode->i_sb->s_dev,
- inode->i_ino);
- else if (mask & (IN_DELETE|IN_MOVED_FROM))
- audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1);
- /* Note: Inotify doesn't remove the watch for the IN_MOVE_SELF event.
- * Work around this by leaving the parent around with an empty
- * watchlist. It will be re-used if new watches are added. */
- else if (mask & (AUDIT_IN_SELF))
- audit_remove_parent_watches(parent);
- else if (mask & IN_IGNORED)
- audit_put_parent(parent); /* match get in audit_init_parent() */
-}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
deleted file mode 100644
index 43512c1..0000000
--- a/kernel/auditsc.c
+++ /dev/null
@@ -1,1401 +0,0 @@
-/* auditsc.c -- System-call auditing support
- * Handles all system-call specific auditing features.
- *
- * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
- * Copyright 2005 Hewlett-Packard Development Company, L.P.
- * Copyright (C) 2005 IBM Corporation
- * All Rights Reserved.
- *
- * 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
- *
- * Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
- *
- * Many of the ideas implemented here are from Stephen C. Tweedie,
- * especially the idea of avoiding a copy by using getname.
- *
- * The method for actual interception of syscall entry and exit (not in
- * this file -- see entry.S) is based on a GPL'd patch written by
- * okir(a)suse.de and Copyright 2003 SuSE Linux AG.
- *
- * The support of additional filter rules compares (>, <, >=, <=) was
- * added by Dustin Kirkland <dustin.kirkland(a)us.ibm.com>, 2005.
- *
- * Modified by Amy Griffis <amy.griffis(a)hp.com> to collect additional
- * filesystem information.
- *
- * Subject and object context labeling support added by <danjones(a)us.ibm.com>
- * and <dustin.kirkland(a)us.ibm.com> for LSPP certification compliance.
- */
-
-#include <linux/init.h>
-#include <asm/types.h>
-#include <asm/atomic.h>
-#include <asm/types.h>
-#include <linux/fs.h>
-#include <linux/namei.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/mount.h>
-#include <linux/socket.h>
-#include <linux/audit.h>
-#include <linux/personality.h>
-#include <linux/time.h>
-#include <linux/netlink.h>
-#include <linux/compiler.h>
-#include <asm/unistd.h>
-#include <linux/security.h>
-#include <linux/list.h>
-#include <linux/tty.h>
-#include <linux/selinux.h>
-#include <linux/binfmts.h>
-
-#include "audit.h"
-
-extern struct list_head audit_filter_list[];
-
-/* No syscall auditing will take place unless audit_enabled != 0. */
-extern int audit_enabled;
-
-/* AUDIT_NAMES is the number of slots we reserve in the audit_context
- * for saving names from getname(). */
-#define AUDIT_NAMES 20
-
-/* AUDIT_NAMES_RESERVED is the number of slots we reserve in the
- * audit_context from being used for nameless inodes from
- * path_lookup. */
-#define AUDIT_NAMES_RESERVED 7
-
-/* When fs/namei.c:getname() is called, we store the pointer in name and
- * we don't let putname() free it (instead we free all of the saved
- * pointers at syscall exit time).
- *
- * Further, in fs/namei.c:path_lookup() we store the inode and device. */
-struct audit_names {
- const char *name;
- unsigned long ino;
- unsigned long pino;
- dev_t dev;
- umode_t mode;
- uid_t uid;
- gid_t gid;
- dev_t rdev;
- u32 osid;
-};
-
-struct audit_aux_data {
- struct audit_aux_data *next;
- int type;
-};
-
-#define AUDIT_AUX_IPCPERM 0
-
-struct audit_aux_data_ipcctl {
- struct audit_aux_data d;
- struct ipc_perm p;
- unsigned long qbytes;
- uid_t uid;
- gid_t gid;
- mode_t mode;
- u32 osid;
-};
-
-struct audit_aux_data_execve {
- struct audit_aux_data d;
- int argc;
- int envc;
- char mem[0];
-};
-
-struct audit_aux_data_socketcall {
- struct audit_aux_data d;
- int nargs;
- unsigned long args[0];
-};
-
-struct audit_aux_data_sockaddr {
- struct audit_aux_data d;
- int len;
- char a[0];
-};
-
-struct audit_aux_data_path {
- struct audit_aux_data d;
- struct dentry *dentry;
- struct vfsmount *mnt;
-};
-
-/* The per-task audit context. */
-struct audit_context {
- int in_syscall; /* 1 if task is in a syscall */
- enum audit_state state;
- unsigned int serial; /* serial number for record */
- struct timespec ctime; /* time of syscall entry */
- uid_t loginuid; /* login uid (identity) */
- int major; /* syscall number */
- unsigned long argv[4]; /* syscall arguments */
- int return_valid; /* return code is valid */
- long return_code;/* syscall return code */
- int auditable; /* 1 if record should be written */
- int name_count;
- struct audit_names names[AUDIT_NAMES];
- struct dentry * pwd;
- struct vfsmount * pwdmnt;
- struct audit_context *previous; /* For nested syscalls */
- struct audit_aux_data *aux;
-
- /* Save things to print about task_struct */
- pid_t pid;
- uid_t uid, euid, suid, fsuid;
- gid_t gid, egid, sgid, fsgid;
- unsigned long personality;
- int arch;
-
-#if AUDIT_DEBUG
- int put_count;
- int ino_count;
-#endif
-};
-
-/* Determine if any context name data matches a rule's watch data */
-static inline int audit_match_watch(struct audit_context *ctx,
- struct audit_watch *watch)
-{
- int i;
-
- if (!ctx)
- return 0;
-
- if (watch->ino == (unsigned long)-1)
- return 0;
-
- for (i = 0; i < ctx->name_count; i++) {
- if (ctx->names[i].dev == watch->dev &&
- (ctx->names[i].ino == watch->ino ||
- ctx->names[i].pino == watch->ino))
- return 1;
- }
-
- return 0;
-}
-
-/* Compare a task_struct with an audit_rule. Return 1 on match, 0
- * otherwise. */
-static int audit_filter_rules(struct task_struct *tsk,
- struct audit_krule *rule,
- struct audit_context *ctx,
- enum audit_state *state)
-{
- int i, j, need_sid = 1;
- u32 sid;
-
- 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(tsk->pid, f->op, f->val);
- break;
- case AUDIT_UID:
- result = audit_comparator(tsk->uid, f->op, f->val);
- break;
- case AUDIT_EUID:
- result = audit_comparator(tsk->euid, f->op, f->val);
- break;
- case AUDIT_SUID:
- result = audit_comparator(tsk->suid, f->op, f->val);
- break;
- case AUDIT_FSUID:
- result = audit_comparator(tsk->fsuid, f->op, f->val);
- break;
- case AUDIT_GID:
- result = audit_comparator(tsk->gid, f->op, f->val);
- break;
- case AUDIT_EGID:
- result = audit_comparator(tsk->egid, f->op, f->val);
- break;
- case AUDIT_SGID:
- result = audit_comparator(tsk->sgid, f->op, f->val);
- break;
- case AUDIT_FSGID:
- result = audit_comparator(tsk->fsgid, f->op, f->val);
- break;
- case AUDIT_PERS:
- result = audit_comparator(tsk->personality, f->op, f->val);
- break;
- case AUDIT_ARCH:
- if (ctx)
- result = audit_comparator(ctx->arch, f->op, f->val);
- break;
-
- case AUDIT_EXIT:
- if (ctx && ctx->return_valid)
- result = audit_comparator(ctx->return_code, f->op, f->val);
- break;
- case AUDIT_SUCCESS:
- if (ctx && ctx->return_valid) {
- if (f->val)
- result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS);
- else
- result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
- }
- break;
- case AUDIT_DEVMAJOR:
- if (ctx) {
- for (j = 0; j < ctx->name_count; j++) {
- if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) {
- ++result;
- break;
- }
- }
- }
- break;
- case AUDIT_DEVMINOR:
- if (ctx) {
- for (j = 0; j < ctx->name_count; j++) {
- if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
- ++result;
- break;
- }
- }
- }
- break;
- case AUDIT_INODE:
- if (ctx) {
- 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)) {
- ++result;
- break;
- }
- }
- }
- break;
- case AUDIT_WATCH:
- result = audit_match_watch(ctx, rule->watch);
- break;
- case AUDIT_LOGINUID:
- result = 0;
- if (ctx)
- result = audit_comparator(ctx->loginuid, f->op, f->val);
- break;
- case AUDIT_SE_USER:
- case AUDIT_SE_ROLE:
- case AUDIT_SE_TYPE:
- case AUDIT_SE_SEN:
- case AUDIT_SE_CLR:
- /* NOTE: this may return negative values indicating
- a temporary error. We simply treat this as a
- match for now to avoid losing information that
- may be wanted. An error message will also be
- logged upon error */
- if (f->se_rule) {
- if (need_sid) {
- selinux_task_ctxid(tsk, &sid);
- need_sid = 0;
- }
- result = selinux_audit_rule_match(sid, f->type,
- f->op,
- f->se_rule,
- ctx);
- }
- break;
- case AUDIT_ARG0:
- case AUDIT_ARG1:
- case AUDIT_ARG2:
- case AUDIT_ARG3:
- if (ctx)
- result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], 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;
-}
-
-/* At process creation time, we can determine if system-call auditing is
- * completely disabled for this task. Since we only have the task
- * structure at this point, we can only check uid and gid.
- */
-static enum audit_state audit_filter_task(struct task_struct *tsk)
-{
- struct audit_entry *e;
- enum audit_state state;
-
- rcu_read_lock();
- list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
- if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
- rcu_read_unlock();
- return state;
- }
- }
- rcu_read_unlock();
- return AUDIT_BUILD_CONTEXT;
-}
-
-/* At syscall entry and exit time, this filter is called if the
- * audit_state is not low enough that auditing cannot take place, but is
- * also not high enough that we already know we have to write an audit
- * record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
- */
-static enum audit_state audit_filter_syscall(struct task_struct *tsk,
- struct audit_context *ctx,
- struct list_head *list)
-{
- struct audit_entry *e;
- enum audit_state state;
-
- if (audit_pid && tsk->tgid == audit_pid)
- return AUDIT_DISABLED;
-
- rcu_read_lock();
- if (!list_empty(list)) {
- int word = AUDIT_WORD(ctx->major);
- int bit = AUDIT_BIT(ctx->major);
-
- list_for_each_entry_rcu(e, list, list) {
- if ((e->rule.mask[word] & bit) == bit
- && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
- rcu_read_unlock();
- return state;
- }
- }
- }
- rcu_read_unlock();
- return AUDIT_BUILD_CONTEXT;
-}
-
-static inline struct audit_context *audit_get_context(struct task_struct *tsk,
- int return_valid,
- int return_code)
-{
- struct audit_context *context = tsk->audit_context;
-
- if (likely(!context))
- return NULL;
- context->return_valid = return_valid;
- context->return_code = return_code;
-
- if (context->in_syscall && !context->auditable) {
- enum audit_state state;
- state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
- if (state == AUDIT_RECORD_CONTEXT)
- context->auditable = 1;
- }
-
- context->pid = tsk->pid;
- context->uid = tsk->uid;
- context->gid = tsk->gid;
- context->euid = tsk->euid;
- context->suid = tsk->suid;
- context->fsuid = tsk->fsuid;
- context->egid = tsk->egid;
- context->sgid = tsk->sgid;
- context->fsgid = tsk->fsgid;
- context->personality = tsk->personality;
- tsk->audit_context = NULL;
- return context;
-}
-
-static inline void audit_free_names(struct audit_context *context)
-{
- int i;
-
-#if AUDIT_DEBUG == 2
- if (context->auditable
- ||context->put_count + context->ino_count != context->name_count) {
- printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
- " name_count=%d put_count=%d"
- " ino_count=%d [NOT freeing]\n",
- __FILE__, __LINE__,
- context->serial, context->major, context->in_syscall,
- context->name_count, context->put_count,
- context->ino_count);
- for (i = 0; i < context->name_count; i++) {
- printk(KERN_ERR "names[%d] = %p = %s\n", i,
- context->names[i].name,
- context->names[i].name ?: "(null)");
- }
- dump_stack();
- return;
- }
-#endif
-#if AUDIT_DEBUG
- context->put_count = 0;
- context->ino_count = 0;
-#endif
-
- for (i = 0; i < context->name_count; i++) {
- if (context->names[i].name)
- __putname(context->names[i].name);
- }
- context->name_count = 0;
- if (context->pwd)
- dput(context->pwd);
- if (context->pwdmnt)
- mntput(context->pwdmnt);
- context->pwd = NULL;
- context->pwdmnt = NULL;
-}
-
-static inline void audit_free_aux(struct audit_context *context)
-{
- struct audit_aux_data *aux;
-
- while ((aux = context->aux)) {
- if (aux->type == AUDIT_AVC_PATH) {
- struct audit_aux_data_path *axi = (void *)aux;
- dput(axi->dentry);
- mntput(axi->mnt);
- }
-
- context->aux = aux->next;
- kfree(aux);
- }
-}
-
-static inline void audit_zero_context(struct audit_context *context,
- enum audit_state state)
-{
- uid_t loginuid = context->loginuid;
-
- memset(context, 0, sizeof(*context));
- context->state = state;
- context->loginuid = loginuid;
-}
-
-static inline struct audit_context *audit_alloc_context(enum audit_state state)
-{
- struct audit_context *context;
-
- if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
- return NULL;
- audit_zero_context(context, state);
- return context;
-}
-
-/**
- * audit_alloc - allocate an audit context block for a task
- * @tsk: task
- *
- * Filter on the task information and allocate a per-task audit context
- * if necessary. Doing so turns on system call auditing for the
- * specified task. This is called from copy_process, so no lock is
- * needed.
- */
-int audit_alloc(struct task_struct *tsk)
-{
- struct audit_context *context;
- enum audit_state state;
-
- if (likely(!audit_enabled))
- return 0; /* Return if not auditing. */
-
- state = audit_filter_task(tsk);
- if (likely(state == AUDIT_DISABLED))
- return 0;
-
- if (!(context = audit_alloc_context(state))) {
- audit_log_lost("out of memory in audit_alloc");
- return -ENOMEM;
- }
-
- /* Preserve login uid */
- context->loginuid = -1;
- if (current->audit_context)
- context->loginuid = current->audit_context->loginuid;
-
- tsk->audit_context = context;
- set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
- return 0;
-}
-
-static inline void audit_free_context(struct audit_context *context)
-{
- struct audit_context *previous;
- int count = 0;
-
- do {
- previous = context->previous;
- if (previous || (count && count < 10)) {
- ++count;
- printk(KERN_ERR "audit(:%d): major=%d name_count=%d:"
- " freeing multiple contexts (%d)\n",
- context->serial, context->major,
- context->name_count, count);
- }
- audit_free_names(context);
- audit_free_aux(context);
- kfree(context);
- context = previous;
- } while (context);
- if (count >= 10)
- printk(KERN_ERR "audit: freed %d contexts\n", count);
-}
-
-static void audit_log_task_context(struct audit_buffer *ab)
-{
- char *ctx = NULL;
- ssize_t len = 0;
-
- len = security_getprocattr(current, "current", NULL, 0);
- if (len < 0) {
- if (len != -EINVAL)
- goto error_path;
- return;
- }
-
- ctx = kmalloc(len, GFP_KERNEL);
- if (!ctx)
- goto error_path;
-
- len = security_getprocattr(current, "current", ctx, len);
- if (len < 0 )
- goto error_path;
-
- audit_log_format(ab, " subj=%s", ctx);
- return;
-
-error_path:
- if (ctx)
- kfree(ctx);
- audit_panic("error in audit_log_task_context");
- return;
-}
-
-static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
-{
- char name[sizeof(tsk->comm)];
- struct mm_struct *mm = tsk->mm;
- struct vm_area_struct *vma;
-
- /* tsk == current */
-
- get_task_comm(name, tsk);
- audit_log_format(ab, " comm=");
- audit_log_untrustedstring(ab, name);
-
- if (mm) {
- down_read(&mm->mmap_sem);
- vma = mm->mmap;
- while (vma) {
- if ((vma->vm_flags & VM_EXECUTABLE) &&
- vma->vm_file) {
- audit_log_d_path(ab, "exe=",
- vma->vm_file->f_dentry,
- vma->vm_file->f_vfsmnt);
- break;
- }
- vma = vma->vm_next;
- }
- up_read(&mm->mmap_sem);
- }
- audit_log_task_context(ab);
-}
-
-static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
-{
- int i, call_panic = 0;
- struct audit_buffer *ab;
- struct audit_aux_data *aux;
- const char *tty;
-
- /* tsk == current */
-
- ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
- if (!ab)
- return; /* audit_panic has been called */
- audit_log_format(ab, "arch=%x syscall=%d",
- context->arch, context->major);
- if (context->personality != PER_LINUX)
- audit_log_format(ab, " per=%lx", context->personality);
- if (context->return_valid)
- audit_log_format(ab, " success=%s exit=%ld",
- (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
- context->return_code);
- if (tsk->signal && tsk->signal->tty &&
tsk->signal->tty->name)
- tty = tsk->signal->tty->name;
- else
- tty = "(none)";
- audit_log_format(ab,
- " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
- " pid=%d auid=%u uid=%u gid=%u"
- " euid=%u suid=%u fsuid=%u"
- " egid=%u sgid=%u fsgid=%u tty=%s",
- context->argv[0],
- context->argv[1],
- context->argv[2],
- context->argv[3],
- context->name_count,
- context->pid,
- context->loginuid,
- context->uid,
- context->gid,
- context->euid, context->suid, context->fsuid,
- context->egid, context->sgid, context->fsgid, tty);
- audit_log_task_info(ab, tsk);
- audit_log_end(ab);
-
- for (aux = context->aux; aux; aux = aux->next) {
-
- ab = audit_log_start(context, GFP_KERNEL, aux->type);
- if (!ab)
- continue; /* audit_panic has been called */
-
- switch (aux->type) {
- case AUDIT_IPC: {
- struct audit_aux_data_ipcctl *axi = (void *)aux;
- audit_log_format(ab,
- " qbytes=%lx iuid=%u igid=%u mode=%x",
- axi->qbytes, axi->uid, axi->gid, axi->mode);
- if (axi->osid != 0) {
- char *ctx = NULL;
- u32 len;
- if (selinux_ctxid_to_string(
- axi->osid, &ctx, &len)) {
- audit_log_format(ab, " osid=%u",
- axi->osid);
- call_panic = 1;
- } else
- audit_log_format(ab, " obj=%s", ctx);
- kfree(ctx);
- }
- break; }
-
- case AUDIT_IPC_SET_PERM: {
- struct audit_aux_data_ipcctl *axi = (void *)aux;
- audit_log_format(ab,
- " new qbytes=%lx new iuid=%u new igid=%u new mode=%x",
- axi->qbytes, axi->uid, axi->gid, axi->mode);
- if (axi->osid != 0) {
- char *ctx = NULL;
- u32 len;
- if (selinux_ctxid_to_string(
- axi->osid, &ctx, &len)) {
- audit_log_format(ab, " osid=%u",
- axi->osid);
- call_panic = 1;
- } else
- audit_log_format(ab, " obj=%s", ctx);
- kfree(ctx);
- }
- break; }
- case AUDIT_EXECVE: {
- struct audit_aux_data_execve *axi = (void *)aux;
- int i;
- const char *p;
- for (i = 0, p = axi->mem; i < axi->argc; i++) {
- audit_log_format(ab, "a%d=", i);
- p = audit_log_untrustedstring(ab, p);
- audit_log_format(ab, "\n");
- }
- break; }
-
- case AUDIT_SOCKETCALL: {
- int i;
- struct audit_aux_data_socketcall *axs = (void *)aux;
- audit_log_format(ab, "nargs=%d", axs->nargs);
- for (i=0; i<axs->nargs; i++)
- audit_log_format(ab, " a%d=%lx", i, axs->args[i]);
- break; }
-
- case AUDIT_SOCKADDR: {
- struct audit_aux_data_sockaddr *axs = (void *)aux;
-
- audit_log_format(ab, "saddr=");
- audit_log_hex(ab, axs->a, axs->len);
- break; }
-
- case AUDIT_AVC_PATH: {
- struct audit_aux_data_path *axi = (void *)aux;
- audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
- break; }
-
- }
- audit_log_end(ab);
- }
-
- if (context->pwd && context->pwdmnt) {
- ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
- if (ab) {
- audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
- audit_log_end(ab);
- }
- }
- for (i = 0; i < context->name_count; i++) {
- unsigned long ino = context->names[i].ino;
- unsigned long pino = context->names[i].pino;
-
- ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
- if (!ab)
- continue; /* audit_panic has been called */
-
- audit_log_format(ab, "item=%d", i);
-
- audit_log_format(ab, " name=");
- if (context->names[i].name)
- audit_log_untrustedstring(ab, context->names[i].name);
- else
- audit_log_format(ab, "(null)");
-
- if (pino != (unsigned long)-1)
- audit_log_format(ab, " parent=%lu", pino);
- if (ino != (unsigned long)-1)
- audit_log_format(ab, " inode=%lu", ino);
- if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
- audit_log_format(ab, " dev=%02x:%02x mode=%#o"
- " ouid=%u ogid=%u rdev=%02x:%02x",
- MAJOR(context->names[i].dev),
- MINOR(context->names[i].dev),
- context->names[i].mode,
- context->names[i].uid,
- context->names[i].gid,
- MAJOR(context->names[i].rdev),
- MINOR(context->names[i].rdev));
- if (context->names[i].osid != 0) {
- char *ctx = NULL;
- u32 len;
- if (selinux_ctxid_to_string(
- context->names[i].osid, &ctx, &len)) {
- audit_log_format(ab, " osid=%u",
- context->names[i].osid);
- call_panic = 2;
- } else
- audit_log_format(ab, " obj=%s", ctx);
- kfree(ctx);
- }
-
- audit_log_end(ab);
- }
- if (call_panic)
- audit_panic("error converting sid to string");
-}
-
-/**
- * audit_free - free a per-task audit context
- * @tsk: task whose audit context block to free
- *
- * Called from copy_process and do_exit
- */
-void audit_free(struct task_struct *tsk)
-{
- struct audit_context *context;
-
- context = audit_get_context(tsk, 0, 0);
- if (likely(!context))
- return;
-
- /* Check for system calls that do not go through the exit
- * function (e.g., exit_group), then free context block.
- * We use GFP_ATOMIC here because we might be doing this
- * in the context of the idle thread */
- /* that can happen only if we are called from do_exit() */
- if (context->in_syscall && context->auditable)
- audit_log_exit(context, tsk);
-
- audit_free_context(context);
-}
-
-/**
- * audit_syscall_entry - fill in an audit record at syscall entry
- * @tsk: task being audited
- * @arch: architecture type
- * @major: major syscall type (function)
- * @a1: additional syscall register 1
- * @a2: additional syscall register 2
- * @a3: additional syscall register 3
- * @a4: additional syscall register 4
- *
- * Fill in audit context at syscall entry. This only happens if the
- * audit context was created when the task was created and the state or
- * filters demand the audit context be built. If the state from the
- * per-task filter or from the per-syscall filter is AUDIT_RECORD_CONTEXT,
- * then the record will be written at syscall exit time (otherwise, it
- * will only be written if another part of the kernel requests that it
- * be written).
- */
-void audit_syscall_entry(int arch, int major,
- unsigned long a1, unsigned long a2,
- unsigned long a3, unsigned long a4)
-{
- struct task_struct *tsk = current;
- struct audit_context *context = tsk->audit_context;
- enum audit_state state;
-
- BUG_ON(!context);
-
- /*
- * This happens only on certain architectures that make system
- * calls in kernel_thread via the entry.S interface, instead of
- * with direct calls. (If you are porting to a new
- * architecture, hitting this condition can indicate that you
- * got the _exit/_leave calls backward in entry.S.)
- *
- * i386 no
- * x86_64 no
- * ppc64 yes (see arch/powerpc/platforms/iseries/misc.S)
- *
- * This also happens with vm86 emulation in a non-nested manner
- * (entries without exits), so this case must be caught.
- */
- if (context->in_syscall) {
- struct audit_context *newctx;
-
-#if AUDIT_DEBUG
- printk(KERN_ERR
- "audit(:%d) pid=%d in syscall=%d;"
- " entering syscall=%d\n",
- context->serial, tsk->pid, context->major, major);
-#endif
- newctx = audit_alloc_context(context->state);
- if (newctx) {
- newctx->previous = context;
- context = newctx;
- tsk->audit_context = newctx;
- } else {
- /* If we can't alloc a new context, the best we
- * can do is to leak memory (any pending putname
- * will be lost). The only other alternative is
- * to abandon auditing. */
- audit_zero_context(context, context->state);
- }
- }
- BUG_ON(context->in_syscall || context->name_count);
-
- if (!audit_enabled)
- return;
-
- context->arch = arch;
- context->major = major;
- context->argv[0] = a1;
- context->argv[1] = a2;
- context->argv[2] = a3;
- context->argv[3] = a4;
-
- state = context->state;
- if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)
- state = audit_filter_syscall(tsk, context,
&audit_filter_list[AUDIT_FILTER_ENTRY]);
- if (likely(state == AUDIT_DISABLED))
- return;
-
- context->serial = 0;
- context->ctime = CURRENT_TIME;
- context->in_syscall = 1;
- context->auditable = !!(state == AUDIT_RECORD_CONTEXT);
-}
-
-/**
- * audit_syscall_exit - deallocate audit context after a system call
- * @tsk: task being audited
- * @valid: success/failure flag
- * @return_code: syscall return value
- *
- * Tear down after system call. If the audit context has been marked as
- * auditable (either because of the AUDIT_RECORD_CONTEXT state from
- * filtering, or because some other part of the kernel write an audit
- * message), then write out the syscall information. In call cases,
- * free the names stored from getname().
- */
-void audit_syscall_exit(int valid, long return_code)
-{
- struct task_struct *tsk = current;
- struct audit_context *context;
-
- context = audit_get_context(tsk, valid, return_code);
-
- if (likely(!context))
- return;
-
- if (context->in_syscall && context->auditable)
- audit_log_exit(context, tsk);
-
- context->in_syscall = 0;
- context->auditable = 0;
-
- if (context->previous) {
- struct audit_context *new_context = context->previous;
- context->previous = NULL;
- audit_free_context(context);
- tsk->audit_context = new_context;
- } else {
- audit_free_names(context);
- audit_free_aux(context);
- tsk->audit_context = context;
- }
-}
-
-/**
- * audit_getname - add a name to the list
- * @name: name to add
- *
- * Add a name to the list of audit names for this context.
- * Called from fs/namei.c:getname().
- */
-void audit_getname(const char *name)
-{
- struct audit_context *context = current->audit_context;
-
- if (!context || IS_ERR(name) || !name)
- return;
-
- if (!context->in_syscall) {
-#if AUDIT_DEBUG == 2
- printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n",
- __FILE__, __LINE__, context->serial, name);
- dump_stack();
-#endif
- return;
- }
- BUG_ON(context->name_count >= AUDIT_NAMES);
- context->names[context->name_count].name = name;
- context->names[context->name_count].ino = (unsigned long)-1;
- ++context->name_count;
- if (!context->pwd) {
- read_lock(¤t->fs->lock);
- context->pwd = dget(current->fs->pwd);
- context->pwdmnt = mntget(current->fs->pwdmnt);
- read_unlock(¤t->fs->lock);
- }
-
-}
-
-/* audit_putname - intercept a putname request
- * @name: name to intercept and delay for putname
- *
- * If we have stored the name from getname in the audit context,
- * then we delay the putname until syscall exit.
- * Called from include/linux/fs.h:putname().
- */
-void audit_putname(const char *name)
-{
- struct audit_context *context = current->audit_context;
-
- BUG_ON(!context);
- if (!context->in_syscall) {
-#if AUDIT_DEBUG == 2
- printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n",
- __FILE__, __LINE__, context->serial, name);
- if (context->name_count) {
- int i;
- for (i = 0; i < context->name_count; i++)
- printk(KERN_ERR "name[%d] = %p = %s\n", i,
- context->names[i].name,
- context->names[i].name ?: "(null)");
- }
-#endif
- __putname(name);
- }
-#if AUDIT_DEBUG
- else {
- ++context->put_count;
- if (context->put_count > context->name_count) {
- printk(KERN_ERR "%s:%d(:%d): major=%d"
- " in_syscall=%d putname(%p) name_count=%d"
- " put_count=%d\n",
- __FILE__, __LINE__,
- context->serial, context->major,
- context->in_syscall, name, context->name_count,
- context->put_count);
- dump_stack();
- }
- }
-#endif
-}
-
-static void audit_inode_context(int idx, const struct inode *inode)
-{
- struct audit_context *context = current->audit_context;
-
- selinux_get_inode_sid(inode, &context->names[idx].osid);
-}
-
-
-/**
- * audit_inode - store the inode and device from a lookup
- * @name: name being audited
- * @inode: inode being audited
- * @flags: lookup flags (as used in path_lookup())
- *
- * Called from fs/namei.c:path_lookup().
- */
-void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
-{
- int idx;
- struct audit_context *context = current->audit_context;
-
- if (!context->in_syscall)
- return;
- if (context->name_count
- && context->names[context->name_count-1].name
- && context->names[context->name_count-1].name == name)
- idx = context->name_count - 1;
- else if (context->name_count > 1
- && context->names[context->name_count-2].name
- && context->names[context->name_count-2].name == name)
- idx = context->name_count - 2;
- else {
- /* FIXME: how much do we care about inodes that have no
- * associated name? */
- if (context->name_count >= AUDIT_NAMES - AUDIT_NAMES_RESERVED)
- return;
- idx = context->name_count++;
- context->names[idx].name = NULL;
-#if AUDIT_DEBUG
- ++context->ino_count;
-#endif
- }
- context->names[idx].dev = inode->i_sb->s_dev;
- context->names[idx].mode = inode->i_mode;
- context->names[idx].uid = inode->i_uid;
- context->names[idx].gid = inode->i_gid;
- context->names[idx].rdev = inode->i_rdev;
- audit_inode_context(idx, inode);
- if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) &&
- (strcmp(name, ".") != 0)) {
- context->names[idx].ino = (unsigned long)-1;
- context->names[idx].pino = inode->i_ino;
- } else {
- context->names[idx].ino = inode->i_ino;
- context->names[idx].pino = (unsigned long)-1;
- }
-}
-
-/**
- * audit_inode_child - collect inode info for created/removed objects
- * @dname: inode's dentry name
- * @inode: inode being audited
- * @pino: inode number of dentry parent
- *
- * For syscalls that create or remove filesystem objects, audit_inode
- * can only collect information for the filesystem object's parent.
- * This call updates the audit context with the child's information.
- * Syscalls that create a new filesystem object must be hooked after
- * the object is created. Syscalls that remove a filesystem object
- * must be hooked prior, in order to capture the target inode during
- * unsuccessful attempts.
- */
-void __audit_inode_child(const char *dname, const struct inode *inode,
- unsigned long pino)
-{
- int idx;
- struct audit_context *context = current->audit_context;
-
- if (!context->in_syscall)
- return;
-
- /* determine matching parent */
- 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 (!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;
- context->names[idx].pino = pino;
-#if AUDIT_DEBUG
- context->ino_count++;
-#endif
-
-update_context:
- if (inode) {
- context->names[idx].ino = inode->i_ino;
- context->names[idx].dev = inode->i_sb->s_dev;
- context->names[idx].mode = inode->i_mode;
- context->names[idx].uid = inode->i_uid;
- context->names[idx].gid = inode->i_gid;
- context->names[idx].rdev = inode->i_rdev;
- audit_inode_context(idx, inode);
- }
-}
-
-/**
- * auditsc_get_stamp - get local copies of audit_context values
- * @ctx: audit_context for the task
- * @t: timespec to store time recorded in the audit_context
- * @serial: serial value that is recorded in the audit_context
- *
- * Also sets the context as auditable.
- */
-void auditsc_get_stamp(struct audit_context *ctx,
- struct timespec *t, unsigned int *serial)
-{
- if (!ctx->serial)
- ctx->serial = audit_serial();
- t->tv_sec = ctx->ctime.tv_sec;
- t->tv_nsec = ctx->ctime.tv_nsec;
- *serial = ctx->serial;
- ctx->auditable = 1;
-}
-
-/**
- * audit_set_loginuid - set a task's audit_context loginuid
- * @task: task whose audit context is being modified
- * @loginuid: loginuid value
- *
- * Returns 0.
- *
- * Called (set) from fs/proc/base.c::proc_loginuid_write().
- */
-int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
-{
- if (task->audit_context) {
- struct audit_buffer *ab;
-
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
- if (ab) {
- audit_log_format(ab, "login pid=%d uid=%u "
- "old auid=%u new auid=%u",
- task->pid, task->uid,
- task->audit_context->loginuid, loginuid);
- audit_log_end(ab);
- }
- task->audit_context->loginuid = loginuid;
- }
- return 0;
-}
-
-/**
- * audit_get_loginuid - get the loginuid for an audit_context
- * @ctx: the audit_context
- *
- * Returns the context's loginuid or -1 if @ctx is NULL.
- */
-uid_t audit_get_loginuid(struct audit_context *ctx)
-{
- return ctx ? ctx->loginuid : -1;
-}
-
-/**
- * audit_ipc_obj - record audit data for ipc object
- * @ipcp: ipc permissions
- *
- * Returns 0 for success or NULL context or < 0 on error.
- */
-int audit_ipc_obj(struct kern_ipc_perm *ipcp)
-{
- struct audit_aux_data_ipcctl *ax;
- struct audit_context *context = current->audit_context;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
- if (!ax)
- return -ENOMEM;
-
- ax->uid = ipcp->uid;
- ax->gid = ipcp->gid;
- ax->mode = ipcp->mode;
- selinux_get_ipc_sid(ipcp, &ax->osid);
-
- ax->d.type = AUDIT_IPC;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-/**
- * audit_ipc_set_perm - record audit data for new ipc permissions
- * @qbytes: msgq bytes
- * @uid: msgq user id
- * @gid: msgq group id
- * @mode: msgq mode (permissions)
- *
- * Returns 0 for success or NULL context or < 0 on error.
- */
-int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct
kern_ipc_perm *ipcp)
-{
- struct audit_aux_data_ipcctl *ax;
- struct audit_context *context = current->audit_context;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
- if (!ax)
- return -ENOMEM;
-
- ax->qbytes = qbytes;
- ax->uid = uid;
- ax->gid = gid;
- ax->mode = mode;
- selinux_get_ipc_sid(ipcp, &ax->osid);
-
- ax->d.type = AUDIT_IPC_SET_PERM;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-int audit_bprm(struct linux_binprm *bprm)
-{
- struct audit_aux_data_execve *ax;
- struct audit_context *context = current->audit_context;
- unsigned long p, next;
- void *to;
-
- if (likely(!audit_enabled || !context))
- return 0;
-
- ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p,
- GFP_KERNEL);
- if (!ax)
- return -ENOMEM;
-
- ax->argc = bprm->argc;
- ax->envc = bprm->envc;
- for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) {
- struct page *page = bprm->page[p / PAGE_SIZE];
- void *kaddr = kmap(page);
- next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1);
- memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p);
- to += next - p;
- kunmap(page);
- }
-
- ax->d.type = AUDIT_EXECVE;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-
-/**
- * audit_socketcall - record audit data for sys_socketcall
- * @nargs: number of args
- * @args: args array
- *
- * Returns 0 for success or NULL context or < 0 on error.
- */
-int audit_socketcall(int nargs, unsigned long *args)
-{
- struct audit_aux_data_socketcall *ax;
- struct audit_context *context = current->audit_context;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax) + nargs * sizeof(unsigned long), GFP_KERNEL);
- if (!ax)
- return -ENOMEM;
-
- ax->nargs = nargs;
- memcpy(ax->args, args, nargs * sizeof(unsigned long));
-
- ax->d.type = AUDIT_SOCKETCALL;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-/**
- * audit_sockaddr - record audit data for sys_bind, sys_connect, sys_sendto
- * @len: data length in user space
- * @a: data address in kernel space
- *
- * Returns 0 for success or NULL context or < 0 on error.
- */
-int audit_sockaddr(int len, void *a)
-{
- struct audit_aux_data_sockaddr *ax;
- struct audit_context *context = current->audit_context;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax) + len, GFP_KERNEL);
- if (!ax)
- return -ENOMEM;
-
- ax->len = len;
- memcpy(ax->a, a, len);
-
- ax->d.type = AUDIT_SOCKADDR;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-/**
- * audit_avc_path - record the granting or denial of permissions
- * @dentry: dentry to record
- * @mnt: mnt to record
- *
- * Returns 0 for success or NULL context or < 0 on error.
- *
- * Called from security/selinux/avc.c::avc_audit()
- */
-int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
-{
- struct audit_aux_data_path *ax;
- struct audit_context *context = current->audit_context;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
- if (!ax)
- return -ENOMEM;
-
- ax->dentry = dget(dentry);
- ax->mnt = mntget(mnt);
-
- ax->d.type = AUDIT_AVC_PATH;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-/**
- * audit_signal_info - record signal info for shutting down audit subsystem
- * @sig: signal value
- * @t: task being signaled
- *
- * If the audit subsystem is being terminated, record the task (pid)
- * and uid that is doing that.
- */
-void audit_signal_info(int sig, struct task_struct *t)
-{
- extern pid_t audit_sig_pid;
- extern uid_t audit_sig_uid;
-
- if (unlikely(audit_pid && t->tgid == audit_pid)) {
- if (sig == SIGTERM || sig == SIGHUP) {
- struct audit_context *ctx = current->audit_context;
- audit_sig_pid = current->pid;
- if (ctx)
- audit_sig_uid = ctx->loginuid;
- else
- audit_sig_uid = current->uid;
- }
- }
-}
diff --git a/kernel/audit/Makefile b/kernel/audit/Makefile
new file mode 100644
index 0000000..69234c5
--- /dev/null
+++ b/kernel/audit/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the audit subsystem.
+#
+obj-$(CONFIG_AUDIT) += audit.o filter.o
+obj-$(CONFIG_AUDITSYSCALL) += syscall.o
diff --git a/kernel/audit/audit.c b/kernel/audit/audit.c
new file mode 100644
index 0000000..7637410
--- /dev/null
+++ b/kernel/audit/audit.c
@@ -0,0 +1,1133 @@
+/* audit.c -- Auditing support
+ * Gateway between the kernel (e.g., selinux) and the user-space audit daemon.
+ * System-call specific features have moved to auditsc.c
+ *
+ * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * 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
+ *
+ * Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
+ *
+ * Goals: 1) Integrate fully with SELinux.
+ * 2) Minimal run-time overhead:
+ * a) Minimal when syscall auditing is disabled (audit_enable=0).
+ * b) Small when syscall auditing is enabled and no audit record
+ * is generated (defer as much work as possible to record
+ * generation time):
+ * i) context is allocated,
+ * ii) names from getname are stored without a copy, and
+ * iii) inode information stored from path_lookup.
+ * 3) Ability to disable syscall auditing at boot time (audit=0).
+ * 4) Usable by other parts of the kernel (if audit_log* is called,
+ * then a syscall record will be generated automatically for the
+ * current syscall).
+ * 5) Netlink interface to user-space.
+ * 6) Support low-overhead kernel-based filtering to minimize the
+ * information that must be passed to user-space.
+ *
+ * Example user-space utilities:
http://people.redhat.com/sgrubb/audit/
+ */
+
+#include <linux/init.h>
+#include <asm/types.h>
+#include <asm/atomic.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kthread.h>
+
+#include <linux/audit.h>
+
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/selinux.h>
+#include <linux/inotify.h>
+
+#include "audit.h"
+
+/* No auditing will take place until audit_initialized != 0.
+ * (Initialization happens after skb_init is called.) */
+static int audit_initialized;
+
+/* No syscall auditing will take place unless audit_enabled != 0. */
+int audit_enabled;
+
+/* Default state when kernel boots without any parameters. */
+static int audit_default;
+
+/* If auditing cannot proceed, audit_failure selects what happens. */
+static int audit_failure = AUDIT_FAIL_PRINTK;
+
+/* If audit records are to be written to the netlink socket, audit_pid
+ * contains the (non-zero) pid. */
+int audit_pid;
+
+/* 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;
+
+/* Number of outstanding audit_buffers allowed. */
+static int audit_backlog_limit = 64;
+static int audit_backlog_wait_time = 60 * HZ;
+static int audit_backlog_wait_overflow = 0;
+
+/* The identity of the user shutting down the audit system. */
+uid_t audit_sig_uid = -1;
+pid_t audit_sig_pid = -1;
+
+/* Records can be lost in several ways:
+ 0) [suppressed in audit_alloc]
+ 1) out of memory in audit_log_start [kmalloc of struct audit_buffer]
+ 2) out of memory in audit_log_move [alloc_skb]
+ 3) suppressed due to audit_rate_limit
+ 4) suppressed due to audit_backlog_limit
+*/
+static atomic_t audit_lost = ATOMIC_INIT(0);
+
+/* The netlink socket. */
+static struct sock *audit_sock;
+
+/* Inotify handle. */
+struct inotify_handle *audit_ih;
+
+/* 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). */
+static DEFINE_SPINLOCK(audit_freelist_lock);
+static int audit_freelist_count;
+static LIST_HEAD(audit_freelist);
+
+static struct sk_buff_head audit_skb_queue;
+static struct task_struct *kauditd_task;
+static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
+static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
+
+/* Serialize requests from userspace. */
+static DEFINE_MUTEX(audit_cmd_mutex);
+
+/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
+ * audit records. Since printk uses a 1024 byte buffer, this buffer
+ * should be at least that large. */
+#define AUDIT_BUFSIZ 1024
+
+/* AUDIT_MAXFREE is the number of empty audit_buffers we keep on the
+ * audit_freelist. Doing so eliminates many kmalloc/kfree calls. */
+#define AUDIT_MAXFREE (2*NR_CPUS)
+
+/* The audit_buffer is used when formatting an audit record. The caller
+ * locks briefly to get the record off the freelist or to allocate the
+ * buffer, and locks briefly to send the buffer to the netlink layer or
+ * to place it on a transmit queue. Multiple audit_buffers can be in
+ * use simultaneously. */
+struct audit_buffer {
+ struct list_head list;
+ struct sk_buff *skb; /* formatted skb ready to send */
+ struct audit_context *ctx; /* NULL or associated context */
+ gfp_t gfp_mask;
+};
+
+static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
+{
+ struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
+ nlh->nlmsg_pid = pid;
+}
+
+void audit_panic(const char *message)
+{
+ switch (audit_failure)
+ {
+ case AUDIT_FAIL_SILENT:
+ break;
+ case AUDIT_FAIL_PRINTK:
+ printk(KERN_ERR "audit: %s\n", message);
+ break;
+ case AUDIT_FAIL_PANIC:
+ panic("audit: %s\n", message);
+ break;
+ }
+}
+
+static inline int audit_rate_check(void)
+{
+ static unsigned long last_check = 0;
+ static int messages = 0;
+ static DEFINE_SPINLOCK(lock);
+ unsigned long flags;
+ unsigned long now;
+ unsigned long elapsed;
+ int retval = 0;
+
+ if (!audit_rate_limit) return 1;
+
+ spin_lock_irqsave(&lock, flags);
+ if (++messages < audit_rate_limit) {
+ retval = 1;
+ } else {
+ now = jiffies;
+ elapsed = now - last_check;
+ if (elapsed > HZ) {
+ last_check = now;
+ messages = 0;
+ retval = 1;
+ }
+ }
+ spin_unlock_irqrestore(&lock, flags);
+
+ return retval;
+}
+
+/**
+ * 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;
+ static DEFINE_SPINLOCK(lock);
+ unsigned long flags;
+ unsigned long now;
+ int print;
+
+ atomic_inc(&audit_lost);
+
+ print = (audit_failure == AUDIT_FAIL_PANIC || !audit_rate_limit);
+
+ if (!print) {
+ spin_lock_irqsave(&lock, flags);
+ now = jiffies;
+ if (now - last_msg > HZ) {
+ print = 1;
+ last_msg = now;
+ }
+ spin_unlock_irqrestore(&lock, flags);
+ }
+
+ if (print) {
+ printk(KERN_WARNING
+ "audit: audit_lost=%d audit_rate_limit=%d audit_backlog_limit=%d\n",
+ atomic_read(&audit_lost),
+ audit_rate_limit,
+ audit_backlog_limit);
+ audit_panic(message);
+ }
+}
+
+static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
+{
+ int old = audit_rate_limit;
+
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ int rc;
+ if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
+ return rc;
+ else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_rate_limit=%d old=%d by auid=%u subj=%s",
+ limit, old, loginuid, ctx);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_rate_limit=%d old=%d by auid=%u",
+ limit, old, loginuid);
+ audit_rate_limit = limit;
+ return old;
+}
+
+static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
+{
+ int old = audit_backlog_limit;
+
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ int rc;
+ if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
+ return rc;
+ else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_backlog_limit=%d old=%d by auid=%u subj=%s",
+ limit, old, loginuid, ctx);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_backlog_limit=%d old=%d by auid=%u",
+ limit, old, loginuid);
+ audit_backlog_limit = limit;
+ return old;
+}
+
+static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
+{
+ int old = audit_enabled;
+
+ if (state != 0 && state != 1)
+ return -EINVAL;
+
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ int rc;
+ if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
+ return rc;
+ else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_enabled=%d old=%d by auid=%u subj=%s",
+ state, old, loginuid, ctx);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_enabled=%d old=%d by auid=%u",
+ state, old, loginuid);
+ audit_enabled = state;
+ return old;
+}
+
+static int audit_set_failure(int state, uid_t loginuid, u32 sid)
+{
+ int old = audit_failure;
+
+ if (state != AUDIT_FAIL_SILENT
+ && state != AUDIT_FAIL_PRINTK
+ && state != AUDIT_FAIL_PANIC)
+ return -EINVAL;
+
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ int rc;
+ if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
+ return rc;
+ else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_failure=%d old=%d by auid=%u subj=%s",
+ state, old, loginuid, ctx);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_failure=%d old=%d by auid=%u",
+ state, old, loginuid);
+ audit_failure = state;
+ return old;
+}
+
+static int kauditd_thread(void *dummy)
+{
+ struct sk_buff *skb;
+
+ while (1) {
+ skb = skb_dequeue(&audit_skb_queue);
+ wake_up(&audit_backlog_wait);
+ if (skb) {
+ if (audit_pid) {
+ int err = netlink_unicast(audit_sock, skb, audit_pid, 0);
+ if (err < 0) {
+ BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */
+ printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
+ audit_pid = 0;
+ }
+ } else {
+ printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0));
+ kfree_skb(skb);
+ }
+ } else {
+ DECLARE_WAITQUEUE(wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&kauditd_wait, &wait);
+
+ if (!skb_queue_len(&audit_skb_queue)) {
+ try_to_freeze();
+ schedule();
+ }
+
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&kauditd_wait, &wait);
+ }
+ }
+ return 0;
+}
+
+int audit_send_list(void *_dest)
+{
+ struct audit_netlink_list *dest = _dest;
+ int pid = dest->pid;
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&dest->q)) != NULL)
+ netlink_unicast(audit_sock, skb, pid, 0);
+
+ kfree(dest);
+
+ return 0;
+}
+
+struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
+ int multi, void *payload, int size)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int len = NLMSG_SPACE(size);
+ void *data;
+ int flags = multi ? NLM_F_MULTI : 0;
+ int t = done ? NLMSG_DONE : type;
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return NULL;
+
+ nlh = NLMSG_PUT(skb, pid, seq, t, size);
+ nlh->nlmsg_flags = flags;
+ data = NLMSG_DATA(nlh);
+ memcpy(data, payload, size);
+ return skb;
+
+nlmsg_failure: /* Used by NLMSG_PUT */
+ if (skb)
+ kfree_skb(skb);
+ return NULL;
+}
+
+/**
+ * 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)
+{
+ struct sk_buff *skb;
+ skb = audit_make_reply(pid, seq, type, done, multi, payload, size);
+ if (!skb)
+ return;
+ /* Ignore failure. It'll only happen if the sender goes away,
+ because our timeout is set to infinite. */
+ netlink_unicast(audit_sock, skb, pid, 0);
+ return;
+}
+
+/*
+ * Check for appropriate CAP_AUDIT_ capabilities on incoming audit
+ * control messages.
+ */
+static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
+{
+ int err = 0;
+
+ 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;
+ default: /* bad msg */
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ u32 uid, pid, seq, sid;
+ void *data;
+ struct audit_status *status_get, status_set;
+ int err;
+ struct audit_buffer *ab;
+ u16 msg_type = nlh->nlmsg_type;
+ uid_t loginuid; /* loginuid of sender */
+ struct audit_sig_info sig_data;
+
+ err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
+ if (err)
+ return err;
+
+ /* 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)) {
+ err = PTR_ERR(kauditd_task);
+ kauditd_task = NULL;
+ return err;
+ }
+
+ pid = NETLINK_CREDS(skb)->pid;
+ uid = NETLINK_CREDS(skb)->uid;
+ loginuid = NETLINK_CB(skb).loginuid;
+ sid = NETLINK_CB(skb).sid;
+ seq = nlh->nlmsg_seq;
+ data = NLMSG_DATA(nlh);
+
+ switch (msg_type) {
+ case AUDIT_GET:
+ status_set.enabled = audit_enabled;
+ status_set.failure = audit_failure;
+ status_set.pid = audit_pid;
+ status_set.rate_limit = audit_rate_limit;
+ status_set.backlog_limit = audit_backlog_limit;
+ status_set.lost = atomic_read(&audit_lost);
+ status_set.backlog = skb_queue_len(&audit_skb_queue);
+ audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0,
+ &status_set, sizeof(status_set));
+ break;
+ case AUDIT_SET:
+ if (nlh->nlmsg_len < sizeof(struct audit_status))
+ return -EINVAL;
+ status_get = (struct audit_status *)data;
+ if (status_get->mask & AUDIT_STATUS_ENABLED) {
+ err = audit_set_enabled(status_get->enabled,
+ loginuid, sid);
+ if (err < 0) return err;
+ }
+ if (status_get->mask & AUDIT_STATUS_FAILURE) {
+ err = audit_set_failure(status_get->failure,
+ loginuid, sid);
+ if (err < 0) return err;
+ }
+ if (status_get->mask & AUDIT_STATUS_PID) {
+ int old = audit_pid;
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ int rc;
+ if ((rc = selinux_ctxid_to_string(
+ sid, &ctx, &len)))
+ return rc;
+ else
+ audit_log(NULL, GFP_KERNEL,
+ AUDIT_CONFIG_CHANGE,
+ "audit_pid=%d old=%d by auid=%u subj=%s",
+ status_get->pid, old,
+ loginuid, ctx);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit_pid=%d old=%d by auid=%u",
+ status_get->pid, old, loginuid);
+ audit_pid = status_get->pid;
+ }
+ if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
+ audit_set_rate_limit(status_get->rate_limit,
+ loginuid, sid);
+ if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
+ audit_set_backlog_limit(status_get->backlog_limit,
+ loginuid, sid);
+ 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;
+
+ err = audit_filter_user(&NETLINK_CB(skb), msg_type);
+ if (err == 1) {
+ err = 0;
+ ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
+ if (ab) {
+ audit_log_format(ab,
+ "user pid=%d uid=%u auid=%u",
+ pid, uid, loginuid);
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ if (selinux_ctxid_to_string(
+ sid, &ctx, &len)) {
+ audit_log_format(ab,
+ " ssid=%u", sid);
+ /* Maybe call audit_panic? */
+ } else
+ audit_log_format(ab,
+ " subj=%s", ctx);
+ kfree(ctx);
+ }
+ audit_log_format(ab, " msg='%.1024s'",
+ (char *)data);
+ audit_set_pid(ab, pid);
+ audit_log_end(ab);
+ }
+ }
+ break;
+ case AUDIT_ADD:
+ case AUDIT_DEL:
+ 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, nlmsg_len(nlh),
+ loginuid, sid);
+ 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, sid);
+ break;
+ case AUDIT_SIGNAL_INFO:
+ sig_data.uid = audit_sig_uid;
+ sig_data.pid = audit_sig_pid;
+ audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO,
+ 0, 0, &sig_data, sizeof(sig_data));
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err < 0 ? err : 0;
+}
+
+/*
+ * 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.
+ */
+static void audit_receive_skb(struct sk_buff *skb)
+{
+ int err;
+ struct nlmsghdr *nlh;
+ u32 rlen;
+
+ while (skb->len >= NLMSG_SPACE(0)) {
+ nlh = (struct nlmsghdr *)skb->data;
+ if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+ return;
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > skb->len)
+ rlen = skb->len;
+ if ((err = audit_receive_msg(skb, nlh))) {
+ netlink_ack(skb, nlh, err);
+ } else if (nlh->nlmsg_flags & NLM_F_ACK)
+ netlink_ack(skb, nlh, 0);
+ skb_pull(skb, rlen);
+ }
+}
+
+/* Receive messages from netlink socket. */
+static void audit_receive(struct sock *sk, int length)
+{
+ struct sk_buff *skb;
+ unsigned int qlen;
+
+ mutex_lock(&audit_cmd_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);
+ }
+ mutex_unlock(&audit_cmd_mutex);
+}
+
+
+/* Initialize audit support at boot time. */
+static int __init audit_init(void)
+{
+ printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
+ audit_default ? "enabled" : "disabled");
+ audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
+ THIS_MODULE);
+ if (!audit_sock)
+ audit_panic("cannot initialize netlink socket");
+ else
+ audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
+
+ skb_queue_head_init(&audit_skb_queue);
+ audit_initialized = 1;
+ audit_enabled = audit_default;
+
+ /* Register the callback with selinux. This callback will be invoked
+ * when a new policy is loaded. */
+ selinux_audit_set_callback(&selinux_audit_rule_update);
+
+ audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
+
+#ifdef CONFIG_AUDITSYSCALL
+ audit_ih = inotify_init(audit_handle_ievent);
+ if (IS_ERR(audit_ih))
+ audit_panic("cannot initialize inotify handle");
+#endif
+
+ return 0;
+}
+__initcall(audit_init);
+
+/* Process kernel command-line parameter at boot time. audit=0 or audit=1. */
+static int __init audit_enable(char *str)
+{
+ audit_default = !!simple_strtol(str, NULL, 0);
+ printk(KERN_INFO "audit: %s%s\n",
+ audit_default ? "enabled" : "disabled",
+ audit_initialized ? "" : " (after initialization)");
+ if (audit_initialized)
+ audit_enabled = audit_default;
+ return 1;
+}
+
+__setup("audit=", audit_enable);
+
+static void audit_buffer_free(struct audit_buffer *ab)
+{
+ unsigned long flags;
+
+ if (!ab)
+ return;
+
+ if (ab->skb)
+ kfree_skb(ab->skb);
+
+ spin_lock_irqsave(&audit_freelist_lock, flags);
+ if (++audit_freelist_count > AUDIT_MAXFREE)
+ kfree(ab);
+ else
+ list_add(&ab->list, &audit_freelist);
+ spin_unlock_irqrestore(&audit_freelist_lock, flags);
+}
+
+static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
+ gfp_t gfp_mask, int type)
+{
+ unsigned long flags;
+ struct audit_buffer *ab = NULL;
+ struct nlmsghdr *nlh;
+
+ spin_lock_irqsave(&audit_freelist_lock, flags);
+ if (!list_empty(&audit_freelist)) {
+ ab = list_entry(audit_freelist.next,
+ struct audit_buffer, list);
+ list_del(&ab->list);
+ --audit_freelist_count;
+ }
+ spin_unlock_irqrestore(&audit_freelist_lock, flags);
+
+ if (!ab) {
+ ab = kmalloc(sizeof(*ab), gfp_mask);
+ if (!ab)
+ goto err;
+ }
+
+ ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask);
+ if (!ab->skb)
+ goto err;
+
+ ab->ctx = ctx;
+ ab->gfp_mask = gfp_mask;
+ nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0));
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = 0;
+ nlh->nlmsg_pid = 0;
+ nlh->nlmsg_seq = 0;
+ return ab;
+err:
+ audit_buffer_free(ab);
+ return NULL;
+}
+
+/**
+ * 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
+ * determine which pieces belong to the same audit record. The
+ * (timestamp,serial) tuple is unique for each syscall and is live from
+ * syscall entry to syscall exit.
+ *
+ * NOTE: Another possibility is to store the formatted records off the
+ * 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).
+ */
+unsigned int audit_serial(void)
+{
+ static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED;
+ static unsigned int serial = 0;
+
+ unsigned long flags;
+ unsigned int ret;
+
+ spin_lock_irqsave(&serial_lock, flags);
+ do {
+ ret = ++serial;
+ } while (unlikely(!ret));
+ spin_unlock_irqrestore(&serial_lock, flags);
+
+ return ret;
+}
+
+static inline void audit_get_stamp(struct audit_context *ctx,
+ struct timespec *t, unsigned int *serial)
+{
+ if (ctx)
+ auditsc_get_stamp(ctx, t, serial);
+ else {
+ *t = CURRENT_TIME;
+ *serial = audit_serial();
+ }
+}
+
+/* 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 tsk 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, 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)
+{
+ struct audit_buffer *ab = NULL;
+ struct timespec t;
+ unsigned int serial;
+ int reserve;
+ unsigned long timeout_start = jiffies;
+
+ if (!audit_initialized)
+ return NULL;
+
+ if (unlikely(audit_filter_type(type)))
+ return NULL;
+
+ if (gfp_mask & __GFP_WAIT)
+ reserve = 0;
+ else
+ reserve = 5; /* Allow atomic callers to go up to five
+ entries over the normal backlog limit */
+
+ while (audit_backlog_limit
+ && skb_queue_len(&audit_skb_queue) > audit_backlog_limit +
reserve) {
+ if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time
+ && time_before(jiffies, timeout_start + audit_backlog_wait_time)) {
+
+ /* Wait for auditd to drain the queue a little */
+ DECLARE_WAITQUEUE(wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&audit_backlog_wait, &wait);
+
+ if (audit_backlog_limit &&
+ skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
+ schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies);
+
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&audit_backlog_wait, &wait);
+ continue;
+ }
+ if (audit_rate_check())
+ printk(KERN_WARNING
+ "audit: audit_backlog=%d > "
+ "audit_backlog_limit=%d\n",
+ skb_queue_len(&audit_skb_queue),
+ audit_backlog_limit);
+ audit_log_lost("backlog limit exceeded");
+ audit_backlog_wait_time = audit_backlog_wait_overflow;
+ wake_up(&audit_backlog_wait);
+ return NULL;
+ }
+
+ ab = audit_buffer_alloc(ctx, gfp_mask, type);
+ if (!ab) {
+ audit_log_lost("out of memory in audit_log_start");
+ return NULL;
+ }
+
+ audit_get_stamp(ab->ctx, &t, &serial);
+
+ audit_log_format(ab, "audit(%lu.%03lu:%u): ",
+ t.tv_sec, t.tv_nsec/1000000, serial);
+ return ab;
+}
+
+/**
+ * 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.
+ */
+static inline int audit_expand(struct audit_buffer *ab, int extra)
+{
+ struct sk_buff *skb = ab->skb;
+ int ret = pskb_expand_head(skb, skb_headroom(skb), extra,
+ ab->gfp_mask);
+ if (ret < 0) {
+ audit_log_lost("out of memory in audit_expand");
+ return 0;
+ }
+ return skb_tailroom(skb);
+}
+
+/*
+ * 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.
+ */
+static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
+ va_list args)
+{
+ int len, avail;
+ struct sk_buff *skb;
+ va_list args2;
+
+ if (!ab)
+ return;
+
+ BUG_ON(!ab->skb);
+ skb = ab->skb;
+ avail = skb_tailroom(skb);
+ if (avail == 0) {
+ avail = audit_expand(ab, AUDIT_BUFSIZ);
+ if (!avail)
+ goto out;
+ }
+ va_copy(args2, args);
+ len = vsnprintf(skb->tail, avail, fmt, args);
+ if (len >= avail) {
+ /* 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));
+ if (!avail)
+ goto out;
+ len = vsnprintf(skb->tail, avail, fmt, args2);
+ }
+ if (len > 0)
+ skb_put(skb, len);
+out:
+ return;
+}
+
+/**
+ * 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;
+
+ if (!ab)
+ return;
+ va_start(args, fmt);
+ audit_log_vformat(ab, fmt, args);
+ va_end(args);
+}
+
+/**
+ * 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;
+ unsigned char *ptr;
+ struct sk_buff *skb;
+ static const unsigned char *hex = "0123456789ABCDEF";
+
+ BUG_ON(!ab->skb);
+ skb = ab->skb;
+ avail = skb_tailroom(skb);
+ new_len = len<<1;
+ if (new_len >= avail) {
+ /* Round the buffer request up to the next multiple */
+ new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
+ avail = audit_expand(ab, new_len);
+ if (!avail)
+ return;
+ }
+
+ ptr = skb->tail;
+ for (i=0; i<len; i++) {
+ *ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */
+ *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */
+ }
+ *ptr = 0;
+ skb_put(skb, len << 1); /* new string is twice the old string */
+}
+
+/**
+ * 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).
+ */
+const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
+{
+ const unsigned char *p = string;
+ size_t len = strlen(string);
+
+ while (*p) {
+ if (*p == '"' || *p < 0x21 || *p > 0x7f) {
+ audit_log_hex(ab, string, len);
+ return string + len + 1;
+ }
+ p++;
+ }
+ audit_log_format(ab, "\"%s\"", string);
+ return p + 1;
+}
+
+/* This is a helper-function to print the escaped d_path */
+void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
+ struct dentry *dentry, struct vfsmount *vfsmnt)
+{
+ char *p, *path;
+
+ if (prefix)
+ audit_log_format(ab, " %s", prefix);
+
+ /* We will allow 11 spaces for ' (deleted)' to be appended */
+ path = kmalloc(PATH_MAX+11, ab->gfp_mask);
+ if (!path) {
+ audit_log_format(ab, "<no memory>");
+ return;
+ }
+ p = d_path(dentry, vfsmnt, path, PATH_MAX+11);
+ if (IS_ERR(p)) { /* Should never happen since we send PATH_MAX */
+ /* FIXME: can we save some information here? */
+ audit_log_format(ab, "<too long>");
+ } else
+ audit_log_untrustedstring(ab, p);
+ kfree(path);
+}
+
+/**
+ * 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.
+ */
+void audit_log_end(struct audit_buffer *ab)
+{
+ if (!ab)
+ return;
+ if (!audit_rate_check()) {
+ audit_log_lost("rate limit exceeded");
+ } else {
+ if (audit_pid) {
+ struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
+ nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
+ skb_queue_tail(&audit_skb_queue, ab->skb);
+ ab->skb = NULL;
+ wake_up_interruptible(&kauditd_wait);
+ } else {
+ printk(KERN_NOTICE "%s\n", ab->skb->data + NLMSG_SPACE(0));
+ }
+ }
+ audit_buffer_free(ab);
+}
+
+/**
+ * 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, ...)
+{
+ struct audit_buffer *ab;
+ va_list args;
+
+ ab = audit_log_start(ctx, gfp_mask, type);
+ if (ab) {
+ va_start(args, fmt);
+ audit_log_vformat(ab, fmt, args);
+ va_end(args);
+ audit_log_end(ab);
+ }
+}
+
+EXPORT_SYMBOL(audit_log_start);
+EXPORT_SYMBOL(audit_log_end);
+EXPORT_SYMBOL(audit_log_format);
+EXPORT_SYMBOL(audit_log);
diff --git a/kernel/audit/audit.h b/kernel/audit/audit.h
new file mode 100644
index 0000000..771833d
--- /dev/null
+++ b/kernel/audit/audit.h
@@ -0,0 +1,118 @@
+/* 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/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_parent;
+
+struct audit_watch {
+ atomic_t count; /* reference count */
+ char *path; /* insertion path */
+ dev_t dev; /* associated superblock device */
+ unsigned long ino; /* associated inode number */
+ struct audit_parent *parent; /* associated parent */
+ struct list_head wlist; /* entry in parent->watches list */
+ struct list_head rules; /* associated rules */
+};
+
+struct audit_field {
+ u32 type;
+ u32 val;
+ u32 op;
+ char *se_str;
+ struct selinux_audit_rule *se_rule;
+};
+
+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_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;
+};
+
+
+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 struct sk_buff * audit_make_reply(int pid, int seq, int type,
+ int done, int multi,
+ void *payload, int size);
+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);
+
+struct audit_netlink_list {
+ int pid;
+ struct sk_buff_head q;
+};
+
+int audit_send_list(void *);
+
+struct inotify_watch;
+extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
+ const char *, struct inode *);
+extern int selinux_audit_rule_update(void);
+
diff --git a/kernel/audit/filter.c b/kernel/audit/filter.c
new file mode 100644
index 0000000..35dca7e
--- /dev/null
+++ b/kernel/audit/filter.c
@@ -0,0 +1,1466 @@
+/* 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/mutex.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/netlink.h>
+#include <linux/inotify.h>
+#include <linux/selinux.h>
+#include "audit.h"
+
+/*
+ * Locking model:
+ *
+ * audit_filter_mutex:
+ * Synchronizes writes and blocking reads of audit's filterlist
+ * data. Rcu is used to traverse the filterlist and access
+ * contents of structs audit_entry, audit_watch and opaque
+ * selinux rules during filtering. If modified, these structures
+ * must be copied and replace their counterparts in the filterlist.
+ * An audit_parent struct is not accessed during filtering, so may
+ * be written directly provided audit_filter_mutex is held.
+ */
+
+/*
+ * Reference counting:
+ *
+ * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED
+ * event. Each audit_watch holds a reference to its associated parent.
+ *
+ * audit_watch: if added to lists, lifetime is from audit_init_watch() to one
+ * of: audit_remove_watch() [user removes], audit_update_watch() [kernel
+ * replaces], or audit_remove_parent_watches() [kernel removes].
+ * Additionally, an audit_watch may exist temporarily to assist in
+ * searching existing filter data. Each audit_krule holds a reference to
+ * its associated watch.
+ */
+
+struct audit_parent {
+ atomic_t count; /* reference count */
+ struct list_head ilist; /* entry in inotify registration list */
+ struct list_head watches; /* associated watches */
+ struct inotify_watch wdata; /* inotify watch data */
+ unsigned flags; /* status flags */
+};
+
+/*
+ * audit_parent status flags:
+ *
+ * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
+ * a filesystem event. Technically not needed for IN_DELETE_SELF or IN_UNMOUNT
+ * events, as we cannot receive them while we have nameidata (during rule add)
+ * and the audit_parent is immediately removed when processing the following
+ * IN_IGNORED event. The IN_MOVE_SELF event is different. We can receive it
+ * while holding nameidata, and inotify will not send us the IN_IGNORED so we
+ * must later remove the inotify watch on audit_parent ourselves.
+ */
+#define AUDIT_PARENT_INVALID 0x001
+
+/* Audit filter lists, defined in <linux/audit.h> */
+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
+};
+
+DEFINE_MUTEX(audit_filter_mutex);
+
+/* Inotify handle */
+extern struct inotify_handle *audit_ih;
+
+/* Inotify events we care about. */
+#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF
+#define AUDIT_IN_SELF IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT
+
+static inline void audit_get_parent(struct audit_parent *parent)
+{
+ atomic_inc(&parent->count);
+}
+
+static inline void audit_put_parent(struct audit_parent *parent)
+{
+ if (atomic_dec_and_test(&parent->count)) {
+ WARN_ON(!list_empty(&parent->watches));
+ kfree(parent);
+ }
+}
+
+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)) {
+ WARN_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)
+{
+ int i;
+
+ /* some rules don't have associated watches */
+ if (e->rule.watch)
+ audit_put_watch(e->rule.watch);
+ if (e->rule.fields)
+ for (i = 0; i < e->rule.field_count; i++) {
+ struct audit_field *f = &e->rule.fields[i];
+ kfree(f->se_str);
+ selinux_audit_rule_free(f->se_rule);
+ }
+ 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);
+}
+
+/* Initialize a parent watch entry. */
+static inline struct audit_parent *audit_init_parent(void)
+{
+ struct audit_parent *parent;
+
+ parent = kzalloc(sizeof(*parent), GFP_KERNEL);
+ if (unlikely(!parent))
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&parent->watches);
+ atomic_set(&parent->count, 1);
+ parent->flags = 0;
+
+ return parent;
+}
+
+/* Initialize a watch entry. */
+static inline struct audit_watch *audit_init_watch(char *path)
+{
+ struct audit_watch *watch;
+
+ watch = kzalloc(sizeof(*watch), GFP_KERNEL);
+ if (unlikely(!watch))
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&watch->rules);
+ atomic_set(&watch->count, 1);
+ watch->path = path;
+ watch->dev = (dev_t)-1;
+ watch->ino = (unsigned long)-1;
+
+ return watch;
+}
+
+/* Initialize an audit filterlist entry. */
+static inline struct audit_entry *audit_init_entry(u32 field_count)
+{
+ struct audit_entry *entry;
+ struct audit_field *fields;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (unlikely(!entry))
+ return NULL;
+
+ fields = kzalloc(sizeof(*fields) * field_count, GFP_KERNEL);
+ if (unlikely(!fields)) {
+ kfree(entry);
+ return NULL;
+ }
+ 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)
+{
+ 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;
+}
+
+/* Translate a watch string to kernel respresentation. */
+static int audit_to_watch(struct audit_krule *krule, char *path, int len,
+ u32 op)
+{
+ struct audit_watch *watch;
+
+ if (path[0] != '/' || path[len-1] == '/' ||
+ krule->listnr != AUDIT_FILTER_EXIT ||
+ op & ~AUDIT_EQUAL ||
+ krule->watch) /* allow only 1 watch per rule */
+ return -EINVAL;
+
+ /* ensure inotify handle was initialized */
+ if (!audit_ih)
+ return -EOPNOTSUPP;
+
+ watch = audit_init_watch(path);
+ if (unlikely(IS_ERR(watch)))
+ return PTR_ERR(watch);
+
+ audit_get_watch(watch);
+ krule->watch = watch;
+
+ 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;
+ 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 = audit_init_entry(rule->field_count);
+ if (!entry)
+ goto exit_err;
+
+ entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
+ entry->rule.listnr = listnr;
+ entry->rule.action = rule->action;
+ entry->rule.field_count = rule->field_count;
+
+ 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];
+
+ 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_SE_USER ||
+ f->type == AUDIT_SE_ROLE ||
+ f->type == AUDIT_SE_TYPE ||
+ f->type == AUDIT_SE_SEN ||
+ f->type == AUDIT_SE_CLR ||
+ f->type == AUDIT_WATCH) {
+ err = -EINVAL;
+ goto exit_free;
+ }
+
+ 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;
+ char *str;
+
+ 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];
+ f->val = data->values[i];
+ f->se_str = NULL;
+ f->se_rule = NULL;
+ switch(f->type) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ str = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(str))
+ goto exit_free;
+ entry->rule.buflen += f->val;
+
+ err = selinux_audit_rule_init(f->type, f->op, str,
+ &f->se_rule);
+ /* Keep currently invalid fields around in case they
+ * become valid after a policy reload. */
+ if (err == -EINVAL) {
+ printk(KERN_WARNING "audit rule for selinux "
+ "\'%s\' is invalid\n", str);
+ err = 0;
+ }
+ if (err) {
+ kfree(str);
+ goto exit_free;
+ } else
+ f->se_str = str;
+ break;
+ case AUDIT_WATCH:
+ str = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(str))
+ goto exit_free;
+ entry->rule.buflen += f->val;
+
+ err = audit_to_watch(&entry->rule, str, f->val, f->op);
+ if (err) {
+ kfree(str);
+ goto exit_free;
+ }
+ break;
+ }
+ }
+
+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) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ data->buflen += data->values[i] =
+ audit_pack_string(&bufp, f->se_str);
+ break;
+ case AUDIT_WATCH:
+ data->buflen += data->values[i] =
+ audit_pack_string(&bufp, krule->watch->path);
+ break;
+ 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) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
+ return 1;
+ break;
+ case AUDIT_WATCH:
+ if (strcmp(a->watch->path, b->watch->path))
+ return 1;
+ break;
+ 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;
+}
+
+/* Duplicate the given audit watch. The new watch's rules list is initialized
+ * to an empty list and wlist is undefined. */
+static inline struct audit_watch *audit_dupe_watch(struct audit_watch *old)
+{
+ char *path;
+ struct audit_watch *new;
+
+ path = kstrdup(old->path, GFP_KERNEL);
+ if (unlikely(!path))
+ return ERR_PTR(-ENOMEM);
+
+ new = audit_init_watch(path);
+ if (unlikely(IS_ERR(new))) {
+ kfree(path);
+ goto out;
+ }
+
+ new->dev = old->dev;
+ new->ino = old->ino;
+ audit_get_parent(old->parent);
+ new->parent = old->parent;
+
+out:
+ return new;
+}
+
+/* Duplicate selinux field information. The se_rule is opaque, so must be
+ * re-initialized. */
+static inline int audit_dupe_selinux_field(struct audit_field *df,
+ struct audit_field *sf)
+{
+ int ret = 0;
+ char *se_str;
+
+ /* our own copy of se_str */
+ se_str = kstrdup(sf->se_str, GFP_KERNEL);
+ if (unlikely(IS_ERR(se_str)))
+ return -ENOMEM;
+ df->se_str = se_str;
+
+ /* our own (refreshed) copy of se_rule */
+ ret = selinux_audit_rule_init(df->type, df->op, df->se_str,
+ &df->se_rule);
+ /* Keep currently invalid fields around in case they
+ * become valid after a policy reload. */
+ if (ret == -EINVAL) {
+ printk(KERN_WARNING "audit rule for selinux \'%s\' is "
+ "invalid\n", df->se_str);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Duplicate an audit rule. This will be a deep copy with the exception
+ * of the watch - that pointer is carried over. The selinux specific fields
+ * will be updated in the copy. The point is to be able to replace the old
+ * rule with the new rule in the filterlist, then free the old rule.
+ * The rlist element is undefined; list manipulations are handled apart from
+ * the initial copy. */
+static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
+ struct audit_watch *watch)
+{
+ u32 fcount = old->field_count;
+ struct audit_entry *entry;
+ struct audit_krule *new;
+ int i, err = 0;
+
+ entry = audit_init_entry(fcount);
+ if (unlikely(!entry))
+ return ERR_PTR(-ENOMEM);
+
+ new = &entry->rule;
+ new->vers_ops = old->vers_ops;
+ new->flags = old->flags;
+ new->listnr = old->listnr;
+ new->action = old->action;
+ for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+ new->mask[i] = old->mask[i];
+ new->buflen = old->buflen;
+ new->watch = NULL;
+ new->field_count = old->field_count;
+ memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
+
+ /* deep copy this information, updating the se_rule fields, because
+ * the originals will all be freed when the old rule is freed. */
+ for (i = 0; i < fcount; i++) {
+ switch (new->fields[i].type) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ err = audit_dupe_selinux_field(&new->fields[i],
+ &old->fields[i]);
+ }
+ if (err) {
+ audit_free_rule(entry);
+ return ERR_PTR(err);
+ }
+ }
+
+ if (watch) {
+ audit_get_watch(watch);
+ new->watch = watch;
+ }
+
+ return entry;
+}
+
+/* Update inode info in audit rules based on filesystem event. */
+static inline void audit_update_watch(struct audit_parent *parent,
+ const char *dname, dev_t dev,
+ unsigned long ino)
+{
+ struct audit_watch *owatch, *nwatch, *nextw;
+ struct audit_krule *r, *nextr;
+ struct audit_entry *oentry, *nentry;
+ struct audit_buffer *ab;
+
+ mutex_lock(&audit_filter_mutex);
+ list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
+ if (audit_compare_dname_path(dname, owatch->path))
+ continue;
+
+ nwatch = audit_dupe_watch(owatch);
+ if (unlikely(IS_ERR(nwatch))) {
+ mutex_unlock(&audit_filter_mutex);
+ audit_panic("error updating watch, skipping");
+ return;
+ }
+ nwatch->dev = dev;
+ nwatch->ino = ino;
+
+ list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
+ oentry = container_of(r, struct audit_entry, rule);
+
+ nentry = audit_dupe_rule(&oentry->rule, nwatch);
+ if (unlikely(IS_ERR(nentry))) {
+ audit_panic("error updating watch, removing");
+ list_del(&oentry->rule.rlist);
+ list_del_rcu(&oentry->list);
+ } else {
+ list_add(&nentry->rule.rlist, &nwatch->rules);
+ list_del(&oentry->rule.rlist);
+ list_replace_rcu(&oentry->list, &nentry->list);
+ }
+ call_rcu(&oentry->rcu, audit_free_rule_rcu);
+ }
+
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+ audit_log_format(ab, "audit updated rules specifying watch=");
+ audit_log_untrustedstring(ab, owatch->path);
+ audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
+ audit_log_end(ab);
+
+ list_del(&owatch->wlist);
+ audit_put_watch(owatch); /* matches initial get */
+ goto add_watch_to_parent; /* event applies to a single watch */
+ }
+ mutex_unlock(&audit_filter_mutex);
+ return;
+
+add_watch_to_parent:
+ list_add(&nwatch->wlist, &parent->watches);
+ mutex_unlock(&audit_filter_mutex);
+ return;
+}
+
+/* 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;
+
+ mutex_lock(&audit_filter_mutex);
+ parent->flags |= AUDIT_PARENT_INVALID;
+ 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);
+ list_del(&r->rlist);
+ list_del_rcu(&e->list);
+ call_rcu(&e->rcu, audit_free_rule_rcu);
+
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "audit implicitly removed rule from list=%d\n",
+ AUDIT_FILTER_EXIT);
+ }
+ list_del(&w->wlist);
+ audit_put_watch(w); /* matches initial get */
+ }
+ mutex_unlock(&audit_filter_mutex);
+}
+
+/* Register inotify watches for parents on in_list. */
+static int audit_inotify_register(struct nameidata *nd,
+ struct list_head *in_list)
+{
+ struct audit_parent *p;
+ s32 wd;
+ int ret = 0;
+
+ list_for_each_entry(p, in_list, ilist) {
+ wd = inotify_add_watch(audit_ih, &p->wdata, nd->dentry->d_inode,
+ AUDIT_IN_WATCH);
+ if (wd < 0) {
+ audit_remove_parent_watches(p);
+ /* the put matching the get in audit_init_parent() */
+ audit_put_parent(p);
+ /* save the first error for return value */
+ if (!ret)
+ ret = wd;
+ }
+ }
+
+ return ret;
+}
+
+/* Unregister inotify watches for parents on in_list.
+ * Generates an IN_IGNORED event. */
+static void audit_inotify_unregister(struct list_head *in_list)
+{
+ struct audit_parent *p;
+
+ list_for_each_entry(p, in_list, ilist) {
+ inotify_rm_watch(audit_ih, &p->wdata);
+ /* the put matching the get in audit_remove_watch() */
+ audit_put_parent(p);
+ }
+}
+
+/* Get path information necessary for adding watches. */
+static int audit_get_nd(char *path, struct nameidata **ndp,
+ struct nameidata **ndw)
+{
+ struct nameidata *ndparent, *ndwatch;
+ int err;
+
+ ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL);
+ if (unlikely(!ndparent))
+ return -ENOMEM;
+
+ ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL);
+ if (unlikely(!ndwatch)) {
+ kfree(ndparent);
+ return -ENOMEM;
+ }
+
+ err = path_lookup(path, LOOKUP_PARENT, ndparent);
+ if (err) {
+ kfree(ndparent);
+ kfree(ndwatch);
+ return err;
+ }
+
+ err = path_lookup(path, 0, ndwatch);
+ if (err) {
+ kfree(ndwatch);
+ ndwatch = NULL;
+ }
+
+ *ndp = ndparent;
+ *ndw = ndwatch;
+
+ return 0;
+}
+
+/* Release resources used for watch path information. */
+static inline void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
+{
+ if (ndp) {
+ path_release(ndp);
+ kfree(ndp);
+ }
+ if (ndw) {
+ path_release(ndw);
+ kfree(ndw);
+ }
+}
+
+/* Add a parent inotify_watch for the given rule. */
+static int audit_add_parent(struct audit_krule *krule,
+ struct list_head *inotify_list)
+{
+ struct audit_parent *parent;
+ struct audit_watch *watch = krule->watch;
+
+ parent = audit_init_parent();
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ audit_get_parent(parent);
+ watch->parent = parent;
+
+ /* krule, watch and parent have not been added to any global
+ * lists, so we don't need to take audit_filter_mutex. */
+ list_add(&watch->wlist, &parent->watches);
+ list_add(&krule->rlist, &watch->rules);
+
+ /* add parent to inotify registration list */
+ list_add(&parent->ilist, inotify_list);
+
+ return 0;
+}
+
+/* Associate the given rule with an existing parent inotify_watch.
+ * Caller must hold audit_filter_mutex. */
+static int audit_add_to_parent(struct audit_krule *krule,
+ struct inotify_watch *iwatch)
+{
+ struct audit_parent *parent;
+ struct audit_watch *w, *watch = krule->watch;
+ int watch_found = 0;
+
+ parent = container_of(iwatch, struct audit_parent, wdata);
+
+ /* parent was moved before we took audit_filter_mutex */
+ if (parent->flags & AUDIT_PARENT_INVALID)
+ return -ENOENT;
+
+ list_for_each_entry(w, &parent->watches, wlist) {
+ if (strcmp(watch->path, w->path))
+ continue;
+
+ watch_found = 1;
+
+ /* put krule's and initial refs to temporary watch */
+ audit_put_watch(watch);
+ audit_put_watch(watch);
+
+ audit_get_watch(w);
+ krule->watch = watch = w;
+ break;
+ }
+
+ if (!watch_found) {
+ audit_get_parent(parent);
+ watch->parent = parent;
+
+ list_add(&watch->wlist, &parent->watches);
+ }
+
+ list_add(&krule->rlist, &watch->rules);
+
+ return 0;
+}
+
+/* Find a matching watch entry, or add this one.
+ * Caller must hold audit_filter_mutex. */
+static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
+ struct nameidata *ndw,
+ struct list_head *inotify_list)
+{
+ struct audit_watch *watch = krule->watch;
+ struct inotify_watch *iwatch;
+ int ret;
+
+ /* update watch filter fields */
+ if (ndw) {
+ watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
+ watch->ino = ndw->dentry->d_inode->i_ino;
+ }
+
+ /* The audit_filter_mutex must not be held during inotify calls because
+ * we hold it during inotify event callback processing.
+ * We can trust iwatch to stick around because we hold nameidata (ndp). */
+ mutex_unlock(&audit_filter_mutex);
+
+ if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &iwatch) < 0) {
+ ret = audit_add_parent(krule, inotify_list);
+ mutex_lock(&audit_filter_mutex);
+ } else {
+ mutex_lock(&audit_filter_mutex);
+ ret = audit_add_to_parent(krule, iwatch);
+ }
+
+ return ret;
+}
+
+/* Add rule to given filterlist if not a duplicate. */
+static inline int audit_add_rule(struct audit_entry *entry,
+ struct list_head *list)
+{
+ struct audit_entry *e;
+ struct audit_watch *watch = entry->rule.watch;
+ struct nameidata *ndp, *ndw;
+ LIST_HEAD(inotify_list);
+ int err;
+
+ /* Taking audit_filter_mutex protects from stale rule data. */
+ mutex_lock(&audit_filter_mutex);
+ list_for_each_entry(e, list, list) {
+ if (!audit_compare_rule(&entry->rule, &e->rule)) {
+ err = -EEXIST;
+ mutex_unlock(&audit_filter_mutex);
+ goto error;
+ }
+ }
+ mutex_unlock(&audit_filter_mutex);
+
+ /* Avoid calling path_lookup under audit_filter_mutex. */
+ if (watch) {
+ err = audit_get_nd(watch->path, &ndp, &ndw);
+ if (err)
+ goto error;
+ }
+
+ mutex_lock(&audit_filter_mutex);
+ if (watch) {
+ /* audit_filter_mutex is dropped and re-taken during this call */
+ err = audit_add_watch(&entry->rule, ndp, ndw, &inotify_list);
+ if (err) {
+ audit_put_nd(ndp, ndw);
+ goto error;
+ }
+ }
+
+ if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
+ list_add_rcu(&entry->list, list);
+ } else {
+ list_add_tail_rcu(&entry->list, list);
+ }
+ mutex_unlock(&audit_filter_mutex);
+
+ if (!list_empty(&inotify_list)) {
+ err = audit_inotify_register(ndp, &inotify_list);
+ if (err)
+ goto error;
+ audit_put_nd(ndp, ndw);
+ }
+
+ return 0;
+
+error:
+ if (watch)
+ audit_put_watch(watch); /* tmp watch, matches initial get */
+ return err;
+}
+
+/* Remove given krule from its associated watch's rules list and clean up any
+ * last instances of associated watch and parent.
+ * Caller must hold audit_filter_mutex. */
+static inline void audit_remove_watch(struct audit_krule *krule,
+ struct list_head *in_list)
+{
+ struct audit_watch *watch = krule->watch;
+ struct audit_parent *parent = watch->parent;
+
+ list_del(&krule->rlist);
+ if (list_empty(&watch->rules)) {
+ list_del(&watch->wlist);
+ audit_put_watch(watch); /* matches initial get */
+
+ if (list_empty(&parent->watches)) {
+ /* Put parent on the inotify un-registration list.
+ * Grab a reference before releasing audit_filter_mutex,
+ * to be released in audit_inotify_unregister(). */
+ list_add(&parent->ilist, in_list);
+ audit_get_parent(parent);
+ }
+ }
+}
+
+/* Remove an existing rule from filterlist. */
+static inline int audit_del_rule(struct audit_entry *entry,
+ struct list_head *list)
+{
+ struct audit_entry *e;
+ LIST_HEAD(inotify_list);
+
+ mutex_lock(&audit_filter_mutex);
+ list_for_each_entry(e, list, list) {
+ if (audit_compare_rule(&entry->rule, &e->rule))
+ continue;
+
+ if (e->rule.watch) {
+ audit_remove_watch(&e->rule, &inotify_list);
+ /* match initial get for tmp watch */
+ audit_put_watch(entry->rule.watch);
+ }
+
+ list_del_rcu(&e->list);
+ call_rcu(&e->rcu, audit_free_rule_rcu);
+ mutex_unlock(&audit_filter_mutex);
+
+ if (!list_empty(&inotify_list))
+ audit_inotify_unregister(&inotify_list);
+
+ return 0;
+ }
+ mutex_unlock(&audit_filter_mutex);
+ /* match initial get for tmp watch */
+ if (entry->rule.watch)
+ audit_put_watch(entry->rule.watch);
+ return -ENOENT; /* No matching rule */
+}
+
+/* List rules using struct audit_rule. Exists for backward
+ * compatibility with userspace. */
+static void audit_list(int pid, int seq, struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+ struct audit_entry *entry;
+ int i;
+
+ /* This is a blocking read, so use audit_filter_mutex instead of rcu
+ * iterator to sync with list writers. */
+ 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;
+ skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
+ rule, sizeof(*rule));
+ if (skb)
+ skb_queue_tail(q, skb);
+ kfree(rule);
+ }
+ }
+ skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+ if (skb)
+ skb_queue_tail(q, skb);
+}
+
+/* List rules using struct audit_rule_data. */
+static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+ struct audit_entry *e;
+ int i;
+
+ /* This is a blocking read, so use audit_filter_mutex instead of rcu
+ * iterator to sync with list writers. */
+ 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;
+ skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
+ data, sizeof(*data) + data->buflen);
+ if (skb)
+ skb_queue_tail(q, skb);
+ kfree(data);
+ }
+ }
+ skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
+ if (skb)
+ skb_queue_tail(q, skb);
+}
+
+/**
+ * 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
+ * @sid: SE Linux Security ID of sender
+ */
+int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
+ size_t datasz, uid_t loginuid, u32 sid)
+{
+ struct task_struct *tsk;
+ struct audit_netlink_list *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(sizeof(struct audit_netlink_list), GFP_KERNEL);
+ if (!dest)
+ return -ENOMEM;
+ dest->pid = pid;
+ skb_queue_head_init(&dest->q);
+
+ mutex_lock(&audit_filter_mutex);
+ if (type == AUDIT_LIST)
+ audit_list(pid, seq, &dest->q);
+ else
+ audit_list_rules(pid, seq, &dest->q);
+ mutex_unlock(&audit_filter_mutex);
+
+ tsk = kthread_run(audit_send_list, dest, "audit_send_list");
+ if (IS_ERR(tsk)) {
+ skb_queue_purge(&dest->q);
+ 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]);
+
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ if (selinux_ctxid_to_string(sid, &ctx, &len)) {
+ /* Maybe call audit_panic? */
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u ssid=%u add rule to list=%d res=%d",
+ loginuid, sid, entry->rule.listnr, !err);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u subj=%s add rule to list=%d res=%d",
+ loginuid, ctx, entry->rule.listnr, !err);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u add rule to list=%d res=%d",
+ 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]);
+
+ if (sid) {
+ char *ctx = NULL;
+ u32 len;
+ if (selinux_ctxid_to_string(sid, &ctx, &len)) {
+ /* Maybe call audit_panic? */
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u ssid=%u remove rule from list=%d res=%d",
+ loginuid, sid, entry->rule.listnr, !err);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u subj=%s remove rule from list=%d res=%d",
+ loginuid, ctx, entry->rule.listnr, !err);
+ kfree(ctx);
+ } else
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u remove rule from list=%d res=%d",
+ 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;
+}
+
+/* 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,
+ 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;
+}
+
+/* Check to see if the rule contains any selinux fields. Returns 1 if there
+ are selinux fields specified in the rule, 0 otherwise. */
+static inline int audit_rule_has_selinux(struct audit_krule *rule)
+{
+ int i;
+
+ for (i = 0; i < rule->field_count; i++) {
+ struct audit_field *f = &rule->fields[i];
+ switch (f->type) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* This function will re-initialize the se_rule field of all applicable rules.
+ * It will traverse the filter lists serarching for rules that contain selinux
+ * specific filter fields. When such a rule is found, it is copied, the
+ * selinux field is re-initialized, and the old rule is replaced with the
+ * updated rule. */
+int selinux_audit_rule_update(void)
+{
+ struct audit_entry *entry, *n, *nentry;
+ struct audit_watch *watch;
+ int i, err = 0;
+
+ /* audit_filter_mutex synchronizes the writers */
+ mutex_lock(&audit_filter_mutex);
+
+ for (i = 0; i < AUDIT_NR_FILTERS; i++) {
+ list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
+ if (!audit_rule_has_selinux(&entry->rule))
+ continue;
+
+ watch = entry->rule.watch;
+ nentry = audit_dupe_rule(&entry->rule, watch);
+ if (unlikely(IS_ERR(nentry))) {
+ /* save the first error encountered for the
+ * return value */
+ if (!err)
+ err = PTR_ERR(nentry);
+ audit_panic("error updating selinux filters");
+ if (watch)
+ list_del(&entry->rule.rlist);
+ list_del_rcu(&entry->list);
+ } else {
+ if (watch) {
+ list_add(&nentry->rule.rlist,
+ &watch->rules);
+ list_del(&entry->rule.rlist);
+ }
+ list_replace_rcu(&entry->list, &nentry->list);
+ }
+ call_rcu(&entry->rcu, audit_free_rule_rcu);
+ }
+ }
+
+ mutex_unlock(&audit_filter_mutex);
+
+ return err;
+}
+
+/* Update watch data in audit rules based on inotify events. */
+void audit_handle_ievent(struct inotify_watch *iwatch, u32 wd, u32 mask,
+ u32 cookie, const char *dname, struct inode *inode)
+{
+ struct audit_parent *parent = container_of(iwatch, struct audit_parent, wdata);
+
+ if (mask & (IN_CREATE|IN_MOVED_TO) && inode)
+ audit_update_watch(parent, dname, inode->i_sb->s_dev,
+ inode->i_ino);
+ else if (mask & (IN_DELETE|IN_MOVED_FROM))
+ audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1);
+ /* Note: Inotify doesn't remove the watch for the IN_MOVE_SELF event.
+ * Work around this by leaving the parent around with an empty
+ * watchlist. It will be re-used if new watches are added. */
+ else if (mask & (AUDIT_IN_SELF))
+ audit_remove_parent_watches(parent);
+ else if (mask & IN_IGNORED)
+ audit_put_parent(parent); /* match get in audit_init_parent() */
+}
diff --git a/kernel/audit/syscall.c b/kernel/audit/syscall.c
new file mode 100644
index 0000000..43512c1
--- /dev/null
+++ b/kernel/audit/syscall.c
@@ -0,0 +1,1401 @@
+/* auditsc.c -- System-call auditing support
+ * Handles all system-call specific auditing features.
+ *
+ * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2005 IBM Corporation
+ * All Rights Reserved.
+ *
+ * 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
+ *
+ * Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
+ *
+ * Many of the ideas implemented here are from Stephen C. Tweedie,
+ * especially the idea of avoiding a copy by using getname.
+ *
+ * The method for actual interception of syscall entry and exit (not in
+ * this file -- see entry.S) is based on a GPL'd patch written by
+ * okir(a)suse.de and Copyright 2003 SuSE Linux AG.
+ *
+ * The support of additional filter rules compares (>, <, >=, <=) was
+ * added by Dustin Kirkland <dustin.kirkland(a)us.ibm.com>, 2005.
+ *
+ * Modified by Amy Griffis <amy.griffis(a)hp.com> to collect additional
+ * filesystem information.
+ *
+ * Subject and object context labeling support added by <danjones(a)us.ibm.com>
+ * and <dustin.kirkland(a)us.ibm.com> for LSPP certification compliance.
+ */
+
+#include <linux/init.h>
+#include <asm/types.h>
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/socket.h>
+#include <linux/audit.h>
+#include <linux/personality.h>
+#include <linux/time.h>
+#include <linux/netlink.h>
+#include <linux/compiler.h>
+#include <asm/unistd.h>
+#include <linux/security.h>
+#include <linux/list.h>
+#include <linux/tty.h>
+#include <linux/selinux.h>
+#include <linux/binfmts.h>
+
+#include "audit.h"
+
+extern struct list_head audit_filter_list[];
+
+/* No syscall auditing will take place unless audit_enabled != 0. */
+extern int audit_enabled;
+
+/* AUDIT_NAMES is the number of slots we reserve in the audit_context
+ * for saving names from getname(). */
+#define AUDIT_NAMES 20
+
+/* AUDIT_NAMES_RESERVED is the number of slots we reserve in the
+ * audit_context from being used for nameless inodes from
+ * path_lookup. */
+#define AUDIT_NAMES_RESERVED 7
+
+/* When fs/namei.c:getname() is called, we store the pointer in name and
+ * we don't let putname() free it (instead we free all of the saved
+ * pointers at syscall exit time).
+ *
+ * Further, in fs/namei.c:path_lookup() we store the inode and device. */
+struct audit_names {
+ const char *name;
+ unsigned long ino;
+ unsigned long pino;
+ dev_t dev;
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t rdev;
+ u32 osid;
+};
+
+struct audit_aux_data {
+ struct audit_aux_data *next;
+ int type;
+};
+
+#define AUDIT_AUX_IPCPERM 0
+
+struct audit_aux_data_ipcctl {
+ struct audit_aux_data d;
+ struct ipc_perm p;
+ unsigned long qbytes;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ u32 osid;
+};
+
+struct audit_aux_data_execve {
+ struct audit_aux_data d;
+ int argc;
+ int envc;
+ char mem[0];
+};
+
+struct audit_aux_data_socketcall {
+ struct audit_aux_data d;
+ int nargs;
+ unsigned long args[0];
+};
+
+struct audit_aux_data_sockaddr {
+ struct audit_aux_data d;
+ int len;
+ char a[0];
+};
+
+struct audit_aux_data_path {
+ struct audit_aux_data d;
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+};
+
+/* The per-task audit context. */
+struct audit_context {
+ int in_syscall; /* 1 if task is in a syscall */
+ enum audit_state state;
+ unsigned int serial; /* serial number for record */
+ struct timespec ctime; /* time of syscall entry */
+ uid_t loginuid; /* login uid (identity) */
+ int major; /* syscall number */
+ unsigned long argv[4]; /* syscall arguments */
+ int return_valid; /* return code is valid */
+ long return_code;/* syscall return code */
+ int auditable; /* 1 if record should be written */
+ int name_count;
+ struct audit_names names[AUDIT_NAMES];
+ struct dentry * pwd;
+ struct vfsmount * pwdmnt;
+ struct audit_context *previous; /* For nested syscalls */
+ struct audit_aux_data *aux;
+
+ /* Save things to print about task_struct */
+ pid_t pid;
+ uid_t uid, euid, suid, fsuid;
+ gid_t gid, egid, sgid, fsgid;
+ unsigned long personality;
+ int arch;
+
+#if AUDIT_DEBUG
+ int put_count;
+ int ino_count;
+#endif
+};
+
+/* Determine if any context name data matches a rule's watch data */
+static inline int audit_match_watch(struct audit_context *ctx,
+ struct audit_watch *watch)
+{
+ int i;
+
+ if (!ctx)
+ return 0;
+
+ if (watch->ino == (unsigned long)-1)
+ return 0;
+
+ for (i = 0; i < ctx->name_count; i++) {
+ if (ctx->names[i].dev == watch->dev &&
+ (ctx->names[i].ino == watch->ino ||
+ ctx->names[i].pino == watch->ino))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Compare a task_struct with an audit_rule. Return 1 on match, 0
+ * otherwise. */
+static int audit_filter_rules(struct task_struct *tsk,
+ struct audit_krule *rule,
+ struct audit_context *ctx,
+ enum audit_state *state)
+{
+ int i, j, need_sid = 1;
+ u32 sid;
+
+ 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(tsk->pid, f->op, f->val);
+ break;
+ case AUDIT_UID:
+ result = audit_comparator(tsk->uid, f->op, f->val);
+ break;
+ case AUDIT_EUID:
+ result = audit_comparator(tsk->euid, f->op, f->val);
+ break;
+ case AUDIT_SUID:
+ result = audit_comparator(tsk->suid, f->op, f->val);
+ break;
+ case AUDIT_FSUID:
+ result = audit_comparator(tsk->fsuid, f->op, f->val);
+ break;
+ case AUDIT_GID:
+ result = audit_comparator(tsk->gid, f->op, f->val);
+ break;
+ case AUDIT_EGID:
+ result = audit_comparator(tsk->egid, f->op, f->val);
+ break;
+ case AUDIT_SGID:
+ result = audit_comparator(tsk->sgid, f->op, f->val);
+ break;
+ case AUDIT_FSGID:
+ result = audit_comparator(tsk->fsgid, f->op, f->val);
+ break;
+ case AUDIT_PERS:
+ result = audit_comparator(tsk->personality, f->op, f->val);
+ break;
+ case AUDIT_ARCH:
+ if (ctx)
+ result = audit_comparator(ctx->arch, f->op, f->val);
+ break;
+
+ case AUDIT_EXIT:
+ if (ctx && ctx->return_valid)
+ result = audit_comparator(ctx->return_code, f->op, f->val);
+ break;
+ case AUDIT_SUCCESS:
+ if (ctx && ctx->return_valid) {
+ if (f->val)
+ result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS);
+ else
+ result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
+ }
+ break;
+ case AUDIT_DEVMAJOR:
+ if (ctx) {
+ for (j = 0; j < ctx->name_count; j++) {
+ if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) {
+ ++result;
+ break;
+ }
+ }
+ }
+ break;
+ case AUDIT_DEVMINOR:
+ if (ctx) {
+ for (j = 0; j < ctx->name_count; j++) {
+ if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
+ ++result;
+ break;
+ }
+ }
+ }
+ break;
+ case AUDIT_INODE:
+ if (ctx) {
+ 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)) {
+ ++result;
+ break;
+ }
+ }
+ }
+ break;
+ case AUDIT_WATCH:
+ result = audit_match_watch(ctx, rule->watch);
+ break;
+ case AUDIT_LOGINUID:
+ result = 0;
+ if (ctx)
+ result = audit_comparator(ctx->loginuid, f->op, f->val);
+ break;
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ /* NOTE: this may return negative values indicating
+ a temporary error. We simply treat this as a
+ match for now to avoid losing information that
+ may be wanted. An error message will also be
+ logged upon error */
+ if (f->se_rule) {
+ if (need_sid) {
+ selinux_task_ctxid(tsk, &sid);
+ need_sid = 0;
+ }
+ result = selinux_audit_rule_match(sid, f->type,
+ f->op,
+ f->se_rule,
+ ctx);
+ }
+ break;
+ case AUDIT_ARG0:
+ case AUDIT_ARG1:
+ case AUDIT_ARG2:
+ case AUDIT_ARG3:
+ if (ctx)
+ result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], 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;
+}
+
+/* At process creation time, we can determine if system-call auditing is
+ * completely disabled for this task. Since we only have the task
+ * structure at this point, we can only check uid and gid.
+ */
+static enum audit_state audit_filter_task(struct task_struct *tsk)
+{
+ struct audit_entry *e;
+ enum audit_state state;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
+ if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
+ rcu_read_unlock();
+ return state;
+ }
+ }
+ rcu_read_unlock();
+ return AUDIT_BUILD_CONTEXT;
+}
+
+/* At syscall entry and exit time, this filter is called if the
+ * audit_state is not low enough that auditing cannot take place, but is
+ * also not high enough that we already know we have to write an audit
+ * record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
+ */
+static enum audit_state audit_filter_syscall(struct task_struct *tsk,
+ struct audit_context *ctx,
+ struct list_head *list)
+{
+ struct audit_entry *e;
+ enum audit_state state;
+
+ if (audit_pid && tsk->tgid == audit_pid)
+ return AUDIT_DISABLED;
+
+ rcu_read_lock();
+ if (!list_empty(list)) {
+ int word = AUDIT_WORD(ctx->major);
+ int bit = AUDIT_BIT(ctx->major);
+
+ list_for_each_entry_rcu(e, list, list) {
+ if ((e->rule.mask[word] & bit) == bit
+ && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
+ rcu_read_unlock();
+ return state;
+ }
+ }
+ }
+ rcu_read_unlock();
+ return AUDIT_BUILD_CONTEXT;
+}
+
+static inline struct audit_context *audit_get_context(struct task_struct *tsk,
+ int return_valid,
+ int return_code)
+{
+ struct audit_context *context = tsk->audit_context;
+
+ if (likely(!context))
+ return NULL;
+ context->return_valid = return_valid;
+ context->return_code = return_code;
+
+ if (context->in_syscall && !context->auditable) {
+ enum audit_state state;
+ state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
+ if (state == AUDIT_RECORD_CONTEXT)
+ context->auditable = 1;
+ }
+
+ context->pid = tsk->pid;
+ context->uid = tsk->uid;
+ context->gid = tsk->gid;
+ context->euid = tsk->euid;
+ context->suid = tsk->suid;
+ context->fsuid = tsk->fsuid;
+ context->egid = tsk->egid;
+ context->sgid = tsk->sgid;
+ context->fsgid = tsk->fsgid;
+ context->personality = tsk->personality;
+ tsk->audit_context = NULL;
+ return context;
+}
+
+static inline void audit_free_names(struct audit_context *context)
+{
+ int i;
+
+#if AUDIT_DEBUG == 2
+ if (context->auditable
+ ||context->put_count + context->ino_count != context->name_count) {
+ printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
+ " name_count=%d put_count=%d"
+ " ino_count=%d [NOT freeing]\n",
+ __FILE__, __LINE__,
+ context->serial, context->major, context->in_syscall,
+ context->name_count, context->put_count,
+ context->ino_count);
+ for (i = 0; i < context->name_count; i++) {
+ printk(KERN_ERR "names[%d] = %p = %s\n", i,
+ context->names[i].name,
+ context->names[i].name ?: "(null)");
+ }
+ dump_stack();
+ return;
+ }
+#endif
+#if AUDIT_DEBUG
+ context->put_count = 0;
+ context->ino_count = 0;
+#endif
+
+ for (i = 0; i < context->name_count; i++) {
+ if (context->names[i].name)
+ __putname(context->names[i].name);
+ }
+ context->name_count = 0;
+ if (context->pwd)
+ dput(context->pwd);
+ if (context->pwdmnt)
+ mntput(context->pwdmnt);
+ context->pwd = NULL;
+ context->pwdmnt = NULL;
+}
+
+static inline void audit_free_aux(struct audit_context *context)
+{
+ struct audit_aux_data *aux;
+
+ while ((aux = context->aux)) {
+ if (aux->type == AUDIT_AVC_PATH) {
+ struct audit_aux_data_path *axi = (void *)aux;
+ dput(axi->dentry);
+ mntput(axi->mnt);
+ }
+
+ context->aux = aux->next;
+ kfree(aux);
+ }
+}
+
+static inline void audit_zero_context(struct audit_context *context,
+ enum audit_state state)
+{
+ uid_t loginuid = context->loginuid;
+
+ memset(context, 0, sizeof(*context));
+ context->state = state;
+ context->loginuid = loginuid;
+}
+
+static inline struct audit_context *audit_alloc_context(enum audit_state state)
+{
+ struct audit_context *context;
+
+ if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
+ return NULL;
+ audit_zero_context(context, state);
+ return context;
+}
+
+/**
+ * audit_alloc - allocate an audit context block for a task
+ * @tsk: task
+ *
+ * Filter on the task information and allocate a per-task audit context
+ * if necessary. Doing so turns on system call auditing for the
+ * specified task. This is called from copy_process, so no lock is
+ * needed.
+ */
+int audit_alloc(struct task_struct *tsk)
+{
+ struct audit_context *context;
+ enum audit_state state;
+
+ if (likely(!audit_enabled))
+ return 0; /* Return if not auditing. */
+
+ state = audit_filter_task(tsk);
+ if (likely(state == AUDIT_DISABLED))
+ return 0;
+
+ if (!(context = audit_alloc_context(state))) {
+ audit_log_lost("out of memory in audit_alloc");
+ return -ENOMEM;
+ }
+
+ /* Preserve login uid */
+ context->loginuid = -1;
+ if (current->audit_context)
+ context->loginuid = current->audit_context->loginuid;
+
+ tsk->audit_context = context;
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
+ return 0;
+}
+
+static inline void audit_free_context(struct audit_context *context)
+{
+ struct audit_context *previous;
+ int count = 0;
+
+ do {
+ previous = context->previous;
+ if (previous || (count && count < 10)) {
+ ++count;
+ printk(KERN_ERR "audit(:%d): major=%d name_count=%d:"
+ " freeing multiple contexts (%d)\n",
+ context->serial, context->major,
+ context->name_count, count);
+ }
+ audit_free_names(context);
+ audit_free_aux(context);
+ kfree(context);
+ context = previous;
+ } while (context);
+ if (count >= 10)
+ printk(KERN_ERR "audit: freed %d contexts\n", count);
+}
+
+static void audit_log_task_context(struct audit_buffer *ab)
+{
+ char *ctx = NULL;
+ ssize_t len = 0;
+
+ len = security_getprocattr(current, "current", NULL, 0);
+ if (len < 0) {
+ if (len != -EINVAL)
+ goto error_path;
+ return;
+ }
+
+ ctx = kmalloc(len, GFP_KERNEL);
+ if (!ctx)
+ goto error_path;
+
+ len = security_getprocattr(current, "current", ctx, len);
+ if (len < 0 )
+ goto error_path;
+
+ audit_log_format(ab, " subj=%s", ctx);
+ return;
+
+error_path:
+ if (ctx)
+ kfree(ctx);
+ audit_panic("error in audit_log_task_context");
+ return;
+}
+
+static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
+{
+ char name[sizeof(tsk->comm)];
+ struct mm_struct *mm = tsk->mm;
+ struct vm_area_struct *vma;
+
+ /* tsk == current */
+
+ get_task_comm(name, tsk);
+ audit_log_format(ab, " comm=");
+ audit_log_untrustedstring(ab, name);
+
+ if (mm) {
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ if ((vma->vm_flags & VM_EXECUTABLE) &&
+ vma->vm_file) {
+ audit_log_d_path(ab, "exe=",
+ vma->vm_file->f_dentry,
+ vma->vm_file->f_vfsmnt);
+ break;
+ }
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+ }
+ audit_log_task_context(ab);
+}
+
+static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
+{
+ int i, call_panic = 0;
+ struct audit_buffer *ab;
+ struct audit_aux_data *aux;
+ const char *tty;
+
+ /* tsk == current */
+
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
+ if (!ab)
+ return; /* audit_panic has been called */
+ audit_log_format(ab, "arch=%x syscall=%d",
+ context->arch, context->major);
+ if (context->personality != PER_LINUX)
+ audit_log_format(ab, " per=%lx", context->personality);
+ if (context->return_valid)
+ audit_log_format(ab, " success=%s exit=%ld",
+ (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
+ context->return_code);
+ if (tsk->signal && tsk->signal->tty &&
tsk->signal->tty->name)
+ tty = tsk->signal->tty->name;
+ else
+ tty = "(none)";
+ audit_log_format(ab,
+ " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
+ " pid=%d auid=%u uid=%u gid=%u"
+ " euid=%u suid=%u fsuid=%u"
+ " egid=%u sgid=%u fsgid=%u tty=%s",
+ context->argv[0],
+ context->argv[1],
+ context->argv[2],
+ context->argv[3],
+ context->name_count,
+ context->pid,
+ context->loginuid,
+ context->uid,
+ context->gid,
+ context->euid, context->suid, context->fsuid,
+ context->egid, context->sgid, context->fsgid, tty);
+ audit_log_task_info(ab, tsk);
+ audit_log_end(ab);
+
+ for (aux = context->aux; aux; aux = aux->next) {
+
+ ab = audit_log_start(context, GFP_KERNEL, aux->type);
+ if (!ab)
+ continue; /* audit_panic has been called */
+
+ switch (aux->type) {
+ case AUDIT_IPC: {
+ struct audit_aux_data_ipcctl *axi = (void *)aux;
+ audit_log_format(ab,
+ " qbytes=%lx iuid=%u igid=%u mode=%x",
+ axi->qbytes, axi->uid, axi->gid, axi->mode);
+ if (axi->osid != 0) {
+ char *ctx = NULL;
+ u32 len;
+ if (selinux_ctxid_to_string(
+ axi->osid, &ctx, &len)) {
+ audit_log_format(ab, " osid=%u",
+ axi->osid);
+ call_panic = 1;
+ } else
+ audit_log_format(ab, " obj=%s", ctx);
+ kfree(ctx);
+ }
+ break; }
+
+ case AUDIT_IPC_SET_PERM: {
+ struct audit_aux_data_ipcctl *axi = (void *)aux;
+ audit_log_format(ab,
+ " new qbytes=%lx new iuid=%u new igid=%u new mode=%x",
+ axi->qbytes, axi->uid, axi->gid, axi->mode);
+ if (axi->osid != 0) {
+ char *ctx = NULL;
+ u32 len;
+ if (selinux_ctxid_to_string(
+ axi->osid, &ctx, &len)) {
+ audit_log_format(ab, " osid=%u",
+ axi->osid);
+ call_panic = 1;
+ } else
+ audit_log_format(ab, " obj=%s", ctx);
+ kfree(ctx);
+ }
+ break; }
+ case AUDIT_EXECVE: {
+ struct audit_aux_data_execve *axi = (void *)aux;
+ int i;
+ const char *p;
+ for (i = 0, p = axi->mem; i < axi->argc; i++) {
+ audit_log_format(ab, "a%d=", i);
+ p = audit_log_untrustedstring(ab, p);
+ audit_log_format(ab, "\n");
+ }
+ break; }
+
+ case AUDIT_SOCKETCALL: {
+ int i;
+ struct audit_aux_data_socketcall *axs = (void *)aux;
+ audit_log_format(ab, "nargs=%d", axs->nargs);
+ for (i=0; i<axs->nargs; i++)
+ audit_log_format(ab, " a%d=%lx", i, axs->args[i]);
+ break; }
+
+ case AUDIT_SOCKADDR: {
+ struct audit_aux_data_sockaddr *axs = (void *)aux;
+
+ audit_log_format(ab, "saddr=");
+ audit_log_hex(ab, axs->a, axs->len);
+ break; }
+
+ case AUDIT_AVC_PATH: {
+ struct audit_aux_data_path *axi = (void *)aux;
+ audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
+ break; }
+
+ }
+ audit_log_end(ab);
+ }
+
+ if (context->pwd && context->pwdmnt) {
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
+ if (ab) {
+ audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
+ audit_log_end(ab);
+ }
+ }
+ for (i = 0; i < context->name_count; i++) {
+ unsigned long ino = context->names[i].ino;
+ unsigned long pino = context->names[i].pino;
+
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
+ if (!ab)
+ continue; /* audit_panic has been called */
+
+ audit_log_format(ab, "item=%d", i);
+
+ audit_log_format(ab, " name=");
+ if (context->names[i].name)
+ audit_log_untrustedstring(ab, context->names[i].name);
+ else
+ audit_log_format(ab, "(null)");
+
+ if (pino != (unsigned long)-1)
+ audit_log_format(ab, " parent=%lu", pino);
+ if (ino != (unsigned long)-1)
+ audit_log_format(ab, " inode=%lu", ino);
+ if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
+ audit_log_format(ab, " dev=%02x:%02x mode=%#o"
+ " ouid=%u ogid=%u rdev=%02x:%02x",
+ MAJOR(context->names[i].dev),
+ MINOR(context->names[i].dev),
+ context->names[i].mode,
+ context->names[i].uid,
+ context->names[i].gid,
+ MAJOR(context->names[i].rdev),
+ MINOR(context->names[i].rdev));
+ if (context->names[i].osid != 0) {
+ char *ctx = NULL;
+ u32 len;
+ if (selinux_ctxid_to_string(
+ context->names[i].osid, &ctx, &len)) {
+ audit_log_format(ab, " osid=%u",
+ context->names[i].osid);
+ call_panic = 2;
+ } else
+ audit_log_format(ab, " obj=%s", ctx);
+ kfree(ctx);
+ }
+
+ audit_log_end(ab);
+ }
+ if (call_panic)
+ audit_panic("error converting sid to string");
+}
+
+/**
+ * audit_free - free a per-task audit context
+ * @tsk: task whose audit context block to free
+ *
+ * Called from copy_process and do_exit
+ */
+void audit_free(struct task_struct *tsk)
+{
+ struct audit_context *context;
+
+ context = audit_get_context(tsk, 0, 0);
+ if (likely(!context))
+ return;
+
+ /* Check for system calls that do not go through the exit
+ * function (e.g., exit_group), then free context block.
+ * We use GFP_ATOMIC here because we might be doing this
+ * in the context of the idle thread */
+ /* that can happen only if we are called from do_exit() */
+ if (context->in_syscall && context->auditable)
+ audit_log_exit(context, tsk);
+
+ audit_free_context(context);
+}
+
+/**
+ * audit_syscall_entry - fill in an audit record at syscall entry
+ * @tsk: task being audited
+ * @arch: architecture type
+ * @major: major syscall type (function)
+ * @a1: additional syscall register 1
+ * @a2: additional syscall register 2
+ * @a3: additional syscall register 3
+ * @a4: additional syscall register 4
+ *
+ * Fill in audit context at syscall entry. This only happens if the
+ * audit context was created when the task was created and the state or
+ * filters demand the audit context be built. If the state from the
+ * per-task filter or from the per-syscall filter is AUDIT_RECORD_CONTEXT,
+ * then the record will be written at syscall exit time (otherwise, it
+ * will only be written if another part of the kernel requests that it
+ * be written).
+ */
+void audit_syscall_entry(int arch, int major,
+ unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4)
+{
+ struct task_struct *tsk = current;
+ struct audit_context *context = tsk->audit_context;
+ enum audit_state state;
+
+ BUG_ON(!context);
+
+ /*
+ * This happens only on certain architectures that make system
+ * calls in kernel_thread via the entry.S interface, instead of
+ * with direct calls. (If you are porting to a new
+ * architecture, hitting this condition can indicate that you
+ * got the _exit/_leave calls backward in entry.S.)
+ *
+ * i386 no
+ * x86_64 no
+ * ppc64 yes (see arch/powerpc/platforms/iseries/misc.S)
+ *
+ * This also happens with vm86 emulation in a non-nested manner
+ * (entries without exits), so this case must be caught.
+ */
+ if (context->in_syscall) {
+ struct audit_context *newctx;
+
+#if AUDIT_DEBUG
+ printk(KERN_ERR
+ "audit(:%d) pid=%d in syscall=%d;"
+ " entering syscall=%d\n",
+ context->serial, tsk->pid, context->major, major);
+#endif
+ newctx = audit_alloc_context(context->state);
+ if (newctx) {
+ newctx->previous = context;
+ context = newctx;
+ tsk->audit_context = newctx;
+ } else {
+ /* If we can't alloc a new context, the best we
+ * can do is to leak memory (any pending putname
+ * will be lost). The only other alternative is
+ * to abandon auditing. */
+ audit_zero_context(context, context->state);
+ }
+ }
+ BUG_ON(context->in_syscall || context->name_count);
+
+ if (!audit_enabled)
+ return;
+
+ context->arch = arch;
+ context->major = major;
+ context->argv[0] = a1;
+ context->argv[1] = a2;
+ context->argv[2] = a3;
+ context->argv[3] = a4;
+
+ state = context->state;
+ if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)
+ state = audit_filter_syscall(tsk, context,
&audit_filter_list[AUDIT_FILTER_ENTRY]);
+ if (likely(state == AUDIT_DISABLED))
+ return;
+
+ context->serial = 0;
+ context->ctime = CURRENT_TIME;
+ context->in_syscall = 1;
+ context->auditable = !!(state == AUDIT_RECORD_CONTEXT);
+}
+
+/**
+ * audit_syscall_exit - deallocate audit context after a system call
+ * @tsk: task being audited
+ * @valid: success/failure flag
+ * @return_code: syscall return value
+ *
+ * Tear down after system call. If the audit context has been marked as
+ * auditable (either because of the AUDIT_RECORD_CONTEXT state from
+ * filtering, or because some other part of the kernel write an audit
+ * message), then write out the syscall information. In call cases,
+ * free the names stored from getname().
+ */
+void audit_syscall_exit(int valid, long return_code)
+{
+ struct task_struct *tsk = current;
+ struct audit_context *context;
+
+ context = audit_get_context(tsk, valid, return_code);
+
+ if (likely(!context))
+ return;
+
+ if (context->in_syscall && context->auditable)
+ audit_log_exit(context, tsk);
+
+ context->in_syscall = 0;
+ context->auditable = 0;
+
+ if (context->previous) {
+ struct audit_context *new_context = context->previous;
+ context->previous = NULL;
+ audit_free_context(context);
+ tsk->audit_context = new_context;
+ } else {
+ audit_free_names(context);
+ audit_free_aux(context);
+ tsk->audit_context = context;
+ }
+}
+
+/**
+ * audit_getname - add a name to the list
+ * @name: name to add
+ *
+ * Add a name to the list of audit names for this context.
+ * Called from fs/namei.c:getname().
+ */
+void audit_getname(const char *name)
+{
+ struct audit_context *context = current->audit_context;
+
+ if (!context || IS_ERR(name) || !name)
+ return;
+
+ if (!context->in_syscall) {
+#if AUDIT_DEBUG == 2
+ printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n",
+ __FILE__, __LINE__, context->serial, name);
+ dump_stack();
+#endif
+ return;
+ }
+ BUG_ON(context->name_count >= AUDIT_NAMES);
+ context->names[context->name_count].name = name;
+ context->names[context->name_count].ino = (unsigned long)-1;
+ ++context->name_count;
+ if (!context->pwd) {
+ read_lock(¤t->fs->lock);
+ context->pwd = dget(current->fs->pwd);
+ context->pwdmnt = mntget(current->fs->pwdmnt);
+ read_unlock(¤t->fs->lock);
+ }
+
+}
+
+/* audit_putname - intercept a putname request
+ * @name: name to intercept and delay for putname
+ *
+ * If we have stored the name from getname in the audit context,
+ * then we delay the putname until syscall exit.
+ * Called from include/linux/fs.h:putname().
+ */
+void audit_putname(const char *name)
+{
+ struct audit_context *context = current->audit_context;
+
+ BUG_ON(!context);
+ if (!context->in_syscall) {
+#if AUDIT_DEBUG == 2
+ printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n",
+ __FILE__, __LINE__, context->serial, name);
+ if (context->name_count) {
+ int i;
+ for (i = 0; i < context->name_count; i++)
+ printk(KERN_ERR "name[%d] = %p = %s\n", i,
+ context->names[i].name,
+ context->names[i].name ?: "(null)");
+ }
+#endif
+ __putname(name);
+ }
+#if AUDIT_DEBUG
+ else {
+ ++context->put_count;
+ if (context->put_count > context->name_count) {
+ printk(KERN_ERR "%s:%d(:%d): major=%d"
+ " in_syscall=%d putname(%p) name_count=%d"
+ " put_count=%d\n",
+ __FILE__, __LINE__,
+ context->serial, context->major,
+ context->in_syscall, name, context->name_count,
+ context->put_count);
+ dump_stack();
+ }
+ }
+#endif
+}
+
+static void audit_inode_context(int idx, const struct inode *inode)
+{
+ struct audit_context *context = current->audit_context;
+
+ selinux_get_inode_sid(inode, &context->names[idx].osid);
+}
+
+
+/**
+ * audit_inode - store the inode and device from a lookup
+ * @name: name being audited
+ * @inode: inode being audited
+ * @flags: lookup flags (as used in path_lookup())
+ *
+ * Called from fs/namei.c:path_lookup().
+ */
+void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
+{
+ int idx;
+ struct audit_context *context = current->audit_context;
+
+ if (!context->in_syscall)
+ return;
+ if (context->name_count
+ && context->names[context->name_count-1].name
+ && context->names[context->name_count-1].name == name)
+ idx = context->name_count - 1;
+ else if (context->name_count > 1
+ && context->names[context->name_count-2].name
+ && context->names[context->name_count-2].name == name)
+ idx = context->name_count - 2;
+ else {
+ /* FIXME: how much do we care about inodes that have no
+ * associated name? */
+ if (context->name_count >= AUDIT_NAMES - AUDIT_NAMES_RESERVED)
+ return;
+ idx = context->name_count++;
+ context->names[idx].name = NULL;
+#if AUDIT_DEBUG
+ ++context->ino_count;
+#endif
+ }
+ context->names[idx].dev = inode->i_sb->s_dev;
+ context->names[idx].mode = inode->i_mode;
+ context->names[idx].uid = inode->i_uid;
+ context->names[idx].gid = inode->i_gid;
+ context->names[idx].rdev = inode->i_rdev;
+ audit_inode_context(idx, inode);
+ if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) &&
+ (strcmp(name, ".") != 0)) {
+ context->names[idx].ino = (unsigned long)-1;
+ context->names[idx].pino = inode->i_ino;
+ } else {
+ context->names[idx].ino = inode->i_ino;
+ context->names[idx].pino = (unsigned long)-1;
+ }
+}
+
+/**
+ * audit_inode_child - collect inode info for created/removed objects
+ * @dname: inode's dentry name
+ * @inode: inode being audited
+ * @pino: inode number of dentry parent
+ *
+ * For syscalls that create or remove filesystem objects, audit_inode
+ * can only collect information for the filesystem object's parent.
+ * This call updates the audit context with the child's information.
+ * Syscalls that create a new filesystem object must be hooked after
+ * the object is created. Syscalls that remove a filesystem object
+ * must be hooked prior, in order to capture the target inode during
+ * unsuccessful attempts.
+ */
+void __audit_inode_child(const char *dname, const struct inode *inode,
+ unsigned long pino)
+{
+ int idx;
+ struct audit_context *context = current->audit_context;
+
+ if (!context->in_syscall)
+ return;
+
+ /* determine matching parent */
+ 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 (!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;
+ context->names[idx].pino = pino;
+#if AUDIT_DEBUG
+ context->ino_count++;
+#endif
+
+update_context:
+ if (inode) {
+ context->names[idx].ino = inode->i_ino;
+ context->names[idx].dev = inode->i_sb->s_dev;
+ context->names[idx].mode = inode->i_mode;
+ context->names[idx].uid = inode->i_uid;
+ context->names[idx].gid = inode->i_gid;
+ context->names[idx].rdev = inode->i_rdev;
+ audit_inode_context(idx, inode);
+ }
+}
+
+/**
+ * auditsc_get_stamp - get local copies of audit_context values
+ * @ctx: audit_context for the task
+ * @t: timespec to store time recorded in the audit_context
+ * @serial: serial value that is recorded in the audit_context
+ *
+ * Also sets the context as auditable.
+ */
+void auditsc_get_stamp(struct audit_context *ctx,
+ struct timespec *t, unsigned int *serial)
+{
+ if (!ctx->serial)
+ ctx->serial = audit_serial();
+ t->tv_sec = ctx->ctime.tv_sec;
+ t->tv_nsec = ctx->ctime.tv_nsec;
+ *serial = ctx->serial;
+ ctx->auditable = 1;
+}
+
+/**
+ * audit_set_loginuid - set a task's audit_context loginuid
+ * @task: task whose audit context is being modified
+ * @loginuid: loginuid value
+ *
+ * Returns 0.
+ *
+ * Called (set) from fs/proc/base.c::proc_loginuid_write().
+ */
+int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
+{
+ if (task->audit_context) {
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
+ if (ab) {
+ audit_log_format(ab, "login pid=%d uid=%u "
+ "old auid=%u new auid=%u",
+ task->pid, task->uid,
+ task->audit_context->loginuid, loginuid);
+ audit_log_end(ab);
+ }
+ task->audit_context->loginuid = loginuid;
+ }
+ return 0;
+}
+
+/**
+ * audit_get_loginuid - get the loginuid for an audit_context
+ * @ctx: the audit_context
+ *
+ * Returns the context's loginuid or -1 if @ctx is NULL.
+ */
+uid_t audit_get_loginuid(struct audit_context *ctx)
+{
+ return ctx ? ctx->loginuid : -1;
+}
+
+/**
+ * audit_ipc_obj - record audit data for ipc object
+ * @ipcp: ipc permissions
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_ipc_obj(struct kern_ipc_perm *ipcp)
+{
+ struct audit_aux_data_ipcctl *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->uid = ipcp->uid;
+ ax->gid = ipcp->gid;
+ ax->mode = ipcp->mode;
+ selinux_get_ipc_sid(ipcp, &ax->osid);
+
+ ax->d.type = AUDIT_IPC;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_ipc_set_perm - record audit data for new ipc permissions
+ * @qbytes: msgq bytes
+ * @uid: msgq user id
+ * @gid: msgq group id
+ * @mode: msgq mode (permissions)
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct
kern_ipc_perm *ipcp)
+{
+ struct audit_aux_data_ipcctl *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->qbytes = qbytes;
+ ax->uid = uid;
+ ax->gid = gid;
+ ax->mode = mode;
+ selinux_get_ipc_sid(ipcp, &ax->osid);
+
+ ax->d.type = AUDIT_IPC_SET_PERM;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+int audit_bprm(struct linux_binprm *bprm)
+{
+ struct audit_aux_data_execve *ax;
+ struct audit_context *context = current->audit_context;
+ unsigned long p, next;
+ void *to;
+
+ if (likely(!audit_enabled || !context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p,
+ GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->argc = bprm->argc;
+ ax->envc = bprm->envc;
+ for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) {
+ struct page *page = bprm->page[p / PAGE_SIZE];
+ void *kaddr = kmap(page);
+ next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1);
+ memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p);
+ to += next - p;
+ kunmap(page);
+ }
+
+ ax->d.type = AUDIT_EXECVE;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+
+/**
+ * audit_socketcall - record audit data for sys_socketcall
+ * @nargs: number of args
+ * @args: args array
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_socketcall(int nargs, unsigned long *args)
+{
+ struct audit_aux_data_socketcall *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax) + nargs * sizeof(unsigned long), GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->nargs = nargs;
+ memcpy(ax->args, args, nargs * sizeof(unsigned long));
+
+ ax->d.type = AUDIT_SOCKETCALL;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_sockaddr - record audit data for sys_bind, sys_connect, sys_sendto
+ * @len: data length in user space
+ * @a: data address in kernel space
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_sockaddr(int len, void *a)
+{
+ struct audit_aux_data_sockaddr *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax) + len, GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->len = len;
+ memcpy(ax->a, a, len);
+
+ ax->d.type = AUDIT_SOCKADDR;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_avc_path - record the granting or denial of permissions
+ * @dentry: dentry to record
+ * @mnt: mnt to record
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ *
+ * Called from security/selinux/avc.c::avc_audit()
+ */
+int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
+{
+ struct audit_aux_data_path *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->dentry = dget(dentry);
+ ax->mnt = mntget(mnt);
+
+ ax->d.type = AUDIT_AVC_PATH;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_signal_info - record signal info for shutting down audit subsystem
+ * @sig: signal value
+ * @t: task being signaled
+ *
+ * If the audit subsystem is being terminated, record the task (pid)
+ * and uid that is doing that.
+ */
+void audit_signal_info(int sig, struct task_struct *t)
+{
+ extern pid_t audit_sig_pid;
+ extern uid_t audit_sig_uid;
+
+ if (unlikely(audit_pid && t->tgid == audit_pid)) {
+ if (sig == SIGTERM || sig == SIGHUP) {
+ struct audit_context *ctx = current->audit_context;
+ audit_sig_pid = current->pid;
+ if (ctx)
+ audit_sig_uid = ctx->loginuid;
+ else
+ audit_sig_uid = current->uid;
+ }
+ }
+}