Merge branch 'audit.b3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit...
Linus Torvalds [Sat, 25 Mar 2006 17:24:53 +0000 (09:24 -0800)]
* 'audit.b3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current: (22 commits)
  [PATCH] fix audit_init failure path
  [PATCH] EXPORT_SYMBOL patch for audit_log, audit_log_start, audit_log_end and audit_format
  [PATCH] sem2mutex: audit_netlink_sem
  [PATCH] simplify audit_free() locking
  [PATCH] Fix audit operators
  [PATCH] promiscuous mode
  [PATCH] Add tty to syscall audit records
  [PATCH] add/remove rule update
  [PATCH] audit string fields interface + consumer
  [PATCH] SE Linux audit events
  [PATCH] Minor cosmetic cleanups to the code moved into auditfilter.c
  [PATCH] Fix audit record filtering with !CONFIG_AUDITSYSCALL
  [PATCH] Fix IA64 success/failure indication in syscall auditing.
  [PATCH] Miscellaneous bug and warning fixes
  [PATCH] Capture selinux subject/object context information.
  [PATCH] Exclude messages by message type
  [PATCH] Collect more inode information during syscall processing.
  [PATCH] Pass dentry, not just name, in fsnotify creation hooks.
  [PATCH] Define new range of userspace messages.
  [PATCH] Filter rule comparators
  ...

Fixed trivial conflict in security/selinux/hooks.c

22 files changed:
arch/i386/kernel/vm86.c
arch/ia64/kernel/ptrace.c
fs/namei.c
fs/open.c
fs/xattr.c
include/linux/audit.h
include/linux/fsnotify.h
include/linux/security.h
ipc/msg.c
ipc/sem.c
ipc/shm.c
kernel/Makefile
kernel/audit.c
kernel/audit.h [new file with mode: 0644]
kernel/auditfilter.c [new file with mode: 0644]
kernel/auditsc.c
net/core/dev.c
security/dummy.c
security/selinux/hooks.c
security/selinux/nlmsgtab.c
security/selinux/selinuxfs.c
security/selinux/ss/services.c

index f51c894..aee14fa 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/smp_lock.h>
 #include <linux/highmem.h>
 #include <linux/ptrace.h>
+#include <linux/audit.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -252,6 +253,7 @@ out:
 static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk)
 {
        struct tss_struct *tss;
+       long eax;
 /*
  * make sure the vm86() system call doesn't try to do anything silly
  */
@@ -305,13 +307,19 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
        tsk->thread.screen_bitmap = info->screen_bitmap;
        if (info->flags & VM86_SCREEN_BITMAP)
                mark_screen_rdonly(tsk->mm);
+       __asm__ __volatile__("xorl %eax,%eax; movl %eax,%fs; movl %eax,%gs\n\t");
+       __asm__ __volatile__("movl %%eax, %0\n" :"=r"(eax));
+
+       /*call audit_syscall_exit since we do not exit via the normal paths */
+       if (unlikely(current->audit_context))
+               audit_syscall_exit(current, AUDITSC_RESULT(eax), eax);
+
        __asm__ __volatile__(
-               "xorl %%eax,%%eax; movl %%eax,%%fs; movl %%eax,%%gs\n\t"
                "movl %0,%%esp\n\t"
                "movl %1,%%ebp\n\t"
                "jmp resume_userspace"
                : /* no outputs */
-               :"r" (&info->regs), "r" (task_thread_info(tsk)) : "ax");
+               :"r" (&info->regs), "r" (task_thread_info(tsk)));
        /* we never return here */
 }
 
index eaed14a..9887c87 100644 (file)
@@ -1656,8 +1656,14 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
                     long arg4, long arg5, long arg6, long arg7,
                     struct pt_regs regs)
 {
-       if (unlikely(current->audit_context))
-               audit_syscall_exit(current, AUDITSC_RESULT(regs.r10), regs.r8);
+       if (unlikely(current->audit_context)) {
+               int success = AUDITSC_RESULT(regs.r10);
+               long result = regs.r8;
+
+               if (success != AUDITSC_SUCCESS)
+                       result = -result;
+               audit_syscall_exit(current, success, result);
+       }
 
        if (test_thread_flag(TIF_SYSCALL_TRACE)
            && (current->ptrace & PT_PTRACED))
index 712dfc7..98dc2e1 100644 (file)
@@ -1353,6 +1353,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
                return -ENOENT;
 
        BUG_ON(victim->d_parent->d_inode != dir);
+       audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino);
 
        error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
        if (error)
@@ -1472,7 +1473,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        DQUOT_INIT(dir);
        error = dir->i_op->create(dir, dentry, mode, nd);
        if (!error)
-               fsnotify_create(dir, dentry->d_name.name);
+               fsnotify_create(dir, dentry);
        return error;
 }
 
@@ -1799,7 +1800,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        DQUOT_INIT(dir);
        error = dir->i_op->mknod(dir, dentry, mode, dev);
        if (!error)
-               fsnotify_create(dir, dentry->d_name.name);
+               fsnotify_create(dir, dentry);
        return error;
 }
 
@@ -1876,7 +1877,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        DQUOT_INIT(dir);
        error = dir->i_op->mkdir(dir, dentry, mode);
        if (!error)
-               fsnotify_mkdir(dir, dentry->d_name.name);
+               fsnotify_mkdir(dir, dentry);
        return error;
 }
 
@@ -2139,7 +2140,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
        DQUOT_INIT(dir);
        error = dir->i_op->symlink(dir, dentry, oldname);
        if (!error)
-               fsnotify_create(dir, dentry->d_name.name);
+               fsnotify_create(dir, dentry);
        return error;
 }
 
@@ -2216,7 +2217,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
        error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&old_dentry->d_inode->i_mutex);
        if (!error)
-               fsnotify_create(dir, new_dentry->d_name.name);
+               fsnotify_create(dir, new_dentry);
        return error;
 }
 
index 7d02d19..c32c89d 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -27,6 +27,7 @@
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
 #include <linux/rcupdate.h>
+#include <linux/audit.h>
 
 #include <asm/unistd.h>
 
@@ -626,6 +627,8 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        dentry = file->f_dentry;
        inode = dentry->d_inode;
 
+       audit_inode(NULL, inode, 0);
+
        err = -EROFS;
        if (IS_RDONLY(inode))
                goto out_putf;
@@ -775,7 +778,10 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
 
        file = fget(fd);
        if (file) {
-               error = chown_common(file->f_dentry, user, group);
+               struct dentry * dentry;
+               dentry = file->f_dentry;
+               audit_inode(NULL, dentry->d_inode, 0);
+               error = chown_common(dentry, user, group);
                fput(file);
        }
        return error;
index 80eca7d..e416190 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/syscalls.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
+#include <linux/audit.h>
 #include <asm/uaccess.h>
 
 
@@ -234,12 +235,15 @@ sys_fsetxattr(int fd, char __user *name, void __user *value,
              size_t size, int flags)
 {
        struct file *f;
+       struct dentry *dentry;
        int error = -EBADF;
 
        f = fget(fd);
        if (!f)
                return error;
-       error = setxattr(f->f_dentry, name, value, size, flags);
+       dentry = f->f_dentry;
+       audit_inode(NULL, dentry->d_inode, 0);
+       error = setxattr(dentry, name, value, size, flags);
        fput(f);
        return error;
 }
@@ -458,12 +462,15 @@ asmlinkage long
 sys_fremovexattr(int fd, char __user *name)
 {
        struct file *f;
+       struct dentry *dentry;
        int error = -EBADF;
 
        f = fget(fd);
        if (!f)
                return error;
-       error = removexattr(f->f_dentry, name);
+       dentry = f->f_dentry;
+       audit_inode(NULL, dentry->d_inode, 0);
+       error = removexattr(dentry, name);
        fput(f);
        return error;
 }
index da3c019..1c47c59 100644 (file)
  * 1200 - 1299 messages internal to the audit daemon
  * 1300 - 1399 audit event messages
  * 1400 - 1499 SE Linux use
- * 1500 - 1999 future use
- * 2000 is for otherwise unclassified kernel audit messages
+ * 1500 - 1599 kernel LSPP events
+ * 1600 - 1699 kernel crypto events
+ * 1700 - 1799 kernel anomaly records
+ * 1800 - 1999 future kernel use (maybe integrity labels and related events)
+ * 2000 is for otherwise unclassified kernel audit messages (legacy)
+ * 2001 - 2099 unused (kernel)
+ * 2100 - 2199 user space anomaly records
+ * 2200 - 2299 user space actions taken in response to anomalies
+ * 2300 - 2399 user space generated LSPP events
+ * 2400 - 2499 user space crypto events
+ * 2500 - 2999 future user space (maybe integrity labels and related events)
  *
- * Messages from 1000-1199 are bi-directional. 1200-1299 are exclusively user
- * space. Anything over that is kernel --> user space communication.
+ * Messages from 1000-1199 are bi-directional. 1200-1299 & 2100 - 2999 are
+ * exclusively user space. 1300-2099 is kernel --> user space 
+ * communication.
  */
 #define AUDIT_GET              1000    /* Get status */
 #define AUDIT_SET              1001    /* Set status (enable/disable/auditd) */
-#define AUDIT_LIST             1002    /* List syscall filtering rules */
-#define AUDIT_ADD              1003    /* Add syscall filtering rule */
-#define AUDIT_DEL              1004    /* Delete syscall filtering rule */
+#define AUDIT_LIST             1002    /* List syscall rules -- deprecated */
+#define AUDIT_ADD              1003    /* Add syscall rule -- deprecated */
+#define AUDIT_DEL              1004    /* Delete syscall rule -- deprecated */
 #define AUDIT_USER             1005    /* Message from userspace -- deprecated */
 #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_WATCH_LIST       1009    /* List all file/dir watches */
 #define AUDIT_SIGNAL_INFO      1010    /* Get info about sender of signal to auditd */
+#define AUDIT_ADD_RULE         1011    /* Add syscall filtering rule */
+#define AUDIT_DEL_RULE         1012    /* Delete syscall filtering rule */
+#define AUDIT_LIST_RULES       1013    /* List syscall filtering rules */
 
 #define AUDIT_FIRST_USER_MSG   1100    /* Userspace messages mostly uninteresting to kernel */
 #define AUDIT_USER_AVC         1107    /* We filter this differently */
 #define AUDIT_LAST_USER_MSG    1199
+#define AUDIT_FIRST_USER_MSG2  2100    /* More user space messages */
+#define AUDIT_LAST_USER_MSG2   2999
  
 #define AUDIT_DAEMON_START      1200    /* Daemon startup record */
 #define AUDIT_DAEMON_END        1201    /* Daemon normal stop record */
 #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
 #define AUDIT_AVC_PATH         1402    /* dentry, vfsmount pair from avc */
+#define AUDIT_MAC_POLICY_LOAD  1403    /* Policy file load */
+#define AUDIT_MAC_STATUS       1404    /* Changed enforcing,permissive,off */
+#define AUDIT_MAC_CONFIG_CHANGE        1405    /* Changes to booleans */
+
+#define AUDIT_FIRST_KERN_ANOM_MSG   1700
+#define AUDIT_LAST_KERN_ANOM_MSG    1799
+#define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 
 #define AUDIT_KERNEL           2000    /* Asynchronous audit record. NOT A REQUEST. */
 
 #define AUDIT_FILTER_ENTRY     0x02    /* Apply rule at syscall entry */
 #define AUDIT_FILTER_WATCH     0x03    /* Apply rule to file system watches */
 #define AUDIT_FILTER_EXIT      0x04    /* Apply rule at syscall exit */
+#define AUDIT_FILTER_TYPE      0x05    /* Apply rule at audit_log_start */
 
-#define AUDIT_NR_FILTERS       5
+#define AUDIT_NR_FILTERS       6
 
 #define AUDIT_FILTER_PREPEND   0x10    /* Prepend to front of list */
 
 #define AUDIT_WORD(nr) ((__u32)((nr)/32))
 #define AUDIT_BIT(nr)  (1 << ((nr) - AUDIT_WORD(nr)*32))
 
+/* This bitmask is used to validate user input.  It represents all bits that
+ * are currently used in an audit field constant understood by the kernel.
+ * If you are adding a new #define AUDIT_<whatever>, please ensure that
+ * AUDIT_UNUSED_BITS is updated if need be. */
+#define AUDIT_UNUSED_BITS      0x0FFFFC00
+
+
 /* Rule fields */
                                /* These are useful when checking the
                                 * task structure at task creation time
 #define AUDIT_LOGINUID 9
 #define AUDIT_PERS     10
 #define AUDIT_ARCH     11
+#define AUDIT_MSGTYPE  12
 
                                /* These are ONLY useful when checking
                                 * at syscall exit time (AUDIT_AT_EXIT). */
 #define AUDIT_ARG2      (AUDIT_ARG0+2)
 #define AUDIT_ARG3      (AUDIT_ARG0+3)
 
-#define AUDIT_NEGATE    0x80000000
+#define AUDIT_NEGATE                   0x80000000
 
+/* These are the supported operators.
+ *     4  2  1
+ *     =  >  <
+ *     -------
+ *     0  0  0         0       nonsense
+ *     0  0  1         1       <
+ *     0  1  0         2       >
+ *     0  1  1         3       !=
+ *     1  0  0         4       =
+ *     1  0  1         5       <=
+ *     1  1  0         6       >=
+ *     1  1  1         7       all operators
+ */
+#define AUDIT_LESS_THAN                        0x10000000
+#define AUDIT_GREATER_THAN             0x20000000
+#define AUDIT_NOT_EQUAL                        0x30000000
+#define AUDIT_EQUAL                    0x40000000
+#define AUDIT_LESS_THAN_OR_EQUAL       (AUDIT_LESS_THAN|AUDIT_EQUAL)
+#define AUDIT_GREATER_THAN_OR_EQUAL    (AUDIT_GREATER_THAN|AUDIT_EQUAL)
+#define AUDIT_OPERATORS                        (AUDIT_EQUAL|AUDIT_NOT_EQUAL)
 
 /* Status symbols */
                                /* Mask values */
@@ -186,6 +237,26 @@ struct audit_status {
        __u32           backlog;        /* messages waiting in queue */
 };
 
+/* audit_rule_data supports filter rules with both integer and string
+ * fields.  It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
+ * AUDIT_LIST_RULES requests.
+ */
+struct audit_rule_data {
+       __u32           flags;  /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
+       __u32           action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
+       __u32           field_count;
+       __u32           mask[AUDIT_BITMASK_SIZE]; /* syscall(s) affected */
+       __u32           fields[AUDIT_MAX_FIELDS];
+       __u32           values[AUDIT_MAX_FIELDS];
+       __u32           fieldflags[AUDIT_MAX_FIELDS];
+       __u32           buflen; /* total length of string fields */
+       char            buf[0]; /* string fields buffer */
+};
+
+/* audit_rule is supported to maintain backward compatibility with
+ * userspace.  It supports integer fields only and corresponds to
+ * AUDIT_ADD, AUDIT_DEL and AUDIT_LIST requests.
+ */
 struct audit_rule {            /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
        __u32           flags;  /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
        __u32           action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
@@ -222,22 +293,33 @@ extern void audit_syscall_entry(struct task_struct *task, int arch,
 extern void audit_syscall_exit(struct task_struct *task, int failed, long return_code);
 extern void audit_getname(const char *name);
 extern void audit_putname(const char *name);
-extern void audit_inode(const char *name, const struct inode *inode, unsigned flags);
+extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags);
+extern void __audit_inode_child(const char *dname, const struct inode *inode,
+                               unsigned long pino);
+static inline void audit_inode(const char *name, const struct inode *inode,
+                              unsigned flags) {
+       if (unlikely(current->audit_context))
+               __audit_inode(name, inode, flags);
+}
+static inline void audit_inode_child(const char *dname, 
+                                    const struct inode *inode, 
+                                    unsigned long pino) {
+       if (unlikely(current->audit_context))
+               __audit_inode_child(dname, inode, pino);
+}
 
                                /* Private API (for audit.c only) */
-extern int  audit_receive_filter(int type, int pid, int uid, int seq,
-                                void *data, uid_t loginuid);
 extern unsigned int audit_serial(void);
 extern void auditsc_get_stamp(struct audit_context *ctx,
                              struct timespec *t, unsigned int *serial);
 extern int  audit_set_loginuid(struct task_struct *task, uid_t loginuid);
 extern uid_t audit_get_loginuid(struct audit_context *ctx);
-extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
+extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
 extern int audit_socketcall(int nargs, unsigned long *args);
 extern int audit_sockaddr(int len, void *addr);
 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_set_macxattr(const char *name);
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
@@ -245,16 +327,18 @@ extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
 #define audit_syscall_exit(t,f,r) do { ; } while (0)
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
+#define __audit_inode(n,i,f) do { ; } while (0)
+#define __audit_inode_child(d,i,p) do { ; } while (0)
 #define audit_inode(n,i,f) do { ; } while (0)
-#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
+#define audit_inode_child(d,i,p) do { ; } while (0)
 #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
 #define audit_get_loginuid(c) ({ -1; })
-#define audit_ipc_perms(q,u,g,m) ({ 0; })
+#define audit_ipc_perms(q,u,g,m,i) ({ 0; })
 #define audit_socketcall(n,a) ({ 0; })
 #define audit_sockaddr(len, addr) ({ 0; })
 #define audit_avc_path(dentry, mnt) ({ 0; })
 #define audit_signal_info(s,t) do { ; } while (0)
-#define audit_filter_user(cb,t) ({ 1; })
+#define audit_set_macxattr(n) do { ; } while (0)
 #endif
 
 #ifdef CONFIG_AUDIT
@@ -278,12 +362,11 @@ extern void                   audit_log_d_path(struct audit_buffer *ab,
                                             const char *prefix,
                                             struct dentry *dentry,
                                             struct vfsmount *vfsmnt);
-                               /* Private API (for auditsc.c only) */
-extern void                audit_send_reply(int pid, int seq, int type,
-                                            int done, int multi,
-                                            void *payload, int size);
-extern void                audit_log_lost(const char *message);
-extern struct semaphore audit_netlink_sem;
+                               /* Private API (for audit.c only) */
+extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
+extern int audit_filter_type(int type);
+extern int  audit_receive_filter(int type, int pid, int uid, int seq,
+                                void *data, size_t datasz, uid_t loginuid);
 #else
 #define audit_log(c,g,t,f,...) do { ; } while (0)
 #define audit_log_start(c,g,t) ({ NULL; })
@@ -293,6 +376,7 @@ extern struct semaphore audit_netlink_sem;
 #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)
+#define audit_panic(m) do { ; } while (0)
 #endif
 #endif
 #endif
index f7e517c..11438ef 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/dnotify.h>
 #include <linux/inotify.h>
+#include <linux/audit.h>
 
 /*
  * fsnotify_d_instantiate - instantiate a dentry for inode
@@ -64,6 +65,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
        if (source) {
                inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL);
        }
+       audit_inode_child(old_name, source, old_dir->i_ino);
+       audit_inode_child(new_name, target, new_dir->i_ino);
 }
 
 /*
@@ -89,19 +92,22 @@ static inline void fsnotify_inoderemove(struct inode *inode)
 /*
  * fsnotify_create - 'name' was linked in
  */
-static inline void fsnotify_create(struct inode *inode, const char *name)
+static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
 {
        inode_dir_notify(inode, DN_CREATE);
-       inotify_inode_queue_event(inode, IN_CREATE, 0, name);
+       inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name);
+       audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
 }
 
 /*
  * fsnotify_mkdir - directory 'name' was created
  */
-static inline void fsnotify_mkdir(struct inode *inode, const char *name)
+static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
 {
        inode_dir_notify(inode, DN_CREATE);
-       inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, name);
+       inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, 
+                                 dentry->d_name.name);
+       audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
 }
 
 /*
index 3c19be3..aaa0a5c 100644 (file)
@@ -869,6 +869,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:
@@ -1168,7 +1173,8 @@ struct security_operations {
        int (*inode_getxattr) (struct dentry *dentry, char *name);
        int (*inode_listxattr) (struct dentry *dentry);
        int (*inode_removexattr) (struct dentry *dentry, char *name);
-       int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err);
+       const char *(*inode_xattr_getsuffix) (void);
+       int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t size, int err);
        int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
        int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
 
@@ -1217,6 +1223,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);
@@ -1680,7 +1687,12 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
        return security_ops->inode_removexattr (dentry, name);
 }
 
-static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static inline const char *security_inode_xattr_getsuffix(void)
+{
+       return security_ops->inode_xattr_getsuffix();
+}
+
+static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
        if (unlikely (IS_PRIVATE (inode)))
                return 0;
@@ -1875,6 +1887,11 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
        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);
@@ -2327,7 +2344,12 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
        return cap_inode_removexattr(dentry, name);
 }
 
-static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static inline const char *security_inode_xattr_getsuffix (void)
+{
+       return NULL ;
+}
+
+static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
        return -EOPNOTSUPP;
 }
@@ -2510,6 +2532,11 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
        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;
index 60c1e5c..7eec5ed 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -428,8 +428,6 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
                        return -EFAULT;
                if (copy_msqid_from_user (&setbuf, buf, version))
                        return -EFAULT;
-               if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode)))
-                       return err;
                break;
        case IPC_RMID:
                break;
@@ -460,6 +458,9 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
        switch (cmd) {
        case IPC_SET:
        {
+               if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
+                       goto out_unlock_up;
+
                err = -EPERM;
                if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
                        goto out_unlock_up;
index 31fd402..59696a8 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -809,8 +809,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
        if(cmd == IPC_SET) {
                if(copy_semid_from_user (&setbuf, arg.buf, version))
                        return -EFAULT;
-               if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode)))
-                       return err;
        }
        sma = sem_lock(semid);
        if(sma==NULL)
@@ -821,7 +819,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
                goto out_unlock;
        }       
        ipcp = &sma->sem_perm;
-       
        if (current->euid != ipcp->cuid && 
            current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
                err=-EPERM;
@@ -838,6 +835,8 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
                err = 0;
                break;
        case IPC_SET:
+               if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
+                       goto out_unlock;
                ipcp->uid = setbuf.uid;
                ipcp->gid = setbuf.gid;
                ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
index 16fe278..6f9615c 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -620,13 +620,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                        err = -EFAULT;
                        goto out;
                }
-               if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode)))
-                       return err;
                down(&shm_ids.sem);
                shp = shm_lock(shmid);
                err=-EINVAL;
                if(shp==NULL)
                        goto out_up;
+               if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm))))
+                       goto out_unlock_up;
                err = shm_checkid(shp,shmid);
                if(err)
                        goto out_unlock_up;
index aebd7a7..ff1c11d 100644 (file)
@@ -26,7 +26,7 @@ obj-$(CONFIG_COMPAT) += compat.o
 obj-$(CONFIG_CPUSETS) += cpuset.o
 obj-$(CONFIG_IKCONFIG) += configs.o
 obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
-obj-$(CONFIG_AUDIT) += audit.o
+obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
 obj-$(CONFIG_KPROBES) += kprobes.o
 obj-$(CONFIG_SYSFS) += ksysfs.o
index 0a813d2..04fe2e3 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/audit.h>
 
 #include <net/sock.h>
+#include <net/netlink.h>
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
 
@@ -72,7 +73,7 @@ static int    audit_failure = AUDIT_FAIL_PRINTK;
  * contains the (non-zero) pid. */
 int            audit_pid;
 
-/* If audit_limit is non-zero, limit the rate of sending audit records
+/* If audit_rate_limit is non-zero, limit the rate of sending audit records
  * to that number per second.  This prevents DoS attacks, but results in
  * audit records being dropped. */
 static int     audit_rate_limit;
@@ -102,7 +103,7 @@ static struct sock *audit_sock;
  * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
  * being placed on the freelist). */
 static DEFINE_SPINLOCK(audit_freelist_lock);
-static int        audit_freelist_count = 0;
+static int        audit_freelist_count;
 static LIST_HEAD(audit_freelist);
 
 static struct sk_buff_head audit_skb_queue;
@@ -113,7 +114,7 @@ static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
 /* The netlink socket is only to be read by 1 CPU, which lets us assume
  * that list additions and deletions never happen simultaneously in
  * auditsc.c */
-DECLARE_MUTEX(audit_netlink_sem);
+DEFINE_MUTEX(audit_netlink_mutex);
 
 /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
  * audit records.  Since printk uses a 1024 byte buffer, this buffer
@@ -142,7 +143,7 @@ static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
        nlh->nlmsg_pid = pid;
 }
 
-static void audit_panic(const char *message)
+void audit_panic(const char *message)
 {
        switch (audit_failure)
        {
@@ -186,8 +187,14 @@ static inline int audit_rate_check(void)
        return retval;
 }
 
-/* Emit at least 1 message per second, even if audit_rate_check is
- * throttling. */
+/**
+ * audit_log_lost - conditionally log lost audit message event
+ * @message: the message stating reason for lost audit message
+ *
+ * Emit at least 1 message per second, even if audit_rate_check is
+ * throttling.
+ * Always increment the lost messages counter.
+*/
 void audit_log_lost(const char *message)
 {
        static unsigned long    last_msg = 0;
@@ -218,7 +225,6 @@ void audit_log_lost(const char *message)
                       audit_backlog_limit);
                audit_panic(message);
        }
-
 }
 
 static int audit_set_rate_limit(int limit, uid_t loginuid)
@@ -300,8 +306,22 @@ static int kauditd_thread(void *dummy)
                        remove_wait_queue(&kauditd_wait, &wait);
                }
        }
+       return 0;
 }
 
+/**
+ * audit_send_reply - send an audit reply message via netlink
+ * @pid: process id to send reply to
+ * @seq: sequence number
+ * @type: audit message type
+ * @done: done (last) flag
+ * @multi: multi-part message flag
+ * @payload: payload data
+ * @size: payload size
+ *
+ * Allocates an skb, builds the netlink message, and sends it to the pid.
+ * No failure notifications.
+ */
 void audit_send_reply(int pid, int seq, int type, int done, int multi,
                      void *payload, int size)
 {
@@ -342,15 +362,19 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
        switch (msg_type) {
        case AUDIT_GET:
        case AUDIT_LIST:
+       case AUDIT_LIST_RULES:
        case AUDIT_SET:
        case AUDIT_ADD:
+       case AUDIT_ADD_RULE:
        case AUDIT_DEL:
+       case AUDIT_DEL_RULE:
        case AUDIT_SIGNAL_INFO:
                if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
                        err = -EPERM;
                break;
        case AUDIT_USER:
        case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
+       case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
                if (!cap_raised(eff_cap, CAP_AUDIT_WRITE))
                        err = -EPERM;
                break;
@@ -376,7 +400,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (err)
                return err;
 
-       /* As soon as there's any sign of userspace auditd, start kauditd to talk to it */
+       /* As soon as there's any sign of userspace auditd,
+        * start kauditd to talk to it */
        if (!kauditd_task)
                kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");
        if (IS_ERR(kauditd_task)) {
@@ -430,6 +455,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                break;
        case AUDIT_USER:
        case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
+       case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
                if (!audit_enabled && msg_type != AUDIT_USER_AVC)
                        return 0;
 
@@ -448,12 +474,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                break;
        case AUDIT_ADD:
        case AUDIT_DEL:
-               if (nlh->nlmsg_len < sizeof(struct audit_rule))
+               if (nlmsg_len(nlh) < sizeof(struct audit_rule))
                        return -EINVAL;
                /* fallthrough */
        case AUDIT_LIST:
                err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
-                                          uid, seq, data, loginuid);
+                                          uid, seq, data, nlmsg_len(nlh),
+                                          loginuid);
+               break;
+       case AUDIT_ADD_RULE:
+       case AUDIT_DEL_RULE:
+               if (nlmsg_len(nlh) < sizeof(struct audit_rule_data))
+                       return -EINVAL;
+               /* fallthrough */
+       case AUDIT_LIST_RULES:
+               err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
+                                          uid, seq, data, nlmsg_len(nlh),
+                                          loginuid);
                break;
        case AUDIT_SIGNAL_INFO:
                sig_data.uid = audit_sig_uid;
@@ -469,9 +506,11 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        return err < 0 ? err : 0;
 }
 
-/* Get message from skb (based on rtnetlink_rcv_skb).  Each message is
+/*
+ * Get message from skb (based on rtnetlink_rcv_skb).  Each message is
  * processed by audit_receive_msg.  Malformed skbs with wrong length are
- * discarded silently.  */
+ * discarded silently.
+ */
 static void audit_receive_skb(struct sk_buff *skb)
 {
        int             err;
@@ -499,14 +538,14 @@ static void audit_receive(struct sock *sk, int length)
        struct sk_buff  *skb;
        unsigned int qlen;
 
-       down(&audit_netlink_sem);
+       mutex_lock(&audit_netlink_mutex);
 
        for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
                skb = skb_dequeue(&sk->sk_receive_queue);
                audit_receive_skb(skb);
                kfree_skb(skb);
        }
-       up(&audit_netlink_sem);
+       mutex_unlock(&audit_netlink_mutex);
 }
 
 
@@ -519,8 +558,9 @@ static int __init audit_init(void)
                                           THIS_MODULE);
        if (!audit_sock)
                audit_panic("cannot initialize netlink socket");
+       else
+               audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
 
-       audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
        skb_queue_head_init(&audit_skb_queue);
        audit_initialized = 1;
        audit_enabled = audit_default;
@@ -600,7 +640,10 @@ err:
        return NULL;
 }
 
-/* Compute a serial number for the audit record.  Audit records are
+/**
+ * audit_serial - compute a serial number for the audit record
+ *
+ * Compute a serial number for the audit record.  Audit records are
  * written to user-space as soon as they are generated, so a complete
  * audit record may be written in several pieces.  The timestamp of the
  * record and this serial number are used by the user-space tools to
@@ -612,8 +655,8 @@ err:
  * audit context (for those records that have a context), and emit them
  * all at syscall exit.  However, this could delay the reporting of
  * significant errors until syscall exit (or never, if the system
- * halts). */
-
+ * halts).
+ */
 unsigned int audit_serial(void)
 {
        static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED;
@@ -649,6 +692,21 @@ static inline void audit_get_stamp(struct audit_context *ctx,
  * will be written at syscall exit.  If there is no associated task, tsk
  * should be NULL. */
 
+/**
+ * audit_log_start - obtain an audit buffer
+ * @ctx: audit_context (may be NULL)
+ * @gfp_mask: type of allocation
+ * @type: audit message type
+ *
+ * Returns audit_buffer pointer on success or NULL on error.
+ *
+ * Obtain an audit buffer.  This routine does locking to obtain the
+ * audit buffer, but then no locking is required for calls to
+ * audit_log_*format.  If the task (ctx) is a task that is currently in a
+ * syscall, then the syscall is marked as auditable and an audit record
+ * will be written at syscall exit.  If there is no associated task, then
+ * task context (ctx) should be NULL.
+ */
 struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
                                     int type)
 {
@@ -661,6 +719,9 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
        if (!audit_initialized)
                return NULL;
 
+       if (unlikely(audit_filter_type(type)))
+               return NULL;
+
        if (gfp_mask & __GFP_WAIT)
                reserve = 0;
        else
@@ -713,6 +774,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
 /**
  * audit_expand - expand skb in the audit buffer
  * @ab: audit_buffer
+ * @extra: space to add at tail of the skb
  *
  * Returns 0 (no space) on failed expansion, or available space if
  * successful.
@@ -729,10 +791,12 @@ static inline int audit_expand(struct audit_buffer *ab, int extra)
        return skb_tailroom(skb);
 }
 
-/* Format an audit message into the audit buffer.  If there isn't enough
+/*
+ * Format an audit message into the audit buffer.  If there isn't enough
  * room in the audit buffer, more room will be allocated and vsnprint
  * will be called a second time.  Currently, we assume that a printk
- * can't format message larger than 1024 bytes, so we don't either. */
+ * can't format message larger than 1024 bytes, so we don't either.
+ */
 static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
                              va_list args)
 {
@@ -757,7 +821,8 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
                /* The printk buffer is 1024 bytes long, so if we get
                 * here and AUDIT_BUFSIZ is at least 1024, then we can
                 * log everything that printk could have logged. */
-               avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
+               avail = audit_expand(ab,
+                       max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
                if (!avail)
                        goto out;
                len = vsnprintf(skb->tail, avail, fmt, args2);
@@ -768,8 +833,14 @@ out:
        return;
 }
 
-/* Format a message into the audit buffer.  All the work is done in
- * audit_log_vformat. */
+/**
+ * audit_log_format - format a message into the audit buffer.
+ * @ab: audit_buffer
+ * @fmt: format string
+ * @...: optional parameters matching @fmt string
+ *
+ * All the work is done in audit_log_vformat.
+ */
 void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
 {
        va_list args;
@@ -781,9 +852,18 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
        va_end(args);
 }
 
-/* This function will take the passed buf and convert it into a string of
- * ascii hex digits. The new string is placed onto the skb. */
-void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, 
+/**
+ * audit_log_hex - convert a buffer to hex and append it to the audit skb
+ * @ab: the audit_buffer
+ * @buf: buffer to convert to hex
+ * @len: length of @buf to be converted
+ *
+ * No return value; failure to expand is silently ignored.
+ *
+ * This function will take the passed buf and convert it into a string of
+ * ascii hex digits. The new string is placed onto the skb.
+ */
+void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
                size_t len)
 {
        int i, avail, new_len;
@@ -812,10 +892,16 @@ void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
        skb_put(skb, len << 1); /* new string is twice the old string */
 }
 
-/* This code will escape a string that is passed to it if the string
- * contains a control character, unprintable character, double quote mark, 
+/**
+ * audit_log_unstrustedstring - log a string that may contain random characters
+ * @ab: audit_buffer
+ * @string: string to be logged
+ *
+ * This code will escape a string that is passed to it if the string
+ * contains a control character, unprintable character, double quote mark,
  * or a space. Unescaped strings will start and end with a double quote mark.
- * Strings that are escaped are printed in hex (2 digits per char). */
+ * Strings that are escaped are printed in hex (2 digits per char).
+ */
 void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
 {
        const unsigned char *p = string;
@@ -854,10 +940,15 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
        kfree(path);
 }
 
-/* The netlink_* functions cannot be called inside an irq context, so
- * the audit buffer is places on a queue and a tasklet is scheduled to
+/**
+ * audit_log_end - end one audit record
+ * @ab: the audit_buffer
+ *
+ * The netlink_* functions cannot be called inside an irq context, so
+ * the audit buffer is placed on a queue and a tasklet is scheduled to
  * remove them from the queue outside the irq context.  May be called in
- * any context. */
+ * any context.
+ */
 void audit_log_end(struct audit_buffer *ab)
 {
        if (!ab)
@@ -878,9 +969,18 @@ void audit_log_end(struct audit_buffer *ab)
        audit_buffer_free(ab);
 }
 
-/* Log an audit record.  This is a convenience function that calls
- * audit_log_start, audit_log_vformat, and audit_log_end.  It may be
- * called in any context. */
+/**
+ * audit_log - Log an audit record
+ * @ctx: audit context
+ * @gfp_mask: type of allocation
+ * @type: audit message type
+ * @fmt: format string to use
+ * @...: variable parameters matching the format string
+ *
+ * This is a convenience function that calls audit_log_start,
+ * audit_log_vformat, and audit_log_end.  It may be called
+ * in any context.
+ */
 void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, 
               const char *fmt, ...)
 {
@@ -895,3 +995,8 @@ void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
                audit_log_end(ab);
        }
 }
+
+EXPORT_SYMBOL(audit_log_start);
+EXPORT_SYMBOL(audit_log_end);
+EXPORT_SYMBOL(audit_log_format);
+EXPORT_SYMBOL(audit_log);
diff --git a/kernel/audit.h b/kernel/audit.h
new file mode 100644 (file)
index 0000000..bc53920
--- /dev/null
@@ -0,0 +1,88 @@
+/* audit -- definition of audit_context structure and supporting types 
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright 2005 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/audit.h>
+
+/* 0 = no checking
+   1 = put_count checking
+   2 = verbose put_count checking
+*/
+#define AUDIT_DEBUG 0
+
+/* At task start time, the audit_state is set in the audit_context using
+   a per-task filter.  At syscall entry, the audit_state is augmented by
+   the syscall filter. */
+enum audit_state {
+       AUDIT_DISABLED,         /* Do not create per-task audit_context.
+                                * No syscall-specific audit records can
+                                * be generated. */
+       AUDIT_SETUP_CONTEXT,    /* Create the per-task audit_context,
+                                * but don't necessarily fill it in at
+                                * syscall entry time (i.e., filter
+                                * instead). */
+       AUDIT_BUILD_CONTEXT,    /* Create the per-task audit_context,
+                                * and always fill it in at syscall
+                                * entry time.  This makes a full
+                                * syscall record available if some
+                                * other part of the kernel decides it
+                                * should be recorded. */
+       AUDIT_RECORD_CONTEXT    /* Create the per-task audit_context,
+                                * always fill it in at syscall entry
+                                * time, and always write out the audit
+                                * record at syscall exit time.  */
+};
+
+/* Rule lists */
+struct audit_field {
+       u32                     type;
+       u32                     val;
+       u32                     op;
+};
+
+struct audit_krule {
+       int                     vers_ops;
+       u32                     flags;
+       u32                     listnr;
+       u32                     action;
+       u32                     mask[AUDIT_BITMASK_SIZE];
+       u32                     buflen; /* for data alloc on list rules */
+       u32                     field_count;
+       struct audit_field      *fields;
+};
+
+struct audit_entry {
+       struct list_head        list;
+       struct rcu_head         rcu;
+       struct audit_krule      rule;
+};
+
+
+extern int audit_pid;
+extern int audit_comparator(const u32 left, const u32 op, const u32 right);
+
+extern void                audit_send_reply(int pid, int seq, int type,
+                                            int done, int multi,
+                                            void *payload, int size);
+extern void                audit_log_lost(const char *message);
+extern void                audit_panic(const char *message);
+extern struct mutex audit_netlink_mutex;
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
new file mode 100644 (file)
index 0000000..d3a8539
--- /dev/null
@@ -0,0 +1,630 @@
+/* auditfilter.c -- filtering of audit events
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright 2005 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/audit.h>
+#include <linux/kthread.h>
+#include <linux/netlink.h>
+#include "audit.h"
+
+/* There are three lists of rules -- one to search at task creation
+ * time, one to search at syscall entry time, and another to search at
+ * syscall exit time. */
+struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
+       LIST_HEAD_INIT(audit_filter_list[0]),
+       LIST_HEAD_INIT(audit_filter_list[1]),
+       LIST_HEAD_INIT(audit_filter_list[2]),
+       LIST_HEAD_INIT(audit_filter_list[3]),
+       LIST_HEAD_INIT(audit_filter_list[4]),
+       LIST_HEAD_INIT(audit_filter_list[5]),
+#if AUDIT_NR_FILTERS != 6
+#error Fix audit_filter_list initialiser
+#endif
+};
+
+static inline void audit_free_rule(struct audit_entry *e)
+{
+       kfree(e->rule.fields);
+       kfree(e);
+}
+
+static inline void audit_free_rule_rcu(struct rcu_head *head)
+{
+       struct audit_entry *e = container_of(head, struct audit_entry, rcu);
+       audit_free_rule(e);
+}
+
+/* Unpack a filter field's string representation from user-space
+ * buffer. */
+static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
+{
+       char *str;
+
+       if (!*bufp || (len == 0) || (len > *remain))
+               return ERR_PTR(-EINVAL);
+
+       /* Of the currently implemented string fields, PATH_MAX
+        * defines the longest valid length.
+        */
+       if (len > PATH_MAX)
+               return ERR_PTR(-ENAMETOOLONG);
+
+       str = kmalloc(len + 1, GFP_KERNEL);
+       if (unlikely(!str))
+               return ERR_PTR(-ENOMEM);
+
+       memcpy(str, *bufp, len);
+       str[len] = 0;
+       *bufp += len;
+       *remain -= len;
+
+       return str;
+}
+
+/* Common user-space to kernel rule translation. */
+static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
+{
+       unsigned listnr;
+       struct audit_entry *entry;
+       struct audit_field *fields;
+       int i, err;
+
+       err = -EINVAL;
+       listnr = rule->flags & ~AUDIT_FILTER_PREPEND;
+       switch(listnr) {
+       default:
+               goto exit_err;
+       case AUDIT_FILTER_USER:
+       case AUDIT_FILTER_TYPE:
+#ifdef CONFIG_AUDITSYSCALL
+       case AUDIT_FILTER_ENTRY:
+       case AUDIT_FILTER_EXIT:
+       case AUDIT_FILTER_TASK:
+#endif
+               ;
+       }
+       if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE &&
+           rule->action != AUDIT_ALWAYS)
+               goto exit_err;
+       if (rule->field_count > AUDIT_MAX_FIELDS)
+               goto exit_err;
+
+       err = -ENOMEM;
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (unlikely(!entry))
+               goto exit_err;
+       fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL);
+       if (unlikely(!fields)) {
+               kfree(entry);
+               goto exit_err;
+       }
+
+       memset(&entry->rule, 0, sizeof(struct audit_krule));
+       memset(fields, 0, sizeof(struct audit_field));
+
+       entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
+       entry->rule.listnr = listnr;
+       entry->rule.action = rule->action;
+       entry->rule.field_count = rule->field_count;
+       entry->rule.fields = fields;
+
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+               entry->rule.mask[i] = rule->mask[i];
+
+       return entry;
+
+exit_err:
+       return ERR_PTR(err);
+}
+
+/* Translate struct audit_rule to kernel's rule respresentation.
+ * Exists for backward compatibility with userspace. */
+static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
+{
+       struct audit_entry *entry;
+       int err = 0;
+       int i;
+
+       entry = audit_to_entry_common(rule);
+       if (IS_ERR(entry))
+               goto exit_nofree;
+
+       for (i = 0; i < rule->field_count; i++) {
+               struct audit_field *f = &entry->rule.fields[i];
+
+               if (rule->fields[i] & AUDIT_UNUSED_BITS) {
+                       err = -EINVAL;
+                       goto exit_free;
+               }
+
+               f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
+               f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
+               f->val = rule->values[i];
+
+               entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
+
+               /* Support for legacy operators where
+                * AUDIT_NEGATE bit signifies != and otherwise assumes == */
+               if (f->op & AUDIT_NEGATE)
+                       f->op = AUDIT_NOT_EQUAL;
+               else if (!f->op)
+                       f->op = AUDIT_EQUAL;
+               else if (f->op == AUDIT_OPERATORS) {
+                       err = -EINVAL;
+                       goto exit_free;
+               }
+       }
+
+exit_nofree:
+       return entry;
+
+exit_free:
+       audit_free_rule(entry);
+       return ERR_PTR(err);
+}
+
+/* Translate struct audit_rule_data to kernel's rule respresentation. */
+static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
+                                              size_t datasz)
+{
+       int err = 0;
+       struct audit_entry *entry;
+       void *bufp;
+       /* size_t remain = datasz - sizeof(struct audit_rule_data); */
+       int i;
+
+       entry = audit_to_entry_common((struct audit_rule *)data);
+       if (IS_ERR(entry))
+               goto exit_nofree;
+
+       bufp = data->buf;
+       entry->rule.vers_ops = 2;
+       for (i = 0; i < data->field_count; i++) {
+               struct audit_field *f = &entry->rule.fields[i];
+
+               err = -EINVAL;
+               if (!(data->fieldflags[i] & AUDIT_OPERATORS) ||
+                   data->fieldflags[i] & ~AUDIT_OPERATORS)
+                       goto exit_free;
+
+               f->op = data->fieldflags[i] & AUDIT_OPERATORS;
+               f->type = data->fields[i];
+               switch(f->type) {
+               /* call type-specific conversion routines here */
+               default:
+                       f->val = data->values[i];
+               }
+       }
+
+exit_nofree:
+       return entry;
+
+exit_free:
+       audit_free_rule(entry);
+       return ERR_PTR(err);
+}
+
+/* Pack a filter field's string representation into data block. */
+static inline size_t audit_pack_string(void **bufp, char *str)
+{
+       size_t len = strlen(str);
+
+       memcpy(*bufp, str, len);
+       *bufp += len;
+
+       return len;
+}
+
+/* Translate kernel rule respresentation to struct audit_rule.
+ * Exists for backward compatibility with userspace. */
+static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
+{
+       struct audit_rule *rule;
+       int i;
+
+       rule = kmalloc(sizeof(*rule), GFP_KERNEL);
+       if (unlikely(!rule))
+               return ERR_PTR(-ENOMEM);
+       memset(rule, 0, sizeof(*rule));
+
+       rule->flags = krule->flags | krule->listnr;
+       rule->action = krule->action;
+       rule->field_count = krule->field_count;
+       for (i = 0; i < rule->field_count; i++) {
+               rule->values[i] = krule->fields[i].val;
+               rule->fields[i] = krule->fields[i].type;
+
+               if (krule->vers_ops == 1) {
+                       if (krule->fields[i].op & AUDIT_NOT_EQUAL)
+                               rule->fields[i] |= AUDIT_NEGATE;
+               } else {
+                       rule->fields[i] |= krule->fields[i].op;
+               }
+       }
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
+
+       return rule;
+}
+
+/* Translate kernel rule respresentation to struct audit_rule_data. */
+static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
+{
+       struct audit_rule_data *data;
+       void *bufp;
+       int i;
+
+       data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
+       if (unlikely(!data))
+               return ERR_PTR(-ENOMEM);
+       memset(data, 0, sizeof(*data));
+
+       data->flags = krule->flags | krule->listnr;
+       data->action = krule->action;
+       data->field_count = krule->field_count;
+       bufp = data->buf;
+       for (i = 0; i < data->field_count; i++) {
+               struct audit_field *f = &krule->fields[i];
+
+               data->fields[i] = f->type;
+               data->fieldflags[i] = f->op;
+               switch(f->type) {
+               /* call type-specific conversion routines here */
+               default:
+                       data->values[i] = f->val;
+               }
+       }
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i];
+
+       return data;
+}
+
+/* Compare two rules in kernel format.  Considered success if rules
+ * don't match. */
+static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
+{
+       int i;
+
+       if (a->flags != b->flags ||
+           a->listnr != b->listnr ||
+           a->action != b->action ||
+           a->field_count != b->field_count)
+               return 1;
+
+       for (i = 0; i < a->field_count; i++) {
+               if (a->fields[i].type != b->fields[i].type ||
+                   a->fields[i].op != b->fields[i].op)
+                       return 1;
+
+               switch(a->fields[i].type) {
+               /* call type-specific comparison routines here */
+               default:
+                       if (a->fields[i].val != b->fields[i].val)
+                               return 1;
+               }
+       }
+
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+               if (a->mask[i] != b->mask[i])
+                       return 1;
+
+       return 0;
+}
+
+/* Add rule to given filterlist if not a duplicate.  Protected by
+ * audit_netlink_mutex. */
+static inline int audit_add_rule(struct audit_entry *entry,
+                                 struct list_head *list)
+{
+       struct audit_entry *e;
+
+       /* Do not use the _rcu iterator here, since this is the only
+        * addition routine. */
+       list_for_each_entry(e, list, list) {
+               if (!audit_compare_rule(&entry->rule, &e->rule))
+                       return -EEXIST;
+       }
+
+       if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
+               list_add_rcu(&entry->list, list);
+       } else {
+               list_add_tail_rcu(&entry->list, list);
+       }
+
+       return 0;
+}
+
+/* Remove an existing rule from filterlist.  Protected by
+ * audit_netlink_mutex. */
+static inline int audit_del_rule(struct audit_entry *entry,
+                                struct list_head *list)
+{
+       struct audit_entry  *e;
+
+       /* Do not use the _rcu iterator here, since this is the only
+        * deletion routine. */
+       list_for_each_entry(e, list, list) {
+               if (!audit_compare_rule(&entry->rule, &e->rule)) {
+                       list_del_rcu(&e->list);
+                       call_rcu(&e->rcu, audit_free_rule_rcu);
+                       return 0;
+               }
+       }
+       return -ENOENT;         /* No matching rule */
+}
+
+/* List rules using struct audit_rule.  Exists for backward
+ * compatibility with userspace. */
+static int audit_list(void *_dest)
+{
+       int pid, seq;
+       int *dest = _dest;
+       struct audit_entry *entry;
+       int i;
+
+       pid = dest[0];
+       seq = dest[1];
+       kfree(dest);
+
+       mutex_lock(&audit_netlink_mutex);
+
+       /* The *_rcu iterators not needed here because we are
+          always called with audit_netlink_mutex held. */
+       for (i=0; i<AUDIT_NR_FILTERS; i++) {
+               list_for_each_entry(entry, &audit_filter_list[i], list) {
+                       struct audit_rule *rule;
+
+                       rule = audit_krule_to_rule(&entry->rule);
+                       if (unlikely(!rule))
+                               break;
+                       audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
+                                        rule, sizeof(*rule));
+                       kfree(rule);
+               }
+       }
+       audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+       
+       mutex_unlock(&audit_netlink_mutex);
+       return 0;
+}
+
+/* List rules using struct audit_rule_data. */
+static int audit_list_rules(void *_dest)
+{
+       int pid, seq;
+       int *dest = _dest;
+       struct audit_entry *e;
+       int i;
+
+       pid = dest[0];
+       seq = dest[1];
+       kfree(dest);
+
+       mutex_lock(&audit_netlink_mutex);
+
+       /* The *_rcu iterators not needed here because we are
+          always called with audit_netlink_mutex held. */
+       for (i=0; i<AUDIT_NR_FILTERS; i++) {
+               list_for_each_entry(e, &audit_filter_list[i], list) {
+                       struct audit_rule_data *data;
+
+                       data = audit_krule_to_data(&e->rule);
+                       if (unlikely(!data))
+                               break;
+                       audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
+                                        data, sizeof(*data));
+                       kfree(data);
+               }
+       }
+       audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
+
+       mutex_unlock(&audit_netlink_mutex);
+       return 0;
+}
+
+/**
+ * audit_receive_filter - apply all rules to the specified message type
+ * @type: audit message type
+ * @pid: target pid for netlink audit messages
+ * @uid: target uid for netlink audit messages
+ * @seq: netlink audit message sequence (serial) number
+ * @data: payload data
+ * @datasz: size of payload data
+ * @loginuid: loginuid of sender
+ */
+int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
+                        size_t datasz, uid_t loginuid)
+{
+       struct task_struct *tsk;
+       int *dest;
+       int err = 0;
+       struct audit_entry *entry;
+
+       switch (type) {
+       case AUDIT_LIST:
+       case AUDIT_LIST_RULES:
+               /* We can't just spew out the rules here because we might fill
+                * the available socket buffer space and deadlock waiting for
+                * auditctl to read from it... which isn't ever going to
+                * happen if we're actually running in the context of auditctl
+                * trying to _send_ the stuff */
+                
+               dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
+               if (!dest)
+                       return -ENOMEM;
+               dest[0] = pid;
+               dest[1] = seq;
+
+               if (type == AUDIT_LIST)
+                       tsk = kthread_run(audit_list, dest, "audit_list");
+               else
+                       tsk = kthread_run(audit_list_rules, dest,
+                                         "audit_list_rules");
+               if (IS_ERR(tsk)) {
+                       kfree(dest);
+                       err = PTR_ERR(tsk);
+               }
+               break;
+       case AUDIT_ADD:
+       case AUDIT_ADD_RULE:
+               if (type == AUDIT_ADD)
+                       entry = audit_rule_to_entry(data);
+               else
+                       entry = audit_data_to_entry(data, datasz);
+               if (IS_ERR(entry))
+                       return PTR_ERR(entry);
+
+               err = audit_add_rule(entry,
+                                    &audit_filter_list[entry->rule.listnr]);
+               audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+                       "auid=%u add rule to list=%d res=%d\n",
+                       loginuid, entry->rule.listnr, !err);
+
+               if (err)
+                       audit_free_rule(entry);
+               break;
+       case AUDIT_DEL:
+       case AUDIT_DEL_RULE:
+               if (type == AUDIT_DEL)
+                       entry = audit_rule_to_entry(data);
+               else
+                       entry = audit_data_to_entry(data, datasz);
+               if (IS_ERR(entry))
+                       return PTR_ERR(entry);
+
+               err = audit_del_rule(entry,
+                                    &audit_filter_list[entry->rule.listnr]);
+               audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+                       "auid=%u remove rule from list=%d res=%d\n",
+                       loginuid, entry->rule.listnr, !err);
+
+               audit_free_rule(entry);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return err;
+}
+
+int audit_comparator(const u32 left, const u32 op, const u32 right)
+{
+       switch (op) {
+       case AUDIT_EQUAL:
+               return (left == right);
+       case AUDIT_NOT_EQUAL:
+               return (left != right);
+       case AUDIT_LESS_THAN:
+               return (left < right);
+       case AUDIT_LESS_THAN_OR_EQUAL:
+               return (left <= right);
+       case AUDIT_GREATER_THAN:
+               return (left > right);
+       case AUDIT_GREATER_THAN_OR_EQUAL:
+               return (left >= right);
+       }
+       BUG();
+       return 0;
+}
+
+
+
+static int audit_filter_user_rules(struct netlink_skb_parms *cb,
+                                  struct audit_krule *rule,
+                                  enum audit_state *state)
+{
+       int i;
+
+       for (i = 0; i < rule->field_count; i++) {
+               struct audit_field *f = &rule->fields[i];
+               int result = 0;
+
+               switch (f->type) {
+               case AUDIT_PID:
+                       result = audit_comparator(cb->creds.pid, f->op, f->val);
+                       break;
+               case AUDIT_UID:
+                       result = audit_comparator(cb->creds.uid, f->op, f->val);
+                       break;
+               case AUDIT_GID:
+                       result = audit_comparator(cb->creds.gid, f->op, f->val);
+                       break;
+               case AUDIT_LOGINUID:
+                       result = audit_comparator(cb->loginuid, f->op, f->val);
+                       break;
+               }
+
+               if (!result)
+                       return 0;
+       }
+       switch (rule->action) {
+       case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
+       case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
+       case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
+       }
+       return 1;
+}
+
+int audit_filter_user(struct netlink_skb_parms *cb, int type)
+{
+       struct audit_entry *e;
+       enum audit_state   state;
+       int ret = 1;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
+               if (audit_filter_user_rules(cb, &e->rule, &state)) {
+                       if (state == AUDIT_DISABLED)
+                               ret = 0;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       return ret; /* Audit by default */
+}
+
+int audit_filter_type(int type)
+{
+       struct audit_entry *e;
+       int result = 0;
+       
+       rcu_read_lock();
+       if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
+               goto unlock_and_return;
+
+       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
+                               list) {
+               int i;
+               for (i = 0; i < e->rule.field_count; i++) {
+                       struct audit_field *f = &e->rule.fields[i];
+                       if (f->type == AUDIT_MSGTYPE) {
+                               result = audit_comparator(type, f->op, f->val);
+                               if (!result)
+                                       break;
+                       }
+               }
+               if (result)
+                       goto unlock_and_return;
+       }
+unlock_and_return:
+       rcu_read_unlock();
+       return result;
+}
index c4394ab..7f160df 100644 (file)
@@ -2,6 +2,8 @@
  * Handles all system-call specific auditing features.
  *
  * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2005 IBM Corporation
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * this file -- see entry.S) is based on a GPL'd patch written by
  * okir@suse.de and Copyright 2003 SuSE Linux AG.
  *
+ * The support of additional filter rules compares (>, <, >=, <=) was
+ * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
+ *
+ * Modified by Amy Griffis <amy.griffis@hp.com> to collect additional
+ * filesystem information.
+ *
+ * Subject and object context labeling support added by <danjones@us.ibm.com>
+ * and <dustin.kirkland@us.ibm.com> for LSPP certification compliance.
  */
 
 #include <linux/init.h>
 #include <asm/types.h>
 #include <asm/atomic.h>
+#include <asm/types.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
 #include <linux/personality.h>
 #include <linux/time.h>
-#include <linux/kthread.h>
 #include <linux/netlink.h>
 #include <linux/compiler.h>
 #include <asm/unistd.h>
+#include <linux/security.h>
+#include <linux/list.h>
+#include <linux/tty.h>
+
+#include "audit.h"
 
-/* 0 = no checking
-   1 = put_count checking
-   2 = verbose put_count checking
-*/
-#define AUDIT_DEBUG 0
+extern struct list_head audit_filter_list[];
 
 /* No syscall auditing will take place unless audit_enabled != 0. */
 extern int audit_enabled;
@@ -62,29 +75,6 @@ extern int audit_enabled;
  * path_lookup. */
 #define AUDIT_NAMES_RESERVED 7
 
-/* At task start time, the audit_state is set in the audit_context using
-   a per-task filter.  At syscall entry, the audit_state is augmented by
-   the syscall filter. */
-enum audit_state {
-       AUDIT_DISABLED,         /* Do not create per-task audit_context.
-                                * No syscall-specific audit records can
-                                * be generated. */
-       AUDIT_SETUP_CONTEXT,    /* Create the per-task audit_context,
-                                * but don't necessarily fill it in at
-                                * syscall entry time (i.e., filter
-                                * instead). */
-       AUDIT_BUILD_CONTEXT,    /* Create the per-task audit_context,
-                                * and always fill it in at syscall
-                                * entry time.  This makes a full
-                                * syscall record available if some
-                                * other part of the kernel decides it
-                                * should be recorded. */
-       AUDIT_RECORD_CONTEXT    /* Create the per-task audit_context,
-                                * always fill it in at syscall entry
-                                * time, and always write out the audit
-                                * record at syscall exit time.  */
-};
-
 /* When fs/namei.c:getname() is called, we store the pointer in name and
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
@@ -93,12 +83,13 @@ enum audit_state {
 struct audit_names {
        const char      *name;
        unsigned long   ino;
+       unsigned long   pino;
        dev_t           dev;
        umode_t         mode;
        uid_t           uid;
        gid_t           gid;
        dev_t           rdev;
-       unsigned        flags;
+       char            *ctx;
 };
 
 struct audit_aux_data {
@@ -115,6 +106,7 @@ struct audit_aux_data_ipcctl {
        uid_t                   uid;
        gid_t                   gid;
        mode_t                  mode;
+       char                    *ctx;
 };
 
 struct audit_aux_data_socketcall {
@@ -167,290 +159,72 @@ struct audit_context {
 #endif
 };
 
-                               /* 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
- * syscall exit time. */
-static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
-       LIST_HEAD_INIT(audit_filter_list[0]),
-       LIST_HEAD_INIT(audit_filter_list[1]),
-       LIST_HEAD_INIT(audit_filter_list[2]),
-       LIST_HEAD_INIT(audit_filter_list[3]),
-       LIST_HEAD_INIT(audit_filter_list[4]),
-#if AUDIT_NR_FILTERS != 5
-#error Fix audit_filter_list initialiser
-#endif
-};
-
-struct audit_entry {
-       struct list_head  list;
-       struct rcu_head   rcu;
-       struct audit_rule rule;
-};
-
-extern int audit_pid;
-
-/* Copy rule from user-space to kernel-space.  Called from 
- * audit_add_rule during AUDIT_ADD. */
-static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
-{
-       int i;
-
-       if (s->action != AUDIT_NEVER
-           && s->action != AUDIT_POSSIBLE
-           && s->action != AUDIT_ALWAYS)
-               return -1;
-       if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
-               return -1;
-       if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
-               return -1;
-
-       d->flags        = s->flags;
-       d->action       = s->action;
-       d->field_count  = s->field_count;
-       for (i = 0; i < d->field_count; i++) {
-               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];
-       return 0;
-}
-
-/* Check to see if two rules are identical.  It is called from
- * audit_add_rule during AUDIT_ADD and 
- * audit_del_rule during AUDIT_DEL. */
-static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
-{
-       int i;
-
-       if (a->flags != b->flags)
-               return 1;
-
-       if (a->action != b->action)
-               return 1;
-
-       if (a->field_count != b->field_count)
-               return 1;
-
-       for (i = 0; i < a->field_count; i++) {
-               if (a->fields[i] != b->fields[i]
-                   || a->values[i] != b->values[i])
-                       return 1;
-       }
-
-       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
-               if (a->mask[i] != b->mask[i])
-                       return 1;
-
-       return 0;
-}
-
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
- * audit_netlink_sem. */
-static inline int audit_add_rule(struct audit_rule *rule,
-                                 struct list_head *list)
-{
-       struct audit_entry  *entry;
-
-       /* Do not use the _rcu iterator here, since this is the only
-        * addition routine. */
-       list_for_each_entry(entry, list, list) {
-               if (!audit_compare_rule(rule, &entry->rule)) {
-                       return -EEXIST;
-               }
-       }
-
-       if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
-               return -ENOMEM;
-       if (audit_copy_rule(&entry->rule, rule)) {
-               kfree(entry);
-               return -EINVAL;
-       }
-
-       if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
-               entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
-               list_add_rcu(&entry->list, list);
-       } else {
-               list_add_tail_rcu(&entry->list, list);
-       }
-
-       return 0;
-}
-
-static inline void audit_free_rule(struct rcu_head *head)
-{
-       struct audit_entry *e = container_of(head, struct audit_entry, rcu);
-       kfree(e);
-}
-
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
- * audit_netlink_sem. */
-static inline int audit_del_rule(struct audit_rule *rule,
-                                struct list_head *list)
-{
-       struct audit_entry  *e;
-
-       /* Do not use the _rcu iterator here, since this is the only
-        * deletion routine. */
-       list_for_each_entry(e, list, list) {
-               if (!audit_compare_rule(rule, &e->rule)) {
-                       list_del_rcu(&e->list);
-                       call_rcu(&e->rcu, audit_free_rule);
-                       return 0;
-               }
-       }
-       return -ENOENT;         /* No matching rule */
-}
-
-static int audit_list_rules(void *_dest)
-{
-       int pid, seq;
-       int *dest = _dest;
-       struct audit_entry *entry;
-       int i;
-
-       pid = dest[0];
-       seq = dest[1];
-       kfree(dest);
-
-       down(&audit_netlink_sem);
-
-       /* The *_rcu iterators not needed here because we are
-          always called with audit_netlink_sem held. */
-       for (i=0; i<AUDIT_NR_FILTERS; i++) {
-               list_for_each_entry(entry, &audit_filter_list[i], list)
-                       audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
-                                        &entry->rule, sizeof(entry->rule));
-       }
-       audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
-       
-       up(&audit_netlink_sem);
-       return 0;
-}
-
-int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
-                                                       uid_t loginuid)
-{
-       struct task_struct *tsk;
-       int *dest;
-       int                err = 0;
-       unsigned listnr;
-
-       switch (type) {
-       case AUDIT_LIST:
-               /* We can't just spew out the rules here because we might fill
-                * the available socket buffer space and deadlock waiting for
-                * auditctl to read from it... which isn't ever going to
-                * happen if we're actually running in the context of auditctl
-                * trying to _send_ the stuff */
-                
-               dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
-               if (!dest)
-                       return -ENOMEM;
-               dest[0] = pid;
-               dest[1] = seq;
-
-               tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
-               if (IS_ERR(tsk)) {
-                       kfree(dest);
-                       err = PTR_ERR(tsk);
-               }
-               break;
-       case AUDIT_ADD:
-               listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-               if (listnr >= AUDIT_NR_FILTERS)
-                       return -EINVAL;
-
-               err = audit_add_rule(data, &audit_filter_list[listnr]);
-               if (!err)
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                 "auid=%u added an audit rule\n", loginuid);
-               break;
-       case AUDIT_DEL:
-               listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-               if (listnr >= AUDIT_NR_FILTERS)
-                       return -EINVAL;
-
-               err = audit_del_rule(data, &audit_filter_list[listnr]);
-               if (!err)
-                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-                                 "auid=%u removed an audit rule\n", loginuid);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return err;
-}
 
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise. */
 static int audit_filter_rules(struct task_struct *tsk,
-                             struct audit_rule *rule,
+                             struct audit_krule *rule,
                              struct audit_context *ctx,
                              enum audit_state *state)
 {
        int i, j;
 
        for (i = 0; i < rule->field_count; i++) {
-               u32 field  = rule->fields[i] & ~AUDIT_NEGATE;
-               u32 value  = rule->values[i];
+               struct audit_field *f = &rule->fields[i];
                int result = 0;
 
-               switch (field) {
+               switch (f->type) {
                case AUDIT_PID:
-                       result = (tsk->pid == value);
+                       result = audit_comparator(tsk->pid, f->op, f->val);
                        break;
                case AUDIT_UID:
-                       result = (tsk->uid == value);
+                       result = audit_comparator(tsk->uid, f->op, f->val);
                        break;
                case AUDIT_EUID:
-                       result = (tsk->euid == value);
+                       result = audit_comparator(tsk->euid, f->op, f->val);
                        break;
                case AUDIT_SUID:
-                       result = (tsk->suid == value);
+                       result = audit_comparator(tsk->suid, f->op, f->val);
                        break;
                case AUDIT_FSUID:
-                       result = (tsk->fsuid == value);
+                       result = audit_comparator(tsk->fsuid, f->op, f->val);
                        break;
                case AUDIT_GID:
-                       result = (tsk->gid == value);
+                       result = audit_comparator(tsk->gid, f->op, f->val);
                        break;
                case AUDIT_EGID:
-                       result = (tsk->egid == value);
+                       result = audit_comparator(tsk->egid, f->op, f->val);
                        break;
                case AUDIT_SGID:
-                       result = (tsk->sgid == value);
+                       result = audit_comparator(tsk->sgid, f->op, f->val);
                        break;
                case AUDIT_FSGID:
-                       result = (tsk->fsgid == value);
+                       result = audit_comparator(tsk->fsgid, f->op, f->val);
                        break;
                case AUDIT_PERS:
-                       result = (tsk->personality == value);
+                       result = audit_comparator(tsk->personality, f->op, f->val);
                        break;
                case AUDIT_ARCH:
-                       if (ctx) 
-                               result = (ctx->arch == value);
+                       if (ctx)
+                               result = audit_comparator(ctx->arch, f->op, f->val);
                        break;
 
                case AUDIT_EXIT:
                        if (ctx && ctx->return_valid)
-                               result = (ctx->return_code == value);
+                               result = audit_comparator(ctx->return_code, f->op, f->val);
                        break;
                case AUDIT_SUCCESS:
                        if (ctx && ctx->return_valid) {
-                               if (value)
-                                       result = (ctx->return_valid == AUDITSC_SUCCESS);
+                               if (f->val)
+                                       result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS);
                                else
-                                       result = (ctx->return_valid == AUDITSC_FAILURE);
+                                       result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
                        }
                        break;
                case AUDIT_DEVMAJOR:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (MAJOR(ctx->names[j].dev)==value) {
+                                       if (audit_comparator(MAJOR(ctx->names[j].dev),  f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
@@ -460,7 +234,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_DEVMINOR:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (MINOR(ctx->names[j].dev)==value) {
+                                       if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
@@ -470,7 +244,8 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_INODE:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (ctx->names[j].ino == value) {
+                                       if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
+                                           audit_comparator(ctx->names[j].pino, f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
@@ -480,19 +255,17 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_LOGINUID:
                        result = 0;
                        if (ctx)
-                               result = (ctx->loginuid == value);
+                               result = audit_comparator(ctx->loginuid, f->op, f->val);
                        break;
                case AUDIT_ARG0:
                case AUDIT_ARG1:
                case AUDIT_ARG2:
                case AUDIT_ARG3:
                        if (ctx)
-                               result = (ctx->argv[field-AUDIT_ARG0]==value);
+                               result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
                        break;
                }
 
-               if (rule->fields[i] & AUDIT_NEGATE)
-                       result = !result;
                if (!result)
                        return 0;
        }
@@ -527,7 +300,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
 /* 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,
@@ -541,77 +314,19 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
 
        rcu_read_lock();
        if (!list_empty(list)) {
-                   int word = AUDIT_WORD(ctx->major);
-                   int bit  = AUDIT_BIT(ctx->major);
-
-                   list_for_each_entry_rcu(e, list, list) {
-                           if ((e->rule.mask[word] & bit) == bit
-                               && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
-                                   rcu_read_unlock();
-                                   return state;
-                           }
-                   }
-       }
-       rcu_read_unlock();
-       return AUDIT_BUILD_CONTEXT;
-}
-
-static int audit_filter_user_rules(struct netlink_skb_parms *cb,
-                             struct audit_rule *rule,
-                             enum audit_state *state)
-{
-       int i;
-
-       for (i = 0; i < rule->field_count; i++) {
-               u32 field  = rule->fields[i] & ~AUDIT_NEGATE;
-               u32 value  = rule->values[i];
-               int result = 0;
-
-               switch (field) {
-               case AUDIT_PID:
-                       result = (cb->creds.pid == value);
-                       break;
-               case AUDIT_UID:
-                       result = (cb->creds.uid == value);
-                       break;
-               case AUDIT_GID:
-                       result = (cb->creds.gid == value);
-                       break;
-               case AUDIT_LOGINUID:
-                       result = (cb->loginuid == value);
-                       break;
-               }
-
-               if (rule->fields[i] & AUDIT_NEGATE)
-                       result = !result;
-               if (!result)
-                       return 0;
-       }
-       switch (rule->action) {
-       case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
-       case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
-       }
-       return 1;
-}
-
-int audit_filter_user(struct netlink_skb_parms *cb, int type)
-{
-       struct audit_entry *e;
-       enum audit_state   state;
-       int ret = 1;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
-               if (audit_filter_user_rules(cb, &e->rule, &state)) {
-                       if (state == AUDIT_DISABLED)
-                               ret = 0;
-                       break;
+               int word = AUDIT_WORD(ctx->major);
+               int bit  = AUDIT_BIT(ctx->major);
+
+               list_for_each_entry_rcu(e, list, list) {
+                       if ((e->rule.mask[word] & bit) == bit
+                                       && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
+                               rcu_read_unlock();
+                               return state;
+                       }
                }
        }
        rcu_read_unlock();
-
-       return ret; /* Audit by default */
+       return AUDIT_BUILD_CONTEXT;
 }
 
 /* This should be called with task_lock() held. */
@@ -654,17 +369,18 @@ static inline void audit_free_names(struct audit_context *context)
 #if AUDIT_DEBUG == 2
        if (context->auditable
            ||context->put_count + context->ino_count != context->name_count) {
-               printk(KERN_ERR "audit.c:%d(:%d): major=%d in_syscall=%d"
+               printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
                       " name_count=%d put_count=%d"
                       " ino_count=%d [NOT freeing]\n",
-                      __LINE__,
+                      __FILE__, __LINE__,
                       context->serial, context->major, context->in_syscall,
                       context->name_count, context->put_count,
                       context->ino_count);
-               for (i = 0; i < context->name_count; i++)
+               for (i = 0; i < context->name_count; i++) {
                        printk(KERN_ERR "names[%d] = %p = %s\n", i,
                               context->names[i].name,
-                              context->names[i].name);
+                              context->names[i].name ?: "(null)");
+               }
                dump_stack();
                return;
        }
@@ -674,9 +390,13 @@ static inline void audit_free_names(struct audit_context *context)
        context->ino_count  = 0;
 #endif
 
-       for (i = 0; i < context->name_count; i++)
+       for (i = 0; i < context->name_count; i++) {
+               char *p = context->names[i].ctx;
+               context->names[i].ctx = NULL;
+               kfree(p);
                if (context->names[i].name)
                        __putname(context->names[i].name);
+       }
        context->name_count = 0;
        if (context->pwd)
                dput(context->pwd);
@@ -696,6 +416,12 @@ static inline void audit_free_aux(struct audit_context *context)
                        dput(axi->dentry);
                        mntput(axi->mnt);
                }
+               if ( aux->type == AUDIT_IPC ) {
+                       struct audit_aux_data_ipcctl *axi = (void *)aux;
+                       if (axi->ctx)
+                               kfree(axi->ctx);
+               }
+
                context->aux = aux->next;
                kfree(aux);
        }
@@ -721,10 +447,15 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
        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;
@@ -775,7 +506,37 @@ static inline void audit_free_context(struct audit_context *context)
                printk(KERN_ERR "audit: freed %d contexts\n", count);
 }
 
-static void audit_log_task_info(struct audit_buffer *ab)
+static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask)
+{
+       char *ctx = NULL;
+       ssize_t len = 0;
+
+       len = security_getprocattr(current, "current", NULL, 0);
+       if (len < 0) {
+               if (len != -EINVAL)
+                       goto error_path;
+               return;
+       }
+
+       ctx = kmalloc(len, gfp_mask);
+       if (!ctx)
+               goto error_path;
+
+       len = security_getprocattr(current, "current", ctx, len);
+       if (len < 0 )
+               goto error_path;
+
+       audit_log_format(ab, " subj=%s", ctx);
+       return;
+
+error_path:
+       if (ctx)
+               kfree(ctx);
+       audit_panic("error in audit_log_task_context");
+       return;
+}
+
+static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask)
 {
        char name[sizeof(current->comm)];
        struct mm_struct *mm = current->mm;
@@ -788,6 +549,10 @@ static void audit_log_task_info(struct audit_buffer *ab)
        if (!mm)
                return;
 
+       /*
+        * this is brittle; all callers that pass GFP_ATOMIC will have
+        * NULL current->mm and we won't get here.
+        */
        down_read(&mm->mmap_sem);
        vma = mm->mmap;
        while (vma) {
@@ -801,6 +566,7 @@ static void audit_log_task_info(struct audit_buffer *ab)
                vma = vma->vm_next;
        }
        up_read(&mm->mmap_sem);
+       audit_log_task_context(ab, gfp_mask);
 }
 
 static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
@@ -808,6 +574,7 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
        int i;
        struct audit_buffer *ab;
        struct audit_aux_data *aux;
+       const char *tty;
 
        ab = audit_log_start(context, gfp_mask, AUDIT_SYSCALL);
        if (!ab)
@@ -820,11 +587,15 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                audit_log_format(ab, " success=%s exit=%ld", 
                                 (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
                                 context->return_code);
+       if (current->signal->tty && current->signal->tty->name)
+               tty = current->signal->tty->name;
+       else
+               tty = "(none)";
        audit_log_format(ab,
                  " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
                  " pid=%d auid=%u uid=%u gid=%u"
                  " euid=%u suid=%u fsuid=%u"
-                 " egid=%u sgid=%u fsgid=%u",
+                 " egid=%u sgid=%u fsgid=%u tty=%s",
                  context->argv[0],
                  context->argv[1],
                  context->argv[2],
@@ -835,8 +606,8 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                  context->uid,
                  context->gid,
                  context->euid, context->suid, context->fsuid,
-                 context->egid, context->sgid, context->fsgid);
-       audit_log_task_info(ab);
+                 context->egid, context->sgid, context->fsgid, tty);
+       audit_log_task_info(ab, gfp_mask);
        audit_log_end(ab);
 
        for (aux = context->aux; aux; aux = aux->next) {
@@ -849,8 +620,8 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                case AUDIT_IPC: {
                        struct audit_aux_data_ipcctl *axi = (void *)aux;
                        audit_log_format(ab, 
-                                        " qbytes=%lx iuid=%u igid=%u mode=%x",
-                                        axi->qbytes, axi->uid, axi->gid, axi->mode);
+                                        " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s",
+                                        axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx);
                        break; }
 
                case AUDIT_SOCKETCALL: {
@@ -885,42 +656,62 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
                }
        }
        for (i = 0; i < context->name_count; i++) {
+               unsigned long ino  = context->names[i].ino;
+               unsigned long pino = context->names[i].pino;
+
                ab = audit_log_start(context, gfp_mask, AUDIT_PATH);
                if (!ab)
                        continue; /* audit_panic has been called */
 
                audit_log_format(ab, "item=%d", i);
-               if (context->names[i].name) {
-                       audit_log_format(ab, " name=");
+
+               audit_log_format(ab, " name=");
+               if (context->names[i].name)
                        audit_log_untrustedstring(ab, context->names[i].name);
-               }
-               audit_log_format(ab, " flags=%x\n", context->names[i].flags);
-                        
-               if (context->names[i].ino != (unsigned long)-1)
-                       audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
-                                            " ouid=%u ogid=%u rdev=%02x:%02x",
-                                        context->names[i].ino,
-                                        MAJOR(context->names[i].dev),
-                                        MINOR(context->names[i].dev),
-                                        context->names[i].mode,
-                                        context->names[i].uid,
-                                        context->names[i].gid,
-                                        MAJOR(context->names[i].rdev),
+               else
+                       audit_log_format(ab, "(null)");
+
+               if (pino != (unsigned long)-1)
+                       audit_log_format(ab, " parent=%lu",  pino);
+               if (ino != (unsigned long)-1)
+                       audit_log_format(ab, " inode=%lu",  ino);
+               if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
+                       audit_log_format(ab, " dev=%02x:%02x mode=%#o" 
+                                        " ouid=%u ogid=%u rdev=%02x:%02x", 
+                                        MAJOR(context->names[i].dev), 
+                                        MINOR(context->names[i].dev), 
+                                        context->names[i].mode, 
+                                        context->names[i].uid, 
+                                        context->names[i].gid, 
+                                        MAJOR(context->names[i].rdev), 
                                         MINOR(context->names[i].rdev));
+               if (context->names[i].ctx) {
+                       audit_log_format(ab, " obj=%s",
+                                       context->names[i].ctx);
+               }
+
                audit_log_end(ab);
        }
 }
 
-/* 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;
 
-       task_lock(tsk);
+       /*
+        * No need to lock the task - when we execute audit_free()
+        * then the task has no external references anymore, and
+        * we are tearing it down. (The locking also confuses
+        * DEBUG_LOCKDEP - this freeing may occur in softirq
+        * contexts as well, via RCU.)
+        */
        context = audit_get_context(tsk, 0, 0);
-       task_unlock(tsk);
-
        if (likely(!context))
                return;
 
@@ -934,13 +725,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)
@@ -950,7 +752,8 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
 
        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
@@ -966,11 +769,6 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
        if (context->in_syscall) {
                struct audit_context *newctx;
 
-#if defined(__NR_vm86) && defined(__NR_vm86old)
-               /* vm86 mode should only be entered once */
-               if (major == __NR_vm86 || major == __NR_vm86old)
-                       return;
-#endif
 #if AUDIT_DEBUG
                printk(KERN_ERR
                       "audit(:%d) pid=%d in syscall=%d;"
@@ -1014,11 +812,18 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
        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;
@@ -1053,7 +858,13 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
        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;
@@ -1082,10 +893,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;
@@ -1100,7 +914,7 @@ void audit_putname(const char *name)
                        for (i = 0; i < context->name_count; i++)
                                printk(KERN_ERR "name[%d] = %p = %s\n", i,
                                       context->names[i].name,
-                                      context->names[i].name);
+                                      context->names[i].name ?: "(null)");
                }
 #endif
                __putname(name);
@@ -1122,9 +936,52 @@ void audit_putname(const char *name)
 #endif
 }
 
-/* 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)
+void audit_inode_context(int idx, const struct inode *inode)
+{
+       struct audit_context *context = current->audit_context;
+       const char *suffix = security_inode_xattr_getsuffix();
+       char *ctx = NULL;
+       int len = 0;
+
+       if (!suffix)
+               goto ret;
+
+       len = security_inode_getsecurity(inode, suffix, NULL, 0, 0);
+       if (len == -EOPNOTSUPP)
+               goto ret;
+       if (len < 0) 
+               goto error_path;
+
+       ctx = kmalloc(len, GFP_KERNEL);
+       if (!ctx) 
+               goto error_path;
+
+       len = security_inode_getsecurity(inode, suffix, ctx, len, 0);
+       if (len < 0)
+               goto error_path;
+
+       kfree(context->names[idx].ctx);
+       context->names[idx].ctx = ctx;
+       goto ret;
+
+error_path:
+       if (ctx)
+               kfree(ctx);
+       audit_panic("error in audit_inode_context");
+ret:
+       return;
+}
+
+
+/**
+ * audit_inode - store the inode and device from a lookup
+ * @name: name being audited
+ * @inode: inode being audited
+ * @flags: lookup flags (as used in path_lookup())
+ *
+ * Called from fs/namei.c:path_lookup().
+ */
+void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
 {
        int idx;
        struct audit_context *context = current->audit_context;
@@ -1150,15 +1007,105 @@ void audit_inode(const char *name, const struct inode *inode, unsigned flags)
                ++context->ino_count;
 #endif
        }
-       context->names[idx].flags = flags;
-       context->names[idx].ino   = inode->i_ino;
        context->names[idx].dev   = inode->i_sb->s_dev;
        context->names[idx].mode  = inode->i_mode;
        context->names[idx].uid   = inode->i_uid;
        context->names[idx].gid   = inode->i_gid;
        context->names[idx].rdev  = inode->i_rdev;
+       audit_inode_context(idx, inode);
+       if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && 
+           (strcmp(name, ".") != 0)) {
+               context->names[idx].ino   = (unsigned long)-1;
+               context->names[idx].pino  = inode->i_ino;
+       } else {
+               context->names[idx].ino   = inode->i_ino;
+               context->names[idx].pino  = (unsigned long)-1;
+       }
+}
+
+/**
+ * audit_inode_child - collect inode info for created/removed objects
+ * @dname: inode's dentry name
+ * @inode: inode being audited
+ * @pino: inode number of dentry parent
+ *
+ * For syscalls that create or remove filesystem objects, audit_inode
+ * can only collect information for the filesystem object's parent.
+ * This call updates the audit context with the child's information.
+ * Syscalls that create a new filesystem object must be hooked after
+ * the object is created.  Syscalls that remove a filesystem object
+ * must be hooked prior, in order to capture the target inode during
+ * unsuccessful attempts.
+ */
+void __audit_inode_child(const char *dname, const struct inode *inode,
+                        unsigned long pino)
+{
+       int idx;
+       struct audit_context *context = current->audit_context;
+
+       if (!context->in_syscall)
+               return;
+
+       /* determine matching parent */
+       if (dname)
+               for (idx = 0; idx < context->name_count; idx++)
+                       if (context->names[idx].pino == pino) {
+                               const char *n;
+                               const char *name = context->names[idx].name;
+                               int dlen = strlen(dname);
+                               int nlen = name ? strlen(name) : 0;
+
+                               if (nlen < dlen)
+                                       continue;
+                               
+                               /* disregard trailing slashes */
+                               n = name + nlen - 1;
+                               while ((*n == '/') && (n > name))
+                                       n--;
+
+                               /* find last path component */
+                               n = n - dlen + 1;
+                               if (n < name)
+                                       continue;
+                               else if (n > name) {
+                                       if (*--n != '/')
+                                               continue;
+                                       else
+                                               n++;
+                               }
+
+                               if (strncmp(n, dname, dlen) == 0)
+                                       goto update_context;
+                       }
+
+       /* catch-all in case match not found */
+       idx = context->name_count++;
+       context->names[idx].name  = NULL;
+       context->names[idx].pino  = pino;
+#if AUDIT_DEBUG
+       context->ino_count++;
+#endif
+
+update_context:
+       if (inode) {
+               context->names[idx].ino   = inode->i_ino;
+               context->names[idx].dev   = inode->i_sb->s_dev;
+               context->names[idx].mode  = inode->i_mode;
+               context->names[idx].uid   = inode->i_uid;
+               context->names[idx].gid   = inode->i_gid;
+               context->names[idx].rdev  = inode->i_rdev;
+               audit_inode_context(idx, inode);
+       }
 }
 
+/**
+ * auditsc_get_stamp - get local copies of audit_context values
+ * @ctx: audit_context for the task
+ * @t: timespec to store time recorded in the audit_context
+ * @serial: serial value that is recorded in the audit_context
+ *
+ * Also sets the context as auditable.
+ */
 void auditsc_get_stamp(struct audit_context *ctx,
                       struct timespec *t, unsigned int *serial)
 {
@@ -1170,6 +1117,15 @@ void auditsc_get_stamp(struct audit_context *ctx,
        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) {
@@ -1188,12 +1144,59 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
        return 0;
 }
 
+/**
+ * audit_get_loginuid - get the loginuid for an audit_context
+ * @ctx: the audit_context
+ *
+ * Returns the context's loginuid or -1 if @ctx is NULL.
+ */
 uid_t audit_get_loginuid(struct audit_context *ctx)
 {
        return ctx ? ctx->loginuid : -1;
 }
 
-int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
+static char *audit_ipc_context(struct kern_ipc_perm *ipcp)
+{
+       struct audit_context *context = current->audit_context;
+       char *ctx = NULL;
+       int len = 0;
+
+       if (likely(!context))
+               return NULL;
+
+       len = security_ipc_getsecurity(ipcp, NULL, 0);
+       if (len == -EOPNOTSUPP)
+               goto ret;
+       if (len < 0)
+               goto error_path;
+
+       ctx = kmalloc(len, GFP_ATOMIC);
+       if (!ctx)
+               goto error_path;
+
+       len = security_ipc_getsecurity(ipcp, ctx, len);
+       if (len < 0)
+               goto error_path;
+
+       return ctx;
+
+error_path:
+       kfree(ctx);
+       audit_panic("error in audit_ipc_context");
+ret:
+       return NULL;
+}
+
+/**
+ * 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 kern_ipc_perm *ipcp)
 {
        struct audit_aux_data_ipcctl *ax;
        struct audit_context *context = current->audit_context;
@@ -1201,7 +1204,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        if (likely(!context))
                return 0;
 
-       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
        if (!ax)
                return -ENOMEM;
 
@@ -1209,6 +1212,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        ax->uid = uid;
        ax->gid = gid;
        ax->mode = mode;
+       ax->ctx = audit_ipc_context(ipcp);
 
        ax->d.type = AUDIT_IPC;
        ax->d.next = context->aux;
@@ -1216,6 +1220,13 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        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;
@@ -1237,6 +1248,13 @@ int audit_socketcall(int nargs, unsigned long *args)
        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;
@@ -1258,6 +1276,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;
@@ -1279,6 +1306,14 @@ int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
        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;
@@ -1295,4 +1330,3 @@ void audit_signal_info(int sig, struct task_struct *t)
                }
        }
 }
-
index e0489ca..8e1dc30 100644 (file)
 #include <linux/wireless.h>
 #include <net/iw_handler.h>
 #include <asm/current.h>
+#include <linux/audit.h>
 
 /*
  *     The list of packet types we will receive (as opposed to discard)
@@ -2147,6 +2148,12 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
                printk(KERN_INFO "device %s %s promiscuous mode\n",
                       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
                                                               "left");
+               audit_log(current->audit_context, GFP_ATOMIC,
+                       AUDIT_ANOM_PROMISCUOUS,
+                       "dev=%s prom=%d old_prom=%d auid=%u",
+                       dev->name, (dev->flags & IFF_PROMISC),
+                       (old_flags & IFF_PROMISC),
+                       audit_get_loginuid(current->audit_context)); 
        }
 }
 
index a678f09..fd99429 100644 (file)
@@ -378,7 +378,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name)
        return 0;
 }
 
-static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static int dummy_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
        return -EOPNOTSUPP;
 }
@@ -393,6 +393,11 @@ static int dummy_inode_listsecurity(struct inode *inode, char *buffer, size_t bu
        return 0;
 }
 
+static const char *dummy_inode_xattr_getsuffix(void)
+{
+       return NULL;
+}
+
 static int dummy_file_permission (struct file *file, int mask)
 {
        return 0;
@@ -558,6 +563,11 @@ static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag)
        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;
@@ -931,6 +941,7 @@ void security_fixup_ops (struct security_operations *ops)
        set_to_dummy_if_null(ops, inode_getxattr);
        set_to_dummy_if_null(ops, inode_listxattr);
        set_to_dummy_if_null(ops, inode_removexattr);
+       set_to_dummy_if_null(ops, inode_xattr_getsuffix);
        set_to_dummy_if_null(ops, inode_getsecurity);
        set_to_dummy_if_null(ops, inode_setsecurity);
        set_to_dummy_if_null(ops, inode_listsecurity);
@@ -965,6 +976,7 @@ void security_fixup_ops (struct security_operations *ops)
        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);
index ccaf988..b61b955 100644 (file)
@@ -119,6 +119,32 @@ static DEFINE_SPINLOCK(sb_security_lock);
 
 static kmem_cache_t *sel_inode_cache;
 
+/* 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;
+
+       rc = security_sid_to_context(sid, &context, &len);
+       if (rc)
+               return rc;
+
+       if (!buffer || !size)
+               goto getsecurity_exit;
+
+       if (size < len) {
+               len = -ERANGE;
+               goto getsecurity_exit;
+       }
+       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)
@@ -2210,6 +2236,11 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name)
        return -EACCES;
 }
 
+static const char *selinux_inode_xattr_getsuffix(void)
+{
+      return XATTR_SELINUX_SUFFIX;
+}
+
 /*
  * Copy the in-core inode security context value to the user.  If the
  * getxattr() prior to this succeeded, check to see if we need to
@@ -2217,47 +2248,14 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name)
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
-static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
        struct inode_security_struct *isec = inode->i_security;
-       char *context;
-       unsigned len;
-       int rc;
-
-       if (strcmp(name, XATTR_SELINUX_SUFFIX)) {
-               rc = -EOPNOTSUPP;
-               goto out;
-       }
-
-       rc = security_sid_to_context(isec->sid, &context, &len);
-       if (rc)
-               goto out;
-
-       /* Probe for required buffer size */
-       if (!buffer || !size) {
-               rc = len;
-               goto out_free;
-       }
 
-       if (size < len) {
-               rc = -ERANGE;
-               goto out_free;
-       }
+       if (strcmp(name, XATTR_SELINUX_SUFFIX))
+               return -EOPNOTSUPP;
 
-       if (err > 0) {
-               if ((len == err) && !(memcmp(context, buffer, len))) {
-                       /* Don't need to canonicalize value */
-                       rc = err;
-                       goto out_free;
-               }
-               memset(buffer, 0, size);
-       }
-       memcpy(buffer, context, len);
-       rc = len;
-out_free:
-       kfree(context);
-out:
-       return rc;
+       return selinux_getsecurity(isec->sid, buffer, size);
 }
 
 static int selinux_inode_setsecurity(struct inode *inode, const char *name,
@@ -4054,6 +4052,13 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
        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)
 {
@@ -4095,8 +4100,7 @@ static int selinux_getprocattr(struct task_struct *p,
                               char *name, void *value, size_t size)
 {
        struct task_security_struct *tsec;
-       u32 sid, len;
-       char *context;
+       u32 sid;
        int error;
 
        if (current != p) {
@@ -4105,9 +4109,6 @@ static int selinux_getprocattr(struct task_struct *p,
                        return error;
        }
 
-       if (!size)
-               return -ERANGE;
-
        tsec = p->security;
 
        if (!strcmp(name, "current"))
@@ -4124,16 +4125,7 @@ static int selinux_getprocattr(struct task_struct *p,
        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,
@@ -4291,6 +4283,7 @@ static struct security_operations selinux_ops = {
        .inode_getxattr =               selinux_inode_getxattr,
        .inode_listxattr =              selinux_inode_listxattr,
        .inode_removexattr =            selinux_inode_removexattr,
+       .inode_xattr_getsuffix =        selinux_inode_xattr_getsuffix,
        .inode_getsecurity =            selinux_inode_getsecurity,
        .inode_setsecurity =            selinux_inode_setsecurity,
        .inode_listsecurity =           selinux_inode_listsecurity,
@@ -4328,6 +4321,7 @@ static struct security_operations selinux_ops = {
        .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,
index 85e3992..b8f4d25 100644 (file)
@@ -106,6 +106,9 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
        { AUDIT_LIST,           NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
        { AUDIT_ADD,            NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
        { AUDIT_DEL,            NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
+       { AUDIT_LIST_RULES,     NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
+       { AUDIT_ADD_RULE,       NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
+       { AUDIT_DEL_RULE,       NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
        { AUDIT_USER,           NETLINK_AUDIT_SOCKET__NLMSG_RELAY    },
        { AUDIT_SIGNAL_INFO,    NETLINK_AUDIT_SOCKET__NLMSG_READ     },
 };
@@ -152,8 +155,10 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
                break;
 
        case SECCLASS_NETLINK_AUDIT_SOCKET:
-               if (nlmsg_type >= AUDIT_FIRST_USER_MSG &&
-                   nlmsg_type <= AUDIT_LAST_USER_MSG) {
+               if ((nlmsg_type >= AUDIT_FIRST_USER_MSG &&
+                    nlmsg_type <= AUDIT_LAST_USER_MSG) ||
+                   (nlmsg_type >= AUDIT_FIRST_USER_MSG2 &&
+                     nlmsg_type <= AUDIT_LAST_USER_MSG2)) {
                        *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY;
                } else {
                        err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
index f5d7836..a4efc96 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/major.h>
 #include <linux/seq_file.h>
 #include <linux/percpu.h>
+#include <linux/audit.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
 
@@ -127,6 +128,10 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf,
                length = task_has_security(current, SECURITY__SETENFORCE);
                if (length)
                        goto out;
+               audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+                       "enforcing=%d old_enforcing=%d auid=%u", new_value, 
+                       selinux_enforcing,
+                       audit_get_loginuid(current->audit_context));
                selinux_enforcing = new_value;
                if (selinux_enforcing)
                        avc_ss_reset(0);
@@ -177,6 +182,9 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf,
                length = selinux_disable();
                if (length < 0)
                        goto out;
+               audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+                       "selinux=0 auid=%u",
+                       audit_get_loginuid(current->audit_context));
        }
 
        length = count;
@@ -262,6 +270,9 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
                length = ret;
        else
                length = count;
+       audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
+               "policy loaded auid=%u",
+               audit_get_loginuid(current->audit_context));
 out:
        mutex_unlock(&sel_mutex);
        vfree(data);
index 63e0b7f..6149248 100644 (file)
@@ -1759,19 +1759,22 @@ int security_set_bools(int len, int *values)
                goto out;
        }
 
-       printk(KERN_INFO "security: committed booleans { ");
        for (i = 0; i < len; i++) {
+               if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
+                       audit_log(current->audit_context, GFP_ATOMIC,
+                               AUDIT_MAC_CONFIG_CHANGE,
+                               "bool=%s val=%d old_val=%d auid=%u",
+                               policydb.p_bool_val_to_name[i],
+                               !!values[i],
+                               policydb.bool_val_to_struct[i]->state,
+                               audit_get_loginuid(current->audit_context));
+               }
                if (values[i]) {
                        policydb.bool_val_to_struct[i]->state = 1;
                } else {
                        policydb.bool_val_to_struct[i]->state = 0;
                }
-               if (i != 0)
-                       printk(", ");
-               printk("%s:%d", policydb.p_bool_val_to_name[i],
-                      policydb.bool_val_to_struct[i]->state);
        }
-       printk(" }\n");
 
        for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
                rc = evaluate_cond_node(&policydb, cur);