Skip to content

Commit

Permalink
fanotify: permissions and blocking
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Paris committed Jul 28, 2010
1 parent c4ec54b commit 9e66e42
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 4 deletions.
14 changes: 14 additions & 0 deletions fs/notify/fanotify/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
54 changes: 50 additions & 4 deletions fs/notify/fanotify/fanotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -88,10 +91,37 @@ static int fanotify_merge(struct list_head *list,
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);
Expand All @@ -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 **)&notify_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;
}

Expand Down
5 changes: 5 additions & 0 deletions fs/notify/fanotify/fanotify_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions include/linux/fanotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
/* FIXME currently Q's have no limit.... */
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */

#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */

/* helper events */
#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */

Expand Down Expand Up @@ -52,7 +55,14 @@
FAN_CLOSE |\
FAN_OPEN)

/*
* All events which require a permission response from userspace
*/
#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\
FAN_ACCESS_PERM)

#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\
FAN_ALL_PERM_EVENTS |\
FAN_Q_OVERFLOW)

#define FANOTIFY_METADATA_VERSION 1
Expand All @@ -65,6 +75,10 @@ struct fanotify_event_metadata {
__s64 pid;
} __attribute__ ((packed));

/* Legit userspace responses to a _PERM event */
#define FAN_ALLOW 0x01
#define FAN_DENY 0x02

/* Helper functions to deal with fanotify_event_metadata buffers */
#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

Expand All @@ -78,5 +92,9 @@ struct fanotify_event_metadata {

#ifdef __KERNEL__

struct fanotify_wait {
struct fsnotify_event *event;
__s32 fd;
};
#endif /* __KERNEL__ */
#endif /* _LINUX_FANOTIFY_H */
12 changes: 12 additions & 0 deletions include/linux/fsnotify_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ struct fsnotify_group {
struct fasync_struct *fa; /* async notification */
struct user_struct *user;
} inotify_data;
#endif
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
struct fanotify_group_private_data {
/* allows a group to block waiting for a userspace response */
struct mutex access_mutex;
struct list_head access_list;
wait_queue_head_t access_waitq;
} fanotify_data;
#endif
};
};
Expand Down Expand Up @@ -227,6 +235,10 @@ struct fsnotify_event {
size_t name_len;
struct pid *tgid;

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
__u32 response; /* userspace answer to question */
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */

struct list_head private_data_list; /* groups can store private data here */
};

Expand Down

0 comments on commit 9e66e42

Please sign in to comment.