[PATCH] LSPP audit enablement: storing selinux ocontext and scontext
by Dustin Kirkland
The attached patch contains functionality specified by the labeled
security protection profile--basically appending object context and
subject context labels to audit records.
This code was originally written by Dan Jones against a 2.6.9 kernel
tree. I have ported it forward to the 2.6.13-rc3-mm1 tree and tested
it.
I expect at least one section of this code to generate some animated
discussion. This code utilizes "static inline" function definitions
rather than "#define" macros, as per Documentation/SubmittingPatches
lines 328-372 and Documentation/CodingStyle line 358. This is
inconsistent with the rest of the code in include/linux/audit.h, so I
suspect someone will have to give, in the interest of consistency.
Please advise.
Additionally, we've arbitrarily defined AUDIT_SECURITY_CONTEXT = 1350.
We expect this to change to whatever the appropriate value might be.
Signed-off-by: Dustin Kirkland <dustin.kirkland(a)us.ibm.com>
diff -uprN linux-2.6.13-rc3-mm1/include/linux/audit.h
linux-2.6.13-rc3-mm1-lspp_audit/include/linux/audit.h
--- linux-2.6.13-rc3-mm1/include/linux/audit.h 2005-07-18 15:20:34.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/include/linux/audit.h 2005-07-19 11:33:37.000000000 -0500
@@ -68,6 +68,7 @@
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
#define AUDIT_CWD 1307 /* Current working directory */
+#define AUDIT_SECURITY_CONTEXT 1350 /* security context */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -238,6 +239,8 @@ extern int audit_sockaddr(int len, void
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
extern void audit_signal_info(int sig, struct task_struct *t);
extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
+extern int audit_ipc_security_context(struct kern_ipc_perm *ipcp);
+extern int audit_set_macxattr(const char *name);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -255,6 +258,14 @@ extern int audit_filter_user(struct netl
#define audit_avc_path(dentry, mnt) ({ 0; })
#define audit_signal_info(s,t) do { ; } while (0)
#define audit_filter_user(cb,t) ({ 1; })
+static inline int audit_ipc_security_context(struct kern_ipc_perm *ipcp)
+{
+ return 0;
+}
+static inline int audit_set_macxattr(const char *name)
+{
+ return 0;
+}
#endif
#ifdef CONFIG_AUDIT
@@ -283,6 +294,7 @@ extern void audit_send_reply(int pi
int done, int multi,
void *payload, int size);
extern void audit_log_lost(const char *message);
+extern void audit_panic(const char *message);
extern struct semaphore audit_netlink_sem;
#else
#define audit_log(c,g,t,f,...) do { ; } while (0)
@@ -293,6 +305,10 @@ extern struct semaphore audit_netlink_se
#define audit_log_hex(a,b,l) do { ; } while (0)
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_d_path(b,p,d,v) do { ; } while (0)
+static inline void audit_panic(const char *message)
+{
+ return;
+}
#endif
#endif
#endif
diff -uprN linux-2.6.13-rc3-mm1/include/linux/security.h linux-2.6.13-rc3-mm1-lspp_audit/include/linux/security.h
--- linux-2.6.13-rc3-mm1/include/linux/security.h 2005-07-18 15:20:35.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/include/linux/security.h 2005-07-18 16:35:02.000000000 -0500
@@ -792,6 +792,11 @@ struct swap_info_struct;
* @ipcp contains the kernel IPC permission structure
* @flag contains the desired (requested) permission set
* Return 0 if permission is granted.
+ * @ipc_getsecurity:
+ * Copy the security label associated with the ipc object into
+ * @buffer. @buffer may be NULL to request the size of the buffer
+ * required. @size indicates the size of @buffer in bytes. Return
+ * number of bytes used/required on success.
*
* Security hooks for individual messages held in System V IPC message queues
* @msg_msg_alloc_security:
@@ -1140,6 +1145,7 @@ struct security_operations {
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
+ int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size);
int (*msg_msg_alloc_security) (struct msg_msg * msg);
void (*msg_msg_free_security) (struct msg_msg * msg);
@@ -1775,6 +1781,11 @@ static inline int security_ipc_permissio
return security_ops->ipc_permission (ipcp, flag);
}
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+ return security_ops->ipc_getsecurity(ipcp, buffer, size);
+}
+
static inline int security_msg_msg_alloc (struct msg_msg * msg)
{
return security_ops->msg_msg_alloc_security (msg);
@@ -2405,6 +2416,11 @@ static inline int security_ipc_permissio
return 0;
}
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int security_msg_msg_alloc (struct msg_msg * msg)
{
return 0;
diff -uprN linux-2.6.13-rc3-mm1/ipc/msg.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/msg.c
--- linux-2.6.13-rc3-mm1/ipc/msg.c 2005-06-17 14:48:29.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/msg.c 2005-07-18 16:35:02.000000000 -0500
@@ -441,6 +441,13 @@ asmlinkage long sys_msgctl (int msqid, i
if (msq == NULL)
goto out_up;
+ ipcp = &msq->q_perm;
+
+ if (cmd == IPC_SET) {
+ if ((err = audit_ipc_security_context(ipcp)))
+ goto out_unlock_up;
+ }
+
err = -EIDRM;
if (msg_checkid(msq,msqid))
goto out_unlock_up;
diff -uprN linux-2.6.13-rc3-mm1/ipc/sem.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/sem.c
--- linux-2.6.13-rc3-mm1/ipc/sem.c 2005-07-18 15:20:05.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/sem.c 2005-07-18 16:35:02.000000000 -0500
@@ -811,12 +811,18 @@ static int semctl_down(int semid, int se
if(sma==NULL)
return -EINVAL;
+ ipcp = &sma->sem_perm;
+
+ if(cmd == IPC_SET) {
+ if ((err = audit_ipc_security_context(ipcp)))
+ goto out_unlock;
+ }
+
if (sem_checkid(sma,semid)) {
err=-EIDRM;
goto out_unlock;
}
- ipcp = &sma->sem_perm;
-
+
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
err=-EPERM;
diff -uprN linux-2.6.13-rc3-mm1/ipc/shm.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/shm.c
--- linux-2.6.13-rc3-mm1/ipc/shm.c 2005-06-17 14:48:29.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/shm.c 2005-07-18 16:35:02.000000000 -0500
@@ -610,6 +610,9 @@ asmlinkage long sys_shmctl (int shmid, i
err=-EINVAL;
if(shp==NULL)
goto out_up;
+ err = audit_ipc_security_context(&(shp->shm_perm));
+ if(err)
+ goto out_unlock_up;
err = shm_checkid(shp,shmid);
if(err)
goto out_unlock_up;
diff -uprN linux-2.6.13-rc3-mm1/ipc/util.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/util.c
--- linux-2.6.13-rc3-mm1/ipc/util.c 2005-06-17 14:48:29.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/util.c 2005-07-18 16:35:02.000000000 -0500
@@ -24,6 +24,7 @@
#include <linux/security.h>
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
+#include <linux/audit.h>
#include <asm/unistd.h>
@@ -420,6 +421,8 @@ int ipcperms (struct kern_ipc_perm *ipcp
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
int requested_mode, granted_mode;
+ audit_ipc_security_context(ipcp);
+
requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode;
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
diff -uprN linux-2.6.13-rc3-mm1/kernel/audit.c linux-2.6.13-rc3-mm1-lspp_audit/kernel/audit.c
--- linux-2.6.13-rc3-mm1/kernel/audit.c 2005-07-18 15:20:35.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/kernel/audit.c 2005-07-18 16:35:02.000000000 -0500
@@ -147,7 +147,7 @@ struct audit_entry {
struct audit_rule rule;
};
-static void audit_panic(const char *message)
+void audit_panic(const char *message)
{
switch (audit_failure)
{
@@ -896,3 +896,4 @@ void audit_log(struct audit_context *ctx
audit_log_end(ab);
}
}
+
diff -uprN linux-2.6.13-rc3-mm1/kernel/auditsc.c linux-2.6.13-rc3-mm1-lspp_audit/kernel/auditsc.c
--- linux-2.6.13-rc3-mm1/kernel/auditsc.c 2005-07-18 15:20:35.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/kernel/auditsc.c 2005-07-19 11:50:15.000000000 -0500
@@ -43,6 +43,7 @@
#include <linux/netlink.h>
#include <linux/compiler.h>
#include <asm/unistd.h>
+#include <linux/security.h>
/* 0 = no checking
1 = put_count checking
@@ -99,6 +100,7 @@ struct audit_names {
gid_t gid;
dev_t rdev;
unsigned flags;
+ char *security_context;
};
struct audit_aux_data {
@@ -108,6 +110,12 @@ struct audit_aux_data {
#define AUDIT_AUX_IPCPERM 0
+struct audit_aux_data_security_context {
+ struct audit_aux_data d;
+ char *security_context;
+ size_t security_context_len;
+};
+
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
struct ipc_perm p;
@@ -167,6 +175,8 @@ struct audit_context {
#endif
};
+static const char *audit_macxattr;
+
/* Public API */
/* There are three lists of rules -- one to search at task creation
* time, one to search at syscall entry time, and another to search at
@@ -650,9 +660,11 @@ static inline void audit_free_names(stru
context->ino_count = 0;
#endif
- for (i = 0; i < context->name_count; i++)
+ for (i = 0; i < context->name_count; i++) {
if (context->names[i].name)
__putname(context->names[i].name);
+ kfree(context->names[i].security_context);
+ }
context->name_count = 0;
if (context->pwd)
dput(context->pwd);
@@ -751,6 +763,37 @@ static inline void audit_free_context(st
printk(KERN_ERR "audit: freed %d contexts\n", count);
}
+static void audit_log_task_security_context(struct audit_buffer *ab)
+{
+ char *security_context;
+ ssize_t len = 0;
+
+ len = security_getprocattr(current, "current", NULL, 0);
+ if (len < 0) {
+ if (len != -EINVAL)
+ audit_panic("security_getprocattr error in audit_log_task_security_context");
+ return;
+ }
+
+ security_context = kmalloc(len, GFP_KERNEL);
+ if (!security_context) {
+ audit_panic("memory allocation error in audit_log_task_security_context");
+ return;
+ }
+
+ len = security_getprocattr(current, "current", security_context, len);
+ if (len < 0 ) {
+ audit_panic("security_getprocattr error in audit_log_task_security_context");
+ goto out;
+ }
+
+ audit_log_format(ab, " scontext=%s", security_context);
+
+out:
+ kfree(security_context);
+ return;
+}
+
static void audit_log_task_info(struct audit_buffer *ab)
{
char name[sizeof(current->comm)];
@@ -777,6 +820,7 @@ static void audit_log_task_info(struct a
vma = vma->vm_next;
}
up_read(&mm->mmap_sem);
+ audit_log_task_security_context(ab);
}
static void audit_log_exit(struct audit_context *context, unsigned int gfp_mask)
@@ -848,6 +892,11 @@ static void audit_log_exit(struct audit_
struct audit_aux_data_path *axi = (void *)aux;
audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
break; }
+ case AUDIT_SECURITY_CONTEXT: {
+ struct audit_aux_data_security_context *axi = (void *)aux;
+ audit_log_format(ab, " ocontext=%s", axi->security_context);
+ kfree(axi->security_context);
+ break; }
}
audit_log_end(ab);
@@ -883,6 +932,10 @@ static void audit_log_exit(struct audit_
context->names[i].gid,
MAJOR(context->names[i].rdev),
MINOR(context->names[i].rdev));
+ if (context->names[i].security_context) {
+ audit_log_format(ab, " ocontext=%s",
+ context->names[i].security_context);
+ }
audit_log_end(ab);
}
}
@@ -1098,6 +1151,37 @@ void audit_putname(const char *name)
#endif
}
+void audit_inode_security_context(int idx, const struct inode *inode)
+{
+ struct audit_context *context = current->audit_context;
+ int len = 0;
+
+ if (!audit_macxattr)
+ return;
+
+ len = security_inode_getsecurity((struct inode *)inode, audit_macxattr, NULL, 0);
+ if (len < 0) {
+ if (len != -EOPNOTSUPP)
+ audit_panic("security_inode_getsecurity error in audit_inode_security_context");
+ return;
+ }
+
+ context->names[idx].security_context = kmalloc(len, GFP_KERNEL);
+ if (!(context->names[idx].security_context)) {
+ audit_panic("memory allocation error in audit_inode_security_context");
+ return;
+ }
+
+ len = security_inode_getsecurity((struct inode *)inode, audit_macxattr,
+ context->names[idx].security_context, len);
+ if (len < 0) {
+ kfree(context->names[idx].security_context);
+ audit_panic("security_inode_getsecurity error in audit_inode_security_context");
+ }
+
+ return;
+}
+
/* Store the inode and device from a lookup. Called from
* fs/namei.c:path_lookup(). */
void audit_inode(const char *name, const struct inode *inode, unsigned flags)
@@ -1133,6 +1217,8 @@ void audit_inode(const char *name, const
context->names[idx].uid = inode->i_uid;
context->names[idx].gid = inode->i_gid;
context->names[idx].rdev = inode->i_rdev;
+
+ audit_inode_security_context(idx, inode);
}
void auditsc_get_stamp(struct audit_context *ctx,
@@ -1270,3 +1356,63 @@ void audit_signal_info(int sig, struct t
}
}
+int audit_ipc_security_context(struct kern_ipc_perm *ipcp)
+{
+ struct audit_aux_data_security_context *ax;
+ struct audit_context *context = current->audit_context;
+ int len = 0;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax) {
+ audit_panic("memory allocation error in audit_ipc_security_context");
+ return -ENOMEM;
+ }
+
+ len = security_ipc_getsecurity(ipcp, NULL, 0);
+ if (len < 0) {
+ if (len != -EOPNOTSUPP)
+ audit_panic("security_ipc_getsecurity error in audit_ipc_security_context");
+ return len;
+ }
+
+ ax->security_context = kmalloc(len, GFP_ATOMIC);
+ if (!ax->security_context) {
+ audit_panic("memory allocation error in audit_ipc_security_context");
+ kfree(ax);
+ return -ENOMEM;
+ }
+
+ len = security_ipc_getsecurity(ipcp, ax->security_context, len);
+ if (len < 0) {
+ audit_panic("security_ipc_getsecurity error in audit_ipc_security_context");
+ kfree(ax->security_context);
+ kfree(ax);
+ return len;
+ }
+
+ ax->security_context_len = len;
+
+ ax->d.type = AUDIT_SECURITY_CONTEXT;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/* Set the security XATTR name. This is needed for audit functions
+ * to obtain the security context of file system objects. */
+int audit_set_macxattr(const char *name)
+{
+ size_t len = strlen(name)+1;
+
+ if (audit_macxattr)
+ return -EINVAL;
+
+ audit_macxattr = kmalloc(len, GFP_KERNEL);
+ if (!audit_macxattr)
+ return -ENOMEM;
+ memcpy((void *)audit_macxattr, (const void *)name, len);
+ return 0;
+}
diff -uprN linux-2.6.13-rc3-mm1/security/dummy.c linux-2.6.13-rc3-mm1-lspp_audit/security/dummy.c
--- linux-2.6.13-rc3-mm1/security/dummy.c 2005-07-18 15:20:36.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/security/dummy.c 2005-07-18 16:35:02.000000000 -0500
@@ -557,6 +557,11 @@ static int dummy_ipc_permission (struct
return 0;
}
+static int dummy_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
{
return 0;
@@ -907,6 +912,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, task_reparent_to_init);
set_to_dummy_if_null(ops, task_to_inode);
set_to_dummy_if_null(ops, ipc_permission);
+ set_to_dummy_if_null(ops, ipc_getsecurity);
set_to_dummy_if_null(ops, msg_msg_alloc_security);
set_to_dummy_if_null(ops, msg_msg_free_security);
set_to_dummy_if_null(ops, msg_queue_alloc_security);
diff -uprN linux-2.6.13-rc3-mm1/security/selinux/hooks.c linux-2.6.13-rc3-mm1-lspp_audit/security/selinux/hooks.c
--- linux-2.6.13-rc3-mm1/security/selinux/hooks.c 2005-07-18 15:20:36.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/security/selinux/hooks.c 2005-07-19 10:56:44.000000000 -0500
@@ -116,6 +116,35 @@ static struct security_operations *secon
static LIST_HEAD(superblock_security_head);
static DEFINE_SPINLOCK(sb_security_lock);
+/* Return security context for a given sid or just the context
+ length if the buffer is null or length is 0 */
+static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
+{
+ char *context;
+ unsigned len;
+ int rc;
+
+ if (buffer && !size)
+ return -ERANGE;
+
+ rc = security_sid_to_context(sid, &context, &len);
+ if (rc)
+ return rc;
+
+ if (!buffer || !size)
+ goto getsecurity_exit;
+
+ if (size < len) {
+ kfree(context);
+ return -ERANGE;
+ }
+ memcpy(buffer, context, len);
+
+getsecurity_exit:
+ kfree(context);
+ return len;
+}
+
/* Allocate and free functions for each kind of security blob. */
static int task_alloc_security(struct task_struct *task)
@@ -2232,30 +2261,13 @@ static int selinux_inode_removexattr (st
static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
{
struct inode_security_struct *isec = inode->i_security;
- char *context;
- unsigned len;
- int rc;
/* Permission check handled by selinux_inode_getxattr hook.*/
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
- rc = security_sid_to_context(isec->sid, &context, &len);
- if (rc)
- return rc;
-
- if (!buffer || !size) {
- kfree(context);
- return len;
- }
- if (size < len) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(buffer, context, len);
- kfree(context);
- return len;
+ return(selinux_getsecurity(isec->sid, buffer, size));
}
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
@@ -3360,6 +3372,13 @@ out:
return err;
}
+static int selinux_socket_getsecurity(struct socket *sock, void *buffer, size_t size)
+{
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+
+ return(selinux_getsecurity(isec->sid, buffer, size));
+}
+
static int selinux_sk_alloc_security(struct sock *sk, int family, int priority)
{
return sk_alloc_security(sk, family, priority);
@@ -4025,6 +4044,13 @@ static int selinux_ipc_permission(struct
return ipc_has_perm(ipcp, av);
}
+static int selinux_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+ struct ipc_security_struct *isec = ipcp->security;
+
+ return(selinux_getsecurity(isec->sid, buffer, size));
+}
+
/* module stacking operations */
static int selinux_register_security (const char *name, struct security_operations *ops)
{
@@ -4066,8 +4092,7 @@ static int selinux_getprocattr(struct ta
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
- u32 sid, len;
- char *context;
+ u32 sid;
int error;
if (current != p) {
@@ -4076,9 +4101,6 @@ static int selinux_getprocattr(struct ta
return error;
}
- if (!size)
- return -ERANGE;
-
tsec = p->security;
if (!strcmp(name, "current"))
@@ -4095,16 +4117,7 @@ static int selinux_getprocattr(struct ta
if (!sid)
return 0;
- error = security_sid_to_context(sid, &context, &len);
- if (error)
- return error;
- if (len > size) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(value, context, len);
- kfree(context);
- return len;
+ return(selinux_getsecurity(sid, value, size));
}
static int selinux_setprocattr(struct task_struct *p,
@@ -4299,6 +4312,7 @@ static struct security_operations selinu
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
+ .ipc_getsecurity = selinux_ipc_getsecurity,
.msg_msg_alloc_security = selinux_msg_msg_alloc_security,
.msg_msg_free_security = selinux_msg_msg_free_security,
@@ -4379,6 +4393,10 @@ static __init int selinux_init(void)
if (register_security (&selinux_ops))
panic("SELinux: Unable to register with kernel.\n");
+ if (audit_set_macxattr(XATTR_SELINUX_SUFFIX)) {
+ printk(KERN_WARNING "SELinux: Unable to register xattr name with audit.\n");
+ }
+
if (selinux_enforcing) {
printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
} else {
19 years, 2 months
[PATCH] kerneldoc for kernel/audit*.c
by randy_dunlap
From: Randy Dunlap <rdunlap(a)xenotime.net>
for kernel/audit*.c:
- add kerneldoc for non-static functions;
- don't init static data to 0;
- limit lines to < 80 columns;
- fix long-format style;
- delete whitespace at end of some lines;
- break a for loop into 2 lines;
Signed-off-by: Randy Dunlap <rdunlap(a)xenotime.net>
diffstat:=
kernel/audit.c | 133 ++++++++++++++++++++++++++++++++++++-----------
kernel/auditsc.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 236 insertions(+), 50 deletions(-)
diff -Naurp linux-2613-rc1-git5/kernel/audit.c~kdoc_kernel_audit linux-2613-rc1-git5/kernel/audit.c
--- linux-2613-rc1-git5/kernel/audit.c~kdoc_kernel_audit 2005-07-03 20:38:57.000000000 -0700
+++ linux-2613-rc1-git5/kernel/audit.c 2005-07-07 11:46:21.000000000 -0700
@@ -72,7 +72,7 @@ static int audit_failure = AUDIT_FAIL_PR
* contains the (non-zero) pid. */
int audit_pid;
-/* If audit_limit is non-zero, limit the rate of sending audit records
+/* If audit_rate_limit is non-zero, limit the rate of sending audit records
* to that number per second. This prevents DoS attacks, but results in
* audit records being dropped. */
static int audit_rate_limit;
@@ -100,7 +100,7 @@ static struct sock *audit_sock;
* than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
* being placed on the freelist). */
static DEFINE_SPINLOCK(audit_freelist_lock);
-static int audit_freelist_count = 0;
+static int audit_freelist_count;
static LIST_HEAD(audit_freelist);
static struct sk_buff_head audit_skb_queue;
@@ -194,8 +194,14 @@ static inline int audit_rate_check(void)
return retval;
}
-/* Emit at least 1 message per second, even if audit_rate_check is
- * throttling. */
+/**
+ * audit_log_lost - conditionally log an audit message
+ * @message: the message to be logged
+ *
+ * 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;
@@ -226,7 +232,6 @@ void audit_log_lost(const char *message)
audit_backlog_limit);
audit_panic(message);
}
-
}
static int audit_set_rate_limit(int limit, uid_t loginuid)
@@ -307,6 +312,19 @@ int kauditd_thread(void *dummy)
}
}
+/**
+ * audit_send_reply - send an audit reply message via netlink
+ * @pid: process id of the listener
+ * @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)
{
@@ -381,7 +399,8 @@ static int audit_receive_msg(struct sk_b
if (err)
return err;
- /* As soon as there's any sign of userspace auditd, start kauditd to talk to it */
+ /* As soon as there's any sign of userspace auditd,
+ * start kauditd to talk to it */
if (!kauditd_task)
kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");
if (IS_ERR(kauditd_task)) {
@@ -468,9 +487,11 @@ static int audit_receive_msg(struct sk_b
return err < 0 ? err : 0;
}
-/* Get message from skb (based on rtnetlink_rcv_skb). Each message is
+/*
+ * Get message from skb (based on rtnetlink_rcv_skb). Each message is
* processed by audit_receive_msg. Malformed skbs with wrong length are
- * discarded silently. */
+ * discarded silently.
+ */
static void audit_receive_skb(struct sk_buff *skb)
{
int err;
@@ -597,7 +618,10 @@ err:
return NULL;
}
-/* Compute a serial number for the audit record. Audit records are
+/**
+ * audit_serial - compute a serial number for the audit record
+ *
+ * Compute a serial number for the audit record. Audit records are
* written to user-space as soon as they are generated, so a complete
* audit record may be written in several pieces. The timestamp of the
* record and this serial number are used by the user-space tools to
@@ -611,7 +635,8 @@ err:
* audit context (for those records that have a context), and emit them
* all at syscall exit. However, this could delay the reporting of
* significant errors until syscall exit (or never, if the system
- * halts). */
+ * halts).
+ */
unsigned int audit_serial(void)
{
static atomic_t serial = ATOMIC_INIT(0xffffff);
@@ -638,12 +663,20 @@ static inline void audit_get_stamp(struc
}
}
-/* Obtain an audit buffer. This routine does locking to obtain the
+/**
+ * audit_log_start - obtain an audit buffer
+ * @ctx: audit_context (may be NULL)
+ * @type: audit netlink 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 tsk is a task that is currently in a
+ * 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, tsk
- * should be NULL. */
+ * 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, int type)
{
struct audit_buffer *ab = NULL;
@@ -681,6 +714,7 @@ struct audit_buffer *audit_log_start(str
/**
* 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.
@@ -697,10 +731,12 @@ static inline int audit_expand(struct au
return skb_tailroom(skb);
}
-/* Format an audit message into the audit buffer. If there isn't enough
+/*
+ * Format an audit message into the audit buffer. If there isn't enough
* room in the audit buffer, more room will be allocated and vsnprint
* will be called a second time. Currently, we assume that a printk
- * can't format message larger than 1024 bytes, so we don't either. */
+ * can't format message larger than 1024 bytes, so we don't either.
+ */
static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
va_list args)
{
@@ -725,7 +761,8 @@ static void audit_log_vformat(struct aud
/* The printk buffer is 1024 bytes long, so if we get
* here and AUDIT_BUFSIZ is at least 1024, then we can
* log everything that printk could have logged. */
- avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
+ avail = audit_expand(ab,
+ max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
if (!avail)
goto out;
len = vsnprintf(skb->tail, avail, fmt, args2);
@@ -736,8 +773,14 @@ out:
return;
}
-/* Format a message into the audit buffer. All the work is done in
- * audit_log_vformat. */
+/**
+ * audit_log_format - format a message into the audit buffer.
+ * @ab: audit_buffer
+ * @fmt: format string
+ * @...: optional parameters matching @fmt string
+ *
+ * All the work is done in audit_log_vformat.
+ */
void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
{
va_list args;
@@ -749,9 +792,18 @@ void audit_log_format(struct audit_buffe
va_end(args);
}
-/* This function will take the passed buf and convert it into a string of
- * ascii hex digits. The new string is placed onto the skb. */
-void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
+/**
+ * audit_log_hex - convert a buffer to hex and append it to the audit skb
+ * @ab: the audit_buffer
+ * @buf: buffer to convert to hex
+ * @len: length of @buf to be converted
+ *
+ * No return value; failure to expand is silently ignored.
+ *
+ * This function will take the passed buf and convert it into a string of
+ * ascii hex digits. The new string is placed onto the skb.
+ */
+void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
size_t len)
{
int i, avail, new_len;
@@ -780,10 +832,16 @@ void audit_log_hex(struct audit_buffer *
skb_put(skb, len << 1); /* new string is twice the old string */
}
-/* This code will escape a string that is passed to it if the string
- * contains a control character, unprintable character, double quote mark,
+/**
+ * audit_log_unstrustedstring - log a string that may contain random characters
+ * @ab: audit_buffer
+ * @string: string to be logged
+ *
+ * This code will escape a string that is passed to it if the string
+ * contains a control character, unprintable character, double quote mark,
* or a space. Unescaped strings will start and end with a double quote mark.
- * Strings that are escaped are printed in hex (2 digits per char). */
+ * Strings that are escaped are printed in hex (2 digits per char).
+ */
void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
{
const unsigned char *p = string;
@@ -822,10 +880,15 @@ void audit_log_d_path(struct audit_buffe
kfree(path);
}
-/* The netlink_* functions cannot be called inside an irq context, so
- * the audit buffer is places on a queue and a tasklet is scheduled to
+/**
+ * audit_log_end - end one audit record
+ * @ab: the audit_buffer
+ *
+ * The netlink_* functions cannot be called inside an irq context, so
+ * the audit buffer is placed on a queue and a tasklet is scheduled to
* remove them from the queue outside the irq context. May be called in
- * any context. */
+ * any context.
+ */
void audit_log_end(struct audit_buffer *ab)
{
if (!ab)
@@ -846,9 +909,17 @@ void audit_log_end(struct audit_buffer *
audit_buffer_free(ab);
}
-/* Log an audit record. This is a convenience function that calls
- * audit_log_start, audit_log_vformat, and audit_log_end. It may be
- * called in any context. */
+/**
+ * audit_log - log an audit record
+ * @ctx: the audit_context
+ * @type: the 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, int type, const char *fmt, ...)
{
struct audit_buffer *ab;
diff -Naurp linux-2613-rc1-git5/kernel/auditsc.c~kdoc_kernel_audit linux-2613-rc1-git5/kernel/auditsc.c
--- linux-2613-rc1-git5/kernel/auditsc.c~kdoc_kernel_audit 2005-07-03 20:38:57.000000000 -0700
+++ linux-2613-rc1-git5/kernel/auditsc.c 2005-07-07 10:57:44.000000000 -0700
@@ -268,10 +268,21 @@ static int audit_copy_rule(struct audit_
d->fields[i] = s->fields[i];
d->values[i] = s->values[i];
}
- for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
+ for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+ d->mask[i] = s->mask[i];
return 0;
}
+/**
+ * audit_receive_filter - apply all rules to the specified message type
+ * @type: audit message type
+ * @pid: target pid for netlink audit messages
+ * @uid: target uid for netlink audit messages
+ * @seq: netlink audit message sequence (serial) number
+ * @data: payload data
+ * @loginuid: loginuid of sender
+ *
+ */
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
uid_t loginuid)
{
@@ -467,7 +478,7 @@ static enum audit_state audit_filter_tas
/* 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).
+ * 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,
@@ -597,10 +608,15 @@ static inline struct audit_context *audi
return context;
}
-/* Filter on the task information and allocate a per-task audit 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. */
+ * needed.
+ */
int audit_alloc(struct task_struct *tsk)
{
struct audit_context *context;
@@ -785,8 +801,12 @@ static void audit_log_exit(struct audit_
}
}
-/* Free a per-task audit context. Called from copy_process and
- * __put_task_struct. */
+/**
+ * audit_free - free a per-task audit context
+ * @tsk: task whose audit context block to free
+ *
+ * Called from copy_process and __put_task_struct.
+ */
void audit_free(struct task_struct *tsk)
{
struct audit_context *context;
@@ -806,13 +826,24 @@ void audit_free(struct task_struct *tsk)
audit_free_context(context);
}
-/* Fill in audit context at syscall entry. This only happens if the
+/**
+ * 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). */
+ * be written).
+ */
void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4)
@@ -822,7 +853,8 @@ void audit_syscall_entry(struct task_str
BUG_ON(!context);
- /* This happens only on certain architectures that make system
+ /*
+ * 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
@@ -886,11 +918,18 @@ void audit_syscall_entry(struct task_str
context->auditable = !!(state == AUDIT_RECORD_CONTEXT);
}
-/* Tear down after system call. If the audit context has been marked as
+/**
+ * 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(). */
+ * free the names stored from getname().
+ */
void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
{
struct audit_context *context;
@@ -925,7 +964,13 @@ void audit_syscall_exit(struct task_stru
put_task_struct(tsk);
}
-/* Add a name to the list. Called from fs/namei.c:getname(). */
+/**
+ * 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;
@@ -954,10 +999,13 @@ void audit_getname(const char *name)
}
-/* Intercept a putname request. Called from
- * include/linux/fs.h:putname(). If we have stored the name from
- * getname in the audit context, then we delay the putname until syscall
- * exit. */
+/* 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;
@@ -994,8 +1042,13 @@ void audit_putname(const char *name)
#endif
}
-/* Store the inode and device from a lookup. Called from
- * fs/namei.c:path_lookup(). */
+/**
+ * audit_inode - store the inode and device from a lookup
+ * @name: name being audited
+ * @inode: inode being audited
+ *
+ * Called from fs/namei.c:path_lookup().
+ */
void audit_inode(const char *name, const struct inode *inode)
{
int idx;
@@ -1030,6 +1083,14 @@ void audit_inode(const char *name, const
context->names[idx].rdev = inode->i_rdev;
}
+/**
+ * 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)
{
@@ -1039,6 +1100,15 @@ void auditsc_get_stamp(struct audit_cont
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) {
@@ -1057,11 +1127,26 @@ int audit_set_loginuid(struct task_struc
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_perms - record audit data for ipc
+ * @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_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
{
struct audit_aux_data_ipcctl *ax;
@@ -1085,6 +1170,13 @@ int audit_ipc_perms(unsigned long qbytes
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;
@@ -1106,6 +1198,13 @@ int audit_socketcall(int nargs, unsigned
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;
@@ -1127,6 +1226,15 @@ int audit_sockaddr(int len, void *a)
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;
@@ -1148,6 +1256,14 @@ int audit_avc_path(struct dentry *dentry
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;
@@ -1164,4 +1280,3 @@ void audit_signal_info(int sig, struct t
}
}
}
-
---
19 years, 3 months
VFS hooks analysis (pass 1)
by Amy Griffis
Hello,
I've been investigating audit's needs for VFS hook placement, to
determine whether audit would require additional events or additional
information about events, other than what is currently provided by
Inotify.
I've written up my findings in hopes that some of you who may be more
familiar with the situation than I can correct my mistakes, and that
we can all be on the same page with regard to audit's needs.
At this point, I haven't addressed the issue of race conditions. For
the purpose of the first pass, I make the assumption that the current
fsnotify hook placement is sufficient. I'll address the possibility
of race conditions in a second pass.
Background:
The purpose of the VFS hooks is to capture information about an
object's identity. In this implementation, identity means inode and
name. Identity information is needed for filtering and for log record
detail.
To narrow the field, I limited the set of syscalls to those which are
defined by CAPP as security-relevant and are also filesystem-related.
I believe this is the correct list:
sys_access
sys_chdir
sys_chmod
sys_chown
sys_creat
sys_execve
sys_fchmod
sys_fchown
sys_fremovexattr
sys_fsetxattr
sys_lchown
sys_link
sys_lremovexattr
sys_lsetxattr
sys_mkdir
sys_mknod
sys_mount
sys_open
sys_removexattr
sys_rename
sys_rmdir
sys_setxattr
sys_swapon
sys_symlink
sys_truncate
sys_unlink
sys_utime(s)
Available Object Identity Info:
The upstream audit code uses getname() and path_lookup() hooks to
collect object identity information during syscall processing. This
is sufficient for the following syscalls:
sys_access
sys_chdir
sys_chmod
sys_chown
sys_execve
sys_lchown
sys_link
sys_lremovexattr
sys_lsetxattr
sys_removexattr
sys_setxattr
sys_swapon
sys_truncate
sys_utime(s)
Lacking Object Identity Info:
The information audit needs isn't available when path_lookup is called
with LOOKUP_PARENT, or when getname/path_lookup are not called at all.
In these situations, audit needs more VFS hooks to get the necessary
information.
Attribute Changes:
The getname/path_lookup hooks are never called for these syscalls:
sys_fchmod
sys_fchown
sys_fremovexattr
sys_fsetxattr
The fsnotify_change() and fsnotify_xattr() hooks capture the relevant
dentry, so the dentry's name & inode could be passed to the event
callback.
Mounts:
For sys_mount, audit does not currently capture the pathname for the
device. This is because getname is not called before path_lookup. We
could modify audit_inode() to save the name from path_lookup for
"nameless" inodes. Note the FIXME in audit_inode().
The rest of the syscalls are mostly lacking information due to
path_lookup being called with LOOKUP_PARENT.
Created Objects:
sys_creat
sys_mkdir
sys_mknod
sys_open
The fsnotify_create() and fsnotify_mkdir() hooks have the same
placement as audit_notify_watch(), but audit needs the dentry's inode.
The fsnotify hooks could be modified to capture the dentry, instead of
just the dentry->d_name.name.
Removed Objects:
sys_rmdir
sys_unlink
The fsnotify_inoderemove() hook provides the inode, but see note below
on capturing attempted removals.
Symlinks:
sys_symlink
The inodes for the target path and symlink are not available from the
getname/path_lookup hoooks. The PATH records do not indicate which
name is the symlink. Maybe this isn't necessary since you can tell
from the syscall args.
As previously mentioned, the fsnotify_create() hook could be modified
to capture the dentry instead of the dentry->d_name.name. This would
provide audit with the symlink's inode.
I haven't found a good way to capture the target inode. We don't do
it in the current implementation, which means we don't log events when
someone makes a symlink to a watched inode or pathname. It seems like
we should, though.
Renames:
sys_rename
The inode for the relevant object is not available from the
getname/path_lookup hooks. There is also no way to indicate the old
name versus the new name, other than by the syscall args.
The fsnotify_move() hook could be modified to capture the old and new
dentrys, instead of just the names, which would provide audit with the
inode (twice). Inotify provides separate to/from events.
Capturing Unsuccessful Attempts:
CAPP dictates that audit must capture unsuccessful attempts from open,
rename, truncate, and unlink. The open and truncate calls are a
non-issue, the inode is obtained from the path_lookup hook.
The rename and unlink calls present the problem. Since the inode
wouldn't be captured unless the rename/remove was successful, an
inode-based filter wouldn't catch unsuccessful attempts.
Summary:
To summarize, I haven't found a situation requiring the current
permission() and exec_permission_lite() hooks.
The issues seem to be with symlinks and logging unsuccessful
rename/remove attempts.
I can think of three possible solutions for the latter:
(1) Require filters based on filenames to capture unsuccessful
attempts. This seems undesirable.
(2) Have an audit-specific hook in may_delete(), as is currently
done, but have it tie in to inode-based filters as well.
(3) Request an additional Inotify event that would not be included
in IN_ALL_EVENTS, so wouldn't impact Inotify's other users.
I think 2 or 3 are our best options, with the answer likely depending
on what Inotify is willing to add. It is probably doable to add
audit-specific hooks if they are few and if Inotify isn't interested.
I do not yet have a solution for capturing the target inode from a
symlink operation. However since it's not currently done, maybe it
isn't a hard requirement?
I'd appreciate any comments on my analysis.
Thanks,
Amy
19 years, 3 months
New development
by Steve Grubb
Hello,
I am in the process of reviewing the requirements for the next round of
development for the audit system. I've worked out a rough schedule for the
user space side of things. I will produce more documentation over the next
couple of days describing what is needed and what would be nice to add. I
would like for this to be an open discussion among all parties as this
affects the whole linux community.
The rough schedule for the next series goes something like this:
1.1 -> 1.2 event dispatcher, plugin framework, and some basic plugins
1.2 -> 1.3 label support + more plugins
1.3 -> 1.4 add new config options, summary reports, binary format
1.4 -> 1.5 audit explorer & gui config
There are several reasons for doing plugins first. Partly due to limited time
of people working on it and also to give file system auditing a chance to get
upstream. This way we are working in parallel.
If you have ideas about nice things to add, lets start the discussion. We
don't need to talk about LSPP as that will be by-the-book. (I want that
discussion to be its own thread, but not yet. This is just pie in the sky
planning.) I'm looking for usability and neat to have items.
Another thing I'd like to point out is that the plugin architecture will let
us eventually layer an IDS on top of the audit system. This is a long range
goal that will take some time to get to.
-Steve
19 years, 3 months
[RFC][PATCH] Inotify kernel API
by Amy Griffis
Attached is a patch (against Linus' git tree) that implements a basic
kernel API for inotify. Here is a description of the patch:
The Inotify kernel API provides these functions:
inotify_init - initialize an inotify instance
inotify_add_watch - add a watch on an inode
inotify_ignore - remove a watch on an inode
inotify_free - destroy an inotify instance
The kernel API differs from the userspace API in the following ways:
- Instead of a file descriptor, inotify_init() returns a pointer to
struct inotify_dev, which is an incomplete type to kernel consumers.
- The consumer provides a callback to inotify_init(), which is used
for filesystem event notification instead of the kevents used for
userspace.
- Watches are added on inodes rather than paths.
- inotify_add_watch() takes a callback argument, which is used to
provide the consumer with a quick-access method back to its own data
structure, rather than needing to hash the watch descriptor or walk
a list.
- The path is given to the event callback as an additional argument
rather than being appended to the inotify_event structure;
inotify_event.len is unused.
- User-based limits on number of watches, etc. are ignored.
Here is a list of other things I've been working on, but are not
included in this patch:
- Adding inode information to the event callback.
- Allowing for adding/removing inotify watches from an event callback.
I've also sketched out some data structures and written some prototype
audit code that makes use of this patch.
Please take a look and let me know what you think!
Regards,
Amy
19 years, 3 months
[PATCH] audit_panic() renamed to audit_handle_failure()
by Dustin Kirkland
It has become apparent that the name of the function currently labeled
"audit_panic" might mislead some developers.
This is due to the operation of the function, which may or may not
necessarily panic the kernel. A look at the function shows that there
are currently three different paths the function can take, only one of
which will cause a kernel panic:
static 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;
}
}
The following simple patch renames the function "audit_panic" to
"audit_handle_failure" and updates all references to the function.
:-Dustin
19 years, 3 months
Re: [PATCH] LSPP audit enablement: storing selinux ocontext and scontext
by Dustin Kirkland
Forwarding a note from Mounir which did not copy linux-audit...
On Tue, 2005-08-30 at 13:20 -0500, Mounir Bsaibes wrote:
> On Tue, 2005-08-30 at 10:18 -0500, Dustin Kirkland wrote:
> > Ok, then anyone who disagrees with failing the syscall speak up now...
> > If this is the preferred operation, please say so. Klaus--I, too, am
> > calling for your input.
>
> While it can be one of the configurable options for panic, failing the
> system call is not enough in all cases. Due to the requirement that the
> system must not loose audit record, the system must panic, when
> resources are exhausted.
> Refer to the linux-audit archive of January 2005.
> https://www.redhat.com/archives/linux-audit/2005-January/msg00030.html
> Similar issue was discussed for what to do when audit log is full and
> what to do when kernel resources are exhausted.
19 years, 3 months
[RFC] upstreaming audit filesystem pieces
by Amy Griffis
Hello,
A few weeks ago I posted some mail stating my concern for the
direction being taken to try to get audit's additional filesystem
pieces upstream. I've done some research into other possible
approaches and thought I'd post some thoughts.
Audit Requirements
------------------
First, here is my understanding of how the upstream audit code must be
augmented to meet CAPP requirements, which are the baseline
requirements for audit.
- Provide the capability to specify filters for non-existent
files/directories, provided their parent directory exists. Filters
must take effect when a file/directory is created at the specific
location.
- Provide persistence for filters specified with a given pathname,
given the existence of the parent directory. Filters must be
applied to all filesystem events happening at said pathname.
- Provide additional filesystem information not captured by the
getname() and path_lookup() hooks, e.g. inode information for
created files.
Suggested Approach
------------------
The plan proposed several weeks ago was to write a general filesystem
event notification component for the kernel, based on the current
auditfs patch. I think this is a mistake for several reasons.
- The auditfs patch was not designed to be used by multiple clients.
- The locking performance is relatively untested, and was not designed
for multi-client use.
- The auditfs patch was designed to meet the specific needs of audit
and may not meet the needs of other consumers. We would need to go
through some requirements discussions to ensure any consumers needs
were met.
- The desired functionality is essentially already there in Inotify.
Inotify is designed to be used by multiple clients, has clean locking,
and has undergone quite a bit of testing and performance work. It
already does most of what audit needs. It provides the capability to
watch a parent directory for file or link creations, renames, and
removals. It also provides events for attribute changes. I believe
audit's remaining requirements on Inotify are few.
In order for audit to use Inotify, Inotify would need to provide:
- An Inotify kernel API.
- A pointer to the relevant inode struct when a filesystem event
occurs.
- The ability to begin watching a file at the moment of creation.
Currently audit is pre-notified, via dcache hooks, when a file is
created, moved, or deleted. This allows audit to enable or disable
a watch on the appropriate inode. Audit would need a similar
pre-notification, or preferably the ability to add (and possibly
remove) watches from an Inotify event callback.
- Possible changes to fsnotify hook placement to ensure that the
information recorded by audit (inode, filename) is correct.
- Possible additional filesystem events correlating with some of the
events currently obtained from audit's catch-all hooks in
permission() and exec_permission_lite().
In order for audit to use Inotify, audit would need to:
- Create an Inotify watch on the inode of any existing file/directory
specified in audit filters.
- Create a corresponding Inotify watch on the parent directory of any
file/directory specified in audit filters. This watch may only need
to exist when the specified file/directory doesn't exist.
- Provide an event callback which would perform the necessary actions
for various filesystem events. This would involve updating the
audit_context and adding (and possibly removing) Inotify watches.
- When watched inodes go away, Inotify automatically removes any
relevant watches and generates a removal event(s). Upon receiving
the removal event, audit would log an 'implicit removal' record.
Progress & Next Steps
---------------------
A couple weeks ago I wrote a patch that provides a basic Inotify
kernel API, as a proof of concept. I'm sending that patch in a
follow-up email. I also sent it to John McCutchan (cc'd) for
feedback. He has been supportive of the effort, but would like us to
tighten audit's requirements so he can determine if the necessary
changes would make sense for Inotify.
I've also been working on understanding audit's specific needs
regarding VFS hook placement. I would like to discuss this some more
to determine whether this approach is indeed viable. John (and
Robert), please let us know if you would like to be party to these
converations, or if you would prefer we send you a summary.
Other Things to Consider
------------------------
Any approach to getting the audit filesystem pieces upstream will
require some changes to the current implementation. I think this
would be a good opportunity to do some data structure consolidation.
Filesystem data collected by the auditfs filesystem hooks is not
integrated with other filesystem data collected during system call
processing.
Information collected from the getname() and path_lookup() hooks is
stored in the audit_context's names[] list, and is logged as
AUDIT_PATH records.
Information collected by hooks in namei.c, attr.c, and xattr.c is
stored in auxiliary audit_aux_data_watched structures and is logged as
AUDIT_FS_INODE and AUDIT_FS_WATCH records.
These pieces of information are never tied together other than by
having the same audit record serial number. I don't see any reason
why this needs to be the case. At the very least, combining these two
parts of syscall processing would result in more readable audit records.
Thanks for taking the time to read this email. I would appreciate
any comments on both the Inotify approach and the data structure
consolidation possibilities.
Regards,
Amy
19 years, 4 months
audit.88 kernel
by David Woodhouse
This fixes the return path from sigsuspend() on PPC64, which was failing
to go via audit_syscall_exit() and hence leaving old contexts lying
around and causing the 'freeing multiple contexts' messages which were
reported. Since the sigsuspend() return path is by necessity 'special'
on all architectures, it would be good to make sure this isn't a problem
for any others -- LTP's fcntl15 test is one way to trigger it.
It also allows us to check for success or failure, as requested.
* Mon Aug 8 2005 David Woodhouse <dwmw2(a)redhat.com> audit.88
- Fix sigsuspend exit on PPC64 to go via audit_syscall_exit()
- Allow filtering on success or failure
--
dwmw2
19 years, 4 months
Plugin framework
by Steve Grubb
Hi,
Its time to start the ball rolling with the new development. First up is the
plugin architecture. Let me explain a proposal and then we can discuss it.
To make the audit system more useful, we need to be able to pass events to
other software for realtime analysis and responding. We also need to be able
pass protection profiles which mean the audit system cannot be complicated to
explain its operation. We also cannot allow plugins to the audit system to
corrupt or crash the audit daemon.
The way that this will be accomplished is to add another configuration
parameter dispatcher. This will take the full path to a program that
dispatches audit events. The audit daemon will fork on startup and open a
pipe.
The dispatcher will read from stdin to get audit events. The audit daemon will
use nonblocking writes to the pipe so that it can move on to process the
event itself. (We could make it configurable as to whether to use blocking or
nonblocking writes.) If the dispatcher parameter is empty, the audit daemon
will behave as it currently does. The dispatcher that is standard with the
audit package will be /sbin/audisp. By having the dispatcher specified as a
parameter, third parties can write their own.
The proposed architecture looks something like this:
[fd1] [fd2] [fd3]
I1 I2 I3
\ | /
event queue
/ | \
F1 F2 F3
| | |
O1 O2 O3
The different items along the top are input plugins. The audit daemon is one
of them. The user may have other sources of data that they want fed to the
dispatcher. Maybe IPTables events for example.
These events get queued where different filters examine the event. The filter
picks off the event, processes it and sends it to an output plugin. The
output plugin could be used to send the event out of the machine to an
aggregator. It might be arranged something like this:
C1 C2 C3
\ | /
NETWORK
|
I1 I2
\ |
event queue
/ | \
F1 F2 F3
| | |
O1 O2 O3
C1 - C3 are the output plugins from different computers. They come into a
listener plugin on the monitor computer. The monitor computer is running the
same audisp program and handles the other computer's events in its own queue.
This also lends itself to remote logging instead of alarm or IDS. The new
audit daemon will also have a new format option NONE, that tells it not to
log the events to disk. This means it depends on audisp as the transport
mechanism.
Now some specs going forward. What I would like to see is a framework that any
number of plugins can be developed for. There should be:
PLUGINS
input plugins - These gather events and send them to event dispatcher.
filter plugins - these perform reduction of data. They may be used for either
input or output chains. For example, event dispatcher sends all events to
filter. The user may be using a plugin that filters all but AVC denial
messages. Only avc denial messages are output. The filter plugins can also
transform data. For example, maybe the user want the data changed to xml, or
python, or java friendly data. Perhaps they want some fields removed.
output plugins - These are assigned to filter plugin and write the data
received from its input. There can be a number of these. One for console, ssl
socket, af_unix socket, syslog, dbus, append to file, send email, or execute
a program passing the data.
CONFIGURATION
The config file should let the user specify a chain of plugins that input
events. The config file should let the user specify the chain of plugins for
output. The order in which data moves is specified by the chain. It is
unclear how to specify configuration information for the plugins. Maybe the
command line. For example, the relay is going to need to have a destination
address & port. The filter plugins may need a list of fields to pass or list
of fields to suppress.
THREADING
Threading - multiple threads may be used. They must be detached. They should
have cancellation points. It is unclear if the user should be able to specify
thread pool size.
TERMINATION
auditd must be able to send sigterm and have all threads shutdown.
RECONFIGURE
The audit daemon shall send a message type AUDIT_DAEMON_RECONFIG which will
cause audisp to re-read the configuration.
At this point we should open the discussion and determine what plugins we
would like to have. I can create the list if no one any requests.
-Steve
19 years, 4 months