lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <1222463947.2872.219.camel@localhost.localdomain>
Date:	Fri, 26 Sep 2008 17:19:07 -0400
From:	Eric Paris <eparis@...hat.com>
To:	linux-kernel@...r.kernel.org, malware-list@...ts.printk.net
Subject: [RFC 7/11] fanotify: blocking and access granting

fanotify: blocking and access granting

From: Eric Paris <eparis@...hat.com>

This patch introduces blocking an access granting for fanotify.  Events are
sent to userspace and the original process is blocked (assuming not
O_NONBLOCK) for at most the amount of time determined by the group.  Total
maximum blocking time is the sum of all groups possible blocking time.

O_NONBLOCK processes trying to read and a group registered to require
blocking/access control on read MUST have a fastpath entry in order to ever
make progress.

Signed-off-by: Eric Paris <eparis@...hat.com>
---

 fs/aio.c                      |    7 ++
 fs/notify/Makefile            |    2 -
 fs/notify/access.c            |  137 +++++++++++++++++++++++++++++++++++++++
 fs/notify/access_user.c       |  144 +++++++++++++++++++++++++++++++++++++++++
 fs/notify/fanotify.c          |   47 +++++++++++--
 fs/notify/fanotify.h          |   27 +++++++-
 fs/notify/fastpath.c          |    2 -
 fs/notify/group.c             |    9 +++
 fs/notify/notification.c      |    1 
 fs/notify/notification_user.c |   75 +++++++++++++++++++--
 fs/open.c                     |    5 +
 fs/read_write.c               |    6 ++
 include/linux/fanotify.h      |   23 ++++++-
 13 files changed, 462 insertions(+), 23 deletions(-)
 create mode 100644 fs/notify/access.c
 create mode 100644 fs/notify/access_user.c


diff --git a/fs/aio.c b/fs/aio.c
index f658441..febe4d0 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -31,6 +31,7 @@
 #include <linux/workqueue.h>
 #include <linux/security.h>
 #include <linux/eventfd.h>
+#include <linux/fanotify.h>
 
 #include <asm/kmap_types.h>
 #include <asm/uaccess.h>
@@ -1454,6 +1455,9 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
 		ret = security_file_permission(file, MAY_READ);
 		if (unlikely(ret))
 			break;
+		ret = fanotify(file, FAN_READ_ACCESS);
+		if (unlikely(ret))
+			break;
 		ret = aio_setup_single_vector(kiocb);
 		if (ret)
 			break;
@@ -1486,6 +1490,9 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
 		ret = security_file_permission(file, MAY_READ);
 		if (unlikely(ret))
 			break;
+		ret = fanotify(file, FAN_READ_ACCESS);
+		if (unlikely(ret))
+			break;
 		ret = aio_setup_vectored_rw(READ, kiocb);
 		if (ret)
 			break;
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 8fac822..e29435a 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -3,4 +3,4 @@ obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
 
 obj-$(CONFIG_DNOTIFY)		+= dnotify.o
 
-obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o notification_user.o group.o group_user.o info_user.o fastpath.o fastpath_user.o
+obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o notification_user.o group.o group_user.o info_user.o fastpath.o fastpath_user.o access.o access_user.o
diff --git a/fs/notify/access.c b/fs/notify/access.c
new file mode 100644
index 0000000..132ef7b
--- /dev/null
+++ b/fs/notify/access.c
@@ -0,0 +1,137 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@...hat.com>
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/atomic.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+int add_event_to_group_access(struct fanotify_group *group, struct fanotify_event *event)
+{
+	struct fanotify_event_holder *holder;
+
+	if (!atomic_read(&group->num_clients))
+		return 0;
+
+	if (list_empty(&event->holder.event_list))
+		holder = (struct fanotify_event_holder *)event;
+	else
+		holder = alloc_event_holder();
+	if (!holder)
+		return -ENOMEM;
+
+	get_event(event);
+	holder->event = event;
+
+	mutex_lock(&group->access_mutex);
+	list_add_tail(&holder->event_list, &group->access_list);
+	mutex_unlock(&group->access_mutex);
+
+	return 0;
+}
+
+int get_response_from_group_access(struct fanotify_group *group, struct fanotify_event *event)
+{
+	int ret;
+	ret = wait_event_interruptible_timeout(group->access_waitq,
+						event->response,
+						msecs_to_jiffies(5000));
+	/* Timeout or signal? */
+	if (ret <= 0) {
+		struct fanotify_event_holder *holder;
+
+		/* pull the event off the access_list */
+		mutex_lock(&group->access_mutex);
+		list_for_each_entry(holder, &group->access_list, event_list) {
+			if (holder->event != event)
+				continue;
+
+			holder->event = NULL;
+			/* as soon as we do this the event.holder might get reused */
+			list_del_init(&holder->event_list);
+
+			if (event != (struct fanotify_event *)holder)
+				destroy_event_holder(holder);
+			put_event(event);
+			break;
+		}
+		mutex_unlock(&group->access_mutex);
+
+		/*
+		 * if we took a signal, return ERESTARTSYS
+		 * if we timed out userspace is broken so return ALLOW
+		 */
+		return ret;
+	}
+
+	/* userspace responded, convert to something usable */
+	switch (event->response) {
+	case FAN_ALLOW:
+		return 0;
+	case FAN_DENY:
+	default:
+		return -EPERM;
+	}
+}
+
+int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response)
+{
+	struct fanotify_event *event = NULL;
+	struct fanotify_event_holder *holder;
+
+	/*
+	 * make sure the response is valid, if invalid we do nothing and either
+	 * userspace can send a valid responce or we will clean it up after the
+	 * timeout
+	 */
+	if (response & ~(FAN_ALLOW | FAN_DENY))
+		return -EINVAL;
+
+	mutex_lock(&group->access_mutex);
+	list_for_each_entry(holder, &group->access_list, event_list) {
+		if (holder->event->cookie != cookie)
+			continue;
+
+		event = holder->event;
+		holder->event = NULL;
+		/* as soon as we do this the event.holder might be reused */
+		list_del_init(&holder->event_list);
+
+		if (event != (struct fanotify_event *)holder)
+			destroy_event_holder(holder);
+		break;
+	}
+	mutex_unlock(&group->access_mutex);
+
+	if (!event)
+		return -ENOENT;
+
+	event->response = response;
+	wake_up(&group->access_waitq);
+	put_event(event);
+
+	return 0;
+}
diff --git a/fs/notify/access_user.c b/fs/notify/access_user.c
new file mode 100644
index 0000000..afd1c7f
--- /dev/null
+++ b/fs/notify/access_user.c
@@ -0,0 +1,144 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@...hat.com>
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+static ssize_t fanotify_access_write(struct file *file, const char __user *buf, size_t lenp, loff_t *offset)
+{
+	char *p;
+	unsigned long cookie;
+	unsigned int response;
+	int rc = 0;
+	struct fanotify_group *group = file->f_path.dentry->d_inode->i_private;
+
+	if (!group)
+		return -EBADF;
+
+	if (!lenp)
+		return 0;
+
+	if (*offset)
+		return -EINVAL;
+
+	if (lenp > PAGE_SIZE)
+		return -ENOMEM;
+
+	p = (char *)get_zeroed_page(GFP_KERNEL);
+
+	rc = copy_from_user(p, buf, lenp);
+	if (rc)
+		goto out;
+	rc = sscanf(p, "%lu %x\n", &cookie, &response);
+	if (rc != 2) {
+		rc = -EPROTO;
+		goto out;
+	}
+
+	rc = fanotify_process_access_response(group, cookie, response);
+	if (rc)
+		goto out;
+
+	rc = lenp;
+out:
+	free_page((unsigned long)p);
+	return rc;
+}
+
+static int fanotify_access_open(struct inode *inode, struct file *file)
+{
+	struct fanotify_group *fgroup = inode->i_private;
+	struct fanotify_group *lgroup;
+	int found = 0;
+
+	/*
+	 * we can't trust fgroup as this open might be simultaneous with an
+	 * unregister.  Since unregister is done with the mutex and makes
+	 * sure there are no users if we get() the group under the lock it
+	 * can't disappear under us.
+	 */
+	mutex_lock(&groups_mutex);
+	list_for_each_entry(lgroup, &groups, group_list) {
+		if (lgroup == fgroup) {
+			fanotify_get_group(lgroup);
+			found = 1;
+			break;
+		}
+	}
+	mutex_unlock(&groups_mutex);
+
+	if (!found)
+		return -EINVAL;
+	return 0;
+}
+
+static int fanotify_access_release(struct inode *inode, struct file *file)
+{
+	struct fanotify_group *group = inode->i_private;
+
+	BUG_ON(!group);
+
+	fanotify_put_group(group);
+
+	return 0;
+}
+
+static struct file_operations access_fops = {
+	.open =		fanotify_access_open,
+	.release =	fanotify_access_release,
+	.write =	fanotify_access_write,
+};
+
+int fanotify_access_user_destroy(struct fanotify_group *group)
+{
+	securityfs_remove(group->access);
+	group->access = NULL;
+
+	return 0;
+}
+
+int fanotify_access_user_create(struct fanotify_group *group)
+{
+	struct dentry *access_file;
+
+	group->access = NULL;
+
+	access_file = securityfs_create_file("access", S_IRUSR|S_IWUSR, group->subdir, group, &access_fops);
+	if (IS_ERR(access_file))
+		return PTR_ERR(access_file);
+
+	group->access = access_file;
+
+	/* initialize the access list elements */
+	atomic_long_set(&group->cookie, 0);
+	INIT_LIST_HEAD(&group->access_list);
+	mutex_init(&group->access_mutex);
+	init_waitqueue_head(&group->access_waitq);
+
+	return 0;
+}
diff --git a/fs/notify/fanotify.c b/fs/notify/fanotify.c
index c7aa1ad..a55d76c 100644
--- a/fs/notify/fanotify.c
+++ b/fs/notify/fanotify.c
@@ -44,22 +44,22 @@ void fanotify_inode_delete(struct inode *inode)
 }
 EXPORT_SYMBOL_GPL(fanotify_inode_delete);
 
-void fanotify(struct file *file, unsigned int mask)
+int fanotify(struct file *file, unsigned int mask)
 {
 	struct fanotify_group *group;
 	struct fanotify_event *event = NULL;
 	struct task_struct *tsk = current;
 	struct inode *inode = file->f_path.dentry->d_inode;
-	int idx;
+	int idx, ret = 0;
 
 	if (likely(list_empty(&groups)))
-		return;
+		return 0;
 
 	if (tsk->flags & PF_NOFACCESS)
-		return;
+		return 0;
 
 	if (!S_ISREG(inode->i_mode))
-		return;
+		return 0;
 
 	if (mask & FAN_MODIFY)
 		fanotify_fastpath_clear(inode);
@@ -86,7 +86,39 @@ void fanotify(struct file *file, unsigned int mask)
 				if (!event)
 					break;
 			}
-			add_event_to_group_notification(group, event);
+			if (mask & FAN_ALL_EVENTS_ACCESS) {
+				/*
+				 * if you register for READ_ACCESS you MUST be setting
+				 * fastpath events or the client will NEVER make progress.
+				 * open is a blocking event so we don't require fastpath
+				 * entries and will wait for a decision.
+				 *
+				 * someday we could do an add_timer here, schedule a workqueue,
+				 * and in there we could check to see if this file got a fastpath
+				 * groups which don't obey the protocol could be evicted or an
+				 * O_NONBLOCK exclusion added.  There are some locking and
+				 * security implications to doing it that way, so for now we
+				 * are just going to assume userspace is adding fastpaths for
+				 * O_NONBLOCK files.....
+				 */
+				if ((mask & FAN_READ_ACCESS) && (file->f_flags & O_NONBLOCK)) {
+					add_event_to_group_notification(group, event);
+					ret = -EWOULDBLOCK;
+					break;
+				}
+				event->cookie = atomic_long_inc_return(&group->cookie);
+				event->response = 0;
+				/* put on access_list first so userspace can't be so fast responding it isn't on the list yet */
+				add_event_to_group_access(group, event);
+
+				add_event_to_group_notification(group, event);
+
+				ret = get_response_from_group_access(group, event);
+				if (ret)
+					break;
+			} else {
+				add_event_to_group_notification(group, event);
+			}
 		}
 	}
 	srcu_read_unlock(&groups_srcu_struct, idx);
@@ -96,10 +128,11 @@ void fanotify(struct file *file, unsigned int mask)
 	 */
 	if (event)
 		put_event(event);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(fanotify);
 
-
 static __init int fanotify_init(void)
 {
 	int rc;
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index 12fb8d3..38e156e 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -21,6 +21,12 @@ struct fanotify_group {
 	struct list_head notification_list; /* list of event_holder this group needs to send to userspace */
 	wait_queue_head_t notification_waitq; /* read() on the notification file blocks on this waitq */
 
+	/* needed to track outstanding requests we expect to hear from userspace */
+	atomic_long_t cookie;		/* next cookie send to userspace for a decision */
+	struct mutex access_mutex;	/* protect access_list list */
+	struct list_head access_list;	/* event_holder we need an answer from userspace */
+	wait_queue_head_t access_waitq;	/* we wait here for userspace access decisions */
+
 	/* stores all fastapth entries assoc with this group so they can be cleaned on unregister */
 	struct mutex fastpath_mutex;	/* protect fastpath_entries list */
 	struct list_head fastpath_entries; /* all fastpath entries for this group */
@@ -31,15 +37,18 @@ struct fanotify_group {
 	struct dentry *notification;	/* pointer to fanotify/name/notification dentry */
 	struct dentry *info;		/* pointer to fanotify/name/info dentry */
 	struct dentry *fastpath;	/* pointer to fanotify/name/fastpath dentry */
+	struct dentry *access;		/* pointer to fanotify/name/access dentry */
 };
 
 /*
- * A single event can be queued in multiple group->notification_lists.
+ * A single event can be queued in multiple group->notification_lists and to at
+ * most one group->access_list at the same time.
  *
- * each group->notification_list will point to an event_holer which in turns points
- * to the actual event that needs to be sent to userspace.
+ * each group->notification_list or group->access_list will point to an
+ * event_holer which in turns points to the actual event that needs to be sent
+ * to userspace.
  *
- * Seemed cheaper to create a refcnt'd event and a small holder for every group
+ * Seemed cheaper to create a refcnt'd event and a small holder for every list
  * than create a different event for every group
  * 
  */
@@ -63,6 +72,9 @@ struct fanotify_event {
 	struct path path;	/* path from the original access */
 	unsigned int mask;	/* the type of access */
 	atomic_t refcnt;	/* how many groups still are using/need to send this event */
+	/* if waiting for a userspace access answer this is the cookie they will send back */
+	unsigned long cookie;
+	unsigned int response;	/* userspace answer to question */
 };
 
 
@@ -128,6 +140,13 @@ extern int fanotify_fastpath_add(struct fanotify_group *group, int fd, unsigned
 extern __init int fastpath_init(void);
 extern __init int fastpath_uninit(void);
 
+extern int fanotify_access_user_create(struct fanotify_group *group);
+extern int fanotify_access_user_destroy(struct fanotify_group *group);
+
+extern int add_event_to_group_access(struct fanotify_group *group, struct fanotify_event *event);
+extern int get_response_from_group_access(struct fanotify_group *group, struct fanotify_event *event);
+extern int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response);
+
 extern void fanotify_get_group(struct fanotify_group *group);
 extern void fanotify_put_group(struct fanotify_group *group);
 extern int fanotify_register_group(char *name, unsigned int priority, unsigned int mask);
diff --git a/fs/notify/fastpath.c b/fs/notify/fastpath.c
index c134426..7f783c0 100644
--- a/fs/notify/fastpath.c
+++ b/fs/notify/fastpath.c
@@ -74,7 +74,7 @@ int is_fastpath(struct file *file, unsigned int mask, struct fanotify_group *gro
 	int found = 0;
 
 	/* if noone is listening, might as well fastpath this group huh? */
-	if (!atomic_read(&group->num_clients))
+	if (unlikely(!atomic_read(&group->num_clients)))
 		return 1;
 
 	read_lock(&inode->fastpath_rwlock);
diff --git a/fs/notify/group.c b/fs/notify/group.c
index 81563ad..90cf59e 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -49,6 +49,8 @@ void fanotify_kill_group(struct fanotify_group *group)
 
 	fanotify_fastpath_user_destroy(group);
 
+	fanotify_access_user_destroy(group);
+
 	securityfs_remove(group->subdir);
 	group->subdir = NULL;
 
@@ -123,6 +125,11 @@ int fanotify_register_group(char *name, unsigned int priority, unsigned int mask
 	if (rc)
 		goto out_clean_info;
 
+	rc = fanotify_access_user_create(group);
+	if (rc)
+		goto out_clean_fastpath;
+
+
 	/* Do we need to be the first entry? */
 	if (list_empty(&groups)) {
 		list_add_rcu(&group->group_list, &groups);
@@ -148,6 +155,8 @@ int fanotify_register_group(char *name, unsigned int priority, unsigned int mask
 
 	return 0;
 
+out_clean_fastpath:
+	fanotify_fastpath_user_destroy(group);
 out_clean_info:
 	fanotify_info_user_destroy(group);
 out_clean_notification:
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index 8f4f439..e1a6a24 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -140,6 +140,7 @@ struct fanotify_event *create_event(struct file *file, unsigned int mask)
 	event->path.mnt = file->f_path.mnt;
 	path_get(&event->path);
 
+	event->cookie = 0;
 	event->mask = mask;
 
 	WARN_ON(!event->path.dentry);
diff --git a/fs/notify/notification_user.c b/fs/notify/notification_user.c
index 4cea5f2..b492137 100644
--- a/fs/notify/notification_user.c
+++ b/fs/notify/notification_user.c
@@ -17,6 +17,7 @@
  */
 
 #include <linux/dcache.h>
+#include <linux/fdtable.h>
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
@@ -41,15 +42,17 @@
  *    %d  = 11 characters
  *    %ld = 19 characters
  *    %x  = 8 characters
+ *    %lu = 19 characters
  *
  * "fd=%d "	= 15 characters
  * "mask=%x "	= 14 characters
+ * "cookie=%lu "= 27 characters
  * "\n "	=  2 characters
  * NULL		=  1 character
  * 
- * MAX_MESG_LEN = 32
+ * MAX_MESG_LEN = 59
  */
-#define MAX_MESG_LEN 32
+#define MAX_MESG_LEN 59
 
 static ssize_t fanotify_notification_read(struct file *file, char __user *buf, size_t lenp, loff_t *offset)
 {
@@ -74,6 +77,7 @@ static ssize_t fanotify_notification_read(struct file *file, char __user *buf, s
 	if (lenp > PAGE_SIZE)
 		lenp = PAGE_SIZE;
 
+
 	output = kmalloc(lenp, GFP_KERNEL);
 	if (!output)
 		return -ENOMEM;
@@ -129,7 +133,7 @@ static ssize_t fanotify_notification_read(struct file *file, char __user *buf, s
 	 * Build metadata string to send to the listener
 	 * IF YOU CHANGE THIS STRING UPDATE MAX_MSG_LEN!!!!!!11111!!!!
 	 */
-	rc = snprintf(output, lenp-1, "fd=%d mask=%x\n", client_fd, event->mask);
+	rc = snprintf(output, lenp-1, "fd=%d mask=%x cookie=%lu\n", client_fd, event->mask, event->cookie);
 	if (rc < 0)
 		goto out;
 	output[rc] = '\0';
@@ -164,7 +168,6 @@ static int fanotify_notification_open(struct inode *inode, struct file *file)
 {
 	struct fanotify_group *fgroup = inode->i_private;
 	struct fanotify_group *lgroup;
-	struct task_struct *tsk = current;
 	int found = 0;
 
 	/*
@@ -176,7 +179,7 @@ static int fanotify_notification_open(struct inode *inode, struct file *file)
 	mutex_lock(&groups_mutex);
 	list_for_each_entry(lgroup, &groups, group_list) {
 		if (lgroup == fgroup) {
-			tsk->flags |= PF_NOFACCESS;
+			current->flags |= PF_NOFACCESS;
 			fanotify_get_group(lgroup);
 			found = 1;
 			break;
@@ -193,12 +196,70 @@ static int fanotify_notification_open(struct inode *inode, struct file *file)
 static int fanotify_notification_release(struct inode *inode, struct file *file)
 {
 	struct fanotify_group *group = inode->i_private;
-	struct task_struct *tsk = current;
+	struct fanotify_group *lgroup;
+	struct files_struct *files;
+	struct file *testfile;
+	struct inode *testinode;
+	struct fdtable *fdt;
+	long j = -1;
+	unsigned long set, i;
+	int found = 0, fput_needed;
 
 	BUG_ON(!group);
 
+	/*
+	 * we do all of this shit to figure out if this process is closing the last
+	 * open it has for any of he magic group->access files.  If this is the last
+	 * access being closed we clear the exclusion flag.  If this is not the last
+	 * close we should remain excluded.
+	 *
+	 * the basic idea behind running all of the open fd's was stolen from
+	 * fs/exec.c:flush_old_files.
+	 */
+	files = current->files;
+	/* if the process is ending current->files will be null */
+	if (files)
+		spin_lock(&files->file_lock);
+	for (; !found && files ;) {
+		j++;
+		i = j * __NFDBITS;
+		fdt = files_fdtable(files);
+		if (i >= fdt->max_fds)
+			break;
+		set = fdt->open_fds->fds_bits[j];
+		if (!set)
+			continue;
+		spin_unlock(&files->file_lock);
+		for ( ; set && !found ; i++, set >>= 1) {
+			if (set & 1) {
+				testfile = fget_light(i, &fput_needed);
+				if (!testfile)
+					continue;
+				/* don't count this file we are closing as being a reference to an access file */
+				if (testfile == file)
+					continue;
+				mutex_lock(&groups_mutex);
+				list_for_each_entry(lgroup, &groups, group_list) {
+					testinode = testfile->f_path.dentry->d_inode;
+					if (testinode == lgroup->access->d_inode) {
+						found = 1;
+						break;
+					}
+					
+				}
+				mutex_unlock(&groups_mutex);
+				fput_light(testfile, fput_needed);
+			}
+		}
+		spin_lock(&files->file_lock);
+	}
+	if (files)
+		spin_unlock(&files->file_lock);
+
 	fanotify_put_group(group);
-	tsk->flags &= ~PF_NOFACCESS;
+
+	if (!found)
+		current->flags &= ~PF_NOFACCESS;
 
 	return 0;
 }
diff --git a/fs/open.c b/fs/open.c
index 1057c34..06a6f79 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -29,6 +29,7 @@
 #include <linux/rcupdate.h>
 #include <linux/audit.h>
 #include <linux/falloc.h>
+#include <linux/fanotify.h>
 
 int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
@@ -820,6 +821,10 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
 	if (error)
 		goto cleanup_all;
 
+	error = fanotify(f, FAN_OPEN_ACCESS);
+	if (error)
+		goto cleanup_all;
+
 	if (!open && f->f_op)
 		open = f->f_op->open;
 	if (open) {
diff --git a/fs/read_write.c b/fs/read_write.c
index 39f1fce..3cc8c41 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -16,6 +16,7 @@
 #include <linux/syscalls.h>
 #include <linux/pagemap.h>
 #include <linux/splice.h>
+#include <linux/fanotify.h>
 #include "read_write.h"
 
 #include <asm/uaccess.h>
@@ -201,6 +202,11 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count
 				read_write == READ ? MAY_READ : MAY_WRITE);
 	if (retval)
 		return retval;
+	if (read_write == READ) {
+		retval = fanotify(file, FAN_READ_ACCESS);
+		if (retval)
+			return retval;
+	}
 	return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
 }
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index a23ab97..5d3e5d0 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -16,6 +16,10 @@
 #define FAN_CLOSE_NOWRITE	0x00000008	/* Unwrittable file closed */
 #define FAN_OPEN		0x00000010	/* File was opened */
 
+/* userspace may also request blocking for permission checks for open and read */
+#define FAN_OPEN_ACCESS		0x00000100
+#define FAN_READ_ACCESS		0x00000200
+
 /* FIXME currently Q's have no limit.... */
 #define FAN_Q_OVERFLOW		0x00004000	/* Event queued overflowed */
 
@@ -33,6 +37,17 @@
 			 FAN_CLOSE_NOWRITE |\
 			 FAN_OPEN)
 
+/*
+ * Like the above list of events only these are the event types in which the
+ * kernel will wait for answers.
+ */
+#define FAN_ALL_EVENTS_ACCESS   (FAN_OPEN_ACCESS |\
+				 FAN_READ_ACCESS)
+
+/* answers will need to be either allow or deny */
+#define FAN_ALLOW		0x00000001
+#define FAN_DENY		0x00000010
+
 #ifdef __KERNEL__
 
 #include <linux/fs.h>
@@ -40,13 +55,15 @@
 
 #ifdef CONFIG_FANOTIFY
 
-extern void fanotify(struct file *file, unsigned int mask);
+extern int fanotify(struct file *file, unsigned int mask);
 extern void fanotify_inode_delete(struct inode *inode);
 
 #else
 
-static inline void fanotify(struct file *file, unsigned int mask)
-{}
+static inline int fanotify(struct file *file, unsigned int mask)
+{
+	return 0;
+}
 
 static inline void fanotify_inode_delete(struct inode *inode)
 {}


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ