Commit d9eaec9e authored by Linus Torvalds's avatar Linus Torvalds

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

* 'audit.b21' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current: (25 commits)
  [PATCH] make set_loginuid obey audit_enabled
  [PATCH] log more info for directory entry change events
  [PATCH] fix AUDIT_FILTER_PREPEND handling
  [PATCH] validate rule fields' types
  [PATCH] audit: path-based rules
  [PATCH] Audit of POSIX Message Queue Syscalls v.2
  [PATCH] fix se_sen audit filter
  [PATCH] deprecate AUDIT_POSSBILE
  [PATCH] inline more audit helpers
  [PATCH] proc_loginuid_write() uses simple_strtoul() on non-terminated array
  [PATCH] update of IPC audit record cleanup
  [PATCH] minor audit updates
  [PATCH] fix audit_krule_to_{rule,data} return values
  [PATCH] add filtering by ppid
  [PATCH] log ppid
  [PATCH] collect sid of those who send signals to auditd
  [PATCH] execve argument logging
  [PATCH] fix deadlocks in AUDIT_LIST/AUDIT_LIST_RULES
  [PATCH] audit_panic() is audit-internal
  [PATCH] inotify (5/5): update kernel documentation
  ...

Manual fixup of conflict in unclude/linux/inotify.h
parents cee4cca7 41757106
......@@ -69,17 +69,135 @@ Prototypes:
int inotify_rm_watch (int fd, __u32 mask);
(iii) Internal Kernel Implementation
(iii) Kernel Interface
Each inotify instance is associated with an inotify_device structure.
Inotify's kernel API consists a set of functions for managing watches and an
event callback.
To use the kernel API, you must first initialize an inotify instance with a set
of inotify_operations. You are given an opaque inotify_handle, which you use
for any further calls to inotify.
struct inotify_handle *ih = inotify_init(my_event_handler);
You must provide a function for processing events and a function for destroying
the inotify watch.
void handle_event(struct inotify_watch *watch, u32 wd, u32 mask,
u32 cookie, const char *name, struct inode *inode)
watch - the pointer to the inotify_watch that triggered this call
wd - the watch descriptor
mask - describes the event that occurred
cookie - an identifier for synchronizing events
name - the dentry name for affected files in a directory-based event
inode - the affected inode in a directory-based event
void destroy_watch(struct inotify_watch *watch)
You may add watches by providing a pre-allocated and initialized inotify_watch
structure and specifying the inode to watch along with an inotify event mask.
You must pin the inode during the call. You will likely wish to embed the
inotify_watch structure in a structure of your own which contains other
information about the watch. Once you add an inotify watch, it is immediately
subject to removal depending on filesystem events. You must grab a reference if
you depend on the watch hanging around after the call.
inotify_init_watch(&my_watch->iwatch);
inotify_get_watch(&my_watch->iwatch); // optional
s32 wd = inotify_add_watch(ih, &my_watch->iwatch, inode, mask);
inotify_put_watch(&my_watch->iwatch); // optional
You may use the watch descriptor (wd) or the address of the inotify_watch for
other inotify operations. You must not directly read or manipulate data in the
inotify_watch. Additionally, you must not call inotify_add_watch() more than
once for a given inotify_watch structure, unless you have first called either
inotify_rm_watch() or inotify_rm_wd().
To determine if you have already registered a watch for a given inode, you may
call inotify_find_watch(), which gives you both the wd and the watch pointer for
the inotify_watch, or an error if the watch does not exist.
wd = inotify_find_watch(ih, inode, &watchp);
You may use container_of() on the watch pointer to access your own data
associated with a given watch. When an existing watch is found,
inotify_find_watch() bumps the refcount before releasing its locks. You must
put that reference with:
put_inotify_watch(watchp);
Call inotify_find_update_watch() to update the event mask for an existing watch.
inotify_find_update_watch() returns the wd of the updated watch, or an error if
the watch does not exist.
wd = inotify_find_update_watch(ih, inode, mask);
An existing watch may be removed by calling either inotify_rm_watch() or
inotify_rm_wd().
int ret = inotify_rm_watch(ih, &my_watch->iwatch);
int ret = inotify_rm_wd(ih, wd);
A watch may be removed while executing your event handler with the following:
inotify_remove_watch_locked(ih, iwatch);
Call inotify_destroy() to remove all watches from your inotify instance and
release it. If there are no outstanding references, inotify_destroy() will call
your destroy_watch op for each watch.
inotify_destroy(ih);
When inotify removes a watch, it sends an IN_IGNORED event to your callback.
You may use this event as an indication to free the watch memory. Note that
inotify may remove a watch due to filesystem events, as well as by your request.
If you use IN_ONESHOT, inotify will remove the watch after the first event, at
which point you may call the final inotify_put_watch.
(iv) Kernel Interface Prototypes
struct inotify_handle *inotify_init(struct inotify_operations *ops);
inotify_init_watch(struct inotify_watch *watch);
s32 inotify_add_watch(struct inotify_handle *ih,
struct inotify_watch *watch,
struct inode *inode, u32 mask);
s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
struct inotify_watch **watchp);
s32 inotify_find_update_watch(struct inotify_handle *ih,
struct inode *inode, u32 mask);
int inotify_rm_wd(struct inotify_handle *ih, u32 wd);
int inotify_rm_watch(struct inotify_handle *ih,
struct inotify_watch *watch);
void inotify_remove_watch_locked(struct inotify_handle *ih,
struct inotify_watch *watch);
void inotify_destroy(struct inotify_handle *ih);
void get_inotify_watch(struct inotify_watch *watch);
void put_inotify_watch(struct inotify_watch *watch);
(v) Internal Kernel Implementation
Each inotify instance is represented by an inotify_handle structure.
Inotify's userspace consumers also have an inotify_device which is
associated with the inotify_handle, and on which events are queued.
Each watch is associated with an inotify_watch structure. Watches are chained
off of each associated device and each associated inode.
off of each associated inotify_handle and each associated inode.
See fs/inotify.c for the locking and lifetime rules.
See fs/inotify.c and fs/inotify_user.c for the locking and lifetime rules.
(iv) Rationale
(vi) Rationale
Q: What is the design decision behind not tying the watch to the open fd of
the watched object?
......@@ -145,7 +263,7 @@ A: The poor user-space interface is the second biggest problem with dnotify.
file descriptor-based one that allows basic file I/O and poll/select.
Obtaining the fd and managing the watches could have been done either via a
device file or a family of new system calls. We decided to implement a
family of system calls because that is the preffered approach for new kernel
family of system calls because that is the preferred approach for new kernel
interfaces. The only real difference was whether we wanted to use open(2)
and ioctl(2) or a couple of new system calls. System calls beat ioctls.
......@@ -393,18 +393,30 @@ config INOTIFY
bool "Inotify file change notification support"
default y
---help---
Say Y here to enable inotify support and the associated system
calls. Inotify is a file change notification system and a
replacement for dnotify. Inotify fixes numerous shortcomings in
dnotify and introduces several new features. It allows monitoring
of both files and directories via a single open fd. Other features
include multiple file events, one-shot support, and unmount
Say Y here to enable inotify support. Inotify is a file change
notification system and a replacement for dnotify. Inotify fixes
numerous shortcomings in dnotify and introduces several new features
including multiple file events, one-shot support, and unmount
notification.
For more information, see Documentation/filesystems/inotify.txt
If unsure, say Y.
config INOTIFY_USER
bool "Inotify support for userspace"
depends on INOTIFY
default y
---help---
Say Y here to enable inotify support for userspace, including the
associated system calls. Inotify allows monitoring of both files and
directories via a single open fd. Events are read from the file
descriptor, which is also select()- and poll()-able.
For more information, see Documentation/filesystems/inotify.txt
If unsure, say Y.
config QUOTA
bool "Quota support"
help
......
......@@ -13,6 +13,7 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \
ioprio.o pnode.o drop_caches.o splice.o sync.o
obj-$(CONFIG_INOTIFY) += inotify.o
obj-$(CONFIG_INOTIFY_USER) += inotify_user.o
obj-$(CONFIG_EPOLL) += eventpoll.o
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
......
......@@ -49,6 +49,7 @@
#include <linux/rmap.h>
#include <linux/acct.h>
#include <linux/cn_proc.h>
#include <linux/audit.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
......@@ -1085,6 +1086,11 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
/* kernel module loader fixup */
/* so we don't try to load run modprobe in kernel space. */
set_fs(USER_DS);
retval = audit_bprm(bprm);
if (retval)
return retval;
retval = -ENOENT;
for (try=0; try<2; try++) {
read_lock(&binfmt_lock);
......
......@@ -5,7 +5,10 @@
* John McCutchan <ttb@tentacle.dhs.org>
* Robert Love <rml@novell.com>
*
* Kernel API added by: Amy Griffis <amy.griffis@hp.com>
*
* Copyright (C) 2005 John McCutchan
* Copyright 2006 Hewlett-Packard Development Company, L.P.
*
* 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
......@@ -20,35 +23,17 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/writeback.h>
#include <linux/inotify.h>
#include <linux/syscalls.h>
#include <asm/ioctls.h>
static atomic_t inotify_cookie;
static kmem_cache_t *watch_cachep __read_mostly;
static kmem_cache_t *event_cachep __read_mostly;
static struct vfsmount *inotify_mnt __read_mostly;
/* these are configurable via /proc/sys/fs/inotify/ */
int inotify_max_user_instances __read_mostly;
int inotify_max_user_watches __read_mostly;
int inotify_max_queued_events __read_mostly;
/*
* Lock ordering:
*
......@@ -56,327 +41,108 @@ int inotify_max_queued_events __read_mostly;
* iprune_mutex (synchronize shrink_icache_memory())
* inode_lock (protects the super_block->s_inodes list)
* inode->inotify_mutex (protects inode->inotify_watches and watches->i_list)
* inotify_dev->mutex (protects inotify_device and watches->d_list)
* inotify_handle->mutex (protects inotify_handle and watches->h_list)
*
* The inode->inotify_mutex and inotify_handle->mutex and held during execution
* of a caller's event handler. Thus, the caller must not hold any locks
* taken in their event handler while calling any of the published inotify
* interfaces.
*/
/*
* Lifetimes of the three main data structures--inotify_device, inode, and
* Lifetimes of the three main data structures--inotify_handle, inode, and
* inotify_watch--are managed by reference count.
*
* inotify_device: Lifetime is from inotify_init() until release. Additional
* references can bump the count via get_inotify_dev() and drop the count via
* put_inotify_dev().
* inotify_handle: Lifetime is from inotify_init() to inotify_destroy().
* Additional references can bump the count via get_inotify_handle() and drop
* the count via put_inotify_handle().
*
* inotify_watch: Lifetime is from create_watch() to destory_watch().
* Additional references can bump the count via get_inotify_watch() and drop
* the count via put_inotify_watch().
* inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch()
* to remove_watch_no_event(). Additional references can bump the count via
* get_inotify_watch() and drop the count via put_inotify_watch(). The caller
* is reponsible for the final put after receiving IN_IGNORED, or when using
* IN_ONESHOT after receiving the first event. Inotify does the final put if
* inotify_destroy() is called.
*
* inode: Pinned so long as the inode is associated with a watch, from
* create_watch() to put_inotify_watch().
* inotify_add_watch() to the final put_inotify_watch().
*/
/*
* struct inotify_device - represents an inotify instance
* struct inotify_handle - represents an inotify instance
*
* This structure is protected by the mutex 'mutex'.
*/
struct inotify_device {
wait_queue_head_t wq; /* wait queue for i/o */
struct inotify_handle {
struct idr idr; /* idr mapping wd -> watch */
struct mutex mutex; /* protects this bad boy */
struct list_head events; /* list of queued events */
struct list_head watches; /* list of watches */
atomic_t count; /* reference count */
struct user_struct *user; /* user who opened this dev */
unsigned int queue_size; /* size of the queue (bytes) */
unsigned int event_count; /* number of pending events */
unsigned int max_events; /* maximum number of events */
u32 last_wd; /* the last wd allocated */
const struct inotify_operations *in_ops; /* inotify caller operations */
};
/*
* struct inotify_kernel_event - An inotify event, originating from a watch and
* queued for user-space. A list of these is attached to each instance of the
* device. In read(), this list is walked and all events that can fit in the
* buffer are returned.
*
* Protected by dev->mutex of the device in which we are queued.
*/
struct inotify_kernel_event {
struct inotify_event event; /* the user-space event */
struct list_head list; /* entry in inotify_device's list */
char *name; /* filename, if any */
};
/*
* struct inotify_watch - represents a watch request on a specific inode
*
* d_list is protected by dev->mutex of the associated watch->dev.
* i_list and mask are protected by inode->inotify_mutex of the associated inode.
* dev, inode, and wd are never written to once the watch is created.
*/
struct inotify_watch {
struct list_head d_list; /* entry in inotify_device's list */
struct list_head i_list; /* entry in inode's list */
atomic_t count; /* reference count */
struct inotify_device *dev; /* associated device */
struct inode *inode; /* associated inode */
s32 wd; /* watch descriptor */
u32 mask; /* event mask for this watch */
};
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
static int zero;
ctl_table inotify_table[] = {
{
.ctl_name = INOTIFY_MAX_USER_INSTANCES,
.procname = "max_user_instances",
.data = &inotify_max_user_instances,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &zero,
},
{
.ctl_name = INOTIFY_MAX_USER_WATCHES,
.procname = "max_user_watches",
.data = &inotify_max_user_watches,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &zero,
},
{
.ctl_name = INOTIFY_MAX_QUEUED_EVENTS,
.procname = "max_queued_events",
.data = &inotify_max_queued_events,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &zero
},
{ .ctl_name = 0 }
};
#endif /* CONFIG_SYSCTL */
static inline void get_inotify_dev(struct inotify_device *dev)
static inline void get_inotify_handle(struct inotify_handle *ih)
{
atomic_inc(&dev->count);
atomic_inc(&ih->count);
}
static inline void put_inotify_dev(struct inotify_device *dev)
static inline void put_inotify_handle(struct inotify_handle *ih)
{
if (atomic_dec_and_test(&dev->count)) {
atomic_dec(&dev->user->inotify_devs);
free_uid(dev->user);
idr_destroy(&dev->idr);
kfree(dev);
if (atomic_dec_and_test(&ih->count)) {
idr_destroy(&ih->idr);
kfree(ih);
}
}
static inline void get_inotify_watch(struct inotify_watch *watch)
/**
* get_inotify_watch - grab a reference to an inotify_watch
* @watch: watch to grab
*/
void get_inotify_watch(struct inotify_watch *watch)
{
atomic_inc(&watch->count);
}
EXPORT_SYMBOL_GPL(get_inotify_watch);
/*
/**
* put_inotify_watch - decrements the ref count on a given watch. cleans up
* the watch and its references if the count reaches zero.
* watch references if the count reaches zero. inotify_watch is freed by
* inotify callers via the destroy_watch() op.
* @watch: watch to release
*/
static inline void put_inotify_watch(struct inotify_watch *watch)
void put_inotify_watch(struct inotify_watch *watch)
{
if (atomic_dec_and_test(&watch->count)) {
put_inotify_dev(watch->dev);
iput(watch->inode);
kmem_cache_free(watch_cachep, watch);
}
}
/*
* kernel_event - create a new kernel event with the given parameters
*
* This function can sleep.
*/
static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
const char *name)
{
struct inotify_kernel_event *kevent;
kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
if (unlikely(!kevent))
return NULL;
/* we hand this out to user-space, so zero it just in case */
memset(&kevent->event, 0, sizeof(struct inotify_event));
kevent->event.wd = wd;
kevent->event.mask = mask;
kevent->event.cookie = cookie;
INIT_LIST_HEAD(&kevent->list);
if (name) {
size_t len, rem, event_size = sizeof(struct inotify_event);
/*
* We need to pad the filename so as to properly align an
* array of inotify_event structures. Because the structure is
* small and the common case is a small filename, we just round
* up to the next multiple of the structure's sizeof. This is
* simple and safe for all architectures.
*/
len = strlen(name) + 1;
rem = event_size - len;
if (len > event_size) {
rem = event_size - (len % event_size);
if (len % event_size == 0)
rem = 0;
}
kevent->name = kmalloc(len + rem, GFP_KERNEL);
if (unlikely(!kevent->name)) {
kmem_cache_free(event_cachep, kevent);
return NULL;
}
memcpy(kevent->name, name, len);
if (rem)
memset(kevent->name + len, 0, rem);
kevent->event.len = len + rem;
} else {
kevent->event.len = 0;
kevent->name = NULL;
}
return kevent;
}
/*
* inotify_dev_get_event - return the next event in the given dev's queue
*
* Caller must hold dev->mutex.
*/
static inline struct inotify_kernel_event *
inotify_dev_get_event(struct inotify_device *dev)
{
return list_entry(dev->events.next, struct inotify_kernel_event, list);
}
/*
* inotify_dev_queue_event - add a new event to the given device
*
* Caller must hold dev->mutex. Can sleep (calls kernel_event()).
*/
static void inotify_dev_queue_event(struct inotify_device *dev,
struct inotify_watch *watch, u32 mask,
u32 cookie, const char *name)
{
struct inotify_kernel_event *kevent, *last;
/* coalescing: drop this event if it is a dupe of the previous */
last = inotify_dev_get_event(dev);
if (last && last->event.mask == mask && last->event.wd == watch->wd &&
last->event.cookie == cookie) {
const char *lastname = last->name;
if (!name && !lastname)
return;
if (name && lastname && !strcmp(lastname, name))
return;
}
/* the queue overflowed and we already sent the Q_OVERFLOW event */
if (unlikely(dev->event_count > dev->max_events))
return;
/* if the queue overflows, we need to notify user space */
if (unlikely(dev->event_count == dev->max_events))
kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
else
kevent = kernel_event(watch->wd, mask, cookie, name);
if (unlikely(!kevent))
return;
/* queue the event and wake up anyone waiting */
dev->event_count++;
dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
list_add_tail(&kevent->list, &dev->events);
wake_up_interruptible(&dev->wq);
}
/*
* remove_kevent - cleans up and ultimately frees the given kevent
*
* Caller must hold dev->mutex.
*/
static void remove_kevent(struct inotify_device *dev,
struct inotify_kernel_event *kevent)
{
list_del(&kevent->list);
dev->event_count--;
dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
kfree(kevent->name);
kmem_cache_free(event_cachep, kevent);
}
struct inotify_handle *ih = watch->ih;
/*
* inotify_dev_event_dequeue - destroy an event on the given device
*
* Caller must hold dev->mutex.
*/
static void inotify_dev_event_dequeue(struct inotify_device *dev)
{
if (!list_empty(&dev->events)) {
struct inotify_kernel_event *kevent;
kevent = inotify_dev_get_event(dev);
remove_kevent(dev, kevent);
iput(watch->inode);
ih->in_ops->destroy_watch(watch);
put_inotify_handle(ih);
}
}
EXPORT_SYMBOL_GPL(put_inotify_watch);
/*
* inotify_dev_get_wd - returns the next WD for use by the given dev
* inotify_handle_get_wd - returns the next WD for use by the given handle
*
* Callers must hold dev->mutex. This function can sleep.
* Callers must hold ih->mutex. This function can sleep.
*/
static int inotify_dev_get_wd(struct inotify_device *dev,
struct inotify_watch *watch)
static int inotify_handle_get_wd(struct inotify_handle *ih,
struct inotify_watch *watch)
{
int ret;
do {
if (unlikely(!idr_pre_get(&dev->idr, GFP_KERNEL)))
if (unlikely(!idr_pre_get(&ih->idr, GFP_KERNEL)))
return -ENOSPC;
ret = idr_get_new_above(&dev->idr, watch, dev->last_wd+1, &watch->wd);
ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd);
} while (ret == -EAGAIN);
return ret;
}
if (likely(!ret))
ih->last_wd = watch->wd;
/*
* find_inode - resolve a user-given path to a specific inode and return a nd
*/
static int find_inode(const char __user *dirname, struct nameidata *nd,
unsigned flags)
{
int error;
error = __user_walk(dirname, flags, nd);
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
error = vfs_permission(nd, MAY_READ);
if (error)
path_release(nd);
return error;
return ret;
}
/*
......@@ -422,67 +188,18 @@ static void set_dentry_child_flags(struct inode *inode, int watched)
}
/*
* create_watch - creates a watch on the given device.
*
* Callers must hold dev->mutex. Calls inotify_dev_get_wd() so may sleep.
* Both 'dev' and 'inode' (by way of nameidata) need to be pinned.
*/
static struct inotify_watch *create_watch(struct inotify_device *dev,
u32 mask, struct inode *inode)
{
struct inotify_watch *watch;
int ret;
if (atomic_read(&dev->user->inotify_watches) >=
inotify_max_user_watches)
return ERR_PTR(-ENOSPC);
watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
if (unlikely(!watch))
return ERR_PTR(-ENOMEM);
ret = inotify_dev_get_wd(dev, watch);
if (unlikely(ret)) {
kmem_cache_free(watch_cachep, watch);
return ERR_PTR(ret);
}
dev->last_wd = watch->wd;
watch->mask = mask;
atomic_set(&watch->count, 0);
INIT_LIST_HEAD(&watch->d_list);
INIT_LIST_HEAD(&watch->i_list);
/* save a reference to device and bump the count to make it official */
get_inotify_dev(dev);
watch->dev = dev;
/*
* Save a reference to the inode and bump the ref count to make it
* official. We hold a reference to nameidata, which makes this safe.
*/
watch->inode = igrab(inode);
/* bump our own count, corresponding to our entry in dev->watches */
get_inotify_watch(watch);
atomic_inc(&dev->user->inotify_watches);
return watch;
}
/*
* inotify_find_dev - find the watch associated with the given inode and dev
* inotify_find_handle - find the watch associated with the given inode and
* handle
*
* Callers must hold inode->inotify_mutex.
*/
static struct inotify_watch *inode_find_dev(struct inode *inode,
struct inotify_device *dev)
static struct inotify_watch *inode_find_handle(struct inode *inode,
struct inotify_handle *ih)
{
struct inotify_watch *watch;
list_for_each_entry(watch, &inode->inotify_watches, i_list) {
if (watch->dev == dev)
if (watch->ih == ih)
return watch;
}
......@@ -490,40 +207,40 @@ static struct inotify_watch *inode_find_dev(struct inode *inode,
}
/*
* remove_watch_no_event - remove_watch() without the IN_IGNORED event.
* remove_watch_no_event - remove watch without the IN_IGNORED event.
*
* Callers must hold both inode->inotify_mutex and ih->mutex.
*/
static void remove_watch_no_event(struct inotify_watch *watch,
struct inotify_device *dev)
struct inotify_handle *ih)
{
list_del(&watch->i_list);
list_del(&watch->d_list);
list_del(&watch->h_list);
if (!inotify_inode_watched(watch->inode))
set_dentry_child_flags(watch->inode, 0);
atomic_dec(&dev->user->inotify_watches);
idr_remove(&dev->idr, watch->wd);
put_inotify_watch(watch);
idr_remove(&ih->idr, watch->wd);
}
/*
* remove_watch - Remove a watch from both the device and the inode. Sends
* the IN_IGNORED event to the given device signifying that the inode is no
* longer watched.
*
* Callers must hold both inode->inotify_mutex and dev->mutex. We drop a
* reference to the inode before returning.
/**
* inotify_remove_watch_locked - Remove a watch from both the handle and the
* inode. Sends the IN_IGNORED event signifying that the inode is no longer
* watched. May be invoked from a caller's event handler.
* @ih: inotify handle associated with watch
* @watch: watch to remove
*
* The inode is not iput() so as to remain atomic. If the inode needs to be
* iput(), the call returns one. Otherwise, it returns zero.
* Callers must hold both inode->inotify_mutex and ih->mutex.
*/
static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev)
void inotify_remove_watch_locked(struct inotify_handle *ih,
struct inotify_watch *watch)
{
inotify_dev_queue_event(dev, watch, IN_IGNORED, 0, NULL);
remove_watch_no_event(watch, dev);
remove_watch_no_event(watch, ih);
ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(inotify_remove_watch_locked);
/* Kernel API */
/* Kernel API for producing events */
/*
* inotify_d_instantiate - instantiate dcache entry for inode
......@@ -563,9 +280,10 @@ void inotify_d_move(struct dentry *entry)
* @mask: event mask describing this event
* @cookie: cookie for synchronization, or zero
* @name: filename, if any
* @n_inode: inode associated with name
*/
void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
const char *name)
const char *name, struct inode *n_inode)
{
struct inotify_watch *watch, *next;
......@@ -576,14 +294,13 @@ void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
u32 watch_mask = watch->mask;
if (watch_mask & mask) {
struct inotify_device *dev = watch->dev;
get_inotify_watch(watch);
mutex_lock(&dev->mutex);
inotify_dev_queue_event(dev, watch, mask, cookie, name);
struct inotify_handle *ih= watch->ih;
mutex_lock(&ih->mutex);
if (watch_mask & IN_ONESHOT)
remove_watch_no_event(watch, dev);
mutex_unlock(&dev->mutex);
put_inotify_watch(watch);
remove_watch_no_event(watch, ih);
ih->in_ops->handle_event(watch, watch->wd, mask, cookie,
name, n_inode);
mutex_unlock(&ih->mutex);
}
}
mutex_unlock(&inode->inotify_mutex);
......@@ -613,7 +330,8 @@ void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
if (inotify_inode_watched(inode)) {
dget(parent);
spin_unlock(&dentry->d_lock);
inotify_inode_queue_event(inode, mask, cookie, name);
inotify_inode_queue_event(inode, mask, cookie, name,
dentry->d_inode);
dput(parent);
} else
spin_unlock(&dentry->d_lock);
......@@ -665,7 +383,7 @@ void inotify_unmount_inodes(struct list_head *list)
need_iput_tmp = need_iput;
need_iput = NULL;
/* In case the remove_watch() drops a reference. */
/* In case inotify_remove_watch_locked() drops a reference. */
if (inode != need_iput_tmp)
__iget(inode);
else
......@@ -694,11 +412,12 @@ void inotify_unmount_inodes(struct list_head *list)
mutex_lock(&inode->inotify_mutex);
watches = &inode->inotify_watches;
list_for_each_entry_safe(watch, next_w, watches, i_list) {
struct inotify_device *dev = watch->dev;
mutex_lock(&dev->mutex);
inotify_dev_queue_event(dev, watch, IN_UNMOUNT,0,NULL);
remove_watch(watch, dev);
mutex_unlock(&dev->mutex);
struct inotify_handle *ih= watch->ih;
mutex_lock(&ih->mutex);
ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0,
NULL, NULL);
inotify_remove_watch_locked(ih, watch);
mutex_unlock(&ih->mutex);
}
mutex_unlock(&inode->inotify_mutex);
iput(inode);
......@@ -718,432 +437,292 @@ void inotify_inode_is_dead(struct inode *inode)
mutex_lock(&inode->inotify_mutex);
list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
struct inotify_device *dev = watch->dev;
mutex_lock(&dev->mutex);
remove_watch(watch, dev);
mutex_unlock(&dev->mutex);
struct inotify_handle *ih = watch->ih;
mutex_lock(&ih->mutex);
inotify_remove_watch_locked(ih, watch);
mutex_unlock(&ih->mutex);
}
mutex_unlock(&inode->inotify_mutex);
}
EXPORT_SYMBOL_GPL(inotify_inode_is_dead);
/* Device Interface */
/* Kernel Consumer API */
static unsigned int inotify_poll(struct file *file, poll_table *wait)
/**
* inotify_init - allocate and initialize an inotify instance
* @ops: caller's inotify operations
*/
struct inotify_handle *inotify_init(const struct inotify_operations *ops)
{
struct inotify_device *dev = file->private_data;
int ret = 0;
struct inotify_handle *ih;
poll_wait(file, &dev->wq, wait);
mutex_lock(&dev->mutex);
if (!list_empty(&dev->events))
ret = POLLIN | POLLRDNORM;
mutex_unlock(&dev->mutex);
ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL);
if (unlikely(!ih))
return ERR_PTR(-ENOMEM);
return ret;
idr_init(&ih->idr);
INIT_LIST_HEAD(&ih->watches);
mutex_init(&ih->mutex);
ih->last_wd = 0;
ih->in_ops = ops;
atomic_set(&ih->count, 0);
get_inotify_handle(ih);
return ih;
}
EXPORT_SYMBOL_GPL(inotify_init);
static ssize_t inotify_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
/**
* inotify_init_watch - initialize an inotify watch
* @watch: watch to initialize
*/
void inotify_init_watch(struct inotify_watch *watch)
{
size_t event_size = sizeof (struct inotify_event);
struct inotify_device *dev;
char __user *start;
int ret;
DEFINE_WAIT(wait);
start = buf;
dev = file->private_data;
while (1) {
int events;
prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
mutex_lock(&dev->mutex);
events = !list_empty(&dev->events);
mutex_unlock(&dev->mutex);
if (events) {
ret = 0;
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -EINTR;
break;
}
schedule();
}
finish_wait(&dev->wq, &wait);
if (ret)
return ret;
mutex_lock(&dev->mutex);
while (1) {
struct inotify_kernel_event *kevent;
ret = buf - start;
if (list_empty(&dev->events))
break;
kevent = inotify_dev_get_event(dev);
if (event_size + kevent->event.len > count)
break;
if (copy_to_user(buf, &kevent->event, event_size)) {
ret = -EFAULT;
break;
}
buf += event_size;
count -= event_size;
if (kevent->name) {
if (copy_to_user(buf, kevent->name, kevent->event.len)){
ret = -EFAULT;
break;
}
buf += kevent->event.len;
count -= kevent->event.len;
}
remove_kevent(dev, kevent);
}
mutex_unlock(&dev->mutex);
return ret;
INIT_LIST_HEAD(&watch->h_list);
INIT_LIST_HEAD(&watch->i_list);
atomic_set(&watch->count, 0);
get_inotify_watch(watch); /* initial get */
}
EXPORT_SYMBOL_GPL(inotify_init_watch);
static int inotify_release(struct inode *ignored, struct file *file)
/**
* inotify_destroy - clean up and destroy an inotify instance
* @ih: inotify handle
*/
void inotify_destroy(struct inotify_handle *ih)
{
struct inotify_device *dev = file->private_data;
/*
* Destroy all of the watches on this device. Unfortunately, not very
* Destroy all of the watches for this handle. Unfortunately, not very
* pretty. We cannot do a simple iteration over the list, because we
* do not know the inode until we iterate to the watch. But we need to
* hold inode->inotify_mutex before dev->mutex. The following works.
* hold inode->inotify_mutex before ih->mutex. The following works.
*/
while (1) {
struct inotify_watch *watch;
struct list_head *watches;
struct inode *inode;
mutex_lock(&dev->mutex);
watches = &dev->watches;
mutex_lock(&ih->mutex);
watches = &ih->watches;
if (list_empty(watches)) {
mutex_unlock(&dev->mutex);
mutex_unlock(&ih->mutex);
break;
}
watch = list_entry(watches->next, struct inotify_watch, d_list);
watch = list_entry(watches->next, struct inotify_watch, h_list);
get_inotify_watch(watch);
mutex_unlock(&dev->mutex);
mutex_unlock(&ih->mutex);
inode = watch->inode;
mutex_lock(&inode->inotify_mutex);
mutex_lock(&dev->mutex);
mutex_lock(&ih->mutex);
/* make sure we didn't race with another list removal */
if (likely(idr_find(&dev->idr, watch->wd)))
remove_watch_no_event(watch, dev);
if (likely(idr_find(&ih->idr, watch->wd))) {
remove_watch_no_event(watch, ih);
put_inotify_watch(watch);
}
mutex_unlock(&dev->mutex);
mutex_unlock(&ih->mutex);
mutex_unlock(&inode->inotify_mutex);
put_inotify_watch(watch);
}
/* destroy all of the events on this device */
mutex_lock(&dev->mutex);
while (!list_empty(&dev->events))
inotify_dev_event_dequeue(dev);
mutex_unlock(&dev->mutex);
/* free this device: the put matching the get in inotify_init() */
put_inotify_dev(dev);
return 0;
/* free this handle: the put matching the get in inotify_init() */
put_inotify_handle(ih);
}
EXPORT_SYMBOL_GPL(inotify_destroy);
/*
* inotify_ignore - remove a given wd from this inotify instance.
/**
* inotify_find_watch - find an existing watch for an (ih,inode) pair
* @ih: inotify handle
* @inode: inode to watch
* @watchp: pointer to existing inotify_watch
*
* Can sleep.
* Caller must pin given inode (via nameidata).
*/
static int inotify_ignore(struct inotify_device *dev, s32 wd)
s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
struct inotify_watch **watchp)
{
struct inotify_watch *watch;
struct inode *inode;
mutex_lock(&dev->mutex);
watch = idr_find(&dev->idr, wd);
if (unlikely(!watch)) {
mutex_unlock(&dev->mutex);
return -EINVAL;
}
get_inotify_watch(watch);
inode = watch->inode;
mutex_unlock(&dev->mutex);
struct inotify_watch *old;
int ret = -ENOENT;
mutex_lock(&inode->inotify_mutex);
mutex_lock(&dev->mutex);
mutex_lock(&ih->mutex);
/* make sure that we did not race */
if (likely(idr_find(&dev->idr, wd) == watch))
remove_watch(watch, dev);
old = inode_find_handle(inode, ih);
if (unlikely(old)) {
get_inotify_watch(old); /* caller must put watch */
*watchp = old;
ret = old->wd;
}
mutex_unlock(&dev->mutex);
mutex_unlock(&ih->mutex);
mutex_unlock(&inode->inotify_mutex);
put_inotify_watch(watch);
return 0;
return ret;
}
EXPORT_SYMBOL_GPL(inotify_find_watch);
static long inotify_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
/**
* inotify_find_update_watch - find and update the mask of an existing watch
* @ih: inotify handle
* @inode: inode's watch to update
* @mask: mask of events to watch
*
* Caller must pin given inode (via nameidata).
*/
s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode,
u32 mask)
{
struct inotify_device *dev;
void __user *p;
int ret = -ENOTTY;
dev = file->private_data;
p = (void __user *) arg;
switch (cmd) {
case FIONREAD:
ret = put_user(dev->queue_size, (int __user *) p);
break;
}
return ret;
}
struct inotify_watch *old;
int mask_add = 0;
int ret;
static const struct file_operations inotify_fops = {
.poll = inotify_poll,
.read = inotify_read,
.release = inotify_release,
.unlocked_ioctl = inotify_ioctl,
.compat_ioctl = inotify_ioctl,
};
if (mask & IN_MASK_ADD)
mask_add = 1;
asmlinkage long sys_inotify_init(void)
{
struct inotify_device *dev;
struct user_struct *user;
struct file *filp;
int fd, ret;
fd = get_unused_fd();
if (fd < 0)
return fd;
filp = get_empty_filp();
if (!filp) {
ret = -ENFILE;
goto out_put_fd;
}
/* don't allow invalid bits: we don't want flags set */
mask &= IN_ALL_EVENTS | IN_ONESHOT;
if (unlikely(!mask))
return -EINVAL;
user = get_uid(current->user);
if (unlikely(atomic_read(&user->inotify_devs) >=
inotify_max_user_instances)) {
ret = -EMFILE;
goto out_free_uid;
}
mutex_lock(&inode->inotify_mutex);
mutex_lock(&ih->mutex);
dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
if (unlikely(!dev)) {
ret = -ENOMEM;
goto out_free_uid;
/*
* Handle the case of re-adding a watch on an (inode,ih) pair that we
* are already watching. We just update the mask and return its wd.
*/
old = inode_find_handle(inode, ih);
if (unlikely(!old)) {
ret = -ENOENT;
goto out;
}
filp->f_op = &inotify_fops;
filp->f_vfsmnt = mntget(inotify_mnt);
filp->f_dentry = dget(inotify_mnt->mnt_root);
filp->f_mapping = filp->f_dentry->d_inode->i_mapping;
filp->f_mode = FMODE_READ;
filp->f_flags = O_RDONLY;
filp->private_data = dev;
idr_init(&dev->idr);
INIT_LIST_HEAD(&dev->events);
INIT_LIST_HEAD(&dev->watches);
init_waitqueue_head(&dev->wq);
mutex_init(&dev->mutex);
dev->event_count = 0;
dev->queue_size = 0;
dev->max_events = inotify_max_queued_events;
dev->user = user;
dev->last_wd = 0;
atomic_set(&dev->count, 0);
get_inotify_dev(dev);
atomic_inc(&user->inotify_devs);
fd_install(fd, filp);
return fd;
out_free_uid:
free_uid(user);
put_filp(filp);
out_put_fd:
put_unused_fd(fd);
if (mask_add)
old->mask |= mask;
else
old->mask = mask;
ret = old->wd;
out:
mutex_unlock(&ih->mutex);
mutex_unlock(&inode->inotify_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(inotify_find_update_watch);
asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
/**
* inotify_add_watch - add a watch to an inotify instance
* @ih: inotify handle
* @watch: caller allocated watch structure
* @inode: inode to watch
* @mask: mask of events to watch
*
* Caller must pin given inode (via nameidata).
* Caller must ensure it only calls inotify_add_watch() once per watch.
* Calls inotify_handle_get_wd() so may sleep.
*/
s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
struct inode *inode, u32 mask)
{
struct inotify_watch *watch, *old;
struct inode *inode;
struct inotify_device *dev;
struct nameidata nd;
struct file *filp;
int ret, fput_needed;
int mask_add = 0;
unsigned flags = 0;
filp = fget_light(fd, &fput_needed);
if (unlikely(!filp))
return -EBADF;
/* verify that this is indeed an inotify instance */
if (unlikely(filp->f_op != &inotify_fops)) {
ret = -EINVAL;
goto fput_and_out;
}
if (!(mask & IN_DONT_FOLLOW))
flags |= LOOKUP_FOLLOW;
if (mask & IN_ONLYDIR)
flags |= LOOKUP_DIRECTORY;
ret = find_inode(path, &nd, flags);
if (unlikely(ret))
goto fput_and_out;
int ret = 0;
/* inode held in place by reference to nd; dev by fget on fd */
inode = nd.dentry->d_inode;
dev = filp->private_data;
/* don't allow invalid bits: we don't want flags set */
mask &= IN_ALL_EVENTS | IN_ONESHOT;
if (unlikely(!mask))
return -EINVAL;
watch->mask = mask;
mutex_lock(&inode->inotify_mutex);
mutex_lock(&dev->mutex);
if (mask & IN_MASK_ADD)
mask_add = 1;
mutex_lock(&ih->mutex);
/* don't let user-space set invalid bits: we don't want flags set */
mask &= IN_ALL_EVENTS | IN_ONESHOT;
if (unlikely(!mask)) {
ret = -EINVAL;
/* Initialize a new watch */
ret = inotify_handle_get_wd(ih, watch);
if (unlikely(ret))
goto out;
}
ret = watch->wd;
/* save a reference to handle and bump the count to make it official */
get_inotify_handle(ih);
watch->ih = ih;
/*
* Handle the case of re-adding a watch on an (inode,dev) pair that we
* are already watching. We just update the mask and return its wd.
* Save a reference to the inode and bump the ref count to make it
* official. We hold a reference to nameidata, which makes this safe.
*/
old = inode_find_dev(inode, dev);
if (unlikely(old)) {
if (mask_add)
old->mask |= mask;
else
old->mask = mask;
ret = old->wd;
goto out;
}
watch = create_watch(dev, mask, inode);
if (unlikely(IS_ERR(watch))) {
ret = PTR_ERR(watch);
goto out;
}
watch->inode = igrab(inode);
if (!inotify_inode_watched(inode))
set_dentry_child_flags(inode, 1);
/* Add the watch to the device's and the inode's list */
list_add(&watch->d_list, &dev->watches);
/* Add the watch to the handle's and the inode's list */
list_add(&watch->h_list, &ih->watches);
list_add(&watch->i_list, &inode->inotify_watches);
ret = watch->wd;
out:
mutex_unlock(&dev->mutex);
mutex_unlock(&ih->mutex);
mutex_unlock(&inode->inotify_mutex);
path_release(&nd);
fput_and_out:
fput_light(filp, fput_needed);
return ret;
}
EXPORT_SYMBOL_GPL(inotify_add_watch);
asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
/**
* inotify_rm_wd - remove a watch from an inotify instance
* @ih: inotify handle
* @wd: watch descriptor to remove
*
* Can sleep.
*/
int inotify_rm_wd(struct inotify_handle *ih, u32 wd)
{
struct file *filp;
struct inotify_device *dev;
int ret, fput_needed;
filp = fget_light(fd, &fput_needed);
if (unlikely(!filp))
return -EBADF;
struct inotify_watch *watch;
struct inode *inode;
/* verify that this is indeed an inotify instance */
if (unlikely(filp->f_op != &inotify_fops)) {
ret = -EINVAL;
goto out;
mutex_lock(&ih->mutex);
watch = idr_find(&ih->idr, wd);
if (unlikely(!watch)) {
mutex_unlock(&ih->mutex);
return -EINVAL;
}
get_inotify_watch(watch);
inode = watch->inode;
mutex_unlock(&ih->mutex);
dev = filp->private_data;
ret = inotify_ignore(dev, wd);
mutex_lock(&inode->inotify_mutex);
mutex_lock(&ih->mutex);
out:
fput_light(filp, fput_needed);
return ret;
/* make sure that we did not race */
if (likely(idr_find(&ih->idr, wd) == watch))
inotify_remove_watch_locked(ih, watch);
mutex_unlock(&ih->mutex);
mutex_unlock(&inode->inotify_mutex);
put_inotify_watch(watch);
return 0;
}
EXPORT_SYMBOL_GPL(inotify_rm_wd);
static struct super_block *
inotify_get_sb(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
/**
* inotify_rm_watch - remove a watch from an inotify instance
* @ih: inotify handle
* @watch: watch to remove
*
* Can sleep.
*/
int inotify_rm_watch(struct inotify_handle *ih,
struct inotify_watch *watch)
{
return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA);
return inotify_rm_wd(ih, watch->wd);
}
static struct file_system_type inotify_fs_type = {
.name = "inotifyfs",
.get_sb = inotify_get_sb,
.kill_sb = kill_anon_super,
};
EXPORT_SYMBOL_GPL(inotify_rm_watch);
/*
* inotify_setup - Our initialization function. Note that we cannnot return
* error because we have compiled-in VFS hooks. So an (unlikely) failure here
* must result in panic().
* inotify_setup - core initialization function
*/
static int __init inotify_setup(void)
{
int ret;
ret = register_filesystem(&inotify_fs_type);
if (unlikely(ret))
panic("inotify: register_filesystem returned %d!\n", ret);
inotify_mnt = kern_mount(&inotify_fs_type);
if (IS_ERR(inotify_mnt))
panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
inotify_max_queued_events = 16384;
inotify_max_user_instances = 128;
inotify_max_user_watches = 8192;
atomic_set(&inotify_cookie, 0);
watch_cachep = kmem_cache_create("inotify_watch_cache",
sizeof(struct inotify_watch),
0, SLAB_PANIC, NULL, NULL);
event_cachep = kmem_cache_create("inotify_event_cache",
sizeof(struct inotify_kernel_event),
0, SLAB_PANIC, NULL, NULL);
return 0;
}
......
/*
* fs/inotify_user.c - inotify support for userspace
*
* Authors:
* John McCutchan <ttb@tentacle.dhs.org>
* Robert Love <rml@novell.com>
*
* Copyright (C) 2005 John McCutchan
* Copyright 2006 Hewlett-Packard Development Company, L.P.
*
* 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, 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.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/inotify.h>
#include <linux/syscalls.h>
#include <asm/ioctls.h>
static kmem_cache_t *watch_cachep __read_mostly;
static kmem_cache_t *event_cachep __read_mostly;
static struct vfsmount *inotify_mnt __read_mostly;
/* these are configurable via /proc/sys/fs/inotify/ */
int inotify_max_user_instances __read_mostly;
int inotify_max_user_watches __read_mostly;
int inotify_max_queued_events __read_mostly;
/*
* Lock ordering:
*
* inotify_dev->up_mutex (ensures we don't re-add the same watch)
* inode->inotify_mutex (protects inode's watch list)
* inotify_handle->mutex (protects inotify_handle's watch list)
* inotify_dev->ev_mutex (protects device's event queue)
*/
/*
* Lifetimes of the main data structures:
*
* inotify_device: Lifetime is managed by reference count, from
* sys_inotify_init() until release. Additional references can bump the count
* via get_inotify_dev() and drop the count via put_inotify_dev().
*
* inotify_user_watch: Lifetime is from create_watch() to the receipt of an
* IN_IGNORED event from inotify, or when using IN_ONESHOT, to receipt of the
* first event, or to inotify_destroy().
*/
/*
* struct inotify_device - represents an inotify instance
*
* This structure is protected by the mutex 'mutex'.
*/
struct inotify_device {
wait_queue_head_t wq; /* wait queue for i/o */
struct mutex ev_mutex; /* protects event queue */
struct mutex up_mutex; /* synchronizes watch updates */
struct list_head events; /* list of queued events */
atomic_t count; /* reference count */
struct user_struct *user; /* user who opened this dev */
struct inotify_handle *ih; /* inotify handle */
unsigned int queue_size; /* size of the queue (bytes) */
unsigned int event_count; /* number of pending events */
unsigned int max_events; /* maximum number of events */
};
/*
* struct inotify_kernel_event - An inotify event, originating from a watch and
* queued for user-space. A list of these is attached to each instance of the
* device. In read(), this list is walked and all events that can fit in the
* buffer are returned.
*
* Protected by dev->ev_mutex of the device in which we are queued.
*/
struct inotify_kernel_event {
struct inotify_event event; /* the user-space event */
struct list_head list; /* entry in inotify_device's list */
char *name; /* filename, if any */
};
/*
* struct inotify_user_watch - our version of an inotify_watch, we add
* a reference to the associated inotify_device.
*/
struct inotify_user_watch {
struct inotify_device *dev; /* associated device */
struct inotify_watch wdata; /* inotify watch data */
};
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
static int zero;
ctl_table inotify_table[] = {
{
.ctl_name = INOTIFY_MAX_USER_INSTANCES,
.procname = "max_user_instances",
.data = &inotify_max_user_instances,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &zero,
},
{
.ctl_name = INOTIFY_MAX_USER_WATCHES,
.procname = "max_user_watches",
.data = &inotify_max_user_watches,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &zero,
},
{
.ctl_name = INOTIFY_MAX_QUEUED_EVENTS,
.procname = "max_queued_events",
.data = &inotify_max_queued_events,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &zero
},
{ .ctl_name = 0 }
};
#endif /* CONFIG_SYSCTL */
static inline void get_inotify_dev(struct inotify_device *dev)
{
atomic_inc(&dev->count);
}
static inline void put_inotify_dev(struct inotify_device *dev)
{
if (atomic_dec_and_test(&dev->count)) {
atomic_dec(&dev->user->inotify_devs);
free_uid(dev->user);
kfree(dev);
}
}
/*
* free_inotify_user_watch - cleans up the watch and its references
*/
static void free_inotify_user_watch(struct inotify_watch *w)
{
struct inotify_user_watch *watch;
struct inotify_device *dev;
watch = container_of(w, struct inotify_user_watch, wdata);
dev = watch->dev;
atomic_dec(&dev->user->inotify_watches);
put_inotify_dev(dev);
kmem_cache_free(watch_cachep, watch);
}
/*
* kernel_event - create a new kernel event with the given parameters
*
* This function can sleep.
*/
static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
const char *name)
{
struct inotify_kernel_event *kevent;
kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
if (unlikely(!kevent))
return NULL;
/* we hand this out to user-space, so zero it just in case */
memset(&kevent->event, 0, sizeof(struct inotify_event));
kevent->event.wd = wd;
kevent->event.mask = mask;
kevent->event.cookie = cookie;
INIT_LIST_HEAD(&kevent->list);
if (name) {
size_t len, rem, event_size = sizeof(struct inotify_event);
/*
* We need to pad the filename so as to properly align an
* array of inotify_event structures. Because the structure is
* small and the common case is a small filename, we just round
* up to the next multiple of the structure's sizeof. This is
* simple and safe for all architectures.
*/
len = strlen(name) + 1;
rem = event_size - len;
if (len > event_size) {
rem = event_size - (len % event_size);
if (len % event_size == 0)
rem = 0;
}
kevent->name = kmalloc(len + rem, GFP_KERNEL);
if (unlikely(!kevent->name)) {
kmem_cache_free(event_cachep, kevent);
return NULL;
}
memcpy(kevent->name, name, len);
if (rem)
memset(kevent->name + len, 0, rem);
kevent->event.len = len + rem;
} else {
kevent->event.len = 0;
kevent->name = NULL;
}
return kevent;
}
/*
* inotify_dev_get_event - return the next event in the given dev's queue
*
* Caller must hold dev->ev_mutex.
*/
static inline struct inotify_kernel_event *
inotify_dev_get_event(struct inotify_device *dev)
{
return list_entry(dev->events.next, struct inotify_kernel_event, list);
}
/*
* inotify_dev_queue_event - event handler registered with core inotify, adds
* a new event to the given device
*
* Can sleep (calls kernel_event()).
*/
static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
u32 cookie, const char *name,
struct inode *ignored)
{
struct inotify_user_watch *watch;
struct inotify_device *dev;
struct inotify_kernel_event *kevent, *last;
watch = container_of(w, struct inotify_user_watch, wdata);
dev = watch->dev;
mutex_lock(&dev->ev_mutex);
/* we can safely put the watch as we don't reference it while
* generating the event
*/
if (mask & IN_IGNORED || mask & IN_ONESHOT)
put_inotify_watch(w); /* final put */
/* coalescing: drop this event if it is a dupe of the previous */
last = inotify_dev_get_event(dev);
if (last && last->event.mask == mask && last->event.wd == wd &&
last->event.cookie == cookie) {
const char *lastname = last->name;
if (!name && !lastname)
goto out;
if (name && lastname && !strcmp(lastname, name))
goto out;
}
/* the queue overflowed and we already sent the Q_OVERFLOW event */
if (unlikely(dev->event_count > dev->max_events))
goto out;
/* if the queue overflows, we need to notify user space */
if (unlikely(dev->event_count == dev->max_events))
kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
else
kevent = kernel_event(wd, mask, cookie, name);
if (unlikely(!kevent))
goto out;
/* queue the event and wake up anyone waiting */
dev->event_count++;
dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
list_add_tail(&kevent->list, &dev->events);
wake_up_interruptible(&dev->wq);
out:
mutex_unlock(&dev->ev_mutex);
}
/*
* remove_kevent - cleans up and ultimately frees the given kevent
*
* Caller must hold dev->ev_mutex.
*/
static void remove_kevent(struct inotify_device *dev,
struct inotify_kernel_event *kevent)
{
list_del(&kevent->list);
dev->event_count--;
dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
kfree(kevent->name);
kmem_cache_free(event_cachep, kevent);
}
/*
* inotify_dev_event_dequeue - destroy an event on the given device
*
* Caller must hold dev->ev_mutex.
*/
static void inotify_dev_event_dequeue(struct inotify_device *dev)
{
if (!list_empty(&dev->events)) {
struct inotify_kernel_event *kevent;
kevent = inotify_dev_get_event(dev);
remove_kevent(dev, kevent);
}
}
/*
* find_inode - resolve a user-given path to a specific inode and return a nd
*/
static int find_inode(const char __user *dirname, struct nameidata *nd,
unsigned flags)
{
int error;
error = __user_walk(dirname, flags, nd);
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
error = vfs_permission(nd, MAY_READ);
if (error)
path_release(nd);
return error;
}
/*
* create_watch - creates a watch on the given device.
*
* Callers must hold dev->up_mutex.
*/
static int create_watch(struct inotify_device *dev, struct inode *inode,
u32 mask)
{
struct inotify_user_watch *watch;
int ret;
if (atomic_read(&dev->user->inotify_watches) >=
inotify_max_user_watches)
return -ENOSPC;
watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
if (unlikely(!watch))
return -ENOMEM;
/* save a reference to device and bump the count to make it official */
get_inotify_dev(dev);
watch->dev = dev;
atomic_inc(&dev->user->inotify_watches);
inotify_init_watch(&watch->wdata);
ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask);
if (ret < 0)
free_inotify_user_watch(&watch->wdata);
return ret;
}
/* Device Interface */
static unsigned int inotify_poll(struct file *file, poll_table *wait)
{
struct inotify_device *dev = file->private_data;
int ret = 0;
poll_wait(file, &dev->wq, wait);
mutex_lock(&dev->ev_mutex);
if (!list_empty(&dev->events))
ret = POLLIN | POLLRDNORM;
mutex_unlock(&dev->ev_mutex);
return ret;
}
static ssize_t inotify_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
size_t event_size = sizeof (struct inotify_event);
struct inotify_device *dev;
char __user *start;
int ret;
DEFINE_WAIT(wait);
start = buf;
dev = file->private_data;
while (1) {
int events;
prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
mutex_lock(&dev->ev_mutex);
events = !list_empty(&dev->events);
mutex_unlock(&dev->ev_mutex);
if (events) {
ret = 0;
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -EINTR;
break;
}
schedule();
}
finish_wait(&dev->wq, &wait);
if (ret)
return ret;
mutex_lock(&dev->ev_mutex);
while (1) {
struct inotify_kernel_event *kevent;
ret = buf - start;
if (list_empty(&dev->events))
break;
kevent = inotify_dev_get_event(dev);
if (event_size + kevent->event.len > count)
break;
if (copy_to_user(buf, &kevent->event, event_size)) {
ret = -EFAULT;
break;
}
buf += event_size;
count -= event_size;
if (kevent->name) {
if (copy_to_user(buf, kevent->name, kevent->event.len)){
ret = -EFAULT;
break;
}
buf += kevent->event.len;
count -= kevent->event.len;
}
remove_kevent(dev, kevent);
}
mutex_unlock(&dev->ev_mutex);
return ret;
}
static int inotify_release(struct inode *ignored, struct file *file)
{
struct inotify_device *dev = file->private_data;
inotify_destroy(dev->ih);
/* destroy all of the events on this device */
mutex_lock(&dev->ev_mutex);
while (!list_empty(&dev->events))
inotify_dev_event_dequeue(dev);
mutex_unlock(&dev->ev_mutex);
/* free this device: the put matching the get in inotify_init() */
put_inotify_dev(dev);
return 0;
}
static long inotify_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct inotify_device *dev;
void __user *p;
int ret = -ENOTTY;
dev = file->private_data;
p = (void __user *) arg;
switch (cmd) {
case FIONREAD:
ret = put_user(dev->queue_size, (int __user *) p);
break;
}
return ret;
}
static const struct file_operations inotify_fops = {
.poll = inotify_poll,
.read = inotify_read,
.release = inotify_release,
.unlocked_ioctl = inotify_ioctl,
.compat_ioctl = inotify_ioctl,
};
static const struct inotify_operations inotify_user_ops = {
.handle_event = inotify_dev_queue_event,
.destroy_watch = free_inotify_user_watch,
};
asmlinkage long sys_inotify_init(void)
{
struct inotify_device *dev;
struct inotify_handle *ih;
struct user_struct *user;
struct file *filp;
int fd, ret;
fd = get_unused_fd();
if (fd < 0)
return fd;
filp = get_empty_filp();
if (!filp) {
ret = -ENFILE;
goto out_put_fd;
}
user = get_uid(current->user);
if (unlikely(atomic_read(&user->inotify_devs) >=
inotify_max_user_instances)) {
ret = -EMFILE;
goto out_free_uid;
}
dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
if (unlikely(!dev)) {
ret = -ENOMEM;
goto out_free_uid;
}
ih = inotify_init(&inotify_user_ops);
if (unlikely(IS_ERR(ih))) {
ret = PTR_ERR(ih);
goto out_free_dev;
}
dev->ih = ih;
filp->f_op = &inotify_fops;
filp->f_vfsmnt = mntget(inotify_mnt);
filp->f_dentry = dget(inotify_mnt->mnt_root);
filp->f_mapping = filp->f_dentry->d_inode->i_mapping;
filp->f_mode = FMODE_READ;
filp->f_flags = O_RDONLY;
filp->private_data = dev;
INIT_LIST_HEAD(&dev->events);
init_waitqueue_head(&dev->wq);
mutex_init(&dev->ev_mutex);
mutex_init(&dev->up_mutex);
dev->event_count = 0;
dev->queue_size = 0;
dev->max_events = inotify_max_queued_events;
dev->user = user;
atomic_set(&dev->count, 0);
get_inotify_dev(dev);
atomic_inc(&user->inotify_devs);
fd_install(fd, filp);
return fd;
out_free_dev:
kfree(dev);
out_free_uid:
free_uid(user);
put_filp(filp);
out_put_fd:
put_unused_fd(fd);
return ret;
}
asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
{
struct inode *inode;
struct inotify_device *dev;
struct nameidata nd;
struct file *filp;
int ret, fput_needed;
unsigned flags = 0;
filp = fget_light(fd, &fput_needed);
if (unlikely(!filp))
return -EBADF;
/* verify that this is indeed an inotify instance */
if (unlikely(filp->f_op != &inotify_fops)) {
ret = -EINVAL;
goto fput_and_out;
}
if (!(mask & IN_DONT_FOLLOW))
flags |= LOOKUP_FOLLOW;
if (mask & IN_ONLYDIR)
flags |= LOOKUP_DIRECTORY;
ret = find_inode(path, &nd, flags);
if (unlikely(ret))
goto fput_and_out;
/* inode held in place by reference to nd; dev by fget on fd */
inode = nd.dentry->d_inode;
dev = filp->private_data;
mutex_lock(&dev->up_mutex);
ret = inotify_find_update_watch(dev->ih, inode, mask);
if (ret == -ENOENT)
ret = create_watch(dev, inode, mask);
mutex_unlock(&dev->up_mutex);
path_release(&nd);
fput_and_out:
fput_light(filp, fput_needed);
return ret;
}
asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
{
struct file *filp;
struct inotify_device *dev;
int ret, fput_needed;
filp = fget_light(fd, &fput_needed);
if (unlikely(!filp))
return -EBADF;
/* verify that this is indeed an inotify instance */
if (unlikely(filp->f_op != &inotify_fops)) {
ret = -EINVAL;
goto out;
}
dev = filp->private_data;
/* we free our watch data when we get IN_IGNORED */
ret = inotify_rm_wd(dev->ih, wd);
out:
fput_light(filp, fput_needed);
return ret;
}
static struct super_block *
inotify_get_sb(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
{
return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA);
}
static struct file_system_type inotify_fs_type = {
.name = "inotifyfs",
.get_sb = inotify_get_sb,
.kill_sb = kill_anon_super,
};
/*
* inotify_user_setup - Our initialization function. Note that we cannnot return
* error because we have compiled-in VFS hooks. So an (unlikely) failure here
* must result in panic().
*/
static int __init inotify_user_setup(void)
{
int ret;
ret = register_filesystem(&inotify_fs_type);
if (unlikely(ret))
panic("inotify: register_filesystem returned %d!\n", ret);
inotify_mnt = kern_mount(&inotify_fs_type);
if (IS_ERR(inotify_mnt))
panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
inotify_max_queued_events = 16384;
inotify_max_user_instances = 128;
inotify_max_user_watches = 8192;
watch_cachep = kmem_cache_create("inotify_watch_cache",
sizeof(struct inotify_user_watch),
0, SLAB_PANIC, NULL, NULL);
event_cachep = kmem_cache_create("inotify_event_cache",
sizeof(struct inotify_kernel_event),
0, SLAB_PANIC, NULL, NULL);
return 0;
}
module_init(inotify_user_setup);
......@@ -1127,7 +1127,7 @@ static int fastcall do_path_lookup(int dfd, const char *name,
if (likely(retval == 0)) {
if (unlikely(current->audit_context && nd && nd->dentry &&
nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode, flags);
audit_inode(name, nd->dentry->d_inode);
}
out_fail:
return retval;
......
......@@ -633,7 +633,7 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
dentry = file->f_dentry;
inode = dentry->d_inode;
audit_inode(NULL, inode, 0);
audit_inode(NULL, inode);
err = -EROFS;
if (IS_RDONLY(inode))
......@@ -786,7 +786,7 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
if (file) {
struct dentry * dentry;
dentry = file->f_dentry;
audit_inode(NULL, dentry->d_inode, 0);
audit_inode(NULL, dentry->d_inode);
error = chown_common(dentry, user, group);
fput(file);
}
......
......@@ -1019,8 +1019,8 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
if (current != task)
return -EPERM;
if (count > PAGE_SIZE)
count = PAGE_SIZE;
if (count >= PAGE_SIZE)
count = PAGE_SIZE - 1;
if (*ppos != 0) {
/* No partial writes. */
......@@ -1033,6 +1033,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
if (copy_from_user(page, buf, count))
goto out_free_page;
page[count] = '\0';
loginuid = simple_strtoul(page, &tmp, 10);
if (tmp == page) {
length = -EINVAL;
......
......@@ -242,7 +242,7 @@ sys_fsetxattr(int fd, char __user *name, void __user *value,
if (!f)
return error;
dentry = f->f_dentry;
audit_inode(NULL, dentry->d_inode, 0);
audit_inode(NULL, dentry->d_inode);
error = setxattr(dentry, name, value, size, flags);
fput(f);
return error;
......@@ -469,7 +469,7 @@ sys_fremovexattr(int fd, char __user *name)
if (!f)
return error;
dentry = f->f_dentry;
audit_inode(NULL, dentry->d_inode, 0);
audit_inode(NULL, dentry->d_inode);
error = removexattr(dentry, name);
fput(f);
return error;
......
......@@ -82,7 +82,12 @@
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
#define AUDIT_CWD 1307 /* Current working directory */
#define AUDIT_EXECVE 1309 /* execve arguments */
#define AUDIT_IPC_SET_PERM 1311 /* IPC new permissions record type */
#define AUDIT_MQ_OPEN 1312 /* POSIX MQ open record type */
#define AUDIT_MQ_SENDRECV 1313 /* POSIX MQ send/receive record type */
#define AUDIT_MQ_NOTIFY 1314 /* POSIX MQ notify record type */
#define AUDIT_MQ_GETSETATTR 1315 /* POSIX MQ get/set attribute record type */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
......@@ -150,6 +155,7 @@
#define AUDIT_SE_TYPE 15 /* security label type */
#define AUDIT_SE_SEN 16 /* security label sensitivity label */
#define AUDIT_SE_CLR 17 /* security label clearance label */
#define AUDIT_PPID 18
/* These are ONLY useful when checking
* at syscall exit time (AUDIT_AT_EXIT). */
......@@ -158,6 +164,7 @@
#define AUDIT_INODE 102
#define AUDIT_EXIT 103
#define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */
#define AUDIT_WATCH 105
#define AUDIT_ARG0 200
#define AUDIT_ARG1 (AUDIT_ARG0+1)
......@@ -277,12 +284,16 @@ struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
struct audit_sig_info {
uid_t uid;
pid_t pid;
char ctx[0];
};
struct audit_buffer;
struct audit_context;
struct inode;
struct netlink_skb_parms;
struct linux_binprm;
struct mq_attr;
struct mqstat;
#define AUDITSC_INVALID 0
#define AUDITSC_SUCCESS 1
......@@ -297,15 +308,19 @@ extern void audit_syscall_entry(int arch,
int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void audit_syscall_exit(int failed, long return_code);
extern void audit_getname(const char *name);
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);
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) {
static inline void audit_getname(const char *name)
{
if (unlikely(current->audit_context))
__audit_inode(name, inode, flags);
__audit_getname(name);
}
static inline void audit_inode(const char *name, const struct inode *inode) {
if (unlikely(current->audit_context))
__audit_inode(name, inode);
}
static inline void audit_inode_child(const char *dname,
const struct inode *inode,
......@@ -320,13 +335,61 @@ 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_obj(struct kern_ipc_perm *ipcp);
extern int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp);
extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
extern int audit_bprm(struct linux_binprm *bprm);
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_set_macxattr(const char *name);
extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr);
extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout);
extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
if (unlikely(current->audit_context))
return __audit_ipc_obj(ipcp);
return 0;
}
static inline int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
{
if (unlikely(current->audit_context))
return __audit_ipc_set_perm(qbytes, uid, gid, mode);
return 0;
}
static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
{
if (unlikely(current->audit_context))
return __audit_mq_open(oflag, mode, u_attr);
return 0;
}
static inline int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout)
{
if (unlikely(current->audit_context))
return __audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
return 0;
}
static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout)
{
if (unlikely(current->audit_context))
return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
return 0;
}
static inline int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
{
if (unlikely(current->audit_context))
return __audit_mq_notify(mqdes, u_notification);
return 0;
}
static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
{
if (unlikely(current->audit_context))
return __audit_mq_getsetattr(mqdes, mqstat);
return 0;
}
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
......@@ -334,19 +397,24 @@ extern int audit_set_macxattr(const char *name);
#define audit_syscall_exit(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(n,i) do { ; } while (0)
#define __audit_inode_child(d,i,p) do { ; } while (0)
#define audit_inode(n,i,f) do { ; } while (0)
#define audit_inode(n,i) do { ; } while (0)
#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_obj(i) ({ 0; })
#define audit_ipc_set_perm(q,u,g,m,i) ({ 0; })
#define audit_ipc_set_perm(q,u,g,m) ({ 0; })
#define audit_bprm(p) ({ 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_set_macxattr(n) do { ; } while (0)
#define audit_mq_open(o,m,a) ({ 0; })
#define audit_mq_timedsend(d,l,p,t) ({ 0; })
#define audit_mq_timedreceive(d,l,p,t) ({ 0; })
#define audit_mq_notify(d,n) ({ 0; })
#define audit_mq_getsetattr(d,s) ({ 0; })
#endif
#ifdef CONFIG_AUDIT
......@@ -364,8 +432,11 @@ extern void audit_log_end(struct audit_buffer *ab);
extern void audit_log_hex(struct audit_buffer *ab,
const unsigned char *buf,
size_t len);
extern void audit_log_untrustedstring(struct audit_buffer *ab,
extern const char * audit_log_untrustedstring(struct audit_buffer *ab,
const char *string);
extern const char * audit_log_n_untrustedstring(struct audit_buffer *ab,
size_t n,
const char *string);
extern void audit_log_d_path(struct audit_buffer *ab,
const char *prefix,
struct dentry *dentry,
......@@ -383,8 +454,8 @@ extern int audit_receive_filter(int type, int pid, int uid, int seq,
#define audit_log_end(b) do { ; } while (0)
#define audit_log_hex(a,b,l) do { ; } while (0)
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_n_untrustedstring(a,n,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
......@@ -54,19 +54,20 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
if (isdir)
isdir = IN_ISDIR;
inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name);
inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name);
inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name,
source);
inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name,
source);
if (target) {
inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL);
inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL);
inotify_inode_is_dead(target);
}
if (source) {
inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL);
inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL);
}
audit_inode_child(old_name, source, old_dir->i_ino);
audit_inode_child(new_name, target, new_dir->i_ino);
audit_inode_child(new_name, source, new_dir->i_ino);
}
/*
......@@ -85,7 +86,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
*/
static inline void fsnotify_inoderemove(struct inode *inode)
{
inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL);
inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL);
inotify_inode_is_dead(inode);
}
......@@ -95,7 +96,8 @@ static inline void fsnotify_inoderemove(struct inode *inode)
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, dentry->d_name.name);
inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name,
dentry->d_inode);
audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
}
......@@ -106,7 +108,7 @@ 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,
dentry->d_name.name);
dentry->d_name.name, dentry->d_inode);
audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
}
......@@ -123,7 +125,7 @@ static inline void fsnotify_access(struct dentry *dentry)
dnotify_parent(dentry, DN_ACCESS);
inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
inotify_inode_queue_event(inode, mask, 0, NULL);
inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
}
/*
......@@ -139,7 +141,7 @@ static inline void fsnotify_modify(struct dentry *dentry)
dnotify_parent(dentry, DN_MODIFY);
inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
inotify_inode_queue_event(inode, mask, 0, NULL);
inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
}
/*
......@@ -154,7 +156,7 @@ static inline void fsnotify_open(struct dentry *dentry)
mask |= IN_ISDIR;
inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
inotify_inode_queue_event(inode, mask, 0, NULL);
inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
}
/*
......@@ -172,7 +174,7 @@ static inline void fsnotify_close(struct file *file)
mask |= IN_ISDIR;
inotify_dentry_parent_queue_event(dentry, mask, 0, name);
inotify_inode_queue_event(inode, mask, 0, NULL);
inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
}
/*
......@@ -187,7 +189,7 @@ static inline void fsnotify_xattr(struct dentry *dentry)
mask |= IN_ISDIR;
inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
inotify_inode_queue_event(inode, mask, 0, NULL);
inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
}
/*
......@@ -234,7 +236,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
if (in_mask) {
if (S_ISDIR(inode->i_mode))
in_mask |= IN_ISDIR;
inotify_inode_queue_event(inode, in_mask, 0, NULL);
inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL);
inotify_dentry_parent_queue_event(dentry, in_mask, 0,
dentry->d_name.name);
}
......
......@@ -68,18 +68,65 @@ struct inotify_event {
#include <linux/dcache.h>
#include <linux/fs.h>
/*
* struct inotify_watch - represents a watch request on a specific inode
*
* h_list is protected by ih->mutex of the associated inotify_handle.
* i_list, mask are protected by inode->inotify_mutex of the associated inode.
* ih, inode, and wd are never written to once the watch is created.
*
* Callers must use the established inotify interfaces to access inotify_watch
* contents. The content of this structure is private to the inotify
* implementation.
*/
struct inotify_watch {
struct list_head h_list; /* entry in inotify_handle's list */
struct list_head i_list; /* entry in inode's list */
atomic_t count; /* reference count */
struct inotify_handle *ih; /* associated inotify handle */
struct inode *inode; /* associated inode */
__s32 wd; /* watch descriptor */
__u32 mask; /* event mask for this watch */
};
struct inotify_operations {
void (*handle_event)(struct inotify_watch *, u32, u32, u32,
const char *, struct inode *);
void (*destroy_watch)(struct inotify_watch *);
};
#ifdef CONFIG_INOTIFY
/* Kernel API for producing events */
extern void inotify_d_instantiate(struct dentry *, struct inode *);
extern void inotify_d_move(struct dentry *);
extern void inotify_inode_queue_event(struct inode *, __u32, __u32,
const char *);
const char *, struct inode *);
extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32,
const char *);
extern void inotify_unmount_inodes(struct list_head *);
extern void inotify_inode_is_dead(struct inode *);
extern u32 inotify_get_cookie(void);
/* Kernel Consumer API */
extern struct inotify_handle *inotify_init(const struct inotify_operations *);
extern void inotify_init_watch(struct inotify_watch *);
extern void inotify_destroy(struct inotify_handle *);
extern __s32 inotify_find_watch(struct inotify_handle *, struct inode *,
struct inotify_watch **);
extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *,
u32);
extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *,
struct inode *, __u32);
extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *);
extern int inotify_rm_wd(struct inotify_handle *, __u32);
extern void inotify_remove_watch_locked(struct inotify_handle *,
struct inotify_watch *);
extern void get_inotify_watch(struct inotify_watch *);
extern void put_inotify_watch(struct inotify_watch *);
#else
static inline void inotify_d_instantiate(struct dentry *dentry,
......@@ -93,7 +140,8 @@ static inline void inotify_d_move(struct dentry *dentry)
static inline void inotify_inode_queue_event(struct inode *inode,
__u32 mask, __u32 cookie,
const char *filename)
const char *filename,
struct inode *n_inode)
{
}
......@@ -116,6 +164,62 @@ static inline u32 inotify_get_cookie(void)
return 0;
}
static inline struct inotify_handle *inotify_init(const struct inotify_operations *ops)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline void inotify_init_watch(struct inotify_watch *watch)
{
}
static inline void inotify_destroy(struct inotify_handle *ih)
{
}
static inline __s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
struct inotify_watch **watchp)
{
return -EOPNOTSUPP;
}
static inline __s32 inotify_find_update_watch(struct inotify_handle *ih,
struct inode *inode, u32 mask)
{
return -EOPNOTSUPP;
}
static inline __s32 inotify_add_watch(struct inotify_handle *ih,
struct inotify_watch *watch,
struct inode *inode, __u32 mask)
{
return -EOPNOTSUPP;
}
static inline int inotify_rm_watch(struct inotify_handle *ih,
struct inotify_watch *watch)
{
return -EOPNOTSUPP;
}
static inline int inotify_rm_wd(struct inotify_handle *ih, __u32 wd)
{
return -EOPNOTSUPP;
}
static inline void inotify_remove_watch_locked(struct inotify_handle *ih,
struct inotify_watch *watch)
{
}
static inline void get_inotify_watch(struct inotify_watch *watch)
{
}
static inline void put_inotify_watch(struct inotify_watch *watch)
{
}
#endif /* CONFIG_INOTIFY */
#endif /* __KERNEL __ */
......
......@@ -494,7 +494,7 @@ struct user_struct {
atomic_t processes; /* How many processes does this user have? */
atomic_t files; /* How many open files does this user have? */
atomic_t sigpending; /* How many pending signals does this user have? */
#ifdef CONFIG_INOTIFY
#ifdef CONFIG_INOTIFY_USER
atomic_t inotify_watches; /* How many inotify watches does this user have? */
atomic_t inotify_devs; /* How many inotify devs does this user have opened? */
#endif
......
......@@ -182,7 +182,8 @@ config AUDITSYSCALL
help
Enable low-overhead system-call auditing infrastructure that
can be used independently or with another kernel subsystem,
such as SELinux.
such as SELinux. To use audit's filesystem watch feature, please
ensure that INOTIFY is configured.
config IKCONFIG
bool "Kernel .config support"
......
......@@ -8,6 +8,8 @@
* Lockless receive & send, fd based notify:
* Manfred Spraul (manfred@colorfullife.com)
*
* Audit: George Wilson (ltcgcw@us.ibm.com)
*
* This file is released under the GPL.
*/
......@@ -24,6 +26,7 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/syscalls.h>
#include <linux/audit.h>
#include <linux/signal.h>
#include <linux/mutex.h>
......@@ -657,6 +660,10 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
char *name;
int fd, error;
error = audit_mq_open(oflag, mode, u_attr);
if (error != 0)
return error;
if (IS_ERR(name = getname(u_name)))
return PTR_ERR(name);
......@@ -814,6 +821,10 @@ asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
long timeout;
int ret;
ret = audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
if (ret != 0)
return ret;
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
......@@ -896,6 +907,10 @@ asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
struct mqueue_inode_info *info;
struct ext_wait_queue wait;
ret = audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
if (ret != 0)
return ret;
timeout = prepare_timeout(u_abs_timeout);
ret = -EBADF;
......@@ -975,6 +990,10 @@ asmlinkage long sys_mq_notify(mqd_t mqdes,
struct mqueue_inode_info *info;
struct sk_buff *nc;
ret = audit_mq_notify(mqdes, u_notification);
if (ret != 0)
return ret;
nc = NULL;
sock = NULL;
if (u_notification != NULL) {
......@@ -1115,6 +1134,9 @@ asmlinkage long sys_mq_getsetattr(mqd_t mqdes,
omqstat = info->attr;
omqstat.mq_flags = filp->f_flags & O_NONBLOCK;
if (u_mqstat) {
ret = audit_mq_getsetattr(mqdes, &mqstat);
if (ret != 0)
goto out;
if (mqstat.mq_flags & O_NONBLOCK)
filp->f_flags |= O_NONBLOCK;
else
......
......@@ -454,6 +454,11 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
err = audit_ipc_obj(ipcp);
if (err)
goto out_unlock_up;
if (cmd==IPC_SET) {
err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode);
if (err)
goto out_unlock_up;
}
err = -EPERM;
if (current->euid != ipcp->cuid &&
......@@ -468,10 +473,6 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
switch (cmd) {
case IPC_SET:
{
err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp);
if (err)
goto out_unlock_up;
err = -EPERM;
if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
goto out_unlock_up;
......
......@@ -828,6 +828,11 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
if (err)
goto out_unlock;
if (cmd == IPC_SET) {
err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
if (err)
goto out_unlock;
}
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
err=-EPERM;
......@@ -844,9 +849,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
err = 0;
break;
case IPC_SET:
err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp);
if (err)
goto out_unlock;
ipcp->uid = setbuf.uid;
ipcp->gid = setbuf.gid;
ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
......
......@@ -643,7 +643,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
err = audit_ipc_obj(&(shp->shm_perm));
if (err)
goto out_unlock_up;
err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm));
err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
if (err)
goto out_unlock_up;
err=-EPERM;
......
......@@ -56,6 +56,7 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/selinux.h>
#include <linux/inotify.h>
#include "audit.h"
......@@ -89,6 +90,7 @@ static int audit_backlog_wait_overflow = 0;
/* The identity of the user shutting down the audit system. */
uid_t audit_sig_uid = -1;
pid_t audit_sig_pid = -1;
u32 audit_sig_sid = 0;
/* Records can be lost in several ways:
0) [suppressed in audit_alloc]
......@@ -102,6 +104,12 @@ static atomic_t audit_lost = ATOMIC_INIT(0);
/* The netlink socket. */
static struct sock *audit_sock;
/* Inotify handle. */
struct inotify_handle *audit_ih;
/* Hash for inode-based rules */
struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
/* The audit_freelist is a list of pre-allocated audit buffers (if more
* than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
* being placed on the freelist). */
......@@ -114,10 +122,8 @@ static struct task_struct *kauditd_task;
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
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 */
DEFINE_MUTEX(audit_netlink_mutex);
/* Serialize requests from userspace. */
static DEFINE_MUTEX(audit_cmd_mutex);
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
* audit records. Since printk uses a 1024 byte buffer, this buffer
......@@ -250,7 +256,7 @@ static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
"audit_rate_limit=%d old=%d by auid=%u",
limit, old, loginuid);
audit_rate_limit = limit;
return old;
return 0;
}
static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
......@@ -273,7 +279,7 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
"audit_backlog_limit=%d old=%d by auid=%u",
limit, old, loginuid);
audit_backlog_limit = limit;
return old;
return 0;
}
static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
......@@ -299,7 +305,7 @@ static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
"audit_enabled=%d old=%d by auid=%u",
state, old, loginuid);
audit_enabled = state;
return old;
return 0;
}
static int audit_set_failure(int state, uid_t loginuid, u32 sid)
......@@ -327,7 +333,7 @@ static int audit_set_failure(int state, uid_t loginuid, u32 sid)
"audit_failure=%d old=%d by auid=%u",
state, old, loginuid);
audit_failure = state;
return old;
return 0;
}
static int kauditd_thread(void *dummy)
......@@ -363,9 +369,52 @@ static int kauditd_thread(void *dummy)
remove_wait_queue(&kauditd_wait, &wait);
}
}
}
int audit_send_list(void *_dest)
{
struct audit_netlink_list *dest = _dest;
int pid = dest->pid;
struct sk_buff *skb;
/* wait for parent to finish and send an ACK */
mutex_lock(&audit_cmd_mutex);
mutex_unlock(&audit_cmd_mutex);
while ((skb = __skb_dequeue(&dest->q)) != NULL)
netlink_unicast(audit_sock, skb, pid, 0);
kfree(dest);
return 0;
}
struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
int multi, void *payload, int size)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
int len = NLMSG_SPACE(size);
void *data;
int flags = multi ? NLM_F_MULTI : 0;
int t = done ? NLMSG_DONE : type;
skb = alloc_skb(len, GFP_KERNEL);
if (!skb)
return NULL;
nlh = NLMSG_PUT(skb, pid, seq, t, size);
nlh->nlmsg_flags = flags;
data = NLMSG_DATA(nlh);
memcpy(data, payload, size);
return skb;
nlmsg_failure: /* Used by NLMSG_PUT */
if (skb)
kfree_skb(skb);
return NULL;
}
/**
* audit_send_reply - send an audit reply message via netlink
* @pid: process id to send reply to
......@@ -383,29 +432,13 @@ void audit_send_reply(int pid, int seq, int type, int done, int multi,
void *payload, int size)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
int len = NLMSG_SPACE(size);
void *data;
int flags = multi ? NLM_F_MULTI : 0;
int t = done ? NLMSG_DONE : type;
skb = alloc_skb(len, GFP_KERNEL);
skb = audit_make_reply(pid, seq, type, done, multi, payload, size);
if (!skb)
return;
nlh = NLMSG_PUT(skb, pid, seq, t, size);
nlh->nlmsg_flags = flags;
data = NLMSG_DATA(nlh);
memcpy(data, payload, size);
/* Ignore failure. It'll only happen if the sender goes away,
because our timeout is set to infinite. */
netlink_unicast(audit_sock, skb, pid, 0);
return;
nlmsg_failure: /* Used by NLMSG_PUT */
if (skb)
kfree_skb(skb);
}
/*
......@@ -451,7 +484,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type;
uid_t loginuid; /* loginuid of sender */
struct audit_sig_info sig_data;
struct audit_sig_info *sig_data;
char *ctx;
u32 len;
err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
if (err)
......@@ -503,12 +538,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (status_get->mask & AUDIT_STATUS_PID) {
int old = audit_pid;
if (sid) {
char *ctx = NULL;
u32 len;
int rc;
if ((rc = selinux_ctxid_to_string(
if ((err = selinux_ctxid_to_string(
sid, &ctx, &len)))
return rc;
return err;
else
audit_log(NULL, GFP_KERNEL,
AUDIT_CONFIG_CHANGE,
......@@ -523,10 +555,10 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
audit_pid = status_get->pid;
}
if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
audit_set_rate_limit(status_get->rate_limit,
err = audit_set_rate_limit(status_get->rate_limit,
loginuid, sid);
if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
audit_set_backlog_limit(status_get->backlog_limit,
err = audit_set_backlog_limit(status_get->backlog_limit,
loginuid, sid);
break;
case AUDIT_USER:
......@@ -544,8 +576,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
"user pid=%d uid=%u auid=%u",
pid, uid, loginuid);
if (sid) {
char *ctx = NULL;
u32 len;
if (selinux_ctxid_to_string(
sid, &ctx, &len)) {
audit_log_format(ab,
......@@ -584,10 +614,21 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
loginuid, sid);
break;
case AUDIT_SIGNAL_INFO:
sig_data.uid = audit_sig_uid;
sig_data.pid = audit_sig_pid;
err = selinux_ctxid_to_string(audit_sig_sid, &ctx, &len);
if (err)
return err;
sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
if (!sig_data) {
kfree(ctx);
return -ENOMEM;
}
sig_data->uid = audit_sig_uid;
sig_data->pid = audit_sig_pid;
memcpy(sig_data->ctx, ctx, len);
kfree(ctx);
audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO,
0, 0, &sig_data, sizeof(sig_data));
0, 0, sig_data, sizeof(*sig_data) + len);
kfree(sig_data);
break;
default:
err = -EINVAL;
......@@ -629,20 +670,30 @@ static void audit_receive(struct sock *sk, int length)
struct sk_buff *skb;
unsigned int qlen;
mutex_lock(&audit_netlink_mutex);
mutex_lock(&audit_cmd_mutex);
for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
skb = skb_dequeue(&sk->sk_receive_queue);
audit_receive_skb(skb);
kfree_skb(skb);
}
mutex_unlock(&audit_netlink_mutex);
mutex_unlock(&audit_cmd_mutex);
}
#ifdef CONFIG_AUDITSYSCALL
static const struct inotify_operations audit_inotify_ops = {
.handle_event = audit_handle_ievent,
.destroy_watch = audit_free_parent,
};
#endif
/* Initialize audit support at boot time. */
static int __init audit_init(void)
{
#ifdef CONFIG_AUDITSYSCALL
int i;
#endif
printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
audit_default ? "enabled" : "disabled");
audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
......@@ -661,6 +712,16 @@ static int __init audit_init(void)
selinux_audit_set_callback(&selinux_audit_rule_update);
audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
#ifdef CONFIG_AUDITSYSCALL
audit_ih = inotify_init(&audit_inotify_ops);
if (IS_ERR(audit_ih))
audit_panic("cannot initialize inotify handle");
for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
INIT_LIST_HEAD(&audit_inode_hash[i]);
#endif
return 0;
}
__initcall(audit_init);
......@@ -690,10 +751,12 @@ static void audit_buffer_free(struct audit_buffer *ab)
kfree_skb(ab->skb);
spin_lock_irqsave(&audit_freelist_lock, flags);
if (++audit_freelist_count > AUDIT_MAXFREE)
if (audit_freelist_count > AUDIT_MAXFREE)
kfree(ab);
else
else {
audit_freelist_count++;
list_add(&ab->list, &audit_freelist);
}
spin_unlock_irqrestore(&audit_freelist_lock, flags);
}
......@@ -988,28 +1051,76 @@ void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
skb_put(skb, len << 1); /* new string is twice the old string */
}
/*
* Format a string of no more than slen characters into the audit buffer,
* enclosed in quote marks.
*/
static void audit_log_n_string(struct audit_buffer *ab, size_t slen,
const char *string)
{
int avail, new_len;
unsigned char *ptr;
struct sk_buff *skb;
BUG_ON(!ab->skb);
skb = ab->skb;
avail = skb_tailroom(skb);
new_len = slen + 3; /* enclosing quotes + null terminator */
if (new_len > avail) {
avail = audit_expand(ab, new_len);
if (!avail)
return;
}
ptr = skb->tail;
*ptr++ = '"';
memcpy(ptr, string, slen);
ptr += slen;
*ptr++ = '"';
*ptr = 0;
skb_put(skb, slen + 2); /* don't include null terminator */
}
/**
* audit_log_unstrustedstring - log a string that may contain random characters
* audit_log_n_unstrustedstring - log a string that may contain random characters
* @ab: audit_buffer
* @len: lenth of string (not including trailing null)
* @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).
*
* The caller specifies the number of characters in the string to log, which may
* or may not be the entire string.
*/
void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len,
const char *string)
{
const unsigned char *p = string;
while (*p) {
if (*p == '"' || *p < 0x21 || *p > 0x7f) {
audit_log_hex(ab, string, strlen(string));
return;
audit_log_hex(ab, string, len);
return string + len + 1;
}
p++;
}
audit_log_format(ab, "\"%s\"", string);
audit_log_n_string(ab, len, string);
return p + 1;
}
/**
* audit_log_unstrustedstring - log a string that may contain random characters
* @ab: audit_buffer
* @string: string to be logged
*
* Same as audit_log_n_unstrustedstring(), except that strlen is used to
* determine string length.
*/
const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
{
return audit_log_n_untrustedstring(ab, strlen(string), string);
}
/* This is a helper-function to print the escaped d_path */
......
......@@ -19,9 +19,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/mutex.h>
#include <linux/fs.h>
#include <linux/audit.h>
#include <linux/skbuff.h>
/* 0 = no checking
1 = put_count checking
......@@ -53,6 +53,18 @@ enum audit_state {
};
/* Rule lists */
struct audit_parent;
struct audit_watch {
atomic_t count; /* reference count */
char *path; /* insertion path */
dev_t dev; /* associated superblock device */
unsigned long ino; /* associated inode number */
struct audit_parent *parent; /* associated parent */
struct list_head wlist; /* entry in parent->watches list */
struct list_head rules; /* associated rules */
};
struct audit_field {
u32 type;
u32 val;
......@@ -70,6 +82,9 @@ struct audit_krule {
u32 buflen; /* for data alloc on list rules */
u32 field_count;
struct audit_field *fields;
struct audit_field *inode_f; /* quick access to an inode field */
struct audit_watch *watch; /* associated watch */
struct list_head rlist; /* entry in audit_watch.rules list */
};
struct audit_entry {
......@@ -78,15 +93,53 @@ struct audit_entry {
struct audit_krule rule;
};
extern int audit_pid;
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
#define AUDIT_INODE_BUCKETS 32
extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
static inline int audit_hash_ino(u32 ino)
{
return (ino & (AUDIT_INODE_BUCKETS-1));
}
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
extern int audit_compare_dname_path(const char *dname, const char *path,
int *dirlen);
extern struct sk_buff * audit_make_reply(int pid, int seq, int type,
int done, int multi,
void *payload, int size);
extern void audit_send_reply(int pid, int seq, int type,
int done, int multi,
void *payload, int size);
extern void audit_log_lost(const char *message);
extern void audit_panic(const char *message);
extern struct mutex audit_netlink_mutex;
struct audit_netlink_list {
int pid;
struct sk_buff_head q;
};
int audit_send_list(void *);
struct inotify_watch;
extern void audit_free_parent(struct inotify_watch *);
extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
const char *, struct inode *);
extern int selinux_audit_rule_update(void);
#ifdef CONFIG_AUDITSYSCALL
extern void __audit_signal_info(int sig, struct task_struct *t);
static inline void audit_signal_info(int sig, struct task_struct *t)
{
if (unlikely(audit_pid && t->tgid == audit_pid))
__audit_signal_info(sig, t);
}
extern enum audit_state audit_filter_inodes(struct task_struct *,
struct audit_context *);
extern void audit_set_auditable(struct audit_context *);
#else
#define audit_signal_info(s,t)
#define audit_filter_inodes(t,c) AUDIT_DISABLED
#define audit_set_auditable(c)
#endif
......@@ -22,13 +22,59 @@
#include <linux/kernel.h>
#include <linux/audit.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/netlink.h>
#include <linux/sched.h>
#include <linux/inotify.h>
#include <linux/selinux.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. */
/*
* Locking model:
*
* audit_filter_mutex:
* Synchronizes writes and blocking reads of audit's filterlist
* data. Rcu is used to traverse the filterlist and access
* contents of structs audit_entry, audit_watch and opaque
* selinux rules during filtering. If modified, these structures
* must be copied and replace their counterparts in the filterlist.
* An audit_parent struct is not accessed during filtering, so may
* be written directly provided audit_filter_mutex is held.
*/
/*
* Reference counting:
*
* audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED
* event. Each audit_watch holds a reference to its associated parent.
*
* audit_watch: if added to lists, lifetime is from audit_init_watch() to
* audit_remove_watch(). Additionally, an audit_watch may exist
* temporarily to assist in searching existing filter data. Each
* audit_krule holds a reference to its associated watch.
*/
struct audit_parent {
struct list_head ilist; /* entry in inotify registration list */
struct list_head watches; /* associated watches */
struct inotify_watch wdata; /* inotify watch data */
unsigned flags; /* status flags */
};
/*
* audit_parent status flags:
*
* AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
* a filesystem event to ensure we're adding audit watches to a valid parent.
* Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot
* receive them while we have nameidata, but must be used for IN_MOVE_SELF which
* we can receive while holding nameidata.
*/
#define AUDIT_PARENT_INVALID 0x001
/* Audit filter lists, defined in <linux/audit.h> */
struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
LIST_HEAD_INIT(audit_filter_list[0]),
LIST_HEAD_INIT(audit_filter_list[1]),
......@@ -41,9 +87,53 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
#endif
};
static DEFINE_MUTEX(audit_filter_mutex);
/* Inotify handle */
extern struct inotify_handle *audit_ih;
/* Inotify events we care about. */
#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF
void audit_free_parent(struct inotify_watch *i_watch)
{
struct audit_parent *parent;
parent = container_of(i_watch, struct audit_parent, wdata);
WARN_ON(!list_empty(&parent->watches));
kfree(parent);
}
static inline void audit_get_watch(struct audit_watch *watch)
{
atomic_inc(&watch->count);
}
static void audit_put_watch(struct audit_watch *watch)
{
if (atomic_dec_and_test(&watch->count)) {
WARN_ON(watch->parent);
WARN_ON(!list_empty(&watch->rules));
kfree(watch->path);
kfree(watch);
}
}
static void audit_remove_watch(struct audit_watch *watch)
{
list_del(&watch->wlist);
put_inotify_watch(&watch->parent->wdata);
watch->parent = NULL;
audit_put_watch(watch); /* match initial get */
}
static inline void audit_free_rule(struct audit_entry *e)
{
int i;
/* some rules don't have associated watches */
if (e->rule.watch)
audit_put_watch(e->rule.watch);
if (e->rule.fields)
for (i = 0; i < e->rule.field_count; i++) {
struct audit_field *f = &e->rule.fields[i];
......@@ -60,6 +150,50 @@ static inline void audit_free_rule_rcu(struct rcu_head *head)
audit_free_rule(e);
}
/* Initialize a parent watch entry. */
static struct audit_parent *audit_init_parent(struct nameidata *ndp)
{
struct audit_parent *parent;
s32 wd;
parent = kzalloc(sizeof(*parent), GFP_KERNEL);
if (unlikely(!parent))
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&parent->watches);
parent->flags = 0;
inotify_init_watch(&parent->wdata);
/* grab a ref so inotify watch hangs around until we take audit_filter_mutex */
get_inotify_watch(&parent->wdata);
wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode,
AUDIT_IN_WATCH);
if (wd < 0) {
audit_free_parent(&parent->wdata);
return ERR_PTR(wd);
}
return parent;
}
/* Initialize a watch entry. */
static struct audit_watch *audit_init_watch(char *path)
{
struct audit_watch *watch;
watch = kzalloc(sizeof(*watch), GFP_KERNEL);
if (unlikely(!watch))
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&watch->rules);
atomic_set(&watch->count, 1);
watch->path = path;
watch->dev = (dev_t)-1;
watch->ino = (unsigned long)-1;
return watch;
}
/* Initialize an audit filterlist entry. */
static inline struct audit_entry *audit_init_entry(u32 field_count)
{
......@@ -107,6 +241,43 @@ static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
return str;
}
/* Translate an inode field to kernel respresentation. */
static inline int audit_to_inode(struct audit_krule *krule,
struct audit_field *f)
{
if (krule->listnr != AUDIT_FILTER_EXIT ||
krule->watch || krule->inode_f)
return -EINVAL;
krule->inode_f = f;
return 0;
}
/* Translate a watch string to kernel respresentation. */
static int audit_to_watch(struct audit_krule *krule, char *path, int len,
u32 op)
{
struct audit_watch *watch;
if (!audit_ih)
return -EOPNOTSUPP;
if (path[0] != '/' || path[len-1] == '/' ||
krule->listnr != AUDIT_FILTER_EXIT ||
op & ~AUDIT_EQUAL ||
krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */
return -EINVAL;
watch = audit_init_watch(path);
if (unlikely(IS_ERR(watch)))
return PTR_ERR(watch);
audit_get_watch(watch);
krule->watch = watch;
return 0;
}
/* Common user-space to kernel rule translation. */
static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
{
......@@ -128,8 +299,11 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
#endif
;
}
if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE &&
rule->action != AUDIT_ALWAYS)
if (unlikely(rule->action == AUDIT_POSSIBLE)) {
printk(KERN_ERR "AUDIT_POSSIBLE is deprecated\n");
goto exit_err;
}
if (rule->action != AUDIT_NEVER && rule->action != AUDIT_ALWAYS)
goto exit_err;
if (rule->field_count > AUDIT_MAX_FIELDS)
goto exit_err;
......@@ -158,6 +332,7 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
{
struct audit_entry *entry;
struct audit_field *f;
int err = 0;
int i;
......@@ -172,14 +347,37 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
f->val = rule->values[i];
if (f->type & AUDIT_UNUSED_BITS ||
f->type == AUDIT_SE_USER ||
f->type == AUDIT_SE_ROLE ||
f->type == AUDIT_SE_TYPE ||
f->type == AUDIT_SE_SEN ||
f->type == AUDIT_SE_CLR) {
err = -EINVAL;
err = -EINVAL;
switch(f->type) {
default:
goto exit_free;
case AUDIT_PID:
case AUDIT_UID:
case AUDIT_EUID:
case AUDIT_SUID:
case AUDIT_FSUID:
case AUDIT_GID:
case AUDIT_EGID:
case AUDIT_SGID:
case AUDIT_FSGID:
case AUDIT_LOGINUID:
case AUDIT_PERS:
case AUDIT_ARCH:
case AUDIT_MSGTYPE:
case AUDIT_DEVMAJOR:
case AUDIT_DEVMINOR:
case AUDIT_EXIT:
case AUDIT_SUCCESS:
case AUDIT_ARG0:
case AUDIT_ARG1:
case AUDIT_ARG2:
case AUDIT_ARG3:
break;
case AUDIT_INODE:
err = audit_to_inode(&entry->rule, f);
if (err)
goto exit_free;
break;
}
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
......@@ -196,6 +394,18 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
}
}
f = entry->rule.inode_f;
if (f) {
switch(f->op) {
case AUDIT_NOT_EQUAL:
entry->rule.inode_f = NULL;
case AUDIT_EQUAL:
break;
default:
goto exit_free;
}
}
exit_nofree:
return entry;
......@@ -210,6 +420,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
{
int err = 0;
struct audit_entry *entry;
struct audit_field *f;
void *bufp;
size_t remain = datasz - sizeof(struct audit_rule_data);
int i;
......@@ -235,6 +446,29 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
f->se_str = NULL;
f->se_rule = NULL;
switch(f->type) {
case AUDIT_PID:
case AUDIT_UID:
case AUDIT_EUID:
case AUDIT_SUID:
case AUDIT_FSUID:
case AUDIT_GID:
case AUDIT_EGID:
case AUDIT_SGID:
case AUDIT_FSGID:
case AUDIT_LOGINUID:
case AUDIT_PERS:
case AUDIT_ARCH:
case AUDIT_MSGTYPE:
case AUDIT_PPID:
case AUDIT_DEVMAJOR:
case AUDIT_DEVMINOR:
case AUDIT_EXIT:
case AUDIT_SUCCESS:
case AUDIT_ARG0:
case AUDIT_ARG1:
case AUDIT_ARG2:
case AUDIT_ARG3:
break;
case AUDIT_SE_USER:
case AUDIT_SE_ROLE:
case AUDIT_SE_TYPE:
......@@ -260,6 +494,37 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
} else
f->se_str = str;
break;
case AUDIT_WATCH:
str = audit_unpack_string(&bufp, &remain, f->val);
if (IS_ERR(str))
goto exit_free;
entry->rule.buflen += f->val;
err = audit_to_watch(&entry->rule, str, f->val, f->op);
if (err) {
kfree(str);
goto exit_free;
}
break;
case AUDIT_INODE:
err = audit_to_inode(&entry->rule, f);
if (err)
goto exit_free;
break;
default:
goto exit_free;
}
}
f = entry->rule.inode_f;
if (f) {
switch(f->op) {
case AUDIT_NOT_EQUAL:
entry->rule.inode_f = NULL;
case AUDIT_EQUAL:
break;
default:
goto exit_free;
}
}
......@@ -291,7 +556,7 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
rule = kmalloc(sizeof(*rule), GFP_KERNEL);
if (unlikely(!rule))
return ERR_PTR(-ENOMEM);
return NULL;
memset(rule, 0, sizeof(*rule));
rule->flags = krule->flags | krule->listnr;
......@@ -322,7 +587,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
if (unlikely(!data))
return ERR_PTR(-ENOMEM);
return NULL;
memset(data, 0, sizeof(*data));
data->flags = krule->flags | krule->listnr;
......@@ -343,6 +608,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
data->buflen += data->values[i] =
audit_pack_string(&bufp, f->se_str);
break;
case AUDIT_WATCH:
data->buflen += data->values[i] =
audit_pack_string(&bufp, krule->watch->path);
break;
default:
data->values[i] = f->val;
}
......@@ -378,6 +647,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
return 1;
break;
case AUDIT_WATCH:
if (strcmp(a->watch->path, b->watch->path))
return 1;
break;
default:
if (a->fields[i].val != b->fields[i].val)
return 1;
......@@ -391,6 +664,32 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
return 0;
}
/* Duplicate the given audit watch. The new watch's rules list is initialized
* to an empty list and wlist is undefined. */
static struct audit_watch *audit_dupe_watch(struct audit_watch *old)
{
char *path;
struct audit_watch *new;
path = kstrdup(old->path, GFP_KERNEL);
if (unlikely(!path))
return ERR_PTR(-ENOMEM);
new = audit_init_watch(path);
if (unlikely(IS_ERR(new))) {
kfree(path);
goto out;
}
new->dev = old->dev;
new->ino = old->ino;
get_inotify_watch(&old->parent->wdata);
new->parent = old->parent;
out:
return new;
}
/* Duplicate selinux field information. The se_rule is opaque, so must be
* re-initialized. */
static inline int audit_dupe_selinux_field(struct audit_field *df,
......@@ -422,8 +721,11 @@ static inline int audit_dupe_selinux_field(struct audit_field *df,
/* Duplicate an audit rule. This will be a deep copy with the exception
* of the watch - that pointer is carried over. The selinux specific fields
* will be updated in the copy. The point is to be able to replace the old
* rule with the new rule in the filterlist, then free the old rule. */
static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
* rule with the new rule in the filterlist, then free the old rule.
* The rlist element is undefined; list manipulations are handled apart from
* the initial copy. */
static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
struct audit_watch *watch)
{
u32 fcount = old->field_count;
struct audit_entry *entry;
......@@ -442,6 +744,8 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
new->mask[i] = old->mask[i];
new->buflen = old->buflen;
new->inode_f = old->inode_f;
new->watch = NULL;
new->field_count = old->field_count;
memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
......@@ -463,68 +767,409 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
}
}
if (watch) {
audit_get_watch(watch);
new->watch = watch;
}
return entry;
}
/* Add rule to given filterlist if not a duplicate. Protected by
* audit_netlink_mutex. */
/* Update inode info in audit rules based on filesystem event. */
static void audit_update_watch(struct audit_parent *parent,
const char *dname, dev_t dev,
unsigned long ino, unsigned invalidating)
{
struct audit_watch *owatch, *nwatch, *nextw;
struct audit_krule *r, *nextr;
struct audit_entry *oentry, *nentry;
struct audit_buffer *ab;
mutex_lock(&audit_filter_mutex);
list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
if (audit_compare_dname_path(dname, owatch->path, NULL))
continue;
/* If the update involves invalidating rules, do the inode-based
* filtering now, so we don't omit records. */
if (invalidating &&
audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
audit_set_auditable(current->audit_context);
nwatch = audit_dupe_watch(owatch);
if (unlikely(IS_ERR(nwatch))) {
mutex_unlock(&audit_filter_mutex);
audit_panic("error updating watch, skipping");
return;
}
nwatch->dev = dev;
nwatch->ino = ino;
list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
oentry = container_of(r, struct audit_entry, rule);
list_del(&oentry->rule.rlist);
list_del_rcu(&oentry->list);
nentry = audit_dupe_rule(&oentry->rule, nwatch);
if (unlikely(IS_ERR(nentry)))
audit_panic("error updating watch, removing");
else {
int h = audit_hash_ino((u32)ino);
list_add(&nentry->rule.rlist, &nwatch->rules);
list_add_rcu(&nentry->list, &audit_inode_hash[h]);
}
call_rcu(&oentry->rcu, audit_free_rule_rcu);
}
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
audit_log_format(ab, "audit updated rules specifying watch=");
audit_log_untrustedstring(ab, owatch->path);
audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
audit_log_end(ab);
audit_remove_watch(owatch);
goto add_watch_to_parent; /* event applies to a single watch */
}
mutex_unlock(&audit_filter_mutex);
return;
add_watch_to_parent:
list_add(&nwatch->wlist, &parent->watches);
mutex_unlock(&audit_filter_mutex);
return;
}
/* Remove all watches & rules associated with a parent that is going away. */
static void audit_remove_parent_watches(struct audit_parent *parent)
{
struct audit_watch *w, *nextw;
struct audit_krule *r, *nextr;
struct audit_entry *e;
mutex_lock(&audit_filter_mutex);
parent->flags |= AUDIT_PARENT_INVALID;
list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
e = container_of(r, struct audit_entry, rule);
list_del(&r->rlist);
list_del_rcu(&e->list);
call_rcu(&e->rcu, audit_free_rule_rcu);
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
"audit implicitly removed rule from list=%d\n",
AUDIT_FILTER_EXIT);
}
audit_remove_watch(w);
}
mutex_unlock(&audit_filter_mutex);
}
/* Unregister inotify watches for parents on in_list.
* Generates an IN_IGNORED event. */
static void audit_inotify_unregister(struct list_head *in_list)
{
struct audit_parent *p, *n;
list_for_each_entry_safe(p, n, in_list, ilist) {
list_del(&p->ilist);
inotify_rm_watch(audit_ih, &p->wdata);
/* the put matching the get in audit_do_del_rule() */
put_inotify_watch(&p->wdata);
}
}
/* Find an existing audit rule.
* Caller must hold audit_filter_mutex to prevent stale rule data. */
static struct audit_entry *audit_find_rule(struct audit_entry *entry,
struct list_head *list)
{
struct audit_entry *e, *found = NULL;
int h;
if (entry->rule.watch) {
/* we don't know the inode number, so must walk entire hash */
for (h = 0; h < AUDIT_INODE_BUCKETS; h++) {
list = &audit_inode_hash[h];
list_for_each_entry(e, list, list)
if (!audit_compare_rule(&entry->rule, &e->rule)) {
found = e;
goto out;
}
}
goto out;
}
list_for_each_entry(e, list, list)
if (!audit_compare_rule(&entry->rule, &e->rule)) {
found = e;
goto out;
}
out:
return found;
}
/* Get path information necessary for adding watches. */
static int audit_get_nd(char *path, struct nameidata **ndp,
struct nameidata **ndw)
{
struct nameidata *ndparent, *ndwatch;
int err;
ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL);
if (unlikely(!ndparent))
return -ENOMEM;
ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL);
if (unlikely(!ndwatch)) {
kfree(ndparent);
return -ENOMEM;
}
err = path_lookup(path, LOOKUP_PARENT, ndparent);
if (err) {
kfree(ndparent);
kfree(ndwatch);
return err;
}
err = path_lookup(path, 0, ndwatch);
if (err) {
kfree(ndwatch);
ndwatch = NULL;
}
*ndp = ndparent;
*ndw = ndwatch;
return 0;
}
/* Release resources used for watch path information. */
static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
{
if (ndp) {
path_release(ndp);
kfree(ndp);
}
if (ndw) {
path_release(ndw);
kfree(ndw);
}
}
/* Associate the given rule with an existing parent inotify_watch.
* Caller must hold audit_filter_mutex. */
static void audit_add_to_parent(struct audit_krule *krule,
struct audit_parent *parent)
{
struct audit_watch *w, *watch = krule->watch;
int watch_found = 0;
list_for_each_entry(w, &parent->watches, wlist) {
if (strcmp(watch->path, w->path))
continue;
watch_found = 1;
/* put krule's and initial refs to temporary watch */
audit_put_watch(watch);
audit_put_watch(watch);
audit_get_watch(w);
krule->watch = watch = w;
break;
}
if (!watch_found) {
get_inotify_watch(&parent->wdata);
watch->parent = parent;
list_add(&watch->wlist, &parent->watches);
}
list_add(&krule->rlist, &watch->rules);
}
/* Find a matching watch entry, or add this one.
* Caller must hold audit_filter_mutex. */
static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
struct nameidata *ndw)
{
struct audit_watch *watch = krule->watch;
struct inotify_watch *i_watch;
struct audit_parent *parent;
int ret = 0;
/* update watch filter fields */
if (ndw) {
watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
watch->ino = ndw->dentry->d_inode->i_ino;
}
/* The audit_filter_mutex must not be held during inotify calls because
* we hold it during inotify event callback processing. If an existing
* inotify watch is found, inotify_find_watch() grabs a reference before
* returning.
*/
mutex_unlock(&audit_filter_mutex);
if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &i_watch) < 0) {
parent = audit_init_parent(ndp);
if (IS_ERR(parent)) {
/* caller expects mutex locked */
mutex_lock(&audit_filter_mutex);
return PTR_ERR(parent);
}
} else
parent = container_of(i_watch, struct audit_parent, wdata);
mutex_lock(&audit_filter_mutex);
/* parent was moved before we took audit_filter_mutex */
if (parent->flags & AUDIT_PARENT_INVALID)
ret = -ENOENT;
else
audit_add_to_parent(krule, parent);
/* match get in audit_init_parent or inotify_find_watch */
put_inotify_watch(&parent->wdata);
return ret;
}
/* Add rule to given filterlist if not a duplicate. */
static inline int audit_add_rule(struct audit_entry *entry,
struct list_head *list)
struct list_head *list)
{
struct audit_entry *e;
struct audit_field *inode_f = entry->rule.inode_f;
struct audit_watch *watch = entry->rule.watch;
struct nameidata *ndp, *ndw;
int h, err, putnd_needed = 0;
if (inode_f) {
h = audit_hash_ino(inode_f->val);
list = &audit_inode_hash[h];
}
/* 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;
mutex_lock(&audit_filter_mutex);
e = audit_find_rule(entry, list);
mutex_unlock(&audit_filter_mutex);
if (e) {
err = -EEXIST;
goto error;
}
/* Avoid calling path_lookup under audit_filter_mutex. */
if (watch) {
err = audit_get_nd(watch->path, &ndp, &ndw);
if (err)
goto error;
putnd_needed = 1;
}
mutex_lock(&audit_filter_mutex);
if (watch) {
/* audit_filter_mutex is dropped and re-taken during this call */
err = audit_add_watch(&entry->rule, ndp, ndw);
if (err) {
mutex_unlock(&audit_filter_mutex);
goto error;
}
h = audit_hash_ino((u32)watch->ino);
list = &audit_inode_hash[h];
}
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
list_add_rcu(&entry->list, list);
entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
} else {
list_add_tail_rcu(&entry->list, list);
}
mutex_unlock(&audit_filter_mutex);
return 0;
if (putnd_needed)
audit_put_nd(ndp, ndw);
return 0;
error:
if (putnd_needed)
audit_put_nd(ndp, ndw);
if (watch)
audit_put_watch(watch); /* tmp watch, matches initial get */
return err;
}
/* Remove an existing rule from filterlist. Protected by
* audit_netlink_mutex. */
/* Remove an existing rule from filterlist. */
static inline int audit_del_rule(struct audit_entry *entry,
struct list_head *list)
{
struct audit_entry *e;
struct audit_field *inode_f = entry->rule.inode_f;
struct audit_watch *watch, *tmp_watch = entry->rule.watch;
LIST_HEAD(inotify_list);
int h, ret = 0;
if (inode_f) {
h = audit_hash_ino(inode_f->val);
list = &audit_inode_hash[h];
}
/* 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;
mutex_lock(&audit_filter_mutex);
e = audit_find_rule(entry, list);
if (!e) {
mutex_unlock(&audit_filter_mutex);
ret = -ENOENT;
goto out;
}
watch = e->rule.watch;
if (watch) {
struct audit_parent *parent = watch->parent;
list_del(&e->rule.rlist);
if (list_empty(&watch->rules)) {
audit_remove_watch(watch);
if (list_empty(&parent->watches)) {
/* Put parent on the inotify un-registration
* list. Grab a reference before releasing
* audit_filter_mutex, to be released in
* audit_inotify_unregister(). */
list_add(&parent->ilist, &inotify_list);
get_inotify_watch(&parent->wdata);
}
}
}
return -ENOENT; /* No matching rule */
list_del_rcu(&e->list);
call_rcu(&e->rcu, audit_free_rule_rcu);
mutex_unlock(&audit_filter_mutex);
if (!list_empty(&inotify_list))
audit_inotify_unregister(&inotify_list);
out:
if (tmp_watch)
audit_put_watch(tmp_watch); /* match initial get */
return ret;
}
/* List rules using struct audit_rule. Exists for backward
* compatibility with userspace. */
static int audit_list(void *_dest)
static void audit_list(int pid, int seq, struct sk_buff_head *q)
{
int pid, seq;
int *dest = _dest;
struct sk_buff *skb;
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. */
/* This is a blocking read, so use audit_filter_mutex instead of rcu
* iterator to sync with list writers. */
for (i=0; i<AUDIT_NR_FILTERS; i++) {
list_for_each_entry(entry, &audit_filter_list[i], list) {
struct audit_rule *rule;
......@@ -532,33 +1177,41 @@ static int audit_list(void *_dest)
rule = audit_krule_to_rule(&entry->rule);
if (unlikely(!rule))
break;
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
rule, sizeof(*rule));
if (skb)
skb_queue_tail(q, skb);
kfree(rule);
}
}
audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
mutex_unlock(&audit_netlink_mutex);
return 0;
for (i = 0; i < AUDIT_INODE_BUCKETS; i++) {
list_for_each_entry(entry, &audit_inode_hash[i], list) {
struct audit_rule *rule;
rule = audit_krule_to_rule(&entry->rule);
if (unlikely(!rule))
break;
skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
rule, sizeof(*rule));
if (skb)
skb_queue_tail(q, skb);
kfree(rule);
}
}
skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
if (skb)
skb_queue_tail(q, skb);
}
/* List rules using struct audit_rule_data. */
static int audit_list_rules(void *_dest)
static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
{
int pid, seq;
int *dest = _dest;
struct sk_buff *skb;
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. */
/* This is a blocking read, so use audit_filter_mutex instead of rcu
* iterator to sync with list writers. */
for (i=0; i<AUDIT_NR_FILTERS; i++) {
list_for_each_entry(e, &audit_filter_list[i], list) {
struct audit_rule_data *data;
......@@ -566,15 +1219,30 @@ static int audit_list_rules(void *_dest)
data = audit_krule_to_data(&e->rule);
if (unlikely(!data))
break;
audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
data, sizeof(*data));
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
data, sizeof(*data) + data->buflen);
if (skb)
skb_queue_tail(q, skb);
kfree(data);
}
}
audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
for (i=0; i< AUDIT_INODE_BUCKETS; i++) {
list_for_each_entry(e, &audit_inode_hash[i], list) {
struct audit_rule_data *data;
mutex_unlock(&audit_netlink_mutex);
return 0;
data = audit_krule_to_data(&e->rule);
if (unlikely(!data))
break;
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
data, sizeof(*data) + data->buflen);
if (skb)
skb_queue_tail(q, skb);
kfree(data);
}
}
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
if (skb)
skb_queue_tail(q, skb);
}
/**
......@@ -592,7 +1260,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
size_t datasz, uid_t loginuid, u32 sid)
{
struct task_struct *tsk;
int *dest;
struct audit_netlink_list *dest;
int err = 0;
struct audit_entry *entry;
......@@ -605,18 +1273,22 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
* happen if we're actually running in the context of auditctl
* trying to _send_ the stuff */
dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL);
if (!dest)
return -ENOMEM;
dest[0] = pid;
dest[1] = seq;
dest->pid = pid;
skb_queue_head_init(&dest->q);
mutex_lock(&audit_filter_mutex);
if (type == AUDIT_LIST)
tsk = kthread_run(audit_list, dest, "audit_list");
audit_list(pid, seq, &dest->q);
else
tsk = kthread_run(audit_list_rules, dest,
"audit_list_rules");
audit_list_rules(pid, seq, &dest->q);
mutex_unlock(&audit_filter_mutex);
tsk = kthread_run(audit_send_list, dest, "audit_send_list");
if (IS_ERR(tsk)) {
skb_queue_purge(&dest->q);
kfree(dest);
err = PTR_ERR(tsk);
}
......@@ -632,6 +1304,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
err = audit_add_rule(entry,
&audit_filter_list[entry->rule.listnr]);
if (sid) {
char *ctx = NULL;
u32 len;
......@@ -712,7 +1385,43 @@ int audit_comparator(const u32 left, const u32 op, const u32 right)
return 0;
}
/* Compare given dentry name with last component in given path,
* return of 0 indicates a match. */
int audit_compare_dname_path(const char *dname, const char *path,
int *dirlen)
{
int dlen, plen;
const char *p;
if (!dname || !path)
return 1;
dlen = strlen(dname);
plen = strlen(path);
if (plen < dlen)
return 1;
/* disregard trailing slashes */
p = path + plen - 1;
while ((*p == '/') && (p > path))
p--;
/* find last path component */
p = p - dlen + 1;
if (p < path)
return 1;
else if (p > path) {
if (*--p != '/')
return 1;
else
p++;
}
/* return length of path's directory component */
if (dirlen)
*dirlen = p - path;
return strncmp(p, dname, dlen);
}
static int audit_filter_user_rules(struct netlink_skb_parms *cb,
struct audit_krule *rule,
......@@ -744,7 +1453,6 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb,
}
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;
......@@ -826,32 +1534,65 @@ static inline int audit_rule_has_selinux(struct audit_krule *rule)
int selinux_audit_rule_update(void)
{
struct audit_entry *entry, *n, *nentry;
struct audit_watch *watch;
int i, err = 0;
/* audit_netlink_mutex synchronizes the writers */
mutex_lock(&audit_netlink_mutex);
/* audit_filter_mutex synchronizes the writers */
mutex_lock(&audit_filter_mutex);
for (i = 0; i < AUDIT_NR_FILTERS; i++) {
list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
if (!audit_rule_has_selinux(&entry->rule))
continue;
nentry = audit_dupe_rule(&entry->rule);
watch = entry->rule.watch;
nentry = audit_dupe_rule(&entry->rule, watch);
if (unlikely(IS_ERR(nentry))) {
/* save the first error encountered for the
* return value */
if (!err)
err = PTR_ERR(nentry);
audit_panic("error updating selinux filters");
if (watch)
list_del(&entry->rule.rlist);
list_del_rcu(&entry->list);
} else {
if (watch) {
list_add(&nentry->rule.rlist,
&watch->rules);
list_del(&entry->rule.rlist);
}
list_replace_rcu(&entry->list, &nentry->list);
}
call_rcu(&entry->rcu, audit_free_rule_rcu);
}
}
mutex_unlock(&audit_netlink_mutex);
mutex_unlock(&audit_filter_mutex);
return err;
}
/* Update watch data in audit rules based on inotify events. */
void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask,
u32 cookie, const char *dname, struct inode *inode)
{
struct audit_parent *parent;
parent = container_of(i_watch, struct audit_parent, wdata);
if (mask & (IN_CREATE|IN_MOVED_TO) && inode)
audit_update_watch(parent, dname, inode->i_sb->s_dev,
inode->i_ino, 0);
else if (mask & (IN_DELETE|IN_MOVED_FROM))
audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
/* inotify automatically removes the watch and sends IN_IGNORED */
else if (mask & (IN_DELETE_SELF|IN_UNMOUNT))
audit_remove_parent_watches(parent);
/* inotify does not remove the watch, so remove it manually */
else if(mask & IN_MOVE_SELF) {
audit_remove_parent_watches(parent);
inotify_remove_watch_locked(audit_ih, i_watch);
} else if (mask & IN_IGNORED)
put_inotify_watch(i_watch);
}
......@@ -3,7 +3,7 @@
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
* Copyright 2005 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2005 IBM Corporation
* Copyright (C) 2005, 2006 IBM Corporation
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
......@@ -29,6 +29,9 @@
* this file -- see entry.S) is based on a GPL'd patch written by
* okir@suse.de and Copyright 2003 SuSE Linux AG.
*
* POSIX message queue support added by George Wilson <ltcgcw@us.ibm.com>,
* 2006.
*
* The support of additional filter rules compares (>, <, >=, <=) was
* added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
*
......@@ -49,6 +52,7 @@
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/socket.h>
#include <linux/mqueue.h>
#include <linux/audit.h>
#include <linux/personality.h>
#include <linux/time.h>
......@@ -59,6 +63,8 @@
#include <linux/list.h>
#include <linux/tty.h>
#include <linux/selinux.h>
#include <linux/binfmts.h>
#include <linux/syscalls.h>
#include "audit.h"
......@@ -76,6 +82,9 @@ extern int audit_enabled;
* path_lookup. */
#define AUDIT_NAMES_RESERVED 7
/* Indicates that audit should log the full pathname. */
#define AUDIT_NAME_FULL -1
/* 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).
......@@ -83,8 +92,9 @@ extern int audit_enabled;
* Further, in fs/namei.c:path_lookup() we store the inode and device. */
struct audit_names {
const char *name;
int name_len; /* number of name's characters to log */
unsigned name_put; /* call __putname() for this name */
unsigned long ino;
unsigned long pino;
dev_t dev;
umode_t mode;
uid_t uid;
......@@ -100,6 +110,33 @@ struct audit_aux_data {
#define AUDIT_AUX_IPCPERM 0
struct audit_aux_data_mq_open {
struct audit_aux_data d;
int oflag;
mode_t mode;
struct mq_attr attr;
};
struct audit_aux_data_mq_sendrecv {
struct audit_aux_data d;
mqd_t mqdes;
size_t msg_len;
unsigned int msg_prio;
struct timespec abs_timeout;
};
struct audit_aux_data_mq_notify {
struct audit_aux_data d;
mqd_t mqdes;
struct sigevent notification;
};
struct audit_aux_data_mq_getsetattr {
struct audit_aux_data d;
mqd_t mqdes;
struct mq_attr mqstat;
};
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
struct ipc_perm p;
......@@ -110,6 +147,13 @@ struct audit_aux_data_ipcctl {
u32 osid;
};
struct audit_aux_data_execve {
struct audit_aux_data d;
int argc;
int envc;
char mem[0];
};
struct audit_aux_data_socketcall {
struct audit_aux_data d;
int nargs;
......@@ -148,7 +192,7 @@ struct audit_context {
struct audit_aux_data *aux;
/* Save things to print about task_struct */
pid_t pid;
pid_t pid, ppid;
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;
......@@ -160,12 +204,13 @@ struct audit_context {
#endif
};
/* Determine if any context name data matches a rule's watch data */
/* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise. */
static int audit_filter_rules(struct task_struct *tsk,
struct audit_krule *rule,
struct audit_context *ctx,
struct audit_names *name,
enum audit_state *state)
{
int i, j, need_sid = 1;
......@@ -179,6 +224,10 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_PID:
result = audit_comparator(tsk->pid, f->op, f->val);
break;
case AUDIT_PPID:
if (ctx)
result = audit_comparator(ctx->ppid, f->op, f->val);
break;
case AUDIT_UID:
result = audit_comparator(tsk->uid, f->op, f->val);
break;
......@@ -224,7 +273,10 @@ static int audit_filter_rules(struct task_struct *tsk,
}
break;
case AUDIT_DEVMAJOR:
if (ctx) {
if (name)
result = audit_comparator(MAJOR(name->dev),
f->op, f->val);
else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) {
++result;
......@@ -234,7 +286,10 @@ static int audit_filter_rules(struct task_struct *tsk,
}
break;
case AUDIT_DEVMINOR:
if (ctx) {
if (name)
result = audit_comparator(MINOR(name->dev),
f->op, f->val);
else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
++result;
......@@ -244,16 +299,22 @@ static int audit_filter_rules(struct task_struct *tsk,
}
break;
case AUDIT_INODE:
if (ctx) {
if (name)
result = (name->ino == f->val);
else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
audit_comparator(ctx->names[j].pino, f->op, f->val)) {
if (audit_comparator(ctx->names[j].ino, f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_WATCH:
if (name && rule->watch->ino != (unsigned long)-1)
result = (name->dev == rule->watch->dev &&
name->ino == rule->watch->ino);
break;
case AUDIT_LOGINUID:
result = 0;
if (ctx)
......@@ -294,7 +355,6 @@ static int audit_filter_rules(struct task_struct *tsk,
}
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;
......@@ -311,7 +371,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
rcu_read_lock();
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
if (audit_filter_rules(tsk, &e->rule, NULL, NULL, &state)) {
rcu_read_unlock();
return state;
}
......@@ -341,8 +401,47 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
int bit = AUDIT_BIT(ctx->major);
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit
&& audit_filter_rules(tsk, &e->rule, ctx, &state)) {
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, NULL,
&state)) {
rcu_read_unlock();
return state;
}
}
}
rcu_read_unlock();
return AUDIT_BUILD_CONTEXT;
}
/* At syscall exit time, this filter is called if any audit_names[] have been
* collected during syscall processing. We only check rules in sublists at hash
* buckets applicable to the inode numbers in audit_names[].
* Regarding audit_state, same rules apply as for audit_filter_syscall().
*/
enum audit_state audit_filter_inodes(struct task_struct *tsk,
struct audit_context *ctx)
{
int i;
struct audit_entry *e;
enum audit_state state;
if (audit_pid && tsk->tgid == audit_pid)
return AUDIT_DISABLED;
rcu_read_lock();
for (i = 0; i < ctx->name_count; i++) {
int word = AUDIT_WORD(ctx->major);
int bit = AUDIT_BIT(ctx->major);
struct audit_names *n = &ctx->names[i];
int h = audit_hash_ino((u32)n->ino);
struct list_head *list = &audit_inode_hash[h];
if (list_empty(list))
continue;
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, n, &state)) {
rcu_read_unlock();
return state;
}
......@@ -352,6 +451,11 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
return AUDIT_BUILD_CONTEXT;
}
void audit_set_auditable(struct audit_context *ctx)
{
ctx->auditable = 1;
}
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
int return_valid,
int return_code)
......@@ -365,12 +469,22 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
if (context->in_syscall && !context->auditable) {
enum audit_state state;
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
if (state == AUDIT_RECORD_CONTEXT) {
context->auditable = 1;
goto get_context;
}
state = audit_filter_inodes(tsk, context);
if (state == AUDIT_RECORD_CONTEXT)
context->auditable = 1;
}
get_context:
context->pid = tsk->pid;
context->ppid = sys_getppid(); /* sic. tsk == current in all cases */
context->uid = tsk->uid;
context->gid = tsk->gid;
context->euid = tsk->euid;
......@@ -413,7 +527,7 @@ static inline void audit_free_names(struct audit_context *context)
#endif
for (i = 0; i < context->name_count; i++) {
if (context->names[i].name)
if (context->names[i].name && context->names[i].name_put)
__putname(context->names[i].name);
}
context->name_count = 0;
......@@ -606,7 +720,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
tty = "(none)";
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" pid=%d auid=%u uid=%u gid=%u"
" ppid=%d pid=%d auid=%u uid=%u gid=%u"
" euid=%u suid=%u fsuid=%u"
" egid=%u sgid=%u fsgid=%u tty=%s",
context->argv[0],
......@@ -614,6 +728,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
context->argv[2],
context->argv[3],
context->name_count,
context->ppid,
context->pid,
context->loginuid,
context->uid,
......@@ -630,11 +745,48 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
continue; /* audit_panic has been called */
switch (aux->type) {
case AUDIT_MQ_OPEN: {
struct audit_aux_data_mq_open *axi = (void *)aux;
audit_log_format(ab,
"oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld "
"mq_msgsize=%ld mq_curmsgs=%ld",
axi->oflag, axi->mode, axi->attr.mq_flags,
axi->attr.mq_maxmsg, axi->attr.mq_msgsize,
axi->attr.mq_curmsgs);
break; }
case AUDIT_MQ_SENDRECV: {
struct audit_aux_data_mq_sendrecv *axi = (void *)aux;
audit_log_format(ab,
"mqdes=%d msg_len=%zd msg_prio=%u "
"abs_timeout_sec=%ld abs_timeout_nsec=%ld",
axi->mqdes, axi->msg_len, axi->msg_prio,
axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec);
break; }
case AUDIT_MQ_NOTIFY: {
struct audit_aux_data_mq_notify *axi = (void *)aux;
audit_log_format(ab,
"mqdes=%d sigev_signo=%d",
axi->mqdes,
axi->notification.sigev_signo);
break; }
case AUDIT_MQ_GETSETATTR: {
struct audit_aux_data_mq_getsetattr *axi = (void *)aux;
audit_log_format(ab,
"mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld "
"mq_curmsgs=%ld ",
axi->mqdes,
axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg,
axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs);
break; }
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);
"ouid=%u ogid=%u mode=%x",
axi->uid, axi->gid, axi->mode);
if (axi->osid != 0) {
char *ctx = NULL;
u32 len;
......@@ -652,19 +804,18 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
case AUDIT_IPC_SET_PERM: {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
" new qbytes=%lx new iuid=%u new igid=%u new mode=%x",
"qbytes=%lx ouid=%u ogid=%u mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode);
if (axi->osid != 0) {
char *ctx = NULL;
u32 len;
if (selinux_ctxid_to_string(
axi->osid, &ctx, &len)) {
audit_log_format(ab, " osid=%u",
axi->osid);
call_panic = 1;
} else
audit_log_format(ab, " obj=%s", ctx);
kfree(ctx);
break; }
case AUDIT_EXECVE: {
struct audit_aux_data_execve *axi = (void *)aux;
int i;
const char *p;
for (i = 0, p = axi->mem; i < axi->argc; i++) {
audit_log_format(ab, "a%d=", i);
p = audit_log_untrustedstring(ab, p);
audit_log_format(ab, "\n");
}
break; }
......@@ -700,8 +851,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
}
}
for (i = 0; i < context->name_count; i++) {
unsigned long ino = context->names[i].ino;
unsigned long pino = context->names[i].pino;
struct audit_names *n = &context->names[i];
ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
if (!ab)
......@@ -709,33 +859,47 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_format(ab, "item=%d", i);
audit_log_format(ab, " name=");
if (context->names[i].name)
audit_log_untrustedstring(ab, context->names[i].name);
else
audit_log_format(ab, "(null)");
if (pino != (unsigned long)-1)
audit_log_format(ab, " parent=%lu", pino);
if (ino != (unsigned long)-1)
audit_log_format(ab, " inode=%lu", ino);
if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
audit_log_format(ab, " dev=%02x:%02x mode=%#o"
" ouid=%u ogid=%u rdev=%02x:%02x",
MAJOR(context->names[i].dev),
MINOR(context->names[i].dev),
context->names[i].mode,
context->names[i].uid,
context->names[i].gid,
MAJOR(context->names[i].rdev),
MINOR(context->names[i].rdev));
if (context->names[i].osid != 0) {
if (n->name) {
switch(n->name_len) {
case AUDIT_NAME_FULL:
/* log the full path */
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, n->name);
break;
case 0:
/* name was specified as a relative path and the
* directory component is the cwd */
audit_log_d_path(ab, " name=", context->pwd,
context->pwdmnt);
break;
default:
/* log the name's directory component */
audit_log_format(ab, " name=");
audit_log_n_untrustedstring(ab, n->name_len,
n->name);
}
} else
audit_log_format(ab, " name=(null)");
if (n->ino != (unsigned long)-1) {
audit_log_format(ab, " inode=%lu"
" dev=%02x:%02x mode=%#o"
" ouid=%u ogid=%u rdev=%02x:%02x",
n->ino,
MAJOR(n->dev),
MINOR(n->dev),
n->mode,
n->uid,
n->gid,
MAJOR(n->rdev),
MINOR(n->rdev));
}
if (n->osid != 0) {
char *ctx = NULL;
u32 len;
if (selinux_ctxid_to_string(
context->names[i].osid, &ctx, &len)) {
audit_log_format(ab, " osid=%u",
context->names[i].osid);
n->osid, &ctx, &len)) {
audit_log_format(ab, " osid=%u", n->osid);
call_panic = 2;
} else
audit_log_format(ab, " obj=%s", ctx);
......@@ -908,11 +1072,11 @@ void audit_syscall_exit(int valid, long return_code)
* Add a name to the list of audit names for this context.
* Called from fs/namei.c:getname().
*/
void audit_getname(const char *name)
void __audit_getname(const char *name)
{
struct audit_context *context = current->audit_context;
if (!context || IS_ERR(name) || !name)
if (IS_ERR(name) || !name)
return;
if (!context->in_syscall) {
......@@ -925,6 +1089,8 @@ void audit_getname(const char *name)
}
BUG_ON(context->name_count >= AUDIT_NAMES);
context->names[context->name_count].name = name;
context->names[context->name_count].name_len = AUDIT_NAME_FULL;
context->names[context->name_count].name_put = 1;
context->names[context->name_count].ino = (unsigned long)-1;
++context->name_count;
if (!context->pwd) {
......@@ -991,11 +1157,10 @@ static void audit_inode_context(int idx, const struct inode *inode)
* 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)
void __audit_inode(const char *name, const struct inode *inode)
{
int idx;
struct audit_context *context = current->audit_context;
......@@ -1021,20 +1186,13 @@ void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
++context->ino_count;
#endif
}
context->names[idx].ino = inode->i_ino;
context->names[idx].dev = inode->i_sb->s_dev;
context->names[idx].mode = inode->i_mode;
context->names[idx].uid = inode->i_uid;
context->names[idx].gid = inode->i_gid;
context->names[idx].rdev = inode->i_rdev;
audit_inode_context(idx, inode);
if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) &&
(strcmp(name, ".") != 0)) {
context->names[idx].ino = (unsigned long)-1;
context->names[idx].pino = inode->i_ino;
} else {
context->names[idx].ino = inode->i_ino;
context->names[idx].pino = (unsigned long)-1;
}
}
/**
......@@ -1056,51 +1214,40 @@ void __audit_inode_child(const char *dname, const struct inode *inode,
{
int idx;
struct audit_context *context = current->audit_context;
const char *found_name = NULL;
int dirlen = 0;
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;
if (!dname)
goto update_context;
for (idx = 0; idx < context->name_count; idx++)
if (context->names[idx].ino == pino) {
const char *name = context->names[idx].name;
if (!name)
continue;
if (audit_compare_dname_path(dname, name, &dirlen) == 0) {
context->names[idx].name_len = dirlen;
found_name = name;
break;
}
}
/* catch-all in case match not found */
update_context:
idx = context->name_count++;
context->names[idx].name = NULL;
context->names[idx].pino = pino;
#if AUDIT_DEBUG
context->ino_count++;
#endif
/* Re-use the name belonging to the slot for a matching parent directory.
* All names for this context are relinquished in audit_free_names() */
context->names[idx].name = found_name;
context->names[idx].name_len = AUDIT_NAME_FULL;
context->names[idx].name_put = 0; /* don't call __putname() */
update_context:
if (inode) {
context->names[idx].ino = inode->i_ino;
context->names[idx].dev = inode->i_sb->s_dev;
......@@ -1109,7 +1256,8 @@ void __audit_inode_child(const char *dname, const struct inode *inode,
context->names[idx].gid = inode->i_gid;
context->names[idx].rdev = inode->i_rdev;
audit_inode_context(idx, inode);
}
} else
context->names[idx].ino = (unsigned long)-1;
}
/**
......@@ -1142,18 +1290,23 @@ void auditsc_get_stamp(struct audit_context *ctx,
*/
int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
{
if (task->audit_context) {
struct audit_buffer *ab;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
if (ab) {
audit_log_format(ab, "login pid=%d uid=%u "
"old auid=%u new auid=%u",
task->pid, task->uid,
task->audit_context->loginuid, loginuid);
audit_log_end(ab);
struct audit_context *context = task->audit_context;
if (context) {
/* Only log if audit is enabled */
if (context->in_syscall) {
struct audit_buffer *ab;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
if (ab) {
audit_log_format(ab, "login pid=%d uid=%u "
"old auid=%u new auid=%u",
task->pid, task->uid,
context->loginuid, loginuid);
audit_log_end(ab);
}
}
task->audit_context->loginuid = loginuid;
context->loginuid = loginuid;
}
return 0;
}
......@@ -1170,19 +1323,220 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
}
/**
* audit_ipc_obj - record audit data for ipc object
* @ipcp: ipc permissions
* __audit_mq_open - record audit data for a POSIX MQ open
* @oflag: open flag
* @mode: mode bits
* @u_attr: queue attributes
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int audit_ipc_obj(struct kern_ipc_perm *ipcp)
int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
{
struct audit_aux_data_ipcctl *ax;
struct audit_aux_data_mq_open *ax;
struct audit_context *context = current->audit_context;
if (!audit_enabled)
return 0;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
if (u_attr != NULL) {
if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr))) {
kfree(ax);
return -EFAULT;
}
} else
memset(&ax->attr, 0, sizeof(ax->attr));
ax->oflag = oflag;
ax->mode = mode;
ax->d.type = AUDIT_MQ_OPEN;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
/**
* __audit_mq_timedsend - record audit data for a POSIX MQ timed send
* @mqdes: MQ descriptor
* @msg_len: Message length
* @msg_prio: Message priority
* @abs_timeout: Message timeout in absolute time
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
const struct timespec __user *u_abs_timeout)
{
struct audit_aux_data_mq_sendrecv *ax;
struct audit_context *context = current->audit_context;
if (!audit_enabled)
return 0;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
if (u_abs_timeout != NULL) {
if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
kfree(ax);
return -EFAULT;
}
} else
memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
ax->mqdes = mqdes;
ax->msg_len = msg_len;
ax->msg_prio = msg_prio;
ax->d.type = AUDIT_MQ_SENDRECV;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
/**
* __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
* @mqdes: MQ descriptor
* @msg_len: Message length
* @msg_prio: Message priority
* @abs_timeout: Message timeout in absolute time
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
unsigned int __user *u_msg_prio,
const struct timespec __user *u_abs_timeout)
{
struct audit_aux_data_mq_sendrecv *ax;
struct audit_context *context = current->audit_context;
if (!audit_enabled)
return 0;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
if (u_msg_prio != NULL) {
if (get_user(ax->msg_prio, u_msg_prio)) {
kfree(ax);
return -EFAULT;
}
} else
ax->msg_prio = 0;
if (u_abs_timeout != NULL) {
if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
kfree(ax);
return -EFAULT;
}
} else
memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
ax->mqdes = mqdes;
ax->msg_len = msg_len;
ax->d.type = AUDIT_MQ_SENDRECV;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
/**
* __audit_mq_notify - record audit data for a POSIX MQ notify
* @mqdes: MQ descriptor
* @u_notification: Notification event
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
{
struct audit_aux_data_mq_notify *ax;
struct audit_context *context = current->audit_context;
if (!audit_enabled)
return 0;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
if (u_notification != NULL) {
if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification))) {
kfree(ax);
return -EFAULT;
}
} else
memset(&ax->notification, 0, sizeof(ax->notification));
ax->mqdes = mqdes;
ax->d.type = AUDIT_MQ_NOTIFY;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
/**
* __audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute
* @mqdes: MQ descriptor
* @mqstat: MQ flags
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
{
struct audit_aux_data_mq_getsetattr *ax;
struct audit_context *context = current->audit_context;
if (!audit_enabled)
return 0;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
ax->mqdes = mqdes;
ax->mqstat = *mqstat;
ax->d.type = AUDIT_MQ_GETSETATTR;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
/**
* audit_ipc_obj - record audit data for ipc object
* @ipcp: ipc permissions
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int __audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
struct audit_aux_data_ipcctl *ax;
struct audit_context *context = current->audit_context;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
......@@ -1207,14 +1561,11 @@ int audit_ipc_obj(struct kern_ipc_perm *ipcp)
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
{
struct audit_aux_data_ipcctl *ax;
struct audit_context *context = current->audit_context;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
......@@ -1223,7 +1574,6 @@ int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
ax->uid = uid;
ax->gid = gid;
ax->mode = mode;
selinux_get_ipc_sid(ipcp, &ax->osid);
ax->d.type = AUDIT_IPC_SET_PERM;
ax->d.next = context->aux;
......@@ -1231,6 +1581,39 @@ int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
return 0;
}
int audit_bprm(struct linux_binprm *bprm)
{
struct audit_aux_data_execve *ax;
struct audit_context *context = current->audit_context;
unsigned long p, next;
void *to;
if (likely(!audit_enabled || !context))
return 0;
ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p,
GFP_KERNEL);
if (!ax)
return -ENOMEM;
ax->argc = bprm->argc;
ax->envc = bprm->envc;
for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) {
struct page *page = bprm->page[p / PAGE_SIZE];
void *kaddr = kmap(page);
next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1);
memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p);
to += next - p;
kunmap(page);
}
ax->d.type = AUDIT_EXECVE;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
/**
* audit_socketcall - record audit data for sys_socketcall
* @nargs: number of args
......@@ -1325,19 +1708,20 @@ int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
* 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)
void __audit_signal_info(int sig, struct task_struct *t)
{
extern pid_t audit_sig_pid;
extern uid_t audit_sig_uid;
if (unlikely(audit_pid && t->tgid == audit_pid)) {
if (sig == SIGTERM || sig == SIGHUP) {
struct audit_context *ctx = current->audit_context;
audit_sig_pid = current->pid;
if (ctx)
audit_sig_uid = ctx->loginuid;
else
audit_sig_uid = current->uid;
}
extern u32 audit_sig_sid;
if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) {
struct task_struct *tsk = current;
struct audit_context *ctx = tsk->audit_context;
audit_sig_pid = tsk->pid;
if (ctx)
audit_sig_uid = ctx->loginuid;
else
audit_sig_uid = tsk->uid;
selinux_get_task_sid(tsk, &audit_sig_sid);
}
}
......@@ -23,12 +23,12 @@
#include <linux/syscalls.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/audit.h>
#include <linux/capability.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/siginfo.h>
#include "audit.h" /* audit_signal_info() */
/*
* SLAB caches for signal bits.
......
......@@ -150,7 +150,7 @@ extern ctl_table random_table[];
#ifdef CONFIG_UNIX98_PTYS
extern ctl_table pty_table[];
#endif
#ifdef CONFIG_INOTIFY
#ifdef CONFIG_INOTIFY_USER
extern ctl_table inotify_table[];
#endif
......@@ -1028,7 +1028,7 @@ static ctl_table fs_table[] = {
.mode = 0644,
.proc_handler = &proc_doulongvec_minmax,
},
#ifdef CONFIG_INOTIFY
#ifdef CONFIG_INOTIFY_USER
{
.ctl_name = FS_INOTIFY,
.procname = "inotify",
......
......@@ -140,7 +140,7 @@ struct user_struct * alloc_uid(uid_t uid)
atomic_set(&new->processes, 0);
atomic_set(&new->files, 0);
atomic_set(&new->sigpending, 0);
#ifdef CONFIG_INOTIFY
#ifdef CONFIG_INOTIFY_USER
atomic_set(&new->inotify_watches, 0);
atomic_set(&new->inotify_devs, 0);
#endif
......
......@@ -1980,7 +1980,7 @@ int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op,
break;
case AUDIT_SE_SEN:
case AUDIT_SE_CLR:
level = (op == AUDIT_SE_SEN ?
level = (field == AUDIT_SE_SEN ?
&ctxt->range.level[0] : &ctxt->range.level[1]);
switch (op) {
case AUDIT_EQUAL:
......
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