forked from Minki/linux
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
This commit is contained in:
commit
d9eaec9e29
@ -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.
|
||||
|
||||
|
24
fs/Kconfig
24
fs/Kconfig
@ -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);
|
||||
|
1039
fs/inotify.c
1039
fs/inotify.c
File diff suppressed because it is too large
Load Diff
719
fs/inotify_user.c
Normal file
719
fs/inotify_user.c
Normal file
@ -0,0 +1,719 @@
|
||||
/*
|
||||
* 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 @@ out:
|
||||
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"
|
||||
|
22
ipc/mqueue.c
22
ipc/mqueue.c
@ -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;
|
||||
|
205
kernel/audit.c
205
kernel/audit.c
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
638
kernel/auditsc.c
638
kernel/auditsc.c
@ -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,9 @@ 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;
|
||||
}
|
||||
@ -352,6 +413,49 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
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 (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 (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->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 (!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 (nlen < dlen)
|
||||
continue;
|
||||
|
||||
/* disregard trailing slashes */
|
||||
n = name + nlen - 1;
|
||||
while ((*n == '/') && (n > name))
|
||||
n--;
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
/* 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 (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 @@ update_context:
|
||||
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;
|
||||
struct audit_context *context = task->audit_context;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@ -1169,20 +1322,221 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
|
||||
return ctx ? ctx->loginuid : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* __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_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
|
||||
{
|
||||
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)
|
||||
int __audit_ipc_obj(struct kern_ipc_perm *ipcp)
|
||||
{
|
||||
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;
|
||||
@ -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;
|
||||
extern u32 audit_sig_sid;
|
||||
|
||||
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;
|
||||
}
|
||||
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:
|
||||
|
Loading…
Reference in New Issue
Block a user