From: Jeff Mahoney <jeffm@suse.com>
Subject: [PATCH] security: add ->path_permission

 This patch adds a security_ops->path_permission hook that is identical to
 security_ops->inode_permission except that it is passed a struct path
 instead of a struct inode.

 LSMs which don't implement it will have their ->inode_permission call
 used instead.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---

 include/linux/security.h |   21 +++++++++++++++++++++
 security/capability.c    |    6 ++++++
 security/security.c      |    9 +++++++++
 3 files changed, 36 insertions(+)

--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -592,6 +592,20 @@ static inline void security_free_mnt_opt
  *	file_permission, and recheck access if anything has changed
  *	since inode_permission.
  *
+ * Security hook for path
+ *
+ * @path_permission:
+ *	Check permission before accessing a path.  This hook is called by the
+ *	existing Linux permission function, so a security module can use it to
+ *	provide additional checking for existing Linux permission checks.
+ *	Notice that this hook is called when a file is opened (as well as many
+ *	other operations), whereas the file_security_ops permission hook is
+ *	called when the actual read/write operations are performed. This
+ *	hook is optional and if absent, inode_permission will be substituted.
+ *	@path contains the path structure to check.
+ *	@mask contains the permission mask.
+ *	Return 0 if permission is granted.
+
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1434,6 +1448,7 @@ struct security_operations {
 				    struct fown_struct *fown, int sig);
 	int (*file_receive) (struct file *file);
 	int (*dentry_open) (struct file *file);
+	int (*path_permission) (struct path *path, int mask);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct *p);
@@ -1708,6 +1723,7 @@ int security_file_send_sigiotask(struct
 				 struct fown_struct *fown, int sig);
 int security_file_receive(struct file *file);
 int security_dentry_open(struct file *file);
+int security_path_permission(struct path *path, int mask);
 int security_task_create(unsigned long clone_flags);
 int security_task_alloc(struct task_struct *p);
 void security_task_free(struct task_struct *p);
@@ -2240,6 +2256,11 @@ static inline int security_dentry_open(s
 {
 	return 0;
 }
+
+static inline int security_path_permission(struct path *path, int mask)
+{
+	return 0;
+}
 
 static inline int security_task_create(unsigned long clone_flags)
 {
--- a/security/capability.c
+++ b/security/capability.c
@@ -343,6 +343,11 @@ static int cap_dentry_open(struct file *
 	return 0;
 }
 
+static int cap_path_permission(struct path *path, int mask)
+{
+	return security_inode_permission(path->dentry->d_inode, mask);
+}
+
 static int cap_task_create(unsigned long clone_flags)
 {
 	return 0;
@@ -897,6 +902,7 @@ void security_fixup_ops(struct security_
 	set_to_cap_if_null(ops, file_send_sigiotask);
 	set_to_cap_if_null(ops, file_receive);
 	set_to_cap_if_null(ops, dentry_open);
+	set_to_cap_if_null(ops, path_permission);
 	set_to_cap_if_null(ops, task_create);
 	set_to_cap_if_null(ops, task_alloc_security);
 	set_to_cap_if_null(ops, task_free_security);
--- a/security/security.c
+++ b/security/security.c
@@ -615,6 +615,15 @@ int security_dentry_open(struct file *fi
 	return security_ops->dentry_open(file);
 }
 
+int security_path_permission(struct path *path, int mask)
+{
+	struct inode *inode = path->dentry->d_inode;
+	if (unlikely(IS_PRIVATE(inode)))
+		return 0;
+
+	return security_ops->path_permission(path, mask);
+}
+
 int security_task_create(unsigned long clone_flags)
 {
 	return security_ops->task_create(clone_flags);
