fanotify: permissions and blocking
This is the backend work needed for fanotify to support the new FS_OPEN_PERM and FS_ACCESS_PERM fsnotify events. This is done using the new fsnotify secondary queue. No userspace interface is provided actually respond to or request these events. Signed-off-by: Eric Paris <eparis@redhat.com>
This commit is contained in:
@@ -10,3 +10,17 @@ config FANOTIFY
|
||||
the event.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config FANOTIFY_ACCESS_PERMISSIONS
|
||||
bool "fanotify permissions checking"
|
||||
depends on FANOTIFY
|
||||
depends on SECURITY
|
||||
default n
|
||||
---help---
|
||||
Say Y here is you want fanotify listeners to be able to make permissions
|
||||
decisions concerning filesystem events. This is used by some fanotify
|
||||
listeners which need to scan files before allowing the system access to
|
||||
use those files. This is used by some anti-malware vendors and by some
|
||||
hierarchical storage managent systems.
|
||||
|
||||
If unsure, say N.
|
||||
|
@@ -2,9 +2,12 @@
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fsnotify_backend.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h> /* UINT_MAX */
|
||||
#include <linux/mount.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new)
|
||||
{
|
||||
@@ -88,10 +91,37 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
|
||||
static int fanotify_get_response_from_access(struct fsnotify_group *group,
|
||||
struct fsnotify_event *event)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
|
||||
|
||||
wait_event(group->fanotify_data.access_waitq, event->response);
|
||||
|
||||
/* userspace responded, convert to something usable */
|
||||
spin_lock(&event->lock);
|
||||
switch (event->response) {
|
||||
case FAN_ALLOW:
|
||||
ret = 0;
|
||||
break;
|
||||
case FAN_DENY:
|
||||
default:
|
||||
ret = -EPERM;
|
||||
}
|
||||
event->response = 0;
|
||||
spin_unlock(&event->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
|
||||
{
|
||||
int ret;
|
||||
struct fsnotify_event *used_event;
|
||||
struct fsnotify_event *notify_event = NULL;
|
||||
|
||||
BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
|
||||
BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
|
||||
@@ -100,15 +130,31 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e
|
||||
BUILD_BUG_ON(FAN_OPEN != FS_OPEN);
|
||||
BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
|
||||
BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
|
||||
BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
|
||||
BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM);
|
||||
|
||||
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
|
||||
|
||||
ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event);
|
||||
ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge,
|
||||
(void **)¬ify_event);
|
||||
/* -EEXIST means this event was merged with another, not that it was an error */
|
||||
if (ret == -EEXIST)
|
||||
ret = 0;
|
||||
if (used_event)
|
||||
fsnotify_put_event(used_event);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
|
||||
if (event->mask & FAN_ALL_PERM_EVENTS) {
|
||||
/* if we merged we need to wait on the new event */
|
||||
if (notify_event)
|
||||
event = notify_event;
|
||||
ret = fanotify_get_response_from_access(group, event);
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
if (notify_event)
|
||||
fsnotify_put_event(notify_event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -482,6 +482,11 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags,
|
||||
return PTR_ERR(group);
|
||||
|
||||
group->priority = priority;
|
||||
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
|
||||
mutex_init(&group->fanotify_data.access_mutex);
|
||||
init_waitqueue_head(&group->fanotify_data.access_waitq);
|
||||
INIT_LIST_HEAD(&group->fanotify_data.access_list);
|
||||
#endif
|
||||
|
||||
fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
|
||||
if (fd < 0)
|
||||
|
Reference in New Issue
Block a user