Commit 1b9a3917 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'audit.b3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current

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

Fixed trivial conflict in security/selinux/hooks.c
parents 3661f00e 71e1c784
......@@ -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 @@ asmlinkage int sys_vm86(struct pt_regs regs)
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 */
}
......
......@@ -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))
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -33,27 +33,42 @@
* 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 */
......@@ -72,6 +87,13 @@
#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. */
......@@ -81,8 +103,9 @@
#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 */
......@@ -98,6 +121,13 @@
#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
......@@ -114,6 +144,7 @@
#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). */
......@@ -130,6 +161,26 @@
#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
......@@ -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);
}
/*
......
......@@ -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;
......
......@@ -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;
......
......@@ -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)
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
skb_queue_head_init(&audit_skb_queue);
audit_initialized = 1;
audit_enabled = audit_default;
......@@ -600,7 +640,10 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
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 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
* 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 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
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,8 +852,17 @@ 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. */
/**
* 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)
{
......@@ -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
/**
* 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);
/* 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;
/* 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;
}
......@@ -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
......@@ -27,11 +29,22 @@
* 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>
......@@ -39,16 +52,16 @@
#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);
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;
}
......@@ -556,64 +329,6 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
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;
}
}
rcu_read_unlock();
return ret; /* Audit by default */
}
/* This should be called with task_lock() held. */
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
int return_valid,
......@@ -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,21 +656,28 @@ 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=");
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"
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",
context->names[i].ino,
MAJOR(context->names[i].dev),
MINOR(context->names[i].dev),
context->names[i].mode,
......@@ -907,20 +685,33 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
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].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)
}
}
}
......@@ -114,6 +114,7 @@
#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));
}
}
......
......@@ -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);
......
......@@ -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,
......
......@@ -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,
......
......@@ -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);
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment