Re: [patch 1/1] SELinux AVC audit log ipaddr field support (for task_struct->curr_ip)
by Stephen Smalley
On Thu, 2005-03-10 at 16:05 +0100, Lorenzo Hernández García-Hierro
wrote:
> Provides support for a new field ipaddr within the SELinux
> AVC audit log, relying in task_struct->curr_ip (ipv4 only)
> provided by the task-curr_ip or grSecurity patch to be applied
> before.It was first implemented by Joshua Brindle (a.k.a Method)
> from the Hardened Gentoo project.
>
> An example of the audit messages with ipaddr field:
> audit(1110432234.161:0): avc: denied { search } for pid=19057
> exe=/usr/bin/wget name=portage dev=hda3 ino=1024647 ipaddr=192.168.1.30
> scontext=root:sysadm_r:portage_fetch_t tcontext=system_u:object_r:portage_tmp_t tclass=dir
Even if the basic idea were sound (doubtful), this would need to be
generalized (i.e. not ipv4-specific). Also, I think I'd rather see
extensions to the audit data be incorporated into the audit framework,
not the AVC-specific audit code, and some of the existing avc_audit()
code migrated into the audit framework (e.g. the exe= information
currently generated by avc_audit could be done by audit_log_exit
instead).
--
Stephen Smalley <sds(a)tycho.nsa.gov>
National Security Agency
19 years, 9 months
Re: Ponderings from a tin can
by Timothy R. Chavez
On Wednesday 09 March 2005 08:19 pm, David Woodhouse wrote:
> On Wed, 2005-03-09 at 20:02 -0600, Timothy R. Chavez wrote:
> > Thank you for all the valuable comments. Getting caught up in some areas
> > of the code totally made me forget about others. Thank you for the sharp
> > eyes. I would have been completely demolished by fsdevel with some of
> > these.
>
> Lock me in a tin can for 9 hours and I can get almost as grumpy as
> fsdevel can be :)
>
> The aux field is added by my IPC patch. It's just a list which we can
> hang auxiliary audit data off.
Alright, I'll put this as a "to do" and we can merge them eventually. I need
to figure out a good way to lock up audit_watch() without causing deadlock.
19 years, 9 months
[RFC][PATCH] (#5, U3) filesystem auditing
by Timothy R. Chavez (GMail)
Hello,
- This patch reorders the hooks to be consistently placed above the security
hooks (Thanks Serge).
- It also removes the audit_notify_watch() hooks from vfs_rmdir and vfs_unlink
and places it in may_delete() (Thanks Stephen).
- Stripped out the auditfs_enable feature because there was no outstanding
use-case for it. We determined it was sufficient to have the compile time
configure option only. (Thanks David, Thanks Steve).
Sorry I didn't get this out sooner David.
Also, what I'm doing with reference counting and locking doesn't seem to be
too clear (and it really isn't) -- I apologize for that. Though this will
all be described in detail in a document, I'll go ahead and send out an
e-mail explaining what's going on.
-tim
--
-tim
19 years, 9 months
Re: [RFC][PATCH] (#5, U3) filesystem auditing
by Timothy R. Chavez
On Tuesday 08 March 2005 05:53 pm, Timothy R. Chavez wrote:
> Hello,
>
> - This patch reorders the hooks to be consistently placed above the
> security hooks (Thanks Serge).
>
> - It also removes the audit_notify_watch() hooks from vfs_rmdir and
> vfs_unlink and places it in may_delete() (Thanks Stephen).
>
> - Stripped out the auditfs_enable feature because there was no outstanding
> use-case for it. We determined it was sufficient to have the compile time
> configure option only. (Thanks David, Thanks Steve).
>
> Sorry I didn't get this out sooner David.
>
> Also, what I'm doing with reference counting and locking doesn't seem to be
> too clear (and it really isn't) -- I apologize for that. Though this will
> all be described in detail in a document, I'll go ahead and send out an
> e-mail explaining what's going on.
>
>
> -tim
[Oops, set up a seperate e-mail client and I sent this message with a
non-approved e-mail account for this list, so I'm resending it with the
appropriate e-mail acocunt.]
Just a quick note. Loulwa received a kernel panic this morning when rebooting
with patch #5 U3. I was unable to look at the panic message (it was related
to the VFS) she got earlier and I've not received such a panic on my machine
with the patch applied. If you do happen to patch with #5 U3 can you tell me
what architecture you patched on and booted with and if you got a panic (and
any other information you could gather associated with the panic) or
not. It's my intention to test this code on as many platforms as possible,
but I'm really short on time at the moment.
Thanks.
-tim
19 years, 9 months
Re: syscall filtering on personality
by Debora Velarde
Here is a sample test to syscall filtering on personality:
Use the Makefile to create the 64bit version and the 32bit version of the
test.
Execute 'pers64'. See new audit record in /var/log/audit.log.
Execute 'pers32'. No new audit records in /var/log/audit.log.
(chmod is syscall 90)
(See attached file: pers64.c)
(See attached file: pers32.c)
(See attached file: Makefile)
-debbie
19 years, 9 months
audit-0.6.6 released
by Steve Grubb
Hello,
The next version of the audit daemon has been released. You can get it from:
http://people.redhat.com/sgrubb/audit/ or in rawhide tomorrow morning. This
is continuing to be bug fixes and not much in the way of adding features. The
changelog is:
- Fix audit_set_pid to try to read a reply, but its non-fatal if no reply.
- Remove the read status during init
- Change to using pthreads sync mechanism for stopping system
- Worker thread should ignore all signals
- Change main loop to use select for inbound event handling
- Gave pam_loginuid a "failok" option for testing
- Various code cleanups
I added the personality issue to the TODO list so its not forgotten.
Please let me know if there are problems with this release. I think that with
a new kernel and all these bugs put to bed, we are finally ready to start the
search utility and patching trusted programs.
-Steve Grubb
19 years, 9 months
[RFC][PATCH] (#5, Update #2) filesystem auditing support
by Timothy R. Chavez
Hello,
This patch incorporates a lot of feedback from various people (Serge
Hallyn, Steve Grubb, Dave Hansen, Mounir Bsaibes): mostly cosmetic.
But there were some bug fixes in audit_create_wentry().
I'm going to spend a couple days testing this and writing the abstract
and then I want to put it on linux-fsdevel. I'd really appreciate
some scrutiny and feedback on this patch during that time. The goal
is to finally move on this and make it more visible this week. There
is still one remaining feature that was requested that needs to be
implemented and I'll get to it eventually... before March is over (the
end of my development schedule).
Also, should I break this up into a patch set?
-Tim
diff -Nurp linux-2.6.11/fs/dcache.c linux-2.6.11-audit/fs/dcache.c
--- linux-2.6.11/fs/dcache.c 2005-03-02 01:37:48.000000000 -0600
+++ linux-2.6.11-audit/fs/dcache.c 2005-03-04 16:20:29.000000000 -0600
@@ -32,6 +32,7 @@
#include <linux/seqlock.h>
#include <linux/swap.h>
#include <linux/bootmem.h>
+#include <linux/audit.h>
/* #define DCACHE_DEBUG 1 */
@@ -800,6 +801,7 @@ void d_instantiate(struct dentry *entry,
entry->d_inode = inode;
spin_unlock(&dcache_lock);
security_d_instantiate(entry, inode);
+ audit_watch(entry, 0);
}
/**
@@ -975,6 +977,7 @@ struct dentry *d_splice_alias(struct ino
if (new) {
BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
spin_unlock(&dcache_lock);
+ audit_watch(dentry, 0);
security_d_instantiate(new, inode);
d_rehash(dentry);
d_move(new, dentry);
@@ -984,6 +987,7 @@ struct dentry *d_splice_alias(struct ino
list_add(&dentry->d_alias, &inode->i_dentry);
dentry->d_inode = inode;
spin_unlock(&dcache_lock);
+ audit_watch(dentry, 0);
security_d_instantiate(dentry, inode);
d_rehash(dentry);
}
@@ -1094,6 +1098,8 @@ next:
}
rcu_read_unlock();
+ audit_watch(found, 0);
+
return found;
}
@@ -1333,6 +1339,7 @@ already_unhashed:
spin_unlock(&dentry->d_lock);
write_sequnlock(&rename_lock);
spin_unlock(&dcache_lock);
+ audit_watch(dentry, 1);
}
/**
diff -Nurp linux-2.6.11/fs/inode.c linux-2.6.11-audit/fs/inode.c
--- linux-2.6.11/fs/inode.c 2005-03-02 01:38:33.000000000 -0600
+++ linux-2.6.11-audit/fs/inode.c 2005-03-04 16:20:29.000000000 -0600
@@ -21,6 +21,7 @@
#include <linux/pagemap.h>
#include <linux/cdev.h>
#include <linux/bootmem.h>
+#include <linux/audit.h>
/*
* This is needed for the following functions:
@@ -136,6 +137,7 @@ static struct inode *alloc_inode(struct
inode->i_rdev = 0;
inode->i_security = NULL;
inode->dirtied_when = 0;
+ audit_inode_alloc(inode);
if (security_inode_alloc(inode)) {
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
@@ -175,6 +177,7 @@ void destroy_inode(struct inode *inode)
if (inode_has_buffers(inode))
BUG();
security_inode_free(inode);
+ audit_inode_free(inode);
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
diff -Nurp linux-2.6.11/fs/namei.c linux-2.6.11-audit/fs/namei.c
--- linux-2.6.11/fs/namei.c 2005-03-02 01:37:55.000000000 -0600
+++ linux-2.6.11-audit/fs/namei.c 2005-03-07 13:06:33.000000000 -0600
@@ -214,6 +214,8 @@ int permission(struct inode *inode, int
{
int retval, submask;
+ audit_notify_watch(inode, mask);
+
if (mask & MAY_WRITE) {
umode_t mode = inode->i_mode;
@@ -344,6 +346,8 @@ static inline int exec_permission_lite(s
{
umode_t mode = inode->i_mode;
+ audit_notify_watch(inode, MAY_EXEC);
+
if (inode->i_op && inode->i_op->permission)
return -EAGAIN;
@@ -1703,6 +1707,9 @@ int vfs_rmdir(struct inode *dir, struct
{
int error = may_delete(dir, dentry, 1);
+ if (dentry)
+ audit_notify_watch(dentry->d_inode, 0);
+
if (error)
return error;
@@ -1778,6 +1785,9 @@ int vfs_unlink(struct inode *dir, struct
{
int error = may_delete(dir, dentry, 0);
+ if (dentry)
+ audit_notify_watch(dentry->d_inode, 0);
+
if (error)
return error;
diff -Nurp linux-2.6.11/include/linux/audit.h
linux-2.6.11-audit/include/linux/audit.h
--- linux-2.6.11/include/linux/audit.h 2005-03-02 01:38:09.000000000 -0600
+++ linux-2.6.11-audit/include/linux/audit.h 2005-03-07 10:49:49.000000000 -0600
@@ -24,15 +24,23 @@
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
+#ifdef __KERNEL__
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#endif
+
/* Request and reply types */
-#define AUDIT_GET 1000 /* Get status */
-#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
-#define AUDIT_LIST 1002 /* List filtering rules */
-#define AUDIT_ADD 1003 /* Add filtering rule */
-#define AUDIT_DEL 1004 /* Delete filtering rule */
-#define AUDIT_USER 1005 /* Send a message from user-space */
-#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */
-#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
+#define AUDIT_GET 1000 /* Get status */
+#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
+#define AUDIT_LIST 1002 /* List filtering rules */
+#define AUDIT_ADD 1003 /* Add filtering rule */
+#define AUDIT_DEL 1004 /* Delete filtering rule */
+#define AUDIT_USER 1005 /* Send a message from user-space */
+#define AUDIT_LOGIN 1006 /* Define the login id and information */
+#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */
+#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
+#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
@@ -91,11 +99,15 @@
#define AUDIT_STATUS_PID 0x0004
#define AUDIT_STATUS_RATE_LIMIT 0x0008
#define AUDIT_STATUS_BACKLOG_LIMIT 0x0010
+#define AUDIT_STATUS_FSENABLED 0x0020
/* Failure-to-log actions */
#define AUDIT_FAIL_SILENT 0
#define AUDIT_FAIL_PRINTK 1
#define AUDIT_FAIL_PANIC 2
+/* 32 byte max key size */
+#define AUDIT_FILTERKEY_MAX 32
+
#ifndef __KERNEL__
struct audit_message {
struct nlmsghdr nlh;
@@ -106,6 +118,7 @@ struct audit_message {
struct audit_status {
__u32 mask; /* Bit mask for valid entries */
__u32 enabled; /* 1 = enabled, 0 = disbaled */
+ __u32 fs_enabled; /* 1 = fs auditing on, 0 = off */
__u32 failure; /* Failure-to-log action */
__u32 pid; /* pid of auditd process */
__u32 rate_limit; /* messages rate limit (per second) */
@@ -123,14 +136,38 @@ struct audit_rule { /* for AUDIT_LIST,
__u32 values[AUDIT_MAX_FIELDS];
};
+struct audit_watch {
+ int namelen;
+ int fklen;
+ char *name;
+ char *filterkey;
+ __u32 perms;
+};
+
#ifdef __KERNEL__
+struct audit_data {
+ struct audit_wentry *wentry;
+ struct list_head watchlist;
+ rwlock_t watchlist_lock;
+ atomic_t count;
+};
+
+struct audit_wentry {
+ struct list_head w_list;
+ atomic_t w_count;
+ struct audit_data *w_data;
+ struct audit_watch *w_watch;
+ int w_valid;
+};
+
#ifdef CONFIG_AUDIT
struct audit_buffer;
struct audit_context;
#endif
#ifdef CONFIG_AUDITSYSCALL
+struct inode;
/* These are defined in auditsc.c */
/* Public API */
extern int audit_alloc(struct task_struct *task);
@@ -150,6 +187,7 @@ extern void audit_get_stamp(struct audit
struct timespec *t, int *serial);
extern int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx);
+extern int audit_notify_watch(struct inode *inode, int mask);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -159,8 +197,28 @@ extern uid_t audit_get_loginuid(struct a
#define audit_putname(n) do { ; } while (0)
#define audit_inode(n,i,d) do { ; } while (0)
#define audit_get_loginuid(c) ({ -1; })
+#define audit_notify_watch(i,m) ({ 0; })
#endif
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct audit_watch *req);
+extern void audit_filesystem_init(void);
+extern void audit_inode_alloc(struct inode *inode);
+extern void audit_inode_free(struct inode *inode);
+extern void audit_watch(struct dentry *dentry, int drain);
+extern void audit_wentry_put(struct audit_wentry *wentry);
+extern struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry);
+#else
+#define audit_receive_watch(t,p,u,s,r) ({ -EOPNOTSUPP; })
+#define audit_filesystem_init() do { ; } while(0)
+#define audit_inode_alloc(i) do { ; } while(0)
+#define audit_inode_free(i) do { ; } while(0)
+#define audit_watch(dt,d) do { ; } while (0)
+#define audit_watch_put(w) do { ; } while(0)
+#define audit_watch_get(w) ({ 0; })
+#endif
+
#ifdef CONFIG_AUDIT
/* These are defined in audit.c */
/* Public API */
diff -Nurp linux-2.6.11/include/linux/fs.h linux-2.6.11-audit/include/linux/fs.h
--- linux-2.6.11/include/linux/fs.h 2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-audit/include/linux/fs.h 2005-03-04 16:20:29.000000000 -0600
@@ -457,6 +457,9 @@ struct inode {
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
+#ifdef CONFIG_AUDITFILESYSTEM
+ struct audit_data *i_audit;
+#endif
/* These three should probably be a union */
struct list_head i_devices;
struct pipe_inode_info *i_pipe;
diff -Nurp linux-2.6.11/init/Kconfig linux-2.6.11-audit/init/Kconfig
--- linux-2.6.11/init/Kconfig 2005-03-02 01:38:19.000000000 -0600
+++ linux-2.6.11-audit/init/Kconfig 2005-03-04 16:20:29.000000000 -0600
@@ -174,6 +174,17 @@ config AUDITSYSCALL
can be used independently or with another kernel subsystem,
such as SELinux.
+config AUDITFILESYSTEM
+ bool "Enable filesystem auditing support"
+ depends on AUDITSYSCALL
+ default n
+ help
+ Generate audit records for regular files or directories that
+ are being watched for access by audited syscalls. To insert
+ and remove watch points into the filesystem you may use the
+ auditctl program provided with auditd. For more information,
+ 'man auditctl'
+
config LOG_BUF_SHIFT
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
range 12 21
diff -Nurp linux-2.6.11/kernel/Makefile linux-2.6.11-audit/kernel/Makefile
--- linux-2.6.11/kernel/Makefile 2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-audit/kernel/Makefile 2005-03-04 16:20:29.000000000 -0600
@@ -23,6 +23,7 @@ obj-$(CONFIG_IKCONFIG_PROC) += configs.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+obj-$(CONFIG_AUDITFILESYSTEM) += auditfs.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_SYSFS) += ksysfs.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
diff -Nurp linux-2.6.11/kernel/audit.c linux-2.6.11-audit/kernel/audit.c
--- linux-2.6.11/kernel/audit.c 2005-03-02 01:38:33.000000000 -0600
+++ linux-2.6.11-audit/kernel/audit.c 2005-03-07 13:14:10.000000000 -0600
@@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
+ * Modified by Timothy R. Chavez <chavezt(a)us.ibm.com>
*
* Goals: 1) Integrate fully with SELinux.
* 2) Minimal run-time overhead:
@@ -60,6 +61,9 @@ static int audit_initialized;
/* No syscall auditing will take place unless audit_enabled != 0. */
int audit_enabled;
+/* No filesystem auditing will take place unless audit_fsenabled != 0 */
+int auditfs_enabled = 1;
+
/* Default state when kernel boots without any parameters. */
static int audit_default;
@@ -265,6 +269,17 @@ int audit_set_enabled(int state)
return old;
}
+int audit_set_fsenabled(int state)
+{
+ int old = auditfs_enabled;
+ if (state != 0 && state != 1)
+ return -EINVAL;
+ auditfs_enabled = state;
+ audit_log(current->audit_context, "auditfs_enabled=%d old=%d",
+ auditfs_enabled, old);
+ return old;
+}
+
int audit_set_failure(int state)
{
int old = audit_failure;
@@ -319,6 +334,8 @@ static int audit_netlink_ok(kernel_cap_t
case AUDIT_SET:
case AUDIT_ADD:
case AUDIT_DEL:
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
@@ -354,6 +371,7 @@ static int audit_receive_msg(struct sk_b
switch (msg_type) {
case AUDIT_GET:
status_set.enabled = audit_enabled;
+ status_set.fs_enabled = auditfs_enabled;
status_set.failure = audit_failure;
status_set.pid = audit_pid;
status_set.rate_limit = audit_rate_limit;
@@ -371,6 +389,10 @@ static int audit_receive_msg(struct sk_b
err = audit_set_enabled(status_get->enabled);
if (err < 0) return err;
}
+ if (status_get->mask & AUDIT_STATUS_FSENABLED) {
+ err = audit_set_fsenabled(status_get->fs_enabled);
+ if (err < 0) return err;
+ }
if (status_get->mask & AUDIT_STATUS_FAILURE) {
err = audit_set_failure(status_get->failure);
if (err < 0) return err;
@@ -413,6 +435,12 @@ static int audit_receive_msg(struct sk_b
err = -EOPNOTSUPP;
#endif
break;
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
+ err = audit_receive_watch(nlh->nlmsg_type,
+ NETLINK_CB(skb).pid,
+ uid, seq, data);
+ break;
default:
err = -EINVAL;
break;
@@ -557,6 +585,7 @@ int __init audit_init(void)
audit_initialized = 1;
audit_enabled = audit_default;
+ audit_filesystem_init();
audit_log(NULL, "initialized");
return 0;
}
diff -Nurp linux-2.6.11/kernel/auditfs.c linux-2.6.11-audit/kernel/auditfs.c
--- linux-2.6.11/kernel/auditfs.c 1969-12-31 17:00:00.000000000 -0700
+++ linux-2.6.11-audit/kernel/auditfs.c 2005-03-07 13:19:21.000000000 -0600
@@ -0,0 +1,583 @@
+/* auditfs.c -- Filesystem auditing support -*- linux-c -*-
+ * Implements filesystem auditing support, depends on kernel/auditsc.c
+ *
+ * Copyright 2005 International Business Machines Corp. (IBM)
+ * 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 Timothy R. Chavez <chavezt(a)us.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/audit.h>
+#include <asm/uaccess.h>
+
+kmem_cache_t *audit_watch_cache;
+kmem_cache_t *audit_wentry_cache;
+kmem_cache_t *audit_data_cache;
+
+extern int auditfs_enabled;
+
+/* Private Interface */
+
+static void audit_data_put(struct audit_data *data);
+void audit_wentry_put(struct audit_wentry *wentry);
+
+/*
+ * We remove this wentry from the watchlist and mark it as being invalid. When
+ * we invalidate a wentry, we're telling the audit subsystem to ignore any refs
+ * to this wentry that may still exist when auditing. We're also giving it the
+ * permission to remove the reference and attach a new watch if there is one
+ * available.
+ *
+ * We must drop our reference to the inode's audit_data here. Otherwise, we'll
+ * leak a reference and audit_data_free() will never be called.
+ *
+ * Caller must hold i_audit->watchlist_lock
+ */
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ list_del_init(&wentry->w_list);
+ wentry->w_valid = 0;
+ audit_data_put(wentry->w_data);
+ audit_wentry_put(wentry);
+ }
+}
+
+/*
+ * This is the core function for determining whether or not "name"
+ * is in the parent's watchlist (data->watchlist).
+ *
+ * NOTE: Should only be called from a secure source.
+ *
+ * Caller must hold i_audit->watchlist_lock and is responsible for
+ * putting back the returned reference
+ */
+static inline struct audit_wentry *audit_wentry_fetch(const char *name,
+ struct audit_data *data)
+{
+ struct audit_wentry *wentry, *ret = NULL;
+
+ list_for_each_entry(wentry, &data->watchlist, w_list)
+ if (!strcmp(wentry->w_watch->name, name)) {
+ ret = audit_wentry_get(wentry);
+ break;
+ }
+
+ return ret;
+}
+
+static
+inline struct audit_wentry *audit_wentry_fetch_lock(const char *name,
+ struct audit_data *data)
+{
+ unsigned long flags;
+ struct audit_wentry *ret = NULL;
+
+ if (name && data) {
+ read_lock_irqsave(&data->watchlist_lock, flags);
+ ret = audit_wentry_fetch(name, data);
+ read_unlock_irqrestore(&data->watchlist_lock, flags);
+ }
+
+ return ret;
+}
+
+/*
+ * First reference of audit_data is returned on allocation
+ */
+static inline struct audit_data *audit_data_alloc(void)
+{
+ struct audit_data *data;
+
+ data = kmem_cache_alloc(audit_data_cache, GFP_KERNEL);
+ if (data) {
+ data->wentry = NULL;
+ atomic_set(&data->count, 1);
+ INIT_LIST_HEAD(&data->watchlist);
+ data->watchlist_lock = RW_LOCK_UNLOCKED;
+
+ }
+
+ return data;
+}
+
+/*
+ * NOTE: Should only be called from a secure source
+ *
+ * Caller must hold i_audit->watchlist_lock
+ */
+static inline void audit_drain_watchlist(struct audit_data *data)
+{
+ struct audit_wentry *wentry, *tmp;
+
+ list_for_each_entry_safe(wentry, tmp, &data->watchlist, w_list)
+ audit_destroy_wentry(wentry);
+}
+
+static inline void audit_drain_watchlist_lock(struct audit_data *data)
+{
+ unsigned long flags;
+ struct audit_wentry *wentry, *tmp;
+
+ if (data) {
+ write_lock_irqsave(&data->watchlist_lock, flags);
+
+ list_for_each_entry_safe(wentry, tmp, &data->watchlist, w_list)
+ audit_destroy_wentry(wentry);
+
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+ }
+}
+
+static inline void audit_data_free(struct audit_data *data)
+{
+ unsigned long flags;
+
+ if (data) {
+ write_lock_irqsave(&data->watchlist_lock, flags);
+
+ audit_drain_watchlist(data);
+
+ if (data->wentry) {
+ audit_wentry_put(data->wentry);
+ data->wentry = NULL;
+ }
+
+ write_unlock_irqrestore(&data->watchlist_lock,flags);
+
+ kmem_cache_free(audit_data_cache, data);
+ }
+}
+
+static struct audit_data *audit_data_get(struct audit_data *data)
+{
+ if (data) {
+ BUG_ON(!atomic_read(&data->count));
+ atomic_inc(&data->count);
+ }
+
+ return data;
+}
+
+static void audit_data_put(struct audit_data *data)
+{
+ if (data && atomic_dec_and_test(&data->count))
+ audit_data_free(data);
+}
+
+static inline struct audit_watch *audit_watch_alloc(void)
+{
+ struct audit_watch *watch;
+
+ watch = kmem_cache_alloc(audit_watch_cache, GFP_KERNEL);
+ if (watch) {
+ watch->namelen = 0;
+ watch->fklen = 0;
+ watch->name = NULL;
+ watch->filterkey = NULL;
+ watch->perms = 0;
+ }
+
+ return watch;
+}
+
+static inline void audit_watch_free(struct audit_watch *watch)
+{
+ if (watch) {
+ kfree(watch->name);
+ kfree(watch->filterkey);
+ kmem_cache_free(audit_watch_cache, watch);
+ }
+}
+
+static struct audit_watch *audit_create_watch(const char *name,
+ const char *filterkey,
+ __u32 perms)
+{
+ struct audit_watch *err = NULL;
+ struct audit_watch *watch = NULL;
+
+ err = ERR_PTR(-EINVAL);
+
+ if (!name || strlen(name) + 1 > PATH_MAX)
+ goto audit_create_watch_fail;
+
+ if (filterkey && strlen(filterkey) + 1 > AUDIT_FILTERKEY_MAX)
+ goto audit_create_watch_fail;
+
+ if (perms > 15)
+ goto audit_create_watch_fail;
+
+ err = ERR_PTR(-ENOMEM);
+
+ watch = audit_watch_alloc();
+ if (watch) {
+ watch->namelen = strlen(name) + 1;
+ watch->name = kmalloc(watch->namelen, GFP_KERNEL);
+ if (!watch->name)
+ goto audit_create_watch_fail;
+ strcpy(watch->name, name);
+
+ if (filterkey) {
+ watch->fklen = strlen(filterkey) + 1;
+ watch->filterkey = kmalloc(watch->fklen, GFP_KERNEL);
+ if (!watch->filterkey)
+ goto audit_create_watch_fail;
+ strcpy(watch->filterkey, filterkey);
+ }
+
+ watch->perms = perms;
+
+ goto audit_create_watch_exit;
+ }
+
+
+audit_create_watch_fail:
+ audit_watch_free(watch);
+ watch = err;
+audit_create_watch_exit:
+ return watch;
+}
+
+/*
+ * First reference of audit_wentry is returned on allocation
+ */
+static inline struct audit_wentry *audit_wentry_alloc(void)
+{
+ struct audit_wentry *wentry;
+
+ wentry = kmem_cache_alloc(audit_wentry_cache, GFP_KERNEL);
+ if (wentry) {
+ atomic_set(&wentry->w_count, 1);
+ wentry->w_valid = 0;
+ wentry->w_watch = NULL;
+ wentry->w_data = NULL;
+ }
+
+ return wentry;
+}
+
+/*
+ * Because of the circular nature of the design, in order to arrive here,
+ * we may be getting called from within audit_data_free(). The only way
+ * this could happen is if there were no more references to inode->i_audit.
+ * Thus, we couldn't possibly put back the wentry's reference to w_data here.
+ */
+static inline void audit_wentry_free(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ audit_watch_free(wentry->w_watch);
+ kmem_cache_free(audit_wentry_cache, wentry);
+ }
+}
+
+/*
+ * The wentry holds both the watch (w_watch) and the inode's i_audit memory
+ * (w_data). The inode's memory contains a pointer to the wentry that holds
+ * it. This produces a circular effect.
+ *
+ * We do this for the following reasons:
+ * 1) the memory for an inode may not be allocated in our audit_watch()
+ * hook because there may be no reasonable way to handle an -ENOMEM.
+ * 2) there are cases when multiple inode's will refer to the same watch.
+ * 3) the inode associated w/ "name" may or may not exist
+ *
+ */
+static int audit_create_wentry(const char *name,
+ const char *filterkey,
+ unsigned int perms, struct audit_data *data)
+{
+ int ret;
+ unsigned long flags;
+ struct audit_wentry *wentry = NULL;
+ struct audit_wentry *new_wentry = NULL;
+
+ ret = -ENOMEM;
+
+ new_wentry = audit_wentry_alloc();
+ if (!new_wentry)
+ goto audit_create_wentry_fail;
+
+ new_wentry->w_data = audit_data_alloc();
+ if (!new_wentry->w_data)
+ goto audit_create_wentry_fail;
+
+ new_wentry->w_watch = audit_create_watch(name, filterkey, perms);
+ if (IS_ERR(new_wentry->w_watch)) {
+ ret = PTR_ERR(new_wentry->w_watch);
+ new_wentry->w_watch = NULL;
+ goto audit_create_wentry_fail;
+ }
+
+ new_wentry->w_data->wentry = audit_wentry_get(new_wentry);
+
+ write_lock_irqsave(&data->watchlist_lock, flags);
+
+ wentry = audit_wentry_fetch(name, data);
+ if (!wentry) {
+ list_add(&new_wentry->w_list, &data->watchlist);
+ new_wentry->w_valid = 1;
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+ ret = 0;
+ goto audit_create_wentry_exit;
+ }
+ audit_wentry_put(wentry);
+
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+ ret = -EEXIST;
+
+audit_create_wentry_fail:
+ audit_data_put(new_wentry->w_data);
+ audit_wentry_put(new_wentry);
+ if (!data->wentry && list_empty(&data->watchlist))
+ audit_data_put(data);
+audit_create_wentry_exit:
+ return ret;
+}
+
+static int audit_insert_watch(struct audit_watch *req)
+{
+ int ret;
+ char *path = NULL;
+ char *filterkey = NULL;
+ struct nameidata nd;
+
+ path = getname(req->name);
+ if (IS_ERR(req->name)) {
+ ret = PTR_ERR(req->name);
+ goto audit_insert_watch_exit;
+ }
+
+ if (req->fklen) {
+ filterkey = kmalloc(req->fklen, GFP_KERNEL);
+ ret = -ENOMEM;
+ if (!filterkey)
+ goto audit_insert_watch_exit;
+
+ ret = strncpy_from_user(filterkey, req->filterkey, req->fklen);
+ if (ret < 0)
+ goto audit_insert_watch_exit;
+ }
+
+ ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+ if (ret < 0)
+ goto audit_insert_watch_release;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM)
+ goto audit_insert_watch_release;
+
+ if (!nd.dentry->d_inode->i_audit) {
+ nd.dentry->d_inode->i_audit = audit_data_alloc();
+ ret = -ENOMEM;
+ if (!nd.dentry->d_inode->i_audit)
+ goto audit_insert_watch_release;
+ }
+
+ ret = audit_create_wentry(nd.last.name, filterkey, req->perms,
+ nd.dentry->d_inode->i_audit);
+
+audit_insert_watch_release:
+ path_release(&nd);
+audit_insert_watch_exit:
+ putname(path);
+ kfree(filterkey);
+ return ret;
+}
+
+static int audit_remove_watch(struct audit_watch *req)
+{
+ int ret;
+ unsigned long flags;
+ char *path = NULL;
+ struct nameidata nd;
+ struct audit_data *data;
+ struct audit_wentry *wentry;
+
+ path = getname(req->name);
+ if (IS_ERR(req->name)) {
+ ret = PTR_ERR(req->name);
+ goto audit_remove_watch_exit;
+ }
+
+ ret = -EINVAL;
+ if (!path || (path && strlen(path) > PATH_MAX))
+ goto audit_remove_watch_exit;
+
+ ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+ if (ret < 0)
+ goto audit_remove_watch_release;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_remove_watch_release;
+
+ ret = -EACCES;
+
+ data = nd.dentry->d_inode->i_audit;
+ if (!data)
+ goto audit_remove_watch_release;
+
+ write_lock_irqsave(&data->watchlist_lock, flags);
+
+ wentry = audit_wentry_fetch(nd.last.name, data);
+ if (!wentry) {
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+ goto audit_remove_watch_release;
+ }
+ audit_destroy_wentry(wentry);
+ audit_wentry_put(wentry);
+
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+ if (!data->wentry && list_empty(&data->watchlist)) {
+ audit_data_put(data);
+ nd.dentry->d_inode->i_audit = NULL;
+ }
+
+ ret = 0;
+
+audit_remove_watch_release:
+ path_release(&nd);
+audit_remove_watch_exit:
+ putname(path);
+ return ret;
+}
+
+/* Public interface */
+
+struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ BUG_ON(!atomic_read(&wentry->w_count));
+ atomic_inc(&wentry->w_count);
+ }
+
+ return wentry;
+}
+
+void audit_wentry_put(struct audit_wentry *wentry)
+{
+ if (wentry && atomic_dec_and_test(&wentry->w_count))
+ audit_wentry_free(wentry);
+}
+
+/*
+ * Hook appears in:
+ * fs/dcache.c:d_instantiate(), d_move(), d_lookup(), and d_splice_alias()
+ */
+void audit_watch(struct dentry *dentry, int drain)
+{
+ struct audit_wentry *wentry;
+ struct audit_data *data;
+
+ if (!dentry || !dentry->d_inode)
+ return;
+
+ if (!dentry->d_parent || !dentry->d_parent->d_inode)
+ return;
+
+ wentry = audit_wentry_fetch_lock(dentry->d_name.name,
+ dentry->d_parent->d_inode->i_audit);
+
+ data = dentry->d_inode->i_audit;
+ if (data) {
+ if (drain)
+ audit_drain_watchlist_lock(data);
+ if (wentry && !list_empty(&data->watchlist)) {
+ audit_data_put(wentry->w_data);
+ wentry->w_data = audit_data_get(data);
+ audit_wentry_put(wentry->w_data->wentry);
+ wentry->w_data->wentry = audit_wentry_get(wentry);
+ /* Keep get/put's consistent, stealing is bad :( */
+ audit_data_put(data);
+ dentry->d_inode->i_audit =
+ audit_data_get(wentry->w_data);
+ } else if (wentry && !data->wentry->w_valid) {
+ audit_data_put(data);
+ dentry->d_inode->i_audit =
+ audit_data_get(wentry->w_data);
+ }
+ } else if (wentry)
+ dentry->d_inode->i_audit = audit_data_get(wentry->w_data);
+
+ audit_wentry_put(wentry);
+}
+
+int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct audit_watch *req)
+{
+ int err;
+
+ if (auditfs_enabled) {
+
+ switch (type) {
+ case AUDIT_WATCH_INS:
+ err = audit_insert_watch(req);
+ break;
+ case AUDIT_WATCH_REM:
+ err = audit_remove_watch(req);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ audit_send_reply(pid, seq, type, 0, 1, &err, sizeof(int));
+
+ } else
+ err = -EOPNOTSUPP;
+
+ return err;
+}
+
+void audit_inode_alloc(struct inode *inode)
+{
+ if (inode)
+ inode->i_audit = NULL;
+}
+
+void audit_inode_free(struct inode *inode)
+{
+ if (inode && inode->i_audit) {
+ audit_data_put(inode->i_audit);
+ inode->i_audit = NULL;
+ }
+}
+
+void audit_filesystem_init()
+{
+ audit_watch_cache =
+ kmem_cache_create("audit_watch_cache",
+ sizeof(struct audit_watch), 0, 0, NULL, NULL);
+ audit_wentry_cache =
+ kmem_cache_create("audit_wentry_cache",
+ sizeof(struct audit_wentry), 0, 0, NULL, NULL);
+ audit_data_cache =
+ kmem_cache_create("audit_data_cache",
+ sizeof(struct audit_data), 0, 0, NULL, NULL);
+}
diff -Nurp linux-2.6.11/kernel/auditsc.c linux-2.6.11-audit/kernel/auditsc.c
--- linux-2.6.11/kernel/auditsc.c 2005-03-02 01:38:17.000000000 -0600
+++ linux-2.6.11-audit/kernel/auditsc.c 2005-03-07 12:31:06.000000000 -0600
@@ -19,6 +19,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
+ * Modified by Timothy R. Chavez <chavezt(a)us.ibm.com>
*
* Many of the ideas implemented here are from Stephen C. Tweedie,
* especially the idea of avoiding a copy by using getname.
@@ -48,6 +49,7 @@
/* No syscall auditing will take place unless audit_enabled != 0. */
extern int audit_enabled;
+extern int auditfs_enabled;
/* AUDIT_NAMES is the number of slots we reserve in the audit_context
* for saving names from getname(). */
@@ -113,6 +115,10 @@ struct audit_context {
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;
+ struct list_head wtrail; /* The list of watched files/dirs that were
+ * accessed and determined to be valid and
+ * unfiltered in this audit_context
+ */
#if AUDIT_DEBUG
int put_count;
@@ -134,6 +140,22 @@ struct audit_entry {
struct audit_rule rule;
};
+/* The structure that stores information about files/directories being
+ * watched in the filesystem, that the syscall accessed.
+ */
+
+struct audit_file {
+ struct audit_wentry *wentry;
+ struct list_head list;
+ unsigned long ino;
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t dev;
+ dev_t rdev;
+ int mask;
+};
+
/* Check to see if two rules are identical. It is called from
* audit_del_rule during AUDIT_DEL. */
static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
@@ -504,6 +526,37 @@ static inline void audit_free_names(stru
context->name_count = 0;
}
+static struct audit_file *audit_file_alloc(void)
+{
+ struct audit_file *file;
+
+ file = kmalloc(sizeof(struct audit_file), GFP_KERNEL);
+
+ if (file)
+ file->wentry = NULL;
+
+ return file;
+}
+
+static void audit_file_free(struct audit_file *file)
+{
+ if (file) {
+ audit_wentry_put(file->wentry);
+ file->wentry = NULL;
+ kfree(file);
+ }
+}
+
+static inline void audit_free_files(struct audit_context *context)
+{
+ struct audit_file *file, *tmp;
+
+ list_for_each_entry_safe(file, tmp, &context->wtrail, list) {
+ list_del(&file->list);
+ audit_file_free(file);
+ }
+}
+
static inline void audit_zero_context(struct audit_context *context,
enum audit_state state)
{
@@ -512,6 +565,7 @@ static inline void audit_zero_context(st
memset(context, 0, sizeof(*context));
context->state = state;
context->loginuid = loginuid;
+ INIT_LIST_HEAD(&context->wtrail);
}
static inline struct audit_context *audit_alloc_context(enum audit_state state)
@@ -570,6 +624,7 @@ static inline void audit_free_context(st
context->name_count, count);
}
audit_free_names(context);
+ audit_free_files(context);
kfree(context);
context = previous;
} while (context);
@@ -580,6 +635,7 @@ static inline void audit_free_context(st
static void audit_log_exit(struct audit_context *context)
{
int i;
+ struct audit_file *file;
struct audit_buffer *ab;
ab = audit_log_start(context);
@@ -626,6 +682,28 @@ static void audit_log_exit(struct audit_
MINOR(context->names[i].rdev));
audit_log_end(ab);
}
+
+ if (!auditfs_enabled)
+ return;
+
+ list_for_each_entry(file, &context->wtrail, list) {
+ ab = audit_log_start(context);
+ if (!ab)
+ continue;
+
+ /* Do we need more information? */
+ audit_log_format(ab,
+ "name=%s filter_key=%s perm_mask=%u perm=%d inode=%lu "
+ "inode_mode=%d inode_uid=%d inode_gid=%d "
+ "inode_dev=%02x:%02x inode_rdev=%02x:%02x",
+ file->wentry->w_watch->name,
+ file->wentry->w_watch->filterkey,
+ file->wentry->w_watch->perms,
+ file->mask, file->ino, file->mode, file->uid,
+ file->gid, MAJOR(file->dev), MINOR(file->dev),
+ MAJOR(file->rdev), MINOR(file->rdev));
+ audit_log_end(ab);
+ }
}
/* Free a per-task audit context. Called from copy_process and
@@ -789,6 +867,7 @@ void audit_syscall_exit(struct task_stru
tsk->audit_context = new_context;
} else {
audit_free_names(context);
+ audit_free_files(context);
audit_zero_context(context, context->state);
tsk->audit_context = context;
}
@@ -927,3 +1006,59 @@ uid_t audit_get_loginuid(struct audit_co
{
return ctx ? ctx->loginuid : -1;
}
+
+/* If file/dir has an audit_context and has filesystem auditing
+ * turned on, then if this inode is being watched, collect info
+ * about it.
+ *
+ * Hook appears in:
+ * fs/namie.c:permission(), exec_permission_lite(), vfs_unlink/rmdir
+ *
+ */
+
+int audit_notify_watch(struct inode *inode, int mask)
+{
+ int ret = 0;
+ struct audit_context *context;
+ struct audit_file *file;
+
+ context = current->audit_context;
+
+ if (!auditfs_enabled)
+ goto audit_notify_watch_exit;
+
+ if (!context || !context->in_syscall)
+ goto audit_notify_watch_exit;
+
+ if (!inode || !inode->i_audit)
+ goto audit_notify_watch_exit;
+
+ if (!inode->i_audit->wentry || !inode->i_audit->wentry->w_valid)
+ goto audit_notify_watch_exit;
+
+ if (!mask || (inode->i_audit->wentry->w_watch->perms &&
+ !(inode->i_audit->wentry->w_watch->perms & mask))) {
+ audit_free_files(context);
+ goto audit_notify_watch_exit;
+ }
+
+ file = audit_file_alloc();
+ if (!file) {
+ ret = -ENOMEM;
+ goto audit_notify_watch_exit;
+ }
+
+ file->wentry = audit_wentry_get(inode->i_audit->wentry);
+ file->ino = inode->i_ino;
+ file->uid = inode->i_uid;
+ file->gid = inode->i_gid;
+ file->dev = inode->i_sb->s_dev;
+ file->rdev = inode->i_rdev;
+ file->mask = mask;
+
+ list_add(&file->list, &context->wtrail);
+
+ audit_notify_watch_exit:
+ return ret;
+}
+
19 years, 9 months
[RFC][PATCH] auditfs userspace
by Timothy R. Chavez
Here's the userspace patch for audit-0.6.5 -- It doesn't work as is.
To get this working I had to make a directory audit-0.6.5/linux and cp
/ ln -s audit.h into it. The patch should do this for you. However,
you will have to 'install -m 0644 lib/libaudit.h audit-0.6.5/'
yourself.
cd audit-0.6.5/
autoreconf -fv --install
./configure --sbindir=/path/to/sbin --libdir=/lib --with-pam=yes/no
install -m 0644 lib/libaudit.h .
make
It's probably just best to not "make install" and just cd src/ and run
auditctl from there for testing the auditfs kernel piece. I'm hoping
that some form of this code will eventually be integrated into the
userspace tool.
So please break..er test my code :)
--
- Timothy R. Chavez
19 years, 9 months
Audit Filesystem
by Steve Grubb
Hi,
I was trying to create a new kernel with all the latest patches. I ran into
this error:
+ make ARCH=i386 nonint_oldconfig
.config:2128: trying to assign nonexistent symbol USB_PWC
CONFIG_AUDITFILESYSTEM
make[1]: *** [nonint_oldconfig] Error 1
make: *** [nonint_oldconfig] Error 2
Which makes me wonder why the filesystem auditing piece is a separate compile
option? Do we want ala carte or a unified audit system? How does the user
space tools find out what was compiled in?
I would like to suggest we get rid of this config option and make it all run
as the audit system. Is there any reason to make it configurable?
Thanks,
-Steve Grubb
19 years, 9 months